/** * `h2a` CLI dispatcher — stable JSON output contract + exit-code table (DEC-034). * * Output shapes * ------------- * Every JSON-emitting verb writes ONE of three canonical envelopes on stdout: * * - **resource** — bare JSON of a single entity. Used by verbs that return the * persisted/loaded record itself (`negotiate open`, `negotiate status`, * `negotiate event`, `negotiate offer`, `negotiate counter`, `negotiate sign`, * `inbox pop`, `host setup --print`). * - **list** — bare JSON array. Used by `hosts`, `mcp-tools`, `discover`, * `inbox read`, `outbox read`, `negotiate journal`. * - **action** — `{ ok: true, ...details }` confirmation envelope. Used by * verbs that perform side effects without a natural entity to return * (`init`, `register`, `inbox put`, `outbox put`, `negotiate stabilize`, * `host setup --write`). * * Stderr lines always follow `h2a [sub]: ` so callers can * grep them deterministically. The `mcp-serve` verb is a long-running * JSON-RPC 2.0 stdio transport and does not fit the envelope contract. * * Exit codes * ---------- * * - `0` — success. * - `1` — user error: missing/bad flag, invalid JSON, validation failure on * caller-supplied data, unknown verb/subverb/host. * - `2` — runtime/state error: store conflict or business-rule violation * (negotiation not found, already open, already stabilized, signature * fails verification, quorum incomplete, broken journal, divergent * pre-existing config file refusing merge without `--force`). * - `3` — I/O / OS error: file unreadable, permission denied, write * refused by the filesystem. * * The full machine-readable manifest lives in `./cli-contract.ts` * (`H2A_CLI_VERB_CONTRACTS`). Human-readable reference: `docs/cli-contract.md`. */ import type { H2AWorkspaceRef } from "@sentropic/h2a"; import { type HostInstallationDoctorOptions, type HostInstallationDoctorReport } from "./hosts/installation-doctor.js"; import { type MirrorPushDaemonOptions } from "./runtime/mirror/index.js"; import { type H2ADrumbeatEntry } from "./runtime/drumbeat/index.js"; import { type H2ADriveInstructionPayload } from "./runtime/drive/index.js"; import { type UpgradeRuntime } from "./runtime/upgrade/index.js"; export interface H2ACliStreams { stderr: Pick; stdout: Pick; cwd?: () => string; stdinText?: string | (() => string); } /** Injectable only for embedders that need hermetic host-installation checks. */ export interface H2ACliOptions { readonly doctorHostInstallations?: (options: HostInstallationDoctorOptions) => HostInstallationDoctorReport; } export declare function renderCliHelp(): string; /** * `h2a mcp-serve` binds directly to the real process std streams because it * is a long-running JSON-RPC loop. The test-friendly `streams` interface * (write-only) cannot express a readable stdin; tests cover `runMcpStdio` * with `PassThrough` streams instead of going through this verb. */ /** * DEC-105 (EVO-6): resolve the auto-open session config from `mcp-serve` flags. * `--auto-open` enables it; the instance is `--instance ` or, if absent, * `:` (host = `--host` or "agent"). Pure + total so it can be * unit-tested without spawning the server. Returns undefined when not enabled. */ export declare function resolveAutoOpen(flags: Record, cwd: () => string): { instance: string; host?: string; workspace?: H2AWorkspaceRef; name?: string; scopes?: string[]; migrationNotice?: string; privateKeyPath?: string; /** * Re-reads the host-native display title on each heartbeat (spec * 2026-07-25-h2a-lane-addressing §D1b). Present only when the operator did * NOT pass `--name`: an explicit name is the operator's, and must never be * overwritten by a host rename. */ refreshDisplayName?: () => string | undefined; /** Only a locally-derived identity may attest an MCP delegation. */ delegationEligible?: true; } | undefined; /** * `h2a upgrade [--check]` (DEC-107, EVO-8 level 1): explicit self-upgrade. * `--check` reports current vs latest (no install); bare runs the global * install of `@latest`. Sync (spawnSync). `runtime` is injectable for tests. */ export declare function cmdUpgrade(flags: Record, streams: H2ACliStreams, runtime?: UpgradeRuntime): number; /** * `h2a presence-reap` — false-live janitor. Deletes presence files whose owning * process is provably dead: the stale "live" presence a host (Claude Code / * Codex) leaves when it drops the MCP stdio connection WITHOUT killing the * child, so the lingering process's blind heartbeat kept the presence fresh and * peers kept routing to an unreachable agent. Safe by construction — a dead * process owns no live session — and it never mints identity, so it is safe to * call from a SessionStart hook. Default reaps EVERY dead-pid presence * (host-wide; assumes a single-machine bus); `--instance ` scopes it to one * instance (cross-machine-safe). Prints the reaped set as JSON for audit. */ export declare function cmdPresenceReap(flags: Record, streams: H2ACliStreams): number; export declare function runMcpServe(flags: Record, io?: { stdin: NodeJS.ReadableStream; stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; /** Test seam for the internal structured-readiness environment. */ env?: NodeJS.ProcessEnv; /** Test seam for boot upgrade ordering; production uses the default runtime. */ upgradeRuntime?: UpgradeRuntime; /** Graceful-shutdown signal; bin.ts wires SIGTERM/SIGINT/SIGHUP to it. */ signal?: AbortSignal; }): Promise; /** * `h2a mcp-central-serve` — the opt-in, one-per-UID Streamable HTTP MCP * process. Unlike mcp-serve it has no stdio protocol: clients connect directly * to H2A_MCP_CENTRAL_ENDPOINT. The startup primitive performs every ownership, * liveness, and divergence check before this function reports readiness. */ export declare function runCentralMcpServe(flags: Record, io?: { stderr: NodeJS.WritableStream; cwd?: () => string; env?: NodeJS.ProcessEnv; signal?: AbortSignal; }): Promise; /** * `h2a mcp-central-connect` — stdio client shim for the private central MCP * server. The endpoint is public configuration, while the bearer token is read * from the owner-only marker only when this child process connects. */ export declare function runCentralMcpConnect(flags: Record, io?: { stdin: NodeJS.ReadableStream; stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; signal?: AbortSignal; }): Promise; /** * `h2a remote serve` (DEC-077): long-running HTTP listener that authenticates * POSTed envelopes against the store registry and delivers them to local * inboxes. Async + blocking, so it is dispatched from bin.ts (like mcp-serve), * not the synchronous runCli. Binds 127.0.0.1 by default — never expose to all * interfaces implicitly; pass `--host 0.0.0.0` to opt in. */ export declare function runRemoteServe(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; onListening?: (server: import("node:http").Server) => void; }): Promise; export interface RunDriveServeOptions { readonly inject?: (payload: H2ADriveInstructionPayload, signedLine: string) => boolean | Promise; } /** * `h2a drive serve` (EVO-1 E1d): long-running HTTP endpoint for remote/sidecar * injection. It verifies signature, target, authority, freshness, and replay * before crossing the remote trust boundary into the caller-provided injector. */ export declare function runDriveServe(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; onListening?: (server: import("node:http").Server) => void; }, options?: RunDriveServeOptions): Promise; /** * `h2a remote send` (DEC-077): sign an envelope and POST it to a remote h2a * endpoint. Async (network), so dispatched from bin.ts. Exit 0 on a 2xx, * 1 otherwise; prints `{ status, body }`. */ export declare function runRemoteSend(flags: Record, streams?: H2ACliStreams): Promise; /** * `h2a remote mirror-serve` (EVO-13 P1): long-running ingester that applies * signed instance mirrors to the store registry. Authority = an operator-enrolled * key or an already-registered key (never a self-declared id). Binds 127.0.0.1 * unless `--host 0.0.0.0`. Async + blocking → dispatched from bin.ts. */ export declare function runMirrorServe(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; onListening?: (server: import("node:http").Server) => void; }): Promise; /** * `h2a remote mirror` (EVO-13 P1): build the local instance's own registration * mirror, sign it with `--private-key`, and POST it to a remote ingester `--url`. * Exit 0 on a 2xx. Async (network) → dispatched from bin.ts. * * ONE-SHOT BY DEFAULT. Passing `--interval-ms ` — and only that — opts into * the live daemon (feed-contract P1 step 4a): the identical build → sign → POST * cycle, repeated on a monotonic beat with overlap prevention, transient-error * backoff, and a hard stop on repeated 401/403 (re-enrollment required). With no * `--interval-ms` the code path below is byte-identical to the pre-daemon one. * The global kill-switch `H2A_MIRROR_PUSH_OFF` disables the daemon entirely. */ export declare function runMirrorPush(flags: Record, streams?: H2ACliStreams, signal?: AbortSignal, overrides?: H2AMirrorPushOverrides): Promise; /** * Timing/transport seams for the live mirror push, so an integration test can * exercise the real CLI wiring at millisecond speed instead of sleeping through * real backoffs. Same intent as `sendImpl` on the drumbeat relaunchers. Never set * by any production caller — `bin.ts` passes nothing. */ export type H2AMirrorPushOverrides = Pick; /** * Async dispatcher for `h2a loop tick|watch` (SLICE-1: dry-run only — produces a * plan via the pure core + lazy adapters, executes nothing). Called from bin.ts * because these are async (lazy runtime import + periodic loop). `argv` is the * full bin argv: `["loop", "tick"|"watch", "", ...flags]`. */ export declare function runLoopEngineCli(argv: readonly string[], streams: H2ACliStreams, signal?: AbortSignal): Promise; /** * Async dispatcher for `h2a canevas serve` (read-only Hono server on 127.0.0.1). * Called from bin.ts (async + long-running). `argv` = full bin argv. */ export declare function runCanevasServeCli(argv: readonly string[], streams: H2ACliStreams, signal?: AbortSignal): Promise; /** * `h2a org` (EVO-7 slice 2, DEC-109): read-only tooling over the committed org * manifest (`org.h2a.yaml`). `validate` parses + checks the h2a invariants; * `show` prints the normalized manifest with its validation result. The coach's * propose/ratify lifecycle is `h2a coach`; live provisioning is a later slice. */ export declare function cmdOrg(argv: readonly string[], streams: H2ACliStreams): number; /** * `h2a coach` (EVO-7 slice 2, DEC-109): the coach **proposes, does not impose**. * `propose` emits the *unsigned* `org-proposal` envelope for a validated * manifest, signed (later) by the coach (a CONDUCTOR); affected agents may * counter and the owning PRINCIPAL then ratifies. Read-only here — signing, * persistence and provisioning are later slices. */ export declare function cmdCoach(argv: readonly string[], streams: H2ACliStreams): number; export declare function runDrumbeatRelanceInbox(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; }): Promise; /** * D4 resume messages can only act on a durable, non-terminal stop record. * Keeping this projection bounded prevents one huge historical registry from * starving every drumbeat tick before its anti-stall scan runs. */ export declare function drumbeatResumeInboxTargets(entries: readonly Pick[]): string[]; /** * `h2a drumbeat watch` (DEC-086): long-running anti-stall daemon. Async + * blocking, dispatched from bin.ts like mcp-serve. Uses the logging relauncher * by default; concrete relaunchers (local-tmux / remote) land in D3/D4. */ export declare function runDrumbeatWatch(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; signal?: AbortSignal; }): Promise; /** * `h2a sysml verify` (DEC-099, S3): verify an envelope's embedded SysML ref — * commit-trust (signature) by default; add `--content-integrity` to re-fetch + * re-hash the element (network). Async → dispatched from bin.ts like remote. */ export declare function runSysmlVerify(flags: Record, io?: { stdout: NodeJS.WritableStream; stderr: NodeJS.WritableStream; cwd?: () => string; }): Promise; /** * `h2a conductor-launch --workspace [--root] [--idle-ms ] [--confirm] [--remote ] [--instance ]` * * D3 EMISSION: when `conductorLaunchCheck` returns recommendation="launch", * h2a emits a launch-REQUEST envelope to a live remote agent. * * Gates: * 1. `conductorLaunchCheck` recommendation must be "launch". * 2. Cooldown: at most 1 request per 30 min per workspace (checked via spawns store). * 3. Human confirmation: WITHOUT `--confirm`, only PREVIEW the request (dry-run). * WITH `--confirm`, emit + record the marker. * * h2a NEVER spawns a process itself. It only puts a request envelope to remote. * The remote agent reads the envelope and executes the actual spawn. * * Exit 0 (success: none/cooldown/would-emit/no-remote/emitted). * Exit 1 (user error: missing --instance when --confirm given). */ /** * `h2a wake-request --to ` (WP-F, Codex reachability): emit a signed * `wake-request` envelope to a live remote/launcher agent so IT wakes the TARGET * agent's tmux pane out-of-band. * * Why: a Codex agent tears down its h2a stdio MCP child on transport drop and * never reconnects, so h2a deletes its presence and the in-process EVO-1 * self-wake can no longer fire — the agent is unreachable even though its pane * is alive. The launcher (remote) holds the target's DURABLE pane and performs * the actual wake; h2a never types into a pane here, so there is no stale-pane / * wrong-pane risk on the h2a side. Claude does NOT need this (its presence stays * live → the self-wake fires in-process). * * Flags: --to (required), --instance signer (required to emit), * --remote (override; else first live remote, pane-preferred), --dry-run, * --reason . */ export declare function cmdWakeRequest(argv: readonly string[], streams: H2ACliStreams): number; export declare function cmdConductorLaunch(argv: readonly string[], streams: H2ACliStreams, opts?: { /** Injectable check for tests (bypasses conductorLaunchCheck). */ injectedCheck?: import("./runtime/governance/launch-check.js").ConductorLaunchCheckResult; }): number; /** * Single-pass keepalive logic, testable without real tmux. * * For each presence file under `root`, if the session has a * `launchContext.tmux.pane` that is in `livePanes`, rewrite its `heartbeatAt` * to `now` so the session does not expire. */ export declare function keepaliveOnce(opts: { root: string; livePanes: Set; now?: Date; }): Array<{ instance: string; sessionId: string; pane: string; }>; /** * `h2a keepalive [--root ] [--interval ] [--once]` * * External keepalive prober: runs by the launcher/remote so a host-suspended * `mcp-serve` still shows live as long as its tmux pane is alive. `--once` * does a single pass then exits 0. Without `--once`, loops on an unref'd * interval (default 30 000 ms). */ export declare function cmdKeepalive(flags: Record, streams: H2ACliStreams): Promise; export declare const TRACK_FACADE_VERBS: Set; export declare const TRACK_NATIVE_READONLY_VERBS: Set; export declare const TRACK_NATIVE_WRITE_VERBS: Set; export declare const TRACK_NATIVE_VERBS: Set; /** * Consolidation ④-S2 — serve the read-only track MCP IN-PROCESS (native `h2a * track-mcp` verb). Reuses `@sentropic/track`'s shared `serveTrackMcpStdio`, * lazy-imported so the MCP stdio transport loads ONLY for this verb (not on every * h2a invocation). The store is resolved lazily by track per read call * (`--track-dir`→`TRACK_DIR`→nearest-ancestor `.track`). stdout stays pure * JSON-RPC (track logs to stderr); a real connect/transport failure → rc=1. */ export declare function runTrackMcpServe(flags: Record, io?: { stderr: NodeJS.WritableStream; cwd?: () => string; signal?: AbortSignal; }): Promise; export declare function runCli(argv?: readonly string[], streams?: H2ACliStreams, options?: H2ACliOptions): number; //# sourceMappingURL=cli.d.ts.map