@madarco/agentbox 0.7.0 → 0.9.0
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.
- package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
- package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
- package/dist/chunk-BXQMIEHC.js.map +1 -0
- package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
- package/dist/chunk-G3H2L3O2.js.map +1 -0
- package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
- package/dist/chunk-GU5LW4B5.js.map +1 -0
- package/dist/chunk-KL36BRN4.js +455 -0
- package/dist/chunk-KL36BRN4.js.map +1 -0
- package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
- package/dist/chunk-LEV3KICD.js.map +1 -0
- package/dist/chunk-MTVI44DW.js +662 -0
- package/dist/chunk-MTVI44DW.js.map +1 -0
- package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
- package/dist/chunk-NCJP5MTN.js.map +1 -0
- package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
- package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
- package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
- package/dist/dist-CX5CGVEB.js.map +1 -0
- package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
- package/dist/dist-GDHP34ZK.js.map +1 -0
- package/dist/dist-XML54CNB.js +849 -0
- package/dist/dist-XML54CNB.js.map +1 -0
- package/dist/index.js +3881 -867
- package/dist/index.js.map +1 -1
- package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
- package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
- package/package.json +7 -5
- package/runtime/daytona/custom-system-CLAUDE.md +39 -0
- package/runtime/docker/Dockerfile.box +22 -0
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
- package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
- package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
- package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
- package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
- package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
- package/runtime/hetzner/agentbox-setup-skill.md +1 -1
- package/runtime/hetzner/agentbox-vnc-start +15 -1
- package/runtime/hetzner/claude-managed-settings.json +62 -1
- package/runtime/hetzner/ctl.cjs +1214 -98
- package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
- package/runtime/hetzner/gh-shim +263 -0
- package/runtime/hetzner/git-shim +131 -0
- package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/scripts/install-box.sh +11 -2
- package/runtime/relay/bin.cjs +1146 -63
- package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
- package/runtime/vercel/agentbox-codex-hooks.json +68 -0
- package/runtime/vercel/agentbox-open +28 -0
- package/runtime/vercel/agentbox-setup-skill.md +196 -0
- package/runtime/vercel/agentbox-vnc-start +91 -0
- package/runtime/vercel/claude-managed-settings.json +115 -0
- package/runtime/vercel/ctl.cjs +23466 -0
- package/runtime/vercel/custom-system-CLAUDE.md +50 -0
- package/runtime/vercel/gh-shim +263 -0
- package/runtime/vercel/git-shim +131 -0
- package/runtime/vercel/scripts/provision.sh +274 -0
- package/share/agentbox-setup/SKILL.md +1 -1
- package/share/host-skills/agentbox/SKILL.md +29 -0
- package/share/host-skills/agentbox-info/SKILL.md +211 -0
- package/share/host-skills/codex/agentbox.md +35 -0
- package/share/host-skills/opencode/agentbox.md +26 -0
- package/dist/_cloud-attach-DMVH6GWO.js +0 -12
- package/dist/chunk-7KOEFGN2.js.map +0 -1
- package/dist/chunk-NAVL4R34.js.map +0 -1
- package/dist/chunk-NW5NYTQM.js.map +0 -1
- package/dist/chunk-UK72UQ5U.js.map +0 -1
- package/dist/chunk-V5KZGB5V.js.map +0 -1
- package/dist/dist-QZGJIBT5.js.map +0 -1
- package/dist/dist-R67WMLCF.js.map +0 -1
- /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
- /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
- /package/dist/{dist-ETCFRVPA.js.map → dist-32EZBYG4.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/_cloud-attach.ts","../src/provider/registry.ts","../src/wrapped-pty/run.ts","../src/pty/pty-backend.ts","../src/terminal/host.ts","../src/terminal/title.ts","../src/wrapped-pty/input-router.ts","../src/dashboard/sidebar.ts","../src/wrapped-pty/footer.ts","../src/wrapped-pty/prompt-client.ts","../src/lib/paste-image.ts","../src/lib/host-clipboard.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { DEFAULT_RELAY_PORT } from '@agentbox/sandbox-docker';\nimport type { BoxRecord } from '@agentbox/core';\nimport type { AttachOpenIn } from '@agentbox/config';\nimport { providerForBox } from '../provider/registry.js';\nimport { runWrappedAttach } from '../wrapped-pty/index.js';\nimport { pasteHostClipboardImage } from '../lib/paste-image.js';\nimport { clipboardCaptureAvailable } from '../lib/host-clipboard.js';\n\nconst RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;\n\n/**\n * Attach to (or create) a tmux session inside a cloud sandbox over SSH and\n * run an agent CLI inside it. Shared between `agentbox claude`/`codex`/\n * `opencode` so the SSH + tmux mechanics live in one place.\n *\n * The inner command tmux runs is `bash -lc 'exec <binary>'`:\n * - login shell so `/home/vscode/.local/bin` is on PATH and `/etc/profile.d/\n * agentbox.sh` exports `AGENTBOX_BOX_*` env;\n * - `exec` so the agent gets PID 2 (Ctrl-c in the agent kills the session\n * cleanly rather than dropping to bash).\n *\n * When `extraArgs` is non-empty, we base64-encode the argv (one arg per line)\n * and hand the inner shell a small `mapfile`-based launcher that reconstructs\n * the array — see `buildCloudAttachInnerCommand`. Base64 is alphanumeric+`/+=`\n * so it survives every shell-quoting layer (host single-quote, SSH, tmux,\n * bash) untouched, which avoids the 3-layer escaping mess the literal form\n * would otherwise require.\n */\nexport interface CloudAgentAttachArgs {\n box: BoxRecord;\n /** In-sandbox binary path or name (`claude`, `codex`, `opencode`). */\n binary: string;\n /** Tmux session name (e.g. `claude`). */\n sessionName: string;\n /** Mode label for the wrapper's footer. */\n mode: 'claude' | 'codex' | 'opencode';\n /**\n * Extra args the user typed after `--`. Passed through to the in-box agent\n * verbatim via a base64-encoded launcher. Limitation: args containing\n * literal `\\n` aren't supported (none of claude/codex/opencode flags do).\n */\n extraArgs?: string[];\n /**\n * Where to open the attached session in the host's terminal (`split`/`window`/\n * `tab`/`same`). Forwarded to `runWrappedAttach`. Daytona attaches are forced\n * to `same` for now because `provider.buildAttach()` may return a `cleanup`\n * that tears down per-call SSH tunnels — running cleanup while a detached\n * new pane still holds the connection would kill the pane. Hetzner's\n * ControlMaster is per-box-lifetime so spawn-and-detach is safe there.\n */\n openIn?: AttachOpenIn;\n}\n\n/**\n * Render the inner shell command tmux runs inside the cloud sandbox. Exported\n * so unit tests can exercise the base64 round-trip without spinning up SSH.\n *\n * Empty `extraArgs` keeps the no-args path identical to the pre-args\n * behaviour — `bash -lc 'exec <binary>'` with a backslash-space so the outer\n * shell-quoting layers don't split `exec` from the binary name.\n */\nexport function buildCloudAttachInnerCommand(binary: string, extraArgs?: string[]): string {\n if (!extraArgs || extraArgs.length === 0) {\n return `bash -lc exec\\\\ ${binary}`;\n }\n // One arg per line, base64-encoded. The launcher runs `mapfile -t A` against\n // the decoded stream, then `exec <binary> \"${A[@]}\"` so each arg lands as\n // its own argv element — quotes/spaces inside an arg are preserved exactly\n // because base64 is opaque to every outer shell quoting pass.\n const blob = Buffer.from(extraArgs.join('\\n'), 'utf8').toString('base64');\n // The decode feeds `mapfile` via a **here-string**, NOT process substitution\n // (`< <(…)`). Process substitution needs `/dev/fd/N`, and the Vercel Sandbox\n // (Firecracker microVM, AL2023) has no `/dev/fd` — so `mapfile -t A < <(…)`\n // fails with `/dev/fd/63: No such file or directory`, A stays empty, and the\n // agent launches with no args (the wizard's initial setup prompt is silently\n // dropped). A here-string is backed by a temp file, needs no `/dev/fd`, and\n // works on every backend (docker/daytona/hetzner unaffected). `$(…)` strips\n // the trailing newline `<<<` re-adds, so mapfile -t yields one element per\n // arg exactly as the join produced them.\n //\n // **bash -lc body MUST be single-quoted, not double-quoted.** When tmux\n // launches the session command, it goes through `/bin/sh -c <cmd>`. If we\n // double-quote, sh's parser sees `\"${A[@]}\"` and expands it eagerly —\n // before mapfile ever runs — to the empty string, so claude is invoked as\n // `claude \"\"` and the wizard's initial prompt is silently dropped. Single\n // quotes are inert in sh's parser: the literal `${A[@]}` (and `$(…)`) reach\n // bash, which runs them AFTER the outer sh layer. The outer shellSingle wrap\n // in renderInnerCommand re-escapes any internal `'` as `'\\''`; this body has\n // no single quotes (it uses double quotes around the here-string), so it\n // composes fine.\n return `bash -lc 'mapfile -t A <<< \"$(echo ${blob} | base64 -d)\"; exec ${binary} \"\\${A[@]}\"'`;\n}\n\nexport async function cloudAgentAttach(args: CloudAgentAttachArgs): Promise<void> {\n const provider = await providerForBox(args.box);\n if (!provider.buildAttach) {\n throw new Error(`provider '${provider.name}' does not support interactive attach`);\n }\n const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs);\n // Daytona-only: force inline attach. `spec.cleanup` would otherwise run as\n // soon as the host process returns from the spawn (before the new pane has\n // released the per-call SSH tunnel), breaking the detached attach.\n const safeOpenIn: AttachOpenIn | undefined =\n args.box.provider === 'daytona' ? 'same' : args.openIn;\n\n // New-terminal attaches (tab/window/split) re-invoke `agentbox <agent> attach`\n // in the fresh pane, and that re-invocation carries NO `extraArgs` — so for a\n // resume/teleport launch (`claude --resume <id>`, etc.) the session would\n // otherwise be created fresh, dropping the resumed session. Pre-create the\n // session detached here with the full command; the re-invoked attach then\n // finds it via `tmux has-session` and just attaches. (Inline attach runs the\n // full command itself, so it doesn't need this.)\n if (safeOpenIn && safeOpenIn !== 'same' && args.extraArgs && args.extraArgs.length > 0) {\n const pre = await provider.buildAttach(args.box, 'agent', {\n sessionName: args.sessionName,\n command,\n detached: true,\n });\n try {\n await runDetached(pre.argv, pre.env);\n } finally {\n if (pre.cleanup) await pre.cleanup();\n }\n }\n\n const spec = await provider.buildAttach(args.box, 'agent', {\n sessionName: args.sessionName,\n command,\n });\n // claude only, and only when this host can capture a clipboard image (macOS,\n // or a Linux desktop with xclip/wl-paste). Otherwise Ctrl+V forwards verbatim.\n const canPaste =\n args.mode === 'claude' && (await clipboardCaptureAvailable());\n try {\n const code = await runWrappedAttach({\n container: args.box.name,\n command: spec.argv[0],\n dockerArgv: spec.argv.slice(1),\n env: spec.env,\n relayBaseUrl: RELAY_HOST_URL,\n boxId: args.box.id,\n boxName: args.box.name,\n projectIndex: args.box.projectIndex,\n mode: args.mode,\n detachable: true,\n openIn: safeOpenIn,\n onPasteImage: canPaste\n ? () => pasteHostClipboardImage(provider, args.box)\n : undefined,\n });\n process.exit(code);\n } finally {\n if (spec.cleanup) await spec.cleanup();\n }\n}\n\n/**\n * Run an attach-style argv non-interactively to completion (used for the\n * `detached` session pre-start). stdio is ignored — the remote command only\n * creates + configures the tmux session and exits; there's nothing to show.\n * Resolves on exit regardless of code (a non-zero here shouldn't block the\n * subsequent attach, which surfaces any real failure to the user).\n */\nfunction runDetached(argv: string[], env?: Record<string, string>): Promise<void> {\n return new Promise((resolve) => {\n const child = spawn(argv[0]!, argv.slice(1), {\n stdio: 'ignore',\n env: env ? { ...process.env, ...env } : process.env,\n });\n child.on('error', () => resolve());\n child.on('exit', () => resolve());\n });\n}\n","/**\n * Provider registry — resolves a `Provider` for either an existing box (from\n * its `provider` discriminator) or a fresh `create` (from --provider flag /\n * config / default). Lazy `import()` keeps the Daytona SDK out of the Docker\n * hot path.\n */\n\nimport type { EffectiveConfig } from '@agentbox/config';\nimport type { BoxRecord, Provider, ProviderName } from '@agentbox/core';\n\nexport type KnownProviderName = 'docker' | 'daytona' | 'hetzner' | 'vercel';\n\nconst KNOWN: readonly KnownProviderName[] = ['docker', 'daytona', 'hetzner', 'vercel'];\n\nexport function isKnownProvider(name: string): name is KnownProviderName {\n return (KNOWN as readonly string[]).includes(name);\n}\n\nexport async function getProvider(name: ProviderName): Promise<Provider> {\n switch (name) {\n case 'docker': {\n const mod = await import('@agentbox/sandbox-docker');\n return mod.dockerProvider;\n }\n case 'daytona': {\n // Single lazy import covers both the first-run prompt gate and the\n // provider itself — keeps the Daytona SDK off the Docker hot path.\n // The prompt is a no-op when env is already configured or stdin isn't\n // a TTY (scripted callers get the SDK's \"not configured\" error instead\n // of a hung prompt).\n const mod = await import('@agentbox/sandbox-daytona');\n await mod.ensureDaytonaCredentials();\n return mod.daytonaProvider;\n }\n case 'hetzner': {\n // Same lazy-import pattern as daytona. `ensureHetznerCredentials` walks\n // the user through `agentbox hetzner login` on first use. The base-\n // snapshot gate (`ensureHetznerBaseSnapshot`) is deliberately *not*\n // called here: it would chicken-and-egg `agentbox prepare --provider\n // hetzner` (which exists precisely to BUILD the snapshot). The gate\n // lives inside `backend.provision` instead — `prepare` calls the REST\n // client directly, never `provision`, so it slips past the gate while\n // `create`/`claude`/etc. still trip it.\n const mod = await import('@agentbox/sandbox-hetzner');\n await mod.ensureHetznerCredentials();\n return mod.hetznerProvider;\n }\n case 'vercel': {\n // Same lazy-import pattern. `ensureVercelCredentials` walks the user\n // through `agentbox vercel login` (OIDC or token trio) on first use. The\n // base-snapshot gate lives inside `backend.provision` (so `prepare` can\n // build it without tripping the gate), matching the hetzner shape.\n const mod = await import('@agentbox/sandbox-vercel');\n await mod.ensureVercelCredentials();\n return mod.vercelProvider;\n }\n default:\n throw new Error(`unknown sandbox provider: ${String(name)}`);\n }\n}\n\n/** Provider for an existing box record. Defaults to 'docker' for legacy records. */\nexport async function providerForBox(box: BoxRecord): Promise<Provider> {\n return getProvider(box.provider ?? 'docker');\n}\n\nexport interface CreateProviderChoice {\n /** Explicit --provider flag, if the command exposed one. */\n flag?: string;\n /** Effective config (carries box.provider for the layered default). */\n config: EffectiveConfig;\n}\n\n/**\n * Provider for a fresh `agentbox create`. Precedence: --provider flag >\n * box.provider config > 'docker'. Throws if the resolved name isn't registered.\n */\nexport async function providerForCreate(choice: CreateProviderChoice): Promise<Provider> {\n const flag = choice.flag?.trim();\n const name = (flag && flag.length > 0 ? flag : choice.config.box.provider) as ProviderName;\n if (typeof name !== 'string' || name.length === 0 || !isKnownProvider(name)) {\n throw new Error(\n `unknown sandbox provider \"${String(name)}\" (known: ${KNOWN.join(', ')})`,\n );\n }\n return getProvider(name);\n}\n","import { spawn, spawnSync } from 'node:child_process';\nimport { readBoxStatus } from '@agentbox/sandbox-docker';\nimport type { AttachOpenIn } from '@agentbox/config';\nimport { loadPtyBackend } from '../pty/pty-backend.js';\nimport { detectHostTerminal, spawnInNewTerminal } from '../terminal/host.js';\nimport { popTerminalTitle, pushTerminalTitle, setTerminalTitle } from '../terminal/title.js';\nimport {\n createInputRouter,\n type InputRouter,\n type LeaderAction,\n} from './input-router.js';\nimport {\n CURSOR_RESTORE,\n CURSOR_SAVE,\n cursorMoveTo,\n renderFooter,\n SYNC_BEGIN,\n SYNC_END,\n type FooterState,\n} from './footer.js';\nimport { postAnswer, subscribePrompts, type PromptStream } from './prompt-client.js';\nimport type { BoxNoticeEvent, PromptAskEvent } from '@agentbox/relay';\n\nexport interface WrappedAttachOptions {\n /** Docker container name (only used for log lines). */\n container: string;\n /** Full docker argv (e.g. result of buildClaudeAttachArgv). */\n dockerArgv: string[];\n /**\n * The program to spawn for the PTY. Defaults to `'docker'` (the historical\n * behavior; `dockerArgv` is then the docker subcommand argv). Cloud boxes\n * pass `'ssh'` with the Daytona SSH argv instead.\n */\n command?: string;\n /** Extra env merged over `process.env` for the spawned child (e.g. the\n * Vercel provider's `VERCEL_AUTH_TOKEN` for the `sbx` CLI). */\n env?: Record<string, string>;\n /** Relay base URL — http://127.0.0.1:8787 in normal use. */\n relayBaseUrl: string;\n boxId: string;\n /** Friendly box name; rendered in the idle footer. */\n boxName: string;\n /** Per-project box index (BoxRecord.projectIndex). Used together with\n * boxId/boxName to read the per-box status.json for the live session\n * title. Pre-feature boxes lack it; absent is fine. */\n projectIndex?: number;\n /** Mode label affects the idle footer state label only. */\n mode: 'claude' | 'shell' | 'codex' | 'opencode';\n /** Whether the inner session can be detached (tmux-backed). Drives the\n * `Ctrl+a d` detach chord + footer hint. Defaults to `mode === 'claude'`\n * (claude is always tmux-backed); a tmux-backed `agentbox shell` passes\n * `true`, a `--no-tmux` shell leaves it false. */\n detachable?: boolean;\n /** Optional notice printed to stdout *after* the pty exits with code 0\n * (mirrors today's `formatDetachNotice` for `agentbox claude`). */\n detachNotice?: string;\n /** Optional sink for non-fatal errors that we'd otherwise swallow (Ctrl+a\n * action spawn failures, status-poll failures, unexpected prompt-capture\n * rejections). Callers wire this to their command log so post-mortem\n * inspection isn't blind. */\n onError?: (msg: string) => void;\n /** Where to open the attached session. When set to anything other than\n * `same` (or undefined) and the host shell is running inside tmux or iTerm2,\n * the attach runs in a fresh pane/tab/window and this function returns 0\n * without taking over the current terminal. Outside tmux/iTerm2 it falls\n * back to inline attach (the original behavior). */\n openIn?: AttachOpenIn;\n /** Optional host→box clipboard image paste, invoked when the user presses\n * Ctrl+V (wired for claude only). Ships the host clipboard image into the\n * box and loads it into the box's X11 clipboard; resolves with the outcome\n * so the footer can flash a result. The input router re-emits Ctrl+V after\n * this settles, so Claude Code reads the now-loaded clipboard. Omitted →\n * Ctrl+V forwards verbatim. */\n onPasteImage?: () => Promise<'pasted' | 'no-image' | 'error'>;\n}\n\nconst FOOTER_ROWS = 1;\nconst STATUS_POLL_INTERVAL_MS = 3000;\n/** Spinner advance cadence while a `notice` footer is active. */\nconst SPINNER_INTERVAL_MS = 120;\n/** How long the post-action confirmation flash stays in the footer. */\nconst FLASH_DURATION_MS = 2000;\n\n/** Per-action confirmation text shown in the footer flash. */\nconst ACTION_FLASH: Record<Exclude<LeaderAction, 'detach'>, string> = {\n screen: 'Opening noVNC viewer…',\n code: 'Launching VS Code / Cursor…',\n url: 'Opening box URL…',\n};\n\n/** Per-action `agentbox` subcommand: `<sub> <boxId> <...flags>`. */\nconst ACTION_CMD: Record<\n Exclude<LeaderAction, 'detach'>,\n { sub: string; flags: string[] }\n> = {\n screen: { sub: 'screen', flags: [] },\n // --no-wait: don't block on `wait-ready` — the box is already running.\n code: { sub: 'code', flags: ['--no-wait'] },\n url: { sub: 'url', flags: [] },\n};\n\n/** Recursive `agentbox <agent> attach <box> --attach-in same` argv for the\n * new-pane re-entry. Returns null for modes that don't have an `attach`\n * subcommand (notably `shell`), so the caller can skip new-pane spawning. */\nfunction buildAgentboxAttachArgv(\n mode: WrappedAttachOptions['mode'],\n boxName: string,\n): string[] | null {\n if (mode !== 'claude' && mode !== 'codex' && mode !== 'opencode') return null;\n return [mode, 'attach', boxName, '--attach-in', 'same'];\n}\n\n/**\n * Replace `spawnSync('docker', argv, { stdio: 'inherit' })` with a\n * node-pty wrapper that reserves the bottom row for a permission-prompt\n * footer. Falls back transparently to today's spawnSync behavior when\n * node-pty isn't available (optional dep missing), or when stdin/stdout\n * isn't a TTY (piping / non-interactive use).\n *\n * Returns the pty's exit code; caller `process.exit`s with it.\n */\nexport async function runWrappedAttach(opts: WrappedAttachOptions): Promise<number> {\n const command = opts.command ?? 'docker';\n const logErr = (msg: string): void => {\n opts.onError?.(msg);\n };\n\n // Open-in-new-terminal short-circuit: if the user asked for split/window/tab\n // and we're inside tmux or iTerm2, re-invoke `agentbox <agent> attach <box>\n // --attach-in same` in a fresh pane so the new pane runs the full wrapper\n // (footer + prompt channel) against the already-prepared session — same UX\n // as inline, just in a new pane. The host process then exits 0. Unknown\n // hosts, shell mode (no attach subcommand to recurse into), and spawn\n // failures fall through to the inline attach below.\n const openIn = opts.openIn ?? 'same';\n if (openIn !== 'same') {\n const subArgv = buildAgentboxAttachArgv(opts.mode, opts.boxName);\n const host = subArgv ? detectHostTerminal() : 'unknown';\n if (subArgv && host !== 'unknown' && process.argv[1]) {\n const r = await spawnInNewTerminal({\n host,\n mode: openIn,\n argv: [process.execPath, process.argv[1], ...subArgv],\n cwd: process.cwd(),\n title: opts.boxName,\n });\n if (r.launched) {\n process.stdout.write(r.note + '\\n');\n return 0;\n }\n if (r.error) logErr(r.error);\n // fall through to inline attach\n }\n }\n\n if (!process.stdout.isTTY || !process.stdin.isTTY) {\n // Non-interactive path: piping / scripts. Don't wrap — preserves\n // machine-readable stdout, no footer corruption.\n return runFallback(command, opts.dockerArgv, opts.env);\n }\n const backend = await loadPtyBackend();\n if (!backend) {\n // One-line stderr notice; preserves current behavior bit-for-bit.\n process.stderr.write(\n 'agentbox: permission prompts disabled (node-pty backend unavailable)\\n',\n );\n return runFallback(command, opts.dockerArgv, opts.env);\n }\n\n const cols = process.stdout.columns ?? 80;\n const rows = process.stdout.rows ?? 24;\n const innerRows = Math.max(1, rows - FOOTER_ROWS);\n\n const pty = backend.ptySpawn(command, opts.dockerArgv, {\n name: 'xterm-256color',\n cols,\n rows: innerRows,\n env: opts.env ? { ...process.env, ...opts.env } : process.env,\n });\n\n // Mirror the agent's session title to the host terminal/tab title (iTerm2\n // etc.). tmux swallows the inner OSC title (set-titles off), so the host\n // never sees it; we re-emit it ourselves from the polled status below. Save\n // the user's current title first so teardown can restore it. Seed with the\n // box name so the tab is named immediately, before the first status poll.\n pushTerminalTitle();\n let lastEmittedTitle = opts.boxName;\n setTerminalTitle(lastEmittedTitle);\n\n // claude is always tmux-backed; a tmux-backed `agentbox shell` opts in via\n // `detachable: true`, a `--no-tmux` shell leaves it false (nothing to detach).\n const detachable = opts.detachable ?? opts.mode === 'claude';\n\n // Idle footer = dashboard's statusLine() with a single hint (`Control+a:\n // Actions`, expanding to the chord menu while the leader is open). Session\n // title + claude activity come from the per-box status.json polled below.\n let leaderActive = false;\n const buildIdle = (sessionTitle?: string, claudeActivity?: string): FooterState => ({\n kind: 'idle',\n boxName: opts.boxName,\n sessionTitle,\n claudeActivity,\n mode: opts.mode,\n detachable,\n leaderActive,\n });\n let footerState: FooterState = buildIdle();\n let lastSessionTitle: string | undefined;\n let lastActivity: string | undefined;\n // Prompt + notice + flash are tracked independently of `footerState`;\n // `recomputeFooter` derives the visible state (prompt > notice > flash > idle).\n let capturingPrompt: PromptAskEvent | null = null;\n let activeNotice: BoxNoticeEvent | null = null;\n let noticeFrame = 0;\n let spinnerTimer: ReturnType<typeof setInterval> | null = null;\n // Transient confirmation shown after a Ctrl+a action fires.\n let flashMessage: string | null = null;\n let flashTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Lazy SGR mirror: when the inner pty's most recent attribute is bright\n // bold, our footer paint won't reset it correctly via the inner program's\n // next byte. We always end the footer with SGR reset, but the inner\n // program may be in the middle of a graphics run when the footer redraw\n // happens — wrap the redraw in cursor save/restore + sync output so the\n // inner program never sees our cursor moves and the user sees one atomic\n // frame.\n const redrawFooter = (): void => {\n const cs = process.stdout.columns ?? cols;\n const rs = process.stdout.rows ?? rows;\n const line = renderFooter(footerState, cs);\n // Position at the last row, then write. Save/restore around the lot so\n // the inner pty's cursor doesn't move.\n const payload =\n SYNC_BEGIN +\n CURSOR_SAVE +\n cursorMoveTo(rs, 1) +\n line +\n CURSOR_RESTORE +\n SYNC_END;\n process.stdout.write(payload);\n };\n\n // Derive `footerState` from the current prompt / notice / flash / idle\n // inputs. A pending prompt outranks a notice (it hard-blocks the box's\n // RPC); a notice outranks a flash; a flash outranks idle. Called after any\n // input changes.\n const recomputeFooter = (): void => {\n if (capturingPrompt) {\n footerState = { kind: 'prompt', prompt: capturingPrompt };\n } else if (activeNotice) {\n footerState = { kind: 'notice', message: activeNotice.message, frame: noticeFrame };\n } else if (flashMessage) {\n footerState = { kind: 'flash', message: flashMessage };\n } else {\n footerState = buildIdle(lastSessionTitle, lastActivity);\n }\n };\n\n const startSpinner = (): void => {\n if (spinnerTimer) return;\n spinnerTimer = setInterval(() => {\n noticeFrame++;\n // Only repaint while the notice is the visible state — if a prompt is\n // covering it the frame still advances so it resumes mid-animation.\n if (footerState.kind === 'notice') {\n recomputeFooter();\n redrawFooter();\n }\n }, SPINNER_INTERVAL_MS);\n if (typeof spinnerTimer.unref === 'function') spinnerTimer.unref();\n };\n const stopSpinner = (): void => {\n if (spinnerTimer) {\n clearInterval(spinnerTimer);\n spinnerTimer = null;\n }\n };\n\n // Wire pty -> stdout. The inner program writes raw bytes; we forward as-is.\n // The outer terminal has `rows` real rows, but the pty thinks it has `innerRows`.\n // The inner program's writes can still physically touch row `rows` (our footer\n // row) via: (1) scroll when its bottom line emits a newline — the terminal\n // scrolls the whole screen and row `rows` gets cleared; (2) clear-screen\n // sequences like `\\x1b[2J`; (3) alt-screen entry `\\x1b[?1049h`; (4) column\n // wraparound from the inner program's last row. The scroll-region setup\n // below limits (1); always-repaint here handles the rest. Each redraw is\n // wrapped in synchronized output (DECSET 2026) so the user never sees a\n // half-painted frame on terminals that support it (iTerm2/WezTerm/kitty/\n // Apple Terminal/Ghostty).\n pty.onData((d: string) => {\n process.stdout.write(d);\n redrawFooter();\n });\n\n // Ctrl+a leader chord map — keys mirror the dashboard's (`c`/`s`/`u`).\n // A detachable (tmux-backed) session also gets `d: detach`; a plain\n // `--no-tmux` shell has nothing to detach from.\n const leaderChords: Record<string, LeaderAction> = detachable\n ? { c: 'code', s: 'screen', u: 'url', d: 'detach' }\n : { c: 'code', s: 'screen', u: 'url' };\n\n // Run a Ctrl+a leader action. `detach` writes the tmux detach sequence to\n // the pty (`\\x02` = Ctrl+b, tmux's secondary prefix; `d` = detach-client) —\n // the attach process then exits 0 and teardown runs normally. The other\n // actions shell out to the real `agentbox` subcommand, detached, so the\n // long-running open/launch never blocks (or corrupts) this terminal.\n const runAction = (name: LeaderAction): void => {\n if (name === 'detach') {\n pty.write('\\x02d');\n return;\n }\n const cliEntry = process.argv[1];\n if (typeof cliEntry === 'string' && cliEntry.length > 0) {\n const cmd = ACTION_CMD[name];\n try {\n spawn(\n process.execPath,\n [cliEntry, cmd.sub, opts.boxId, ...cmd.flags],\n { detached: true, stdio: 'ignore' },\n ).unref();\n } catch (e) {\n // Best-effort — the footer flash still shows. Surface for inspection.\n logErr(`leader-action spawn (${name}) failed: ${(e as Error).message}`);\n }\n }\n flashMessage = ACTION_FLASH[name];\n if (flashTimer) clearTimeout(flashTimer);\n flashTimer = setTimeout(() => {\n flashTimer = null;\n flashMessage = null;\n recomputeFooter();\n redrawFooter();\n }, FLASH_DURATION_MS);\n if (typeof flashTimer.unref === 'function') flashTimer.unref();\n recomputeFooter();\n redrawFooter();\n };\n\n // Ctrl+V image paste: hold a \"Pasting image…\" notice in the footer while the\n // host clipboard image is shipped into the box, then flash the outcome. The\n // input router re-emits the Ctrl+V once this resolves, so Claude reads the\n // now-loaded box clipboard. Never throws — failures degrade to a flash.\n const handlePasteImage = async (): Promise<void> => {\n if (!opts.onPasteImage) return;\n if (flashTimer) {\n clearTimeout(flashTimer);\n flashTimer = null;\n }\n flashMessage = 'Pasting image…';\n recomputeFooter();\n redrawFooter();\n let result: 'pasted' | 'no-image' | 'error' = 'error';\n try {\n result = await opts.onPasteImage();\n } catch (e) {\n logErr(`paste-image failed: ${(e as Error).message}`);\n }\n flashMessage =\n result === 'pasted'\n ? 'Image pasted'\n : result === 'no-image'\n ? 'No image in clipboard'\n : 'Image paste failed';\n flashTimer = setTimeout(() => {\n flashTimer = null;\n flashMessage = null;\n recomputeFooter();\n redrawFooter();\n }, FLASH_DURATION_MS);\n if (typeof flashTimer.unref === 'function') flashTimer.unref();\n recomputeFooter();\n redrawFooter();\n };\n\n // Wire stdin -> pty (through the router so prompts + the leader can intercept).\n const router: InputRouter = createInputRouter({\n onForward: (b) => {\n // node-pty wants utf8 strings; stdin is binary safe via Buffer.\n pty.write(b.toString('utf8'));\n },\n onAnswer: (body) => {\n // Fire-and-forget; the relay-side route is idempotent. We don't\n // block the input flow on the network roundtrip.\n void postAnswer({ relayBaseUrl: opts.relayBaseUrl, body });\n capturingPrompt = null;\n recomputeFooter();\n redrawFooter();\n },\n leaderChords,\n onLeaderChange: (open) => {\n leaderActive = open;\n recomputeFooter();\n redrawFooter();\n },\n onAction: (name) => {\n runAction(name);\n },\n onPasteImage: opts.onPasteImage ? handlePasteImage : undefined,\n });\n\n if (process.stdin.isTTY) process.stdin.setRawMode(true);\n process.stdin.resume();\n const onStdinData = (chunk: Buffer): void => {\n router.feed(chunk);\n };\n process.stdin.on('data', onStdinData);\n\n // Resize: keep the pty one row shorter than the host terminal; the footer\n // owns the last row directly. Re-apply the scroll region too — most\n // terminals reset DECSTBM on resize.\n const onResize = (): void => {\n const cs = process.stdout.columns ?? cols;\n const rs = process.stdout.rows ?? rows;\n const inner = Math.max(1, rs - FOOTER_ROWS);\n pty.resize(cs, inner);\n process.stdout.write(`\\x1b[1;${String(inner)}r`);\n redrawFooter();\n };\n process.stdout.on('resize', onResize);\n\n // SSE: subscribe to the relay's prompt stream for this box.\n const stream: PromptStream = subscribePrompts({\n relayBaseUrl: opts.relayBaseUrl,\n boxId: opts.boxId,\n onPrompt: (ev: PromptAskEvent) => {\n capturingPrompt = ev;\n recomputeFooter();\n redrawFooter();\n // capture() returns a Promise that resolves with the answer body; the\n // input-router's onAnswer callback already POSTs and resets the footer.\n // We just need to await so unhandled rejections (router.abort) don't\n // crash the process.\n router.capture(ev).catch((e: unknown) => {\n // Expected reasons: sibling answered ('resolved-elsewhere'), pty exit.\n // Anything else is a real bug worth surfacing.\n const msg = e instanceof Error ? e.message : String(e);\n if (msg !== 'resolved-elsewhere') {\n logErr(`prompt capture rejected: ${msg}`);\n }\n });\n },\n onResolved: (id: string) => {\n // Clear footer if it's still showing this id (sibling wrapper won).\n if (capturingPrompt && capturingPrompt.id === id) {\n capturingPrompt = null;\n router.abort('resolved-elsewhere');\n recomputeFooter();\n redrawFooter();\n }\n },\n onNotice: (ev: BoxNoticeEvent) => {\n activeNotice = ev;\n startSpinner();\n recomputeFooter();\n redrawFooter();\n },\n onNoticeCleared: (id: string) => {\n if (activeNotice && activeNotice.id === id) {\n activeNotice = null;\n stopSpinner();\n recomputeFooter();\n redrawFooter();\n }\n },\n });\n\n // Poll the box's status.json for `claude.sessionTitle` so the idle\n // footer can show what claude set as its terminal title (mirrors the\n // dashboard's sidebar entry). Best-effort — paused/stopped boxes and\n // pre-status-feature boxes return null and we just keep the previous\n // title (or no title).\n const pollStatus = async (): Promise<void> => {\n try {\n const status = await readBoxStatus({\n id: opts.boxId,\n name: opts.boxName,\n projectIndex: opts.projectIndex,\n });\n // Read the title/activity from the body of the agent we attached to;\n // shell mode has no agent session so it keeps the box-name title.\n const body =\n opts.mode === 'codex'\n ? status?.codex\n : opts.mode === 'opencode'\n ? status?.opencode\n : opts.mode === 'shell'\n ? undefined\n : status?.claude;\n const nextTitle = body?.sessionTitle?.trim() || undefined;\n const nextActivity = body?.state || undefined;\n // Mirror the live title to the host terminal/tab, falling back to the box\n // name until the agent sets one. Deduped so we don't spam the terminal.\n const desiredTitle = nextTitle ?? opts.boxName;\n if (desiredTitle !== lastEmittedTitle) {\n lastEmittedTitle = desiredTitle;\n setTerminalTitle(desiredTitle);\n }\n if (nextTitle === lastSessionTitle && nextActivity === lastActivity) return;\n lastSessionTitle = nextTitle;\n lastActivity = nextActivity;\n if (footerState.kind === 'idle') {\n recomputeFooter();\n redrawFooter();\n }\n } catch (e) {\n // readBoxStatus already swallows the common cases (paused/stopped/pre-feature);\n // anything reaching here is unexpected and worth a log line.\n logErr(`status poll failed: ${(e as Error).message}`);\n }\n };\n void pollStatus();\n const statusTimer = setInterval(() => {\n void pollStatus();\n }, STATUS_POLL_INTERVAL_MS);\n if (typeof statusTimer.unref === 'function') statusTimer.unref();\n\n // Restrict the outer terminal's scroll region to rows 1..innerRows so the\n // inner program's natural scrolling (bottom-line newline) doesn't push\n // content into our footer row. DECSTBM also resets the cursor to (1,1) on\n // some terminals, so we follow it with a cursor restore. Reverted in\n // teardown via `\\x1b[r` (clear scroll region -> full screen).\n process.stdout.write(`\\x1b[1;${String(innerRows)}r`);\n\n // Plain shell (`--no-tmux`): bash doesn't enter alt-screen, so without help\n // the user's pre-shell host-terminal content stays visible above bash's\n // freshly drawn prompt. Clear the visible screen + home the cursor before\n // the pty's first write. We don't touch scrollback (`\\x1b[3J`) — the user's\n // pre-shell context stays scroll-up-able. Claude and the tmux-backed shell\n // skip this: they enter their own alt-screen on init and would just\n // overpaint anyway (clearing first would only flicker).\n if (opts.mode === 'shell' && !detachable) {\n process.stdout.write('\\x1b[H\\x1b[2J');\n }\n\n // Initial paint so the idle footer appears immediately.\n redrawFooter();\n\n // Wait for the pty to exit, then tear down everything.\n const exitCode = await new Promise<number>((resolve) => {\n pty.onExit(({ exitCode }) => resolve(exitCode));\n });\n\n // Teardown order: stop reading stdin, restore cooked mode, drop SSE,\n // dispose the router (rejects any in-flight capture), clear the footer\n // row so the shell prompt below doesn't sit on top of our bar.\n process.stdin.off('data', onStdinData);\n process.stdout.off('resize', onResize);\n clearInterval(statusTimer);\n stopSpinner();\n if (flashTimer) clearTimeout(flashTimer);\n if (process.stdin.isTTY) process.stdin.setRawMode(false);\n process.stdin.pause();\n stream.close();\n router.dispose();\n const rsFinal = process.stdout.rows ?? rows;\n const csFinal = process.stdout.columns ?? cols;\n // Clear the scroll region first so the cursor moves below can reach row N\n // without the terminal trying to keep them inside the smaller region.\n // Then move to the footer row, erase it, return the cursor.\n process.stdout.write(\n '\\x1b[r' +\n cursorMoveTo(rsFinal, 1) +\n `\\x1b[2K` +\n cursorMoveTo(rsFinal, csFinal),\n );\n // Restore the host terminal/tab title we saved at attach time.\n popTerminalTitle();\n\n if (exitCode === 0 && opts.detachNotice) {\n // Match the cosmetic of the old attachClaudeSession: overwrite tmux's\n // own `[detached]` line if it's visible, then print the reattach hint.\n process.stdout.write('\\x1b[1A\\x1b[2K\\r' + opts.detachNotice + '\\n');\n }\n return exitCode;\n}\n\n/**\n * Fallback when node-pty is unavailable or stdio isn't a TTY. Identical to\n * today's call: blocking spawnSync with inherited stdio.\n */\nfunction runFallback(command: string, argv: string[], env?: Record<string, string>): number {\n const child = spawnSync(command, argv, {\n stdio: 'inherit',\n env: env ? { ...process.env, ...env } : process.env,\n });\n return child.status ?? 0;\n}\n","import type { Terminal as XtermTerminal } from '@xterm/headless';\n\n/**\n * The `@xterm/headless` `Terminal` class. Injected (not imported) because\n * @xterm/headless is CJS — a static ESM named import breaks Node's loader for\n * the whole CLI, so callers dynamic-import it and pass the ctor through.\n *\n * Used by the dashboard (for its xterm-headless screen-state mirror). The\n * wrapped-pty wrapper does not need it — the inner program writes raw bytes\n * directly to the user's terminal, no parsing required.\n */\nexport type TerminalCtor = new (opts: {\n cols: number;\n rows: number;\n allowProposedApi: boolean;\n scrollback: number;\n convertEol: boolean;\n}) => XtermTerminal;\n\n/**\n * Minimal shape of a node-pty IPty (avoids a hard type dep on the optional\n * module — node-pty is in optionalDependencies, may not be installed).\n */\nexport interface IPtyLike {\n onData(cb: (d: string) => void): void;\n onExit(cb: (e: { exitCode: number }) => void): void;\n write(d: string): void;\n resize(cols: number, rows: number): void;\n kill(): void;\n}\n\nexport type PtySpawn = (\n file: string,\n args: string[],\n opts: { name: string; cols: number; rows: number; env: NodeJS.ProcessEnv },\n) => IPtyLike;\n\nexport interface PtyBackend {\n ptySpawn: PtySpawn;\n /** Present for callers that also need the xterm headless ctor (dashboard). */\n termCtor: TerminalCtor;\n}\n\n/**\n * Dynamic-load the optional pty + xterm/headless backends. Returns null\n * when either prebuild is missing (we don't throw — callers decide how to\n * degrade). Centralized here so the dashboard and the wrapped-pty wrapper\n * use the same exact load dance.\n */\nexport async function loadPtyBackend(): Promise<PtyBackend | null> {\n try {\n const ptyMod = (await import('@homebridge/node-pty-prebuilt-multiarch')) as Record<\n string,\n unknown\n >;\n const xtermMod = (await import('@xterm/headless')) as Record<string, unknown>;\n const spawn =\n (ptyMod['spawn'] as unknown) ??\n (ptyMod['default'] as Record<string, unknown> | undefined)?.['spawn'];\n const Terminal =\n (xtermMod['Terminal'] as unknown) ??\n (xtermMod['default'] as Record<string, unknown> | undefined)?.['Terminal'];\n if (typeof spawn !== 'function' || typeof Terminal !== 'function') {\n return null;\n }\n return {\n ptySpawn: spawn as unknown as PtySpawn,\n termCtor: Terminal as unknown as TerminalCtor,\n };\n } catch {\n return null;\n }\n}\n","import { spawn } from 'node:child_process';\nimport type { AttachOpenIn } from '@agentbox/config';\n\nexport type HostTerminal = 'tmux' | 'iterm2' | 'unknown';\n\n/**\n * Identify the user's host terminal from env vars. tmux wins over iTerm2 even\n * when nested — when `TMUX` is set, the tmux CLI is the right primitive (it can\n * split the current pane / open a new window without going through AppleScript).\n *\n * macOS-only by design: the CLI itself is macOS-only (see CLAUDE.md), so we\n * don't try to recognize gnome-terminal / alacritty / Windows Terminal.\n */\nexport function detectHostTerminal(env: NodeJS.ProcessEnv = process.env): HostTerminal {\n const tmux = env['TMUX'];\n if (tmux && tmux.length > 0) return 'tmux';\n const termProgram = env['TERM_PROGRAM'];\n if (termProgram === 'iTerm.app') return 'iterm2';\n return 'unknown';\n}\n\n/** Single-quote a string so it survives a shell parse intact. */\nfunction shellQuote(s: string): string {\n if (s.length === 0) return \"''\";\n // Replace any internal `'` with the four-byte sequence `'\\''` (close, escaped\n // quote, reopen). Cheaper than picking double-quotes — no $/`/\\ to worry about.\n return \"'\" + s.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n\n/** Escape a string for embedding in a double-quoted AppleScript literal. */\nfunction appleScriptEscape(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/** Join an argv into a single shell-safe command line. */\nfunction shellJoin(argv: string[]): string {\n return argv.map(shellQuote).join(' ');\n}\n\nexport interface SpawnInNewTerminalArgs {\n host: Exclude<HostTerminal, 'unknown'>;\n /** Where to open the session in that host's terminology. `'same'` is rejected\n * by the caller — we never produce `same` here. */\n mode: Exclude<AttachOpenIn, 'same'>;\n /** Full argv to run in the new pane: `[program, ...args]`. The first element\n * is the binary; the rest are passed verbatim. */\n argv: string[];\n /** Working directory for the new pane. Passed to tmux via `-c` and prepended\n * to the iTerm2 command as `cd <cwd> && exec …`. */\n cwd: string;\n /** Short title for the new tmux window / iTerm2 tab when applicable. */\n title: string;\n}\n\nexport interface SpawnInNewTerminalResult {\n launched: boolean;\n /** One-line user-facing message printed to the host's stdout on success.\n * Empty string when `launched` is false. */\n note: string;\n /** stderr captured from the spawner, when `launched` is false. Used only for\n * the command log; not surfaced to the user. */\n error?: string;\n}\n\n/**\n * Open a fresh tmux pane / iTerm2 split-tab-window and run `<command> <argv...>`\n * there. Returns synchronously after the new pane is requested — the inner\n * command runs in its own terminal and is no longer this process's child.\n *\n * On failure (tmux/osascript exits non-zero, or wasn't found), the caller is\n * expected to fall back to inline attach.\n */\nexport async function spawnInNewTerminal(\n args: SpawnInNewTerminalArgs,\n): Promise<SpawnInNewTerminalResult> {\n if (args.host === 'tmux') return spawnInTmux(args);\n return spawnInITerm2(args);\n}\n\nasync function spawnInTmux(args: SpawnInNewTerminalArgs): Promise<SpawnInNewTerminalResult> {\n // `-c <cwd>` drops the new pane in the host pane's directory so the\n // recursive `agentbox` invocation can resolve project-scoped refs (and so\n // any commands the user runs after detaching start somewhere sensible).\n // The command is passed as a single shell-quoted positional after `--`;\n // tmux hands it to /bin/sh -c, which is why each argv element needs\n // single-quoting.\n const cmdStr = shellJoin(args.argv);\n let tmuxArgv: string[];\n let noteKind: string;\n if (args.mode === 'split') {\n tmuxArgv = ['split-window', '-h', '-c', args.cwd, '--', cmdStr];\n noteKind = 'tmux split';\n } else {\n // `window` and `tab` both map to tmux's only \"another full screen\" primitive.\n tmuxArgv = ['new-window', '-n', args.title, '-c', args.cwd, '--', cmdStr];\n noteKind = 'tmux window';\n }\n const r = await runQuiet('tmux', tmuxArgv);\n if (r.code !== 0) {\n return {\n launched: false,\n note: '',\n error: `tmux ${tmuxArgv.join(' ')} exited ${String(r.code)}: ${r.stderr.trim()}`,\n };\n }\n return {\n launched: true,\n note: `Attached in new ${noteKind} — Ctrl+a d to detach the box's tmux session.`,\n };\n}\n\nasync function spawnInITerm2(args: SpawnInNewTerminalArgs): Promise<SpawnInNewTerminalResult> {\n // iTerm2 launches `command` through a shell, but doesn't honor a starting\n // directory parameter on its AppleScript verbs. Prepend `cd <cwd> && exec`\n // so the new tab/window/split lands in the host pane's cwd and replaces\n // the launching shell with the agentbox process.\n const inner = shellJoin(args.argv);\n const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${inner}`;\n const cmdLit = `\"${appleScriptEscape(cmdLine)}\"`;\n\n // Always create the tab/window/split first, then `write text` into its\n // session. The `... with default profile command \"<cmd>\"` parameter form is\n // unreliable on iTerm 3.7 betas — it fails (returns `missing value`) and the\n // command bounces to Terminal.app instead of running in iTerm. The\n // create-then-write-text form is the supported path and works across\n // versions, so every mode uses it.\n let lines: string[];\n let noteKind: string;\n switch (args.mode) {\n case 'split':\n lines = [\n 'tell application \"iTerm\"',\n ' tell current session of current window to set _s to (split vertically with default profile)',\n ` tell _s to write text ${cmdLit}`,\n 'end tell',\n ];\n noteKind = 'iTerm2 split';\n break;\n case 'tab':\n lines = [\n 'tell application \"iTerm\"',\n ' tell current window to set _t to (create tab with default profile)',\n ` tell current session of _t to write text ${cmdLit}`,\n 'end tell',\n ];\n noteKind = 'iTerm2 tab';\n break;\n case 'window':\n lines = [\n 'tell application \"iTerm\"',\n ' set _w to (create window with default profile)',\n ` tell current session of _w to write text ${cmdLit}`,\n 'end tell',\n ];\n noteKind = 'iTerm2 window';\n break;\n }\n\n const r = await runQuiet('osascript', ['-e', lines.join('\\n')]);\n if (r.code !== 0) {\n return {\n launched: false,\n note: '',\n error: `osascript exited ${String(r.code)}: ${r.stderr.trim()}`,\n };\n }\n return {\n launched: true,\n note: `Attached in new ${noteKind} — Ctrl+a d to detach the box's tmux session.`,\n };\n}\n\ninterface QuietResult {\n code: number;\n stderr: string;\n}\n\n/** Spawn `cmd argv...`, capture stderr, ignore stdout. Resolves on exit. */\nfunction runQuiet(cmd: string, argv: string[]): Promise<QuietResult> {\n return new Promise((resolve) => {\n const child = spawn(cmd, argv, { stdio: ['ignore', 'ignore', 'pipe'] });\n let stderr = '';\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString('utf8');\n });\n child.on('error', (err) => {\n resolve({ code: 127, stderr: err.message });\n });\n child.on('exit', (code) => {\n resolve({ code: typeof code === 'number' ? code : 1, stderr });\n });\n });\n}\n","const ESC = '\\x1b';\nconst BEL = '\\x07';\n\n/** Replace control chars that would otherwise break the OSC string (the BEL\n * terminator, or any C0 byte) and trim — a stray newline in the agent's\n * session title must not corrupt the host terminal. */\nfunction sanitize(title: string): string {\n return title.replace(/[\\x00-\\x1f\\x7f]/g, ' ').trim();\n}\n\n/**\n * Set the host terminal's title via OSC 0 (`ESC ] 0 ; <title> BEL`), which sets\n * both the window and icon title — the same sequence Claude Code emits. Guarded\n * by `isTTY` so piped / redirected output stays clean.\n */\nexport function setTerminalTitle(\n title: string,\n stream: NodeJS.WriteStream = process.stdout,\n): void {\n if (!stream.isTTY) return;\n stream.write(`${ESC}]0;${sanitize(title)}${BEL}`);\n}\n\n/**\n * Push the terminal's current title onto its title stack (XTPUSHTITLE,\n * `CSI 22 ; 2 t`). Pair with {@link popTerminalTitle} on exit so the user's\n * original tab title is restored. Terminals without title-stack support ignore\n * the unknown CSI.\n */\nexport function pushTerminalTitle(stream: NodeJS.WriteStream = process.stdout): void {\n if (!stream.isTTY) return;\n stream.write(`${ESC}[22;2t`);\n}\n\n/** Pop the title saved by {@link pushTerminalTitle} (XTPOPTITLE, `CSI 23 ; 2 t`). */\nexport function popTerminalTitle(stream: NodeJS.WriteStream = process.stdout): void {\n if (!stream.isTTY) return;\n stream.write(`${ESC}[23;2t`);\n}\n","import type { PromptAnswerBody, PromptAskEvent } from '@agentbox/relay';\n\n/**\n * Steady-state input forwarder + active-prompt capture + Ctrl+a leader.\n *\n * In steady state every byte goes to the pty unmodified — *unless* a\n * `leaderChords` map is supplied, in which case `Ctrl+a` (0x01) opens the\n * actions menu (leader-only: a literal Ctrl+a needs a double-press).\n *\n * Only when `capture()` is awaiting does the router intercept the next\n * keystroke and resolve the prompt with a y/n/cancel answer. Anything else\n * the user types while a prompt is active is dropped (not forwarded) — the\n * inner program doesn't see partial keys.\n */\nexport interface InputRouter {\n /** True while a prompt is being captured. Used by the run loop to know\n * whether to redraw the footer eagerly. */\n readonly capturing: boolean;\n /** Feed raw bytes from process.stdin. Forwards or captures internally. */\n feed(buf: Buffer): void;\n /** Activate prompt capture. Resolves with the answer body. Subsequent\n * capture() calls before resolution overwrite the previous prompt (the\n * newer one wins — relay broadcast order is canonical). */\n capture(p: PromptAskEvent): Promise<PromptAnswerBody>;\n /** Reject the in-flight capture (pty exit, sibling-wrapper answered). */\n abort(reason: 'pty-exit' | 'resolved-elsewhere'): void;\n dispose(): void;\n}\n\ninterface ActivePrompt {\n ev: PromptAskEvent;\n resolve: (b: PromptAnswerBody) => void;\n reject: (e: Error) => void;\n}\n\n/** Actions reachable from the Ctrl+a leader menu. */\nexport type LeaderAction = 'screen' | 'code' | 'url' | 'detach';\n\nconst KEY_ENTER = 0x0d;\nconst KEY_LF = 0x0a;\nconst KEY_ESC = 0x1b;\nconst KEY_CTRL_C = 0x03;\nconst KEY_Y_LOW = 0x79;\nconst KEY_Y_UP = 0x59;\nconst KEY_N_LOW = 0x6e;\nconst KEY_N_UP = 0x4e;\nconst KEY_LEADER = 0x01; // Ctrl-a\nconst KEY_CTRL_V = 0x16; // Ctrl-v — Claude Code's \"paste image from clipboard\"\nconst KEY_A_LOW = 0x61; // 'a'\n\n/**\n * A key decoded from an enhanced-keyboard escape sequence. TUIs like Claude Code\n * can switch the terminal into the kitty keyboard protocol or xterm\n * modifyOtherKeys, in which `Ctrl+a` and even plain letters arrive not as raw\n * bytes but as escape sequences. The leader must decode those so the Actions\n * footer keeps working under those modes.\n */\ninterface CsiKey {\n /** Total byte length of the sequence (so the caller can advance past it). */\n len: number;\n /** Base unicode codepoint of the key (e.g. 97 for 'a'). */\n code: number;\n /** Whether Ctrl was held. */\n ctrl: boolean;\n}\n\n/**\n * Parse a kitty keyboard (`ESC [ <code> ; <mods> u`) or xterm modifyOtherKeys\n * (`ESC [ 27 ; <mods> ; <code> ~`) key sequence at `buf[i]`. Returns the base\n * keycode + Ctrl flag, or null when `buf[i]` isn't one of those (a plain byte,\n * an arrow/function/mouse sequence, or an incomplete sequence split across\n * reads — all of which just forward unchanged). Precise on purpose: only the\n * exact `…u` / `CSI 27 … ~` key shapes match, so cursor/mouse CSI sequences\n * (`ESC [ A`, `ESC [ < … M`) fall through.\n */\nfunction parseCsiKey(buf: Buffer, i: number): CsiKey | null {\n if (buf[i] !== KEY_ESC || buf[i + 1] !== 0x5b /* [ */) return null;\n const params: number[] = [];\n let val = -1;\n for (let j = i + 2; j < buf.length; j++) {\n const b = buf[j];\n if (b !== undefined && b >= 0x30 && b <= 0x39) {\n val = (val < 0 ? 0 : val) * 10 + (b - 0x30);\n continue;\n }\n if (b === 0x3b /* ; */) {\n params.push(val);\n val = -1;\n continue;\n }\n if (b === 0x3a /* : */) {\n // Sub-parameters (kitty event types / alternate keys): record the param,\n // then skip to the next ';' or final byte.\n params.push(val);\n val = -1;\n while (\n j + 1 < buf.length &&\n buf[j + 1] !== 0x3b &&\n buf[j + 1] !== 0x75 &&\n buf[j + 1] !== 0x7e\n ) {\n j++;\n }\n continue;\n }\n if (b === 0x75 /* u */ || b === 0x7e /* ~ */) {\n if (val >= 0) params.push(val);\n const len = j - i + 1;\n const modsToCtrl = (m: number): boolean => ((m - 1) & 4) !== 0;\n if (b === 0x75) {\n // kitty: CSI <code> ; <mods> u\n const code = params[0];\n if (code === undefined || code < 0) return null;\n return { len, code, ctrl: modsToCtrl(params[1] ?? 1) };\n }\n // modifyOtherKeys: CSI 27 ; <mods> ; <code> ~ (other '~' seqs aren't keys)\n if (params[0] !== 27) return null;\n const code = params[2];\n if (code === undefined || code < 0) return null;\n return { len, code, ctrl: modsToCtrl(params[1] ?? 1) };\n }\n return null; // any other byte → not a CSI-u / modifyOtherKeys key\n }\n return null; // incomplete (split across reads) — forward as-is\n}\n\nconst DEFAULT_LEADER_TIMEOUT_MS = 2000;\n\nexport interface InputRouterOptions {\n onForward: (b: Buffer) => void;\n /** Called when a prompt's capture is resolved — the run loop POSTs the answer. */\n onAnswer: (body: PromptAnswerBody) => void;\n /** Ctrl+a leader chord map: a single lowercase character → action. When\n * omitted or empty the leader is disabled and `Ctrl+a` forwards verbatim. */\n leaderChords?: Readonly<Record<string, LeaderAction>>;\n /** Fired when the leader menu opens (true) / closes (false). */\n onLeaderChange?: (active: boolean) => void;\n /** Fired when a recognized chord key resolves the leader. */\n onAction?: (name: LeaderAction) => void;\n /**\n * When set, a lone `Ctrl+V` (0x16) in steady state is intercepted instead of\n * forwarded: the router awaits this hook (which loads the host clipboard\n * image into the box), then re-emits the `Ctrl+V` so the inner program's own\n * paste handler reads the now-populated box clipboard. Presses while one is\n * in flight are dropped (debounced). When omitted, `Ctrl+V` forwards\n * verbatim. Used for claude image paste; other modes don't pass it. */\n onPasteImage?: () => Promise<unknown>;\n /** ms the leader menu stays open with no key before auto-closing (default 2000). */\n leaderTimeoutMs?: number;\n /** Injected for unit tests; defaults to global timers. */\n setTimer?: (ms: number, fn: () => void) => unknown;\n clearTimer?: (h: unknown) => void;\n}\n\nexport function createInputRouter(opts: InputRouterOptions): InputRouter {\n let active: ActivePrompt | null = null;\n let disposed = false;\n\n const leaderChords = opts.leaderChords ?? {};\n const leaderEnabled = Object.keys(leaderChords).length > 0;\n const onPasteImage = opts.onPasteImage;\n const pasteEnabled = typeof onPasteImage === 'function';\n let pasteInFlight = false;\n const leaderTimeoutMs = opts.leaderTimeoutMs ?? DEFAULT_LEADER_TIMEOUT_MS;\n const setTimer = opts.setTimer ?? ((ms, fn) => setTimeout(fn, ms) as unknown);\n const clearTimer =\n opts.clearTimer ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>));\n let leader = false;\n let leaderTimer: unknown = null;\n\n const disarmLeader = (): void => {\n if (leaderTimer != null) {\n clearTimer(leaderTimer);\n leaderTimer = null;\n }\n };\n\n const exitLeader = (): void => {\n if (!leader) return;\n leader = false;\n disarmLeader();\n opts.onLeaderChange?.(false);\n };\n\n const enterLeader = (): void => {\n leader = true;\n disarmLeader();\n // Leader-only: a lone Ctrl+a just times the menu out — it is never\n // auto-forwarded. A literal Ctrl+a is sent via a double-press.\n leaderTimer = setTimer(leaderTimeoutMs, () => {\n leaderTimer = null;\n exitLeader();\n });\n opts.onLeaderChange?.(true);\n };\n\n // The leader is open and `b` is the chord byte that resolves it.\n const resolveLeaderByte = (b: number): void => {\n if (b === KEY_LEADER) {\n // Double Ctrl+a → one literal Ctrl+a to the inner program.\n exitLeader();\n opts.onForward(Buffer.from([KEY_LEADER]));\n return;\n }\n if (b === KEY_ESC) {\n // Esc dismisses the menu; nothing forwarded.\n exitLeader();\n return;\n }\n const action = leaderChords[String.fromCharCode(b).toLowerCase()];\n if (action) {\n exitLeader();\n opts.onAction?.(action);\n return;\n }\n // Unrecognized chord: close the menu, forward the key so typing isn't lost.\n exitLeader();\n opts.onForward(Buffer.from([b]));\n };\n\n const settle = (\n answer: PromptAnswerBody['answer'],\n cancelled?: boolean,\n ): void => {\n if (!active) return;\n const body: PromptAnswerBody = {\n id: active.ev.id,\n answer,\n ...(cancelled ? { cancelled: true } : {}),\n };\n const p = active;\n active = null;\n p.resolve(body);\n opts.onAnswer(body);\n };\n\n const handleCapturedByte = (b: number): void => {\n if (!active) return;\n if (b === KEY_Y_LOW || b === KEY_Y_UP) {\n settle('y');\n return;\n }\n if (b === KEY_N_LOW || b === KEY_N_UP) {\n settle('n');\n return;\n }\n if (b === KEY_ESC || b === KEY_CTRL_C) {\n settle('n', true);\n return;\n }\n if (b === KEY_ENTER || b === KEY_LF) {\n // Enter accepts the default answer.\n const def = active.ev.defaultAnswer ?? 'n';\n settle(def);\n return;\n }\n // Anything else: ignored (not forwarded, not consumed).\n };\n\n // Intercepted Ctrl+V: run the host→box image-paste hook, then re-emit the\n // Ctrl+V so the inner program reads the (now-loaded) box clipboard. A press\n // while one is in flight is dropped — the Ctrl+V was already swallowed by the\n // caller, so there's nothing to forward.\n const triggerPaste = (): void => {\n if (pasteInFlight) return;\n pasteInFlight = true;\n const done = (): void => {\n pasteInFlight = false;\n if (!disposed) opts.onForward(Buffer.from([KEY_CTRL_V]));\n };\n void Promise.resolve()\n .then(() => onPasteImage?.())\n .then(done, done);\n };\n\n // Leader-aware steady-state forwarding: scan bytes, batching plain runs into\n // a single onForward call, and intercept `Ctrl+a` chords + `Ctrl+V` paste.\n const feedSteady = (buf: Buffer): void => {\n let chunkStart = 0;\n const flushChunk = (end: number): void => {\n if (end > chunkStart) opts.onForward(buf.subarray(chunkStart, end));\n chunkStart = end;\n };\n let i = 0;\n while (i < buf.length) {\n const byte = buf[i];\n if (byte === undefined) {\n i++;\n continue;\n }\n\n if (leader) {\n // Resolve the chord. The key may be a raw byte, or — when the inner app\n // enabled an enhanced keyboard protocol — a CSI-u / modifyOtherKeys\n // sequence (e.g. 'c' as `ESC [ 99 u`).\n const k = parseCsiKey(buf, i);\n if (k) {\n if (k.ctrl && k.code === KEY_A_LOW) {\n // Double Ctrl+a → one literal Ctrl+a to the inner program.\n exitLeader();\n opts.onForward(Buffer.from([KEY_LEADER]));\n } else {\n const action = leaderChords[String.fromCharCode(k.code).toLowerCase()];\n exitLeader();\n if (action) opts.onAction?.(action);\n else opts.onForward(buf.subarray(i, i + k.len)); // unknown: don't lose it\n }\n i += k.len;\n chunkStart = i;\n continue;\n }\n resolveLeaderByte(byte);\n i += 1;\n chunkStart = i;\n continue;\n }\n\n if (leaderEnabled && byte === KEY_LEADER) {\n flushChunk(i); // forward everything typed before the Ctrl+a\n enterLeader();\n i += 1;\n chunkStart = i;\n continue;\n }\n // Ctrl+a re-encoded by an enhanced keyboard protocol (kitty / modifyOtherKeys).\n if (leaderEnabled && byte === KEY_ESC) {\n const k = parseCsiKey(buf, i);\n if (k && k.ctrl && k.code === KEY_A_LOW) {\n flushChunk(i);\n enterLeader();\n i += k.len;\n chunkStart = i;\n continue;\n }\n }\n if (pasteEnabled && byte === KEY_CTRL_V) {\n flushChunk(i); // forward everything typed before the Ctrl+V\n i += 1;\n chunkStart = i; // swallow it; triggerPaste re-emits after the load\n triggerPaste();\n continue;\n }\n i += 1;\n }\n flushChunk(buf.length);\n };\n\n return {\n get capturing(): boolean {\n return active !== null;\n },\n feed(buf: Buffer): void {\n if (disposed) return;\n if (active) {\n // A multi-byte read starting with ESC is a CSI/SS3/OSC escape\n // sequence — mouse click (`\\x1b[<…M/m`), arrow / function key,\n // window-focus event, bracketed-paste markers, etc. Drop the\n // whole chunk: the user pressed something we don't model as a\n // confirmation key, and they'd be (correctly) surprised if a stray\n // mouse click registered as \"deny\". A *real* Esc keypress arrives\n // as a single byte in its own read, which still cancels below.\n if (buf.length > 1 && buf[0] === KEY_ESC) return;\n // Process bytes one at a time so a paste of \"yes\\n\" is handled\n // sanely: the 'y' settles, the rest is dropped — we don't want\n // stray bytes leaking to the pty after the prompt closed mid-buf.\n // (After settle, `active` is null; remaining bytes fall through to\n // forward path below.)\n for (let i = 0; i < buf.length; i++) {\n const byte = buf[i];\n if (byte === undefined) continue;\n if (active) {\n handleCapturedByte(byte);\n } else {\n // Active became null mid-buffer (settled). Forward the rest as\n // a normal keystroke chunk.\n opts.onForward(buf.subarray(i));\n return;\n }\n }\n return;\n }\n if (!leaderEnabled && !pasteEnabled) {\n opts.onForward(buf);\n return;\n }\n feedSteady(buf);\n },\n capture(ev: PromptAskEvent): Promise<PromptAnswerBody> {\n return new Promise<PromptAnswerBody>((resolve, reject) => {\n // A relay prompt outranks the actions menu — close the leader first.\n if (leader) exitLeader();\n if (active) {\n // A new prompt arrived before the old one was answered — abort\n // the old one (treated as cancelled) and switch to the new one.\n // The relay already broadcast `prompt-ask` for both; we owe the\n // first an answer or it'll stay pending forever.\n settle('n', true);\n }\n active = { ev, resolve, reject };\n });\n },\n abort(reason): void {\n if (!active) return;\n const p = active;\n active = null;\n const msg = reason === 'pty-exit' ? 'pty exited' : 'resolved by sibling wrapper';\n p.reject(new Error(msg));\n },\n dispose(): void {\n if (disposed) return;\n disposed = true;\n disarmLeader();\n if (active) {\n const p = active;\n active = null;\n p.reject(new Error('input router disposed'));\n }\n },\n };\n}\n","export interface SidebarBox {\n id: string;\n name: string;\n /** Container state: 'running' | 'paused' | 'stopped' | 'missing' | … */\n state: string;\n /** Activity of the agent this box runs (claude / codex) — 'working' | 'idle'\n * | 'waiting' | 'unknown' | undefined. Resolved from whichever agent is\n * active (see `resolveAgent` in commands/dashboard.ts). */\n activity?: string;\n /** The in-box terminal/session title the active agent set, or undefined. */\n sessionTitle?: string;\n /** 1-based per-project box number, shown as `[N]`; undefined for\n * pre-feature boxes and the synthetic \"+ New box\" entry. */\n index?: number;\n /** Absolute project root; used to group boxes under a project header.\n * Undefined for pre-feature boxes and the synthetic \"+ New box\" entry. */\n project?: string;\n /** This box has an unanswered relay `prompt-ask` event (e.g. agentbox-ctl\n * git push / cp / download waiting for user confirmation). The compositor\n * injects this flag from its in-memory map of active prompts. Overrides\n * the activity cell — `▲ prompt` reads more urgent than `● working`. */\n pendingPrompt?: boolean;\n /** This box has an active relay notice (currently: a checkpoint is being\n * captured, freezing the box). Injected by the compositor; shown as\n * `◆ checkpoint` in the activity cell. Outranked by `pendingPrompt`. */\n checkpointing?: boolean;\n}\n\n/** Per-row ownership + styling map returned alongside the rendered lines so\n * the compositor can highlight the selected box and style headers without\n * re-deriving the (now non-uniform) layout. */\nexport interface SidebarRender {\n lines: string[];\n /** boxId rendered on row `i`, else null (banner / group header / blank). */\n rowOwner: (string | null)[];\n /** true for the banner and project-header rows (styled like the banner). */\n headerRows: boolean[];\n}\n\n/** Truncate to `max` printable chars, appending `…` when it had to cut\n * (keeps the head). */\nfunction ellipsize(s: string, max: number): string {\n if (max <= 0) return '';\n if (s.length <= max) return s;\n if (max === 1) return '…';\n return s.slice(0, max - 1) + '…';\n}\n\n/** Truncate keeping the *tail* (the distinguishing part of a box name like\n * `…-78b94c78`), prepending `…` when it had to cut. */\nfunction ellipsizeHead(s: string, max: number): string {\n if (max <= 0) return '';\n if (s.length <= max) return s;\n if (max === 1) return '…';\n return '…' + s.slice(s.length - (max - 1));\n}\n\nexport function activityCell(b: SidebarBox): string {\n // Pending relay prompt outranks every other state — the user needs to\n // act before whatever the box is doing can continue.\n if (b.pendingPrompt) return '▲ prompt';\n // A checkpoint freezes the box; surface it over the activity state.\n if (b.checkpointing) return '◆ checkpoint';\n if (b.state !== 'running') return `[${b.state}]`;\n switch (b.activity) {\n case 'working':\n return '● working';\n case 'idle':\n return '○ idle';\n case 'waiting':\n return '◐ waiting';\n default:\n return '? unknown';\n }\n}\n\n/** Synthetic sidebar entry pinned at the top: selecting it opens the create\n * menu. Carried in the compositor's box list like a real box (sentinel id),\n * so selection/switch/highlight need no special-casing. */\nexport const NEW_BOX_ID = '__agentbox_new__';\nexport const NEW_BOX_LABEL = '+ New box';\n\n/** Sidebar banner label (rendered into the top border). */\nexport const SIDEBAR_HEADER = 'AgentBox';\n\n/** Top border: a flat line on the top + a rounded corner into the right edge\n * only (no left/bottom, to save space): `──── AgentBox ─────…` filling\n * exactly `w`. The left end is a straight line; the matching rounded\n * top-right corner (`╮`) is drawn by the compositor at the sidebar separator\n * column. */\nfunction topBorder(label: string, w: number): string {\n const lead = `──── ${label} `;\n if (lead.length >= w) return lead.slice(0, w);\n return lead + '─'.repeat(w - lead.length);\n}\n/** Lines `sidebarLines` reserves before the box rows (banner + blank). The\n * compositor uses this to locate the selected box row for highlighting. */\nexport const SIDEBAR_HEADER_LINES = 2;\n\nfunction fit(s: string, w: number): string {\n if (s.length === w) return s;\n if (s.length > w) return s.slice(0, w);\n return s + ' '.repeat(w - s.length);\n}\n\n/** `s` centered in a field of `w` columns (truncated if it doesn't fit). */\nfunction center(s: string, w: number): string {\n if (s.length >= w) return s.slice(0, w);\n const pad = w - s.length;\n const leftPad = Math.floor(pad / 2);\n return ' '.repeat(leftPad) + s + ' '.repeat(pad - leftPad);\n}\n\n/** `basename` of an absolute project root, for the group header label. */\nfunction projectLabel(project: string | undefined): string {\n if (!project) return '(no project)';\n const parts = project.split('/').filter(Boolean);\n return parts[parts.length - 1] ?? project;\n}\n\n/** Strip the leading decoration Claude prepends to its terminal title (the\n * spinner glyph, e.g. `✳ `) plus any leading symbols/asterisks/space, so the\n * sidebar shows just the words. Falls back to the trimmed original if the\n * title is all decoration. */\nexport function stripTitleGlyph(s: string): string {\n const t = s.replace(/^[\\s\\p{S}*·]+/u, '');\n return t.length > 0 ? t : s.trim();\n}\n\n/**\n * Render one box row: `marker<num> <title|name> <status>`. The number and\n * the status are width-protected; the middle (title, else the box name with\n * its meaningful tail kept) flexes and ellipsizes so the status is never\n * eaten. Compact: no brackets, no glyph, single-char marker.\n */\nfunction boxRow(b: SidebarBox, marker: string, w: number): string {\n const numStr = b.index != null ? `${b.index} ` : '';\n const status = activityCell(b);\n const left = `${marker}${numStr}`;\n // -2: 1 gap before status, 1 margin after so the label doesn't touch the\n // sidebar's right border.\n const room = w - left.length - status.length - 2;\n if (room <= 0) return fit(`${left}${status}`, w);\n const middle =\n b.state === 'running' && b.sessionTitle\n ? ellipsize(stripTitleGlyph(b.sessionTitle), room)\n : ellipsizeHead(b.name, room);\n // Left segment padded so the status sits one column in from the right edge,\n // with a trailing space as the right margin.\n return fit(`${left}${middle}`, w - status.length - 1) + status + ' ';\n}\n\n/**\n * The sidebar region as exactly `h` lines, each exactly `w` columns, plus a\n * per-row ownership/style map. Pure — no ANSI positioning (the compositor\n * places it). Boxes are grouped under a ` ── <project> ── ` header (callers\n * pass them pre-sorted by project).\n */\nexport function sidebarLines(\n boxes: SidebarBox[],\n selectedId: string,\n w: number,\n h: number,\n): SidebarRender {\n const lines: string[] = [topBorder(SIDEBAR_HEADER, w), fit('', w)];\n const rowOwner: (string | null)[] = [null, null];\n const headerRows: boolean[] = [true, false];\n const push = (line: string, owner: string | null, header: boolean): void => {\n lines.push(fit(line, w));\n rowOwner.push(owner);\n headerRows.push(header);\n };\n\n let prevProject: string | undefined;\n let seenGroup = false;\n for (const b of boxes) {\n const marker = b.id === selectedId ? '▸' : ' ';\n if (b.id === NEW_BOX_ID) {\n push(`${marker}${NEW_BOX_LABEL}`, b.id, false);\n continue;\n }\n if (!seenGroup || b.project !== prevProject) {\n push(center(` ── ${projectLabel(b.project)} ── `, w), null, true);\n prevProject = b.project;\n seenGroup = true;\n }\n push(boxRow(b, marker, w), b.id, false);\n }\n if (boxes.length === 0) push(' (no boxes)', null, false);\n while (lines.length < h) push('', null, false);\n return {\n lines: lines.slice(0, h),\n rowOwner: rowOwner.slice(0, h),\n headerRows: headerRows.slice(0, h),\n };\n}\n\n/**\n * Centered action menu for a running box with no Claude session.\n * Exactly `h` lines, each exactly `w` columns. Pure.\n */\nexport function menuLines(boxName: string, w: number, h: number): string[] {\n const body = [\n '',\n ` No agent session in ${boxName}.`,\n '',\n ' [c] Start Claude',\n ' [x] Start Codex',\n ' [o] Start OpenCode',\n ' [s] Open a shell',\n '',\n ' Ctrl+Option+↑/↓ switch · Ctrl-a then c/s/u/q (code/screen/url/quit)',\n ];\n const top = Math.max(0, Math.floor((h - body.length) / 2));\n const out: string[] = [];\n for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? '', w));\n return out;\n}\n\n/**\n * Centered action menu for a non-running box (paused/stopped): resume +\n * destroy, with a two-step destroy confirm (the TUI can't show a prompt).\n * Exactly `h` lines, each exactly `w` columns. Pure.\n */\nexport function lifecycleMenuLines(\n boxName: string,\n state: 'paused' | 'stopped',\n confirmDestroy: boolean,\n w: number,\n h: number,\n): string[] {\n const body = confirmDestroy\n ? [\n '',\n ` Destroy ${boxName}?`,\n ' This removes the container and its volumes.',\n '',\n ' [y] Yes, destroy',\n ' [any other key] Cancel',\n ]\n : [\n '',\n ` Box ${boxName} is ${state}.`,\n '',\n state === 'paused' ? ' [u] Unpause' : ' [s] Start',\n ' [d] Destroy',\n '',\n ' Ctrl+Option+↑/↓ switch · Ctrl-a then q quit',\n ];\n const top = Math.max(0, Math.floor((h - body.length) / 2));\n const out: string[] = [];\n for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? '', w));\n return out;\n}\n\n/**\n * Centered menu for the synthetic \"+ New box\" entry. Exactly `h` lines, each\n * exactly `w` columns. Pure.\n */\nexport function createMenuLines(where: string, w: number, h: number): string[] {\n const body = [\n '',\n ' Create a new box',\n '',\n ' [c] Create + launch Claude',\n ' [x] Create + launch Codex',\n ' [o] Create + launch OpenCode',\n ' [n] Create only',\n '',\n ` in ${where}`,\n '',\n ' Ctrl+Option+↑/↓ switch · Ctrl-a then q quit',\n ];\n const top = Math.max(0, Math.floor((h - body.length) / 2));\n const out: string[] = [];\n for (let i = 0; i < h; i++) out.push(fit(body[i - top] ?? '', w));\n return out;\n}\n\n// Status-bar palette — matches the in-box tmux footer\n// (`buildTmuxSessionArgs`): dark bar, blue brand block, dim-grey hints\n// with white key chords.\n/** The footer/sidebar background gray. Truecolor (not palette index 236) so\n * it pins an exact RGB — terminals can remap/shade indexed colors per\n * context, which made the sidebar and status bar look like different grays.\n * Single source so the two regions can't drift. */\nexport const BAR_BG = '\\x1b[48;2;48;48;48m';\nconst BAR_BASE = BAR_BG + '\\x1b[38;5;250m';\nconst BAR_BRAND = '\\x1b[48;5;39m\\x1b[38;5;16m'; // blue block (not bold)\nconst BRAND_BOLD = '\\x1b[1m'; // box name only\nconst BRAND_NOBOLD = '\\x1b[22m';\nconst HINT_KEY = '\\x1b[38;5;255m'; // white: the key chord\nconst HINT_TXT = '\\x1b[38;5;245m'; // gray: labels + separators\nconst BAR_RESET = '\\x1b[0m';\n\n// [key chord, label]. Modifiers spelled out (no ⌥/^ glyphs); arrows use the\n// ↑/↓ glyphs. Rendered as `KEYS: label` with the chord white, label gray.\nconst SWITCH_HINT: readonly [string, string] = ['Control+Option+↑/↓', 'switch'];\nconst HINT_GROUPS: ReadonlyArray<readonly [string, string]> = [\n SWITCH_HINT,\n ['Control+a c', 'code'],\n ['Control+a s', 'screen'],\n ['Control+a u', 'url'],\n ['Control+a q', 'quit'],\n];\n\n/** Minimal hint tier when the bar is too narrow for the full `HINT_GROUPS`:\n * box switching (always important) + the leader. Pressing `Ctrl-a` then\n * expands to `ADVANCED_HINT_GROUPS` (the compositor swaps while the leader is\n * active). */\nexport const COLLAPSED_HINT_GROUPS: ReadonlyArray<readonly [string, string]> = [\n SWITCH_HINT,\n ['Control+a', 'more'],\n];\n\n/** The expanded \"which-key\" chord menu shown while the Ctrl-a leader is\n * pending — every chord, compact (`KEY: label`), reverts on the next key. */\nexport const ADVANCED_HINT_GROUPS: ReadonlyArray<readonly [string, string]> = [\n ['c', 'code'],\n ['s', 'screen'],\n ['u', 'url'],\n ['t', 'stop'],\n ['p', 'pause'],\n ['d', 'destroy'],\n ['q', 'quit'],\n];\n\n/**\n * Status line, exactly `w` printable columns, colored to match the in-box tmux\n * footer (dark bar, blue ` agentbox ▸ … ` brand block on the left, dim-grey\n * shortcut hints on the right). `stateLabel` overrides the box's activity text\n * (used for `shell` / `menu` panes where the box `activity` would otherwise\n * show a misleading `unknown`). `fallbackGroups`, when given, is the narrow-bar tier\n * tried before brand-core-only — used to keep one essential chord pinned\n * (instead of the default dashboard `COLLAPSED_HINT_GROUPS`).\n */\nexport function statusLine(\n box: SidebarBox | undefined,\n w: number,\n stateLabel?: string,\n groups: ReadonlyArray<readonly [string, string]> = HINT_GROUPS,\n fallbackGroups?: ReadonlyArray<readonly [string, string]>,\n): string {\n const state =\n stateLabel ?? (box ? (box.state === 'running' ? (box.activity ?? 'unknown') : box.state) : '');\n // \"agentbox ▸ \" stays normal weight; only the box name + state are bold.\n const brandPrefix = box ? ' agentbox ▸ ' : ' agentbox ';\n // Brand *core* (no title) — the width-protected segment. The title is the\n // lowest-priority segment: it only fills space left after brand + hints.\n const base = box ? `${box.name} (${state})` : '';\n const coreMain = box ? `${base} ` : '';\n const corePlain = brandPrefix + coreMain;\n\n const SEP = ' │ ';\n const renderHints = (\n g: ReadonlyArray<readonly [string, string]>,\n ): { plain: string; styled: string } => ({\n plain: g.map(([k, l]) => `${k}: ${l}`).join(SEP) + ' ',\n styled:\n g.map(([k, l]) => `${HINT_KEY}${k}${HINT_TXT}: ${l}`).join(`${HINT_TXT}${SEP}`) + ' ',\n });\n\n // Hint tier: shortcuts beat the title. Try the requested groups; if the\n // brand core + those hints overflow, fall back to the narrow-bar tier\n // (`fallbackGroups` if supplied, else the dashboard's minimal leader hint);\n // if even that overflows, render brand-core-only (title can never push the\n // box name off-screen).\n let hints: { plain: string; styled: string } | null = null;\n for (const g of [groups, fallbackGroups ?? COLLAPSED_HINT_GROUPS]) {\n const h = renderHints(g);\n if (corePlain.length + h.plain.length + 1 <= w) {\n hints = h;\n break;\n }\n }\n if (!hints) {\n return BAR_BASE + BAR_BRAND + fit(corePlain, w) + BAR_RESET;\n }\n\n // Title fills only the leftover, ellipsized; dropped entirely when there's\n // no meaningful room (≈ ` — ` + a few chars). Capped at 40 cols as before.\n const room = w - corePlain.length - hints.plain.length - 1;\n let titleSeg = '';\n if (box?.sessionTitle && room >= 7) {\n titleSeg = ` — ${ellipsize(box.sessionTitle, Math.min(40, room - 3))}`;\n }\n\n const leftPlain = brandPrefix + base + titleSeg + (box ? ' ' : '');\n const leftStyled =\n BAR_BRAND + brandPrefix + BRAND_BOLD + base + titleSeg + (box ? ' ' : '') + BRAND_NOBOLD;\n const gap = w - leftPlain.length - hints.plain.length;\n // brand block (name + title bold) → base bar → gap → white/gray hints.\n return (\n BAR_BASE +\n leftStyled +\n BAR_BASE +\n ' '.repeat(gap) +\n hints.styled +\n BAR_RESET\n );\n}\n","import { BAR_BG, statusLine, type SidebarBox } from '../dashboard/sidebar.js';\nimport type { PromptAskEvent } from '@agentbox/relay';\n\n/**\n * Footer rendering state. `idle` reuses the dashboard's `statusLine` shape\n * (brand chip + box name + optional session title + right-aligned hint);\n * `prompt` is shown while a `prompt-ask` event is being captured; `notice`\n * is an animated informational warning (e.g. checkpoint in progress);\n * `flash` is a transient confirmation after a Ctrl+a action fires.\n */\nexport type FooterState =\n | {\n kind: 'idle';\n boxName: string;\n /** Claude's tmux pane title (from BoxStatus.claude.sessionTitle).\n * Undefined until the first status poll completes (or in shell mode). */\n sessionTitle?: string;\n /** Claude activity hint shown in `(<state>)` after the name. Same field\n * the dashboard sidebar uses (`working` / `idle` / `waiting` / etc.). */\n claudeActivity?: string;\n /** Mode drives the state label: claude shows claude activity, the\n * others show `(shell)` / `(codex)` / `(opencode)`. */\n mode: 'claude' | 'shell' | 'codex' | 'opencode';\n /** Whether the session can be detached (tmux-backed). Drives the\n * expanded leader menu + the pinned `Control+a d: detach` hint. */\n detachable?: boolean;\n /** True while the Ctrl+a leader menu is open — swaps the collapsed\n * `Control+a: Actions` hint for the expanded chord list. */\n leaderActive?: boolean;\n }\n | { kind: 'prompt'; prompt: PromptAskEvent }\n | {\n kind: 'notice';\n /** Warning text, e.g. \"Checkpoint in progress — …\". */\n message: string;\n /** Monotonic counter; the spinner glyph is `SPINNER_FRAMES[frame % len]`. */\n frame: number;\n }\n | {\n kind: 'flash';\n /** Transient confirmation text, e.g. \"Opening noVNC viewer…\". */\n message: string;\n };\n\n/**\n * Spinner cycle for the `notice` footer. Solid half-filled circles, not\n * braille: braille glyphs read as a faint dot cluster on the yellow banner\n * (set vs unset dots are hard to tell apart), so the motion gets lost. The\n * rotating black half of these is unambiguous.\n */\nexport const SPINNER_FRAMES = ['◐', '◓', '◑', '◒'] as const;\n\nconst URGENT = '\\x1b[38;5;220m\\x1b[1m'; // bright yellow + bold (active prompt)\nconst TXT = '\\x1b[38;5;250m'; // dim gray body text\nconst SUBTLE = '\\x1b[38;5;245m'; // very dim (Y/N hint)\nconst RESET = '\\x1b[0m';\n// Notice footer = a full-width warning banner: bright yellow background with\n// near-black bold text. High contrast so the \"box is frozen\" state is\n// unmissable — deliberately louder than the dim-on-dark idle/prompt bars.\nconst NOTICE_BG = '\\x1b[48;5;220m'; // bright yellow background\nconst NOTICE_FG = '\\x1b[38;5;16m\\x1b[1m'; // near-black + bold text\n// Flash footer = a calm one-line confirmation on the normal dark bar.\nconst FLASH_FG = '\\x1b[38;5;150m\\x1b[1m'; // soft green + bold\n\n/** Collapsed idle hint (plain `--no-tmux` shell) — the leader is hidden\n * behind one chord. */\nconst COLLAPSED_HINTS_PLAIN: ReadonlyArray<readonly [string, string]> = [\n ['Control+a', 'Actions'],\n];\n/** Collapsed idle hint (detachable session) — the detach chord stays pinned\n * on the right even while the actions menu is closed. */\nconst COLLAPSED_HINTS_DETACHABLE: ReadonlyArray<readonly [string, string]> = [\n ['Control+a', 'Actions'],\n ['Control+a d', 'detach'],\n];\n/** Narrow-bar fallback for a detachable session: drop the `Actions` hint\n * first, but never the detach chord. */\nconst DETACH_PIN_HINTS: ReadonlyArray<readonly [string, string]> = [\n ['Control+a d', 'detach'],\n];\n/** Expanded which-key menu shown while the Ctrl+a leader is open. A\n * detachable (tmux-backed) session also gets `d: detach`; a plain shell\n * has nothing to detach from. */\nconst DETACHABLE_LEADER_HINTS: ReadonlyArray<readonly [string, string]> = [\n ['c', 'code'],\n ['s', 'screen'],\n ['u', 'url'],\n ['d', 'detach'],\n];\nconst PLAIN_LEADER_HINTS: ReadonlyArray<readonly [string, string]> = [\n ['c', 'code'],\n ['s', 'screen'],\n ['u', 'url'],\n];\n\n/**\n * Truncate `s` to exactly `width` visible columns, padding with spaces when\n * shorter. ANSI SGR sequences must NOT be present in the input.\n */\nfunction padTo(visible: string, width: number): string {\n if (visible.length === width) return visible;\n if (visible.length > width) {\n if (width <= 1) return visible.slice(0, width);\n return visible.slice(0, width - 1) + '…';\n }\n return visible + ' '.repeat(width - visible.length);\n}\n\n/**\n * Render the footer row as a single ANSI string. Caller positions the\n * cursor at the last row, col 0 before writing, and restores it afterwards.\n * Always ends with SGR reset so the inner pty's next byte starts clean.\n */\nexport function renderFooter(state: FooterState, cols: number): string {\n if (cols <= 0) return '';\n if (state.kind === 'idle') {\n const sidebarBox: SidebarBox = {\n id: '', // unused by statusLine\n name: state.boxName,\n state: 'running', // we're attached, so the container is up\n activity: state.claudeActivity,\n sessionTitle: state.sessionTitle,\n };\n const isClaude = state.mode === 'claude';\n const detachable = state.detachable ?? isClaude;\n // Shell/codex modes have no claude activity to surface — passing\n // `stateLabel` overrides statusLine's default (which would otherwise show\n // `(unknown)` because `claudeActivity` is undefined and the container is\n // running).\n const stateLabel = isClaude ? undefined : state.mode === 'shell' ? 'shell' : state.mode;\n if (state.leaderActive) {\n const leaderHints = detachable ? DETACHABLE_LEADER_HINTS : PLAIN_LEADER_HINTS;\n return statusLine(sidebarBox, cols, stateLabel, leaderHints);\n }\n // Collapsed: a detachable session keeps the detach chord pinned on the\n // right (its narrow-bar fallback drops `Actions` first, never `detach`).\n const collapsed = detachable ? COLLAPSED_HINTS_DETACHABLE : COLLAPSED_HINTS_PLAIN;\n const fallback = detachable ? DETACH_PIN_HINTS : undefined;\n return statusLine(sidebarBox, cols, stateLabel, collapsed, fallback);\n }\n if (state.kind === 'flash') {\n // Flash state: a brief \"<arrow> <message>\" confirmation on the dark bar.\n const prefix = ' ▸ '; // ▸\n const inner = Math.max(0, cols - prefix.length);\n const message = padTo(state.message, inner);\n return `${BAR_BG}${FLASH_FG}${prefix}${TXT}${message}${RESET}`;\n }\n if (state.kind === 'notice') {\n // Notice state: \"<spinner> <message>\" rendered as a full-width\n // high-contrast yellow warning banner. The spinner reassures the user\n // the box is busy, not stuck.\n const spinner = SPINNER_FRAMES[state.frame % SPINNER_FRAMES.length]!;\n const prefix = ` ${spinner} `;\n const inner = Math.max(0, cols - prefix.length);\n const message = padTo(state.message, inner);\n return `${NOTICE_BG}${NOTICE_FG}${prefix}${message}${RESET}`;\n }\n // Prompt state: \"[!] <message> [detail] [y/N]\"\n // The y/N hint is suffixed; we squeeze the message+detail into the space\n // left over (truncating message first, then detail).\n const def = state.prompt.defaultAnswer ?? 'n';\n const yn = def === 'y' ? '[Y/n]' : '[y/N]';\n const tag = ' [!] ';\n const sep = ' ';\n const hintW = ` ${yn} `.length;\n const inner = Math.max(0, cols - tag.length - hintW);\n const detailRaw = state.prompt.detail ?? '';\n let message = state.prompt.message;\n let detail = detailRaw;\n const messageBudget = Math.max(8, inner - (detail.length > 0 ? sep.length + 8 : 0));\n if (message.length > messageBudget) {\n message = message.slice(0, Math.max(0, messageBudget - 1)) + '…';\n }\n const usedByMessage = message.length;\n const detailBudget = Math.max(0, inner - usedByMessage - sep.length);\n if (detail.length > detailBudget) {\n detail = detailBudget <= 1 ? '' : detail.slice(0, detailBudget - 1) + '…';\n }\n const middlePlain = detail.length > 0 ? `${message}${sep}${detail}` : message;\n const padded = padTo(middlePlain, inner);\n return `${BAR_BG}${URGENT}${tag}${TXT}${padded}${SUBTLE} ${yn} ${RESET}`;\n}\n\n/**\n * ANSI sequence to move the cursor to (row, col) — 1-based, terminal convention.\n */\nexport function cursorMoveTo(row: number, col: number): string {\n return `\\x1b[${String(row)};${String(col)}H`;\n}\n\nexport const CURSOR_SAVE = '\\x1b7';\nexport const CURSOR_RESTORE = '\\x1b8';\n\n/**\n * Synchronized output toggles (DECSET/DECRST 2026). Wrap a multi-write\n * footer paint so terminals that support it commit one atomic frame.\n */\nexport const SYNC_BEGIN = '\\x1b[?2026h';\nexport const SYNC_END = '\\x1b[?2026l';\n","import { request as httpRequest, type IncomingMessage } from 'node:http';\nimport { request as httpsRequest } from 'node:https';\nimport type { BoxNoticeEvent, PromptAnswerBody, PromptAskEvent } from '@agentbox/relay';\n\n/**\n * SSE subscription back to the relay's `GET /admin/prompts/stream`. The\n * relay pushes:\n * - `event: prompt-ask` data: PromptAskEvent (with id)\n * - `event: prompt-resolved` data: { id }\n * - `event: notice-set` data: BoxNoticeEvent (with id)\n * - `event: notice-clear` data: { id }\n * - `event: ping` data: { ts }\n *\n * We reconnect with exponential backoff on any error or close — the only\n * way to know the relay is back is to keep trying. Subscribers are\n * loopback-only so latency is sub-ms.\n */\nexport interface PromptStream {\n /** Stop subscribing; aborts any in-flight reconnect attempt. */\n close(): void;\n}\n\nexport interface SubscribeOptions {\n relayBaseUrl: string;\n boxId: string;\n onPrompt: (ev: PromptAskEvent) => void;\n /** Server-driven: a sibling wrapper (or this one) answered; the run loop\n * clears the footer for stale ids it didn't originate. */\n onResolved: (id: string) => void;\n /** A box-level informational notice was set (e.g. checkpoint in progress). */\n onNotice?: (ev: BoxNoticeEvent) => void;\n /** A previously-set notice was cleared (explicitly or via its TTL). */\n onNoticeCleared?: (id: string) => void;\n onError?: (err: Error) => void;\n}\n\nconst INITIAL_BACKOFF_MS = 200;\nconst MAX_BACKOFF_MS = 5_000;\n\nexport function subscribePrompts(opts: SubscribeOptions): PromptStream {\n let closed = false;\n let req: ReturnType<typeof httpRequest> | null = null;\n let res: IncomingMessage | null = null;\n let reconnectTimer: NodeJS.Timeout | null = null;\n let backoffMs = INITIAL_BACKOFF_MS;\n let url: URL;\n try {\n url = new URL(opts.relayBaseUrl);\n } catch (err) {\n if (opts.onError) opts.onError(err instanceof Error ? err : new Error(String(err)));\n return { close: () => {} };\n }\n const isHttps = url.protocol === 'https:';\n const transport = isHttps ? httpsRequest : httpRequest;\n const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;\n\n function scheduleReconnect(): void {\n if (closed) return;\n const delay = backoffMs;\n backoffMs = Math.min(MAX_BACKOFF_MS, backoffMs * 2);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n if (typeof reconnectTimer.unref === 'function') reconnectTimer.unref();\n }\n\n /**\n * SSE message parser: server sends `event: <type>\\n` then `data: <json>\\n\\n`.\n * The relay never splits an event across writes (one chunk per dispatch),\n * but we still buffer by message boundary `\\n\\n` so a mid-message slice\n * doesn't corrupt parsing.\n */\n let buffer = '';\n function consumeMessages(): void {\n let idx = buffer.indexOf('\\n\\n');\n while (idx !== -1) {\n const raw = buffer.slice(0, idx);\n buffer = buffer.slice(idx + 2);\n idx = buffer.indexOf('\\n\\n');\n // Drop the SSE comment line we send on connect (`: connected`).\n if (raw.startsWith(':')) continue;\n let event = '';\n let dataLine = '';\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) event = line.slice('event:'.length).trim();\n else if (line.startsWith('data:')) dataLine = line.slice('data:'.length).trim();\n }\n if (event === 'prompt-ask' && dataLine.length > 0) {\n try {\n const ev = JSON.parse(dataLine) as PromptAskEvent;\n if (ev && typeof ev.id === 'string') opts.onPrompt(ev);\n } catch {\n /* malformed; relay should never send this — ignore rather than die */\n }\n } else if (event === 'prompt-resolved' && dataLine.length > 0) {\n try {\n const payload = JSON.parse(dataLine) as { id?: string };\n if (payload && typeof payload.id === 'string') opts.onResolved(payload.id);\n } catch {\n /* malformed; ignore */\n }\n } else if (event === 'notice-set' && dataLine.length > 0) {\n try {\n const ev = JSON.parse(dataLine) as BoxNoticeEvent;\n if (ev && typeof ev.id === 'string') opts.onNotice?.(ev);\n } catch {\n /* malformed; ignore */\n }\n } else if (event === 'notice-clear' && dataLine.length > 0) {\n try {\n const payload = JSON.parse(dataLine) as { id?: string };\n if (payload && typeof payload.id === 'string') opts.onNoticeCleared?.(payload.id);\n } catch {\n /* malformed; ignore */\n }\n }\n // 'ping' has no caller-visible side effect — its purpose is to keep\n // the socket from going idle and to let the wrapper detect dead links\n // via socket-level errors. No-op here.\n }\n }\n\n function connect(): void {\n if (closed) return;\n req = transport({\n host: url.hostname,\n port,\n method: 'GET',\n path: `${url.pathname.replace(/\\/$/, '')}/admin/prompts/stream?boxId=${encodeURIComponent(opts.boxId)}`,\n headers: { Accept: 'text/event-stream' },\n });\n req.on('response', (r) => {\n res = r;\n if (r.statusCode !== 200) {\n // 400/403 — relay says \"no for you\"; bail without retrying since\n // these are config errors (no boxId, not loopback) that won't fix\n // themselves.\n if (opts.onError) opts.onError(new Error(`SSE stream returned ${String(r.statusCode)}`));\n r.resume();\n close();\n return;\n }\n backoffMs = INITIAL_BACKOFF_MS; // reset on a healthy connect\n r.setEncoding('utf8');\n r.on('data', (chunk: string) => {\n buffer += chunk;\n consumeMessages();\n });\n r.on('end', () => {\n if (!closed) scheduleReconnect();\n });\n r.on('error', () => {\n if (!closed) scheduleReconnect();\n });\n });\n req.on('error', () => {\n if (!closed) scheduleReconnect();\n });\n req.end();\n }\n\n function close(): void {\n if (closed) return;\n closed = true;\n if (reconnectTimer) clearTimeout(reconnectTimer);\n try {\n res?.destroy();\n } catch {\n /* best-effort */\n }\n try {\n req?.destroy();\n } catch {\n /* best-effort */\n }\n }\n\n connect();\n return { close };\n}\n\n/**\n * POST a PromptAnswerBody to /admin/prompts/answer. Fire-and-(mostly)-\n * forget: we don't retry on failure because the relay's `prompts.resolve`\n * is idempotent and a double-resolve returns 404. If the relay was dead,\n * the SSE reconnect loop will repush any prompts that are still pending.\n */\nexport interface PostAnswerOptions {\n relayBaseUrl: string;\n body: PromptAnswerBody;\n}\n\nexport interface PostAnswerResult {\n ok: boolean;\n status: number;\n}\n\nexport function postAnswer(opts: PostAnswerOptions): Promise<PostAnswerResult> {\n return new Promise<PostAnswerResult>((resolve) => {\n let url: URL;\n try {\n url = new URL(opts.relayBaseUrl);\n } catch {\n resolve({ ok: false, status: 0 });\n return;\n }\n const isHttps = url.protocol === 'https:';\n const transport = isHttps ? httpsRequest : httpRequest;\n const port = url.port.length > 0 ? Number.parseInt(url.port, 10) : isHttps ? 443 : 80;\n const json = JSON.stringify(opts.body);\n const req = transport(\n {\n host: url.hostname,\n port,\n method: 'POST',\n path: `${url.pathname.replace(/\\/$/, '')}/admin/prompts/answer`,\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(json).toString(),\n },\n timeout: 3000,\n },\n (res) => {\n res.resume();\n const status = res.statusCode ?? 0;\n // 204 = accepted; 404 = already answered (idempotent). Both are \"done\".\n resolve({ ok: status === 204 || status === 404, status });\n },\n );\n req.on('error', () => resolve({ ok: false, status: 0 }));\n req.on('timeout', () => {\n req.destroy();\n resolve({ ok: false, status: 0 });\n });\n req.write(json);\n req.end();\n });\n}\n","/**\n * Host→box clipboard image paste, cross-provider.\n *\n * Wired into the attach wrapper's Ctrl+V hook (`wrapped-pty/run.ts`). When the\n * user pastes while attached to an in-box Claude Code session we:\n * 1. grab the image off the host clipboard (`captureClipboardImage`),\n * 2. make sure the box's X server (`DISPLAY=:1`) is up,\n * 3. ship the PNG into the box (`Provider.uploadPath`),\n * 4. load it into the box's X11 CLIPBOARD via `xclip -t image/png`.\n * The wrapper then forwards the literal Ctrl+V so Claude Code's own\n * \"paste image from clipboard\" binding reads the now-populated selection.\n *\n * All steps go through the provider-neutral `Provider` seam (`uploadPath` +\n * `exec`), so it works identically on docker / daytona / hetzner.\n */\n\nimport { rm } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { BoxRecord, Provider } from '@agentbox/core';\nimport { captureClipboardImage } from './host-clipboard.js';\n\nexport type PasteImageResult = 'pasted' | 'no-image' | 'error';\n\n/** Box-side load: ensure Xvnc is up, wait for the X socket, then hand the PNG\n * to a detached `xclip` that keeps owning the CLIPBOARD selection until Claude\n * reads it. `setsid … &` so it survives `exec` returning. The path is a\n * CLI-generated `/tmp` name (no shell metacharacters), so inlining is safe. */\nfunction loadClipboardScript(boxPngPath: string): string {\n return [\n 'pgrep -x Xvnc >/dev/null 2>&1 || /usr/local/bin/agentbox-vnc-start >/dev/null 2>&1 || true',\n 'for _ in $(seq 1 30); do [ -S /tmp/.X11-unix/X1 ] && break; sleep 0.2; done',\n `setsid sh -c 'DISPLAY=:1 xclip -selection clipboard -t image/png -i ${boxPngPath}' </dev/null >/dev/null 2>&1 &`,\n ].join('; ');\n}\n\nexport async function pasteHostClipboardImage(\n provider: Provider,\n box: BoxRecord,\n): Promise<PasteImageResult> {\n if (typeof provider.uploadPath !== 'function') return 'error';\n\n const hostPng = await captureClipboardImage();\n if (!hostPng) return 'no-image';\n\n const boxPng = `/tmp/agentbox-clip-${String(Date.now())}.png`;\n try {\n await provider.uploadPath(box, hostPng, boxPng);\n await provider.exec(box, ['sh', '-lc', loadClipboardScript(boxPng)], {\n user: 'vscode',\n });\n return 'pasted';\n } catch {\n return 'error';\n } finally {\n await rm(dirname(hostPng), { recursive: true, force: true }).catch(() => {});\n }\n}\n","/**\n * Capture an image off the host clipboard to a temp PNG.\n *\n * Used by the Ctrl+V paste path (`paste-image.ts`): when the user pastes while\n * attached to an in-box Claude Code session, we grab whatever image they copied\n * on the host and ship it into the box. Supported hosts:\n * - macOS: `osascript` coerces the clipboard to PNG (TIFF screenshots are\n * converted with `sips`). Both ship with the OS.\n * - Linux (X11 / Wayland desktop): `xclip` / `wl-paste` read the `image/png`\n * clipboard target. These aren't always installed, so capture degrades to\n * `null` when the tool (or a display) is missing.\n * Every other platform returns `null`, so the caller cleanly forwards Ctrl+V\n * unchanged.\n */\n\nimport { mkdtemp, rm, stat, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { execa } from 'execa';\n\n/**\n * Grab the current clipboard image into a host temp PNG. Returns the file path,\n * or `null` when the clipboard holds no image (unsupported platform, missing\n * tool, or capture failed). The caller owns the returned file and should delete\n * it (and its parent dir) after delivery.\n */\nexport async function captureClipboardImage(): Promise<string | null> {\n if (process.platform !== 'darwin' && process.platform !== 'linux') return null;\n\n const dir = await mkdtemp(join(tmpdir(), 'agentbox-clip-'));\n const pngPath = join(dir, 'clip.png');\n const ok =\n process.platform === 'darwin'\n ? await captureDarwin(dir, pngPath)\n : await captureLinux(pngPath);\n\n if (ok) return pngPath;\n await rm(dir, { recursive: true, force: true }).catch(() => {});\n return null;\n}\n\n/**\n * True when this host has a clipboard-image capture path. Call sites use it to\n * decide whether to wire the Ctrl+V hook at all — so a host with no clipboard\n * tool (or no display) leaves Ctrl+V forwarding verbatim instead of\n * intercepting it for a guaranteed-empty paste.\n */\nexport async function clipboardCaptureAvailable(): Promise<boolean> {\n if (process.platform === 'darwin') return true;\n if (process.platform === 'linux') return (await linuxClipboardTool()) !== null;\n return false;\n}\n\n// ---- macOS ----\n\n/** AppleScript that writes the clipboard to `<path>` as PNG, falling back to\n * TIFF. Prints `PNG`, `TIFF`, or `NONE`. Returns the flattened `-e <line> …`\n * argv for `osascript`. */\nfunction captureScriptArgs(pngPath: string, tiffPath: string): string[] {\n return [\n 'try',\n ' set theData to (the clipboard as «class PNGf»)',\n ` set fh to open for access (POSIX file ${JSON.stringify(pngPath)}) with write permission`,\n ' set eof fh to 0',\n ' write theData to fh',\n ' close access fh',\n ' return \"PNG\"',\n 'on error',\n ' try',\n ' set theData to (the clipboard as «class TIFF»)',\n ` set fh to open for access (POSIX file ${JSON.stringify(tiffPath)}) with write permission`,\n ' set eof fh to 0',\n ' write theData to fh',\n ' close access fh',\n ' return \"TIFF\"',\n ' on error',\n ' return \"NONE\"',\n ' end try',\n 'end try',\n ]\n .map((line) => ['-e', line])\n .flat();\n}\n\nasync function captureDarwin(dir: string, pngPath: string): Promise<boolean> {\n const tiffPath = join(dir, 'clip.tiff');\n const res = await execa('osascript', captureScriptArgs(pngPath, tiffPath), {\n reject: false,\n });\n const kind = res.stdout.trim();\n\n if (kind === 'PNG') return fileHasBytes(pngPath);\n\n if (kind === 'TIFF' && (await fileHasBytes(tiffPath))) {\n // Screenshots land on the clipboard as TIFF; convert to PNG with sips.\n const conv = await execa(\n 'sips',\n ['-s', 'format', 'png', tiffPath, '--out', pngPath],\n { reject: false },\n );\n if (conv.exitCode === 0) return fileHasBytes(pngPath);\n }\n return false;\n}\n\n// ---- Linux (X11 / Wayland) ----\n\n/** Which clipboard tool to use on this Linux host, or `null` when none is\n * usable (no display, or the binary isn't installed). Wayland wins when a\n * Wayland session is present. */\nasync function linuxClipboardTool(): Promise<'wayland' | 'x11' | null> {\n if (process.env['WAYLAND_DISPLAY'] && (await hasCmd('wl-paste'))) return 'wayland';\n if (process.env['DISPLAY'] && (await hasCmd('xclip'))) return 'x11';\n return null;\n}\n\nasync function captureLinux(pngPath: string): Promise<boolean> {\n const tool = await linuxClipboardTool();\n if (!tool) return false;\n\n let buf: Buffer | null = null;\n if (tool === 'wayland') {\n const types = await execa('wl-paste', ['--list-types'], { reject: false });\n if (types.exitCode !== 0 || !/image\\/png/i.test(types.stdout)) return false;\n const r = await execa('wl-paste', ['--type', 'image/png'], {\n encoding: 'buffer',\n reject: false,\n });\n if (r.exitCode === 0) buf = asBuffer(r.stdout);\n } else {\n const sel = ['-selection', 'clipboard'];\n const targets = await execa('xclip', [...sel, '-t', 'TARGETS', '-o'], {\n reject: false,\n });\n if (targets.exitCode !== 0 || !/image\\/png/i.test(targets.stdout)) return false;\n const r = await execa('xclip', [...sel, '-t', 'image/png', '-o'], {\n encoding: 'buffer',\n reject: false,\n });\n if (r.exitCode === 0) buf = asBuffer(r.stdout);\n }\n\n if (!buf || !isPng(buf)) return false;\n await writeFile(pngPath, buf);\n return true;\n}\n\n// ---- helpers ----\n\n/** `command -v <cmd>` — true when the binary is on PATH. `cmd` is a fixed\n * literal at every call site (no injection surface). */\nasync function hasCmd(cmd: string): Promise<boolean> {\n const r = await execa('sh', ['-c', `command -v ${cmd}`], { reject: false });\n return r.exitCode === 0;\n}\n\nfunction asBuffer(out: unknown): Buffer | null {\n if (Buffer.isBuffer(out)) return out;\n if (out instanceof Uint8Array) return Buffer.from(out);\n return null;\n}\n\nfunction isPng(buf: Buffer): boolean {\n return (\n buf.length >= 8 &&\n buf[0] === 0x89 &&\n buf[1] === 0x50 &&\n buf[2] === 0x4e &&\n buf[3] === 0x47\n );\n}\n\nasync function fileHasBytes(path: string): Promise<boolean> {\n try {\n const s = await stat(path);\n return s.isFile() && s.size > 0;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["spawn","spawn","spawn","code","inner","message","spawn","exitCode","rm","rm","spawn"]}
|