@birdybeep/cli 0.0.1

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/index.ts","../src/commands/agent.ts","../src/framework.ts","../src/commands/doctor.ts","../src/config.ts","../src/diagnostics.ts","../src/commands/hook.ts","../src/commands/login.ts","../src/pairing.ts","../src/version.ts","../src/commands/logout.ts","../src/commands/queue.ts","../src/commands/report-status.ts","../src/commands/status.ts","../src/commands/test.ts","../src/commands.ts","../src/cli.ts"],"sourcesContent":["/**\n * @birdybeep/cli — library entry. Re-exports the side-effect-free CLI API so the\n * package ships a real `.d.ts` and is importable for testing/embedding. The\n * executable lives in `bin.ts` (the only module with a shebang + `process` side\n * effects); keeping it separate stops the shebang from leaking into this entry's\n * type declarations.\n */\nexport * from \"./cli.js\";\n","/**\n * `birdybeep agent install|uninstall [all|claude|codex|opencode]` (§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 } from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { type Command, type CommandContext, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [claudeCodeAdapter, codexAdapter, opencodeAdapter];\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};\n\nexport const AGENT_TARGETS: readonly string[] = [\"all\", \"claude\", \"codex\", \"opencode\"];\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\nasync function installSelected(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 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 if (ctx.flags.json) {\n ctx.io.result({ target, 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 to install.\");\n }\n for (const o of outcomes) {\n if (!o.detected) {\n ctx.io.line(`– ${o.displayName}: not detected (skipped)`);\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 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 the three real adapters. */\n adapters?: AgentAdapter[];\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 return {\n name: \"agent\",\n summary: \"Install or uninstall harness adapters\",\n usage: \"birdybeep agent <install|uninstall> [all|claude|codex|opencode]\",\n subcommands: [\n {\n name: \"install\",\n summary: \"Install adapters (all | claude | codex | opencode)\",\n usage: \"birdybeep agent install [all|claude|codex|opencode]\",\n run: (ctx) => installSelected(adapters, ctx),\n },\n {\n name: \"uninstall\",\n summary: \"Restore harness config to its pre-install state\",\n usage: \"birdybeep agent uninstall [all|claude|codex|opencode]\",\n run: (ctx) => uninstallSelected(adapters, ctx),\n },\n ],\n };\n}\n","/**\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\nexport interface Command {\n name: string;\n summary: string;\n /** One-line usage shown in the command's own `--help`. */\n usage?: string;\n /** Nested subcommands (e.g. `agent install` / `agent uninstall`). */\n subcommands?: Command[];\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/** Is `token` an unknown long/short flag (after global flags were stripped)? */\nfunction isUnknownFlag(token: string): boolean {\n return token.startsWith(\"-\") && !GLOBAL_FLAG_TOKENS.has(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 return [\n `birdybeep ${version} — stream coding-agent lifecycle events to BirdyBeep.`,\n \"\",\n \"Usage:\",\n \" birdybeep <command> [options]\",\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\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.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\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 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 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) => ({ name: c.name, summary: c.summary })),\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 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 unknown = args.find(isUnknownFlag);\n if (unknown !== undefined) {\n io.errline(`birdybeep ${path}: unknown option \"${unknown}\".`);\n return EXIT.USAGE;\n }\n\n try {\n return 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","/**\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 type Sender,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\nimport { claudeCodeAdapter } from \"@birdybeep/claude-code\";\nimport { codexAdapter } from \"@birdybeep/codex\";\nimport { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { isLoggedIn, localQueueDepth } from \"../diagnostics\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [claudeCodeAdapter, codexAdapter, opencodeAdapter];\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}\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 = resolveApiUrl();\n\n // 1. Machine token.\n const loggedIn = await isLoggedIn(deps.tokenOptions ?? {});\n checks.push(\n loggedIn\n ? { name: \"Machine token\", ok: true }\n : {\n name: \"Machine token\",\n ok: false,\n detail: \"No machine token found.\",\n remedy: \"Run `birdybeep login` to pair this machine.\",\n },\n );\n\n // 2. Each adapter's own diagnostics (detected? installed? needs_trust/needs_restart/error?).\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 }\n\n // 3. Local queue: drain opportunistically, report depth.\n const depthBefore = localQueueDepth();\n const drain = await makeSender(apiUrl).drainNow();\n const depthAfter = localQueueDepth();\n checks.push({\n name: \"Local queue\",\n ok: true,\n detail: `${depthBefore} queued → ${drain.delivered} delivered, ${depthAfter} remaining`,\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 will 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 queue: { depthBefore, delivered: drain.delivered, depthAfter },\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 login`; 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 `login`); never holds a token. */\n apiUrl?: 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 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 * Shared status/queue plumbing used by `birdybeep status` and `birdybeep doctor`: gather\n * each adapter's integration status, the machine identity + login state, and local queue\n * depth. Read-only + privacy-safe — never prints token material or notification bodies.\n */\nimport {\n type AgentAdapter,\n getMachineIdentity,\n getToken,\n type IntegrationStatus,\n LocalEventQueue,\n type TokenStoreOptions,\n} from \"@birdybeep/agent-core\";\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? (login state — never prints the token.) */\nexport async function isLoggedIn(tokenOptions: TokenStoreOptions = {}): Promise<boolean> {\n return (await getToken(tokenOptions)) !== null;\n}\n\n/** Current local event-queue depth (fresh, non-expired entries). */\nexport function localQueueDepth(): number {\n return new LocalEventQueue().size();\n}\n\n/** Machine label + OS (the event `machine` identity). */\nexport function machineIdentity(): { label: string; os: string } {\n return getMachineIdentity();\n}\n","/**\n * `birdybeep hook <claude|codex|opencode>` (§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), and ALWAYS exits 0 so it\n * never errors the harness. The token is read by the sender from the secure store — never\n * from config — and notification content is never persisted (the adapters' normalizers\n * enforce that).\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 {\n createSender as defaultCreateSender,\n type HookResult,\n type Sender,\n} from \"@birdybeep/agent-core\";\nimport { runClaudeHook } from \"@birdybeep/claude-code\";\nimport { runCodexHook } from \"@birdybeep/codex\";\nimport { runOpenCodeHook } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\nexport type HarnessName = \"claude\" | \"codex\" | \"opencode\";\n\ntype HarnessRunner = (input: unknown, options: { sender: Sender }) => Promise<HookResult>;\n\nconst RUNNERS: Record<HarnessName, HarnessRunner> = {\n claude: runClaudeHook,\n codex: runCodexHook,\n opencode: runOpenCodeHook,\n};\n\nexport const HOOK_HARNESSES: readonly HarnessName[] = [\"claude\", \"codex\", \"opencode\"];\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\"). BUDGET MATH: this cap and the sender's\n * DEFAULT_TOTAL_BUDGET_MS (5s) run SEQUENTIALLY and must sum comfortably under the 10s\n * hook timeout the adapters register, leaving headroom for Node startup — 3s + 5s + ~1s\n * startup < 10s. (5s + 5s summed to exactly the timeout: a slow start got the hook\n * SIGKILLed mid-send, which skips the queue-on-failure catch and loses the event.)\n */\nexport const STDIN_READ_TIMEOUT_MS = 3000;\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 value === \"claude\" || value === \"codex\" || value === \"opencode\";\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): Promise<HookResult> {\n return RUNNERS[harness](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): Promise<string> {\n return args[1] ?? (await readStdin());\n}\n\nexport interface HookCommandDeps {\n /** Build the sender (default: agent-core `createSender` with the resolved API URL). */\n createSender?: (baseUrl: string) => 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}\n\n/** Build the `hook` command. Pure stubs aside, this is the live event path. */\nexport function createHookCommand(deps: HookCommandDeps = {}): Command {\n const makeSender = deps.createSender ?? ((baseUrl) => defaultCreateSender({ baseUrl }));\n const readStdin = deps.readStdin ?? readStdinDefault;\n const stdinTimeoutMs = deps.stdinTimeoutMs ?? STDIN_READ_TIMEOUT_MS;\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>\",\n run: async (ctx) => {\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 // Bounded read: the trailing argv payload resolves instantly; a hung/never-closing\n // stdin falls back to \"\" after the timeout so the hook ALWAYS returns fast (§9.3).\n const raw = await withTimeout(readHookPayload(ctx.args, readStdin), stdinTimeoutMs, \"\");\n let payload: unknown;\n try {\n payload = JSON.parse(raw);\n } catch {\n // Garbled/empty payload → skip silently + fast. Never error the harness.\n ctx.io.result({ harness, outcome: \"skipped\" });\n return EXIT.OK;\n }\n\n const sender = makeSender(resolveApiUrl());\n const result = await runHookCommand(harness, payload, sender);\n // Hot path: human mode is silent; --json emits the outcome for scripts/debugging.\n ctx.io.result({ harness, outcome: result.outcome, eventType: result.eventType });\n return EXIT.OK; // delivered/queued/deduped/skipped all return fast + non-erroring\n },\n };\n}\n","/**\n * `birdybeep login` (§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 pair link + `user_code` → poll `POST /v1/pair/token` 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/code carries only short-lived pairing info.\n *\n * The QR matrix (birdybeep-agent-pe1) renders only on an interactive TTY — piped/CI\n * output keeps the plain link + 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 read the code and approve — 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 { getMachineIdentity, setToken, type TokenStoreOptions } 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 { resolveApiUrl, writeCliConfig } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\nimport { pairStart, pairTokenPoll, type PairTokenResult } from \"../pairing\";\nimport { CLI_VERSION } from \"../version\";\n\n/** Default delay between `/pair/token` polls (the start response has no interval). */\nexport const DEFAULT_POLL_INTERVAL_MS = 2000;\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\nexport interface LoginCommandDeps {\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\nexport function createLoginCommand(deps: LoginCommandDeps = {}): 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\n return {\n name: \"login\",\n summary: \"Pair this machine with your BirdyBeep account (QR or manual)\",\n usage: \"birdybeep login [--json]\",\n run: async (ctx) => {\n const apiUrl = resolveApiUrl();\n const identity = getMachineIdentity(); // { label, os, fingerprintHash }\n const start = await pairStart(\n apiUrl,\n { machineLabel: identity.label, os: identity.os, cliVersion: CLI_VERSION },\n fetchImpl,\n );\n\n if (ctx.flags.json) {\n // NDJSON: emit the pairing info NOW so a script/agent can surface the code for\n // approval while we poll; the final success object is a later line (pe1).\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 ctx.io.line(\n \"To pair this machine, scan the code or open the link, then confirm in the app:\",\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(` Code: ${start.user_code}`);\n ctx.io.line(\"Waiting for confirmation…\");\n }\n\n // Poll /pair/token until approved (201) or the pairing window expires.\n const deadline = Date.parse(start.expires_at);\n let paired: PairTokenResult | undefined;\n while (clock() < deadline) {\n await sleep(intervalMs);\n const poll = await pairTokenPoll(\n apiUrl,\n start.device_code,\n fetchImpl,\n identity.fingerprintHash,\n );\n if (poll.status === \"paired\") {\n paired = poll;\n break;\n }\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 it was confirmed. Run `birdybeep login` to retry.\",\n );\n return EXIT.ERROR;\n }\n\n // Durable token → secure store ONLY. Non-secret apiUrl → config. Never the reverse.\n await setToken(paired.machineToken, deps.tokenOptions ?? {});\n writeCliConfig({ apiUrl });\n\n ctx.io.emit(`✓ Paired. Run \\`birdybeep test\\` to send a test Beep.`, {\n paired: true,\n machineId: paired.machineId,\n });\n return EXIT.OK;\n },\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 `qr_payload` + `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 / user code carries only short-lived pairing info,\n * NEVER a durable token. Request/response shapes are mirrored from the product (agent-core).\n */\nimport {\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\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 };\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 };\n\n/**\n * Poll once for the device token (`POST /v1/pair/token`, unauthenticated). A 201 with a valid\n * token body → paired; ANY other outcome (incl. a `validation_failed`/4xx before the user has\n * approved) → pending, so the caller keeps polling until the `expires_at` deadline.\n */\nexport async function pairTokenPoll(\n apiUrl: string,\n deviceCode: string,\n fetchImpl: typeof fetch,\n machineFingerprint?: string,\n): Promise<PairTokenResult> {\n const body = {\n device_code: deviceCode,\n ...(machineFingerprint !== undefined ? { machine_fingerprint: machineFingerprint } : {}),\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 if (!res.ok) return { status: \"pending\" }; // validation_failed / 4xx → not approved yet\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 };\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 logout` (§9.4) — remove the local machine token from BOTH the OS keychain and\n * the strict-perm file fallback. Idempotent (no error when already logged out). Does NOT\n * touch harness integration config (that is `agent uninstall`) or the local queue.\n */\nimport { clearToken, type TokenStoreOptions } from \"@birdybeep/agent-core\";\n\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 function createLogoutCommand(deps: LogoutCommandDeps = {}): Command {\n return {\n name: \"logout\",\n summary: \"Remove the local machine token\",\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 * `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 { OPENCODE_ADAPTER_VERSION, opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [claudeCodeAdapter, codexAdapter, opencodeAdapter];\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};\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(\"Not logged in — run `birdybeep login` 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 login\\`.`,\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 + login\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 logged in 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 { opencodeAdapter } from \"@birdybeep/opencode\";\n\nimport { resolveApiUrl } from \"../config\";\nimport { gatherIntegrations, isLoggedIn, localQueueDepth, machineIdentity } from \"../diagnostics\";\nimport { type Command, EXIT } from \"../framework\";\n\nconst DEFAULT_ADAPTERS: AgentAdapter[] = [claudeCodeAdapter, codexAdapter, opencodeAdapter];\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}\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 const loggedIn = await isLoggedIn(deps.tokenOptions ?? {});\n const integrations = await gatherIntegrations(adapters);\n const depthBefore = localQueueDepth();\n const drain = await makeSender(resolveApiUrl()).drainNow(); // opportunistic, best-effort\n const depthAfter = localQueueDepth();\n\n const report = {\n machine,\n loggedIn,\n integrations,\n queue: { depthBefore, delivered: drain.delivered, depthAfter },\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 loggedIn ? \"Login: paired\" : \"Login: NOT logged in — run `birdybeep login`\",\n );\n ctx.io.line(\"Integrations:\");\n for (const i of integrations) ctx.io.line(` ${i.displayName}: ${i.status}`);\n ctx.io.line(\n `Queue: ${depthBefore} queued → ${drain.delivered} delivered, ${depthAfter} remaining`,\n );\n }\n return loggedIn ? EXIT.OK : EXIT.ERROR; // not-logged-in → defined non-zero\n },\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 * queued (offline) vs rejected; --json mirrors the outcome.\n *\n * Sends event_type \"test\" (9fh): the backend notifies it by default and exempts it from\n * the beep quota. (The old \"custom\" type is unconditionally suppressed by the §10.5\n * matrix — every test \"succeeded\" while no push could ever be sent.) The session id is\n * unique per run so back-to-back tests don't collapse in the backend's dedupe window,\n * and the CLI reports the backend's actual DECISION instead of assuming a beep.\n */\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n type BirdyBeepAgentEvent,\n createSender as defaultCreateSender,\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 event\",\n body: \"If you can see this, your machine is wired up correctly.\",\n metadata: { test: true },\n },\n opts,\n );\n}\n\nexport interface TestCommandDeps {\n createSender?: (baseUrl: string) => Sender;\n tokenOptions?: TokenStoreOptions;\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 result = await makeSender(resolveApiUrl()).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 });\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 ctx.io.line(\"✓ Test event delivered — check your phone for a test Beep.\");\n } else if (result.decision === \"suppressed\") {\n ctx.io.line(\n \"⚠ The backend accepted the test event but suppressed the push — this machine \" +\n \"or integration is probably muted. Check mutes in the app, or run `birdybeep doctor`.\",\n );\n } else if (result.decision === \"deduped\") {\n ctx.io.line(\n \"⚠ The backend accepted the test event but folded it into a recent duplicate — \" +\n \"wait ~30s and run `birdybeep test` again.\",\n );\n } else {\n ctx.io.line(\n `⚠ The backend accepted the test event but decided \"${result.decision}\" — no push ` +\n \"was sent. Run `birdybeep doctor`.\",\n );\n }\n } else if (result.outcome === \"queued\") {\n ctx.io.line(\"• Offline — test event queued; it will deliver when you reconnect.\");\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); a hard reject is an error.\n return result.outcome === \"dropped\" ? EXIT.ERROR : EXIT.OK;\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 { createLoginCommand } from \"./commands/login\";\nimport { createLogoutCommand } from \"./commands/logout\";\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/** Build the full §9.4 command tree. */\nexport function buildCommands(): Command[] {\n return [\n createLoginCommand(),\n createLogoutCommand(),\n createStatusCommand(),\n createTestCommand(),\n createDoctorCommand(),\n createAgentCommand(),\n createHookCommand(),\n createQueueCommand(),\n createReportStatusCommand(),\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 { 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\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 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 ...(deps.ensureConfig !== undefined ? { ensureConfig: deps.ensureConfig } : {}),\n });\n}\n"],"mappings":"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","names":["import_agent_core","import_claude_code","import_codex","import_opencode","import_node_fs","import_agent_core","import_agent_core","DEFAULT_ADAPTERS","defaultCreateSender","import_agent_core","import_claude_code","import_codex","import_opencode","defaultCreateSender","import_agent_core","import_agent_core","import_agent_core","import_agent_core","import_agent_core","import_claude_code","import_codex","import_opencode","DEFAULT_ADAPTERS","base","import_agent_core","import_claude_code","import_codex","import_opencode","DEFAULT_ADAPTERS","defaultCreateSender","import_agent_core","defaultCreateSender"]}
@@ -0,0 +1,102 @@
1
+ /** Shared exit-code convention so callers (humans + agents) can branch on the result. */
2
+ declare const EXIT: {
3
+ readonly OK: 0;
4
+ readonly ERROR: 1;
5
+ readonly USAGE: 2;
6
+ };
7
+ /** A minimal output sink (process.stdout/stderr in prod; capturing buffers in tests). */
8
+ interface Writer {
9
+ write(s: string): void;
10
+ }
11
+ interface GlobalFlags {
12
+ /** Machine-readable JSON output for agents/scripts. */
13
+ json: boolean;
14
+ /** Never prompt; fail fast (non-zero) when a required value is missing. */
15
+ nonInteractive: boolean;
16
+ help: boolean;
17
+ version: boolean;
18
+ }
19
+ /** Json-aware output. `line`/`result` are mutually exclusive by mode so stdout stays clean. */
20
+ interface Io {
21
+ readonly json: boolean;
22
+ /** Human line → stdout (suppressed in `--json` mode). */
23
+ line(text: string): void;
24
+ /** Always → stderr (errors/warnings show in both modes). */
25
+ errline(text: string): void;
26
+ /** Structured result → stdout as JSON (only in `--json` mode). */
27
+ result(value: unknown): void;
28
+ /** Emit the right one for the mode: human text, or the structured value as JSON. */
29
+ emit(human: string, json: unknown): void;
30
+ }
31
+ declare function createIo(json: boolean, stdout: Writer, stderr: Writer): Io;
32
+ interface CommandContext {
33
+ /** Positional args after the resolved command path. */
34
+ args: string[];
35
+ flags: GlobalFlags;
36
+ io: Io;
37
+ }
38
+ interface Command {
39
+ name: string;
40
+ summary: string;
41
+ /** One-line usage shown in the command's own `--help`. */
42
+ usage?: string;
43
+ /** Nested subcommands (e.g. `agent install` / `agent uninstall`). */
44
+ subcommands?: Command[];
45
+ /** Command logic; returns the intended exit code. Absent for pure command groups. */
46
+ run?(ctx: CommandContext): Promise<number> | number;
47
+ }
48
+ /** Thrown by a command when a required value is missing under `--non-interactive`. */
49
+ declare class MissingInputError extends Error {
50
+ readonly field: string;
51
+ constructor(field: string);
52
+ }
53
+ /**
54
+ * Resolve a value that may require interaction. Returns `provided` when present; otherwise
55
+ * throws {@link MissingInputError} under `--non-interactive` (so the CLI fails fast instead
56
+ * of hanging), or returns undefined for the caller to prompt in interactive mode.
57
+ */
58
+ declare function requireValue<T>(ctx: CommandContext, field: string, provided: T | undefined): T;
59
+ /** Split a raw argv into global flags + the remaining (command path + positional) tokens. */
60
+ declare function parseGlobalFlags(argv: string[]): {
61
+ flags: GlobalFlags;
62
+ rest: string[];
63
+ };
64
+ interface DispatchDeps {
65
+ version: string;
66
+ commands: Command[];
67
+ stdout: Writer;
68
+ stderr: Writer;
69
+ /** Skip the config-dir bootstrap (tests that don't want filesystem side effects). */
70
+ ensureConfig?: boolean;
71
+ }
72
+ /**
73
+ * Run the CLI against an argv slice (without `node`/script path). Resolves the command
74
+ * (with nested subcommands), handles `--help`/`--version`, and returns the exit code.
75
+ * Never throws — command errors become a stderr message + {@link EXIT.ERROR}.
76
+ */
77
+ declare function dispatch(argv: string[], deps: DispatchDeps): Promise<number>;
78
+
79
+ /** Build the full §9.4 command tree. */
80
+ declare function buildCommands(): Command[];
81
+
82
+ /**
83
+ * CLI version — single-sourced from package.json at build time (s0o7). `tsup.config.ts`
84
+ * injects the real `@birdybeep/cli` version via the `__CLI_VERSION__` esbuild define, so
85
+ * the shipped binary reports its true version for `--version` and the `cli_version` it
86
+ * sends on `/pair/start` (the mobile approval sheet's machine identity). The `0.0.0`
87
+ * fallback only applies to non-bundled runs (vitest / tsx), where the define is absent.
88
+ */
89
+ declare const CLI_VERSION: string;
90
+
91
+ interface RunCliDeps {
92
+ stdout?: Writer;
93
+ stderr?: Writer;
94
+ /** Override the command registry (tests). Defaults to the real §9.4 tree. */
95
+ commands?: Command[];
96
+ /** Skip the config-dir bootstrap (tests without filesystem side effects). */
97
+ ensureConfig?: boolean;
98
+ }
99
+ /** Run the CLI against an argv slice (without `node`/script path). Returns the exit code. */
100
+ declare function runCli(argv: string[], deps?: RunCliDeps): Promise<number>;
101
+
102
+ export { CLI_VERSION, type Command, type CommandContext, type DispatchDeps, EXIT, type GlobalFlags, type Io, MissingInputError, type RunCliDeps, type Writer, buildCommands, createIo, dispatch, parseGlobalFlags, requireValue, runCli };
@@ -0,0 +1,102 @@
1
+ /** Shared exit-code convention so callers (humans + agents) can branch on the result. */
2
+ declare const EXIT: {
3
+ readonly OK: 0;
4
+ readonly ERROR: 1;
5
+ readonly USAGE: 2;
6
+ };
7
+ /** A minimal output sink (process.stdout/stderr in prod; capturing buffers in tests). */
8
+ interface Writer {
9
+ write(s: string): void;
10
+ }
11
+ interface GlobalFlags {
12
+ /** Machine-readable JSON output for agents/scripts. */
13
+ json: boolean;
14
+ /** Never prompt; fail fast (non-zero) when a required value is missing. */
15
+ nonInteractive: boolean;
16
+ help: boolean;
17
+ version: boolean;
18
+ }
19
+ /** Json-aware output. `line`/`result` are mutually exclusive by mode so stdout stays clean. */
20
+ interface Io {
21
+ readonly json: boolean;
22
+ /** Human line → stdout (suppressed in `--json` mode). */
23
+ line(text: string): void;
24
+ /** Always → stderr (errors/warnings show in both modes). */
25
+ errline(text: string): void;
26
+ /** Structured result → stdout as JSON (only in `--json` mode). */
27
+ result(value: unknown): void;
28
+ /** Emit the right one for the mode: human text, or the structured value as JSON. */
29
+ emit(human: string, json: unknown): void;
30
+ }
31
+ declare function createIo(json: boolean, stdout: Writer, stderr: Writer): Io;
32
+ interface CommandContext {
33
+ /** Positional args after the resolved command path. */
34
+ args: string[];
35
+ flags: GlobalFlags;
36
+ io: Io;
37
+ }
38
+ interface Command {
39
+ name: string;
40
+ summary: string;
41
+ /** One-line usage shown in the command's own `--help`. */
42
+ usage?: string;
43
+ /** Nested subcommands (e.g. `agent install` / `agent uninstall`). */
44
+ subcommands?: Command[];
45
+ /** Command logic; returns the intended exit code. Absent for pure command groups. */
46
+ run?(ctx: CommandContext): Promise<number> | number;
47
+ }
48
+ /** Thrown by a command when a required value is missing under `--non-interactive`. */
49
+ declare class MissingInputError extends Error {
50
+ readonly field: string;
51
+ constructor(field: string);
52
+ }
53
+ /**
54
+ * Resolve a value that may require interaction. Returns `provided` when present; otherwise
55
+ * throws {@link MissingInputError} under `--non-interactive` (so the CLI fails fast instead
56
+ * of hanging), or returns undefined for the caller to prompt in interactive mode.
57
+ */
58
+ declare function requireValue<T>(ctx: CommandContext, field: string, provided: T | undefined): T;
59
+ /** Split a raw argv into global flags + the remaining (command path + positional) tokens. */
60
+ declare function parseGlobalFlags(argv: string[]): {
61
+ flags: GlobalFlags;
62
+ rest: string[];
63
+ };
64
+ interface DispatchDeps {
65
+ version: string;
66
+ commands: Command[];
67
+ stdout: Writer;
68
+ stderr: Writer;
69
+ /** Skip the config-dir bootstrap (tests that don't want filesystem side effects). */
70
+ ensureConfig?: boolean;
71
+ }
72
+ /**
73
+ * Run the CLI against an argv slice (without `node`/script path). Resolves the command
74
+ * (with nested subcommands), handles `--help`/`--version`, and returns the exit code.
75
+ * Never throws — command errors become a stderr message + {@link EXIT.ERROR}.
76
+ */
77
+ declare function dispatch(argv: string[], deps: DispatchDeps): Promise<number>;
78
+
79
+ /** Build the full §9.4 command tree. */
80
+ declare function buildCommands(): Command[];
81
+
82
+ /**
83
+ * CLI version — single-sourced from package.json at build time (s0o7). `tsup.config.ts`
84
+ * injects the real `@birdybeep/cli` version via the `__CLI_VERSION__` esbuild define, so
85
+ * the shipped binary reports its true version for `--version` and the `cli_version` it
86
+ * sends on `/pair/start` (the mobile approval sheet's machine identity). The `0.0.0`
87
+ * fallback only applies to non-bundled runs (vitest / tsx), where the define is absent.
88
+ */
89
+ declare const CLI_VERSION: string;
90
+
91
+ interface RunCliDeps {
92
+ stdout?: Writer;
93
+ stderr?: Writer;
94
+ /** Override the command registry (tests). Defaults to the real §9.4 tree. */
95
+ commands?: Command[];
96
+ /** Skip the config-dir bootstrap (tests without filesystem side effects). */
97
+ ensureConfig?: boolean;
98
+ }
99
+ /** Run the CLI against an argv slice (without `node`/script path). Returns the exit code. */
100
+ declare function runCli(argv: string[], deps?: RunCliDeps): Promise<number>;
101
+
102
+ export { CLI_VERSION, type Command, type CommandContext, type DispatchDeps, EXIT, type GlobalFlags, type Io, MissingInputError, type RunCliDeps, type Writer, buildCommands, createIo, dispatch, parseGlobalFlags, requireValue, runCli };
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import {
2
+ CLI_VERSION,
3
+ EXIT,
4
+ MissingInputError,
5
+ buildCommands,
6
+ createIo,
7
+ dispatch,
8
+ parseGlobalFlags,
9
+ requireValue,
10
+ runCli
11
+ } from "./chunk-2KWA3QXF.js";
12
+ export {
13
+ CLI_VERSION,
14
+ EXIT,
15
+ MissingInputError,
16
+ buildCommands,
17
+ createIo,
18
+ dispatch,
19
+ parseGlobalFlags,
20
+ requireValue,
21
+ runCli
22
+ };
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@birdybeep/cli",
3
+ "version": "0.0.1",
4
+ "description": "The BirdyBeep CLI — pair your machine, install agent adapters, and stream coding-agent lifecycle events to BirdyBeep.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Mojave-Labs/birdybeep-agent.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "type": "module",
12
+ "bin": {
13
+ "birdybeep": "./dist/bin.js"
14
+ },
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "require": {
25
+ "types": "./dist/index.d.cts",
26
+ "default": "./dist/index.cjs"
27
+ }
28
+ }
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "dependencies": {
34
+ "uqr": "0.1.2",
35
+ "@birdybeep/agent-core": "0.0.1",
36
+ "@birdybeep/claude-code": "0.0.1",
37
+ "@birdybeep/codex": "0.0.1",
38
+ "@birdybeep/opencode": "0.0.1"
39
+ },
40
+ "devDependencies": {
41
+ "@birdybeep/test-harness": "0.0.0"
42
+ },
43
+ "engines": {
44
+ "node": ">=20.11.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "typecheck": "tsc --noEmit -p tsconfig.json",
49
+ "lint": "eslint .",
50
+ "test": "vitest run"
51
+ }
52
+ }