From b7503d9309800a78ed6298db8bad0e2260407ba1 Mon Sep 17 00:00:00 2001 From: weishu Date: Wed, 5 Aug 2026 07:51:16 +0800 Subject: [PATCH] docs: restructure docs site and fix drift against code - split installation.md into installation/deployment/notifications - merge cursor/grok guides into new agents.md with full support matrix - sidebar: grouped sections; add namespace, deployment, notifications, native companion contract - fix license footer (AGPL-3.0), settings schema fields and $id - fix drift in pwa, faq, namespace, how-it-works, voice-assistant, quick-start, native-companion-contract - move mermaid lightbox dogfood doc to localdocs (untracked) - README: complete agent list, replace dead cursor/grok links --- README.md | 7 +- docs/.vitepress/config.ts | 50 +++- docs/api/native-companion-contract.md | 9 +- docs/guide/agents.md | 246 ++++++++++++++++ docs/guide/cursor.md | 96 ------- docs/guide/deployment.md | 295 +++++++++++++++++++ docs/guide/faq.md | 49 +++- docs/guide/grok.md | 90 ------ docs/guide/how-it-works.md | 15 +- docs/guide/installation.md | 348 ++--------------------- docs/guide/namespace.md | 2 +- docs/guide/notifications.md | 67 +++++ docs/guide/pwa.md | 32 ++- docs/guide/quick-start.md | 29 +- docs/guide/voice-assistant.md | 3 +- docs/public/schemas/settings.schema.json | 15 +- docs/tooling/mermaid-lightbox-dogfood.md | 56 ---- 17 files changed, 769 insertions(+), 640 deletions(-) create mode 100644 docs/guide/agents.md delete mode 100644 docs/guide/cursor.md create mode 100644 docs/guide/deployment.md delete mode 100644 docs/guide/grok.md create mode 100644 docs/guide/notifications.md delete mode 100644 docs/tooling/mermaid-lightbox-dogfood.md diff --git a/README.md b/README.md index 1dc8d858..206694e0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # HAPI -Run official Claude Code / Codex / Cursor Agent / Grok Build / OpenCode sessions locally and control them remotely through a Web / PWA / Telegram Mini App. +Run official Claude Code / Codex / Cursor Agent / Grok Build / OpenCode / Kimi / Copilot / Antigravity / Pi sessions locally and control them remotely through a Web / PWA / Telegram Mini App. > **Why HAPI?** HAPI is a local-first alternative to Happy. See [Why Not Happy?](docs/guide/why-hapi.md) for the key differences. @@ -9,7 +9,7 @@ Run official Claude Code / Codex / Cursor Agent / Grok Build / OpenCode sessions - **Seamless Handoff** - Work locally, switch to remote when needed, switch back anytime. No context loss, no session restart. - **Native First** - HAPI wraps your AI agent instead of replacing it. Same terminal, same experience, same muscle memory. - **AFK Without Stopping** - Step away from your desk? Approve AI requests from your phone with one tap. -- **Your AI, Your Choice** - Claude Code, Codex, Cursor Agent, Grok Build, OpenCode—different agents, one unified workflow. +- **Your AI, Your Choice** - Claude Code, Codex, Cursor Agent, Grok Build, OpenCode, Kimi, Copilot, Antigravity, Pi—different agents, one unified workflow. - **Terminal Anywhere** - Run commands from your phone or browser, directly connected to the working machine. - **Voice Control** - Talk to your AI agent hands-free using the built-in voice assistant. - **Workspace Browser** - Opt-in via one or more `hapi runner start --workspace-root ` flags: browse scoped file trees from the web and start sessions in allowed subdirectories. @@ -37,8 +37,7 @@ For self-hosted options (Cloudflare Tunnel, Tailscale), see [Installation](docs/ - [App](docs/guide/pwa.md) - [How it Works](docs/guide/how-it-works.md) -- [Cursor Agent](docs/guide/cursor.md) -- [Grok Build](docs/guide/grok.md) +- [Supported Agents](docs/guide/agents.md) - [Voice Assistant](docs/guide/voice-assistant.md) - [Why HAPI](docs/guide/why-hapi.md) - [FAQ](docs/guide/faq.md) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 676e04af..94a4373d 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -18,15 +18,43 @@ export default defineConfig({ ], sidebar: [ - { text: 'Quick Start', link: '/guide/quick-start' }, - { text: 'Installation', link: '/guide/installation' }, - { text: 'PWA', link: '/guide/pwa' }, - { text: 'How it Works', link: '/guide/how-it-works' }, - { text: 'Cursor Agent', link: '/guide/cursor' }, - { text: 'Grok Build', link: '/guide/grok' }, - { text: 'Voice Assistant', link: '/guide/voice-assistant' }, - { text: 'Why HAPI', link: '/guide/why-hapi' }, - { text: 'FAQ', link: '/guide/faq' } + { + text: 'Get Started', + items: [ + { text: 'Quick Start', link: '/guide/quick-start' }, + { text: 'Installation', link: '/guide/installation' }, + { text: 'PWA', link: '/guide/pwa' } + ] + }, + { + text: 'Guide', + items: [ + { text: 'How it Works', link: '/guide/how-it-works' }, + { text: 'Voice Assistant', link: '/guide/voice-assistant' }, + { text: 'Why HAPI', link: '/guide/why-hapi' }, + { text: 'FAQ', link: '/guide/faq' } + ] + }, + { + text: 'Agents', + items: [ + { text: 'Agents', link: '/guide/agents' } + ] + }, + { + text: 'Advanced', + items: [ + { text: 'Namespace', link: '/guide/namespace' }, + { text: 'Deployment', link: '/guide/deployment' }, + { text: 'Notifications', link: '/guide/notifications' } + ] + }, + { + text: 'API', + items: [ + { text: 'Native Companion Contract', link: '/api/native-companion-contract' } + ] + } ], socialLinks: [ @@ -34,8 +62,8 @@ export default defineConfig({ ], footer: { - message: 'Released under the LGPL-3.0 License.', - copyright: 'Copyright © 2024-present' + message: 'Released under the AGPL-3.0 License.', + copyright: 'Copyright © 2025-present' }, search: { diff --git a/docs/api/native-companion-contract.md b/docs/api/native-companion-contract.md index 09238cf8..308514a3 100644 --- a/docs/api/native-companion-contract.md +++ b/docs/api/native-companion-contract.md @@ -23,12 +23,14 @@ A companion implementing this contract is a **native client to the same hub the { "token": "", "platform": "phone", - "deviceId": "" + "deviceId": "" } ``` `platform`: `"phone"` | `"wear"` +`deviceId`: any string of 1-128 characters chosen by the client (does not have to be a UUID). Must be stable across re-registrations of the same install. + **Response:** `{ "ok": true }` Upsert on `(namespace, deviceId, platform)` - same device re-registering replaces the FCM token. @@ -67,7 +69,8 @@ namespace to avoid duplicate OS notifications. | `title` | string | Notification title | | `body` | string | Notification body | | `severity` | `info` | `info` (ready), `warning` (permission), `success` / `error` (task) | -| `notifySummary` | JSON string | Optional: parsed `AGENT_NOTIFY_SUMMARY` line from agent text | +| `contractVersion` | `1` | Present on every message; see [Versioning](#versioning) | +| `notifySummary` | JSON string | Only on `ready`: parsed `AGENT_NOTIFY_SUMMARY` line from agent text, when present | Native apps **must** handle `data` for Wear; notification block is for display. @@ -79,6 +82,8 @@ Native apps **must** handle `data` for Wear; notification block is for display. | Allow | `POST /api/sessions/:id/permissions/:requestId/approve` | | Deny | `POST /api/sessions/:id/permissions/:requestId/deny` | +`localId` is optional in the send-message body - an opaque client-generated id for reconciling the locally shown message with the server-echoed one. + `sentFrom` extension (optional future): `android-phone`, `android-wear`. --- diff --git a/docs/guide/agents.md b/docs/guide/agents.md new file mode 100644 index 00000000..cd92b8af --- /dev/null +++ b/docs/guide/agents.md @@ -0,0 +1,246 @@ +# Supported Agents + +HAPI is a wrapper around AI coding agents. One CLI (`hapi `) starts any supported agent locally and exposes the same session for remote control from the web app, PWA, and Telegram — with permission prompts, message queueing, and seamless handoff between terminal and phone. + +## Support matrix + +| Agent | Command | Integration | Local | Remote | Permission modes | Resume | +|-------|---------|-------------|:-----:|:------:|------------------|:------:| +| Claude Code | `hapi` / `hapi claude` | Terminal wrapper (local) + Claude Agent SDK (remote) | ✓ | ✓ | `default` `acceptEdits` `auto` `bypassPermissions` `plan` | ✓ | +| Codex | `hapi codex` | TUI wrapper (local) + `codex app-server` JSON-RPC (remote) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` (+ `plan` collaboration mode) | ✓ | +| Cursor Agent | `hapi cursor` | ACP (`agent acp`); legacy stream-json resume | ✓ | ✓ | `default` `plan` `ask` `debug` `autoReview` `yolo` | ✓ | +| Grok Build | `hapi grok` | ACP (`grok agent stdio`) | ✓ | ✓ | `default` `auto` `plan` `bypassPermissions` | ✓ | +| GitHub Copilot | `hapi copilot` | ACP (`copilot --acp --stdio`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ | +| Kimi | `hapi kimi` | ACP (`kimi acp`) | ✓ | ✓ | `default` `read-only` `safe-yolo` `yolo` | ✓ | +| OpenCode | `hapi opencode` | ACP (`opencode acp`) | ✓ | ✓ | `default` `plan` `yolo` | ✓ | +| Antigravity (agy) | `hapi agy` | Interactive PTY + hooks | ✓ | ✓ | `request-review` `always-proceed` | ✓ | +| Pi | `hapi pi` | `pi --mode rpc` (JSON-line RPC over stdio) | — | ✓ | none (always auto-approve) | ✓ | +| Gemini CLI | — | **Removed** — Google sunset the consumer Gemini CLI (2026-06-18) | — | — | — | — | + +Gemini is no longer launchable: `hapi gemini` is kept as a tombstone command that prints a clear error, and existing Gemini sessions remain viewable in the web UI but cannot be resumed. + +## Common concepts + +### ACP + +Most remote integrations speak the [Agent Client Protocol](https://agentclientprotocol.com) (ACP) over stdio through a shared HAPI backend. ACP gives remote sessions bidirectional permission approval, plan/todo updates, question UI, model catalogs, and session resume via `session/load`. Cursor, Grok, Copilot, Kimi, and OpenCode remote sessions all run over ACP. + +### Permission modes + +Permission modes are per-agent — each flavor exposes its own set (see the matrix above). Set the mode at launch with `--permission-mode ` or a shortcut flag (`--yolo`, `--plan`, `--auto-review`, depending on the agent), and switch it mid-session from the web UI. Semantics vary per agent; see the per-agent sections below. + +### Local and remote mode + +Every session is either **local** (driven from the terminal) or **remote** (driven from web/phone). Switching is seamless and keeps the same session state: + +- **Remote → local:** press double-space in the terminal. +- **Local → remote:** send a message from the web UI or phone; the session switches automatically. + +See [Seamless Handoff](./how-it-works.md#seamless-handoff) for details. + +### Resuming sessions + +```bash +hapi resume # Interactive picker of resumable sessions on this machine +hapi resume # Resume a specific HAPI session +``` + +`hapi resume` works for every flavor except Gemini. An active remote session is handed off to the local terminal first. Pi is the exception in the other direction: it has no local input path, so Pi sessions always resume in remote mode. + +## Cursor Agent + +HAPI supports [Cursor Agent CLI](https://cursor.com/docs/cli/using) for running Cursor's AI coding agent with remote control via web and phone. + +### Prerequisites + +Install Cursor Agent CLI: + +- **macOS/Linux:** `curl https://cursor.com/install -fsS | bash` +- **Windows:** `irm 'https://cursor.com/install?win32=true' | iex` + +Verify installation: + +```bash +agent --version +``` + +### Usage + +```bash +hapi cursor # Start Cursor Agent session +hapi cursor resume # Resume a specific chat +hapi cursor --continue # Resume the most recent chat +hapi cursor --plan # Start in Plan mode (shortcut) +hapi cursor --mode plan # Start in Plan mode +hapi cursor --mode ask # Start in Ask mode +hapi cursor --auto-review # Start with Auto-review (Smart Auto) +hapi cursor --yolo # Bypass approval prompts (--force) +hapi cursor --model # Specify model +hapi cursor --cursor-worktree # Cursor-native worktree (auto-named) +hapi cursor --cursor-worktree feature-x # Cursor-native worktree (named) +hapi cursor --cursor-add-dir ../shared # Extra workspace root (repeatable) +``` + +### Permission modes + +| Mode | Description | +|------|-------------| +| `default` | Standard agent behavior | +| `plan` | Plan mode - design approach before coding | +| `ask` | Ask mode - explore code without edits | +| `debug` | Debug mode - hypotheses + instrumentation | +| `autoReview` | Auto-review (Smart Auto) - allowlist/sandbox/classifier instead of full YOLO | +| `yolo` | Bypass approval prompts | + +Set mode via `--plan` / `--mode` / `--permission-mode` / `--auto-review`, or change from the web UI during a session. + +### Cursor-native worktree & multi-root + +- New Session **Worktree** for Cursor uses Cursor's `--worktree` (`~/.cursor/worktrees//`), not HAPI's sibling-directory worktree. +- Exception: if the spawn `directory` is **already** a linked git worktree (HAPI feature worktree, `driver/`, etc.), the runner does **not** pass `--cursor-worktree` — nesting hangs ACP initialize ([#1085](https://github.com/tiann/hapi/issues/1085)). Use the directory as cwd instead. +- Mid-session: send `/worktree`, `/apply-worktree`, `/delete-worktree`, or `/add-dir ` (isolated pass-through). +- CLI: `hapi cursor --cursor-worktree feature-x --cursor-add-dir ../shared` +- ACP ignores Cursor's plain-text `Using worktree: …` stdout banner so remote `sessionType: worktree` can initialize (fixed in [#1085](https://github.com/tiann/hapi/issues/1085)). Other non-JSON ACP stdout remains a fatal protocol error. + +### Slash pass-through (remote) + +These commands are isolated in the queue and forwarded to the agent (ACP prompt or legacy `-p`): + +`/compress` `/summarize` `/compact` `/model` `/multitask` `/best-of-n` `/worktree` `/apply-worktree` `/delete-worktree` `/add-dir` `/context` `/fork` `/auto-review` + +Interactive TUI-only commands (`/config`, `/mcp`, `/sandbox`, `/btw`, `/rewind`, …) are not supported remotely. + +### Modes + +- **Local mode** - Run `hapi cursor` from terminal. Full interactive experience. +- **Remote mode** - Spawn from web/phone when no terminal. New Cursor sessions use `agent acp` with HAPI permission approval, plan/question UI, and richer tool updates. Legacy sessions created before the ACP migration may still resume via the old `agent -p` stream-json path temporarily. + +### Limitations + +- **Multitask UI** - `/multitask` is slash-driven; HAPI does not yet provide an Agents Window-style fleet pane. Subagent `cursor/task` notifications show as CursorTask cards when the agent emits them. +- **Legacy sessions** - Cursor sessions created before the ACP migration can still resume temporarily via stream-json. Start a new Cursor session to get ACP permissions, plans, todos, and question support. +- **Session resume** - ACP sessions resume through `session/load`. Old stream-json `session_id` values are not loadable via ACP; those sessions keep using the legacy path until you start fresh. + +#### Legacy stream-json safety: AskQuestion behavior + +New cursor remote sessions go through ACP, which handles `AskQuestion` via the bidirectional `cursor/ask_question` extension method and is immune to the issue below. The intercept described here exists only for legacy sessions that resume via the older `agent -p` stream-json launcher. + +When running cursor-agent under `--print --output-format stream-json`, the cursor-agent CLI returns a synthetic `Questions skipped by the user, continue with the information you already have` response for the `AskQuestion` tool because there is no IDE surface to render the question. The agent's underlying model can interpret this as legitimate user consent and act on it. + +HAPI's legacy event converter intercepts this synthetic response and rewrites it to an explicit `no_input_surface` error (`status: failed`), so downstream consumers (web UI, Telegram, log readers) surface the fabrication as an error instead of silently passing through fabricated consent. The intercept scans the raw `tool_call` payload for the literal marker text and is scoped to `AskQuestion`-shaped (and converter-fallback `name=unknown`) calls; legitimate read/write/function tools are not affected. + +The intercept drains naturally with the legacy session population - resumed pre-ACP sessions are the only path that still hits this code. + +Tracking issue: [tiann/hapi#784](https://github.com/tiann/hapi/issues/784). + +## Grok Build + +HAPI can run the official Grok Build CLI locally and control the same coding session remotely from the Web/PWA. + +### Install + +Install Grok Build using the official installer: + +::: code-group + +```bash [macOS / Linux / WSL] +curl -fsSL https://x.ai/cli/install.sh | bash +``` + +```powershell [Windows PowerShell] +irm https://x.ai/cli/install.ps1 | iex +``` + +::: + +Verify the installation: + +```bash +grok version +``` + +### Authenticate + +HAPI reuses the Grok CLI's local authentication. On a headless runner machine, authenticate once with device-code login: + +```bash +grok login --device-auth +``` + +Alternatively, configure an xAI API key in the runner environment: + +```bash +export XAI_API_KEY="xai-..." +``` + +Do not place API keys in HAPI configuration files, logs, or a repository. + +### Start a session + +Start the native Grok Build TUI: + +```bash +hapi grok +``` + +Start with explicit launch settings: + +```bash +hapi grok --model grok-4.5 --effort low --permission-mode default +hapi grok --yolo # Shortcut for --permission-mode bypassPermissions +``` + +Sessions created from a HAPI runner start in remote mode automatically. Terminal-created sessions start in the native Grok TUI and can switch to remote control without parsing terminal output. + +### Permission modes + +Grok exposes four permission modes: + +- `default` — tool requests are shown in HAPI for approval or denial. +- `auto` — Grok's own Auto mode: HAPI forwards Grok's `/auto` command to the session. Auto depends on account and CLI-build availability — if Grok does not advertise the `/auto` command, HAPI falls back to `default` and posts a notice in the session. +- `plan` — HAPI asks Grok to plan only and rejects tool execution requests. +- `bypassPermissions` — tool requests are automatically approved for the session (`--yolo` shortcut). + +Use `bypassPermissions` only in a trusted workspace. + +### Resume and handoff + +Remote mode uses Grok's ACP stdio agent (`grok agent stdio`). HAPI stores the native Grok session ID and uses it for: + +- ACP `session/load` after a restart. +- `grok --resume ` when switching back to the native TUI. +- `hapi resume ` from a terminal. + +For a new local session, HAPI supplies a UUID with `grok --session-id`, so the session can be resumed without scraping the fullscreen TUI. + +### Fork and rewind + +When the Grok CLI build advertises them, HAPI uses Grok's ACP extension methods to fork the conversation (current point or from an earlier message, via `_x.ai/session/fork`) and to rewind the conversation to an earlier prompt (via `_x.ai/rewind/*`). Capabilities are probed per session, so older builds simply hide these controls. + +### Model and effort controls + +The Create page discovers Grok's ACP model catalog and the reasoning-effort choices advertised for each model. Remote sessions can switch both model and effort between turns; HAPI applies them through ACP `session/set_model` and `session/set_mode`. From the terminal, pick them at launch with `--model ` and `--effort `. + +HAPI also exposes Grok's common slash commands, discovers skills from `.grok/skills`, `~/.grok/skills`, and shared `.agents/skills`, and asks Grok to set a concise HAPI session title after the first normal prompt. + +### Current limitations + +- OAuth/device-code login must be completed outside the HAPI Web UI. +- Grok subscription, credit, and model availability are controlled by xAI. + +If a remote session reports authentication failure, run `grok login --device-auth` on the runner machine and retry. + +## Other agents + +- **Claude Code** (`hapi` / `hapi claude`) — the default and recommended flavor; local sessions wrap the native TUI, remote sessions drive the Claude Agent SDK. [Claude Code docs](https://docs.anthropic.com/en/docs/claude-code) +- **Codex** (`hapi codex`) — OpenAI's Codex CLI; remote sessions talk to `codex app-server` over JSON-RPC, with a dedicated `plan` collaboration mode. [openai/codex](https://github.com/openai/codex) +- **GitHub Copilot** (`hapi copilot`) — Copilot CLI over ACP (`copilot --acp --stdio`). [GitHub Copilot](https://github.com/features/copilot) +- **Kimi** (`hapi kimi`) — Moonshot AI's Kimi CLI over ACP (`kimi acp`). [MoonshotAI/kimi-cli](https://github.com/MoonshotAI/kimi-cli) +- **OpenCode** (`hapi opencode`) — the open-source OpenCode agent over ACP (`opencode acp`). [opencode.ai](https://opencode.ai) +- **Antigravity** (`hapi agy`) — Google's Antigravity CLI (`agy`), driven as an interactive PTY with hook-based permission bridging. [Google Antigravity](https://antigravity.google) +- **Pi** (`hapi pi`) — the Pi coding agent running as `pi --mode rpc` (JSON-line RPC over piped stdio); remote-control only, no local TUI input path. [badlogic/pi-mono](https://github.com/badlogic/pi-mono) + +## Related + +- [How it Works](./how-it-works.md) - Architecture and data flow +- [Quick Start](./quick-start.md) - Install HAPI and start your first session diff --git a/docs/guide/cursor.md b/docs/guide/cursor.md deleted file mode 100644 index 5c0842cc..00000000 --- a/docs/guide/cursor.md +++ /dev/null @@ -1,96 +0,0 @@ -# Cursor Agent - -HAPI supports [Cursor Agent CLI](https://cursor.com/docs/cli/using) for running Cursor's AI coding agent with remote control via web and phone. - -## Prerequisites - -Install Cursor Agent CLI: - -- **macOS/Linux:** `curl https://cursor.com/install -fsS | bash` -- **Windows:** `irm 'https://cursor.com/install?win32=true' | iex` - -Verify installation: - -```bash -agent --version -``` - -## Usage - -```bash -hapi cursor # Start Cursor Agent session -hapi cursor resume # Resume a specific chat -hapi cursor --continue # Resume the most recent chat -hapi cursor --mode plan # Start in Plan mode -hapi cursor --mode ask # Start in Ask mode -hapi cursor --auto-review # Start with Auto-review (Smart Auto) -hapi cursor --yolo # Bypass approval prompts (--force) -hapi cursor --model # Specify model -hapi cursor --cursor-worktree feature-x # Cursor-native worktree -hapi cursor --cursor-add-dir ../shared # Extra workspace root (repeatable) -``` - -## Permission Modes - -| Mode | Description | -|------|-------------| -| `default` | Standard agent behavior | -| `plan` | Plan mode - design approach before coding | -| `ask` | Ask mode - explore code without edits | -| `debug` | Debug mode - hypotheses + instrumentation | -| `autoReview` | Auto-review (Smart Auto) - allowlist/sandbox/classifier instead of full YOLO | -| `yolo` | Bypass approval prompts | - -Set mode via `--mode` / `--permission-mode` / `--auto-review`, or change from the web UI during a session. - -## Cursor-native worktree & multi-root - -- New Session **Worktree** for Cursor uses Cursor's `--worktree` (`~/.cursor/worktrees//`), not HAPI's sibling-directory worktree. -- Exception: if the spawn `directory` is **already** a linked git worktree (HAPI feature worktree, `driver/`, etc.), the runner does **not** pass `--cursor-worktree` — nesting hangs ACP initialize ([#1085](https://github.com/tiann/hapi/issues/1085)). Use the directory as cwd instead. -- Mid-session: send `/worktree`, `/apply-worktree`, `/delete-worktree`, or `/add-dir ` (isolated pass-through). -- CLI: `hapi cursor --cursor-worktree feature-x --cursor-add-dir ../shared` -- ACP ignores Cursor's plain-text `Using worktree: …` stdout banner so remote `sessionType: worktree` can initialize (fixed in [#1085](https://github.com/tiann/hapi/issues/1085)). Other non-JSON ACP stdout remains a fatal protocol error. - -## Slash pass-through (remote) - -These commands are isolated in the queue and forwarded to the agent (ACP prompt or legacy `-p`): - -`/compress` `/summarize` `/compact` `/model` `/multitask` `/best-of-n` `/worktree` `/apply-worktree` `/delete-worktree` `/add-dir` `/context` `/fork` `/auto-review` - -Interactive TUI-only commands (`/config`, `/mcp`, `/sandbox`, `/btw`, `/rewind`, …) are not supported remotely. - -## Modes - -- **Local mode** - Run `hapi cursor` from terminal. Full interactive experience. -- **Remote mode** - Spawn from web/phone when no terminal. New Cursor sessions use `agent acp` with HAPI permission approval, plan/question UI, and richer tool updates. Legacy sessions created before the ACP migration may still resume via the old `agent -p` stream-json path temporarily. - -## Limitations - -- **Multitask UI** - `/multitask` is slash-driven; HAPI does not yet provide an Agents Window-style fleet pane. Subagent `cursor/task` notifications show as CursorTask cards when the agent emits them. -- **Legacy sessions** - Cursor sessions created before the ACP migration can still resume temporarily via stream-json. Start a new Cursor session to get ACP permissions, plans, todos, and question support. -- **Session resume** - ACP sessions resume through `session/load`. Old stream-json `session_id` values are not loadable via ACP; those sessions keep using the legacy path until you start fresh. - -### Legacy stream-json safety: AskQuestion behavior - -New cursor remote sessions go through ACP, which handles `AskQuestion` via the bidirectional `cursor/ask_question` extension method and is immune to the issue below. The intercept described here exists only for legacy sessions that resume via the older `agent -p` stream-json launcher. - -When running cursor-agent under `--print --output-format stream-json`, the cursor-agent CLI returns a synthetic `Questions skipped by the user, continue with the information you already have` response for the `AskQuestion` tool because there is no IDE surface to render the question. The agent's underlying model can interpret this as legitimate user consent and act on it. - -HAPI's legacy event converter intercepts this synthetic response and rewrites it to an explicit `no_input_surface` error (`status: failed`), so downstream consumers (web UI, Telegram, log readers) surface the fabrication as an error instead of silently passing through fabricated consent. The intercept scans the raw `tool_call` payload for the literal marker text and is scoped to `AskQuestion`-shaped (and converter-fallback `name=unknown`) calls; legitimate read/write/function tools are not affected. - -The intercept drains naturally with the legacy session population - resumed pre-ACP sessions are the only path that still hits this code. - -Tracking issue: [tiann/hapi#784](https://github.com/tiann/hapi/issues/784). - -## Integration - -Once running, your Cursor session appears in the HAPI web app and Telegram Mini App. You can: - -- Monitor session activity -- Approve permissions from your phone -- Send messages when in local mode (messages queue for when you switch) - -## Related - -- [Cursor CLI Documentation](https://cursor.com/docs/cli/using) -- [How it Works](./how-it-works.md) - Architecture and data flow diff --git a/docs/guide/deployment.md b/docs/guide/deployment.md new file mode 100644 index 00000000..91a17073 --- /dev/null +++ b/docs/guide/deployment.md @@ -0,0 +1,295 @@ +# Deployment + +Run the hub and runner as persistent background services, and configure remote access tunnels. + +## Relay tunnel details + +The default public relay (`hapi hub --relay`) works out of the box. This section covers how relay authentication works and how to tune it. + +The hub automatically stores an individually revocable relay key in `settings.json` (`relayAuthKey`). If that persisted key is revoked or the relay rotates its signing secret, HAPI discards it after HTTP 403, requests one replacement, and restarts the tunnel. Relay issuance is limited per public IP; HTTP 429 is reported explicitly, which can affect users sharing a CGNAT or corporate egress address. Set `HAPI_RELAY_AUTH` only when an operator has provided a key manually; rejected environment keys are never overwritten automatically. + +> **Tip:** The relay uses UDP by default. If you experience connectivity issues, set `HAPI_RELAY_FORCE_TCP=true` to force TCP mode. + +Other relay-related environment variables: + +- `HAPI_RELAY_API` - Relay API domain (default: `relay.hapi.run`) +- `HAPI_OFFICIAL_WEB_URL` - Official web app origin allowed via CORS when the relay is enabled (default: `https://app.hapi.run`) + +## Self-hosted tunnels + +If you prefer not to use the public relay (e.g., for lower latency or self-managed infrastructure), you can use these alternatives: + +
+Cloudflare Tunnel + +https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ + +> **Note:** Cloudflare Quick Tunnels (TryCloudflare) are not supported because they [do not support SSE](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/), which HAPI uses for real-time updates. Use a Named Tunnel instead. + +**Named tunnel setup:** + +```bash +# Install cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/ + +# Create and configure a named tunnel +cloudflared tunnel create hapi +cloudflared tunnel route dns hapi hapi.yourdomain.com + +# Run the tunnel +cloudflared tunnel --protocol http2 run hapi +``` + +> **Tip:** Use `--protocol http2` instead of QUIC (the default) to avoid potential timeout issues with long-lived connections. + +
+ +
+Tailscale + +https://tailscale.com/download + +```bash +sudo tailscale up +hapi hub +``` + +Access via your Tailscale IP: + +``` +http://100.x.x.x:3006 +``` +
+ +
+Public IP / Reverse Proxy + +If the hub has a public IP, access directly via `http://your-hub-ip:3006`. + +Use HTTPS (via Nginx, Caddy, etc.) for production. + +**Self-signed certificates (HTTPS)** + +If `HAPI_API_URL` is set to an `https://...` URL with a self-signed (or otherwise untrusted) certificate, the CLI may fail with: + +``` +Error: self signed certificate +``` + +Recommended fixes (in order): + +1. Use a publicly trusted certificate (e.g., Let's Encrypt) +2. Trust your private CA (recommended for private networks) +3. Dev-only workaround: disable TLS verification (insecure) + +```bash +# Preferred: trust your own CA +export NODE_EXTRA_CA_CERTS="/path/to/your-ca.pem" + +# Dev-only workaround: disable TLS verification (INSECURE) +export NODE_TLS_REJECT_UNAUTHORIZED=0 +``` + +If you use the dev-only workaround, assume MITM risk; do not use on public networks. + +
+ +## Background service deployment + +Keep HAPI running persistently so it survives terminal closes, system restarts, and continues running in the background. + +
+Quick: nohup + +Simple one-liner for quick background runs: + +```bash +# Hub +nohup hapi hub --relay > ~/.hapi/logs/hub.log 2>&1 & + +# Runner +nohup hapi runner start-sync > ~/.hapi/logs/runner.log 2>&1 & +``` + +View logs: + +```bash +tail -f ~/.hapi/logs/hub.log +tail -f ~/.hapi/logs/runner.log +``` + +Stop processes: + +```bash +pkill -f "hapi hub" +pkill -f "hapi runner" +``` +
+ +
+pm2 (recommended for Node.js users) + +pm2 provides process management with auto-restart on crashes and system reboot. + +```bash +# Install pm2 +npm install -g pm2 + +# Start hub and runner +pm2 start "hapi hub --relay" --name hapi-hub +pm2 start "hapi runner start-sync" --name hapi-runner + +# View status and logs +pm2 status +pm2 logs hapi-hub +pm2 logs hapi-runner + +# Auto-restart on system reboot +pm2 startup # Follow the printed instructions +pm2 save # Save current process list +``` +
+ +
+macOS: launchd + +Create plist files for automatic startup on macOS. + +**Hub** (`~/Library/LaunchAgents/com.hapi.hub.plist`): + +```xml + + + + + Label + com.hapi.hub + ProgramArguments + + /usr/local/bin/hapi + hub + --relay + + RunAtLoad + + KeepAlive + + StandardOutPath + /Users/YOUR_USERNAME/.hapi/logs/hub.log + StandardErrorPath + /Users/YOUR_USERNAME/.hapi/logs/hub.log + + +``` + +**Runner** (`~/Library/LaunchAgents/com.hapi.runner.plist`): + +```xml + + + + + Label + com.hapi.runner + ProgramArguments + + /usr/local/bin/hapi + runner + start-sync + + RunAtLoad + + KeepAlive + + StandardOutPath + /Users/YOUR_USERNAME/.hapi/logs/runner.log + StandardErrorPath + /Users/YOUR_USERNAME/.hapi/logs/runner.log + + +``` + +Load/unload services: + +```bash +# Load (start) +launchctl load ~/Library/LaunchAgents/com.hapi.hub.plist +launchctl load ~/Library/LaunchAgents/com.hapi.runner.plist + +# Unload (stop) +launchctl unload ~/Library/LaunchAgents/com.hapi.hub.plist +launchctl unload ~/Library/LaunchAgents/com.hapi.runner.plist +``` + +> **macOS sleep note:** macOS may suspend background processes when the display sleeps. Use `caffeinate` to prevent this: +> ```bash +> caffeinate -dimsu hapi hub --relay +> ``` +> Or run `caffeinate -dimsu` in a separate terminal while HAPI is running. +
+ +
+Linux: systemd + +Create user-level systemd services for automatic startup. + +**Hub** (`~/.config/systemd/user/hapi-hub.service`): + +```ini +[Unit] +Description=HAPI Hub +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/hapi hub --relay +Restart=always +RestartSec=5 + +[Install] +WantedBy=default.target +``` + +**Runner** (`~/.config/systemd/user/hapi-runner.service`): + +```ini +[Unit] +Description=HAPI Runner +After=network.target hapi-hub.service + +[Service] +Type=simple +KillMode=process +ExecStart=/usr/local/bin/hapi runner start-sync +Restart=always +RestartSec=5 + +[Install] +WantedBy=default.target +``` + +> **Why `KillMode=process`?** The runner spawns each agent session as a detached child process (`detached: true` in `cli/src/runner/run.ts`) so that sessions stay alive when the runner exits. Without `KillMode=process`, systemd's default `KillMode=control-group` sends SIGTERM to every PID in the runner's cgroup when the unit stops, defeating the detach and forcibly archiving every running session. `KillMode=process` preserves the contract: stopping or restarting the runner only signals the runner itself; agent sessions stay alive, and a fresh runner re-establishes control via the existing socket.io reconnect path. This applies to runner upgrades, manual restarts, and any reboot in which the runner unit is stopped before agents have finished. + +Enable and start: + +```bash +# Reload systemd +systemctl --user daemon-reload + +# Enable (auto-start on login) +systemctl --user enable hapi-hub +systemctl --user enable hapi-runner + +# Start now +systemctl --user start hapi-hub +systemctl --user start hapi-runner + +# View status/logs +systemctl --user status hapi-hub +journalctl --user -u hapi-hub -f +``` + +> **Persist after logout:** To keep services running even when not logged in: +> ```bash +> loginctl enable-linger $USER +> ``` +
diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 49e36f84..464ace94 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -4,7 +4,7 @@ ### What is HAPI? -HAPI is a local-first, self-hosted platform for running and controlling AI coding agents (Claude Code, Codex, Cursor Agent, Grok Build, and OpenCode) remotely. It lets you start coding sessions on your computer and monitor/control them from your phone. +HAPI is a local-first, self-hosted platform for running and controlling AI coding agents remotely (Claude Code, Codex, Cursor Agent, Grok Build, OpenCode, and more — see [Supported Agents](./agents.md)). It lets you start coding sessions on your computer and monitor/control them from your phone. ### What does HAPI stand for? @@ -16,11 +16,7 @@ Yes, HAPI is open source and free to use under the AGPL-3.0-only license. ### What AI agents does HAPI support? -- **Claude Code** (recommended) -- **OpenAI Codex** -- **Cursor Agent** -- **Grok Build** -- **OpenCode** +HAPI supports several coding agents, with Claude Code as the recommended option. See [Supported Agents](./agents.md) for the full list and per-agent setup notes. ## Setup & Installation @@ -40,8 +36,9 @@ http://:3006 If your phone cannot connect, make sure the hub is not only listening on `127.0.0.1`. For LAN access, set `listenHost` to `0.0.0.0` in `~/.hapi/settings.json` or set `HAPI_LISTEN_HOST=0.0.0.0`, then restart `hapi hub`. For internet access: +- Use the built-in relay tunnel: start the hub with `hapi hub --relay` to get a public URL via the tunwg relay (defaults to the official `relay.hapi.run`) - If the hub has a public IP, access it directly (use HTTPS via reverse proxy for production) -- If behind NAT, set up a tunnel (Cloudflare Tunnel, Tailscale, or ngrok) +- If behind NAT, set up your own tunnel (Cloudflare Tunnel, Tailscale, or ngrok) ### What's the access token for? @@ -71,10 +68,11 @@ Yes. Telegram is optional. You can use the web app directly in any browser or in ### How do I receive notifications? -HAPI supports two methods: +HAPI supports three methods: 1. **PWA Push Notifications** - Enable when prompted, works even when app is closed -2. **Telegram Bot** - See [Telegram Setup](./installation.md#telegram-setup) +2. **Telegram Bot** - See [Telegram Setup](./notifications.md#telegram-setup) +3. **FCM native push** - Used by the Android/Wear OS companion apps; notifications are delivered via Firebase Cloud Messaging ### Can I start sessions remotely? @@ -103,7 +101,7 @@ Linux and macOS hosts use Bun's POSIX PTY support. Windows hosts use Bun's ConPT ### How do I use voice control? -Set `ELEVENLABS_API_KEY`, open a session in the web app, and click the microphone button. See [Voice Assistant](./voice-assistant.md). +The voice assistant supports three backends: ElevenLabs, Gemini Live, and Qwen Realtime. Configure at least one, open a session in the web app, and click the microphone button. See [Voice Assistant](./voice-assistant.md) for setup details. ## Security @@ -159,21 +157,34 @@ Also verify your OS firewall allows inbound connections on port `3006`. ### "Invalid token" error +Run `hapi doctor` first - it shows whether `CLI_API_TOKEN` is set and where it comes from (environment variable or settings file). + - Re-run `hapi auth login` - Check token matches in CLI and hub - Verify `~/.hapi/settings.json` has correct `cliApiToken` ### Runner won't start +Run `hapi doctor` first - it shows runner status (including stale state), all hapi processes, and recent log files. + ```bash # Check status hapi runner status +# List sessions the runner is aware of +hapi runner list + +# Stop a specific runner-spawned session +hapi runner stop-session + # Clear stale lock file rm ~/.hapi/runner.state.json.lock # Check logs hapi runner logs + +# Kill runaway hapi processes +hapi doctor clean ``` ### Claude Code not found @@ -204,7 +215,21 @@ Ensure `agent` is on your PATH. hapi doctor ``` -This checks hub connectivity, token validity, agent availability, and more. +This is the first diagnostic step for most issues. It prints: + +- CLI version, platform, and spawn diagnostics +- Configuration and relevant environment variables +- Contents of `settings.json` (token redacted) +- Whether `CLI_API_TOKEN` is set, and its source (it does not contact the hub or validate the token) +- Runner status and runner state (including stale state) +- All running hapi processes +- Recent log files (including runner logs) + +To clean up runaway processes: + +```bash +hapi doctor clean +``` ## Comparison @@ -213,7 +238,7 @@ This checks hub connectivity, token validity, agent availability, and more. | Aspect | Happy | HAPI | |--------|-------|------| | Design | Cloud-first | Local-first | -| Users | Multi-user | Single user | +| Users | Multi-user | Single user by default; lightweight multi-account isolation via [namespaces](./namespace.md) | | Deployment | Multiple services | Single binary | | Data | Encrypted on server | Never leaves your machine | diff --git a/docs/guide/grok.md b/docs/guide/grok.md deleted file mode 100644 index f56f6317..00000000 --- a/docs/guide/grok.md +++ /dev/null @@ -1,90 +0,0 @@ -# Grok Build - -HAPI can run the official Grok Build CLI locally and control the same coding session remotely from the Web/PWA. - -## Install - -Install Grok Build using the official installer: - -::: code-group - -```bash [macOS / Linux / WSL] -curl -fsSL https://x.ai/cli/install.sh | bash -``` - -```powershell [Windows PowerShell] -irm https://x.ai/cli/install.ps1 | iex -``` - -::: - -Verify the installation: - -```bash -grok version -``` - -## Authenticate - -HAPI reuses the Grok CLI's local authentication. On a headless runner machine, authenticate once with device-code login: - -```bash -grok login --device-auth -``` - -Alternatively, configure an xAI API key in the runner environment: - -```bash -export XAI_API_KEY="xai-..." -``` - -Do not place API keys in HAPI configuration files, logs, or a repository. - -## Start a session - -Start the native Grok Build TUI: - -```bash -hapi grok -``` - -Start with explicit launch settings: - -```bash -hapi grok --model grok-4.5 --effort low --permission-mode default -``` - -Sessions created from a HAPI runner start in remote mode automatically. Terminal-created sessions start in the native Grok TUI and can switch to remote control without parsing terminal output. - -## Permission modes - -HAPI exposes a conservative subset for the first integration: - -- `default` — tool requests are shown in HAPI for approval or denial. -- `plan` — HAPI asks Grok to plan only and rejects tool execution requests. -- `bypassPermissions` — tool requests are automatically approved for the session. - -Use `bypassPermissions` only in a trusted workspace. - -## Resume and handoff - -Remote mode uses Grok's ACP stdio agent (`grok agent stdio`). HAPI stores the native Grok session ID and uses it for: - -- ACP `session/load` after a restart. -- `grok --resume ` when switching back to the native TUI. -- `hapi resume ` from a terminal. - -For a new local session, HAPI supplies a UUID with `grok --session-id`, so the session can be resumed without scraping the fullscreen TUI. - -## Model and effort controls - -The Create page discovers Grok's ACP model catalog and the reasoning-effort choices advertised for each model. Remote sessions can switch both model and effort between turns; HAPI applies them through ACP `session/set_model` and `session/set_mode`. - -HAPI also exposes Grok's common slash commands, discovers skills from `.grok/skills`, `~/.grok/skills`, and shared `.agents/skills`, and asks Grok to set a concise HAPI session title after the first normal prompt. - -## Current limitations - -- OAuth/device-code login must be completed outside the HAPI Web UI. -- Grok subscription, credit, and model availability are controlled by xAI. - -If a remote session reports authentication failure, run `grok login --device-auth` on the runner machine and retry. diff --git a/docs/guide/how-it-works.md b/docs/guide/how-it-works.md index 7e44e82a..e4cc1f48 100644 --- a/docs/guide/how-it-works.md +++ b/docs/guide/how-it-works.md @@ -47,7 +47,7 @@ HAPI consists of three interconnected components that work together to provide r ### HAPI CLI -The CLI is a wrapper around AI coding agents (Claude Code, Codex, Cursor Agent, Grok Build, OpenCode). It: +The CLI is a wrapper around AI coding agents. It supports multiple agent flavors out of the box — see [Supported agents](./agents.md) for the full list. It: - Starts and manages coding sessions - Registers sessions with the HAPI hub @@ -56,11 +56,8 @@ The CLI is a wrapper around AI coding agents (Claude Code, Codex, Cursor Agent, **Key Commands:** ```bash -hapi # Start Claude Code session -hapi codex # Start OpenAI Codex session -hapi cursor # Start Cursor Agent session -hapi grok # Start Grok Build session -hapi opencode # Start OpenCode session +hapi # Start a session (Claude Code by default) +hapi # Start a session with another agent flavor (see Supported agents) hapi runner start # Run background service for remote session spawning ``` @@ -82,6 +79,9 @@ A React-based PWA that provides the mobile interface: - **Chat Interface** - Send messages and view agent responses - **Permission Management** - Approve or deny tool access - **File Browser** - Browse project files and view git diffs +- **Terminal View** - Watch the full terminal output of a session +- **Voice Assistant** - Talk to your agent and approve permissions by voice (see [Voice input and assistant](./voice-assistant.md)) +- **Session Sharing** - Share a read-only view of a session via a link - **Remote Spawn** - Start new sessions on any connected machine ## Data Flow @@ -165,6 +165,7 @@ Real-time bidirectional communication for: ### External Access: Tunnel For remote access outside your local network: +- **Built-in relay** (`hapi hub --relay`) - Managed tunwg tunnel (WireGuard + TLS), no third-party account required - **Cloudflare Tunnel** (recommended) - Free, secure, reliable - **Tailscale** - Mesh VPN for private networks - **ngrok** - Quick setup for testing @@ -175,7 +176,7 @@ HAPI's defining feature is the ability to seamlessly hand off control between lo ### Local Mode -When working in local mode, you have the full terminal experience — it is native Claude Code, Codex, or OpenCode: +When working in local mode, you have the full terminal experience — it is the native agent CLI (Claude Code, Codex, OpenCode, and more): - Direct keyboard input with instant response - Full terminal UI with syntax highlighting diff --git a/docs/guide/installation.md b/docs/guide/installation.md index d349ba41..f25c6a82 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -4,7 +4,7 @@ Install the HAPI CLI and set up the hub. ## Prerequisites -- Claude Code, OpenAI Codex CLI, Cursor Agent CLI, Grok Build CLI, or OpenCode CLI installed +- At least one supported agent CLI installed (Claude Code, Codex, Cursor Agent, Grok Build, OpenCode, and more — see [Supported Agents](./agents.md)) Verify your CLI is installed: @@ -31,7 +31,7 @@ HAPI has three components: | Component | Role | Required | |-----------|------|----------| -| **CLI** | Wraps AI agents (Claude/Codex/Cursor/Grok/OpenCode), runs sessions | Yes | +| **CLI** | Wraps AI coding agents, runs sessions | Yes | | **Hub** | Central coordinator: persistence, real-time sync, remote access | Yes | | **Runner** | Background service for remote session spawning | Optional | @@ -115,8 +115,10 @@ cd hapi bun install bun build:single-exe -./cli/dist/hapi +./cli/dist-exe//hapi ``` + +`` is the Bun build target (e.g., `bun-linux-x64`, `bun-darwin-arm64`); it defaults to the host platform and architecture. ## Hub setup @@ -140,9 +142,7 @@ The terminal displays a URL and QR code. Scan to access from anywhere. - No configuration needed - Works behind NAT, firewalls, and any network -> **Tip:** The relay uses UDP by default. If you experience connectivity issues, set `HAPI_RELAY_FORCE_TCP=true` to force TCP mode. - -The hub automatically stores an individually revocable relay key in `settings.json`. If that persisted key is revoked or the relay rotates its signing secret, HAPI discards it after HTTP 403, requests one replacement, and restarts the tunnel. Relay issuance is limited per public IP; HTTP 429 is reported explicitly, which can affect users sharing a CGNAT or corporate egress address. Set `HAPI_RELAY_AUTH` only when an operator has provided a key manually; rejected environment keys are never overwritten automatically. +For relay key management, TCP fallback, and self-hosted tunnel alternatives, see [Deployment](./deployment.md#relay-tunnel-details). ### Local Only @@ -186,10 +186,16 @@ On first run, HAPI: | `CORS_ORIGINS` | - | `corsOrigins` | Allowed CORS origins (comma-separated) | | `TELEGRAM_BOT_TOKEN` | - | `telegramBotToken` | Telegram Bot API token | | `TELEGRAM_NOTIFICATION` | `true` | `telegramNotification` | Enable Telegram notifications | +| `SERVERCHAN_SENDKEY` | - | `serverChanSendKey` | Server酱 (ServerChan) SendKey for push notifications | +| `SERVERCHAN_NOTIFICATION` | `true` | `serverChanNotification` | Enable ServerChan notifications | +| `HAPI_RELAY_API` | `relay.hapi.run` | - | Relay API domain for the public relay | +| `HAPI_RELAY_AUTH` | Per-hub key issued by the relay | `relayAuthKey` | Relay auth key override (set only when an operator provides a key) | | `HAPI_RELAY_FORCE_TCP` | `false` | - | Force TCP mode for relay | +| `HAPI_OFFICIAL_WEB_URL` | `https://app.hapi.run` | - | Official web app origin, added to CORS when the relay is enabled | | `VAPID_SUBJECT` | `mailto:admin@hapi.run` | - | Web Push contact info | | `HAPI_HOME` | `~/.hapi` | - | Config directory path | | `DB_PATH` | `~/.hapi/hapi.db` | - | Database file path | +| `HAPI_EXPERIMENTAL` | - | - | CLI: enable experimental features (`true`/`1`/`yes`) | | `ELEVENLABS_API_KEY` | - | - | ElevenLabs API key for voice | | `ELEVENLABS_AGENT_ID` | Auto-created | - | Custom ElevenLabs agent ID | | `OPENAI_API_KEY` | - | - | OpenAI API key for dictation (`gpt-transcribe` / `gpt-live-transcribe`) | @@ -220,7 +226,7 @@ When ENV values are set and not present in settings.json, they are automatically } ``` -JSON Schema: [settings.schema.json](https://hapi.run/schemas/settings.schema.json) +JSON Schema: [settings.schema.json](https://hapi.run/docs/schemas/settings.schema.json) ## CLI setup @@ -253,109 +259,16 @@ Each machine gets a unique ID stored in `~/.hapi/settings.json`. This allows: - Remote session spawning on specific machines - Machine health monitoring -## Operations +### Diagnostics -### Self-hosted tunnels - -If you prefer not to use the public relay (e.g., for lower latency or self-managed infrastructure), you can use these alternatives: - -
-Cloudflare Tunnel - -https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ - -> **Note:** Cloudflare Quick Tunnels (TryCloudflare) are not supported because they [do not support SSE](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/), which HAPI uses for real-time updates. Use a Named Tunnel instead. - -**Named tunnel setup:** +Run `hapi doctor` for a full diagnostics report: configuration, runner status, logs, and relevant environment info. ```bash -# Install cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/ - -# Create and configure a named tunnel -cloudflared tunnel create hapi -cloudflared tunnel route dns hapi hapi.yourdomain.com - -# Run the tunnel -cloudflared tunnel --protocol http2 run hapi +hapi doctor # Diagnostics report +hapi doctor clean # Kill runaway hapi processes ``` -> **Tip:** Use `--protocol http2` instead of QUIC (the default) to avoid potential timeout issues with long-lived connections. - -
- -
-Tailscale - -https://tailscale.com/download - -```bash -sudo tailscale up -hapi hub -``` - -Access via your Tailscale IP: - -``` -http://100.x.x.x:3006 -``` -
- -
-Public IP / Reverse Proxy - -If the hub has a public IP, access directly via `http://your-hub-ip:3006`. - -Use HTTPS (via Nginx, Caddy, etc.) for production. - -**Self-signed certificates (HTTPS)** - -If `HAPI_API_URL` is set to an `https://...` URL with a self-signed (or otherwise untrusted) certificate, the CLI may fail with: - -``` -Error: self signed certificate -``` - -Recommended fixes (in order): - -1. Use a publicly trusted certificate (e.g., Let's Encrypt) -2. Trust your private CA (recommended for private networks) -3. Dev-only workaround: disable TLS verification (insecure) - -```bash -# Preferred: trust your own CA -export NODE_EXTRA_CA_CERTS="/path/to/your-ca.pem" - -# Dev-only workaround: disable TLS verification (INSECURE) -export NODE_TLS_REJECT_UNAUTHORIZED=0 -``` - -If you use the dev-only workaround, assume MITM risk; do not use on public networks. - -
- -### Telegram setup - -Enable Telegram notifications and Mini App access: - -1. Message [@BotFather](https://t.me/BotFather) and create a bot -2. Set the bot token and public URL -3. Start the hub and bind your account - -```bash -export TELEGRAM_BOT_TOKEN="your-bot-token" -export HAPI_PUBLIC_URL="https://your-public-url" - -hapi hub -``` - -Then message your bot with `/start`, open the app, and enter your `CLI_API_TOKEN`. - -**Troubleshooting:** - -- If binding fails, verify `HAPI_PUBLIC_URL` is accessible from the internet -- Telegram Mini App requires HTTPS (not HTTP) - -### Runner setup +## Runner setup Run a background service for remote session spawning: @@ -372,233 +285,22 @@ With the runner running: - You can spawn sessions remotely from the web app - Sessions persist even when the terminal is closed -
-Alternative: pm2 - -If you prefer pm2 for process management: +Additional runner commands: ```bash -pm2 start "hapi runner start-sync" --name hapi-runner -pm2 save +hapi runner list # List active sessions +hapi runner stop-session # Stop a single session managed by the runner ``` -
-### Background service deployment - -Keep HAPI running persistently so it survives terminal closes, system restarts, and continues running in the background. - -
-Quick: nohup - -Simple one-liner for quick background runs: +Use `--workspace-root ` to restrict which directories the runner can browse and spawn sessions in. Repeat the flag to allow multiple directories; supports `~` expansion: ```bash -# Hub -nohup hapi hub --relay > ~/.hapi/logs/hub.log 2>&1 & - -# Runner -nohup hapi runner start-sync > ~/.hapi/logs/runner.log 2>&1 & +hapi runner start --workspace-root ~/projects --workspace-root ~/work ``` -View logs: +For running the hub and runner as persistent background services (pm2, launchd, systemd), see [Deployment](./deployment.md). -```bash -tail -f ~/.hapi/logs/hub.log -tail -f ~/.hapi/logs/runner.log -``` - -Stop processes: - -```bash -pkill -f "hapi hub" -pkill -f "hapi runner" -``` -
- -
-pm2 (recommended for Node.js users) - -pm2 provides process management with auto-restart on crashes and system reboot. - -```bash -# Install pm2 -npm install -g pm2 - -# Start hub and runner -pm2 start "hapi hub --relay" --name hapi-hub -pm2 start "hapi runner start-sync" --name hapi-runner - -# View status and logs -pm2 status -pm2 logs hapi-hub -pm2 logs hapi-runner - -# Auto-restart on system reboot -pm2 startup # Follow the printed instructions -pm2 save # Save current process list -``` -
- -
-macOS: launchd - -Create plist files for automatic startup on macOS. - -**Hub** (`~/Library/LaunchAgents/com.hapi.hub.plist`): - -```xml - - - - - Label - com.hapi.hub - ProgramArguments - - /usr/local/bin/hapi - hub - --relay - - RunAtLoad - - KeepAlive - - StandardOutPath - /Users/YOUR_USERNAME/.hapi/logs/hub.log - StandardErrorPath - /Users/YOUR_USERNAME/.hapi/logs/hub.log - - -``` - -**Runner** (`~/Library/LaunchAgents/com.hapi.runner.plist`): - -```xml - - - - - Label - com.hapi.runner - ProgramArguments - - /usr/local/bin/hapi - runner - start-sync - - RunAtLoad - - KeepAlive - - StandardOutPath - /Users/YOUR_USERNAME/.hapi/logs/runner.log - StandardErrorPath - /Users/YOUR_USERNAME/.hapi/logs/runner.log - - -``` - -Load/unload services: - -```bash -# Load (start) -launchctl load ~/Library/LaunchAgents/com.hapi.hub.plist -launchctl load ~/Library/LaunchAgents/com.hapi.runner.plist - -# Unload (stop) -launchctl unload ~/Library/LaunchAgents/com.hapi.hub.plist -launchctl unload ~/Library/LaunchAgents/com.hapi.runner.plist -``` - -> **macOS sleep note:** macOS may suspend background processes when the display sleeps. Use `caffeinate` to prevent this: -> ```bash -> caffeinate -dimsu hapi hub --relay -> ``` -> Or run `caffeinate -dimsu` in a separate terminal while HAPI is running. -
- -
-Linux: systemd - -Create user-level systemd services for automatic startup. - -**Hub** (`~/.config/systemd/user/hapi-hub.service`): - -```ini -[Unit] -Description=HAPI Hub -After=network.target - -[Service] -Type=simple -ExecStart=/usr/local/bin/hapi hub --relay -Restart=always -RestartSec=5 - -[Install] -WantedBy=default.target -``` - -**Runner** (`~/.config/systemd/user/hapi-runner.service`): - -```ini -[Unit] -Description=HAPI Runner -After=network.target hapi-hub.service - -[Service] -Type=simple -KillMode=process -ExecStart=/usr/local/bin/hapi runner start-sync -Restart=always -RestartSec=5 - -[Install] -WantedBy=default.target -``` - -> **Why `KillMode=process`?** The runner spawns each agent session as a detached child process (`detached: true` in `cli/src/runner/run.ts`) so that sessions stay alive when the runner exits. Without `KillMode=process`, systemd's default `KillMode=control-group` sends SIGTERM to every PID in the runner's cgroup when the unit stops, defeating the detach and forcibly archiving every running session. `KillMode=process` preserves the contract: stopping or restarting the runner only signals the runner itself; agent sessions stay alive, and a fresh runner re-establishes control via the existing socket.io reconnect path. This applies to runner upgrades, manual restarts, and any reboot in which the runner unit is stopped before agents have finished. - -Enable and start: - -```bash -# Reload systemd -systemctl --user daemon-reload - -# Enable (auto-start on login) -systemctl --user enable hapi-hub -systemctl --user enable hapi-runner - -# Start now -systemctl --user start hapi-hub -systemctl --user start hapi-runner - -# View status/logs -systemctl --user status hapi-hub -journalctl --user -u hapi-hub -f -``` - -> **Persist after logout:** To keep services running even when not logged in: -> ```bash -> loginctl enable-linger $USER -> ``` -
- -### Voice assistant setup - -Enable voice control: - -1. Get an API key from [elevenlabs.io](https://elevenlabs.io/app/settings/api-keys) -2. Set the environment variable: - -```bash -export ELEVENLABS_API_KEY="your-api-key" -hapi hub --relay -``` - -See [Voice Assistant](./voice-assistant.md) for usage details. - -### Security notes +## Security notes - Keep tokens secret and rotate if needed - Use HTTPS for public access diff --git a/docs/guide/namespace.md b/docs/guide/namespace.md index d2ac4a64..6b077cfd 100644 --- a/docs/guide/namespace.md +++ b/docs/guide/namespace.md @@ -27,7 +27,7 @@ CLI_API_TOKEN="your-base-token:alice" ## Limitations and gotchas -- Hub-side `CLI_API_TOKEN` must not include `:`. If it does, the hub will strip the suffix and log a warning. +- Hub-side `CLI_API_TOKEN` must not include `:`. The hub validates the token from both the environment variable and `settings.json`, and refuses to start with an error if a suffix is present. - Namespaces are isolated: sessions, machines, and users are not visible across namespaces. - One machine ID cannot be reused across namespaces. - To run multiple namespaces on one machine, use a separate `HAPI_HOME` per namespace, or clear the machine ID with `hapi auth logout` before switching. diff --git a/docs/guide/notifications.md b/docs/guide/notifications.md new file mode 100644 index 00000000..f6c1c55c --- /dev/null +++ b/docs/guide/notifications.md @@ -0,0 +1,67 @@ +# Notifications + +Get notified when sessions need input, request permissions, fail, or complete — via Telegram, Server酱 (ServerChan), Web Push, or voice. + +Web Push works out of the box once you [install the PWA](./pwa.md); no configuration needed. The channels below are optional. + +## Telegram Setup + +Enable Telegram notifications and Mini App access: + +1. Message [@BotFather](https://t.me/BotFather) and create a bot +2. Set the bot token and public URL +3. Start the hub and bind your account + +```bash +export TELEGRAM_BOT_TOKEN="your-bot-token" +export HAPI_PUBLIC_URL="https://your-public-url" + +hapi hub +``` + +Then message your bot with `/start`, open the app, and enter your `CLI_API_TOKEN`. + +Related environment variables: + +- `TELEGRAM_NOTIFICATION` - Enable/disable Telegram notifications (default: `true`) + +**Troubleshooting:** + +- If binding fails, verify `HAPI_PUBLIC_URL` is accessible from the internet +- Telegram Mini App requires HTTPS (not HTTP) + +## ServerChan (Server酱) Setup + +Server酱 pushes notifications to WeChat and other channels. The hub sends ServerChan messages when a session is ready for input, requests a permission, a task fails, or a session completes. + +1. Get a SendKey from [sct.ftqq.com](https://sct.ftqq.com/) +2. Set the SendKey and start the hub: + +```bash +export SERVERCHAN_SENDKEY="your-sendkey" +export HAPI_PUBLIC_URL="https://your-public-url" + +hapi hub +``` + +Messages include a link back to the session, built from `HAPI_PUBLIC_URL`. + +Related environment variables: + +- `SERVERCHAN_NOTIFICATION` - Enable/disable ServerChan notifications (default: `true`) + +Both values can also be set in `settings.json` (`serverChanSendKey`, `serverChanNotification`). + +## Voice assistant setup + +Enable voice control: + +1. Get an API key from [elevenlabs.io](https://elevenlabs.io/app/settings/api-keys) +2. Set the environment variable: + +```bash +export ELEVENLABS_API_KEY="your-api-key" +hapi hub --relay +``` + +See [Voice Assistant](./voice-assistant.md) for usage details. diff --git a/docs/guide/pwa.md b/docs/guide/pwa.md index 631795f0..3eeb659e 100644 --- a/docs/guide/pwa.md +++ b/docs/guide/pwa.md @@ -50,9 +50,8 @@ When offline, HAPI can: - Display cached session lists - Show previously loaded messages -- Queue actions for when you're back online -An offline indicator appears when you lose connection. +HAPI does not queue actions taken while offline — an offline banner appears at the top when you lose connection, and live features resume once you're back online. ### Auto-Update @@ -65,13 +64,15 @@ HAPI checks for updates in the background and lets you choose when to reload: HAPI uses a user-controlled reload instead of forcing an automatic refresh, so you choose when to reload. The banner cannot be dismissed without upgrading, so you won't forget you're on an old build. -### Background Sync +### Share Target (Android) -Actions taken offline are synced when reconnected: +On Android, HAPI appears in the system share sheet. When you share content to HAPI: -- Pending messages are sent -- Permission decisions are relayed -- Session state is refreshed +1. Chrome sends a `POST /share` multipart form (title, text, URL, and files) to the app +2. The service worker intercepts the request and stores the payload in IndexedDB +3. The app is then redirected (303) to the share picker, which reads the stored content + +This lets you share images, PDFs, text, and other files directly into a session from any app. ## Caching Strategy @@ -82,6 +83,9 @@ HAPI uses intelligent caching: | App shell | Cache first | Until update | | Sessions API | Network first | 5 minutes | | Machines API | Network first | 10 minutes | +| Session detail API | Network first | 5 minutes | +| CDN (cdn.socket.io) | Cache first | 30 days | +| CDN (telegram.org) | Cache first | 7 days | | Static assets | Cache first | Forever | ## Notifications @@ -100,9 +104,14 @@ HAPI supports push notifications to alert you when agents need attention. |------|-----------| | Permission Request | Agent needs your approval | | Ready | Agent finished and awaits input | +| Task completed / Task failed | A background task finishes (success or failure) | + +### Native Push via FCM + +In addition to Web Push, the hub can send notifications through Firebase Cloud Messaging (FCM) to native companion apps on Android and Wear OS. When FCM is configured and a native device is registered for your namespace, the companion app is treated as the canonical notification surface — if FCM already delivered a notification, the hub skips the Web Push duplicate so you only get one alert. See the [native companion API contract](../api/native-companion-contract.md) for setup details. ::: tip -If push notifications don't work in your region (e.g., FCM unavailable), use [Telegram integration](./installation.md#telegram-setup) instead. +If push notifications don't work in your region (e.g., FCM unavailable), use [Telegram integration](./notifications.md#telegram-setup) instead. ::: ## Managing Your PWA @@ -143,9 +152,7 @@ If you experience issues: ### Battery Optimization -On Android, disable battery optimization for HAPI to ensure: -- Background sync works reliably -- Notifications arrive promptly +On Android, disable battery optimization for HAPI to ensure notifications arrive promptly. Settings → Apps → HAPI → Battery → Unrestricted @@ -188,7 +195,6 @@ You can install HAPI on multiple devices: ### iOS-Specific Issues - Must use Safari for installation -- No background sync on iOS - Limited offline capabilities ## Telegram Mini App Alternative @@ -200,4 +206,4 @@ If PWA doesn't suit your needs, consider the Telegram Mini App: - Same features as PWA - Integrated notifications -See [Installation Guide](./installation.md#telegram-setup) for Telegram setup. +See [Notifications](./notifications.md#telegram-setup) for Telegram setup. diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index 62d4cfdd..c5343d58 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -4,25 +4,11 @@ ## Install HAPI -::: code-group - -```bash [npm] +```bash npm install -g @twsxtd/hapi --registry=https://registry.npmjs.org ``` -```bash [Homebrew] -brew install tiann/tap/hapi -``` - -```bash [npx (one-off)] -npx @twsxtd/hapi -``` - -::: - -> Recommendation: use the official npm registry for global install. Some mirrors may not sync platform packages in time. - -Other install options: [Installation](./installation.md) +Other install options (Homebrew, npx, prebuilt binary, source): [Installation](./installation.md#install-the-cli) ## Start the hub @@ -30,13 +16,9 @@ Other install options: [Installation](./installation.md) hapi hub --relay ``` -On first run, HAPI prints an access token and saves it to `~/.hapi/settings.json`. +On first run, HAPI prints an access token and saves it to `~/.hapi/settings.json`. The terminal displays a URL and QR code for remote access. -`hapi server` remains supported as an alias. - -The terminal will display a URL and QR code for remote access. - -> End-to-end encrypted with WireGuard + TLS. +Details and local-only mode: [Hub setup](./installation.md#hub-setup) ## Start a coding session @@ -58,5 +40,6 @@ Enter your access token to log in. - [Seamless Handoff](./how-it-works.md#seamless-handoff) - Switch between terminal and phone seamlessly - [Hub setup](./installation.md#hub-setup) - Access HAPI from anywhere -- [Notifications](./installation.md#telegram-setup) - Set up Telegram notifications +- [Notifications](./notifications.md#telegram-setup) - Set up Telegram or ServerChan notifications +- [Deployment](./deployment.md) - Run HAPI as a persistent background service - [Install the App](./pwa.md) - Add HAPI to your home screen diff --git a/docs/guide/voice-assistant.md b/docs/guide/voice-assistant.md index 59e7196e..e21fa4de 100644 --- a/docs/guide/voice-assistant.md +++ b/docs/guide/voice-assistant.md @@ -38,7 +38,7 @@ The voice assistant lets you: - **Approve permissions by voice** - Say "yes" or "no" to approve or deny permission requests - **Monitor progress** - Receive spoken updates when tasks complete or errors occur -The assistant bridges voice communication with your active coding agent (Claude Code, Codex, Cursor Agent, Grok Build, or OpenCode), relaying your requests and summarizing responses in natural speech. +The assistant bridges voice communication with your active coding session, whatever agent flavor it runs. It relays your requests to the agent and summarizes responses in natural speech. ## Prerequisites @@ -130,6 +130,7 @@ Everything user-facing lives under **Settings → Voice**: - **Persona & instructions** - Rename/rebrand the assistant and shape its character and speaking style (preset or custom text) - **How it sounds** - ElevenLabs tuning sliders (stability, style, speed, similarity boost, speaker boost) and Gemini's affective dialog option +- **Voice diagnostics** - Check the composed system prompt size against per-backend wire limits, see truncation warnings and the last voice session's context notice, and preview the read-only platform rules ## How It Works diff --git a/docs/public/schemas/settings.schema.json b/docs/public/schemas/settings.schema.json index 0c7ec423..9c46732f 100644 --- a/docs/public/schemas/settings.schema.json +++ b/docs/public/schemas/settings.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://hapi.run/schemas/settings.schema.json", + "$id": "https://hapi.run/docs/schemas/settings.schema.json", "title": "HAPI Settings", "description": "Configuration file for HAPI (~/.hapi/settings.json)", "type": "object", @@ -55,6 +55,19 @@ "default": true, "description": "Enable Telegram notifications. ENV: TELEGRAM_NOTIFICATION" }, + "serverChanSendKey": { + "type": "string", + "description": "ServerChan SendKey for WeChat push notifications. ENV: SERVERCHAN_SENDKEY" + }, + "serverChanNotification": { + "type": "boolean", + "default": true, + "description": "Enable ServerChan notifications. ENV: SERVERCHAN_NOTIFICATION" + }, + "relayAuthKey": { + "type": "string", + "description": "Per-hub relay auth key issued by the relay server. Auto-obtained if not set. ENV: HAPI_RELAY_AUTH" + }, "vapidKeys": { "type": "object", "description": "Auto-generated Web Push VAPID keys. Do not edit manually.", diff --git a/docs/tooling/mermaid-lightbox-dogfood.md b/docs/tooling/mermaid-lightbox-dogfood.md deleted file mode 100644 index 081f07e4..00000000 --- a/docs/tooling/mermaid-lightbox-dogfood.md +++ /dev/null @@ -1,56 +0,0 @@ -# Mermaid lightbox dogfood (Playwright) - -Two Playwright targets: - -| Target | What it exercises | Command | -|--------|-------------------|---------| -| **Component (Vite)** | `MermaidDiagram` in isolation on dev server | `npm run test:mermaid-lightbox:playwright` | -| **Live session (hub)** | Real chat thread, click-to-zoom | `npm run test:mermaid-lightbox:live` | - -## Live session (production-shaped) - -**Session URL (after seed):** - -`{HAPI_URL}/sessions/a7370000-0000-4000-8000-000000000737` - -Default `HAPI_URL` for live tests: `http://127.0.0.1:3006` (daily driver). -For tailnet: `HAPI_URL=https://hapi.tail9944ee.ts.net` (seed **that** hub's DB first). - -### 1. Seed fixtures (hub DB) - -On the machine that owns `HAPI_DB_PATH` (usually `~/.hapi/hapi.db`): - -```bash -bun run seed:mermaid-lightbox:session -``` - -Inserts 15 assistant messages (one per diagram type). Re-run to replace messages in that session. - -### 2. Deploy web with your branch - -```bash -hapi-driver-rebuild --build-web -# activate soup when ready (restarts hub) -``` - -Hard-refresh the browser after web changes. - -### 3. Run live Playwright - -```bash -HAPI_LIVE=1 HAPI_URL=http://127.0.0.1:3006 npm run test:mermaid-lightbox:live -``` - -Requires `~/.hapi/settings.json` `cliApiToken` (or `HAPI_ACCESS_TOKEN`). - -**Pass criteria:** dialog opens, SVG in **shadow root** (`[data-mermaid-lightbox]`), expands vs inline, sequence has multiple actors/lines. - -If tests report `legacy` or `empty` lightbox, the served web bundle predates the shadow-DOM fix — rebuild driver. - -## Isolation page (not chat) - -Only for component regression; **not** the same as chat: - -`http://127.0.0.1:5173/mermaid-lightbox-e2e.html?case=sequence` (Vite dev, not on tailnet dist unless you add the HTML to a build). - -Diagram sources: `web/src/dev/mermaid-lightbox-cases.ts`