Files
hapi/cli
9eb0dacf75 fix(cli): filter raw SSE event JSON from leaking into chat messages (#405)
* fix(cli): filter raw SSE event JSON from leaking into chat messages

Two types of internal JSON were appearing as visible text in Telegram
Mini App and web chat:

1. `rate_limit_event` — the rate limit parser returned `null` for
   unknown statuses, causing raw JSON to pass through as assistant text.
   Changed to `{ suppress: true }` so all rate_limit_event variants are
   handled; new statuses that need display can be added explicitly.

2. `{ type: "output", data: { ... } }` — internal session metadata
   envelopes leaked through the ACP text chunk pipeline. Added an
   `isInternalEventJson` filter that catches JSON objects with known
   internal envelope types (output, event, queue-operation) before they
   enter the text buffer.

Closes #386

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): narrow internal event filter to match only leaked metadata shape

Address review feedback: the broad type-based filter could suppress
legitimate assistant JSON with type "event" or "queue-operation".

Narrow the check to only match the specific leaked metadata envelope:
{ type: "output", data: { parentUuid, sessionId, userType } }

Add negative tests confirming other JSON types pass through.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): suppress malformed rate_limit_event without resetsAt

Address review: rate_limit_event payloads missing resetsAt still leaked
as raw JSON because parseRateLimitText returned null before reaching the
unknown-status suppress. Move the allowed check before the resetsAt
guard and suppress malformed payloads instead of passing them through.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): handle parentUuid: null in internal event filter

Root/first-message metadata envelopes have parentUuid: null rather than
a string, so the filter missed them. Accept both string and null.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* test(cli): add e2e regression tests for metadata envelope filtering

Add AcpMessageHandler tests that verify leaked { type: "output", data }
metadata envelopes (both parentUuid string and null) are dropped before
reaching the text buffer.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): clear buffered prefix when cumulative metadata chunk arrives

When a leaked metadata envelope arrives as cumulative streaming chunks
(first an incomplete JSON prefix, then the full blob), the filter
dropped the full chunk but left the prefix in bufferedText. Clear the
buffer when the detected internal JSON starts with the buffered prefix.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): round resetsAt to integer for pipe-delimited format

The web-side regex uses \d+ to parse the timestamp, so a float value
would silently fail to match. Apply Math.round to ensure integer output.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

* fix(cli): clear buffered prefix for cumulative rate_limit_event chunks

The prefix-clearing logic only applied to the isInternalEventJson
branch but not to the parseRateLimitText branch, so cumulative
rate_limit_event chunks could leave a raw JSON prefix in the buffer.

Hoist the prefix check before both filters and apply uniformly.
Add regression tests for suppressed and displayable cumulative
rate_limit_event scenarios.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

---------

Co-authored-by: HAPI <noreply@hapi.run>
2026-04-06 20:42:28 +08:00
..
2025-12-16 15:03:50 +08:00
2025-12-16 15:03:50 +08:00
2025-12-16 15:03:50 +08:00
2026-01-03 22:22:45 +08:00
2025-12-16 15:03:50 +08:00
2026-01-04 20:45:15 +08:00
2026-03-31 18:53:38 +08:00
2025-12-16 15:03:50 +08:00

hapi CLI

Run Claude Code, Codex, Cursor Agent, Gemini, or OpenCode sessions from your terminal and control them remotely through the hapi hub.

What it does

  • Starts Claude Code sessions and registers them with hapi-hub.
  • Starts Codex mode for OpenAI-based sessions.
  • Starts Cursor Agent mode for Cursor CLI sessions.
  • Starts Gemini mode via ACP (Anthropic Code Plugins).
  • Starts OpenCode mode via ACP and its plugin hook system.
  • Provides an MCP stdio bridge for external tools.
  • Manages a background runner for long-running sessions.
  • Includes diagnostics and auth helpers.

Typical flow

  1. Start the hub and set env vars (see ../hub/README.md).
  2. Set the same CLI_API_TOKEN on this machine or run hapi auth login.
  3. Run hapi to start a session.
  4. Use the web app or Telegram Mini App to monitor and control.

Commands

