@birdybeep/cli 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/framework.ts","../src/version.ts","../src/commands/agent.ts","../src/diagnostics.ts","../src/commands/doctor.ts","../src/config.ts","../src/commands/hook.ts","../src/commands/logout.ts","../src/commands/pair.ts","../src/pairing.ts","../src/commands/setup.ts","../src/commands/test.ts","../src/commands/queue.ts","../src/commands/report-status.ts","../src/commands/status.ts","../src/commands.ts","../src/update-check.ts","../src/cli.ts"],"sourcesContent":["/**\n * The CLI framework (§9.4): a small zero-dependency command dispatcher every `birdybeep`\n * command plugs into. Owns global flag parsing (`--json` / `--non-interactive` /\n * `--version` / `--help`), nested subcommand routing, help rendering, the config-dir\n * bootstrap, a json-aware output layer, and a shared exit-code convention. Network/auth,\n * adapter, and secret logic live in the individual commands — never here.\n *\n * Kept dependency-light on purpose: this code installs into developers' machines, so the\n * smaller + more auditable the surface, the better (§16.4).\n */\nimport { mkdirSync } from \"node:fs\";\n\nimport { birdyBeepConfigDir } from \"@birdybeep/agent-core\";\n\n/** Shared exit-code convention so callers (humans + agents) can branch on the result. */\nexport const EXIT = { OK: 0, ERROR: 1, USAGE: 2 } as const;\n\n/** A minimal output sink (process.stdout/stderr in prod; capturing buffers in tests). */\nexport interface Writer {\n write(s: string): void;\n}\n\nexport interface GlobalFlags {\n /** Machine-readable JSON output for agents/scripts. */\n json: boolean;\n /** Never prompt; fail fast (non-zero) when a required value is missing. */\n nonInteractive: boolean;\n help: boolean;\n version: boolean;\n}\n\n/** Json-aware output. `line`/`result` are mutually exclusive by mode so stdout stays clean. */\nexport interface Io {\n readonly json: boolean;\n /** Human line → stdout (suppressed in `--json` mode). */\n line(text: string): void;\n /** Always → stderr (errors/warnings show in both modes). */\n errline(text: string): void;\n /** Structured result → stdout as JSON (only in `--json` mode). */\n result(value: unknown): void;\n /** Emit the right one for the mode: human text, or the structured value as JSON. */\n emit(human: string, json: unknown): void;\n}\n\nexport function createIo(json: boolean, stdout: Writer, stderr: Writer): Io {\n return {\n json,\n line: (text) => {\n if (!json) stdout.write(`${text}\\n`);\n },\n errline: (text) => stderr.write(`${text}\\n`),\n result: (value) => {\n if (json) stdout.write(`${JSON.stringify(value)}\\n`);\n },\n emit: (human, value) => {\n if (json) stdout.write(`${JSON.stringify(value)}\\n`);\n else stdout.write(`${human}\\n`);\n },\n };\n}\n\nexport interface CommandContext {\n /** Positional args after the resolved command path. */\n args: string[];\n flags: GlobalFlags;\n io: Io;\n}\n\n/** A per-command flag: its accepted spellings, optional value placeholder, and help text. */\nexport interface CommandOption {\n /** Primary flag token, e.g. `\"--expect-email\"`. */\n flag: string;\n /** Extra accepted spellings, e.g. `[\"-y\"]`. */\n aliases?: readonly string[];\n /** Value placeholder shown in help (e.g. `\"<addr>\"`); omit for boolean flags. */\n value?: string;\n summary: string;\n}\n\nexport interface Command {\n name: string;\n summary: string;\n /** One-line usage shown in the command's own `--help`. */\n usage?: string;\n /**\n * When set, this command is featured in a \"Getting started\" block ABOVE the command list in\n * the root help, described by this line (birdybeep-agent-gcgp.5). `--help` listed ten commands\n * in registry order with nothing saying which one a new user runs first, so the verb that sets\n * the product up was exactly as discoverable as `report-status`. The framework stays\n * command-independent: it renders whatever the registry marks, and knows no command by name.\n */\n gettingStarted?: string;\n /** Nested subcommands (e.g. `agent install` / `agent uninstall`). */\n subcommands?: Command[];\n /**\n * Flags this command accepts IN ADDITION to the global ones. Without this allowlist the\n * dispatcher rejects every non-global flag as an unknown option, so a command that owns\n * flags must declare them here — and declaring them also documents them in `--help`.\n * Both `--flag value` and `--flag=value` are accepted; parsing stays the command's job.\n */\n options?: readonly CommandOption[];\n /** Command logic; returns the intended exit code. Absent for pure command groups. */\n run?(ctx: CommandContext): Promise<number> | number;\n}\n\n/** Thrown by a command when a required value is missing under `--non-interactive`. */\nexport class MissingInputError extends Error {\n constructor(readonly field: string) {\n super(`missing required value: ${field}`);\n this.name = \"MissingInputError\";\n }\n}\n\n/**\n * Resolve a value that may require interaction. Returns `provided` when present; otherwise\n * throws {@link MissingInputError} under `--non-interactive` (so the CLI fails fast instead\n * of hanging), or returns undefined for the caller to prompt in interactive mode.\n */\nexport function requireValue<T>(ctx: CommandContext, field: string, provided: T | undefined): T {\n if (provided !== undefined) return provided;\n if (ctx.flags.nonInteractive) throw new MissingInputError(field);\n throw new MissingInputError(field); // interactive prompting is a per-command concern; default fail-fast\n}\n\nconst GLOBAL_FLAG_TOKENS = new Set([\n \"--json\",\n \"--non-interactive\",\n \"--version\",\n \"-v\",\n \"--help\",\n \"-h\",\n]);\n\n/** Split a raw argv into global flags + the remaining (command path + positional) tokens. */\nexport function parseGlobalFlags(argv: string[]): { flags: GlobalFlags; rest: string[] } {\n const flags: GlobalFlags = { json: false, nonInteractive: false, help: false, version: false };\n const rest: string[] = [];\n for (const token of argv) {\n switch (token) {\n case \"--json\":\n flags.json = true;\n break;\n case \"--non-interactive\":\n flags.nonInteractive = true;\n break;\n case \"--version\":\n case \"-v\":\n flags.version = true;\n break;\n case \"--help\":\n case \"-h\":\n flags.help = true;\n break;\n default:\n rest.push(token);\n }\n }\n return { flags, rest };\n}\n\n/**\n * Is `token` an unknown long/short flag (after global flags were stripped)? `allowed` carries\n * the resolved command's own {@link Command.options}.\n *\n * The GLOBAL check is EXACT, deliberately: {@link parseGlobalFlags} consumes global flags by\n * exact token, so `--json=true` is NOT a global flag — it stays in the command's args. If this\n * guard split on `=` before the global lookup, `--json=true` / `--non-interactive=1` / `-v=2`\n * would sail past it, never be consumed, and silently run the command in the WRONG MODE with\n * the token landing in the positional args. They must be rejected as usage errors instead.\n * Only a COMMAND's own options accept the `--flag=value` spelling (see {@link Command.options}).\n */\nfunction isUnknownFlag(token: string, allowed: ReadonlySet<string>): boolean {\n if (!token.startsWith(\"-\")) return false;\n if (GLOBAL_FLAG_TOKENS.has(token)) return false; // exact match only — mirrors parseGlobalFlags\n const eq = token.indexOf(\"=\");\n return !allowed.has(eq >= 0 ? token.slice(0, eq) : token);\n}\n\nfunction renderRootHelp(version: string, commands: Command[]): string {\n const width = Math.max(...commands.map((c) => c.name.length));\n const lines = commands.map((c) => ` ${c.name.padEnd(width)} ${c.summary}`);\n const featured = commands.filter((c) => c.gettingStarted !== undefined);\n return [\n `birdybeep ${version}: phone alerts for coding agents.`,\n \"\",\n \"Usage:\",\n \" birdybeep <command> [options]\",\n ...(featured.length > 0\n ? [\n \"\",\n \"Getting started:\",\n ...featured.map((c) => ` birdybeep ${c.name} ${c.gettingStarted ?? \"\"}`),\n ]\n : []),\n \"\",\n \"Commands:\",\n ...lines,\n \"\",\n \"Global options:\",\n \" --json Machine-readable JSON output\",\n \" --non-interactive Never prompt; fail fast if input is required\",\n \" -h, --help Show help (root or per-command)\",\n \" -v, --version Show the CLI version\",\n ].join(\"\\n\");\n}\n\n/**\n * Every flag token (primary + aliases) the resolved command accepts beyond the global ones.\n * A subcommand inherits its PARENT group's options too (`...commands` unions them in), so a\n * flag declared once on a group works on every leaf under it rather than being rejected there.\n */\nfunction commandFlagTokens(...commands: (Command | undefined)[]): ReadonlySet<string> {\n const tokens = new Set<string>();\n for (const command of commands) {\n for (const option of command?.options ?? []) {\n tokens.add(option.flag);\n for (const alias of option.aliases ?? []) tokens.add(alias);\n }\n }\n return tokens;\n}\n\nfunction renderCommandHelp(path: string, command: Command): string {\n const lines = [\n `birdybeep ${path}: ${command.summary}`,\n \"\",\n \"Usage:\",\n ` ${command.usage ?? `birdybeep ${path} [options]`}`,\n ];\n if (command.options && command.options.length > 0) {\n const labels = command.options.map(\n (o) => `${[o.flag, ...(o.aliases ?? [])].join(\", \")}${o.value ? ` ${o.value}` : \"\"}`,\n );\n const width = Math.max(...labels.map((l) => l.length));\n lines.push(\n \"\",\n \"Options:\",\n ...command.options.map((o, i) => ` ${labels[i]?.padEnd(width)} ${o.summary}`),\n );\n }\n if (command.subcommands && command.subcommands.length > 0) {\n const width = Math.max(...command.subcommands.map((c) => c.name.length));\n lines.push(\n \"\",\n \"Subcommands:\",\n ...command.subcommands.map((c) => ` ${c.name.padEnd(width)} ${c.summary}`),\n );\n }\n return lines.join(\"\\n\");\n}\n\nexport interface DispatchDeps {\n version: string;\n commands: Command[];\n stdout: Writer;\n stderr: Writer;\n /** Skip the config-dir bootstrap (tests that don't want filesystem side effects). */\n ensureConfig?: boolean;\n /**\n * Optional post-command update notifier, invoked after a command runs successfully (not for\n * help/version). The framework only invokes it — all registry/cache/semver logic lives in the\n * CLI layer (`update-check.ts`), never here — and its failure never affects the command result.\n */\n notifyUpdate?: (ctx: { command: string; flags: GlobalFlags; io: Io }) => Promise<void>;\n}\n\n/**\n * Run the CLI against an argv slice (without `node`/script path). Resolves the command\n * (with nested subcommands), handles `--help`/`--version`, and returns the exit code.\n * Never throws — command errors become a stderr message + {@link EXIT.ERROR}.\n */\nexport async function dispatch(argv: string[], deps: DispatchDeps): Promise<number> {\n const { flags, rest } = parseGlobalFlags(argv);\n const io = createIo(flags.json, deps.stdout, deps.stderr);\n\n // Config dir is created on first run (non-secret CLI config only — never a token).\n if (deps.ensureConfig !== false) {\n try {\n mkdirSync(birdyBeepConfigDir(), { recursive: true, mode: 0o700 });\n } catch {\n /* non-fatal: a read-only config dir is surfaced by `doctor`, not here */\n }\n }\n\n if (flags.version) {\n io.emit(deps.version, { version: deps.version });\n return EXIT.OK;\n }\n\n // Resolve the command path (supports one level of nested subcommands).\n let command: Command | undefined = deps.commands.find((c) => c.name === rest[0]);\n /** The group a resolved subcommand sits under (undefined for a top-level command). */\n let parent: Command | undefined;\n const pathParts: string[] = [];\n let argsStart = 1;\n if (command) {\n pathParts.push(command.name);\n if (command.subcommands && command.subcommands.length > 0) {\n const sub = command.subcommands.find((c) => c.name === rest[1]);\n if (sub) {\n parent = command;\n command = sub;\n pathParts.push(sub.name);\n argsStart = 2;\n }\n }\n }\n\n if (rest.length === 0 || (flags.help && command === undefined)) {\n io.emit(renderRootHelp(deps.version, deps.commands), {\n version: deps.version,\n commands: deps.commands.map((c) => ({\n name: c.name,\n summary: c.summary,\n ...(c.gettingStarted !== undefined ? { gettingStarted: c.gettingStarted } : {}),\n })),\n });\n return EXIT.OK;\n }\n\n if (command === undefined) {\n io.errline(`birdybeep: unknown command \"${rest[0]}\". Run \\`birdybeep --help\\`.`);\n return EXIT.USAGE;\n }\n\n const path = pathParts.join(\" \");\n if (flags.help) {\n io.emit(renderCommandHelp(path, command), {\n name: path,\n summary: command.summary,\n usage: command.usage,\n options: command.options,\n subcommands: command.subcommands?.map((c) => ({ name: c.name, summary: c.summary })),\n });\n return EXIT.OK;\n }\n\n if (command.run === undefined) {\n // A pure command group invoked without a subcommand → show its help as a usage error.\n io.errline(renderCommandHelp(path, command));\n return EXIT.USAGE;\n }\n\n const args = rest.slice(argsStart);\n const allowed = commandFlagTokens(command, parent);\n const unknown = args.find((token) => isUnknownFlag(token, allowed));\n if (unknown !== undefined) {\n io.errline(`birdybeep ${path}: unknown option \"${unknown}\".`);\n return EXIT.USAGE;\n }\n\n let code: number;\n try {\n code = await command.run({ args, flags, io });\n } catch (err) {\n if (err instanceof MissingInputError) {\n io.errline(\n `birdybeep ${path}: ${err.message} (re-run without --non-interactive to be prompted).`,\n );\n return EXIT.USAGE;\n }\n io.errline(`birdybeep ${path}: ${err instanceof Error ? err.message : String(err)}`);\n return EXIT.ERROR;\n }\n\n // Opportunistic, best-effort update notice (never alters the command's exit code or stdout).\n if (deps.notifyUpdate !== undefined) {\n try {\n await deps.notifyUpdate({ command: pathParts[0] ?? \"\", flags, io });\n } catch {\n /* the notifier is best-effort; a failure must not affect the command result */\n }\n }\n return code;\n}\n","/**\n * CLI version — single-sourced from package.json at build time (s0o7). `tsup.config.ts`\n * injects the real `@birdybeep/cli` version via the `__CLI_VERSION__` esbuild define, so\n * the shipped binary reports its true version for `--version` and the `cli_version` it\n * sends on `/pair/start` (the mobile approval sheet's machine identity). The `0.0.0`\n * fallback only applies to non-bundled runs (vitest / tsx), where the define is absent.\n */\n\n/** Build-time-replaced global; declared so source typechecks before tsup substitutes it. */\ndeclare const __CLI_VERSION__: string | undefined;\n\nexport const CLI_VERSION: string =\n typeof __CLI_VERSION__ === \"string\" && __CLI_VERSION__.length > 0 ? __CLI_VERSION__ : \"0.0.0\";\n","/**\n * `birdybeep agent install|uninstall [all|claude|codex|opencode|cursor|copilot]` (§7.3, §9.4) — the\n * once-per-machine setup half: detect supported harnesses and run each adapter's\n * idempotent, non-destructive install/uninstall. Adds ONLY BirdyBeep-managed entries\n * (existing config backed up + preserved), the installed config invokes\n * `birdybeep hook <harness>`, and NO durable token is ever written into harness/repo\n * config — the hook reads the token from the secure store at runtime. Prints the changed\n * files + any required user action (Codex `/hooks` trust, OpenCode restart).\n *\n * Built as a factory with an injectable adapter set so tests exercise the REAL adapter\n * installs under a temp HOME with deterministic detection.\n */\nimport type { AgentAdapter, InstallResult, TokenStoreOptions } from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { copilotAdapter } from \"@birdybeep/copilot\";\nimport { cursorAdapter } from \"@birdybeep/cursor\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { isPaired } from \"../diagnostics\";\nimport { type Command, type CommandContext, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n codexAdapter,\n opencodeAdapter,\n cursorAdapter,\n copilotAdapter,\n];\n\n/** CLI short target name → adapter id (the CLI says `claude`, the adapter id is `claude_code`). */\nconst TARGET_TO_ID: Record<string, string> = {\n claude: \"claude_code\",\n codex: \"codex\",\n opencode: \"opencode\",\n cursor: \"cursor\",\n copilot: \"copilot\",\n};\n\nexport const AGENT_TARGETS: readonly string[] = [\n \"all\",\n \"claude\",\n \"codex\",\n \"opencode\",\n \"cursor\",\n \"copilot\",\n];\n\n/** Resolve a target to the adapter(s) it names, or `\"unknown\"` for a bad target. */\nexport function selectAdapters(\n target: string,\n adapters: AgentAdapter[],\n): AgentAdapter[] | \"unknown\" {\n if (target === \"all\") return adapters;\n const id = TARGET_TO_ID[target];\n if (id === undefined) return \"unknown\";\n return adapters.filter((a) => a.id === id);\n}\n\ninterface InstallOutcome {\n harness: string;\n displayName: string;\n detected: boolean;\n status?: InstallResult[\"status\"];\n changedFiles?: string[];\n backupFiles?: string[];\n requiredActions?: string[];\n}\n\n/** CLI install target for an adapter id (the CLI says `claude`, the adapter id is `claude_code`). */\nexport function installTarget(harness: string): string {\n return harness === \"claude_code\" ? \"claude\" : harness;\n}\n\nasync function installSelected(\n adapters: AgentAdapter[],\n ctx: CommandContext,\n tokenOptions: TokenStoreOptions,\n): Promise<number> {\n const target = ctx.args[0] ?? \"all\";\n const selected = selectAdapters(target, adapters);\n if (selected === \"unknown\") {\n ctx.io.errline(\n `birdybeep agent install: unknown target \"${target}\" (expected ${AGENT_TARGETS.join(\"|\")}).`,\n );\n return EXIT.USAGE;\n }\n\n const outcomes: InstallOutcome[] = [];\n for (const adapter of selected) {\n const detection = await adapter.detect();\n if (!detection.detected) {\n outcomes.push({ harness: adapter.id, displayName: adapter.displayName, detected: false });\n continue;\n }\n const result = await adapter.install();\n outcomes.push({\n harness: adapter.id,\n displayName: adapter.displayName,\n detected: true,\n status: result.status,\n changedFiles: result.changedFiles,\n backupFiles: result.backupFiles,\n requiredActions: result.requiredActions,\n });\n }\n\n // gcgp.5: installing adapters on an unpaired machine wires up hooks that have nowhere to send.\n // `agent install` never mentioned pairing, so the two halves of setup were each silent about\n // the other. Read once, reported at the end where the user is already looking for next steps.\n const paired = await isPaired(tokenOptions);\n\n if (ctx.flags.json) {\n ctx.io.result({ target, paired, results: outcomes });\n return EXIT.OK;\n }\n\n if (outcomes.length === 0 || outcomes.every((o) => !o.detected)) {\n ctx.io.line(\"No supported harnesses detected. Nothing was installed.\");\n }\n for (const o of outcomes) {\n if (!o.detected) {\n // A skip used to be a dead end: no hint that installing the harness and re-running would\n // finish the job, and nothing recorded so a later run picks it up.\n ctx.io.line(\n `– ${o.displayName}: not detected (skipped). Install it, then run \\`birdybeep agent install ${installTarget(o.harness)}\\`.`,\n );\n continue;\n }\n const changed = (o.changedFiles ?? []).length > 0 ? o.changedFiles!.join(\", \") : \"no changes\";\n ctx.io.line(`✓ ${o.displayName}: ${o.status} (${changed})`);\n for (const action of o.requiredActions ?? []) ctx.io.line(` → ${action}`);\n }\n if (!paired) {\n ctx.io.line(\n \"⚠ This machine is not paired. Run `birdybeep setup` before expecting notifications.\",\n );\n }\n return EXIT.OK;\n}\n\ninterface UninstallOutcome {\n harness: string;\n displayName: string;\n changed: boolean;\n removedFiles: string[];\n restoredFiles: string[];\n}\n\nasync function uninstallSelected(adapters: AgentAdapter[], ctx: CommandContext): Promise<number> {\n const target = ctx.args[0] ?? \"all\";\n const selected = selectAdapters(target, adapters);\n if (selected === \"unknown\") {\n ctx.io.errline(\n `birdybeep agent uninstall: unknown target \"${target}\" (expected ${AGENT_TARGETS.join(\"|\")}).`,\n );\n return EXIT.USAGE;\n }\n\n const outcomes: UninstallOutcome[] = [];\n for (const adapter of selected) {\n // Uninstall is safe + idempotent even if nothing is installed (a no-op).\n const result = await adapter.uninstall();\n outcomes.push({\n harness: adapter.id,\n displayName: adapter.displayName,\n changed: result.changed,\n removedFiles: result.removedFiles,\n restoredFiles: result.restoredFiles,\n });\n }\n\n if (ctx.flags.json) {\n ctx.io.result({ target, results: outcomes });\n return EXIT.OK;\n }\n for (const o of outcomes) {\n if (!o.changed) {\n ctx.io.line(`– ${o.displayName}: nothing to remove`);\n continue;\n }\n const touched = [...o.removedFiles, ...o.restoredFiles].join(\", \") || \"config restored\";\n ctx.io.line(`✓ ${o.displayName}: removed (${touched})`);\n }\n return EXIT.OK;\n}\n\nexport interface AgentCommandDeps {\n /** Adapter set (tests inject deterministic detection). Defaults to all supported adapters. */\n adapters?: AgentAdapter[];\n /** Token-store options for the pairing check (tests inject the file fallback). */\n tokenOptions?: TokenStoreOptions;\n}\n\n/** Build the `agent` command group (install + uninstall, both via the adapter contract). */\nexport function createAgentCommand(deps: AgentCommandDeps = {}): Command {\n const adapters = deps.adapters ?? DEFAULT_ADAPTERS;\n const tokenOptions = deps.tokenOptions ?? {};\n return {\n name: \"agent\",\n summary: \"Install or uninstall harness adapters\",\n usage: \"birdybeep agent <install|uninstall> [all|claude|codex|opencode|cursor|copilot]\",\n subcommands: [\n {\n name: \"install\",\n summary: \"Install adapters (all | claude | codex | opencode | cursor | copilot)\",\n usage: \"birdybeep agent install [all|claude|codex|opencode|cursor|copilot]\",\n run: (ctx) => installSelected(adapters, ctx, tokenOptions),\n },\n {\n name: \"uninstall\",\n summary: \"Restore harness config to its pre-install state\",\n usage: \"birdybeep agent uninstall [all|claude|codex|opencode|cursor|copilot]\",\n run: (ctx) => uninstallSelected(adapters, ctx),\n },\n ],\n };\n}\n","/**\n * Shared status/queue plumbing used by `birdybeep status` and `birdybeep doctor`: gather\n * each adapter's integration status, the machine identity + pairing state, and local queue\n * depth. Read-only + privacy-safe — never prints token material or notification bodies.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nimport {\n type AgentAdapter,\n type DetectionResult,\n type FilteredActivity,\n getMachineIdentity,\n getToken,\n type HarnessObservation,\n type HarnessSurface,\n type IntegrationStatus,\n LocalEventQueue,\n type ObservedBuildsOptions,\n readFilteredActivity,\n readObservedBuilds,\n readToken,\n readUnpairedNotice,\n type TokenStoreKind,\n type TokenStoreOptions,\n type UnpairedNotice,\n} from \"@birdybeep/agent-core\";\nimport {\n BIRDYBEEP_HOOK_EVENTS as CLAUDE_HOOK_EVENTS,\n claudeSettingsPath,\n isBirdyBeepEntry as isClaudeEntry,\n} from \"@birdybeep/claude-code\";\nimport {\n BIRDYBEEP_HOOK_EVENTS as CURSOR_HOOK_EVENTS,\n cursorHooksPath,\n detectCursor,\n isBirdyBeepEntry as isCursorEntry,\n} from \"@birdybeep/cursor\";\n\nexport interface IntegrationState {\n harness: string;\n displayName: string;\n status: IntegrationStatus;\n}\n\n/** Each adapter's current §8.8 integration status (runs the real adapter.status()). */\nexport async function gatherIntegrations(adapters: AgentAdapter[]): Promise<IntegrationState[]> {\n return Promise.all(\n adapters.map(async (a) => ({\n harness: a.id,\n displayName: a.displayName,\n status: await a.status(),\n })),\n );\n}\n\n/** Is a machine token present in the secure store? (pairing state — never prints the token.) */\nexport async function isPaired(tokenOptions: TokenStoreOptions = {}): Promise<boolean> {\n return (await getToken(tokenOptions)) !== null;\n}\n\n/**\n * The three answers `status` and `doctor` can give about pairing (birdybeep-agent-gcgp.23).\n * `unknown` is not a shade of `unpaired`: the store failed, so this machine may well BE paired,\n * and telling that user \"not paired\" is a wrong diagnosis rather than a vague one.\n */\nexport type PairingState = \"paired\" | \"unpaired\" | \"unknown\";\n\nexport interface PairingReport {\n state: PairingState;\n /** Why the store could not answer. Set only when `state` is `unknown`; never token material. */\n reason?: string;\n /** Which store could not answer. Set only when `state` is `unknown`; picks the remedy. */\n store?: TokenStoreKind;\n}\n\n/** Read the pairing state, distinguishing \"no token\" from \"the token store would not answer\". */\nexport async function pairingReport(tokenOptions: TokenStoreOptions = {}): Promise<PairingReport> {\n const lookup = await readToken(tokenOptions);\n if (lookup.state === \"present\") return { state: \"paired\" };\n if (lookup.state === \"absent\") return { state: \"unpaired\" };\n return { state: \"unknown\", reason: lookup.reason, store: lookup.store };\n}\n\n/**\n * One line for a token store that will not answer. It carries the three facts the \"not paired\"\n * line cannot: this says nothing about whether you are paired, events are being QUEUED rather\n * than lost, and it resolves as soon as the store is readable.\n */\nexport function describeTokenStoreUnavailable(report: PairingReport): string {\n return (\n `Could not read the token store (${report.reason ?? \"unknown error\"}), so whether this ` +\n \"machine is paired is unknown. Events fired now are QUEUED, not lost, and send once it \" +\n \"is readable.\"\n );\n}\n\n/**\n * What to do about a token store that will not answer — which depends on WHICH store it was.\n * The keychain case is a lock to open. The file case (Linux, Windows, headless) is a path or a\n * permission to repair: unlocking nothing helps, and `birdybeep pair` writes the same bad path,\n * so telling the user to run it again is advice that cannot work.\n */\nexport function tokenStoreRemedy(report: PairingReport): string {\n if (report.store === \"file\") {\n return (\n \"Repair the token file — check that its directory and the file itself are readable and \" +\n \"writable by you (`chmod 700` the directory, `chmod 600` the file), then run \" +\n \"`birdybeep doctor` again to drain the queue.\"\n );\n }\n return (\n \"Unlock your login keychain (log in to the desktop session, or unlock the screen), then \" +\n \"run `birdybeep doctor` again to drain the queue. If it stays unreadable, run \" +\n \"`birdybeep pair`.\"\n );\n}\n\n/** Current local event-queue depth (fresh, non-expired entries). */\nexport function localQueueDepth(): number {\n return new LocalEventQueue().size();\n}\n\n/** How many events the queue's count cap has dropped on this machine (gcgp.4). */\nexport function localQueueOverflowDrops(): number {\n return new LocalEventQueue().overflowDropCount();\n}\n\n/**\n * Events that fired while this machine had no token, and were therefore never sent (gcgp.4).\n * `null` once the machine is paired — `pair` clears the notice.\n */\nexport function unpairedActivity(): UnpairedNotice | null {\n return readUnpairedNotice();\n}\n\n/** One line describing an unpaired-activity notice, for `status` / `doctor`. */\nexport function describeUnpairedActivity(notice: UnpairedNotice): string {\n const since = new Date(notice.firstAt).toISOString();\n const from = notice.harnesses.length > 0 ? ` from ${notice.harnesses.join(\", \")}` : \"\";\n return `${notice.count} event(s)${from} fired since ${since} and were NOT sent — this machine is not paired.`;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * How many of `events` carry a BirdyBeep-managed entry in a harness hooks config.\n * A missing file is 0; a file that cannot be parsed is `null` — a corrupt config is a\n * different failure, with its own check, and says nothing about what is installed.\n */\nfunction birdyBeepHookCount(\n path: string,\n events: readonly string[],\n isBirdyBeepEntry: (entry: unknown) => boolean,\n): number | null {\n if (!existsSync(path)) return 0;\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return null;\n }\n const hooks = asRecord(asRecord(parsed)[\"hooks\"]);\n let present = 0;\n for (const event of events) {\n const entries = hooks[event];\n if (Array.isArray(entries) && entries.some(isBirdyBeepEntry)) present += 1;\n }\n return present;\n}\n\nexport interface CursorBridgeOptions {\n /** Override the home dir (default `os.homedir()`, which honors `$HOME`). */\n home?: string;\n /** Injectable Cursor detection for tests (avoids shelling out to `cursor-agent --version`). */\n detect?: () => Promise<DetectionResult>;\n}\n\n/**\n * Is Cursor reaching BirdyBeep ONLY through its Claude Code compatibility bridge (gcgp.13)?\n * True when Cursor is present, our Claude hooks are installed (the bridge reads\n * `~/.claude/settings.json` and runs them), and `~/.cursor/hooks.json` carries none of ours.\n * That machine gets lifecycle events attributed to Cursor but no approvals — the bridge drops\n * `Notification` and `PermissionRequest`. Read-only; cross-adapter, so it belongs to neither\n * adapter's own doctor(). False once the Cursor adapter is installed.\n */\nexport async function cursorBridgeOnly(opts: CursorBridgeOptions = {}): Promise<boolean> {\n const home = opts.home ?? homedir();\n const detection = await (opts.detect ?? (() => detectCursor({ home })))();\n if (!detection.detected) return false;\n const claude = birdyBeepHookCount(claudeSettingsPath(home), CLAUDE_HOOK_EVENTS, isClaudeEntry);\n if (claude === null || claude === 0) return false;\n return birdyBeepHookCount(cursorHooksPath(home), CURSOR_HOOK_EVENTS, isCursorEntry) === 0;\n}\n\n/**\n * Events the hook pipeline handled locally and never sent, because the backend can never\n * push their type (gcgp.3). `null` when nothing has been filtered on this machine.\n */\nexport function filteredActivity(): FilteredActivity | null {\n return readFilteredActivity();\n}\n\n/**\n * One line describing locally-filtered activity, for `status` / `doctor`. This is the\n * \"your hooks ARE firing\" evidence — after gcgp.3 the highest-volume proof of a working\n * install (Codex `PostToolUse`) never reaches the backend, so it has to be reported here.\n */\nexport function describeFilteredActivity(activity: FilteredActivity): string {\n const types = Object.entries(activity.byType)\n .sort(([, a], [, b]) => b - a)\n .map(([type, n]) => `${type} ×${n}`)\n .join(\", \");\n const since = new Date(activity.firstAt).toISOString();\n return `${activity.count} local-only event(s) since ${since}${types ? ` (${types})` : \"\"}. Hooks are firing; these types never produce notifications, so they are not sent.`;\n}\n\n/** Machine label + OS (the event `machine` identity). */\nexport function machineIdentity(): { label: string; os: string } {\n return getMachineIdentity();\n}\n\n/**\n * Per-SURFACE coverage (birdybeep-agent-gcgp.6).\n *\n * active — this build has fired BirdyBeep's hook.\n * wired — the harness config carries our entries, but nothing has come from this build yet.\n * uncovered — this build cannot beep: either the harness has no BirdyBeep entries at all, or it\n * has them and every OTHER build of the same harness is delivering while this one\n * never has.\n */\nexport type SurfaceCoverage = \"active\" | \"wired\" | \"uncovered\";\n\nexport interface SurfaceState {\n surface: HarnessSurface;\n coverage: SurfaceCoverage;\n /** Events observed from this build. */\n events: number;\n /** Epoch ms of the most recent one. */\n lastAt?: number;\n /**\n * The build this surface was seen running as, when the filesystem could not say. Set only for\n * a surface whose `version` is unknown and which an observed build could be attributed to.\n */\n observedVersion?: string;\n}\n\nexport interface HarnessSurfaces {\n harness: string;\n displayName: string;\n /** The harness's own §8.8 status — one fact about the shared config, for every surface. */\n status: IntegrationStatus;\n surfaces: SurfaceState[];\n /** Events from this harness that named no build, so they belong to no row. */\n unversionedEvents: number;\n}\n\n/** Statuses that mean BirdyBeep's entries ARE in the harness config (trust/restart still pending). */\nconst CONFIGURED_STATUSES: ReadonlySet<IntegrationStatus> = new Set<IntegrationStatus>([\n \"installed\",\n \"needs_trust\",\n \"needs_restart\",\n]);\n\n/**\n * Attribute observed builds to surfaces and grade each one.\n *\n * Matching is by (SURFACE KIND, VERSION), never version alone. Two release channels can ship the\n * same version, and a version the terminal CLI has since upgraded away from is not evidence about\n * a desktop build — keying on version alone made both of those report a build that had never run\n * the hook as covered.\n *\n * Three ways a surface can be matched, in descending order of certainty:\n * 1. exact — an observation of this surface's kind AND version;\n * 2. sole-of-kind — a surface whose version cannot be read off disk (the ChatGPT-bundled Codex)\n * claims an unclaimed observation OF ITS OWN KIND, and only when that is unambiguous;\n * 3. unattributed — an observation whose surface the harness never named (Cursor says nothing;\n * so does a tally written before this key existed). It counts as evidence only when exactly\n * one surface carries its version. When two do, it settles nothing, and it SUPPRESSES the\n * uncovered verdict for them rather than picking a row — under-claiming is the safe\n * direction, the same call the Codex trust marker makes.\n */\nfunction gradeSurfaces(\n surfaces: HarnessSurface[],\n status: IntegrationStatus,\n observation: HarnessObservation | undefined,\n): SurfaceState[] {\n const builds = Object.values(observation?.builds ?? {});\n const configured = CONFIGURED_STATUSES.has(status);\n\n const claimedByKind = new Map<string, Set<string>>();\n for (const s of surfaces) {\n if (s.version === undefined) continue;\n const versions = claimedByKind.get(s.kind) ?? new Set<string>();\n versions.add(s.version);\n claimedByKind.set(s.kind, versions);\n }\n\n const graded = surfaces.map((surface) => {\n const exact = builds.filter(\n (b) =>\n b.surface === surface.kind &&\n b.version === surface.version &&\n surface.version !== undefined,\n );\n\n // (2) sole-of-kind, scoped to this surface's own kind so a terminal build's retired version\n // can never be adopted by a desktop row.\n let soleOfKind: typeof builds = [];\n if (surface.version === undefined) {\n const sameKindVersionless = surfaces.filter(\n (s) => s.version === undefined && s.kind === surface.kind,\n );\n const unclaimed = builds.filter(\n (b) =>\n b.surface === surface.kind && !(claimedByKind.get(surface.kind)?.has(b.version) ?? false),\n );\n if (unclaimed.length === 1 && sameKindVersionless.length === 1) soleOfKind = unclaimed;\n }\n\n // (3) unattributed observations that carry this surface's version.\n const unattributed =\n surface.version === undefined\n ? []\n : builds.filter((b) => b.surface === \"unknown\" && b.version === surface.version);\n const sharesVersion =\n surface.version !== undefined &&\n surfaces.some((s) => s !== surface && s.version === surface.version);\n const ambiguous = unattributed.length > 0 && sharesVersion;\n\n const matched = [...exact, ...soleOfKind, ...(ambiguous ? [] : unattributed)];\n const events = matched.reduce((total, b) => total + b.count, 0);\n const lastAt = matched.reduce<number | undefined>(\n (latest, b) => (latest === undefined || b.lastAt > latest ? b.lastAt : latest),\n undefined,\n );\n const observedVersion = surface.version === undefined ? soleOfKind[0]?.version : undefined;\n\n return {\n surface,\n events,\n ambiguous,\n ...(lastAt !== undefined ? { lastAt } : {}),\n ...(observedVersion !== undefined ? { observedVersion } : {}),\n };\n });\n\n // \"Uncovered\" needs a comparison, not an absolute: on a machine where nothing has fired yet,\n // every build is equally unproven and none of them is a fault. It becomes a real, actionable\n // gap only once a SIBLING build of the same harness is delivering and this one still is not.\n //\n // A SHADOWED install is exempt from that comparison in both directions: it sits behind another\n // one on PATH, so it is expected never to fire, and calling that a fault would tell the user to\n // go fix a build they cannot even run.\n const anyActive = graded.some((g) => g.events > 0 && g.surface.shadowed !== true);\n return graded.map(({ ambiguous, ...g }) => ({\n ...g,\n coverage: !configured\n ? (\"uncovered\" as const)\n : g.events > 0\n ? (\"active\" as const)\n : anyActive && g.surface.shadowed !== true && !ambiguous\n ? (\"uncovered\" as const)\n : (\"wired\" as const),\n }));\n}\n\nexport interface SurfaceCoverageOptions {\n /** Override the observed-builds tally path (tests). */\n observedBuilds?: ObservedBuildsOptions;\n}\n\n/**\n * Every harness's installed builds, graded. Runs the real `adapter.detect()` (which enumerates\n * surfaces) and `adapter.status()`, and reads the local observed-builds tally — no network, no\n * spawning of any engine beyond the `--version` probe detection already does.\n */\nexport async function gatherSurfaces(\n adapters: AgentAdapter[],\n options: SurfaceCoverageOptions = {},\n): Promise<HarnessSurfaces[]> {\n const observed = readObservedBuilds(options.observedBuilds ?? {});\n return Promise.all(\n adapters.map(async (adapter) => {\n const observation = observed[adapter.id];\n const base = {\n harness: adapter.id,\n displayName: adapter.displayName,\n unversionedEvents: observation?.unversioned ?? 0,\n };\n try {\n const [detection, status] = await Promise.all([adapter.detect(), adapter.status()]);\n return {\n ...base,\n status,\n surfaces: detection.detected\n ? gradeSurfaces(detection.surfaces ?? [], status, observation)\n : [],\n };\n } catch {\n // Coverage reporting is a diagnostic: an adapter that cannot probe itself must degrade\n // to \"no rows\", never take down the `doctor` run that was supposed to explain it.\n return { ...base, status: \"unknown\" as IntegrationStatus, surfaces: [] };\n }\n }),\n );\n}\n\n/** How a surface row is titled in `status` / `doctor`: label plus the build it is. */\nexport function describeSurface(state: SurfaceState): string {\n const version = state.surface.version ?? state.observedVersion;\n return version !== undefined ? `${state.surface.label} ${version}` : state.surface.label;\n}\n\n/**\n * One line saying what is (or is not) reaching this surface, and why. The two ways a surface ends\n * up uncovered have different answers, so they read differently: the harness has no BirdyBeep\n * entries at all, or it has them and every sibling build is delivering while this one never has.\n */\nexport function describeSurfaceCoverage(state: SurfaceState, group: HarnessSurfaces): string {\n if (state.coverage === \"active\") {\n const last = state.lastAt !== undefined ? `, last ${new Date(state.lastAt).toISOString()}` : \"\";\n return `covered: ${state.events} event(s) from this build${last}`;\n }\n if (state.coverage === \"wired\") {\n return state.surface.shadowed === true\n ? `${group.displayName}'s hooks are installed and this build shares them, but another install comes first on PATH. This build runs only if that order changes.`\n : `${group.displayName}'s hooks are installed and this build shares them; nothing has fired from it yet`;\n }\n if (!CONFIGURED_STATUSES.has(group.status)) {\n return `not covered: ${group.displayName} carries no BirdyBeep hooks, so this build cannot produce notifications`;\n }\n const active = group.surfaces.filter((s) => s.coverage === \"active\").map(describeSurface);\n const delivering = active.join(\", \");\n const verb = active.length === 1 ? \"is\" : \"are\";\n return `not covered: nothing has fired from this build, while ${delivering} ${verb} delivering through the same config`;\n}\n\n/** CLI install target for an adapter id (the CLI says `claude`, the adapter id is `claude_code`). */\nfunction installTarget(harness: string): string {\n return harness === \"claude_code\" ? \"claude\" : harness;\n}\n\n/** What to do about an uncovered surface; `undefined` when another check already owns the fix. */\nexport function surfaceRemedy(state: SurfaceState, group: HarnessSurfaces): string | undefined {\n if (state.coverage !== \"uncovered\") return undefined;\n // The harness-level cause already has its own check with its own remedy — don't print it twice.\n if (!CONFIGURED_STATUSES.has(group.status)) return undefined;\n const install = `\\`birdybeep agent install ${installTarget(group.harness)}\\``;\n // The two kinds fail for different reasons, so they get different instructions. A desktop app\n // spawns its engine with the LOGIN shell's PATH, which is where a bare hook command goes\n // missing — the failure this whole epic turned up.\n return state.surface.kind === \"desktop\"\n ? `Run a turn in ${state.surface.label}. If it stays uncovered, that build cannot run the hook ` +\n `command: a desktop app spawns its engine with your LOGIN shell's PATH, not an interactive ` +\n `shell's, so a bare command is invisible to it. Re-run ${install} from a shell where ` +\n `\\`birdybeep\\` resolves. This rewrites the entry with absolute paths that do not depend on PATH.`\n : `Run a turn in ${state.surface.label}. If it stays uncovered, re-run ${install} from a shell ` +\n `where \\`birdybeep\\` resolves, then check that ${state.surface.enginePath} is the build you ` +\n `are actually running.`;\n}\n","/**\n * `birdybeep doctor` (§9.4, §21.1–21.2) — the self-service troubleshooter. Runs a battery\n * of checks (machine token, each adapter's doctor() incl. needs_trust/needs_restart/error,\n * local queue health, backend reachability), prints a concrete copy-pasteable fix for each\n * failure, drains the queue opportunistically, and exits non-zero when anything fails so\n * it's CI/script friendly. Read-only (never mutates harness config); never prints token\n * material or notification bodies. `--json` mirrors all findings.\n */\nimport {\n type AgentAdapter,\n createSender as defaultCreateSender,\n DEFAULT_QUEUE_MAX_ENTRIES as QUEUE_CAP,\n describeCheckIn,\n describeQuota,\n describeReachability,\n type DetectionResult,\n fetchPushReachability,\n type Sender,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { copilotAdapter } from \"@birdybeep/copilot\";\nimport { cursorAdapter } from \"@birdybeep/cursor\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport {\n cursorBridgeOnly,\n describeFilteredActivity,\n describeSurface,\n describeSurfaceCoverage,\n describeTokenStoreUnavailable,\n describeUnpairedActivity,\n filteredActivity,\n gatherSurfaces,\n localQueueDepth,\n localQueueOverflowDrops,\n pairingReport,\n type SurfaceCoverageOptions,\n surfaceRemedy,\n tokenStoreRemedy,\n unpairedActivity,\n} from \"../diagnostics\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n codexAdapter,\n opencodeAdapter,\n cursorAdapter,\n copilotAdapter,\n];\n\ninterface Check {\n name: string;\n ok: boolean;\n detail?: string;\n remedy?: string;\n}\n\n/** Best-effort backend reachability probe (HEAD; any non-5xx response = reachable). */\nasync function defaultProbeNetwork(baseUrl: string): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 3000);\n if (typeof timer.unref === \"function\") timer.unref();\n const res = await fetch(baseUrl, { method: \"HEAD\", signal: controller.signal });\n clearTimeout(timer);\n return res.status < 500;\n } catch {\n return false;\n }\n}\n\nexport interface DoctorCommandDeps {\n adapters?: AgentAdapter[];\n createSender?: (baseUrl: string) => Sender;\n tokenOptions?: TokenStoreOptions;\n /** Backend reachability probe (tests inject reachable/unreachable). */\n probeNetwork?: (baseUrl: string) => Promise<boolean>;\n /** fetch used by the push-reachability read (injected in tests). */\n fetchImpl?: typeof fetch;\n /**\n * Base URL for the push-reachability read. Injected alongside createSender/probeNetwork so a\n * doctor driven by a stub backend does not still reach the REAL API — every existing\n * doctor.test case has a paired token, so without this each one issued an authenticated\n * production request and could stall on the 4s timeout. Same fix as the test command's.\n */\n baseUrl?: string;\n /** Cursor detection for the bridge check (tests avoid shelling out to `cursor-agent`). */\n detectCursor?: () => Promise<DetectionResult>;\n /** Where the observed-builds tally lives (tests point it at a sandbox). */\n surfaceOptions?: SurfaceCoverageOptions;\n}\n\nexport function createDoctorCommand(deps: DoctorCommandDeps = {}): Command {\n const adapters = deps.adapters ?? DEFAULT_ADAPTERS;\n const probeNetwork = deps.probeNetwork ?? defaultProbeNetwork;\n const makeSender =\n deps.createSender ??\n ((baseUrl) =>\n defaultCreateSender(\n deps.tokenOptions ? { baseUrl, tokenOptions: deps.tokenOptions } : { baseUrl },\n ));\n\n return {\n name: \"doctor\",\n summary: \"Diagnose token, trust, restart, and offline-queue issues\",\n usage: \"birdybeep doctor [--json]\",\n run: async (ctx) => {\n const checks: Check[] = [];\n const apiUrl = deps.baseUrl ?? resolveApiUrl();\n\n // 1. Machine token. Three answers, not two (birdybeep-agent-gcgp.23): a store that could\n // not be READ is not a missing token, and \"Run `birdybeep pair`\" is the wrong instruction\n // for a paired user whose keychain is merely locked — pairing again would not fix it.\n const pairing = await pairingReport(deps.tokenOptions ?? {});\n checks.push(\n pairing.state === \"paired\"\n ? { name: \"Machine token\", ok: true }\n : pairing.state === \"unpaired\"\n ? {\n name: \"Machine token\",\n ok: false,\n detail: \"No machine token found.\",\n remedy: \"Run `birdybeep pair` to pair this machine.\",\n }\n : {\n name: \"Machine token\",\n ok: false,\n detail: describeTokenStoreUnavailable(pairing),\n remedy: tokenStoreRemedy(pairing),\n },\n );\n\n // 1a. Can this ACCOUNT actually receive a beep? (birdybeep-agent-oi3.) Everything else in\n // this command inspects the MACHINE, and all of it can be green while no push can arrive.\n // That is what happened: a full green board for two hours while the account's only device\n // had been stale five weeks and every push went to a dead registration. It sits directly\n // under the token row because it answers the same question the user actually has — \"why am\n // I getting no beeps?\" — and because a machine-side failure above makes it moot.\n const reachability = await fetchPushReachability({\n baseUrl: apiUrl,\n ...(deps.tokenOptions ? { tokenOptions: deps.tokenOptions } : {}),\n ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),\n });\n const reach = describeReachability(reachability);\n if (reach !== null) {\n checks.push({\n name: \"Push reachability\",\n ok: reach.ok,\n detail: reach.detail,\n ...(reach.remedy !== undefined ? { remedy: reach.remedy } : {}),\n });\n }\n\n // 1a-i. And is anyone actually THERE? (birdybeep-agent-2x9s.) The row above answers \"is a\n // device registered and is its token alive\"; a registration outlives the app that made it,\n // and APNs accepts a push for one long after the app was deleted — which is the incident\n // that started this. The question could not be asked before, because the only timestamp on\n // a device was written at registration and never moved (that is why oi3 shipped WITHOUT a\n // staleness check rather than shipping one that called a phone in daily use stale). The app\n // now checks in on every foreground, so the row is back — on the new field only, and as a\n // warning, never a ✗: a phone in a drawer is not a broken account, and this command's\n // failures have to keep meaning \"no beep can arrive\".\n const checkIn = describeCheckIn(reachability);\n if (checkIn !== null) {\n checks.push({ name: \"Device check-in\", ok: checkIn.ok, detail: checkIn.detail });\n }\n\n // 1a-ii. And can it still SPEND a beep? (birdybeep-agent-58l.) The row above answers\n // \"is there a phone at the other end\"; this one answers \"is the backend still willing to\n // send\". They are separate failures and only one of them was ever visible: /v1/agent-events\n // answers 202 and the quota gate rejects afterwards, so for a MONTH every notifiable event\n // on the owner's account was rejected while this command printed green and the CLI reported\n // each event delivered. Same response, same round trip — no second request.\n const quota = describeQuota(reachability);\n if (quota !== null) {\n checks.push({\n name: \"Beep quota\",\n ok: quota.ok,\n detail: quota.detail,\n ...(quota.remedy !== undefined ? { remedy: quota.remedy } : {}),\n });\n }\n\n // 1b. Events that fired while unpaired and were therefore never sent (gcgp.4). Placed\n // second on purpose: it is the answer to \"why am I getting no beeps?\", and it has to be\n // visible above the per-adapter checks rather than buried under twenty lines of them.\n const unpaired = unpairedActivity();\n if (unpaired !== null) {\n checks.push({\n name: \"Events lost while unpaired\",\n ok: false,\n detail: describeUnpairedActivity(unpaired),\n remedy:\n \"Run `birdybeep pair`. Events that fired before pairing were not saved and will not \" +\n \"be replayed.\",\n });\n }\n\n // 1c. Cursor runs BirdyBeep through its Claude Code compatibility bridge, which drops\n // Notification and PermissionRequest (gcgp.13) — so a Cursor user with only the Claude\n // hooks installed sees Cursor events arrive but never an approval. It reads two adapters'\n // config at once, so it can live in neither adapter's doctor(); it sits with the other\n // \"why am I missing beeps?\" answers, above the per-adapter checks. Silent once the Cursor\n // adapter is installed — nothing to nag about then.\n if (await cursorBridgeOnly(deps.detectCursor ? { detect: deps.detectCursor } : {})) {\n checks.push({\n name: \"Approval beeps from Cursor\",\n ok: false,\n detail:\n \"Cursor is running your agent through the Claude Code hooks. This is why Cursor \" +\n \"events arrive without a Cursor install. Its bridge drops Notification and \" +\n \"PermissionRequest, so approvals never reach you.\",\n remedy:\n \"Run `birdybeep agent install cursor` to get approval beeps from Cursor's own shell \" +\n \"and MCP permission prompts. Keeping both installed is safe; duplicate events are \" +\n \"collapsed.\",\n });\n }\n\n // 1d. Hooks that fired and were deliberately NOT sent (gcgp.3). An `ok` check, not a\n // failure: it is the positive evidence that the harness is wired up, which the backend\n // can no longer supply for these types because they never reach it.\n const filtered = filteredActivity();\n if (filtered !== null) {\n checks.push({\n name: \"Local-only events (never notifiable)\",\n ok: true,\n detail: describeFilteredActivity(filtered),\n });\n }\n\n // 2. Each adapter's own diagnostics (detected? installed? needs_trust/needs_restart/error?),\n // then 2b: one row per installed BUILD of that harness (gcgp.6). The adapter checks above\n // describe the shared config — one answer for the whole harness — and a machine runs a\n // harness from more than one place: the terminal CLI and the engine a desktop app spawns\n // are separate installs on separate update channels. Kept immediately under their own\n // harness so nothing above is reordered and the two read as one block.\n const surfaceGroups = await gatherSurfaces(adapters, deps.surfaceOptions ?? {});\n for (const adapter of adapters) {\n const result = await adapter.doctor();\n for (const c of result.checks) {\n checks.push({\n name: `${adapter.displayName}: ${c.name}`,\n ok: c.ok,\n ...(c.detail !== undefined ? { detail: c.detail } : {}),\n ...(c.remedy !== undefined ? { remedy: c.remedy } : {}),\n });\n }\n const group = surfaceGroups.find((g) => g.harness === adapter.id);\n if (group === undefined) continue;\n for (const state of group.surfaces) {\n const remedy = surfaceRemedy(state, group);\n checks.push({\n name: `${adapter.displayName}: ${describeSurface(state)}`,\n ok: state.coverage !== \"uncovered\",\n detail: describeSurfaceCoverage(state, group),\n ...(remedy !== undefined ? { remedy } : {}),\n });\n }\n }\n\n // 3. Local queue: drain opportunistically, report depth (and any cap overflow, gcgp.4).\n const depthBefore = localQueueDepth();\n const drain = await makeSender(apiUrl).drainNow();\n const depthAfter = localQueueDepth();\n const overflowDropped = localQueueOverflowDrops();\n checks.push({\n name: \"Local queue\",\n ok: true,\n detail:\n `${depthBefore} queued → ${drain.delivered} delivered, ${depthAfter} remaining` +\n (overflowDropped > 0 ? `; ${overflowDropped} dropped by the ${QUEUE_CAP} entry cap` : \"\"),\n });\n\n // 4. Backend reachability.\n const reachable = await probeNetwork(apiUrl);\n checks.push(\n reachable\n ? { name: \"Backend reachable\", ok: true }\n : {\n name: \"Backend reachable\",\n ok: false,\n detail: `Could not reach ${apiUrl}.`,\n remedy: \"Check your network. Queued events retry automatically.\",\n },\n );\n\n const ok = checks.every((c) => c.ok);\n\n if (ctx.flags.json) {\n ctx.io.result({\n ok,\n checks,\n pairing, // gcgp.23: paired | unpaired | unknown — never a bare boolean\n // The backend's own quota numbers, unrendered (58l) — a script (or a bug report) gets\n // the window and the counts, not just the sentence built from them.\n ...(reachability.state === \"ok\" && reachability.data.quota !== undefined\n ? { quota: reachability.data.quota }\n : {}),\n // The raw check-in (2x9s), unrendered. Present ONLY when the server reported the field\n // at all, so a script can still tell \"this backend predates check-ins\" (key absent)\n // from \"no device has ever checked in\" (key present, null) — the same distinction the\n // rendered row is careful about.\n ...(reachability.state === \"ok\" && reachability.data.most_recent_check_in_at !== undefined\n ? { mostRecentCheckInAt: reachability.data.most_recent_check_in_at }\n : {}),\n surfaces: surfaceGroups,\n queue: { depthBefore, delivered: drain.delivered, depthAfter, overflowDropped },\n ...(unpaired !== null ? { unpairedActivity: unpaired } : {}),\n ...(filtered !== null ? { filteredActivity: filtered } : {}),\n });\n } else {\n for (const c of checks) {\n ctx.io.line(`${c.ok ? \"✓\" : \"✗\"} ${c.name}${c.detail ? `: ${c.detail}` : \"\"}`);\n if (!c.ok && c.remedy) ctx.io.line(` → ${c.remedy}`);\n }\n ctx.io.line(ok ? \"\\nAll checks passed.\" : \"\\nSome checks failed. See fixes above.\");\n }\n return ok ? EXIT.OK : EXIT.ERROR;\n },\n };\n}\n","/**\n * Non-secret CLI config (§9.4): a small `config.json` in the BirdyBeep user config dir\n * holding things like the API base URL. The machine TOKEN never lives here — it is read\n * exclusively from the secure token store (keychain / strict-perm file). Tolerant readers:\n * a missing/corrupt config falls back to defaults rather than crashing the hot path.\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { birdyBeepConfigDir } from \"@birdybeep/agent-core\";\n\n/** Default backend base URL (overridable via env or `birdybeep pair`; finalized in a-release). */\nexport const DEFAULT_API_URL = \"https://api.birdybeep.com\";\nexport const CONFIG_FILE = \"config.json\";\n\nexport interface CliConfig {\n /** Backend base URL (set by `pair`); never holds a token. */\n apiUrl?: string;\n /**\n * Optional identity pin for `birdybeep pair` (birdybeep-md60): the account that must have\n * approved a pairing before its token is trusted. Same effect as `--expect-email` (which\n * overrides it), for fleets/CI images that want the check baked in. Non-secret.\n */\n expectEmail?: string;\n}\n\nexport function cliConfigPath(): string {\n return join(birdyBeepConfigDir(), CONFIG_FILE);\n}\n\n/** Read the CLI config; returns `{}` on a missing/unreadable/corrupt file (never throws). */\nexport function readCliConfig(): CliConfig {\n try {\n const parsed: unknown = JSON.parse(readFileSync(cliConfigPath(), \"utf8\"));\n return typeof parsed === \"object\" && parsed !== null ? parsed : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Merge + persist non-secret CLI config (strict-perm dir). Only the KNOWN non-secret keys\n * are ever written — anything else (e.g. a token someone passed by mistake) is dropped, so\n * the token can only ever live in the secure store, never here.\n */\nexport function writeCliConfig(patch: CliConfig): void {\n const current = readCliConfig();\n const merged: CliConfig = {};\n const apiUrl = patch.apiUrl ?? current.apiUrl;\n if (apiUrl !== undefined) merged.apiUrl = apiUrl;\n const expectEmail = patch.expectEmail ?? current.expectEmail;\n if (expectEmail !== undefined) merged.expectEmail = expectEmail;\n mkdirSync(birdyBeepConfigDir(), { recursive: true, mode: 0o700 });\n writeFileSync(cliConfigPath(), `${JSON.stringify(merged, null, 2)}\\n`, { mode: 0o600 });\n}\n\n/** Resolve the backend base URL: `BIRDYBEEP_API_URL` env → CLI config → default. */\nexport function resolveApiUrl(): string {\n const env = process.env[\"BIRDYBEEP_API_URL\"];\n if (env !== undefined && env.length > 0) return env;\n return readCliConfig().apiUrl ?? DEFAULT_API_URL;\n}\n\n/** Public npm registry — where `@birdybeep/cli` is published; used by the update notifier. */\nexport const DEFAULT_REGISTRY_URL = \"https://registry.npmjs.org\";\n\n/**\n * Resolve the npm registry base URL for the passive update check: honor `npm_config_registry`\n * (which npm/pnpm/yarn export, so a private-registry user's mirror is respected) and fall back to\n * the public registry. Never carries auth or a token.\n */\nexport function resolveRegistryUrl(): string {\n const env = process.env[\"npm_config_registry\"];\n if (env !== undefined && env.length > 0) return env;\n return DEFAULT_REGISTRY_URL;\n}\n","/**\n * `birdybeep hook <claude|codex|opencode|cursor|copilot>` (§9.2–9.3) — the hot-path entrypoint every\n * installed adapter config invokes when its harness fires a lifecycle event. It reads the\n * raw payload (from the trailing arg for Codex's notify argv, else from stdin), selects the\n * named harness's `runXHook` (normalize → redact/hash/truncate → dedup → send w/ short\n * timeout → queue-on-fail → opportunistic drain → fast return). The token is read by the\n * sender from the secure store — never from config — and notification content is never\n * persisted (the adapters' normalizers enforce that).\n *\n * Exit code is 0 for every normal outcome, including deliberate skips, so a hook fire never\n * errors the harness. The exceptions all share one shape — the hook ran, sent NOTHING, and\n * had nothing to say about it, which is precisely what hid the Cursor-bridge drop for months.\n * Each now writes a stderr line and exits non-zero (a non-blocking error every harness\n * surfaces in its log): a payload no adapter recognizes (birdybeep-agent-gcgp.1), and an\n * absent, empty, unparseable or timed-out payload (birdybeep-agent-gcgp.14). A payload we DO\n * recognize but deliberately don't map stays a quiet exit 0.\n *\n * Built as a factory so the sender + stdin reader are injectable: tests drive the full\n * dispatch → command → pipeline → stub-sink path hermetically, exactly like the adapter E2Es.\n */\nimport { spawn } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport { closeSync, openSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { basename, dirname, join } from \"node:path\";\nimport { performance } from \"node:perf_hooks\";\n\nimport {\n createSender as defaultCreateSender,\n DEFAULT_SEND_TIMEOUT_MS,\n DEFAULT_TOTAL_BUDGET_MS,\n type HookResult,\n resolveOnPath,\n type Sender,\n} from \"@birdybeep/agent-core\";\nimport {\n configuredClaudeHookTimeoutSeconds,\n isClaudeCodeHookPayload,\n runClaudeHook,\n} from \"@birdybeep/claude-code\";\nimport {\n configuredCodexHookTimeoutSeconds,\n isCodexHookPayload,\n runCodexHook,\n} from \"@birdybeep/codex\";\nimport {\n configuredCopilotHookTimeoutSeconds,\n type CopilotHookEventName,\n isCopilotHookEventName,\n isCopilotHookPayload,\n runCopilotHook,\n} from \"@birdybeep/copilot\";\nimport {\n configuredCursorHookTimeoutSeconds,\n isCursorHookEventName,\n isCursorHookPayload,\n runCursorHook,\n} from \"@birdybeep/cursor\";\nimport { isOpenCodeEventPayload, runOpenCodeHook } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\nexport type HarnessName = \"claude\" | \"codex\" | \"opencode\" | \"cursor\" | \"copilot\";\n\ntype HarnessRunner = (input: unknown, options: { sender: Sender }) => Promise<HookResult>;\n\nconst RUNNERS: Record<Exclude<HarnessName, \"copilot\">, HarnessRunner> = {\n claude: runClaudeHook,\n codex: runCodexHook,\n opencode: runOpenCodeHook,\n cursor: runCursorHook,\n};\n\nexport const HOOK_HARNESSES: readonly HarnessName[] = [\n \"claude\",\n \"codex\",\n \"opencode\",\n \"cursor\",\n \"copilot\",\n];\n\n/**\n * Hard cap on reading the payload — a misbehaving harness must never hang the hook.\n * 3s (was 2s, erm): a loaded machine can be slow to flush a pipe, and a timeout here\n * silently DROPS the event (\"skipped\"). A live healthy production ingest took 5.8s, so the\n * former 5s sender budget made the client abort and falsely queue already-accepted events.\n * {@link LEGACY_HOOK_RUNTIME_BUDGET_MS} clamps the later send so this read and the 8s sender\n * allowance never overrun a 10s hook left behind by a package-only upgrade.\n */\nexport const STDIN_READ_TIMEOUT_MS = 3000;\n\n/**\n * Runtime available after the hook process starts, before returning control to the harness.\n *\n * Managed installs now use 15s, but an npm-only upgrade does not rewrite an existing 10s hook.\n * Keep every invocation inside that legacy deadline and reserve one second for Node startup,\n * queue persistence, output, and harness scheduling. Fast stdin still gets the full 8s sender\n * allowance; a slow stdin read reduces the send budget instead of letting the harness kill the\n * process before its timeout path can persist the event.\n */\nexport const LEGACY_HOOK_RUNTIME_BUDGET_MS = 9000;\n\n/** Convert a discovered outer hook deadline into the safe total runtime available to us. */\nexport function hookRuntimeBudgetMs(configuredTimeoutSeconds: number | undefined): number {\n if (\n configuredTimeoutSeconds === undefined ||\n !Number.isFinite(configuredTimeoutSeconds) ||\n configuredTimeoutSeconds <= 0\n ) {\n return LEGACY_HOOK_RUNTIME_BUDGET_MS;\n }\n const configuredBudgetMs = Math.max(1, Math.floor(configuredTimeoutSeconds * 1000) - 1000);\n return Math.min(LEGACY_HOOK_RUNTIME_BUDGET_MS, configuredBudgetMs);\n}\n\nfunction configuredHookTimeoutSeconds(harness: HarnessName): number | undefined {\n if (harness === \"claude\") return configuredClaudeHookTimeoutSeconds();\n if (harness === \"codex\") return configuredCodexHookTimeoutSeconds();\n if (harness === \"cursor\") return configuredCursorHookTimeoutSeconds();\n if (harness === \"copilot\") return configuredCopilotHookTimeoutSeconds();\n return undefined;\n}\n\n/** Resolve to `fallback` if `promise` does not settle within `ms` (the timer is unref'd). */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, fallback: T): Promise<T> {\n return new Promise<T>((resolve) => {\n let settled = false;\n const finish = (value: T): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(value);\n };\n const timer = setTimeout(() => finish(fallback), ms);\n if (typeof timer.unref === \"function\") timer.unref();\n void promise.then(finish, () => finish(fallback));\n });\n}\n\nexport function isHarnessName(value: string | undefined): value is HarnessName {\n return (\n value === \"claude\" ||\n value === \"codex\" ||\n value === \"opencode\" ||\n value === \"cursor\" ||\n value === \"copilot\"\n );\n}\n\n/**\n * Which harness's adapter should actually handle this payload.\n *\n * birdybeep-agent-gcgp.1: Cursor desktop's Claude Code compatibility bridge reads\n * `~/.claude/settings.json` and runs `birdybeep hook claude` with a CURSOR payload (lowercase\n * step names + `cursor_version`/`workspace_roots`). Sending that through the Claude normalizer\n * hit its `default:` throw, which the pipeline turns into `skipped` — every bridged event was\n * dropped, exit 0, no output. Route them to the Cursor adapter instead: they normalize\n * correctly AND are attributed to `harness: \"cursor\"`, so bridged traffic never masquerades as\n * Claude Code. Detection keys on fields Claude Code never sends, so a real Claude Code fire\n * can't be reclassified.\n */\nexport function resolveHookHarness(harness: HarnessName, payload: unknown): HarnessName {\n return harness === \"claude\" && isCursorHookPayload(payload) ? \"cursor\" : harness;\n}\n\n/**\n * Is this payload one the handling harness actually fires? A payload we recognize but don't\n * map is a deliberate skip (quiet); one we don't recognize at all means something else is\n * driving this hook, and dropping it silently is the bug gcgp.1 was.\n *\n * All five harnesses answer now (birdybeep-agent-gcgp.14). Codex matters most — its `notify`\n * slot is a single-valued scalar that third-party tools also claim, so a chained tool handing\n * us an unfamiliar shape is a live possibility. Copilot matters differently: its payloads\n * carry no event discriminator (the event name is an argv argument), so a foreign payload did\n * not even skip — it normalized into a FABRICATED Copilot event and was sent.\n */\nfunction recognizesPayload(harness: HarnessName, payload: unknown): boolean {\n switch (harness) {\n case \"claude\":\n return isClaudeCodeHookPayload(payload);\n case \"cursor\":\n return isCursorHookEventName(asRecord(payload)[\"hook_event_name\"]);\n case \"codex\":\n return isCodexHookPayload(payload);\n case \"opencode\":\n return isOpenCodeEventPayload(payload);\n case \"copilot\":\n return isCopilotHookPayload(payload);\n }\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/**\n * The payload's discriminating field for a diagnostic line — `hook_event_name` (Claude Code,\n * Codex hooks, Cursor), else `type` (Codex notify, OpenCode). Length-capped and JSON-quoted:\n * these are safe identifiers, but this is the one place hook output echoes the payload, so it\n * never grows unbounded and never reaches past that single field (no titles, no bodies, no\n * prompts). Copilot payloads have neither field — the caller gets \"the payload\".\n */\nfunction describeDiscriminator(payload: unknown): string {\n const record = asRecord(payload);\n for (const field of [\"hook_event_name\", \"type\"] as const) {\n const value = record[field];\n if (typeof value !== \"string\") continue;\n const capped = value.length > 64 ? `${value.slice(0, 63)}…` : value;\n return `${field} ${JSON.stringify(capped)}`;\n }\n return \"the payload\";\n}\n\n/** Run one hook fire: select the harness runner and execute via the shared pipeline. */\nexport function runHookCommand(\n harness: HarnessName,\n payload: unknown,\n sender: Sender,\n copilotEventName?: CopilotHookEventName,\n): Promise<HookResult> {\n const handler = resolveHookHarness(harness, payload);\n if (handler === \"copilot\") {\n if (copilotEventName === undefined) return Promise.resolve({ outcome: \"skipped\" });\n return runCopilotHook(copilotEventName, payload, { sender });\n }\n return RUNNERS[handler](payload, { sender });\n}\n\n/** Read process.stdin to EOF (the harness pipes a small JSON then closes); never throws. */\nfunction readStdinDefault(): Promise<string> {\n return new Promise((resolve) => {\n if (process.stdin.isTTY) {\n resolve(\"\");\n return;\n }\n let data = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk: string) => (data += chunk));\n process.stdin.on(\"end\", () => resolve(data));\n process.stdin.on(\"error\", () => resolve(\"\"));\n });\n}\n\n/** Resolve the raw payload: the trailing arg (Codex notify argv) wins, else read stdin. */\nexport async function readHookPayload(\n args: string[],\n readStdin: () => Promise<string>,\n stdinOnly = false,\n): Promise<string> {\n return stdinOnly ? readStdin() : (args[1] ?? (await readStdin()));\n}\n\n/**\n * Env var carrying the temp-file path the detached notify worker deletes after reading it —\n * see {@link detachCodexNotifyWorker}. Set only on the detached worker's environment.\n */\nexport const NOTIFY_STDIN_FILE_ENV = \"BIRDYBEEP_CODEX_NOTIFY_STDIN_FILE\";\n\n/**\n * birdybeep-agent-fuf: `codex exec` (headless/one-shot) reaps the notify child's PROCESS\n * GROUP when it exits. Codex fires `notify` at turn-complete — right before it exits — so on\n * a cold/slow backend the in-line send is still in flight when the group is SIGKILLed, and\n * the event is lost before delivery *or* the queue-write finishes (the interactive `codex`\n * TUI stays alive, so it never hit this).\n *\n * The fix, scoped to the notify path only: instead of sending in-line, re-launch\n * `birdybeep hook codex` DETACHED (`detached: true` → `setsid`/new session) reading the\n * payload on stdin, then return immediately. The detached worker is NOT in the group\n * `codex exec` reaps, so it outlives the harness and completes the fast send+queue. Because\n * the worker is invoked WITHOUT a trailing argv payload it reads stdin and runs the ordinary\n * in-process path below — it is never itself re-detached, so this never recurses. Lifecycle\n * `[[hooks.X]]` events are deliberately untouched: they arrive on stdin and fire mid-session.\n *\n * The payload is delivered via a strict-perm (0o600) temp FILE handed to the worker as its\n * stdin fd — NOT a pipe this process holds. Two reasons: (1) the payload is fully written\n * before the spawn, so the worker always reads it complete even though we exit immediately;\n * (2) this process then holds NO pipe/stream to the child, so its prompt exit is DETERMINISTIC\n * on every platform — it never depends on when/whether a parent-held stdin pipe flushes and\n * closes, which is exactly the fast-return codex needs. The worker unlinks the temp file after\n * reading it (via {@link NOTIFY_STDIN_FILE_ENV}).\n *\n * Scoped to POSIX: on Windows a child is NOT killed when its parent exits (see agent-core\n * safe-spawn), so the exec-exit reap race does not arise there — we return false and the\n * caller sends in-line. We also return false when `birdybeep` can't be resolved on PATH or the\n * spawn throws; an in-line best-effort delivery still beats dropping the event outright.\n */\nexport function detachCodexNotifyWorker(payload: string): boolean {\n if (process.platform === \"win32\") return false; // no exec-exit reap race on Windows\n let file: string | undefined;\n let fd: number | undefined;\n try {\n // SECURITY (sec-review-2026-07 H1): resolve `birdybeep` to an absolute path on PATH ONLY\n // (never cwd — the harness's cwd is the attacker-controllable repo), then spawn that\n // absolute path with a trusted cwd. On POSIX `birdybeep` is a real executable (never a\n // shell shim), so no shell is involved.\n const birdybeep = resolveOnPath(\"birdybeep\");\n if (birdybeep === null) return false; // not on PATH → caller sends in-line as a fallback\n\n const tmpFile = join(tmpdir(), `birdybeep-notify-${randomBytes(16).toString(\"hex\")}.json`);\n file = tmpFile; // track for the synchronous catch cleanup path\n writeFileSync(tmpFile, payload, { mode: 0o600 }); // fully written BEFORE spawn\n fd = openSync(tmpFile, \"r\");\n const child = spawn(birdybeep, [\"hook\", \"codex\"], {\n cwd: dirname(birdybeep), // trusted dir, never the inherited/attacker cwd\n detached: true, // new session (setsid) → survives `codex exec` reaping the group\n stdio: [fd, \"ignore\", \"ignore\"], // stdin = the temp file; this process holds no pipe\n env: { ...process.env, [NOTIFY_STDIN_FILE_ENV]: tmpFile }, // worker cleans it up post-read\n windowsHide: true,\n });\n child.on(\"error\", () => {\n // `spawn` reports most launch failures (EMFILE/ENOMEM, or the binary vanishing after\n // resolveOnPath) ASYNCHRONOUSLY via 'error', after we've already returned true. The worker\n // never ran, so it can't delete its stdin temp file — clean it up here so we don't leak a\n // 0o600 file per failed fire. The event itself is lost (we already returned; there's no\n // retroactive in-line send), which is the accepted best-effort contract for detachment.\n try {\n rmSync(tmpFile, { force: true });\n } catch {\n /* the OS reclaims tmp eventually */\n }\n });\n child.unref(); // don't keep the notify process alive waiting on the worker\n return true;\n } catch {\n if (file !== undefined) {\n try {\n rmSync(file, { force: true }); // spawn failed before the worker could clean up\n } catch {\n /* the OS reclaims tmp eventually */\n }\n }\n return false; // any failure → in-line fallback (never throw into the harness)\n } finally {\n if (fd !== undefined) {\n try {\n closeSync(fd); // the child holds its own dup; this process keeps no fd\n } catch {\n /* already closed / never opened */\n }\n }\n }\n}\n\nexport interface HookCommandDeps {\n /** Build the sender with the wall-clock budget left for this invocation. */\n createSender?: (baseUrl: string, budgetMs: number) => Sender;\n /** Read the raw payload from stdin (default: real process.stdin). */\n readStdin?: () => Promise<string>;\n /** Hard cap on the payload read (default {@link STDIN_READ_TIMEOUT_MS}); tests shrink it. */\n stdinTimeoutMs?: number;\n /** Injectable monotonic clock for the legacy-hook budget tests. */\n now?: () => number;\n /** Read the harness's configured outer deadline; tests inject custom preserved values. */\n configuredHookTimeoutSeconds?: (harness: HarnessName) => number | undefined;\n /**\n * Detach the Codex notify send into a process that survives `codex exec` reaping its group\n * (birdybeep-agent-fuf). Default {@link detachCodexNotifyWorker}; returns whether the\n * detached worker launched (true → the notify process returns fast; false → send in-line as\n * a fallback). Injectable so tests drive the branch without spawning a real process.\n */\n detachCodexNotify?: (payload: string) => boolean;\n}\n\n/** Build the `hook` command. Pure stubs aside, this is the live event path. */\nexport function createHookCommand(deps: HookCommandDeps = {}): Command {\n const makeSender =\n deps.createSender ??\n ((baseUrl: string, budgetMs: number) =>\n defaultCreateSender({\n baseUrl,\n timeoutMs: Math.min(DEFAULT_SEND_TIMEOUT_MS, budgetMs),\n totalBudgetMs: budgetMs,\n }));\n const readStdin = deps.readStdin ?? readStdinDefault;\n const stdinTimeoutMs = deps.stdinTimeoutMs ?? STDIN_READ_TIMEOUT_MS;\n const now = deps.now ?? (() => performance.now());\n const readConfiguredHookTimeoutSeconds =\n deps.configuredHookTimeoutSeconds ?? configuredHookTimeoutSeconds;\n const detachCodexNotify = deps.detachCodexNotify ?? detachCodexNotifyWorker;\n\n return {\n name: \"hook\",\n summary: \"Internal: normalize + send an event fired by a harness hook\",\n usage: \"birdybeep hook <claude|codex|opencode|cursor|copilot> [copilot-event]\",\n run: async (ctx) => {\n const hookStartedAt = now();\n const harness = ctx.args[0];\n if (!isHarnessName(harness)) {\n ctx.io.errline(`birdybeep hook: expected one of ${HOOK_HARNESSES.join(\"|\")}`);\n return EXIT.USAGE;\n }\n\n // birdybeep-agent-fuf: a Codex *notify* fire (payload delivered as the trailing argv\n // arg) races `codex exec` exit, which reaps the notify process group. Re-launch the send\n // DETACHED reading the payload on stdin (see {@link detachCodexNotifyWorker}) and return\n // immediately, so it outlives the reap. Scoped to notify only — lifecycle hooks arrive on\n // stdin. If the worker can't be launched we fall through and send in-line (best-effort).\n // An empty trailing arg is not a real notify payload — fall through rather than spawning\n // a worker just to read an empty file; the empty-payload diagnostic below reports it.\n const notifyPayload = ctx.args[1];\n if (\n harness === \"codex\" &&\n notifyPayload !== undefined &&\n notifyPayload.length > 0 &&\n detachCodexNotify(notifyPayload)\n ) {\n ctx.io.result({ harness, outcome: \"detached\" });\n return EXIT.OK; // the detached worker delivers; the notify process must not block codex\n }\n\n // Copilot's second arg is the event name, not a JSON payload. Every Copilot payload must\n // therefore come from stdin; Codex retains its notify argv-payload behavior.\n const copilotEventName =\n harness === \"copilot\" && isCopilotHookEventName(ctx.args[1]) ? ctx.args[1] : undefined;\n // birdybeep-agent-gcgp.14: without a usable event name the Copilot adapter cannot map\n // anything, and this returned `skipped` at exit 0 — a hook that fires, does nothing, and\n // says nothing. The installed config always passes one, so reaching here means the hook\n // entry was hand-edited or something else is invoking the command.\n if (harness === \"copilot\" && copilotEventName === undefined) {\n ctx.io.errline(\n `birdybeep hook copilot: second argument must be a Copilot hook event name, got ` +\n `${JSON.stringify(ctx.args[1] ?? \"(none)\")}. Nothing was sent.`,\n );\n return EXIT.USAGE;\n }\n\n // Bounded read: the trailing argv payload resolves instantly; a hung/never-closing\n // stdin falls back after the timeout so the hook ALWAYS returns fast (§9.3). The\n // fallback is `null` rather than \"\" so a timeout stays distinguishable from a harness\n // that closed stdin without writing — both are drops, and each names itself below.\n const read = await withTimeout<string | null>(\n readHookPayload(ctx.args, readStdin, harness === \"copilot\"),\n stdinTimeoutMs,\n null,\n );\n\n // If we ARE the detached notify worker (spawned by detachCodexNotifyWorker), the payload\n // was handed to us as a strict-perm temp file used for stdin — now that it's read, delete\n // it. Guard the path (our own tmpdir prefix) before unlinking so a stray/injected env value\n // can never make a hook fire force-delete an arbitrary file. Best-effort: the OS reclaims\n // tmp anyway, and a stale file is never a correctness bug.\n const notifyStdinFile = process.env[NOTIFY_STDIN_FILE_ENV];\n if (\n notifyStdinFile !== undefined &&\n dirname(notifyStdinFile) === tmpdir() &&\n basename(notifyStdinFile).startsWith(\"birdybeep-notify-\")\n ) {\n try {\n rmSync(notifyStdinFile, { force: true });\n } catch {\n /* the OS reclaims tmp eventually */\n }\n }\n\n // birdybeep-agent-gcgp.14: every branch below produced `skipped` at exit 0 with NO\n // output — the same invisible-drop shape as gcgp.1, and the one the 3s stdin cap turns\n // into a silent data loss on a loaded machine. Each now names itself on stderr and exits\n // non-zero. The payload itself is never echoed (it holds prompts, commands and tool\n // output); an unparseable one is described by BYTE LENGTH only.\n const drop = (reason: string, detail: string): number => {\n ctx.io.result({ harness, outcome: \"skipped\", reason });\n ctx.io.errline(`birdybeep hook ${harness}: ${detail}. Nothing was sent.`);\n return EXIT.ERROR;\n };\n if (read === null) {\n return drop(\n \"stdin-timeout\",\n `timed out after ${stdinTimeoutMs}ms waiting for the payload on stdin`,\n );\n }\n const raw = read;\n if (raw.trim().length === 0) {\n return drop(\"empty-payload\", \"the payload was empty\");\n }\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n return drop(\"invalid-json\", `the ${raw.length}-byte payload is not valid JSON`);\n }\n\n // A foreign payload is handled by the harness it actually came from (see\n // resolveHookHarness) and reported as such, with `routedFrom` naming the hook that ran.\n const handler = resolveHookHarness(harness, payload);\n const routedFrom = handler !== harness ? { routedFrom: harness } : {};\n // birdybeep-agent-gcgp.1 + gcgp.14: a payload the handling adapter does not recognize\n // means something else is driving this hook. Checked BEFORE the pipeline runs, because\n // for Copilot \"unmappable\" is not the failure mode — its payloads carry no event\n // discriminator, so a foreign one normalizes cleanly and a fabricated event goes out.\n // A payload we DO recognize but don't map keeps its quiet exit 0 below.\n if (!recognizesPayload(handler, payload)) {\n ctx.io.result({\n harness: handler,\n ...routedFrom,\n outcome: \"skipped\",\n reason: \"foreign-payload\",\n });\n const article = handler === \"opencode\" ? \"an\" : \"a\"; // the only vowel-initial harness id\n ctx.io.errline(\n `birdybeep hook ${harness}: ${describeDiscriminator(payload)} is not ${article} ` +\n `${handler} hook event. Nothing was sent. Check which tool is running this hook.`,\n );\n return EXIT.ERROR;\n }\n\n // Package-only upgrades leave the already-installed hook's deadline untouched, including\n // user-customized values below the old 10s default. Account for time already spent reading\n // and validating stdin, then give the sender only the smaller of its normal 8s allowance\n // and the configured-safe remainder. The sender counts secure-store lookup against this\n // budget too, so it can queue before the harness kills the process.\n const runtimeBudgetMs = hookRuntimeBudgetMs(readConfiguredHookTimeoutSeconds(harness));\n const elapsedMs = Math.max(0, now() - hookStartedAt);\n const budgetMs = Math.max(1, Math.min(DEFAULT_TOTAL_BUDGET_MS, runtimeBudgetMs - elapsedMs));\n const sender = makeSender(resolveApiUrl(), budgetMs);\n const result = await runHookCommand(harness, payload, sender, copilotEventName);\n // Hot path: human mode is silent; --json emits the outcome for scripts/debugging.\n // Surface the backend's 202 decision (notified/suppressed/deduped) + HTTP status when\n // a send was attempted — the outcome alone (\"delivered\") can't distinguish a beep that\n // fired from one the backend accepted-but-suppressed, which is exactly the failure mode\n // `doctor` and delivery debugging need to see.\n ctx.io.result({\n harness: handler,\n ...routedFrom,\n ...(copilotEventName !== undefined ? { event: copilotEventName } : {}),\n outcome: result.outcome,\n eventType: result.eventType,\n ...(result.send?.decision ? { decision: result.send.decision } : {}),\n ...(result.send?.status !== undefined ? { status: result.send.status } : {}),\n ...(result.send?.queueCause !== undefined ? { queueCause: result.send.queueCause } : {}),\n ...(result.send?.tokenStoreUnavailable !== undefined ? { tokenStore: \"unavailable\" } : {}),\n });\n // birdybeep-agent-gcgp.4: an unpaired machine sent NOTHING and said NOTHING — the defect\n // that let 1138 events vanish over 18 hours. Say it on stderr (Cursor's hook log has a\n // STDERR section; Claude Code surfaces it), and note that `doctor` has the durable count,\n // because a bare hook command has no other way to reach the user. Exit stays 0: not being\n // paired is a BirdyBeep problem, and erroring the harness over it would be worse than the\n // silence. The durable half of this signal is the notice file agent-core just wrote.\n if (result.outcome === \"unpaired\") {\n ctx.io.errline(\n \"birdybeep: this machine is not paired. The event was not sent or queued. \" +\n \"Run `birdybeep pair`, or run `birdybeep doctor` to see how many events were missed.\",\n );\n }\n // 9u0: a retryable send is still lost when the queue cannot write it. Hooks remain exit 0\n // (BirdyBeep must not break the harness), but stderr and --json must not promise a retry.\n if (result.outcome === \"failed\") {\n ctx.io.errline(\n \"birdybeep: the event could not be sent or saved locally. It will not retry. Check \" +\n \"that BirdyBeep can write to its user data directory, then run `birdybeep doctor`.\",\n );\n }\n // birdybeep-agent-gcgp.23: the same line for a store that would not ANSWER would be a\n // wrong diagnosis — this machine may well be paired. Say what actually happened: the\n // event is queued and will go when the store is readable, so there is nothing to fix in\n // BirdyBeep and nothing lost. Exit stays 0 for the same reason as above.\n const unavailable =\n result.outcome === \"queued\" ? result.send?.tokenStoreUnavailable : undefined;\n if (unavailable !== undefined) {\n ctx.io.errline(\n `birdybeep: the machine token is unreadable (${unavailable.reason}). The event is ` +\n \"queued. Restore token-store access; `birdybeep doctor` drains the queue.\",\n );\n }\n // A recognized event we deliberately don't map stays quiet at exit 0, so normal\n // operation never gets noisier (gcgp.12: the deferred-but-real Claude Code events).\n return EXIT.OK; // delivered/queued/deduped/skipped all return fast + non-erroring\n },\n };\n}\n","/**\n * `birdybeep logout` / `birdybeep unpair` (§9.4) — remove this machine's pairing.\n *\n * They differ on purpose:\n * - `logout` is LOCAL ONLY: it removes the machine token from BOTH the OS keychain and the\n * strict-perm file fallback. It does not touch the server — use it to sign this box out\n * without revoking the installation (you can re-pair the SAME machine later).\n * - `unpair` is the true reverse of `pair`: it REVOKES the machine server-side (best-effort\n * `POST /v1/machine/revoke-self` with the machine token) so the machine disappears from the\n * app, AND then clears the local token. If the server can't be reached, it still clears the\n * local token and tells you to revoke the machine in the app so a ghost row can't linger.\n *\n * Both are idempotent (no error when already signed out) and never touch harness integration\n * config (that is `agent uninstall`) or the local queue.\n */\nimport { clearToken, getToken, type TokenStoreOptions } from \"@birdybeep/agent-core\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\nexport interface LogoutCommandDeps {\n /** Token-store options (tests inject the file fallback). */\n tokenOptions?: TokenStoreOptions;\n}\n\nexport interface UnpairCommandDeps extends LogoutCommandDeps {\n /** Injected for tests; defaults to the global `fetch`. */\n fetchImpl?: typeof fetch;\n /** Bound on the server round-trip so `unpair` can never hang on a dead network. Default 10s. */\n timeoutMs?: number;\n}\n\nconst base = (apiUrl: string): string => apiUrl.replace(/\\/$/, \"\");\n\nexport function createLogoutCommand(deps: LogoutCommandDeps = {}): Command {\n return {\n name: \"logout\",\n summary: \"Remove the local machine token (does NOT revoke the machine server-side)\",\n usage: \"birdybeep logout\",\n run: async (ctx) => {\n await clearToken(deps.tokenOptions ?? {});\n ctx.io.emit(\"Logged out. The machine token was removed.\", { loggedOut: true });\n return EXIT.OK;\n },\n };\n}\n\n/** Outcome of the best-effort server-side revoke during `unpair`. */\ntype RevokeOutcome =\n | \"revoked\" // server confirmed the machine is gone (2xx, or already-revoked 403)\n | \"no_token\" // nothing to revoke — there was no local token to begin with\n | \"unreachable\" // couldn't reach the server (offline / timeout) — machine may still show\n | \"rejected\"; // server answered but didn't confirm removal (e.g. 401/5xx)\n\nasync function revokeSelf(\n token: string,\n fetchImpl: typeof fetch,\n timeoutMs: number,\n): Promise<RevokeOutcome> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetchImpl(`${base(resolveApiUrl())}/v1/machine/revoke-self`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${token}` },\n signal: controller.signal,\n });\n // 2xx → revoked now. 403 (token_revoked) → it was already revoked server-side, so the\n // machine is already gone: from the user's view, unpaired either way. Anything else\n // (401 invalid token, 5xx, 429) → we can't confirm removal.\n if (res.ok || res.status === 403) return \"revoked\";\n return \"rejected\";\n } catch {\n return \"unreachable\"; // offline / DNS / timeout (abort) — never fatal; local clear proceeds\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function createUnpairCommand(deps: UnpairCommandDeps = {}): Command {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const timeoutMs = deps.timeoutMs ?? 10_000;\n return {\n name: \"unpair\",\n summary: \"Revoke this machine and remove its local token\",\n usage: \"birdybeep unpair\",\n run: async (ctx) => {\n const token = await getToken(deps.tokenOptions ?? {});\n const outcome: RevokeOutcome =\n token === null ? \"no_token\" : await revokeSelf(token, fetchImpl, timeoutMs);\n\n // Always clear the local token afterward — idempotent, and `unpair` must succeed even\n // fully offline (the server revoke is best-effort). Ordered AFTER the revoke so the\n // token is still available to authenticate it.\n await clearToken(deps.tokenOptions ?? {});\n\n const serverRevoked = outcome === \"revoked\";\n const human =\n outcome === \"revoked\"\n ? \"Unpaired. The machine was revoked and removed from your account.\"\n : outcome === \"no_token\"\n ? \"Already unpaired. There was no local token to remove.\"\n : outcome === \"unreachable\"\n ? \"Unpaired locally, but the server was unreachable. The machine may still appear in the app. Open BirdyBeep and revoke it there.\"\n : \"Unpaired locally, but the server did not confirm removal. If the machine still appears in the app, revoke it there.\";\n ctx.io.emit(human, { unpaired: true, serverRevoked });\n return EXIT.OK;\n },\n };\n}\n","/**\n * `birdybeep pair` (§7.1/§7.2/§9.4) — pair this machine via the device-code flow.\n * `POST /v1/pair/start` (machine_label derived from hostname/OS) → show a scannable\n * QR matrix + the complete pair link + display-only `user_code` → poll `POST /v1/pair/token`\n * with the device\n * code (+ stable machine fingerprint) until it returns the durable token or the\n * `expires_at` (10-min) deadline. The issued token is stored in the SECURE store only\n * (keychain / strict-perm file — never config or the QR); the non-secret apiUrl is\n * persisted. Per SPEC §11 the QR/link carries only short-lived pairing info; its fragment\n * contains the approval secret, so the displayed user code cannot approve by itself.\n *\n * The QR matrix (birdybeep-agent-pe1) renders only on an interactive TTY — piped/CI\n * output keeps the plain link + display-code lines, which are ALWAYS printed as the SSH/\n * headless fallback (docs/pairing.md \"Headless and SSH machines\"). In `--json` mode\n * the pairing info is emitted as an NDJSON line up front (status \"pairing_started\")\n * so scripts/agents can surface the complete QR payload for approval — previously json mode printed\n * nothing until success, making scripted pairing impossible (birdybeep-agent-pe1).\n *\n * fetch/sleep/clock/QR/TTY are injectable for hermetic tests.\n */\nimport { closeSync, openSync } from \"node:fs\";\n\nimport {\n clearUnpairedNotice,\n deriveCodeChallengeS256,\n generateCodeVerifier,\n getMachineIdentity,\n getToken,\n LocalEventQueue,\n setToken,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\n// uqr is the CLI's ONLY third-party runtime dep (MIT, itself zero-dependency), pinned\n// EXACTLY in package.json: QR encoding (Reed–Solomon + masking) is too error-prone to\n// vendor, and a floating range would defeat the small-auditable-supply-chain goal (§16.4).\nimport { renderUnicodeCompact } from \"uqr\";\n\nimport { cliConfigPath, readCliConfig, resolveApiUrl, writeCliConfig } from \"../config\";\nimport { type Command, type CommandContext, EXIT } from \"../framework\";\nimport { pairStart, pairTokenPoll, type PairTokenResult } from \"../pairing\";\nimport { CLI_VERSION } from \"../version\";\nimport { failedSetupReport, runHarnessSetup, type SetupDeps, type SetupReport } from \"./setup\";\n\n/** Default delay between `/pair/token` polls (the start response has no interval). */\nexport const DEFAULT_POLL_INTERVAL_MS = 2000;\n\n/**\n * How often to reprint a \"still waiting…\" heartbeat while polling. Without it, `pair`\n * prints the pairing instructions once and then appears frozen (\"stuck doing nothing\") for the whole\n * 10-minute window — the reported bug. Time-gated on the injected clock so it never\n * fires spuriously in the fast, instant-sleep tests.\n */\nexport const HEARTBEAT_MS = 15_000;\n\n/**\n * Render the QR payload as a terminal-scannable half-block matrix. `border: 2` keeps a\n * quiet zone around the symbol (phone cameras misread flush-against-text QRs).\n */\nexport function renderQrMatrix(qrPayload: string): string {\n return renderUnicodeCompact(qrPayload, { border: 2 });\n}\n\n/** What the caller asked for on the command line (beyond the global flags). */\nexport interface PairFlags {\n /** `--yes` / `-y`: skip the interactive confirm (CI/headless escape hatch). */\n yes: boolean;\n /** `--expect-email <addr>`: pin the identity that must have approved this pairing. */\n expectEmail?: string;\n /** `--no-install`: stop after the token — don't detect or install any harness (gcgp.5). */\n noInstall: boolean;\n /** `--no-test`: don't send the closing test Beep (gcgp.5). */\n noTest: boolean;\n /** A usage problem (unknown value, stray argument) — the command exits EXIT.USAGE. */\n error?: string;\n}\n\n/**\n * Parse `pair`/`setup`'s own flags out of the post-global argv. Accepts `--expect-email addr` and\n * `--expect-email=addr`; any stray positional is a usage error (neither verb takes one), so a\n * fat-fingered `birdybeep pair becs@example.com` can never be silently ignored while the\n * confirm gate falls back to prompting.\n */\nexport function parsePairFlags(args: string[]): PairFlags {\n const flags: PairFlags = { yes: false, noInstall: false, noTest: false };\n for (let i = 0; i < args.length; i += 1) {\n const token = args[i] ?? \"\";\n if (token === \"--yes\" || token === \"-y\") {\n flags.yes = true;\n } else if (token === \"--no-install\") {\n flags.noInstall = true;\n } else if (token === \"--no-test\") {\n flags.noTest = true;\n } else if (token === \"--expect-email\" || token.startsWith(\"--expect-email=\")) {\n const inline = token.startsWith(\"--expect-email=\")\n ? token.slice(\"--expect-email=\".length)\n : undefined;\n const value = inline ?? args[++i];\n if (value === undefined || value.length === 0 || value.startsWith(\"-\")) {\n return { ...flags, error: \"--expect-email requires an email address\" };\n }\n flags.expectEmail = value;\n } else {\n return { ...flags, error: `unexpected argument \"${token}\"` };\n }\n }\n return flags;\n}\n\n/**\n * The confirm gate's inputs (birdybeep-md60). `approvedByEmail` is what the server said\n * approved this pairing; everything else is how the operator invoked the CLI.\n */\nexport interface PairConfirmInput {\n /** `approved_by_email` from `/v1/pair/token` — absent on older servers. */\n approvedByEmail?: string;\n /** The pinned identity (`--expect-email`, else the `expectEmail` config key). */\n expectEmail?: string;\n /**\n * WHERE the pin came from — the remedy differs. A `--flag` pin can simply be dropped from the\n * next invocation; a `config` pin lives in a file and there is no CLI switch that ignores it,\n * so the message has to name the file instead of suggesting an impossible re-run.\n */\n expectEmailSource?: \"flag\" | \"config\";\n yes: boolean;\n nonInteractive: boolean;\n /** Whether stdin can carry an answer (a real terminal), i.e. prompting won't hang. */\n stdinIsTTY: boolean;\n /**\n * Whether the process can still reach its CONTROLLING TERMINAL (`/dev/tty`) even though stdin\n * isn't one. This is the difference between \"a human is sitting here but their shell hands us\n * pipe-backed stdio\" and \"nobody is there at all\" (a script, a CI job): only the latter may\n * fail closed, the former gets prompted on the terminal it actually has. Always false on\n * Windows — see {@link canOpenControllingTerminal}.\n */\n controllingTerminalAvailable: boolean;\n /** Platform, for platform-specific remediation in the reject text. Default `process.platform`. */\n platform?: string;\n /** Where the CLI config file lives, for the config-pin remedy. */\n configPath?: string;\n}\n\nexport type PairConfirmDecision =\n /** Trust the token without asking (a pin matched, or the operator passed `--yes`). */\n | { action: \"approve\"; reason: \"expected_email_match\" | \"yes_flag\" }\n /**\n * Ask the human. `question` is the exact prompt to write; `on` says WHERE to read the answer\n * — \"stdin\" when stdin is a terminal, \"controlling-terminal\" when it isn't but /dev/tty is\n * reachable (POSIX only).\n */\n | { action: \"prompt\"; question: string; on: \"stdin\" | \"controlling-terminal\" }\n /** Refuse: the token must NOT be persisted, and `message` says why + how to proceed. */\n | {\n action: \"reject\";\n reason: \"expected_email_mismatch\" | \"expected_email_unverifiable\" | \"non_interactive\";\n message: string;\n };\n\n/**\n * Do two addresses denote the SAME account? Trimmed + case-folded, and required to agree both\n * before AND after Unicode NFKC normalization.\n *\n * Why both: NFKC folds visually-distinct code points onto the same ASCII (fullwidth `e` → `e`,\n * ligatures, math letterforms). Comparing only normalized forms would let a server-reported\n * homoglyph address auto-approve against an ASCII pin — the exact wrong-account acceptance this\n * gate exists to stop. Comparing only raw forms would ignore normalization entirely. Requiring\n * agreement means any pair that matches on one side of normalization but not the other is\n * treated as a MISMATCH, which fails closed (the human is asked, or the pin refuses).\n */\nfunction sameEmail(a: string, b: string): boolean {\n const fold = (v: string): string => v.trim().toLowerCase();\n const rawEqual = fold(a) === fold(b);\n const nfkcEqual = fold(a.normalize(\"NFKC\")) === fold(b.normalize(\"NFKC\"));\n return rawEqual && nfkcEqual;\n}\n\n/**\n * Decide whether a freshly minted token may be trusted — the whole of the md60 gate, kept\n * pure so every branch is unit-testable and the command body stays a thin wrapper.\n *\n * Order matters:\n * 1. A pinned identity (`--expect-email`) is the strongest signal: it auto-approves on an\n * exact match and HARD-FAILS on a mismatch — a `--yes` alongside it must not override a\n * wrong account (that would turn the pin into a no-op).\n * A pin with no `approved_by_email` to check against also fails: unverifiable ≠ fine.\n * 2. `--yes` is the blunt escape hatch for CI: trust without asking.\n * 3. Otherwise ask — on stdin when it is a terminal, else on the CONTROLLING TERMINAL when one\n * is still reachable (pipe-backed stdio under a real terminal: MSYS/mintty Git Bash, some\n * wrappers). Only when there is genuinely no one to ask (`--non-interactive`, a script, CI)\n * does it fail closed — never hang, never auto-trust.\n */\nexport function decidePairConfirmation(input: PairConfirmInput): PairConfirmDecision {\n const { approvedByEmail, expectEmail } = input;\n const platform = input.platform ?? process.platform;\n\n if (expectEmail !== undefined) {\n if (approvedByEmail === undefined) {\n // The remedy depends on where the pin came from: a flag can be dropped, a config key\n // cannot (there is no --no-expect-email), so point at the file instead of a dead end.\n const remedy =\n input.expectEmailSource === \"config\"\n ? `Remove or correct the \"expectEmail\" key in ${input.configPath ?? \"the BirdyBeep CLI config\"} ` +\n \"(or upgrade the backend to one that reports the approving account) and re-run.\"\n : \"Re-run without --expect-email (and confirm interactively) if that is expected.\";\n return {\n action: \"reject\",\n reason: \"expected_email_unverifiable\",\n message:\n `Pairing refused: ${expectEmail} was pinned as the expected approving account, but the ` +\n \"server did not report which account approved this machine, so the pin could not be \" +\n `verified. The machine token was NOT stored. ${remedy}`,\n };\n }\n if (sameEmail(approvedByEmail, expectEmail)) {\n return { action: \"approve\", reason: \"expected_email_match\" };\n }\n return {\n action: \"reject\",\n reason: \"expected_email_mismatch\",\n message:\n `Pairing refused: this machine was approved by ${approvedByEmail}, but ${expectEmail} was ` +\n \"expected. The machine token was NOT stored. If you did not expect that account to approve \" +\n \"it, open BirdyBeep and revoke the machine, then re-run `birdybeep pair`.\",\n };\n }\n\n if (input.yes) return { action: \"approve\", reason: \"yes_flag\" };\n\n const question =\n approvedByEmail !== undefined\n ? `Pair this machine to ${approvedByEmail}? [y/N] `\n : \"The server did not report which account approved this machine. Pair anyway? [y/N] \";\n\n // `--non-interactive` is an explicit \"never prompt me\", so it outranks any terminal we could\n // reach. Otherwise stdin wins when it's a terminal; failing that we ask on the POSIX\n // controlling terminal, which is what makes pipe-backed shells usable. On Windows there is no\n // usable equivalent — see {@link canOpenControllingTerminal} — so it falls through to the\n // refusal below, which is fast and honest rather than a hang.\n if (!input.nonInteractive) {\n if (input.stdinIsTTY) return { action: \"prompt\", question, on: \"stdin\" };\n if (input.controllingTerminalAvailable) {\n return { action: \"prompt\", question, on: \"controlling-terminal\" };\n }\n }\n\n const who = approvedByEmail !== undefined ? ` (approved by ${approvedByEmail})` : \"\";\n // On Windows the usual cause is a non-ConPTY MSYS/mintty shell handing us pipe-backed stdio.\n // `winpty` attaches a real console — which makes stdin itself a TTY, so the ordinary prompt\n // path engages. Name it rather than leaving the user stuck.\n const winptyHint =\n platform === \"win32\" && !input.nonInteractive\n ? \" In Git Bash / MSYS, `winpty birdybeep pair` attaches a real console so the prompt can appear.\"\n : \"\";\n return {\n action: \"reject\",\n reason: \"non_interactive\",\n message:\n `Pairing needs confirmation${who}, but there is no terminal to ask on, so the machine ` +\n \"token was NOT stored. Re-run with `--expect-email <addr>` to pin the approving account \" +\n \"(recommended for CI), or `--yes` to accept whichever account approved it.\" +\n winptyHint,\n };\n}\n\n/** Is a prompt answer an explicit yes? Anything else (incl. empty/EOF) means no. */\nexport function isAffirmative(answer: string): boolean {\n return /^(y|yes)$/i.test(answer.trim());\n}\n\n/**\n * The device that reaches this process's CONTROLLING TERMINAL regardless of what stdin is wired\n * to. POSIX only — see {@link canOpenControllingTerminal} for why Windows is excluded.\n */\nexport function controllingTerminalPath(): string {\n return \"/dev/tty\";\n}\n\n/**\n * Can we open the controlling terminal for reading? Probe only — never throws, opens nothing\n * durable. Opening `/dev/tty` succeeds exactly when a terminal really is attached: in a CI job,\n * a daemon, or a detached session (`setsid`) it fails, which is the signal the gate needs.\n *\n * WINDOWS IS DELIBERATELY EXCLUDED. The obvious analogue is the `CONIN$` console device, and an\n * earlier revision used it — but measured on a windows-latest runner with fully piped stdio,\n * `CONIN$` OPENS and then READING it blocks forever. That turns the gate's \"fail closed, fast\"\n * guarantee into a 60s hang (caught by scripts/live-e2e-pair-headless.mjs before release), which\n * is strictly worse than the refusal it was meant to avoid: a script gets neither an answer nor\n * an error. Since opening it proves nothing on Windows, we don't. Windows users whose shell hands\n * the CLI pipe-backed stdio (MSYS/mintty Git Bash) run `winpty birdybeep pair`, which attaches a\n * real console — stdin then IS a TTY and the ordinary stdin path handles it, no fallback needed.\n */\nexport function canOpenControllingTerminal(\n path: string = controllingTerminalPath(),\n /** Explicit so tests can pin BOTH branches on any host, without patching process.platform. */\n platform: string = process.platform,\n): boolean {\n if (platform === \"win32\") return false;\n let fd: number | undefined;\n try {\n fd = openSync(path, \"r\");\n return true;\n } catch {\n return false; // no controlling terminal (script, CI, detached session) → caller fails closed\n } finally {\n if (fd !== undefined) {\n try {\n closeSync(fd);\n } catch {\n /* already gone */\n }\n }\n }\n}\n\n/**\n * Ask a question and read one line back. The question always goes to stderr (not stdout) so\n * `--json` output stays a clean NDJSON stream. EOF/close resolves to \"\" (a decline) so the CLI\n * can never hang waiting for an answer that will never come.\n *\n * `on: \"controlling-terminal\"` reads from /dev/tty instead of stdin — a human IS present but the\n * shell gave us pipe-backed stdio.\n *\n * WHY tty.ReadStream over an explicit fd, and not `createReadStream(\"/dev/tty\")`:\n * a plain fs read stream services reads on the libuv THREADPOOL. Once a read is issued it cannot\n * be cancelled — `destroy()` returns, but the `FSReqCallback` stays pending, and because `bin.ts`\n * sets `process.exitCode` (rather than calling `process.exit`) the event loop never drains. The\n * observable bug: after answering the prompt the CLI printed \"✓ Paired …\" and then HUNG until a\n * keypress or Ctrl-C — flatly contradicting the \"never hangs\" guarantee this gate is built on.\n * Measured: answer read at +9ms, process still alive at 20s with\n * `process._getActiveRequests() === ['FSReqCallback']`. A `tty.ReadStream` uses a poll-backed\n * handle instead, so closing the fd deterministically here ends the read and the process exits\n * (~0.06s). The fd is ours (we opened it), so we close it exactly once, on every path.\n */\nasync function promptForAnswer(\n question: string,\n on: \"stdin\" | \"controlling-terminal\",\n): Promise<string> {\n const { createInterface } = await import(\"node:readline/promises\");\n\n let ttyFd: number | undefined;\n let input: NodeJS.ReadableStream;\n if (on === \"stdin\") {\n input = process.stdin;\n } else {\n const { ReadStream } = await import(\"node:tty\");\n ttyFd = openSync(controllingTerminalPath(), \"r\");\n input = new ReadStream(ttyFd);\n }\n\n return new Promise<string>((resolve) => {\n const rl = createInterface({ input, output: process.stderr });\n let settled = false;\n const done = (value: string): void => {\n if (settled) return;\n settled = true;\n rl.close();\n if (on === \"stdin\") {\n // The interface resumed stdin; unref so a lingering TTY handle can't hold the process open.\n process.stdin.unref?.();\n } else {\n // Order matters: unref + destroy the handle, THEN close the fd. Both are wrapped because\n // destroying a tty stream may already have closed it — a double close must never throw\n // out of the prompt (that would turn a successful answer into a crash).\n try {\n (input as { unref?: () => void }).unref?.();\n (input as { destroy?: () => void }).destroy?.();\n } catch {\n /* stream already torn down */\n }\n if (ttyFd !== undefined) {\n try {\n closeSync(ttyFd);\n } catch {\n /* already closed by the stream */\n }\n }\n }\n resolve(value);\n };\n rl.question(question).then(done, () => done(\"\"));\n rl.once(\"close\", () => done(\"\"));\n input.once?.(\"error\", () => done(\"\")); // tty vanished mid-prompt → decline, never hang\n });\n}\n\nexport interface PairCommandDeps {\n fetchImpl?: typeof fetch;\n tokenOptions?: TokenStoreOptions;\n /** Injectable delay between polls (default real setTimeout; tests make it instant). */\n sleep?: (ms: number) => Promise<void>;\n /** Injectable clock for the expiry deadline (default Date.now). */\n now?: () => number;\n /** Render the QR payload as a matrix (default {@link renderQrMatrix} via uqr). */\n renderQr?: (qrPayload: string) => string;\n /** Whether stdout is an interactive terminal (default process.stdout.isTTY). The QR\n * matrix renders only on a TTY — piped output stays plain text. */\n isTTY?: boolean;\n pollIntervalMs?: number;\n /**\n * Whether stdin can answer a prompt (default process.stdin.isTTY). Drives the md60 confirm\n * gate's fail-closed branch — a piped/CI stdin never gets prompted.\n */\n isStdinTTY?: boolean;\n /**\n * Whether the controlling terminal is reachable when stdin is NOT a TTY (default: probe\n * /dev/tty; always false on Windows). Injected in tests so both branches are exercised\n * without needing a real terminal.\n */\n hasControllingTerminal?: () => boolean;\n /** Ask a question and read one line (default {@link promptForAnswer}); injected in tests. */\n promptLine?: (question: string, on: \"stdin\" | \"controlling-terminal\") => Promise<string>;\n /** The pinned identity from config (default: the `expectEmail` key of the CLI config). */\n configuredExpectEmail?: () => string | undefined;\n /**\n * The one-step chain that runs once the token is stored (gcgp.5): detect + install every\n * harness, print the per-build coverage table, send a real test Beep. On by default — that\n * chain IS the product's setup. `false` turns it off for tests about the pairing handshake\n * alone, which must never touch real adapters or the network.\n */\n setup?: SetupDeps | false;\n}\n\n/**\n * Run the post-pairing chain. ALWAYS returns a report — a failed one when the chain could not\n * complete — because the caller reads it for both the exit code and the `--json` object.\n *\n * Two things this must keep apart, because they have different causes and different fixes:\n *\n * - A HARNESS's adapter throwing is handled inside the chain, per adapter (a CLI that ships on\n * npm meets harnesses newer than itself, since users upgrade when they feel like it). It\n * becomes one `failed` ROW, the other harnesses are still wired up, and the run completes.\n * Never a crash on top of a machine that IS paired.\n * - THE CHAIN failing outright is what this catch is for, and it is NOT the same thing. It used\n * to return nothing, which the caller read as \"no setup ran\": exit 0, `setup` dropped from the\n * report. The human saw the failure and every machine consumer was told it succeeded.\n *\n * Pairing itself is untouched either way. The token is already stored by the time this runs, and\n * a genuine pairing must never be reported as a failure.\n */\nasync function runSetupChain(\n ctx: CommandContext,\n deps: SetupDeps,\n flags: PairFlags,\n): Promise<SetupReport> {\n try {\n return await runHarnessSetup(ctx, { sendTest: !flags.noTest }, deps);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n ctx.io.errline(\n `This machine is paired, but wiring up your coding agents failed: ${message}. ` +\n \"Run `birdybeep agent install all` to do it on its own, then `birdybeep doctor`.\",\n );\n return failedSetupReport(message);\n }\n}\n\n/** How the two verbs that run this flow differ — everything else about them is identical. */\ninterface PairingVerb {\n name: string;\n summary: string;\n usage: string;\n /** One-line \"start here\" hint; makes the verb the featured entry in the root help. */\n gettingStarted?: string;\n /**\n * Go straight to the harness half when a token already exists, instead of minting another one.\n * `setup` is the verb people re-run after installing a harness, and forcing a phone round-trip\n * for that would make the re-run advice this ticket prints a lie. `pair` always re-pairs.\n */\n skipWhenPaired: boolean;\n}\n\nfunction createPairingCommand(verb: PairingVerb, deps: PairCommandDeps = {}): Command {\n const fetchImpl = deps.fetchImpl ?? fetch;\n const sleep = deps.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n const clock = deps.now ?? (() => Date.now());\n const renderQr = deps.renderQr ?? renderQrMatrix;\n const intervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const promptLine = deps.promptLine ?? promptForAnswer;\n const hasControllingTerminal =\n deps.hasControllingTerminal ?? (() => canOpenControllingTerminal());\n // Tolerant read: the config file is user-editable, so a non-string/empty pin is treated as\n // \"no pin\" rather than crashing `pair` (or, worse, comparing against garbage).\n const configuredExpectEmail =\n deps.configuredExpectEmail ??\n ((): string | undefined => {\n const pinned: unknown = readCliConfig().expectEmail;\n return typeof pinned === \"string\" && pinned.trim().length > 0 ? pinned : undefined;\n });\n\n return {\n name: verb.name,\n summary: verb.summary,\n usage: verb.usage,\n ...(verb.gettingStarted !== undefined ? { gettingStarted: verb.gettingStarted } : {}),\n options: [\n {\n flag: \"--yes\",\n aliases: [\"-y\"],\n summary: \"Skip the approving-account confirmation (headless/CI)\",\n },\n {\n flag: \"--expect-email\",\n value: \"<addr>\",\n summary: \"Only trust the pairing if this account approved it (else fail)\",\n },\n {\n flag: \"--no-install\",\n summary: \"Stop after pairing; do not install coding-agent hooks\",\n },\n {\n flag: \"--no-test\",\n summary: \"Don't send the test Beep at the end\",\n },\n ],\n run: async (ctx) => {\n const pairFlags = parsePairFlags(ctx.args);\n if (pairFlags.error !== undefined) {\n ctx.io.errline(`birdybeep ${verb.name}: ${pairFlags.error}.`);\n return EXIT.USAGE;\n }\n const chain = deps.setup === false || pairFlags.noInstall ? undefined : (deps.setup ?? {});\n // The token store this command was given is the one the chain's test Beep has to read from,\n // so it carries through unless the caller pinned a different one for the chain itself.\n const setupDeps: SetupDeps = {\n ...(deps.tokenOptions !== undefined ? { tokenOptions: deps.tokenOptions } : {}),\n ...chain,\n };\n\n // `setup` re-run on an already-paired machine goes straight to the harness half. That is\n // the whole point of the \"install it, then run `birdybeep setup` again\" advice this flow\n // prints — a second phone round-trip for it would be busywork.\n if (verb.skipWhenPaired && (await getToken(deps.tokenOptions ?? {})) !== null) {\n ctx.io.line(\n chain !== undefined\n ? \"✓ Already paired. Checking installed coding-agent hooks.\"\n : \"✓ Already paired. Nothing else to do with --no-install.\",\n );\n // `undefined` here means the chain was never RUN (--no-install) — the only remaining\n // reason `setup` is absent from the report. A chain that ran and failed reports itself.\n const report =\n chain !== undefined ? await runSetupChain(ctx, setupDeps, pairFlags) : undefined;\n ctx.io.result({\n paired: true,\n alreadyPaired: true,\n ...(report !== undefined ? { setup: report } : {}),\n });\n return report !== undefined && !report.ok ? EXIT.ERROR : EXIT.OK;\n }\n\n const apiUrl = resolveApiUrl();\n const identity = getMachineIdentity(); // { label, os, fingerprintHash }\n // PKCE (dgxd): commit to a fresh random verifier by sending only its S256 challenge on\n // /pair/start; prove possession of the verifier on every /pair/token. The verifier is a\n // short-lived SECRET kept in memory for this `pair` run ONLY — never persisted to disk or\n // the token store. Binds the token mint to THIS CLI so an interceptor of the device_code\n // can't redeem it. A newer server enforces it; an older one ignores it (backward compatible).\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = deriveCodeChallengeS256(codeVerifier);\n const start = await pairStart(\n apiUrl,\n { machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION, codeChallenge },\n fetchImpl,\n );\n\n if (ctx.flags.json) {\n // NDJSON: emit the pairing info NOW so a script/agent can surface the complete\n // qr_payload for approval while we poll; the final success object is a later line.\n ctx.io.result({\n status: \"pairing_started\",\n user_code: start.user_code,\n qr_payload: start.qr_payload,\n expires_at: start.expires_at,\n });\n } else {\n // Approval needs the high-entropy fragment secret carried by the complete QR/link.\n // The short user_code remains visible only to identify the same pending session.\n ctx.io.line(\n \"To pair this machine, open the BirdyBeep app, tap “pair a machine”, and scan this QR or open the complete link:\",\n );\n // The matrix is TTY-only (a piped/CI consumer wants greppable lines, and\n // half-block art garbles logs); the link + code lines below ALWAYS print.\n const isTTY = deps.isTTY ?? process.stdout.isTTY === true;\n if (isTTY) ctx.io.line(renderQr(start.qr_payload));\n ctx.io.line(` Scan or open: ${start.qr_payload}`);\n ctx.io.line(\n ` Session code (display only; cannot approve by itself): ${start.user_code}`,\n );\n ctx.io.line(\"Waiting for approval in the BirdyBeep app.\");\n }\n\n // Poll /pair/token until approved (201), a TERMINAL error, or the window expires.\n const deadline = Date.parse(start.expires_at);\n const startedAt = clock();\n let lastBeat = startedAt;\n let paired: PairTokenResult | undefined;\n let terminal: Extract<PairTokenResult, { status: \"error\" }> | undefined;\n for (;;) {\n const nowMs = clock();\n if (nowMs >= deadline) break;\n await sleep(intervalMs);\n const poll = await pairTokenPoll(\n apiUrl,\n start.device_code,\n fetchImpl,\n identity.fingerprintHash,\n codeVerifier, // PKCE proof-of-possession (dgxd) — sent on every poll\n );\n if (poll.status === \"paired\") {\n paired = poll;\n break;\n }\n // A failure that waiting can't fix (e.g. the agent-install cap) must STOP the loop\n // and be shown — never masked as \"not approved yet\" so the prompt hangs silently.\n if (poll.status === \"error\" && !poll.retryable) {\n terminal = poll;\n break;\n }\n // Otherwise pending (not approved yet) or a transient server error → keep waiting,\n // reprinting a heartbeat so the prompt is visibly alive. Human-mode only (NDJSON\n // stays a clean two-line stream); time-gated on the clock so tests never see it.\n if (!ctx.flags.json && nowMs - lastBeat >= HEARTBEAT_MS) {\n ctx.io.line(\n poll.status === \"error\"\n ? ` Backend unavailable (${poll.message}); retrying.`\n : \" Waiting for approval in the BirdyBeep app.\",\n );\n lastBeat = nowMs;\n }\n }\n\n if (terminal !== undefined) {\n // NDJSON: a terminal result object on stderr+stdout so scripts see the reason code.\n ctx.io.result({ paired: false, reason: terminal.code });\n ctx.io.errline(`Pairing failed: ${terminal.message}`);\n return EXIT.ERROR;\n }\n\n if (paired === undefined || paired.status !== \"paired\") {\n // NDJSON contract: json mode gets a TERMINAL result object on every exit path,\n // so scripts can key off the last parseable line instead of only the exit code.\n ctx.io.result({ paired: false, reason: \"timeout\" });\n ctx.io.errline(\n \"Pairing timed out before you approved it. In the BirdyBeep app, tap “pair a machine”, scan a fresh QR or open its complete link, then run `birdybeep pair` again.\",\n );\n return EXIT.ERROR;\n }\n\n // ── md60: the confirm gate ────────────────────────────────────────────────────\n // The token is minted but NOT yet trusted. Before it is persisted, the human (or a\n // pinned identity) must confirm the account that approved this machine — so a\n // wrong-account or hijacked approval is caught at trust time, before any event flows.\n // Nothing below this point runs unless the gate approves: no token, no config write.\n const approvedBy = paired.approvedByEmail;\n // The flag wins over the config pin, so a one-off `pair` can override a fleet default.\n const expectEmail = pairFlags.expectEmail ?? configuredExpectEmail();\n const stdinIsTTY = deps.isStdinTTY ?? process.stdin.isTTY === true;\n const decision = decidePairConfirmation({\n ...(approvedBy !== undefined ? { approvedByEmail: approvedBy } : {}),\n ...(expectEmail !== undefined ? { expectEmail } : {}),\n ...(expectEmail !== undefined\n ? { expectEmailSource: pairFlags.expectEmail !== undefined ? \"flag\" : \"config\" }\n : {}),\n yes: pairFlags.yes,\n nonInteractive: ctx.flags.nonInteractive,\n stdinIsTTY,\n // Probed ONLY when stdin can't answer — opening /dev/tty is a syscall, and when stdin is\n // already a terminal the answer is irrelevant.\n controllingTerminalAvailable: stdinIsTTY ? false : hasControllingTerminal(),\n configPath: cliConfigPath(),\n });\n\n if (decision.action === \"reject\") {\n ctx.io.result({ paired: false, reason: decision.reason });\n ctx.io.errline(decision.message);\n return EXIT.ERROR;\n }\n if (\n decision.action === \"prompt\" &&\n !isAffirmative(await promptLine(decision.question, decision.on))\n ) {\n ctx.io.result({ paired: false, reason: \"declined\" });\n ctx.io.errline(\n \"Pairing declined. The machine token was NOT stored, and this machine will send no \" +\n \"events. The machine may still appear in the BirdyBeep app; revoke it there if you \" +\n \"did not intend to pair it.\",\n );\n return EXIT.ERROR;\n }\n\n // Confirmed. Durable token → secure store ONLY. Non-secret apiUrl → config. Never the reverse.\n await setToken(paired.machineToken, deps.tokenOptions ?? {});\n writeCliConfig({ apiUrl });\n\n // COLD-START GUARD (gcgp.4). A token has just appeared, so the very next hook fire would\n // drain whatever is on the local queue. Everything queued BEFORE this instant belongs to a\n // machine that had nowhere to send it; replaying it means a phone full of notifications\n // about work that finished yesterday, and — because the backend's storm summariser runs\n // ahead of its notify decision — real pushes even for event types that never beep. So the\n // pre-pairing backlog is discarded here, once, at the moment the token is stored. Anything\n // enqueued from now on is an ordinary offline retry and still delivers.\n const discarded = new LocalEventQueue().discardBefore(clock());\n clearUnpairedNotice(); // the \"events went nowhere\" warning has been answered\n\n // Surface the approving account (dgxd) when the server reports it, so the trusted\n // identity is on the record. Additive: absent from older servers.\n const humanSuffix = approvedBy !== undefined ? ` to ${approvedBy}` : \"\";\n const discardedSuffix =\n discarded > 0\n ? ` Discarded ${discarded} event(s) queued before pairing. They will not produce notifications.`\n : \"\";\n // gcgp.5: pairing is the WHOLE of setup, so the token is not the end of the run — the\n // harness half follows and the line above it just says what happened. The old copy pointed\n // at `birdybeep test`, which is why a user could pair, get a Beep, and stop with nothing\n // wired up. It only points anywhere now when the chain has been turned off.\n const nextStep =\n chain === undefined ? \" Run `birdybeep setup` to install coding-agent hooks.\" : \"\";\n ctx.io.line(`✓ Paired${humanSuffix}.${nextStep}${discardedSuffix}`);\n\n const report =\n chain !== undefined ? await runSetupChain(ctx, setupDeps, pairFlags) : undefined;\n\n // The pairing is reported as the success it was; the chain reports itself separately, so a\n // failed chain can never ride out on pairing's exit code.\n ctx.io.result({\n paired: true,\n machineId: paired.machineId,\n discardedPrePairingEvents: discarded,\n ...(approvedBy !== undefined ? { approvedByEmail: approvedBy } : {}),\n ...(report !== undefined ? { setup: report } : {}),\n });\n return report !== undefined && !report.ok ? EXIT.ERROR : EXIT.OK;\n },\n };\n}\n\n/**\n * `birdybeep pair` — mint a machine token, then wire up every coding agent on the machine.\n * Always re-pairs, even when a token is already present.\n */\nexport function createPairCommand(deps: PairCommandDeps = {}): Command {\n return createPairingCommand(\n {\n name: \"pair\",\n summary: \"Pair this machine and install detected coding-agent hooks\",\n usage: \"birdybeep pair [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]\",\n skipWhenPaired: false,\n },\n deps,\n );\n}\n\n/**\n * `birdybeep setup` — the same flow under the verb people look for. `pair` describes the\n * handshake; `setup` describes the job, and it is the one the root help features. It skips\n * straight to the harness half on a machine that already has a token, so re-running it after\n * installing a new harness costs nothing.\n */\nexport function createSetupCommand(deps: PairCommandDeps = {}): Command {\n return createPairingCommand(\n {\n name: \"setup\",\n summary: \"Set up BirdyBeep on this machine\",\n usage: \"birdybeep setup [--yes] [--expect-email <addr>] [--no-install] [--no-test] [--json]\",\n gettingStarted: \"Connect this machine and install hooks for detected coding agents.\",\n skipWhenPaired: true,\n },\n deps,\n );\n}\n","/**\n * CLI pairing client — the device-code flow (§7.2/§13.4). `pairStart` opens a session via\n * `POST /v1/pair/start`; the CLI shows the complete `qr_payload` plus a display-only\n * `user_code`, then polls\n * `POST /v1/pair/token` (`pairTokenPoll`) until it returns 201 `{ machine_token, machine_id }`\n * or the `expires_at` deadline. A `validation_failed`/4xx during polling means \"not approved\n * yet — keep polling\". Per SPEC §11 the QR/link carries short-lived pairing info plus its\n * approval secret, NEVER a durable token; the user code alone cannot approve. Request/response\n * shapes are mirrored from the product (agent-core).\n */\nimport {\n type ErrorCode,\n errorEnvelopeSchema,\n type PairStartResponse,\n pairStartResponseSchema,\n pairTokenResponseSchema,\n} from \"@birdybeep/agent-core\";\n\nfunction base(apiUrl: string): string {\n return apiUrl.replace(/\\/$/, \"\");\n}\n\nexport interface PairStartInput {\n /** Required — the human machine label (derived from hostname/OS). */\n machineLabel: string;\n os?: string;\n cliVersion?: string;\n /**\n * PKCE S256 challenge = base64url(sha256(codeVerifier)) (dgxd). When present, the session is\n * BOUND to this CLI: the product's `/pair/token` then requires the matching `codeVerifier`.\n * Omit to keep the legacy device_code-only path (backward compatible with older servers).\n */\n codeChallenge?: string;\n}\n\n/** Begin a pairing session (`POST /v1/pair/start`, unauthenticated). */\nexport async function pairStart(\n apiUrl: string,\n input: PairStartInput,\n fetchImpl: typeof fetch,\n): Promise<PairStartResponse> {\n const body = {\n machine_label: input.machineLabel,\n ...(input.os !== undefined ? { os: input.os } : {}),\n ...(input.cliVersion !== undefined ? { cli_version: input.cliVersion } : {}),\n ...(input.codeChallenge !== undefined ? { code_challenge: input.codeChallenge } : {}),\n };\n const res = await fetchImpl(`${base(apiUrl)}/v1/pair/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(`pairing could not be started (HTTP ${res.status})`);\n const parsed = pairStartResponseSchema.safeParse(await res.json());\n if (!parsed.success) throw new Error(\"pairing start returned an unexpected response shape\");\n return parsed.data;\n}\n\nexport type PairTokenResult =\n | { status: \"pending\" }\n | { status: \"paired\"; machineToken: string; machineId: string; approvedByEmail?: string }\n /**\n * The backend returned an outcome that will NOT resolve by waiting (`retryable: false`,\n * e.g. `quota_exceeded` — the install cap is hit) or a transient server-side failure\n * (`retryable: true`, e.g. `internal_error`/5xx). Surfacing these is what stops `pair`\n * from masking a real error as \"not approved yet\" and hanging silently until timeout.\n */\n | { status: \"error\"; code: ErrorCode | \"unknown\"; message: string; retryable: boolean };\n\n/**\n * Terminal error codes on `/v1/pair/token`: waiting can never turn them into a 201, so the\n * CLI must STOP polling and show the user the reason. `quota_exceeded` (the agent-install cap)\n * is the one a real user actually hits; the auth-shaped codes should never occur on this\n * unauthenticated endpoint but are treated as terminal defensively (never loop forever).\n */\nconst TERMINAL_TOKEN_ERRORS: ReadonlySet<ErrorCode> = new Set<ErrorCode>([\n \"quota_exceeded\",\n \"unauthorized\",\n \"forbidden\",\n \"token_revoked\",\n \"not_found\",\n \"payload_too_large\",\n]);\n\n/**\n * Poll once for the device token (`POST /v1/pair/token`, unauthenticated). Outcomes:\n * - 201 with a valid token body → `paired`.\n * - `validation_failed`/4xx (the documented \"not approved yet\" signal) → `pending`, so the\n * caller keeps polling until the `expires_at` deadline.\n * - a TERMINAL error (e.g. `quota_exceeded`) → `error` with `retryable: false` — the caller\n * surfaces it and stops, instead of hanging silently on a failure waiting can't fix.\n * - `rate_limited`/`internal_error`/5xx/unparseable → `error` with `retryable: true` — the\n * caller keeps polling (transient) but can warn if it persists.\n */\nexport async function pairTokenPoll(\n apiUrl: string,\n deviceCode: string,\n fetchImpl: typeof fetch,\n machineFingerprint?: string,\n /**\n * PKCE verifier (dgxd) — the secret whose sha256 was committed as `code_challenge` on\n * `/pair/start`. Sent on EVERY poll: when the session was started with a challenge the server\n * requires it and checks `sha256Base64Url(verifier) === stored challenge`. Held in memory only;\n * never written to disk. Omit for a legacy (no-challenge) session.\n */\n codeVerifier?: string,\n): Promise<PairTokenResult> {\n const body = {\n device_code: deviceCode,\n ...(machineFingerprint !== undefined ? { machine_fingerprint: machineFingerprint } : {}),\n ...(codeVerifier !== undefined ? { code_verifier: codeVerifier } : {}),\n };\n const res = await fetchImpl(`${base(apiUrl)}/v1/pair/token`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (res.ok) {\n const parsed = pairTokenResponseSchema.safeParse(await res.json());\n if (!parsed.success) return { status: \"pending\" };\n return {\n status: \"paired\",\n machineToken: parsed.data.machine_token,\n machineId: parsed.data.machine_id,\n // Only surface the key when the server reported it (exactOptionalPropertyTypes: no explicit\n // undefined). Older servers omit approved_by_email; newer ones (dgxd) include it.\n ...(parsed.data.approved_by_email !== undefined\n ? { approvedByEmail: parsed.data.approved_by_email }\n : {}),\n };\n }\n\n // Non-2xx: read the typed §13.4 error envelope to tell \"not approved yet\" (keep polling)\n // apart from a real failure the user must see. A body that isn't a parseable envelope falls\n // back to the status code.\n let errBody: unknown = null;\n try {\n errBody = await res.json();\n } catch {\n /* empty / non-JSON error body → classify by status below */\n }\n const env = errorEnvelopeSchema.safeParse(errBody);\n const code = env.success ? env.data.error.code : undefined;\n\n // \"not approved yet\" is the documented benign signal → keep polling. Also treat any\n // unclassifiable 4xx (except 429) as pending, preserving the endpoint's historical\n // accept-and-keep-waiting behavior.\n if (\n code === \"validation_failed\" ||\n (code === undefined && res.status >= 400 && res.status < 500 && res.status !== 429)\n ) {\n return { status: \"pending\" };\n }\n\n const message = env.success ? env.data.error.message : `pairing failed (HTTP ${res.status})`;\n if (code !== undefined && TERMINAL_TOKEN_ERRORS.has(code)) {\n return { status: \"error\", code, message, retryable: false };\n }\n // rate_limited / internal_error / any 5xx / unrecognized → transient; safe to keep polling.\n return { status: \"error\", code: code ?? \"unknown\", message, retryable: true };\n}\n","/**\n * The one-step setup chain (birdybeep-agent-gcgp.5): everything `birdybeep setup` (and\n * `birdybeep pair`) does once a machine token exists — detect every supported harness, install\n * the ones that are present, and print a per-BUILD coverage table, then send a real test Beep.\n *\n * Pairing on its own wired nothing up. `pair` ended at \"Run `birdybeep test`\", the test Beep\n * arrived, and the machine looked finished with zero harnesses installed. So the chain lives\n * here and both verbs run it.\n *\n * Everything that can still stop a beep is a ROW in the table or a line under one, never\n * swallowed: Codex's one-time `/hooks` trust (and, after gcgp.15, the migration that turns\n * turn-complete off until it is granted), OpenCode's restart, a `notify` slot another tool owns,\n * a build that has never fired, an install that threw, a harness that is not installed at all.\n *\n * Adapters / sender / token store / observed-build tally are injectable, so the whole chain runs\n * hermetically against real adapters under a temp HOME.\n */\nimport {\n type AgentAdapter,\n type HarnessSurfaceKind,\n type InstallResult,\n type IntegrationStatus,\n type Sender,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { copilotAdapter } from \"@birdybeep/copilot\";\nimport { cursorAdapter } from \"@birdybeep/cursor\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport {\n describeSurface,\n gatherSurfaces,\n type HarnessSurfaces,\n type SurfaceCoverageOptions,\n surfaceRemedy,\n type SurfaceState,\n} from \"../diagnostics\";\nimport { type GlobalFlags, type Io } from \"../framework\";\nimport { installTarget } from \"./agent\";\nimport { createTestCommand } from \"./test\";\n\nexport const SETUP_ADAPTERS: readonly AgentAdapter[] = [\n claudeCodeAdapter,\n codexAdapter,\n opencodeAdapter,\n cursorAdapter,\n copilotAdapter,\n];\n\n/**\n * What a row of the coverage table says about one build.\n *\n * `ready` and `beeping` are both wired; they differ in whether anything has come through yet, and\n * on a fresh machine every row is `ready`. The other four each have a different fix, which is why\n * they are not collapsed into one \"broken\".\n */\nexport type SetupState =\n /** Events from this build have already reached BirdyBeep. */\n | \"beeping\"\n /** Wired up; it beeps on the next turn. */\n | \"ready\"\n /** Installed, but a one-time user action (Codex `/hooks`, an OpenCode restart) is pending. */\n | \"needs you\"\n /** Installed for other builds, but this one cannot beep — see its remedy. */\n | \"not covered\"\n /** The harness is not on this machine. */\n | \"not installed\"\n /** Detection or install threw. */\n | \"failed\";\n\n/** One line of the coverage table: a single build of a single harness. */\nexport interface SetupRow {\n harness: string;\n displayName: string;\n /** The build, e.g. \"terminal CLI 2.1.227\". Absent when the harness is not installed. */\n build?: string;\n kind?: HarnessSurfaceKind;\n state: SetupState;\n /** A fix that applies to THIS build only (gcgp.6's per-surface remedy). */\n remedy?: string;\n}\n\n/** Everything the chain did to one harness, plus the rows it produced. */\nexport interface SetupHarnessReport {\n harness: string;\n displayName: string;\n detected: boolean;\n status?: IntegrationStatus;\n changedFiles?: string[];\n backupFiles?: string[];\n /** What the user must still do for this harness, in the adapter's own words. */\n actions: string[];\n /** Present when detect() or install() threw — the run continues, the row says `failed`. */\n error?: string;\n rows: SetupRow[];\n}\n\nexport interface SetupReport {\n harnesses: SetupHarnessReport[];\n counts: { installed: number; needsYou: number; notInstalled: number; failed: number };\n /** The `birdybeep test` result, when the chain sent one. */\n beep?: unknown;\n /**\n * The chain itself could not complete — nothing here was graded, so `harnesses` is empty.\n *\n * DISTINCT from a harness whose adapter threw: that is one `failed` ROW inside `harnesses`\n * carrying its own `error`, and the rest of the run still finished. Attributing an adapter's\n * fault to the chain (or the reverse) sends the reader to the wrong place, so the two never\n * share a field.\n */\n error?: string;\n /** False when a harness install failed, the test Beep was rejected, or the chain itself failed. */\n ok: boolean;\n}\n\n/**\n * The report for a chain that failed outright (birdybeep-agent-gcgp.5, Codex review of #66).\n *\n * There must ALWAYS be a report. Returning nothing read as \"no setup was attempted\": the caller\n * exited 0 and dropped `setup` from the `--json` object, so a human saw the failure on screen\n * while CI and every script were told the machine was wired up. A failure that only reaches a\n * human is the silent drop this epic exists to remove, pointed the other way.\n */\nexport function failedSetupReport(error: string): SetupReport {\n return {\n harnesses: [],\n counts: { installed: 0, needsYou: 0, notInstalled: 0, failed: 0 },\n error,\n ok: false,\n };\n}\n\nexport interface SetupDeps {\n /** Adapter set (tests inject deterministic detection). Defaults to every supported harness. */\n adapters?: AgentAdapter[];\n tokenOptions?: TokenStoreOptions;\n /** Build the sender for the closing test Beep (tests inject a stub). */\n createSender?: (baseUrl: string) => Sender;\n /** Where the observed-builds tally lives (tests point it at a sandbox). */\n surfaceOptions?: SurfaceCoverageOptions;\n}\n\nexport interface SetupOptions {\n /** Send the closing test Beep through the real sender path (`--no-test` turns it off). */\n sendTest: boolean;\n}\n\n/** Statuses that mean a one-time user action stands between the install and the first beep. */\nconst PENDING_STATUSES: ReadonlySet<IntegrationStatus> = new Set<IntegrationStatus>([\n \"needs_trust\",\n \"needs_restart\",\n]);\n\ninterface HarnessInstall {\n adapter: AgentAdapter;\n detected: boolean;\n result?: InstallResult;\n error?: string;\n}\n\n/**\n * Detect every adapter and install the ones that are there. An adapter that throws is recorded\n * and the loop continues: one broken harness must not cost the user the other four, and the\n * failure has to reach the table rather than the exit code alone.\n */\nasync function installDetected(adapters: readonly AgentAdapter[]): Promise<HarnessInstall[]> {\n const installs: HarnessInstall[] = [];\n for (const adapter of adapters) {\n try {\n const detection = await adapter.detect();\n if (!detection.detected) {\n installs.push({ adapter, detected: false });\n continue;\n }\n installs.push({ adapter, detected: true, result: await adapter.install() });\n } catch (err) {\n installs.push({\n adapter,\n detected: true,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return installs;\n}\n\n/** The state of one build, given what its harness's install did and what gcgp.6 graded it. */\nfunction rowState(\n state: SurfaceState,\n group: HarnessSurfaces,\n status: IntegrationStatus | undefined,\n): SetupState {\n if (status === \"error\" || group.status === \"error\") return \"failed\";\n if (status !== undefined && PENDING_STATUSES.has(status)) return \"needs you\";\n if (state.coverage === \"active\") return \"beeping\";\n if (state.coverage === \"wired\") return \"ready\";\n // `uncovered` right after a successful install means this build specifically has never fired\n // while a sibling of the same harness is delivering — gcgp.6 grades that, and owns the fix.\n return \"not covered\";\n}\n\n/** Turn the installs plus gcgp.6's surface grading into the table's rows. */\nexport function buildHarnessReports(\n installs: HarnessInstall[],\n groups: HarnessSurfaces[],\n): SetupHarnessReport[] {\n return installs.map((install) => {\n const { adapter } = install;\n const base = {\n harness: adapter.id,\n displayName: adapter.displayName,\n detected: install.detected,\n ...(install.result !== undefined\n ? {\n status: install.result.status,\n changedFiles: install.result.changedFiles,\n backupFiles: install.result.backupFiles,\n }\n : {}),\n ...(install.error !== undefined ? { error: install.error } : {}),\n };\n\n if (install.error !== undefined) {\n return {\n ...base,\n actions: [\n `${adapter.displayName} could not be set up: ${install.error}`,\n `Run \\`birdybeep agent install ${installTarget(adapter.id)}\\` to retry it on its own.`,\n ],\n rows: [{ harness: adapter.id, displayName: adapter.displayName, state: \"failed\" as const }],\n };\n }\n\n if (!install.detected) {\n return {\n ...base,\n actions: [],\n rows: [\n {\n harness: adapter.id,\n displayName: adapter.displayName,\n state: \"not installed\" as const,\n },\n ],\n };\n }\n\n const group = groups.find((g) => g.harness === adapter.id);\n const status = install.result?.status;\n const surfaces = group?.surfaces ?? [];\n // No surface list means the adapter does not enumerate builds (or its probe failed). The\n // harness still gets one row — silently dropping it would read as \"not supported\".\n const rows: SetupRow[] =\n group === undefined || surfaces.length === 0\n ? [\n {\n harness: adapter.id,\n displayName: adapter.displayName,\n state:\n status !== undefined && PENDING_STATUSES.has(status)\n ? (\"needs you\" as const)\n : (\"ready\" as const),\n },\n ]\n : surfaces.map((state) => {\n const graded = rowState(state, group, status);\n // gcgp.6 returns no per-surface remedy when the harness-level status is the cause —\n // it expects `doctor`'s own harness check to carry the fix, and setup prints no such\n // check, so the row would otherwise say \"not covered\" and stop there.\n const remedy =\n surfaceRemedy(state, group) ??\n (graded === \"not covered\"\n ? `${adapter.displayName} carries no BirdyBeep hooks. Re-run \\`birdybeep agent install ${installTarget(adapter.id)}\\` from a shell where \\`birdybeep\\` resolves.`\n : undefined);\n return {\n harness: adapter.id,\n displayName: adapter.displayName,\n build: describeSurface(state),\n kind: state.surface.kind,\n state: graded,\n ...(remedy !== undefined ? { remedy } : {}),\n };\n });\n\n return { ...base, actions: [...(install.result?.requiredActions ?? [])], rows };\n });\n}\n\n/** Pad to `width`, never truncating — a long build name pushes its row out rather than losing it. */\nfunction pad(text: string, width: number): string {\n return text.length >= width ? text : text + \" \".repeat(width - text.length);\n}\n\nconst MARKS: Record<SetupState, string> = {\n beeping: \"✓\",\n ready: \"✓\",\n \"needs you\": \"!\",\n \"not covered\": \"✗\",\n \"not installed\": \"–\",\n failed: \"✗\",\n};\n\n/** The coverage table: one row per installed build, and one per harness that is not installed. */\nexport function renderCoverageTable(reports: SetupHarnessReport[]): string[] {\n const rows = reports.flatMap((r) => r.rows);\n const nameWidth = Math.max(7, ...rows.map((r) => r.displayName.length));\n const buildWidth = Math.max(5, ...rows.map((r) => (r.build ?? \"—\").length));\n\n const lines = [\"coverage\", ` ${pad(\"harness\", nameWidth)} ${pad(\"build\", buildWidth)} state`];\n for (const report of reports) {\n for (const row of report.rows) {\n lines.push(\n `${MARKS[row.state]} ${pad(row.displayName, nameWidth)} ${pad(row.build ?? \"—\", buildWidth)} ${row.state}`,\n );\n if (row.remedy !== undefined) lines.push(` → ${row.remedy}`);\n }\n for (const action of report.actions) lines.push(` → ${action}`);\n }\n return lines;\n}\n\n/**\n * What to tell someone about the harnesses that are not here. A machine with none of them is the\n * dead end this ticket exists to close: the run must say what to install and that re-running\n * finishes the job, not just print five skips.\n */\nexport function describeMissing(reports: SetupHarnessReport[]): string[] {\n const missing = reports.filter((r) => !r.detected && r.error === undefined);\n if (missing.length === 0) return [];\n const names = missing.map((r) => r.displayName);\n if (missing.length === reports.length) {\n return [\n \"No supported coding agent is installed on this machine.\",\n `Install one of ${names.join(\", \")}, then run \\`birdybeep setup\\` again.`,\n ];\n }\n return [\n `Not installed: ${names.join(\", \")}. Install any of them, then run \\`birdybeep setup\\` again.`,\n ];\n}\n\n/**\n * Run the post-pairing half of setup: install every detected harness, print the coverage table,\n * and (unless turned off) send a real test Beep through the production sender path.\n *\n * Prints nothing under `--json` — the caller folds the returned report into its own result object\n * so the stream stays one terminal line per command.\n */\nexport async function runHarnessSetup(\n ctx: { io: Io; flags: GlobalFlags },\n options: SetupOptions,\n deps: SetupDeps = {},\n): Promise<SetupReport> {\n const adapters = deps.adapters ?? [...SETUP_ADAPTERS];\n const installs = await installDetected(adapters);\n // Graded AFTER the install, so `status` reflects the config we just wrote.\n const groups = await gatherSurfaces(adapters, deps.surfaceOptions ?? {});\n const reports = buildHarnessReports(installs, groups);\n\n ctx.io.line(\"\");\n for (const line of renderCoverageTable(reports)) ctx.io.line(line);\n const missing = describeMissing(reports);\n if (missing.length > 0) {\n ctx.io.line(\"\");\n for (const line of missing) ctx.io.line(line);\n }\n\n const counts = {\n installed: reports.filter((r) => r.detected && r.error === undefined).length,\n needsYou: reports.filter((r) => r.rows.some((row) => row.state === \"needs you\")).length,\n notInstalled: reports.filter((r) => !r.detected && r.error === undefined).length,\n // A row that graded `failed` counts too: an adapter that returned status \"error\" never threw,\n // so counting only thrown errors would report a clean run over a harness that is broken.\n failed: reports.filter((r) => r.error !== undefined || r.rows.some((x) => x.state === \"failed\"))\n .length,\n };\n\n let beep: unknown;\n let beepOk = true;\n if (options.sendTest) {\n ctx.io.line(\"\");\n // The REAL `test` command, so the closing Beep exercises exactly the path a hook does.\n // Its `--json` result is captured rather than printed: setup emits one object, not two.\n const command = createTestCommand({\n ...(deps.createSender !== undefined ? { createSender: deps.createSender } : {}),\n ...(deps.tokenOptions !== undefined ? { tokenOptions: deps.tokenOptions } : {}),\n });\n const beepIo: Io = {\n ...ctx.io,\n result: (value: unknown) => {\n beep = value;\n },\n };\n beepOk = (await command.run?.({ args: [], flags: ctx.flags, io: beepIo })) === 0;\n }\n\n return {\n harnesses: reports,\n counts,\n ...(beep !== undefined ? { beep } : {}),\n ok: counts.failed === 0 && beepOk,\n };\n}\n","/**\n * `birdybeep test` (§7.1, §9.4) — send a representative test event through the REAL sender\n * path (normalize/redact/truncate → send w/ short timeout → queue-on-fail → opportunistic\n * drain) so a developer can confirm end-to-end delivery (and trigger a test Beep) right\n * after pairing. Not a mock — it exercises the production code path. Reports delivered vs\n * NOT PAIRED vs queued vs rejected, and for a queued event which of the three causes parked it\n * (offline / the backend asked for a retry / the token store would not answer); --json mirrors\n * the outcome and the cause.\n *\n * Sends event_type \"test\" (9fh): the backend notifies it by default. (The old \"custom\" type is\n * unconditionally suppressed by the §10.5 matrix — every test \"succeeded\" while no push could\n * ever be sent.) It is METERED against the monthly beep quota like any other event on this route:\n * the exemption was removed backend-side (cjrj) because event_type is client-controlled, so an\n * exemption keyed on it was a bypass. A `test` on an exhausted account is therefore rejected, and\n * says so (58l). The session id is unique per run so back-to-back tests don't collapse in the\n * backend's dedupe window, and the CLI reports the backend's actual DECISION instead of assuming\n * a beep.\n */\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n type BirdyBeepAgentEvent,\n createSender as defaultCreateSender,\n describeExhaustedQuota,\n fetchPushReachability,\n getMachineIdentity,\n normalizeEvent,\n type NormalizeOptions,\n type Sender,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\n/** Build the canonical test event (event_type `test`, unique session per run). cwd is hashed by the normalizer. */\nexport function buildTestEvent(opts: NormalizeOptions = {}): BirdyBeepAgentEvent {\n const machine = getMachineIdentity();\n return normalizeEvent(\n {\n event_type: \"test\",\n status: \"running\",\n harness: \"claude_code\", // schema requires a harness; the \"test\" type distinguishes it\n // Unique per run: a repeat `birdybeep test` inside the backend's dedupe window must\n // still beep — a constant id made the second test silently \"deduped\" (9fh).\n source_session_id: `birdybeep-cli-test-${randomUUID()}`,\n machine: { label: machine.label, os: machine.os },\n workspace: { cwd: process.cwd() },\n title: \"BirdyBeep test\",\n body: \"Notifications from this machine are working.\",\n metadata: { test: true },\n },\n opts,\n );\n}\n\nexport interface TestCommandDeps {\n createSender?: (baseUrl: string) => Sender;\n tokenOptions?: TokenStoreOptions;\n /** fetch used by the push-reachability read (injected in tests). */\n fetchImpl?: typeof fetch;\n /**\n * Base URL for BOTH the send and the reachability read. One value on purpose: injecting a\n * sender that points at a stub while the reachability read still resolved the REAL API would\n * make tests reach the network, and would report on a different account than the one under test.\n */\n baseUrl?: string;\n}\n\nexport function createTestCommand(deps: TestCommandDeps = {}): Command {\n const makeSender =\n deps.createSender ??\n ((baseUrl) =>\n defaultCreateSender(\n deps.tokenOptions ? { baseUrl, tokenOptions: deps.tokenOptions } : { baseUrl },\n ));\n\n return {\n name: \"test\",\n summary: \"Send a test event end-to-end\",\n usage: \"birdybeep test [--json]\",\n run: async (ctx) => {\n const event = buildTestEvent();\n const baseUrl = deps.baseUrl ?? resolveApiUrl();\n const result = await makeSender(baseUrl).send(event); // real path; also drains the queue\n\n if (ctx.flags.json) {\n ctx.io.result({\n outcome: result.outcome,\n ...(result.status ? { status: result.status } : {}),\n ...(result.decision ? { decision: result.decision } : {}),\n ...(result.queueCause ? { queueCause: result.queueCause } : {}),\n ...(result.tokenStoreUnavailable !== undefined\n ? { tokenStore: \"unavailable\", tokenStoreReason: result.tokenStoreUnavailable.reason }\n : {}),\n });\n } else if (result.outcome === \"delivered\") {\n // The 202 body says what the backend DECIDED — \"delivered\" alone only means\n // \"accepted\". Claiming a beep that was suppressed is how 9fh went unnoticed.\n if (result.decision === \"notified\" || result.decision === undefined) {\n // \"delivered\" means the BACKEND accepted it and enqueued a push. It says nothing about\n // whether a device exists to receive one — and this line used to promise a Beep on a\n // machine whose account had no reachable device at all, which is precisely the state\n // that took hours to find (birdybeep-agent-oi3). Ask, and say what is true.\n const reach = await fetchPushReachability({\n baseUrl,\n ...(deps.tokenOptions ? { tokenOptions: deps.tokenOptions } : {}),\n ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),\n });\n if (reach.state === \"ok\" && reach.data.active_device_count === 0) {\n ctx.io.line(\n \"⚠ No active device can receive a Beep. Open BirdyBeep on your phone and sign in \" +\n \"to register it. If the device limit is full, free a slot in Settings › devices.\",\n );\n } else if (reach.state === \"ok\") {\n ctx.io.line(\n `✓ Test event accepted for ${String(reach.data.active_device_count)} registered ` +\n \"device(s). Check your phone for a test Beep.\",\n );\n } else {\n // Could not ask. Do not upgrade that into a promise.\n ctx.io.line(\"✓ Test event accepted by the backend. Check your phone for a test Beep.\");\n }\n } else if (result.decision === \"suppressed\") {\n ctx.io.line(\n \"⚠ Test event accepted; push suppressed. Check machine and integration mutes in the app.\",\n );\n } else if (result.decision === \"deduped\") {\n ctx.io.line(\"⚠ Test event matched a recent duplicate. Retry in 30 seconds.\");\n } else {\n ctx.io.line(\n `⚠ Test event accepted; push decision was \"${result.decision}\". ` +\n \"Run `birdybeep doctor`.\",\n );\n }\n } else if (result.outcome === \"unpaired\") {\n // gcgp.4: this said \"Offline — test event queued\" on a machine that was online and\n // merely unpaired, and exited 0. `test` is the one command whose entire job is to tell\n // you why beeps aren't arriving; naming the wrong cause is worse than saying nothing.\n ctx.io.line(\n \"✗ This machine is not paired. Run `birdybeep pair`. No event was sent or queued.\",\n );\n } else if (result.outcome === \"failed\") {\n // 9u0: a retryable send plus an unwritable queue is loss, not \"queued\". Say so before\n // token-store handling, because that path can also fail to persist its event.\n ctx.io.line(\n \"✗ The test event could not be sent or saved locally. Check BirdyBeep's data-directory \" +\n \"permissions, then retry.\",\n );\n } else if (result.tokenStoreUnavailable !== undefined) {\n // gcgp.23: the machine is online and may well be paired — the token store just would\n // not answer, so neither \"Offline\" nor \"NOT PAIRED\" names the real cause.\n ctx.io.line(\n `• The machine token is unreadable (${result.tokenStoreUnavailable.reason}). The test ` +\n \"event is queued. Restore token-store access, then retry.\",\n );\n } else if (result.outcome === \"queued\" && result.queueCause === \"backend\") {\n // 0yk: rate_limited / internal_error / any 5xx also queue, and this branch used to send\n // the user off to debug a network that had just carried the request to the backend and\n // back. Name what answered, and say the retry is automatic.\n const status = result.status !== undefined ? ` HTTP ${String(result.status)}` : \" an error\";\n ctx.io.line(`• Backend returned${status}. The test event is queued for retry.`);\n } else if (result.outcome === \"queued\") {\n ctx.io.line(\"• Could not reach the backend. The test event is queued.\");\n } else if (result.code === \"quota_exceeded\") {\n // 58l: \"rejected by the backend\" named nothing. The error envelope says WHICH rejection\n // this is, and the reachability read carries the account's meter — so name the cause and\n // what actually clears it. The remedy comes from `describeExhaustedQuota`, the same\n // function behind `doctor`'s quota row, so this command cannot promise a reset that row\n // calls a backend fault, and cannot sell Plus to an account already on Plus.\n const reach = await fetchPushReachability({\n baseUrl,\n ...(deps.tokenOptions ? { tokenOptions: deps.tokenOptions } : {}),\n ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),\n });\n const quota = reach.state === \"ok\" ? reach.data.quota : undefined;\n const exhausted =\n quota !== undefined && quota.beeps_limit !== null\n ? describeExhaustedQuota({ ...quota, beeps_limit: quota.beeps_limit })\n : undefined;\n ctx.io.line(\n quota?.beeps_limit === null\n ? \"✗ Test event rejected. The backend rejected this event for quota, but this \" +\n \"account now reports unlimited beeps on Plus. The plan changed between those \" +\n \"requests; run `birdybeep test` again. If it repeats, run `birdybeep doctor`.\"\n : quota && exhausted\n ? `✗ Test event rejected. This account's monthly beep quota is used up ` +\n `(${String(quota.beeps_accepted)}/${String(quota.beeps_limit)} beeps on the ` +\n `${quota.plan} plan, period ${exhausted.window}). Every notifiable event is ` +\n `being rejected. ${exhausted.remedy}`\n : // An older backend reports no quota, so there is no period and no reset date to\n // give — and `doctor` cannot supply them either, since it reads this same response.\n \"✗ Test event rejected. This account's monthly beep quota is used up, so no Beep \" +\n \"can be sent. This server does not report the period or the reset date; check \" +\n \"your usage in the BirdyBeep app.\",\n );\n } else {\n ctx.io.line(\"✗ Test event was rejected by the backend. Run `birdybeep doctor`.\");\n }\n\n // delivered + queued are non-failure (offline is by design, and so is a store that is\n // momentarily unreadable — the event is parked, not lost). A hard reject is an error —\n // and so is being unpaired, which sent nothing at all (`status` already exits non-zero\n // for it, so a script can branch on either command).\n return result.outcome === \"dropped\" ||\n result.outcome === \"unpaired\" ||\n result.outcome === \"failed\"\n ? EXIT.ERROR\n : EXIT.OK;\n },\n };\n}\n","/**\n * `birdybeep queue clear` (§9.4) — debug maintenance: drop all locally-queued events. The\n * queue is best-effort (≤24h retention), so clearing it only discards pending retries; it\n * never touches harness config or the token. Reports how many entries were removed.\n */\nimport { LocalEventQueue } from \"@birdybeep/agent-core\";\n\nimport { type Command, EXIT } from \"../framework\";\n\nexport function createQueueCommand(): Command {\n return {\n name: \"queue\",\n summary: \"Local event-queue maintenance\",\n usage: \"birdybeep queue <clear>\",\n subcommands: [\n {\n name: \"clear\",\n summary: \"Clear the local offline event queue (debug)\",\n usage: \"birdybeep queue clear\",\n run: (ctx) => {\n const cleared = new LocalEventQueue().clear();\n ctx.io.emit(`Cleared ${cleared} queued event(s).`, { cleared });\n return EXIT.OK;\n },\n },\n ],\n };\n}\n","/**\n * `birdybeep report-status` (§7.3 step 7, §8.8, §21.2) — push each adapter's pre-event\n * integration status to the backend so the Machines/Integrations screen shows them BEFORE\n * any agent event fires. Sends ONE BATCHED `POST /v1/integrations/status` request\n * ({ integrations: [...] }, machine-token auth), parses the `{ integrations: [...] }`\n * response (surfacing the server's EFFECTIVE status, e.g. Codex → needs_trust), and parses\n * the mirrored error envelope: a 401/403 (unauthorized / forbidden / token_revoked) is\n * TERMINAL (exit non-zero), while offline / 5xx / rate_limit is \"deferred\" (surfaced, exit 0)\n * so it never blocks install.\n *\n * Request/response/error shapes are mirrored from the product (agent-core). fetch/adapters/\n * token injectable for hermetic tests; the live post is the deferred cross-repo follow-up.\n */\nimport {\n type AgentAdapter,\n errorEnvelopeSchema,\n getToken,\n type IntegrationStatusItem,\n integrationStatusResponseSchema,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\nimport { CLAUDE_CODE_ADAPTER_VERSION, claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { CODEX_ADAPTER_VERSION, codexAdapter } from \"@birdybeep/codex\";\nimport { COPILOT_ADAPTER_VERSION, copilotAdapter } from \"@birdybeep/copilot\";\nimport { CURSOR_ADAPTER_VERSION, cursorAdapter } from \"@birdybeep/cursor\";\nimport { OPENCODE_ADAPTER_VERSION, opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n codexAdapter,\n opencodeAdapter,\n cursorAdapter,\n copilotAdapter,\n];\n\n/** Per-harness BirdyBeep adapter version (the schema's optional `adapter_version`). */\nconst ADAPTER_VERSIONS: Record<string, string> = {\n claude_code: CLAUDE_CODE_ADAPTER_VERSION,\n codex: CODEX_ADAPTER_VERSION,\n opencode: OPENCODE_ADAPTER_VERSION,\n cursor: CURSOR_ADAPTER_VERSION,\n copilot: COPILOT_ADAPTER_VERSION,\n};\n\nconst base = (apiUrl: string): string => apiUrl.replace(/\\/$/, \"\");\n\nasync function gatherItems(adapters: AgentAdapter[]): Promise<IntegrationStatusItem[]> {\n return Promise.all(\n adapters.map(async (a) => {\n const [detection, status] = await Promise.all([a.detect(), a.status()]);\n const item: IntegrationStatusItem = { harness: a.id, status };\n if (detection.version !== undefined) item.harness_version = detection.version;\n const adapterVersion = ADAPTER_VERSIONS[a.id];\n if (adapterVersion !== undefined) item.adapter_version = adapterVersion;\n return item;\n }),\n );\n}\n\nexport interface ReportStatusCommandDeps {\n adapters?: AgentAdapter[];\n fetchImpl?: typeof fetch;\n tokenOptions?: TokenStoreOptions;\n}\n\nexport function createReportStatusCommand(deps: ReportStatusCommandDeps = {}): Command {\n const adapters = deps.adapters ?? DEFAULT_ADAPTERS;\n const fetchImpl = deps.fetchImpl ?? fetch;\n\n return {\n name: \"report-status\",\n summary: \"Internal: report integration status to the backend\",\n usage: \"birdybeep report-status [--json]\",\n run: async (ctx) => {\n const token = await getToken(deps.tokenOptions ?? {});\n if (token === null) {\n ctx.io.errline(\"No machine token. Run `birdybeep pair` first.\");\n return EXIT.ERROR;\n }\n\n const items = await gatherItems(adapters);\n if (items.length === 0) {\n ctx.io.emit(\"No integrations to report.\", { outcome: \"reported\", integrations: [] });\n return EXIT.OK;\n }\n\n // The effective per-harness status to display; defaults to what we sent, overwritten by\n // the server's response when it 200s.\n let effective = items.map((i) => ({ harness: i.harness, status: i.status }));\n let outcome: \"reported\" | \"deferred\" | \"terminal\" = \"deferred\";\n let errorCode: string | undefined;\n\n try {\n const res = await fetchImpl(`${base(resolveApiUrl())}/v1/integrations/status`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${token}`, \"content-type\": \"application/json\" },\n body: JSON.stringify({ integrations: items }),\n });\n if (res.ok) {\n outcome = \"reported\";\n const parsed = integrationStatusResponseSchema.safeParse(\n await res.json().catch(() => undefined),\n );\n if (parsed.success) {\n effective = parsed.data.integrations.map((i) => ({\n harness: i.harness,\n status: i.status,\n }));\n }\n } else {\n const env = errorEnvelopeSchema.safeParse(await res.json().catch(() => undefined));\n errorCode = env.success ? env.data.error.code : undefined;\n // The error CODE is the canonical terminal signal (auth failures); HTTP status is\n // only the fallback when the envelope didn't parse. Everything else → deferred.\n const terminal =\n errorCode !== undefined\n ? errorCode === \"unauthorized\" ||\n errorCode === \"forbidden\" ||\n errorCode === \"token_revoked\"\n : res.status === 401 || res.status === 403;\n outcome = terminal ? \"terminal\" : \"deferred\";\n }\n } catch {\n outcome = \"deferred\"; // offline / transport error → surfaced, not fatal\n }\n\n if (ctx.flags.json) {\n ctx.io.result({\n outcome,\n integrations: effective,\n ...(errorCode !== undefined ? { error: errorCode } : {}),\n });\n } else if (outcome === \"terminal\") {\n ctx.io.errline(\n `Report rejected (${errorCode ?? \"auth\"}). Your token may be revoked. Re-run \\`birdybeep pair\\`.`,\n );\n } else {\n for (const e of effective) {\n ctx.io.line(\n outcome === \"reported\"\n ? `✓ ${e.harness}: ${e.status} (reported)`\n : `• ${e.harness}: ${e.status} (deferred; backend unreachable)`,\n );\n }\n }\n\n // Terminal auth failure → non-zero; offline/deferred → 0 (must never block install).\n return outcome === \"terminal\" ? EXIT.ERROR : EXIT.OK;\n },\n };\n}\n","/**\n * `birdybeep status` (§9.3, §9.4) — a quick health snapshot: machine identity + pairing\n * state, per-harness integration status, and local queue depth, while opportunistically\n * draining the queue (best-effort, non-blocking) and reporting delivered-vs-remaining.\n * Exits non-zero when not paired so scripts can branch. `--json` mirrors everything.\n * Factory with injectable adapters/sender/token so tests run hermetically against a stub.\n */\nimport {\n type AgentAdapter,\n createSender as defaultCreateSender,\n type Sender,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { copilotAdapter } from \"@birdybeep/copilot\";\nimport { cursorAdapter } from \"@birdybeep/cursor\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport {\n describeFilteredActivity,\n describeSurface,\n describeTokenStoreUnavailable,\n describeUnpairedActivity,\n filteredActivity,\n gatherIntegrations,\n gatherSurfaces,\n localQueueDepth,\n localQueueOverflowDrops,\n machineIdentity,\n pairingReport,\n type SurfaceCoverageOptions,\n unpairedActivity,\n} from \"../diagnostics\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [\n claudeCodeAdapter,\n codexAdapter,\n opencodeAdapter,\n cursorAdapter,\n copilotAdapter,\n];\n\nexport interface StatusCommandDeps {\n adapters?: AgentAdapter[];\n /** Build the drain sender (default: agent-core createSender at the resolved API URL). */\n createSender?: (baseUrl: string) => Sender;\n /** Token-store options (tests inject the file fallback). */\n tokenOptions?: TokenStoreOptions;\n /** Where the observed-builds tally lives (tests point it at a sandbox). */\n surfaceOptions?: SurfaceCoverageOptions;\n}\n\nexport function createStatusCommand(deps: StatusCommandDeps = {}): Command {\n const adapters = deps.adapters ?? DEFAULT_ADAPTERS;\n const makeSender =\n deps.createSender ??\n ((baseUrl) =>\n defaultCreateSender(\n deps.tokenOptions ? { baseUrl, tokenOptions: deps.tokenOptions } : { baseUrl },\n ));\n\n return {\n name: \"status\",\n summary: \"Show pairing + per-harness integration status\",\n usage: \"birdybeep status [--json]\",\n run: async (ctx) => {\n const machine = machineIdentity();\n // gcgp.23: three answers. A token store that would not answer is reported as unknown —\n // \"no\" would be a wrong diagnosis for the common case (a locked keychain on a machine\n // that IS paired), and the events it affects are queued rather than lost.\n const pairing = await pairingReport(deps.tokenOptions ?? {});\n const paired = pairing.state === \"paired\";\n const integrations = await gatherIntegrations(adapters);\n // gcgp.6: which BUILD of each harness is actually delivering. `integrations` above answers\n // for the shared config; a machine runs a harness from a terminal CLI and from a desktop\n // app's own engine, and only one of them may ever reach the hook.\n const surfaces = await gatherSurfaces(adapters, deps.surfaceOptions ?? {});\n const depthBefore = localQueueDepth();\n const unpaired = unpairedActivity(); // gcgp.4: events that fired with no token to send them\n const filtered = filteredActivity(); // gcgp.3: events handled locally, never sent\n const drain = await makeSender(resolveApiUrl()).drainNow(); // opportunistic, best-effort\n const depthAfter = localQueueDepth();\n const overflowDropped = localQueueOverflowDrops();\n\n const report = {\n machine,\n paired,\n pairing, // gcgp.23: paired | unpaired | unknown — `paired: false` cannot say which\n integrations,\n surfaces,\n queue: { depthBefore, delivered: drain.delivered, depthAfter, overflowDropped },\n ...(unpaired !== null ? { unpairedActivity: unpaired } : {}),\n ...(filtered !== null ? { filteredActivity: filtered } : {}),\n };\n\n if (ctx.flags.json) {\n ctx.io.result(report);\n } else {\n ctx.io.line(`Machine: ${machine.label} (${machine.os})`);\n ctx.io.line(\n pairing.state === \"paired\"\n ? \"Paired: yes\"\n : pairing.state === \"unpaired\"\n ? \"Paired: no. Run `birdybeep pair`.\"\n : `Paired: unknown. ${describeTokenStoreUnavailable(pairing)}`,\n );\n ctx.io.line(\"Integrations:\");\n for (const i of integrations) {\n ctx.io.line(` ${i.displayName}: ${i.status}`);\n const group = surfaces.find((g) => g.harness === i.harness);\n for (const state of group?.surfaces ?? []) {\n const mark = state.coverage === \"active\" ? \"✓\" : state.coverage === \"wired\" ? \"·\" : \"✗\";\n ctx.io.line(` ${mark} ${describeSurface(state)}: ${state.coverage}`);\n }\n }\n ctx.io.line(\n `Queue: ${depthBefore} queued → ${drain.delivered} delivered, ${depthAfter} remaining` +\n (overflowDropped > 0 ? `, ${overflowDropped} dropped by the queue cap` : \"\"),\n );\n // The whole point of the notice (gcgp.4): hooks firing into the void is otherwise\n // indistinguishable from no hooks firing at all.\n if (unpaired !== null) ctx.io.line(`⚠ Lost: ${describeUnpairedActivity(unpaired)}`);\n // gcgp.3: the counterpart signal — hooks that fired and were deliberately not sent.\n if (filtered !== null) ctx.io.line(`Local: ${describeFilteredActivity(filtered)}`);\n }\n // not-paired → defined non-zero; so is an unreadable store, which is equally \"not\n // confirmed working\" for a script that branches on it (gcgp.23).\n return paired ? EXIT.OK : EXIT.ERROR;\n },\n };\n}\n","/**\n * The `birdybeep` command registry (§9.4) — the command tree the framework dispatches.\n * Every command is a factory (`create*Command`) so its dependencies (adapters, sender,\n * token store, fetch, stdin) are injectable for hermetic tests; the framework (help /\n * flags / routing / config dir / exit codes) is command-independent.\n */\nimport { createAgentCommand } from \"./commands/agent\";\nimport { createDoctorCommand } from \"./commands/doctor\";\nimport { createHookCommand } from \"./commands/hook\";\nimport { createLogoutCommand, createUnpairCommand } from \"./commands/logout\";\nimport { createPairCommand, createSetupCommand } from \"./commands/pair\";\nimport { createQueueCommand } from \"./commands/queue\";\nimport { createReportStatusCommand } from \"./commands/report-status\";\nimport { createStatusCommand } from \"./commands/status\";\nimport { createTestCommand } from \"./commands/test\";\nimport { type Command } from \"./framework\";\n\n/**\n * Build the full §9.4 command tree.\n *\n * `setup` leads (gcgp.5) — the root help lists these in registry order, and the first entry is\n * what a new reader tries. It and `pair` run the identical flow; `setup` is the verb people look\n * for, and the only one that skips the phone round-trip on a machine that already has a token.\n */\nexport function buildCommands(): Command[] {\n return [\n createSetupCommand(),\n createPairCommand(),\n createLogoutCommand(),\n createUnpairCommand(),\n createStatusCommand(),\n createTestCommand(),\n createDoctorCommand(),\n createAgentCommand(),\n createHookCommand(),\n createQueueCommand(),\n createReportStatusCommand(),\n ];\n}\n","/**\n * Passive update notifier (§9.4). Instead of a manual `update` command, the CLI opportunistically\n * checks the npm registry for a newer `@birdybeep/cli` and prints a subtle \"new version available\"\n * notice to **stderr** after an eligible command runs — so users learn about upgrades just by using\n * the tool. It is:\n *\n * - **Cached (TTL-gated):** the result is stored in the config dir and only refreshed from the\n * network once per {@link DEFAULT_CHECK_INTERVAL_MS}; every other run is a local file read.\n * - **Non-blocking to the hot path:** the `hook` command (which runs inside the harness and must\n * return fast) and the internal `report-status` command are skipped before any I/O.\n * - **Quiet for machines/scripts:** skipped under `--json`, `--non-interactive`, a non-TTY stderr,\n * `CI`, or the `NO_UPDATE_NOTIFIER` / `BIRDYBEEP_NO_UPDATE_NOTIFIER` opt-outs.\n * - **Best-effort & side-effect-free on the result:** it never throws, never changes stdout, and\n * never affects the command's exit code (registry/semver logic lives here, not in the framework).\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nimport { birdyBeepConfigDir } from \"@birdybeep/agent-core\";\n\nimport { resolveRegistryUrl } from \"./config\";\nimport { type GlobalFlags, type Io } from \"./framework\";\nimport { CLI_VERSION } from \"./version\";\n\n/** The published package the notice points at. */\nexport const PACKAGE_NAME = \"@birdybeep/cli\";\n/** URL-encoded scoped path for the registry `latest` dist-tag endpoint. */\nconst PACKAGE_PATH = \"@birdybeep%2Fcli\";\n/** Cache file (non-secret) in the BirdyBeep config dir. */\nexport const UPDATE_CACHE_FILE = \"update-check.json\";\n/** Refresh the registry at most once per this window; every other run reads the cache. */\nexport const DEFAULT_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h\n/** Best-effort timeout for the (rare) registry refresh — short so it can't stall a command. */\nconst DEFAULT_TIMEOUT_MS = 1500;\n\n/**\n * Top-level commands that must never trigger a check/notice:\n * - `hook` runs inside the harness hot path and must return fast (never block the harness);\n * - `report-status` is invoked by BirdyBeep itself, not by an interactive user.\n */\nconst SKIP_COMMANDS = new Set([\"hook\", \"report-status\"]);\n\n/** A parsed semver: numeric core + dot-separated prerelease identifiers (build metadata dropped). */\nexport interface Semver {\n major: number;\n minor: number;\n patch: number;\n /** Prerelease identifiers (e.g. `1.2.0-beta.1` → `[\"beta\", \"1\"]`); empty for a release. */\n prerelease: string[];\n}\n\n// Simplified semver.org grammar: `MAJOR.MINOR.PATCH[-prerelease][+build]`, tolerating a leading `v`.\nconst SEMVER_RE = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?(?:\\+[0-9A-Za-z.-]+)?$/;\n\n/** Parse a semver string; returns null for anything that isn't a clean `MAJOR.MINOR.PATCH[...]`. */\nexport function parseSemver(input: string): Semver | null {\n const m = SEMVER_RE.exec(input.trim());\n if (m === null) return null;\n return {\n major: Number(m[1]),\n minor: Number(m[2]),\n patch: Number(m[3]),\n prerelease: m[4] !== undefined ? m[4].split(\".\") : [],\n };\n}\n\n/** Compare two prerelease identifier lists per semver §11 (a release outranks any prerelease). */\nfunction comparePrerelease(a: string[], b: string[]): number {\n if (a.length === 0 && b.length === 0) return 0;\n if (a.length === 0) return 1; // 1.2.0 > 1.2.0-beta\n if (b.length === 0) return -1;\n const len = Math.min(a.length, b.length);\n for (let i = 0; i < len; i++) {\n const ai = a[i]!;\n const bi = b[i]!;\n const aNum = /^\\d+$/.test(ai);\n const bNum = /^\\d+$/.test(bi);\n if (aNum && bNum) {\n const d = Number(ai) - Number(bi);\n if (d !== 0) return d < 0 ? -1 : 1;\n } else if (aNum) {\n return -1; // numeric identifiers rank lower than alphanumeric\n } else if (bNum) {\n return 1;\n } else if (ai !== bi) {\n return ai < bi ? -1 : 1; // ASCII lexical order\n }\n }\n if (a.length === b.length) return 0;\n return a.length < b.length ? -1 : 1; // more identifiers wins when all preceding are equal\n}\n\n/** -1 if `a < b`, 0 if equal, 1 if `a > b` (semver precedence). */\nexport function compareSemver(a: Semver, b: Semver): number {\n if (a.major !== b.major) return a.major < b.major ? -1 : 1;\n if (a.minor !== b.minor) return a.minor < b.minor ? -1 : 1;\n if (a.patch !== b.patch) return a.patch < b.patch ? -1 : 1;\n return comparePrerelease(a.prerelease, b.prerelease);\n}\n\n/** `true` when `latest` is a strictly higher version than `current` (both must parse). */\nexport function isNewer(current: string, latest: string): boolean {\n const cur = parseSemver(current);\n const lat = parseSemver(latest);\n return cur !== null && lat !== null && compareSemver(cur, lat) < 0;\n}\n\n/** Cached registry result. `latest` is the last-seen published version, or null if never fetched. */\nexport interface UpdateCache {\n /** Epoch ms of the last registry refresh attempt. */\n checkedAt: number;\n latest: string | null;\n}\n\nexport function updateCachePath(): string {\n return join(birdyBeepConfigDir(), UPDATE_CACHE_FILE);\n}\n\n/** Read the cache; returns null on a missing/unreadable/corrupt/invalid file (never throws). */\nexport function readUpdateCache(): UpdateCache | null {\n try {\n const parsed: unknown = JSON.parse(readFileSync(updateCachePath(), \"utf8\"));\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const { checkedAt, latest } = parsed as Record<string, unknown>;\n if (typeof checkedAt !== \"number\") return null;\n if (latest !== null && typeof latest !== \"string\") return null;\n return { checkedAt, latest };\n } catch {\n return null;\n }\n}\n\n/** Persist the cache (strict-perm dir + file); best-effort — a write failure is swallowed by callers. */\nexport function writeUpdateCache(cache: UpdateCache): void {\n mkdirSync(birdyBeepConfigDir(), { recursive: true, mode: 0o700 });\n writeFileSync(updateCachePath(), `${JSON.stringify(cache)}\\n`, { mode: 0o600 });\n}\n\n/** Fetch the `latest` dist-tag version from the registry, or throw a concise reason. */\nasync function fetchLatestVersion(\n registryUrl: string,\n fetchImpl: typeof fetch,\n timeoutMs: number,\n): Promise<string> {\n const url = `${registryUrl.replace(/\\/+$/, \"\")}/${PACKAGE_PATH}/latest`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n if (typeof timer.unref === \"function\") timer.unref();\n try {\n const res = await fetchImpl(url, {\n headers: { accept: \"application/json\" },\n signal: controller.signal,\n });\n if (!res.ok) throw new Error(`registry responded ${res.status}`);\n const body = (await res.json()) as { version?: unknown };\n if (typeof body.version !== \"string\" || body.version.length === 0) {\n throw new Error(\"registry response had no version\");\n }\n return body.version;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** The two-line upgrade notice printed to stderr (lowercase/chirpy per the Perch voice). */\nfunction renderNotice(current: string, latest: string): string {\n return (\n `a new version of birdybeep is available: ${current} → ${latest}\\n` +\n `upgrade with: npm install -g ${PACKAGE_NAME}@latest`\n );\n}\n\nexport interface NotifyUpdateOptions {\n /** Resolved top-level command name (used to skip `hook` / `report-status`). */\n command?: string;\n flags: GlobalFlags;\n io: Io;\n // --- injectables (production defaults are the real registry / fs / clock / env / TTY) ---\n fetchImpl?: typeof fetch;\n currentVersion?: string;\n registryUrl?: string;\n now?: number;\n intervalMs?: number;\n timeoutMs?: number;\n /** Override the stderr-TTY gate (tests set this true to exercise the notice deterministically). */\n isTTY?: boolean;\n env?: NodeJS.ProcessEnv;\n readCache?: () => UpdateCache | null;\n writeCache?: (cache: UpdateCache) => void;\n}\n\n/**\n * The notifier entry point, invoked by the framework after an eligible command runs. Reads the\n * cache, refreshes from the registry when stale (TTL-gated, short timeout, best-effort), and prints\n * the upgrade notice to stderr when a newer version exists. Never throws.\n */\nexport async function maybeNotifyUpdate(opts: NotifyUpdateOptions): Promise<void> {\n try {\n // Hot-path / internal commands: bail before any work so the harness is never slowed.\n if (opts.command !== undefined && SKIP_COMMANDS.has(opts.command)) return;\n // Machine/script output or explicit non-interactive: no chatter on stderr.\n if (opts.flags.json || opts.flags.nonInteractive) return;\n\n const env = opts.env ?? process.env;\n if (env[\"BIRDYBEEP_NO_UPDATE_NOTIFIER\"] || env[\"NO_UPDATE_NOTIFIER\"] || env[\"CI\"]) return;\n\n const isTTY = opts.isTTY ?? Boolean(process.stderr.isTTY);\n if (!isTTY) return; // don't nag in pipes/logs\n\n const current = opts.currentVersion ?? CLI_VERSION;\n const now = opts.now ?? Date.now();\n const intervalMs = opts.intervalMs ?? DEFAULT_CHECK_INTERVAL_MS;\n const readCache = opts.readCache ?? readUpdateCache;\n const writeCache = opts.writeCache ?? writeUpdateCache;\n\n let cache = readCache();\n if (cache === null || now - cache.checkedAt >= intervalMs) {\n // Refresh at most once per interval. On failure, keep the last-known `latest` (so a\n // previously-seen update still shows) but still stamp `checkedAt` to back off, never hammer.\n let latest = cache?.latest ?? null;\n try {\n latest = await fetchLatestVersion(\n opts.registryUrl ?? resolveRegistryUrl(),\n opts.fetchImpl ?? fetch,\n opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n );\n } catch {\n /* offline / registry error: fall back to last-known latest, back off for the interval */\n }\n cache = { checkedAt: now, latest };\n try {\n writeCache(cache);\n } catch {\n /* config dir not writable: notice still works this run, just won't be cached */\n }\n }\n\n if (cache.latest !== null && isNewer(current, cache.latest)) {\n opts.io.errline(renderNotice(current, cache.latest));\n }\n } catch {\n /* the notifier is best-effort — it must never break or slow a command */\n }\n}\n","/**\n * @birdybeep/cli — the public, side-effect-free CLI API. `runCli` wires the §9.4 command\n * registry into the framework dispatcher with injectable output (so it is fully unit\n * testable); the executable shell lives in `bin.ts`.\n */\nimport { buildCommands } from \"./commands\";\nimport { type Command, dispatch, type Writer } from \"./framework\";\nimport { maybeNotifyUpdate, type NotifyUpdateOptions } from \"./update-check\";\nimport { CLI_VERSION } from \"./version\";\n\nexport { buildCommands } from \"./commands\";\nexport * from \"./framework\";\nexport { CLI_VERSION } from \"./version\";\n\nexport interface RunCliDeps {\n stdout?: Writer;\n stderr?: Writer;\n /** Override the command registry (tests). Defaults to the real §9.4 tree. */\n commands?: Command[];\n /** Skip the config-dir bootstrap (tests without filesystem side effects). */\n ensureConfig?: boolean;\n /**\n * Override the passive update-notifier. `false` disables it; an object injects the registry\n * fetch / clock / TTY / cache for hermetic tests. Omitted in production → the real notifier\n * (which no-ops on a non-TTY stderr, so unit tests capturing to buffers stay offline & quiet).\n */\n updateCheck?: Partial<NotifyUpdateOptions> | false;\n}\n\n/** Run the CLI against an argv slice (without `node`/script path). Returns the exit code. */\nexport function runCli(argv: string[], deps: RunCliDeps = {}): Promise<number> {\n const notifyUpdate =\n deps.updateCheck === false\n ? undefined\n : (ctx: {\n command: string;\n flags: NotifyUpdateOptions[\"flags\"];\n io: NotifyUpdateOptions[\"io\"];\n }) => maybeNotifyUpdate({ ...ctx, ...(deps.updateCheck ?? {}) });\n\n return dispatch(argv, {\n version: CLI_VERSION,\n commands: deps.commands ?? buildCommands(),\n stdout: deps.stdout ?? process.stdout,\n stderr: deps.stderr ?? process.stderr,\n ...(notifyUpdate !== undefined ? { notifyUpdate } : {}),\n ...(deps.ensureConfig !== undefined ? { ensureConfig: deps.ensureConfig } : {}),\n });\n}\n"],"mappings":"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","names":["base","installTarget","claudeCodeAdapter","codexAdapter","copilotAdapter","cursorAdapter","opencodeAdapter","mkdirSync","readFileSync","birdyBeepConfigDir","DEFAULT_ADAPTERS","claudeCodeAdapter","codexAdapter","opencodeAdapter","cursorAdapter","copilotAdapter","writeFileSync","join","defaultCreateSender","asRecord","join","writeFileSync","defaultCreateSender","getToken","getToken","closeSync","openSync","getMachineIdentity","getToken","LocalEventQueue","base","claudeCodeAdapter","codexAdapter","copilotAdapter","cursorAdapter","opencodeAdapter","defaultCreateSender","fetchPushReachability","getMachineIdentity","getMachineIdentity","defaultCreateSender","fetchPushReachability","claudeCodeAdapter","codexAdapter","opencodeAdapter","cursorAdapter","copilotAdapter","base","installTarget","openSync","closeSync","getToken","report","getMachineIdentity","LocalEventQueue","LocalEventQueue","LocalEventQueue","errorEnvelopeSchema","getToken","claudeCodeAdapter","codexAdapter","copilotAdapter","cursorAdapter","opencodeAdapter","DEFAULT_ADAPTERS","claudeCodeAdapter","codexAdapter","opencodeAdapter","cursorAdapter","copilotAdapter","base","getToken","errorEnvelopeSchema","defaultCreateSender","claudeCodeAdapter","codexAdapter","copilotAdapter","cursorAdapter","opencodeAdapter","DEFAULT_ADAPTERS","claudeCodeAdapter","codexAdapter","opencodeAdapter","cursorAdapter","copilotAdapter","defaultCreateSender","mkdirSync","readFileSync","writeFileSync","join","birdyBeepConfigDir","join","birdyBeepConfigDir","readFileSync","mkdirSync","writeFileSync"]}