Session commands

  • hapi - Start a Claude Code session (passes through Claude CLI flags). See src/index.ts.
  • hapi codex - Start Codex mode. See src/codex/runCodex.ts.
  • hapi codex resume <sessionId> - Resume existing Codex session.
  • hapi cursor - Start Cursor Agent mode. See src/cursor/runCursor.ts. Supports hapi cursor resume <chatId>, hapi cursor --continue, --mode plan|ask, --yolo, --model. Local and remote modes supported; remote uses agent -p with stream-json.
  • hapi gemini - Start Gemini mode via ACP. See src/agent/runners/runAgentSession.ts. Note: Gemini runs in remote mode only; it waits for messages from the hub UI/Telegram.
  • hapi opencode - Start OpenCode mode via ACP. See src/opencode/runOpencode.ts. Note: OpenCode supports local and remote modes; local mode streams via OpenCode plugins.

Authentication

  • hapi auth status - Show authentication configuration and token source.
  • hapi auth login - Interactively enter and save CLI_API_TOKEN.
  • hapi auth logout - Clear saved credentials.

See src/commands/auth.ts.

Runner management

  • hapi runner start - Start runner as detached process.
  • hapi runner stop - Stop runner gracefully.
  • hapi runner status - Show runner diagnostics.
  • hapi runner list - List active sessions managed by runner.
  • hapi runner stop-session <sessionId> - Terminate specific session.
  • hapi runner logs - Print path to latest runner log file.

See src/runner/run.ts.

Diagnostics

  • hapi doctor - Show full diagnostics (version, runner status, logs, processes).
  • hapi doctor clean - Kill runaway HAPI processes.

See src/ui/doctor.ts.

Other

  • hapi mcp - Start MCP stdio bridge. See src/codex/happyMcpStdioBridge.ts.
  • hapi hub - Start the bundled hub (single binary workflow).
  • hapi server - Alias for hapi hub.

Configuration

See src/configuration.ts for all options.

Required

  • CLI_API_TOKEN - Shared secret; must match the hub. Can be set via env or ~/.hapi/settings.json (env wins).
  • HAPI_API_URL - Hub base URL (default: http://localhost:3006).

Optional

  • HAPI_HOME - Config/data directory (default: ~/.hapi).
  • HAPI_EXPERIMENTAL - Enable experimental features (true/1/yes).
  • HAPI_CLAUDE_PATH - Path to a specific claude executable.
  • HAPI_HTTP_MCP_URL - Default MCP target for hapi mcp.

Runner

  • HAPI_RUNNER_HEARTBEAT_INTERVAL - Heartbeat interval in ms (default: 60000).
  • HAPI_RUNNER_HTTP_TIMEOUT - HTTP timeout for runner control in ms (default: 10000).

Worktree (set by runner)

  • HAPI_WORKTREE_BASE_PATH - Base repository path.
  • HAPI_WORKTREE_BRANCH - Current branch name.
  • HAPI_WORKTREE_NAME - Worktree name.
  • HAPI_WORKTREE_PATH - Full worktree path.
  • HAPI_WORKTREE_CREATED_AT - Creation timestamp (ms).

Storage

Data is stored in ~/.hapi/ (or $HAPI_HOME):

  • settings.json - User settings (machineId, token, onboarding flag). See src/persistence.ts.
  • runner.state.json - Runner state (pid, port, version, heartbeat).
  • logs/ - Log files.

Requirements

  • Claude CLI installed and logged in (claude on PATH).
  • Cursor Agent CLI installed (agent on PATH) for hapi cursor. Install: curl https://cursor.com/install -fsS | bash (macOS/Linux), irm 'https://cursor.com/install?win32=true' | iex (Windows).
  • OpenCode CLI installed (opencode on PATH).
  • Bun for building from source.

Build from source

From the repo root:

bun install
bun run build:cli
bun run build:cli:exe

For an all-in-one binary that also embeds the web app:

bun run build:single-exe

Source structure

  • src/api/ - Bot communication (Socket.IO + REST).
  • src/claude/ - Claude Code integration.
  • src/codex/ - Codex mode integration.
  • src/cursor/ - Cursor Agent integration.
  • src/agent/ - Multi-agent support (Gemini via ACP).
  • src/opencode/ - OpenCode ACP + hook integration.
  • src/runner/ - Background service.
  • src/commands/ - CLI command handlers.
  • src/ui/ - User interface and diagnostics.
  • src/modules/ - Tool implementations (ripgrep, difftastic, git).
  • ../hub/README.md
  • ../web/README.md