@habemus-papadum/aiui 0.2.0 → 0.3.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/cli.js +1450 -728
- package/dist/cli.js.map +1 -1
- package/dist/commands/browser.d.ts +48 -0
- package/dist/commands/clean.d.ts +49 -0
- package/dist/commands/config-tui.d.ts +1 -0
- package/dist/commands/config.d.ts +43 -0
- package/dist/commands/debug.d.ts +15 -0
- package/dist/commands/demo.d.ts +1 -1
- package/dist/commands/paint.d.ts +3 -0
- package/dist/commands/vite.d.ts +38 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/sidecars-BKlhUA0V.js +48 -0
- package/dist/sidecars-BKlhUA0V.js.map +1 -0
- package/dist/util/aiui-args.d.ts +40 -0
- package/dist/util/chrome.d.ts +6 -2
- package/dist/util/config-schema.d.ts +96 -0
- package/dist/util/config.d.ts +31 -59
- package/dist/util/first-run.d.ts +11 -8
- package/dist/util/openai-preflight.d.ts +3 -2
- package/dist/util/resolve-cli.d.ts +1 -11
- package/dist/util/sidecars.d.ts +62 -0
- package/package.json +10 -6
- package/templates/demo/package.json +2 -0
- package/dist/util/browser.d.ts +0 -44
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/util/browser.ts","../src/util/config.ts","../src/util/prompt.ts","../src/util/ui.ts","../src/util/cft.ts","../src/util/resolve-cli.ts","../src/util/chrome.ts","../src/commands/browser.ts","../src/commands/chrome.ts","../src/util/aiui-args.ts","../src/util/enter-nudge.ts","../src/util/first-run.ts","../src/util/openai-preflight.ts","../src/util/version.ts","../src/util/which.ts","../src/commands/claude.ts","../src/commands/demo.ts","../src/commands/mcp.ts","../src/commands/vite.ts","../src/program.ts","../src/cli.ts"],"sourcesContent":["/**\n * The session browser: one user-visible Chrome shared by the human and the\n * agent.\n *\n * In the default \"attach\" mode, aiui launches the browser itself — with a\n * DevTools debug port, the project profile, and the aiui devtools extension —\n * and chrome-devtools-mcp *attaches* to it (`--browser-url`) instead of\n * launching a private one. That's what makes the agent's browser the same\n * window the human is looking at: shared tabs, shared state, visible from\n * session start.\n *\n * There is deliberately no registry file for browsers. Chrome itself writes\n * `DevToolsActivePort` into the user data dir of any instance started with a\n * debug port, and the user data dir is already the profile's identity — so\n * discovery is: read that file, confirm the endpoint answers `/json/version`.\n * A dead file (crash leftovers, stale port) just fails the liveness probe.\n *\n * Security note (documented in docs/guide/warning): the debug endpoint is\n * unauthenticated — any local process can drive the browser through it. It\n * binds to loopback only.\n */\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { Browser, ChromeReleaseChannel, computeSystemExecutablePath } from \"@puppeteer/browsers\";\nimport { execa } from \"execa\";\nimport type { ChromeChannel } from \"./config\";\n\n/** Chrome writes this into the user data dir when debugging is enabled. */\nconst ACTIVE_PORT_FILE = \"DevToolsActivePort\";\n\n/** How long a fresh Chrome gets to bring up its debug endpoint. */\nconst LAUNCH_TIMEOUT_MS = 20_000;\n\nexport interface SessionBrowser {\n /** The DevTools endpoint chrome-devtools-mcp attaches to. */\n browserUrl: string;\n port: number;\n}\n\n/**\n * A live session browser for this profile, if one is running.\n * Never launches anything; safe to call from non-interactive paths.\n */\nexport async function discoverSessionBrowser(\n userDataDir: string,\n): Promise<SessionBrowser | undefined> {\n const port = readActivePort(userDataDir);\n if (port === undefined) {\n return undefined;\n }\n if (!(await debugEndpointAlive(port))) {\n return undefined;\n }\n return { browserUrl: `http://127.0.0.1:${port}`, port };\n}\n\nfunction readActivePort(userDataDir: string): number | undefined {\n try {\n const [first] = readFileSync(join(userDataDir, ACTIVE_PORT_FILE), \"utf8\").split(\"\\n\");\n const port = Number(first);\n return Number.isInteger(port) && port > 0 ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\nasync function debugEndpointAlive(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/json/version`, {\n signal: AbortSignal.timeout(1000),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Which binary a session-browser launch should run: an explicit\n * executablePath (usually the managed Chrome for Testing) wins; otherwise the\n * system install of the requested channel (default stable). Throws when no\n * such browser is installed.\n */\nexport function sessionBrowserBinary(settings: {\n executablePath?: string;\n channel?: ChromeChannel;\n}): string {\n if (settings.executablePath) {\n return settings.executablePath;\n }\n return computeSystemExecutablePath({\n browser: Browser.CHROME,\n channel: RELEASE_CHANNELS[settings.channel ?? \"stable\"],\n });\n}\n\nconst RELEASE_CHANNELS: Record<ChromeChannel, ChromeReleaseChannel> = {\n stable: ChromeReleaseChannel.STABLE,\n beta: ChromeReleaseChannel.BETA,\n dev: ChromeReleaseChannel.DEV,\n canary: ChromeReleaseChannel.CANARY,\n};\n\n/**\n * Launch the session browser detached (it deliberately outlives the aiui\n * process — it's the user's window too) and wait for its debug endpoint.\n *\n * `debugPort` 0 lets the OS pick a free port; Chrome reports the choice via\n * `DevToolsActivePort`, which is removed first so a stale file from a previous\n * run can't win the poll. Fails fast if the process exits early — the classic\n * cause being an already-running Chrome on the same profile *without* a debug\n * port, which swallows the new invocation as a URL-handoff and exits.\n */\nexport async function launchSessionBrowser(opts: {\n binary: string;\n userDataDir: string;\n debugPort?: number;\n extensionDir?: string;\n headless?: boolean;\n startUrl?: string;\n}): Promise<SessionBrowser> {\n mkdirSync(opts.userDataDir, { recursive: true });\n rmSync(join(opts.userDataDir, ACTIVE_PORT_FILE), { force: true });\n\n const args = [\n `--remote-debugging-port=${opts.debugPort ?? 0}`,\n `--user-data-dir=${opts.userDataDir}`,\n \"--no-first-run\",\n \"--no-default-browser-check\",\n ];\n if (opts.extensionDir) {\n args.push(`--load-extension=${opts.extensionDir}`);\n }\n if (opts.headless) {\n args.push(\"--headless\");\n }\n args.push(opts.startUrl ?? \"about:blank\");\n\n const child = execa(opts.binary, args, {\n detached: true,\n stdio: \"ignore\",\n reject: false,\n cleanup: false,\n });\n child.unref();\n let exited = false;\n void child.then(() => {\n exited = true;\n });\n\n const deadline = Date.now() + LAUNCH_TIMEOUT_MS;\n while (Date.now() < deadline) {\n const found = await discoverSessionBrowser(opts.userDataDir);\n if (found) {\n // Purely informational breadcrumb (pid, when) for humans poking around.\n try {\n writeFileSync(\n join(opts.userDataDir, \"aiui-browser.json\"),\n `${JSON.stringify({ pid: child.pid, startedAt: new Date().toISOString() })}\\n`,\n );\n } catch {}\n return found;\n }\n if (exited) {\n throw new Error(\n \"the browser exited before exposing its DevTools endpoint — is another Chrome \" +\n \"already running on this profile without a debug port? Close it and retry.\",\n );\n }\n await sleep(250);\n }\n throw new Error(\n `the browser did not expose its DevTools endpoint within ${LAUNCH_TIMEOUT_MS / 1000}s`,\n );\n}\n\n/**\n * Open a URL as a new tab in a session browser, via the DevTools HTTP API\n * (`PUT /json/new` — PUT is required by current Chrome).\n */\nexport async function openInSessionBrowser(browserUrl: string, url: string): Promise<void> {\n const base = browserUrl.replace(/\\/+$/, \"\");\n const res = await fetch(`${base}/json/new?${encodeURI(url)}`, {\n method: \"PUT\",\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n throw new Error(`the browser refused to open the tab (${res.status} ${res.statusText})`);\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * aiui's two-level configuration file.\n *\n * Settings live in `config.json` at up to two places, merged per-key with the\n * more specific one winning:\n *\n * <user cache>/config.json e.g. ~/.cache/aiui/config.json (respects\n * AIUI_CACHE / XDG_CACHE_HOME)\n * <project>/.aiui-cache/config.json next to the traces and Chrome profiles\n *\n * CLI flags override both. Everything is optional; a missing file is an empty\n * config. A *malformed* file is a hard error, not a warning — these settings\n * gate security-relevant behavior (`claude.skipPermissions`), and a typo that\n * silently reverts to the dangerous default is worse than a failed launch. The\n * same reasoning rejects unknown keys.\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { projectCacheDir } from \"@habemus-papadum/aiui-claude-channel\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\n\nexport const CONFIG_FILENAME = \"config.json\";\n\nexport const CHROME_CHANNELS = [\"stable\", \"beta\", \"dev\", \"canary\"] as const;\nexport type ChromeChannel = (typeof CHROME_CHANNELS)[number];\n\nexport const FOR_TESTING_MODES = [\"prompt\", \"auto\", \"off\"] as const;\nexport type ForTestingMode = (typeof FOR_TESTING_MODES)[number];\n\nexport const CHROME_MODES = [\"attach\", \"launch\"] as const;\nexport type ChromeMode = (typeof CHROME_MODES)[number];\n\nexport interface AiuiConfig {\n claude?: {\n /**\n * Launch Claude Code with `--dangerously-skip-permissions`. A personal\n * preference with real consequences (see docs/guide/warning) — the first\n * interactive launch asks and persists the answer here; when unset in a\n * non-interactive session it defaults to true.\n */\n skipPermissions?: boolean;\n /**\n * Auto-dismiss Claude Code's development-channel acknowledgement prompt\n * by injecting an Enter keypress into the terminal at startup (see\n * util/enter-nudge.ts). Chosen on first interactive run; defaults to true\n * when unset.\n */\n enterNudge?: boolean;\n };\n chrome?: {\n /**\n * Attach the Chrome DevTools MCP (default: true). `false` turns it off\n * everywhere; `true` does not override the CI default-off — only the\n * `--aiui-chrome` flag forces it on under CI.\n */\n enabled?: boolean;\n /**\n * How the MCP reaches a browser (default: \"attach\"). `\"attach\"` shares a\n * user-visible session browser: an already-running one is discovered by\n * profile, or an interactive launch starts one eagerly. `\"launch\"` is the\n * hands-off mode: chrome-devtools-mcp launches its own private browser\n * lazily, on the agent's first browser tool call.\n */\n mode?: ChromeMode;\n /**\n * Attach to this Chrome DevTools endpoint (e.g. \"http://127.0.0.1:9222\")\n * instead of managing a browser at all — the remote-development key: the\n * browser runs on another machine (started there with `aiui browser`) and\n * its port is tunneled over. Setting it implies `mode: \"attach\"` and makes\n * every local-browser setting (profile, executablePath, channel,\n * forTesting…) irrelevant.\n */\n browserUrl?: string;\n /**\n * Fixed DevTools debug port for session browsers aiui launches\n * (default: 0 — an OS-assigned free port). Pin it (e.g. 9222) when\n * something else must find the port, like an ssh tunnel.\n */\n debugPort?: number;\n /** Named profile under `.aiui-cache/chrome/` (default: \"default\"). */\n profile?: string;\n /** Explicit Chrome user data dir; takes precedence over `profile`. */\n dataDir?: string;\n /**\n * Chrome binary to launch — e.g. a Chrome for Testing install, which\n * still honors `--load-extension`. Mutually exclusive with `channel`.\n */\n executablePath?: string;\n /** Installed Chrome release channel to launch (default: stable). */\n channel?: ChromeChannel;\n /**\n * How `aiui claude` manages Chrome for Testing, the recommended browser\n * (default: \"prompt\"). `\"prompt\"` asks before installing or updating it —\n * interactive sessions only, never under CI; `\"auto\"` installs/updates\n * without asking; `\"off\"` never checks. Skipped entirely when\n * `executablePath` or `channel` picks a browser explicitly.\n */\n forTesting?: ForTestingMode;\n /** Launch Chrome headless (default: false). */\n headless?: boolean;\n /**\n * In a dev checkout, rebuild the aiui-devtools-extension package (~0.3s of tsc)\n * on every launch so the auto-loaded panel is never stale (default: true).\n */\n buildExtension?: boolean;\n };\n}\n\n/** The `config.json` paths consulted, user-level first (base: the project dir). */\nexport function configPaths(base: string = process.cwd()): { user: string; project: string } {\n return {\n user: join(cacheDir(undefined, { create: false }), CONFIG_FILENAME),\n project: join(projectCacheDir(base), CONFIG_FILENAME),\n };\n}\n\n/** Load and merge the user- and project-level configs (project wins per key). */\nexport function loadAiuiConfig(base: string = process.cwd()): AiuiConfig {\n const paths = configPaths(base);\n return mergeAiuiConfig(readConfigFile(paths.user) ?? {}, readConfigFile(paths.project) ?? {});\n}\n\n/** Merge two configs section-by-section; `override`'s keys win within a section. */\nexport function mergeAiuiConfig(base: AiuiConfig, override: AiuiConfig): AiuiConfig {\n return {\n claude: { ...base.claude, ...override.claude },\n chrome: { ...base.chrome, ...override.chrome },\n };\n}\n\n/**\n * Read one config file. Missing → undefined; unreadable JSON or an unexpected\n * shape → an error naming the file, so the fix is obvious.\n */\nexport function readConfigFile(file: string): AiuiConfig | undefined {\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return undefined;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error) {\n throw new Error(\n `invalid JSON in ${file}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return validateConfig(raw, file);\n}\n\nfunction validateConfig(raw: unknown, file: string): AiuiConfig {\n const root = asSection(raw, file, \"the top level\");\n rejectUnknownKeys(root, [\"claude\", \"chrome\"], file, \"the top level\");\n\n const config: AiuiConfig = {};\n if (root.claude !== undefined) {\n const claude = asSection(root.claude, file, `\"claude\"`);\n rejectUnknownKeys(claude, [\"skipPermissions\", \"enterNudge\"], file, `\"claude\"`);\n // prune: absent keys must stay absent, or the merge spread would let an\n // explicit `undefined` here clobber a value from the other config level.\n config.claude = prune({\n skipPermissions: asOptional(\n claude.skipPermissions,\n \"boolean\",\n file,\n \"claude.skipPermissions\",\n ),\n enterNudge: asOptional(claude.enterNudge, \"boolean\", file, \"claude.enterNudge\"),\n });\n }\n if (root.chrome !== undefined) {\n const chrome = asSection(root.chrome, file, `\"chrome\"`);\n rejectUnknownKeys(\n chrome,\n [\n \"enabled\",\n \"mode\",\n \"browserUrl\",\n \"debugPort\",\n \"profile\",\n \"dataDir\",\n \"executablePath\",\n \"channel\",\n \"forTesting\",\n \"headless\",\n \"buildExtension\",\n ],\n file,\n `\"chrome\"`,\n );\n const channel = asOptional(chrome.channel, \"string\", file, \"chrome.channel\");\n if (channel !== undefined && !(CHROME_CHANNELS as readonly string[]).includes(channel)) {\n throw new Error(\n `invalid chrome.channel \"${channel}\" in ${file} — expected one of: ${CHROME_CHANNELS.join(\", \")}`,\n );\n }\n const forTesting = asOptional(chrome.forTesting, \"string\", file, \"chrome.forTesting\");\n if (\n forTesting !== undefined &&\n !(FOR_TESTING_MODES as readonly string[]).includes(forTesting)\n ) {\n throw new Error(\n `invalid chrome.forTesting \"${forTesting}\" in ${file} — expected one of: ${FOR_TESTING_MODES.join(\", \")}`,\n );\n }\n const mode = asOptional(chrome.mode, \"string\", file, \"chrome.mode\");\n if (mode !== undefined && !(CHROME_MODES as readonly string[]).includes(mode)) {\n throw new Error(\n `invalid chrome.mode \"${mode}\" in ${file} — expected one of: ${CHROME_MODES.join(\", \")}`,\n );\n }\n const browserUrl = asOptional(chrome.browserUrl, \"string\", file, \"chrome.browserUrl\");\n if (browserUrl !== undefined && !isHttpUrl(browserUrl)) {\n throw new Error(\n `invalid chrome.browserUrl \"${browserUrl}\" in ${file} — expected an http(s) URL like \"http://127.0.0.1:9222\"`,\n );\n }\n const debugPort = asOptional(chrome.debugPort, \"number\", file, \"chrome.debugPort\");\n if (\n debugPort !== undefined &&\n !(Number.isInteger(debugPort) && debugPort >= 0 && debugPort <= 65535)\n ) {\n throw new Error(`invalid chrome.debugPort ${debugPort} in ${file} — expected 0..65535`);\n }\n config.chrome = prune({\n enabled: asOptional(chrome.enabled, \"boolean\", file, \"chrome.enabled\"),\n mode: mode as ChromeMode | undefined,\n browserUrl,\n debugPort,\n profile: asOptional(chrome.profile, \"string\", file, \"chrome.profile\"),\n dataDir: asOptional(chrome.dataDir, \"string\", file, \"chrome.dataDir\"),\n executablePath: asOptional(chrome.executablePath, \"string\", file, \"chrome.executablePath\"),\n channel: channel as ChromeChannel | undefined,\n forTesting: forTesting as ForTestingMode | undefined,\n headless: asOptional(chrome.headless, \"boolean\", file, \"chrome.headless\"),\n buildExtension: asOptional(chrome.buildExtension, \"boolean\", file, \"chrome.buildExtension\"),\n });\n }\n return config;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\n/**\n * Persist a change to the **user-level** config (creating the file if needed).\n *\n * This is how interactive prompt answers like \"never ask again\" become\n * durable: they are per-user decisions, so they land in the user cache — never\n * in the project file, which may be shared/committed by a team.\n */\nexport function updateUserConfig(mutate: (config: AiuiConfig) => void): string {\n const file = configPaths().user;\n const config = readConfigFile(file) ?? {};\n mutate(config);\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, `${JSON.stringify(config, null, 2)}\\n`);\n return file;\n}\n\n/** Drop `undefined`-valued keys so merging can't see them as overrides. */\nfunction prune<T extends Record<string, unknown>>(section: T): T {\n return Object.fromEntries(Object.entries(section).filter(([, v]) => v !== undefined)) as T;\n}\n\nfunction asSection(value: unknown, file: string, where: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`expected an object at ${where} of ${file}`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction rejectUnknownKeys(\n section: Record<string, unknown>,\n known: string[],\n file: string,\n where: string,\n): void {\n for (const key of Object.keys(section)) {\n if (!known.includes(key)) {\n throw new Error(\n `unknown key \"${key}\" at ${where} of ${file} — known keys: ${known.join(\", \")}`,\n );\n }\n }\n}\n\nfunction asOptional<T extends \"boolean\" | \"string\" | \"number\">(\n value: unknown,\n type: T,\n file: string,\n where: string,\n): (T extends \"boolean\" ? boolean : T extends \"number\" ? number : string) | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (typeof value !== type) {\n throw new Error(`expected a ${type} for ${where} in ${file}`);\n }\n return value as T extends \"boolean\" ? boolean : T extends \"number\" ? number : string;\n}\n","/**\n * A minimal interactive chooser for the launcher's rare questions (CfT\n * install/update offers, first-run choices). Menus print to stderr so a piped\n * stdout stays clean; answers match a choice's key or any unambiguous label\n * prefix. With a `defaultKey`, Enter takes the default; without one the\n * question requires an explicit answer (for choices that should be definitive,\n * not waved through). Callers are responsible for only asking in a real\n * interactive session (TTY, not CI).\n */\nimport { createInterface } from \"node:readline/promises\";\nimport chalk from \"chalk\";\n\nexport interface Choice {\n key: string;\n label: string;\n}\n\nexport async function choose(\n question: string,\n choices: Choice[],\n defaultKey?: string,\n): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const menu = choices\n .map(\n (c) =>\n ` ${chalk.bold(`[${c.key === defaultKey ? c.key.toUpperCase() : c.key}]`)} ${c.label}`,\n )\n .join(\"\\n\");\n for (;;) {\n const raw = await rl.question(`${chalk.cyan(question)}\\n${menu}\\n> `);\n const answer = raw.trim().toLowerCase();\n if (!answer) {\n if (defaultKey !== undefined) {\n return defaultKey;\n }\n continue; // definitive questions have no default — ask again\n }\n const hit =\n choices.find((c) => c.key === answer) ??\n choices.find((c) => c.label.toLowerCase().startsWith(answer));\n if (hit) {\n return hit.key;\n }\n // Unrecognized — the loop re-prints the menu.\n }\n } finally {\n rl.close();\n }\n}\n","import chalk from \"chalk\";\n\n/**\n * Styled terminal output — our small stand-in for Python's `rich`.\n *\n * We deliberately keep the surface tiny: the launcher only ever needs to shout\n * about errors and warn about degraded launches. Routine progress (which CLIs\n * were found, what command is being assembled) is intentionally left unprinted\n * so the terminal stays quiet until something actually goes wrong.\n */\nexport function printError(title: string, detail?: string): void {\n console.error(`${chalk.bgRed.white.bold(\" ERROR \")} ${chalk.red.bold(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n\nexport function printWarning(title: string, detail?: string): void {\n console.error(`${chalk.bgYellow.black.bold(\" WARN \")} ${chalk.yellow.bold(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n\n/** A one-off informational aside (tips, config-written confirmations). */\nexport function printNote(title: string, detail?: string): void {\n console.error(`${chalk.bgCyan.black.bold(\" NOTE \")} ${chalk.cyan(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n","/**\n * Managed Chrome for Testing (CfT) — the recommended browser for `aiui claude`.\n *\n * CfT is Google's automation build of Chrome: version-pinned, no auto-update,\n * and it still honors `--load-extension`, so the aiui DevTools panel loads\n * automatically (branded Chrome ≥ 137 ignores that flag). aiui keeps its own\n * CfT install in the **user-level** cache (`~/.cache/aiui/chrome/`, shared\n * across projects — these are ~160 MB downloads) via `@puppeteer/browsers`,\n * which manages `<cacheDir>/chrome/<platform>-<buildId>/` layouts for us.\n *\n * Because CfT never updates itself, staying current is our job: launches check\n * the latest stable build id (at most once per {@link CHECK_TTL_MS}, with a\n * short network timeout, silently skipped offline) and either prompt or\n * auto-update per `chrome.forTesting` in config. All prompt bookkeeping —\n * when we last checked, which update the user skipped, when an install offer\n * was declined — lives in one small state file next to the installs.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\nimport {\n Browser,\n detectBrowserPlatform,\n getInstalledBrowsers,\n install,\n resolveBuildId,\n uninstall,\n} from \"@puppeteer/browsers\";\nimport { type ForTestingMode, updateUserConfig } from \"./config\";\nimport { choose } from \"./prompt\";\nimport { printNote } from \"./ui\";\n\n/** Re-resolve the latest stable build id at most this often. */\nexport const CHECK_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Network budget for the version lookup on the launch path. */\nconst RESOLVE_TIMEOUT_MS = 4000;\n\n/** One managed CfT install. */\nexport interface CftInstall {\n buildId: string;\n executablePath: string;\n}\n\n/** Prompt/check bookkeeping, persisted in the CfT cache dir. */\nexport interface CftState {\n /** Epoch ms of the last successful latest-stable lookup. */\n checkedAt?: number;\n /** The latest stable build id as of `checkedAt`. */\n latestBuildId?: string;\n /** Update prompt answered \"skip\" for this build id — don't re-ask for it. */\n skippedBuildId?: string;\n /** Epoch ms when an install offer was declined — snooze re-asking for a day. */\n installDeclinedAt?: number;\n}\n\n/** Where managed CfT builds (and the state file) live. */\nexport function cftCacheDir(create = true): string {\n return cacheDir(\"chrome\", { create });\n}\n\nconst STATE_FILE = \"update-state.json\";\n\nexport function readCftState(): CftState {\n try {\n return JSON.parse(readFileSync(join(cftCacheDir(false), STATE_FILE), \"utf8\")) as CftState;\n } catch {\n return {};\n }\n}\n\nexport function writeCftState(patch: Partial<CftState>): void {\n const dir = cftCacheDir();\n mkdirSync(dir, { recursive: true });\n writeFileSync(join(dir, STATE_FILE), `${JSON.stringify({ ...readCftState(), ...patch })}\\n`);\n}\n\n/** Numeric, segment-wise build id comparison (\"138.0.7204.94\"-style). */\nexport function compareBuildIds(a: string, b: string): number {\n const as = a.split(\".\").map(Number);\n const bs = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(as.length, bs.length); i++) {\n const diff = (as[i] ?? 0) - (bs[i] ?? 0);\n if (diff) {\n return diff < 0 ? -1 : 1;\n }\n }\n return 0;\n}\n\n/** The newest managed CfT install, if any. Never touches the network. */\nexport async function installedCft(): Promise<CftInstall | undefined> {\n const dir = cftCacheDir(false);\n if (!existsSync(dir)) {\n return undefined;\n }\n const browsers = await getInstalledBrowsers({ cacheDir: dir });\n const chromes = browsers\n .filter((b) => b.browser === Browser.CHROME)\n .sort((a, b) => compareBuildIds(a.buildId, b.buildId));\n const best = chromes.at(-1);\n return best && { buildId: best.buildId, executablePath: best.executablePath };\n}\n\n/**\n * The latest stable CfT build id, freshness-limited and offline-tolerant.\n *\n * Consults the network at most once per `maxAgeMs` (persisted in the state\n * file) with a short timeout; on failure it falls back to the last known\n * value, or undefined — callers treat undefined as \"can't tell, don't nag\".\n */\nexport async function latestStableCft(\n opts: { maxAgeMs?: number; timeoutMs?: number; now?: number } = {},\n): Promise<string | undefined> {\n const { maxAgeMs = CHECK_TTL_MS, timeoutMs = RESOLVE_TIMEOUT_MS, now = Date.now() } = opts;\n const state = readCftState();\n if (state.latestBuildId && state.checkedAt && now - state.checkedAt < maxAgeMs) {\n return state.latestBuildId;\n }\n const platform = detectBrowserPlatform();\n if (!platform) {\n return undefined;\n }\n try {\n const buildId = await withTimeout(\n resolveBuildId(Browser.CHROME, platform, \"stable\"),\n timeoutMs,\n );\n writeCftState({ checkedAt: now, latestBuildId: buildId });\n return buildId;\n } catch {\n return state.latestBuildId;\n }\n}\n\n/**\n * Install the given CfT build into the managed cache and drop superseded\n * builds (they're ~160 MB each; the whole point of the managed dir is that\n * there's exactly one, current, browser in it).\n */\nexport async function installCft(buildId: string): Promise<CftInstall> {\n const dir = cftCacheDir();\n const fresh = await install({\n browser: Browser.CHROME,\n buildId,\n cacheDir: dir,\n downloadProgressCallback: \"default\",\n });\n const others = (await getInstalledBrowsers({ cacheDir: dir })).filter(\n (b) => b.browser === Browser.CHROME && b.buildId !== buildId,\n );\n for (const old of others) {\n await uninstall({ browser: Browser.CHROME, buildId: old.buildId, cacheDir: dir });\n }\n return { buildId, executablePath: fresh.executablePath };\n}\n\n/**\n * Bring the managed CfT to the latest stable (the `aiui chrome install` /\n * `update` implementation). Unlike the launch path this resolves with no\n * timeout — an explicit command is allowed to wait on the network — and\n * reports what it did.\n */\nexport async function ensureLatestCft(\n report: (line: string) => void,\n): Promise<CftInstall & { outcome: \"current\" | \"installed\" | \"updated\" }> {\n const platform = detectBrowserPlatform();\n if (!platform) {\n throw new Error(\"could not detect a supported platform for Chrome for Testing\");\n }\n const latest = await resolveBuildId(Browser.CHROME, platform, \"stable\");\n writeCftState({ checkedAt: Date.now(), latestBuildId: latest });\n const current = await installedCft();\n if (current && compareBuildIds(current.buildId, latest) >= 0) {\n report(`Chrome for Testing ${current.buildId} is up to date`);\n return { ...current, outcome: \"current\" };\n }\n report(\n current\n ? `updating Chrome for Testing ${current.buildId} → ${latest}…`\n : `installing Chrome for Testing ${latest}…`,\n );\n const fresh = await installCft(latest);\n report(`Chrome for Testing ${latest} installed at ${fresh.executablePath}`);\n return { ...fresh, outcome: current ? \"updated\" : \"installed\" };\n}\n\n/**\n * The launch-path CfT sync: decide which browser this session should use, and\n * (interactively, when allowed) offer to install or update the managed CfT.\n *\n * Returns the CfT executable path to prefer, or undefined to fall back to the\n * system Chrome. Callers only invoke this when config names no browser\n * explicitly (no `chrome.executablePath` / `chrome.channel`).\n *\n * The mode ladder (`chrome.forTesting`, default \"prompt\"):\n * - \"off\" — never check, never prompt; an already-installed managed CfT is\n * still used (install one deliberately with `aiui chrome install`).\n * - \"auto\" — install/update to latest stable without asking.\n * - \"prompt\" — offer to install when missing, offer to update when stale;\n * answers can rewrite the mode in the user config.\n *\n * Nothing here ever blocks a non-interactive session: without a TTY (or under\n * CI, or in print mode) this degrades to \"use whatever is already installed\".\n * Downloads are likewise interactive-only — even \"auto\" won't pull ~160 MB\n * into a headless one-shot.\n */\nexport async function syncChromeForTesting(opts: {\n mode: ForTestingMode;\n interactive: boolean;\n now?: number;\n}): Promise<string | undefined> {\n const { mode, interactive, now = Date.now() } = opts;\n const current = await installedCft();\n if (mode === \"off\" || !interactive) {\n return current?.executablePath;\n }\n\n if (!current) {\n return offerInstall(mode, now);\n }\n\n const latest = await latestStableCft({ now });\n if (!latest || compareBuildIds(latest, current.buildId) <= 0) {\n return current.executablePath;\n }\n return offerUpdate(mode, current, latest);\n}\n\n/** No managed CfT: install silently (auto) or ask (prompt). */\nasync function offerInstall(mode: \"prompt\" | \"auto\", now: number): Promise<string | undefined> {\n const latest = await latestStableCft({ now });\n if (!latest) {\n return undefined; // offline / undetectable platform — don't nag, don't block\n }\n if (mode === \"auto\") {\n printNote(`installing Chrome for Testing ${latest} (chrome.forTesting: \"auto\")…`);\n return (await installCft(latest)).executablePath;\n }\n const state = readCftState();\n if (state.installDeclinedAt && now - state.installDeclinedAt < CHECK_TTL_MS) {\n return undefined; // declined recently — don't re-ask every launch\n }\n const answer = await choose(\n \"Chrome for Testing isn't installed. It's the recommended browser for aiui — \" +\n \"version-pinned, separate from your real Chrome, and it auto-loads the aiui \" +\n `DevTools panel (branded Chrome can't). Download ${latest} (~160 MB) to ${cftCacheDir(false)}?`,\n [\n { key: \"y\", label: \"yes, install it\" },\n { key: \"n\", label: \"not now — use the regular Chrome (asks again tomorrow)\" },\n { key: \"never\", label: 'never — stop offering (writes chrome.forTesting: \"off\")' },\n ],\n \"y\",\n );\n if (answer === \"y\") {\n return (await installCft(latest)).executablePath;\n }\n if (answer === \"never\") {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"off\" };\n });\n printNote(`wrote chrome.forTesting: \"off\" to ${file}`);\n } else {\n writeCftState({ installDeclinedAt: now });\n }\n return undefined;\n}\n\n/** Managed CfT is stale: update silently (auto) or ask (prompt). */\nasync function offerUpdate(\n mode: \"prompt\" | \"auto\",\n current: CftInstall,\n latest: string,\n): Promise<string> {\n if (mode === \"auto\") {\n printNote(\n `updating Chrome for Testing ${current.buildId} → ${latest} (chrome.forTesting: \"auto\")…`,\n );\n return (await installCft(latest)).executablePath;\n }\n if (readCftState().skippedBuildId === latest) {\n return current.executablePath;\n }\n const answer = await choose(\n `Your Chrome for Testing (${current.buildId}) is out of date — latest stable is ${latest}. Update?`,\n [\n { key: \"y\", label: \"yes, just this once\" },\n { key: \"a\", label: 'automatically, now and from here on (writes chrome.forTesting: \"auto\")' },\n {\n key: \"s\",\n label: `skip ${latest} — keep ${current.buildId}, don't ask again for this version`,\n },\n { key: \"never\", label: 'never ask again (writes chrome.forTesting: \"off\")' },\n ],\n \"y\",\n );\n switch (answer) {\n case \"y\":\n return (await installCft(latest)).executablePath;\n case \"a\": {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"auto\" };\n });\n printNote(`wrote chrome.forTesting: \"auto\" to ${file}`);\n return (await installCft(latest)).executablePath;\n }\n case \"never\": {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"off\" };\n });\n printNote(`wrote chrome.forTesting: \"off\" to ${file}`);\n return current.executablePath;\n }\n default: // \"s\"\n writeCftState({ skippedBuildId: latest });\n return current.executablePath;\n }\n}\n\nasync function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join, resolve } from \"node:path\";\n\n// Resolve modules the way this package would at runtime.\nconst nodeRequire = createRequire(import.meta.url);\n\n/**\n * Absolute root directory of an installed (or workspace-linked) dependency.\n *\n * Rather than resolve the package *through* the module system — which would hit\n * its `exports` map (forcing a built `dist/`, and normally blocking access to\n * `package.json`) — we ask Node for the `node_modules` dirs it would search and\n * read `package.json` straight off disk. This needs nothing special from the\n * target package (no `exports` entry) and works even when it has not been\n * built, so dev iteration requires no compile step.\n */\nexport function packageRoot(packageName: string): string {\n const segments = packageName.split(\"/\");\n for (const base of nodeRequire.resolve.paths(packageName) ?? []) {\n const manifest = join(base, ...segments, \"package.json\");\n if (existsSync(manifest)) {\n return dirname(manifest);\n }\n }\n throw new Error(`could not locate the \"${packageName}\" package (is it installed?)`);\n}\n\n/** How to spawn a CLI: a program plus the args that precede any subcommand. */\nexport interface CliInvocation {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve how to run a dependency package's CLI, without ever needing it built\n * in a dev checkout.\n *\n * A package is considered \"in dev\" when it still carries its `src/` directory\n * (published tarballs ship only `dist/`). In that case we run the TypeScript\n * source directly through `tsx`, so edits take effect with no build step. Once\n * installed from npm, we run the built `dist` entry instead. Either way it is\n * spawned via the current Node with an absolute path, so it relies on neither\n * the PATH nor an executable bit.\n */\nexport function resolvePackageCli(packageName: string, binName?: string): CliInvocation {\n const root = packageRoot(packageName);\n const pkg = JSON.parse(readFileSync(join(root, \"package.json\"), \"utf8\")) as {\n bin?: string | Record<string, string>;\n };\n\n const binRel =\n typeof pkg.bin === \"string\" ? pkg.bin : binName ? pkg.bin?.[binName] : firstValue(pkg.bin);\n if (!binRel) {\n throw new Error(\n `package ${packageName} declares no bin${binName ? ` named \"${binName}\"` : \"\"}`,\n );\n }\n\n if (existsSync(join(root, \"src\"))) {\n // dev: dist/cli.js -> src/cli.ts, run through tsx (no build needed).\n const srcRel = binRel.replace(/^\\.?\\/?dist\\//, \"src/\").replace(/\\.js$/, \".ts\");\n return { command: process.execPath, args: [\"--import\", \"tsx\", resolve(root, srcRel)] };\n }\n // installed: run the built entry directly.\n return { command: process.execPath, args: [resolve(root, binRel)] };\n}\n\nfunction firstValue(bin: Record<string, string> | undefined): string | undefined {\n return bin ? Object.values(bin)[0] : undefined;\n}\n","/**\n * The Chrome DevTools MCP attachment for `aiui claude`.\n *\n * By default the session gets a second MCP server alongside the channel:\n * Google's [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp),\n * run via npx, giving the agent a Chrome to drive (navigate, click, screenshot,\n * evaluate). It reaches a browser one of two ways — the mode ladder itself\n * lives in commands/claude.ts (chromeServerEntry); the full user-facing story\n * is docs/guide/chrome:\n *\n * - **attach** (default): the MCP is pointed (`--browser-url`) at a shared,\n * user-visible *session browser* — discovered or eagerly launched via\n * util/browser.ts — so human and agent work in the same window.\n * - **launch**: the MCP launches its own private browser, lazily, on the\n * agent's first tool call ({@link chromeMcpServer} builds that entry).\n *\n * The deliberate choices common to both:\n *\n * - **A persistent, project-local profile.** Chrome's user data dir defaults to\n * `.aiui-cache/chrome/default` under the launch directory — the same\n * gitignored cache the lowering traces live in — so browser state (logins,\n * devtools settings, manually installed extensions) survives across sessions\n * and stays out of both git and the user's real browser profile. Named\n * profiles (`--aiui-chrome-profile <name>`) live as siblings and are created\n * on first use; `--aiui-chrome-data-dir <path>` escapes the convention\n * entirely.\n *\n * - **Best-effort auto-load of the aiui DevTools panel.** In a dev checkout we\n * rebuild the `aiui-devtools-extension` package (~0.3s of tsc, so it is never\n * stale) and pass `--load-extension` pointing at it. Chrome-branded builds\n * ≥ 137 ignore that flag (Chromium and Chrome for Testing still honor it), so\n * this is an attempt, not a guarantee — the reliable path is loading the\n * extension unpacked once at `chrome://extensions`, which the persistent\n * profile then remembers.\n *\n * - **Config picks the browser.** `chrome.executablePath` (e.g. a Chrome for\n * Testing binary) or `chrome.channel` choose what to launch;\n * `chrome.browserUrl` says \"don't launch anything, attach here\" (the remote\n * key); flags choose per-invocation things (profile, on/off).\n */\nimport { existsSync, realpathSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { projectCacheDir } from \"@habemus-papadum/aiui-claude-channel\";\nimport { execa } from \"execa\";\nimport type { AiuiArgs } from \"./aiui-args\";\nimport type { AiuiConfig, ChromeChannel, ChromeMode } from \"./config\";\nimport { packageRoot } from \"./resolve-cli\";\nimport { printNote, printWarning } from \"./ui\";\n\n/**\n * The id of the Chrome DevTools entry under `mcpServers`. Deliberately the\n * conventional name from the chrome-devtools-mcp docs: if the user's own Claude\n * config already registers `chrome-devtools`, the two entries collide by name —\n * one definition wins — instead of two MCP servers racing to launch two Chromes\n * with duplicate toolsets.\n */\nexport const CHROME_SERVER_ID = \"chrome-devtools\";\n\nconst DEVTOOLS_PKG = \"@habemus-papadum/aiui-devtools-extension\";\n\n/** The profile used when neither flags nor config name one. */\nexport const DEFAULT_CHROME_PROFILE = \"default\";\n\n/** Profile names must stay plain directory names — no separators, no leading dot. */\nconst PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\ntype ChromeConfig = NonNullable<AiuiConfig[\"chrome\"]>;\ntype ChromeFlags = Pick<AiuiArgs, \"chrome\" | \"noChrome\" | \"chromeProfile\" | \"chromeDataDir\">;\n\n/**\n * Whether this launch should attach the Chrome DevTools MCP.\n *\n * On by default; off under CI (no display, and e2e sessions must not trigger an\n * npx download + Chrome launch), with `--aiui-no-chrome`, or with\n * `chrome.enabled: false` in config. `--aiui-chrome` forces it on even under\n * CI; `chrome.enabled: true` merely restates the default and does not.\n */\nexport function chromeDevtoolsEnabled(\n args: Pick<ChromeFlags, \"chrome\" | \"noChrome\">,\n config: ChromeConfig = {},\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (args.noChrome) {\n return false;\n }\n if (args.chrome) {\n return true;\n }\n if (config.enabled === false) {\n return false;\n }\n return !isCi(env);\n}\n\n/** Truthy `CI` env var, with the conventional \"false\"/\"0\" escape hatches. */\nexport function isCi(env: NodeJS.ProcessEnv = process.env): boolean {\n const ci = env.CI;\n return ci !== undefined && ci !== \"\" && ci !== \"0\" && ci.toLowerCase() !== \"false\";\n}\n\n/** The launch-relevant Chrome settings after flags and config are reconciled. */\nexport interface ChromeSettings {\n /** Absolute user data dir for this launch. */\n userDataDir: string;\n /**\n * \"attach\" (default): share a session browser — discover or eagerly launch\n * one and point the MCP at its debug endpoint. \"launch\": chrome-devtools-mcp\n * owns a private browser, started lazily on first tool use.\n */\n mode: ChromeMode;\n /** Explicit attach endpoint from config; forces attach, disables management. */\n browserUrl?: string;\n /** Debug port for session browsers aiui launches (0 = OS-assigned). */\n debugPort: number;\n /** Chrome binary to launch instead of an installed channel, if configured. */\n executablePath?: string;\n /** Installed Chrome release channel to launch, if configured. */\n channel?: ChromeChannel;\n headless: boolean;\n buildExtension: boolean;\n}\n\n/**\n * Reconcile CLI flags with config into the settings for this launch.\n *\n * Flags beat config. The profile/data-dir pair is reconciled as a unit: a\n * `--aiui-chrome-profile` flag also suppresses a configured `dataDir` (and\n * vice versa), because whichever identity the user named at the prompt is the\n * one they mean. Within config alone, `dataDir` beats `profile`.\n */\nexport function resolveChromeSettings(\n args: Pick<ChromeFlags, \"chromeProfile\" | \"chromeDataDir\">,\n config: ChromeConfig = {},\n base: string = process.cwd(),\n): ChromeSettings {\n if (config.executablePath && config.channel) {\n throw new Error(\n \"config sets both chrome.executablePath and chrome.channel — they pick the browser \" +\n \"two different ways; keep exactly one\",\n );\n }\n const flagged = args.chromeProfile !== undefined || args.chromeDataDir !== undefined;\n const dataDir = args.chromeDataDir ?? (flagged ? undefined : config.dataDir);\n const profile = args.chromeProfile ?? (flagged ? undefined : config.profile);\n return {\n userDataDir: chromeUserDataDir({ dataDir, profile }, base),\n // A configured endpoint means the browser is managed elsewhere (usually\n // another machine) — that's always attach, whatever `mode` says.\n mode: config.browserUrl ? \"attach\" : (config.mode ?? \"attach\"),\n browserUrl: config.browserUrl,\n debugPort: config.debugPort ?? 0,\n executablePath: config.executablePath && resolve(base, config.executablePath),\n channel: config.channel,\n headless: config.headless ?? false,\n buildExtension: config.buildExtension ?? true,\n };\n}\n\n/**\n * The Chrome user data dir to use (absolute).\n *\n * An explicit `dataDir` wins, resolved against `base`. Otherwise the named\n * profile (default: {@link DEFAULT_CHROME_PROFILE}) maps to\n * `.aiui-cache/chrome/<name>` under `base` — alternate profiles are just other\n * names, created on first use by the caller's mkdir.\n */\nexport function chromeUserDataDir(\n ids: { dataDir?: string; profile?: string },\n base: string = process.cwd(),\n): string {\n if (ids.dataDir) {\n return resolve(base, ids.dataDir);\n }\n const profile = ids.profile ?? DEFAULT_CHROME_PROFILE;\n if (!PROFILE_NAME.test(profile)) {\n throw new Error(\n `invalid chrome profile name \"${profile}\" — use letters, digits, \".\", \"_\", \"-\" ` +\n \"(or --aiui-chrome-data-dir for an arbitrary path)\",\n );\n }\n return join(projectCacheDir(base), \"chrome\", profile);\n}\n\n/**\n * The built aiui-devtools-extension directory, if available.\n *\n * The package publishes `extension/` prebuilt, so this resolves both in a dev\n * workspace (where `extension/js` appears once built — see\n * {@link buildDevtoolsExtension}) and installed from npm. Without `js/` the\n * extension is an empty shell, so an unbuilt dev checkout returns undefined.\n */\nexport function devtoolsExtensionDir(): string | undefined {\n let dir: string;\n try {\n // realpath, not the pnpm symlink — the same canonical path a manual\n // \"Load unpacked\" would register in the profile.\n dir = realpathSync(join(packageRoot(DEVTOOLS_PKG), \"extension\"));\n } catch {\n return undefined;\n }\n return existsSync(join(dir, \"js\")) ? dir : undefined;\n}\n\n/**\n * Rebuild the aiui-devtools-extension package so the auto-loaded panel is never stale.\n *\n * A full tsc of the extension (plus the debug-ui esbuild bundle) is well\n * under a second — cheap enough to run on every launch rather than tracking\n * staleness. Best-effort by design: outside a dev\n * checkout (no devtools package, no typescript) it silently does nothing, and\n * a failing compile warns loudly but never blocks the launch — whatever\n * `extension/js` already holds is what gets loaded.\n */\nexport async function buildDevtoolsExtension(): Promise<void> {\n let root: string;\n let tsc: string;\n try {\n root = realpathSync(packageRoot(DEVTOOLS_PKG));\n tsc = join(packageRoot(\"typescript\"), \"bin\", \"tsc\");\n } catch {\n return;\n }\n // Only a dev checkout carries src/ — the published package ships the built\n // extension/js (no sources, nothing to compile), so installed-from-npm\n // layouts skip straight to loading it.\n if (!existsSync(join(root, \"src\"))) {\n return;\n }\n const result = await execa(process.execPath, [tsc, \"-p\", join(root, \"tsconfig.json\")], {\n cwd: root,\n reject: false,\n all: true,\n });\n if (result.exitCode) {\n printWarning(\n \"aiui-devtools-extension failed to compile — the DevTools panel will be stale or missing\",\n result.all || result.message,\n );\n return;\n }\n // The Intent pane's shared debug-ui is bundled (esbuild) from the overlay's\n // source — tsc alone can't produce it. Same best-effort posture: the script\n // is only present in a dev checkout, and a failure degrades exactly one pane\n // (the panel imports debug-ui.js lazily), never the launch.\n const bundleScript = join(root, \"build-debug-ui.mjs\");\n if (!existsSync(bundleScript)) {\n return;\n }\n const bundle = await execa(process.execPath, [bundleScript], {\n cwd: root,\n reject: false,\n all: true,\n });\n if (bundle.exitCode) {\n printWarning(\n \"aiui-devtools-extension debug-ui bundle failed — the Intent pane will be degraded\",\n bundle.all || bundle.message,\n );\n }\n}\n\n/**\n * One-time tip when the extension exists but the chosen browser won't\n * auto-load it (no `executablePath` means a branded Chrome — installed\n * stable or a `channel` build — and branded builds ≥ 137 ignore\n * `--load-extension`). Printed once per profile: the marker file lives in the\n * user data dir, so it survives exactly as long as the profile whose manual\n * install it recommends.\n */\nexport function maybeExtensionAutoloadHint(\n settings: ChromeSettings,\n extensionDir: string | undefined,\n): void {\n if (!extensionDir || settings.executablePath) {\n return;\n }\n const marker = join(settings.userDataDir, \"aiui-devtools-extension-hint\");\n if (existsSync(marker)) {\n return;\n }\n printNote(\n \"the aiui DevTools panel can't auto-load into regular Chrome (≥ 137 ignores --load-extension)\",\n `Load it once in the launched Chrome — chrome://extensions → Developer mode → Load unpacked →\\n` +\n `${extensionDir}\\n` +\n \"— and this profile remembers it. Or switch to Chrome for Testing (`aiui chrome install`),\\n\" +\n \"which auto-loads it. This note won't repeat for this profile.\",\n );\n try {\n writeFileSync(marker, `${new Date().toISOString()}\\n`);\n } catch {\n // Best-effort: an unwritable profile dir just means the note may repeat.\n }\n}\n\n/**\n * The `mcpServers` entry that launches chrome-devtools-mcp.\n *\n * Uses the documented `npx -y chrome-devtools-mcp@latest` invocation with the\n * user data dir pinned to ours. Puppeteer's default launch args include\n * `--disable-extensions`, which would neuter both the auto-loaded panel and\n * anything installed manually into the profile — so it is always stripped.\n * When an extension dir is given, additionally ask Chrome to load it.\n */\n/**\n * The `mcpServers` entry that *attaches* chrome-devtools-mcp to an existing\n * browser's DevTools endpoint (a session browser, or a tunneled remote one).\n * In this mode the MCP manages no browser: user-data-dir, extension, and\n * headless choices all belong to whoever launched it.\n */\nexport function chromeMcpAttachServer(browserUrl: string): { command: string; args: string[] } {\n return {\n command: \"npx\",\n args: [\"-y\", \"chrome-devtools-mcp@latest\", \"--browser-url\", browserUrl],\n };\n}\n\nexport function chromeMcpServer(\n settings: ChromeSettings,\n extensionDir?: string,\n): { command: string; args: string[] } {\n const args = [\n \"-y\",\n \"chrome-devtools-mcp@latest\",\n \"--userDataDir\",\n settings.userDataDir,\n \"--ignoreDefaultChromeArg=--disable-extensions\",\n ];\n if (settings.executablePath) {\n args.push(\"--executablePath\", settings.executablePath);\n }\n if (settings.channel) {\n args.push(\"--channel\", settings.channel);\n }\n if (settings.headless) {\n args.push(\"--headless\");\n }\n if (extensionDir) {\n args.push(`--chromeArg=--load-extension=${extensionDir}`);\n }\n return { command: \"npx\", args };\n}\n","/**\n * `aiui browser` — start (or find) the session browser; `aiui open <url>` —\n * open a page in it.\n *\n * `aiui browser` is how the session browser exists *independently* of a\n * Claude session: locally when you want the window up before (or without)\n * `aiui claude`, and — the headline use — on your **local machine in remote\n * development**, where one command does the whole local half:\n *\n * aiui browser --tunnel dev-box\n *\n * launches the browser (Chrome for Testing preferred, devtools panel loaded),\n * reverse-tunnels its debug port to `dev-box` on a **fixed remote port**\n * (default 9222 — the remote port is the one worth pinning: it's what the\n * remote session and any VS Code launch config reference; the local port can\n * float), and prints the exact command to run over there. Ctrl-C closes the\n * tunnel; the browser stays.\n *\n * Profiles: without `--tunnel`, the profile is project-local as usual\n * (`.aiui-cache/chrome/<name>` under the cwd). With `--tunnel` there is\n * usually no local checkout to anchor to, so the profile lives in the\n * **user-level cache**, keyed by the tunnel host —\n * `~/.cache/aiui/browser-profiles/<host>` — so reconnecting to the same box\n * reuses the same browser state. `--profile <name>` renames that key;\n * `--data-dir <path>` escapes the convention entirely.\n *\n * Both commands identify the browser the same way `aiui claude` does: by the\n * profile's user data dir, via Chrome's own `DevToolsActivePort` file.\n */\nimport { join } from \"node:path\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\nimport { execa } from \"execa\";\nimport {\n discoverSessionBrowser,\n launchSessionBrowser,\n openInSessionBrowser,\n type SessionBrowser,\n sessionBrowserBinary,\n} from \"../util/browser\";\nimport { syncChromeForTesting } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n type ChromeSettings,\n devtoolsExtensionDir,\n isCi,\n maybeExtensionAutoloadHint,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { loadAiuiConfig } from \"../util/config\";\nimport { printError, printNote } from \"../util/ui\";\n\n/** The default *remote* port — the one worth keeping fixed (see module doc). */\nexport const DEFAULT_REMOTE_PORT = 9222;\n\nexport interface BrowserOptions {\n profile?: string;\n dataDir?: string;\n port?: string;\n headless?: boolean;\n open?: string;\n /** `[user@]host` to reverse-tunnel the debug port to (runs until Ctrl-C). */\n tunnel?: string;\n /** Fixed port on the tunnel's remote side (default {@link DEFAULT_REMOTE_PORT}). */\n remotePort?: string;\n}\n\nexport async function runBrowser(opts: BrowserOptions): Promise<void> {\n const config = loadAiuiConfig();\n const chromeCfg = { ...config.chrome };\n if (chromeCfg.browserUrl) {\n printNote(\n `config pins chrome.browserUrl to ${chromeCfg.browserUrl} — the browser is managed elsewhere`,\n \"Run `aiui browser` on the machine that should host it (and drop browserUrl there).\",\n );\n return;\n }\n\n let remotePort: number;\n try {\n remotePort = parsePort(opts.remotePort, \"--remote-port\") ?? DEFAULT_REMOTE_PORT;\n } catch (error) {\n printError(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n return;\n }\n\n // With a tunnel, the profile anchors to the *remote host* in the user cache\n // (no local checkout to be project-local to); see the module doc.\n let cfg = chromeCfg;\n const flags = {\n chromeProfile: opts.tunnel ? undefined : opts.profile,\n chromeDataDir:\n opts.dataDir ?? (opts.tunnel ? tunnelProfileDir(opts.tunnel, opts.profile) : undefined),\n };\n let settings = withPort(resolveChromeSettings(flags, cfg), opts);\n\n let session = await discoverSessionBrowser(settings.userDataDir);\n if (session) {\n report(\"session browser already running\", settings, session);\n if (opts.open) {\n await openInSessionBrowser(session.browserUrl, opts.open);\n console.log(`opened ${opts.open}`);\n }\n } else {\n const interactive = !!process.stdin.isTTY && !!process.stdout.isTTY && !isCi();\n if (!cfg.executablePath && !cfg.channel) {\n const cft = await syncChromeForTesting({ mode: cfg.forTesting ?? \"prompt\", interactive });\n if (cft) {\n cfg = { ...cfg, executablePath: cft };\n settings = withPort(resolveChromeSettings(flags, cfg), opts);\n }\n }\n if (settings.buildExtension) {\n await buildDevtoolsExtension();\n }\n const extensionDir = devtoolsExtensionDir();\n if (interactive) {\n maybeExtensionAutoloadHint(settings, extensionDir);\n }\n\n let binary: string;\n try {\n binary = sessionBrowserBinary(settings);\n } catch (error) {\n printError(\n \"no browser to launch\",\n `${error instanceof Error ? error.message : String(error)}\\n` +\n \"Install Chrome for Testing with `aiui chrome install`, or set chrome.executablePath.\",\n );\n process.exitCode = 1;\n return;\n }\n try {\n session = await launchSessionBrowser({\n binary,\n userDataDir: settings.userDataDir,\n debugPort: settings.debugPort,\n extensionDir,\n headless: settings.headless || opts.headless,\n startUrl: opts.open,\n });\n } catch (error) {\n printError(\n \"the session browser failed to start\",\n error instanceof Error ? error.message : String(error),\n );\n process.exitCode = 1;\n return;\n }\n report(\"session browser started\", settings, session);\n }\n\n if (opts.tunnel) {\n await runTunnel(opts.tunnel, remotePort, session.port);\n } else {\n printNextSteps(session, remotePort);\n }\n}\n\n/**\n * `~/.cache/aiui/browser-profiles/<key>` for a tunneled session browser.\n * Path-only (no mkdir) — the browser launch creates it on first use.\n */\nexport function tunnelProfileDir(target: string, profile?: string): string {\n const key = profile ?? sanitizeHostKey(target);\n return join(cacheDir(\"browser-profiles\", { create: false }), key);\n}\n\n/** \"user@dev.example.com\" → \"dev.example.com\", made filesystem-safe. */\nexport function sanitizeHostKey(target: string): string {\n const host = target.includes(\"@\") ? target.slice(target.indexOf(\"@\") + 1) : target;\n const key = host.replace(/[^A-Za-z0-9._-]/g, \"-\");\n return key || \"remote\";\n}\n\n/** The ssh argv for the reverse tunnel (exported for tests). */\nexport function sshTunnelArgs(target: string, remotePort: number, localPort: number): string[] {\n return [\n // No remote command — the connection exists only to carry the forward...\n \"-N\",\n // ...so if the forward can't be established (remote port taken), fail\n // loudly instead of holding a useless connection open.\n \"-o\",\n \"ExitOnForwardFailure=yes\",\n \"-R\",\n `${remotePort}:localhost:${localPort}`,\n target,\n ];\n}\n\n/** The command to run on the remote side, given the tunnel's remote port. */\nexport function remoteAttachCommand(remotePort: number): string {\n return `aiui claude --aiui-browser-url http://127.0.0.1:${remotePort}`;\n}\n\n/**\n * Hold the reverse tunnel open in the foreground (stdio inherited, so ssh's\n * own auth prompts — passwords, host keys, 2FA — work normally). The browser\n * deliberately outlives the tunnel: Ctrl-C here only drops the forward.\n */\nasync function runTunnel(target: string, remotePort: number, localPort: number): Promise<void> {\n console.log(\n `\\ntunneling ${target}:${remotePort} → localhost:${localPort} — on ${target}, run:\\n\\n` +\n ` ${remoteAttachCommand(remotePort)}\\n\\n` +\n \"(Ctrl-C closes the tunnel; the browser stays running.)\",\n );\n const result = await execa(\"ssh\", sshTunnelArgs(target, remotePort, localPort), {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.failed && !result.isTerminated) {\n printError(\n `the ssh tunnel to ${target} exited (code ${result.exitCode})`,\n \"A taken remote port exits immediately (ExitOnForwardFailure) — try another --remote-port. \" +\n \"The browser is still running; rerun `aiui browser --tunnel …` to reconnect.\",\n );\n process.exitCode = 1;\n }\n}\n\nfunction parsePort(raw: string | undefined, flag: string): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const port = Number(raw);\n if (!(Number.isInteger(port) && port >= 0 && port <= 65535)) {\n throw new Error(`invalid ${flag} ${raw} — expected 0..65535`);\n }\n return port;\n}\n\nfunction withPort(settings: ChromeSettings, opts: BrowserOptions): ChromeSettings {\n const port = parsePort(opts.port, \"--port\");\n return port === undefined ? settings : { ...settings, debugPort: port };\n}\n\nfunction report(title: string, settings: ChromeSettings, session: SessionBrowser): void {\n console.log(title);\n console.log(` profile: ${settings.userDataDir}`);\n console.log(` debug endpoint: ${session.browserUrl}`);\n}\n\n/** The no-tunnel closing hint: local attach is automatic; remote needs steps. */\nfunction printNextSteps(session: SessionBrowser, remotePort: number): void {\n console.log(\n \"\\nAn `aiui claude` in this profile's project attaches automatically. For a *remote*\\n\" +\n \"session, rerun with `--tunnel <[user@]host>` — or do it by hand:\\n\" +\n ` ssh -N -o ExitOnForwardFailure=yes -R ${remotePort}:localhost:${session.port} <host>\\n` +\n `then, on the remote: ${remoteAttachCommand(remotePort)}`,\n );\n}\n\n/** `aiui open <url>` — open a page in the running session browser. */\nexport async function runOpen(\n url: string,\n opts: Pick<BrowserOptions, \"profile\" | \"dataDir\">,\n): Promise<void> {\n const config = loadAiuiConfig();\n const settings = resolveChromeSettings(\n { chromeProfile: opts.profile, chromeDataDir: opts.dataDir },\n config.chrome ?? {},\n );\n // An explicitly configured endpoint (remote browser) is also openable.\n const browserUrl =\n config.chrome?.browserUrl ?? (await discoverSessionBrowser(settings.userDataDir))?.browserUrl;\n if (!browserUrl) {\n printError(\n \"no session browser is running for this profile\",\n `Start one with \\`aiui browser\\` (profile: ${settings.userDataDir}).`,\n );\n process.exitCode = 1;\n return;\n }\n try {\n await openInSessionBrowser(browserUrl, url);\n console.log(`opened ${url}`);\n } catch (error) {\n printError(`couldn't open ${url}`, error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n","/**\n * `aiui chrome <action>` — manage the agent's browser.\n *\n * install | update bring the managed Chrome for Testing to latest stable\n * status what would launch here, and is the devtools panel available\n * extension print the aiui-devtools-extension directory (for Load unpacked)\n *\n * `install` and `update` are the same operation (idempotent \"ensure latest\");\n * both names exist because both questions get asked. `status` is the\n * diagnostic: it reports per the *current directory's* merged config, so run\n * it from the project you're wondering about.\n */\nimport { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { discoverSessionBrowser } from \"../util/browser\";\nimport { ensureLatestCft, installedCft, latestStableCft } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n chromeDevtoolsEnabled,\n chromeUserDataDir,\n devtoolsExtensionDir,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { loadAiuiConfig } from \"../util/config\";\nimport { printError } from \"../util/ui\";\n\nexport async function runChrome(args: string[]): Promise<void> {\n const [action] = args;\n switch (action) {\n case \"install\":\n case \"update\":\n await ensureLatestCft((line) => console.log(line));\n return;\n case \"status\":\n await printStatus();\n return;\n case \"extension\": {\n await buildDevtoolsExtension();\n const dir = devtoolsExtensionDir();\n if (!dir) {\n printError(\n \"the aiui-devtools-extension is not available in this install\",\n \"In a dev checkout, build it first: pnpm --filter @habemus-papadum/aiui-devtools-extension build\",\n );\n process.exitCode = 1;\n return;\n }\n console.log(dir);\n return;\n }\n default:\n printError(\n action ? `unknown aiui chrome action: ${action}` : \"aiui chrome needs an action\",\n \"Usage: aiui chrome <install | update | status | extension>\",\n );\n process.exitCode = 1;\n return;\n }\n}\n\n/** The human-facing dump of every browser decision this directory would get. */\nasync function printStatus(): Promise<void> {\n const config = loadAiuiConfig();\n const chromeCfg = config.chrome ?? {};\n const flags = { chrome: false, noChrome: false };\n\n const cft = await installedCft();\n const latest = await latestStableCft();\n\n console.log(\"Chrome for Testing (managed):\");\n if (cft) {\n const freshness =\n latest === undefined\n ? \"(latest stable unknown — offline?)\"\n : latest === cft.buildId\n ? \"(latest stable)\"\n : `(latest stable is ${latest} — run \\`aiui chrome update\\`)`;\n console.log(` installed ${cft.buildId} ${freshness}`);\n console.log(` ${cft.executablePath}`);\n } else {\n console.log(\" not installed — `aiui chrome install` (recommended; auto-loads the panel)\");\n }\n console.log(` startup checks (chrome.forTesting): ${chromeCfg.forTesting ?? \"prompt\"}`);\n\n console.log(\"\\nThis directory would launch:\");\n if (!chromeDevtoolsEnabled(flags, chromeCfg)) {\n console.log(\" nothing — the Chrome DevTools MCP is disabled here\");\n return;\n }\n if (chromeCfg.browserUrl) {\n console.log(` connection: attach to ${chromeCfg.browserUrl} (chrome.browserUrl)`);\n console.log(\" the browser is managed elsewhere — nothing launches on this machine\");\n return;\n }\n const effective = { ...chromeCfg };\n if (!effective.executablePath && !effective.channel && cft) {\n effective.executablePath = cft.executablePath;\n }\n const settings = resolveChromeSettings({}, effective);\n const running = await discoverSessionBrowser(settings.userDataDir);\n const connection =\n settings.mode === \"attach\"\n ? running\n ? `attach to the running session browser at ${running.browserUrl}`\n : \"attach — a session browser starts with the next interactive launch (or `aiui browser`)\"\n : \"launch — chrome-devtools-mcp starts a private browser on the agent's first tool use\";\n console.log(` connection: ${connection}`);\n const browser = settings.executablePath\n ? settings.executablePath === cft?.executablePath\n ? `Chrome for Testing ${cft.buildId}`\n : settings.executablePath\n : settings.channel\n ? `installed Chrome (${settings.channel} channel)`\n : \"installed Chrome (stable)\";\n console.log(` browser: ${browser}${settings.headless ? \" — headless\" : \"\"}`);\n console.log(` user data dir: ${settings.userDataDir}`);\n const profilesDir = join(chromeUserDataDir({}, process.cwd()), \"..\");\n if (existsSync(profilesDir)) {\n const profiles = readdirSync(profilesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n if (profiles.length) {\n console.log(` profiles here: ${profiles.join(\", \")}`);\n }\n }\n\n console.log(\"\\naiui DevTools panel:\");\n const extension = devtoolsExtensionDir();\n if (!extension) {\n console.log(\" not available (unbuilt dev checkout? run `aiui chrome extension` for help)\");\n return;\n }\n console.log(` ${extension}`);\n if (settings.executablePath) {\n console.log(\" auto-loads via --load-extension (honored by Chrome for Testing/Chromium)\");\n } else {\n console.log(\" can NOT auto-load into branded Chrome ≥ 137 — load it unpacked once\");\n console.log(\n \" (chrome://extensions → Developer mode → Load unpacked), or `aiui chrome install`\",\n );\n }\n}\n","/**\n * Splitting aiui's own flags out of an otherwise pass-through arg list.\n *\n * The `aiui claude` (and future) subcommands forward nearly everything to the\n * underlying tool. The exception is aiui's own options, which by convention all\n * begin with `--aiui-` so they're unambiguously distinguishable from flags meant\n * for the wrapped command (e.g. claude's `--resume`).\n */\n\nexport interface AiuiArgs {\n /** The `--aiui-tag <tag>` value, if provided (the channel/MCP session tag). */\n tag?: string;\n /**\n * The `--aiui-mcp <tag>` value, if provided — the tag of the running channel\n * MCP server to target (e.g. so `aiui vite` connects to a specific session).\n */\n mcp?: string;\n /**\n * `--aiui-chrome` was passed: force the Chrome DevTools MCP on even where it\n * would default off (i.e. under CI).\n */\n chrome: boolean;\n /**\n * `--aiui-no-chrome` was passed: don't attach the Chrome DevTools MCP to the\n * session. (Distinct from claude's own `--no-chrome`, which disables Claude\n * Code's built-in browser integration and forwards via passthrough.)\n */\n noChrome: boolean;\n /**\n * The `--aiui-chrome-profile <name>` value, if provided — which named Chrome\n * profile (user data dir under `.aiui-cache/chrome/`) to launch with.\n */\n chromeProfile?: string;\n /**\n * The `--aiui-chrome-data-dir <path>` value, if provided — an explicit Chrome\n * user data dir, bypassing the named-profile convention entirely.\n */\n chromeDataDir?: string;\n /**\n * The `--aiui-browser-url <url>` value, if provided — attach the Chrome\n * DevTools MCP to this endpoint and manage no browser locally. This is the\n * flag `aiui browser --tunnel` prints for the remote side; it overrides\n * `chrome.browserUrl` in config for this launch.\n */\n browserUrl?: string;\n /** Everything else, to forward verbatim to the wrapped tool. */\n passthrough: string[];\n}\n\nconst AIUI_PREFIX = \"--aiui-\";\n\n/**\n * Detect a standalone `--help`/`-h` or `--version`/`-v` in a passthrough list.\n *\n * The wrapper commands treat these as **inert**: no config read, no browser or\n * Chrome-for-Testing activity, no channel discovery — just aiui's own\n * help/version, then the flag forwarded to the wrapped tool so its output\n * follows. Only exact argument matches count; a flag *value* that happens to\n * contain the text (e.g. `-p \"explain --help\"`) doesn't trigger it.\n */\nexport function infoFlag(passthrough: string[]): \"help\" | \"version\" | undefined {\n if (passthrough.includes(\"--help\") || passthrough.includes(\"-h\")) {\n return \"help\";\n }\n if (passthrough.includes(\"--version\") || passthrough.includes(\"-v\")) {\n return \"version\";\n }\n return undefined;\n}\n\n/**\n * Partition `args` into aiui's own options and the rest.\n *\n * Recognised aiui options:\n * - `--aiui-tag <tag>` / `--aiui-tag=<tag>` — the channel/MCP session tag,\n * forwarded to the channel server (and usable with `quick --tag`).\n * - `--aiui-mcp <tag>` / `--aiui-mcp=<tag>` — the tag of the running channel\n * MCP server to target (e.g. which session `aiui vite` should connect to).\n * - `--aiui-chrome` / `--aiui-no-chrome` — force the Chrome DevTools MCP on\n * (even under CI) / leave it off. Passing both is an error.\n * - `--aiui-chrome-profile <name>` — launch Chrome with the named profile\n * (created on first use under `.aiui-cache/chrome/<name>`).\n * - `--aiui-chrome-data-dir <path>` — launch Chrome with an explicit user data\n * dir instead of a named profile. Mutually exclusive with the above.\n * - `--aiui-browser-url <url>` — attach the Chrome DevTools MCP to this\n * endpoint (e.g. a tunneled remote browser) instead of managing one.\n *\n * Any other `--aiui-*` flag throws, so a typo surfaces loudly instead of being\n * silently dropped or leaking into the child command.\n */\nexport function splitAiuiArgs(args: string[]): AiuiArgs {\n let tag: string | undefined;\n let mcp: string | undefined;\n let chrome = false;\n let noChrome = false;\n let chromeProfile: string | undefined;\n let chromeDataDir: string | undefined;\n let browserUrl: string | undefined;\n const passthrough: string[] = [];\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (!arg.startsWith(AIUI_PREFIX)) {\n passthrough.push(arg);\n continue;\n }\n\n const eq = arg.indexOf(\"=\");\n const name = eq === -1 ? arg : arg.slice(0, eq);\n let value = eq === -1 ? undefined : arg.slice(eq + 1);\n\n switch (name) {\n case \"--aiui-tag\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-tag requires a non-empty value\");\n }\n tag = value;\n break;\n }\n case \"--aiui-mcp\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-mcp requires a non-empty value\");\n }\n mcp = value;\n break;\n }\n case \"--aiui-chrome\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-chrome takes no value\");\n }\n chrome = true;\n break;\n }\n case \"--aiui-no-chrome\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-no-chrome takes no value\");\n }\n noChrome = true;\n break;\n }\n case \"--aiui-chrome-profile\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-chrome-profile requires a non-empty value\");\n }\n chromeProfile = value;\n break;\n }\n case \"--aiui-chrome-data-dir\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-chrome-data-dir requires a non-empty value\");\n }\n chromeDataDir = value;\n break;\n }\n case \"--aiui-browser-url\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-browser-url requires a non-empty value\");\n }\n browserUrl = value;\n break;\n }\n default:\n throw new Error(`unknown aiui option: ${name}`);\n }\n }\n\n if (chrome && noChrome) {\n throw new Error(\"--aiui-chrome and --aiui-no-chrome are mutually exclusive\");\n }\n if (chromeProfile !== undefined && chromeDataDir !== undefined) {\n throw new Error(\"--aiui-chrome-profile and --aiui-chrome-data-dir are mutually exclusive\");\n }\n if (browserUrl !== undefined && (chromeProfile !== undefined || chromeDataDir !== undefined)) {\n throw new Error(\n \"--aiui-browser-url means the browser is managed elsewhere — it can't be combined \" +\n \"with --aiui-chrome-profile or --aiui-chrome-data-dir\",\n );\n }\n\n return { tag, mcp, chrome, noChrome, chromeProfile, chromeDataDir, browserUrl, passthrough };\n}\n","/**\n * Best-effort auto-dismiss of Claude's custom-channel acknowledgement prompt.\n *\n * When `aiui claude` opts a session into our development channel\n * (`--dangerously-load-development-channels`), Claude shows a prompt at startup\n * asking the user to confirm they're using it for local development. It's the\n * same keypress every time, so we press Enter for them.\n *\n * How: `aiui` and the `claude` it spawns share one controlling terminal. A\n * short-lived `perl` helper opens that terminal (`/dev/tty`) and pushes a\n * carriage return into its input queue via the TIOCSTI ioctl — exactly as if\n * the user had typed Enter. Claude, the foreground reader, consumes it. We\n * target the terminal, not Claude's PID, which is both simpler and more robust.\n *\n * Deliberately best-effort and silent — in every failure the user just presses\n * Enter themselves, so we never surface an error:\n * - Only macOS and Linux have known TIOCSTI request numbers; elsewhere we skip.\n * - Modern Linux (≥6.2) disables TIOCSTI by default (`dev.tty.legacy_tiocsti=0`),\n * so the ioctl returns EPERM — a no-op.\n * - No `perl` on PATH, no controlling tty, etc. → also a no-op.\n *\n * Safety if the prompt ever goes away (say Claude adds a skip flag): a stray\n * Enter at Claude's empty main input is a no-op submit, so an unneeded nudge\n * does no harm. The attempts are also kept early (before a user could plausibly\n * have dismissed the prompt and started typing a real message) so we don't race\n * their input. Whether to nudge at all is the user's saved first-run choice\n * (`claude.enterNudge` in config.json) — the caller gates on it.\n */\nimport { spawn } from \"node:child_process\";\n\n// TIOCSTI ioctl request number by platform (\"push one byte into the tty input\n// queue as if typed\"). darwin: _IOW('t',114,char) = 0x80017472; linux: 0x5412.\nconst TIOCSTI_BY_PLATFORM: Partial<Record<NodeJS.Platform, number>> = {\n darwin: 0x80017472,\n linux: 0x5412,\n};\n\n// perl one-liner (perl ships with a builtin ioctl(), avoiding a native addon):\n// open the controlling terminal and inject a carriage return. `$ARGV[0]` is the\n// platform's TIOCSTI number. Any failure — a locked-down kernel, no tty — exits\n// quietly. Passed as a single argv entry (no shell), so `\\r` needs no escaping\n// beyond the JS string literal.\nconst PERL_INJECT = 'open(my $t,\"+<\",\"/dev/tty\") or exit 0; my $c=\"\\\\r\"; ioctl($t,$ARGV[0]+0,$c);';\n\n/** Delays (ms after spawn) at which to attempt the keypress. */\nconst DEFAULT_DELAYS_MS = [250, 750];\n\n/**\n * Schedule a couple of best-effort Enter keypresses into the controlling\n * terminal to dismiss the channel prompt. Returns immediately; the attempts run\n * on unref'd timers so they never hold the CLI open, and every error is\n * swallowed. Call this only for an interactive session (see the caller).\n */\nexport function nudgeChannelAck(delaysMs: number[] = DEFAULT_DELAYS_MS): void {\n const tiocsti = TIOCSTI_BY_PLATFORM[process.platform];\n if (tiocsti === undefined) {\n return;\n }\n\n for (const ms of delaysMs) {\n const timer = setTimeout(() => {\n try {\n const child = spawn(\"perl\", [\"-e\", PERL_INJECT, String(tiocsti)], { stdio: \"ignore\" });\n child.on(\"error\", () => {}); // no perl on PATH, etc. — ignore\n } catch {\n // ignore — the user can always press Enter themselves\n }\n }, ms);\n timer.unref();\n }\n}\n","/**\n * First-run choices: settings that deserve a deliberate answer, not a silent\n * default.\n *\n * Two of `aiui claude`'s behaviors are pure personal preference with real\n * consequences: whether to launch with `--dangerously-skip-permissions`, and\n * whether to auto-dismiss the development-channel acknowledgement prompt by\n * typing into the user's terminal. Neither should be something the user\n * \"tagged along\" with because a default existed — so the first interactive\n * launch asks (definitively: the prompts have no Enter-through default), and\n * the answers persist to the **user-level** config, after which nothing asks\n * again. Non-interactive sessions never prompt; unset values fall back to the\n * documented defaults (skip: true, nudge: true).\n */\nimport { type AiuiConfig, updateUserConfig } from \"./config\";\nimport { type Choice, choose } from \"./prompt\";\nimport { printNote } from \"./ui\";\n\n/** Injectable for tests; matches {@link choose} without a default key. */\ntype Ask = (question: string, choices: Choice[]) => Promise<string>;\n\nconst SKIP_PERMISSIONS_QUESTION =\n \"One-time setup — how should aiui launch Claude Code?\\n\" +\n \"With --dangerously-skip-permissions, every agent action (shell commands, file writes,\\n\" +\n \"network, the browser) runs without asking you first. Fast, and dangerous. It's a personal\\n\" +\n \"preference — aiui works fine either way. Saved as claude.skipPermissions in your user\\n\" +\n \"config; edit or delete it there to change your mind.\";\n\nconst ENTER_NUDGE_QUESTION =\n \"One-time setup — auto-dismiss Claude Code's channel prompt?\\n\" +\n \"aiui loads a custom development channel, so Claude Code shows a one-key acknowledgement\\n\" +\n \"prompt at every startup. aiui can dismiss it for you: shortly after launch it injects a\\n\" +\n \"single Enter keystroke into this terminal (a best-effort TIOCSTI ioctl on /dev/tty — it\\n\" +\n 'literally \"types\" the Enter for you; on platforms that forbid that, nothing happens and\\n' +\n \"you press it yourself). Saying no just means pressing Enter once per launch. Saved as\\n\" +\n \"claude.enterNudge in your user config.\";\n\n/**\n * Ask (once, ever) for any first-run choice that isn't already configured, and\n * return the config with the answers applied. Call only from an interactive\n * session.\n */\nexport async function ensureLaunchChoices(\n config: AiuiConfig,\n ask: Ask = choose,\n): Promise<AiuiConfig> {\n let updated = config;\n\n if (updated.claude?.skipPermissions === undefined) {\n const answer = await ask(SKIP_PERMISSIONS_QUESTION, [\n { key: \"y\", label: \"yes — skip permissions; nothing asks before acting\" },\n { key: \"n\", label: \"no — keep Claude Code's own permission prompts\" },\n ]);\n updated = persist(updated, \"skipPermissions\", answer === \"y\");\n }\n\n if (updated.claude?.enterNudge === undefined) {\n const answer = await ask(ENTER_NUDGE_QUESTION, [\n { key: \"y\", label: \"yes — press Enter for me at startup\" },\n { key: \"n\", label: \"no — I'll press it myself each launch\" },\n ]);\n updated = persist(updated, \"enterNudge\", answer === \"y\");\n }\n\n return updated;\n}\n\nfunction persist(\n config: AiuiConfig,\n key: \"skipPermissions\" | \"enterNudge\",\n value: boolean,\n): AiuiConfig {\n const file = updateUserConfig((c) => {\n c.claude = { ...c.claude, [key]: value };\n });\n printNote(`wrote claude.${key}: ${value} to ${file}`);\n return { ...config, claude: { ...config.claude, [key]: value } };\n}\n","/**\n * OpenAI key preflight for `aiui claude`.\n *\n * Once the multimodal intent modality is the overlay's default, its speech\n * transcription and correction-diff calls need an OpenAI key — and they run in\n * the *channel* process, which inherits the environment `aiui claude` launches\n * in. The adopted key story (see the multimodal-intent-graduation handoff) is\n * deliberately narrow: the key comes from **`OPENAI_API_KEY` in the\n * environment**, never from `config.json` (a shareable, eventually-committed\n * file must not hold secrets) and never from an `aiui claude` flag.\n *\n * So the launcher's whole job is to *preflight* that env var and tell the user\n * what it found — degradation, not refusal. A missing or rejected key never\n * blocks the launch; the modality still mounts and transcription/correction\n * fall back to mock/off. The most valuable case this catches is the one that\n * bit the bench twice (see workbench field-notes, \"Keys & config\"): a **stale\n * shell export** shadowing the real key, which surfaces as a confusing 401 deep\n * in the pipeline rather than a clear message up front.\n *\n * We record only a {@link OpenAiKeyStatus} — never the key or any prefix of it —\n * so the launch-info summary (and the DevTools panel that renders it) can\n * explain a degraded pipeline without ever seeing the secret.\n */\nimport type { OpenAiKeyStatus } from \"@habemus-papadum/aiui-claude-channel\";\nimport { printNote, printWarning } from \"./ui\";\n\nexport type { OpenAiKeyStatus };\n\n/** OpenAI's cheapest authenticated endpoint — we read the status, never the body. */\nconst MODELS_URL = \"https://api.openai.com/v1/models\";\n\n/** Keep the preflight off the critical path: a slow network can't stall launch. */\nconst DEFAULT_TIMEOUT_MS = 3000;\n\nexport interface PreflightOptions {\n /**\n * Perform the authenticated network check. True only for interactive,\n * non-CI launches (the same gate as the Chrome-for-Testing prompts). When\n * false — CI or any non-interactive session — the check is skipped silently\n * and a present key is reported as \"unverified\", never contacted.\n */\n verify?: boolean;\n /** Injectable for tests; defaults to the process environment. */\n env?: NodeJS.ProcessEnv;\n /** Injectable for tests; defaults to global `fetch`. */\n fetchImpl?: typeof fetch;\n /** Network budget for the status check (default {@link DEFAULT_TIMEOUT_MS}). */\n timeoutMs?: number;\n}\n\n/**\n * Determine the status of `OPENAI_API_KEY` for this launch.\n *\n * Never throws, never prints, never returns the key. Absent env var →\n * \"missing\". Present but `verify` off → \"unverified\" (no network touched).\n * Present and verifying → a single `GET /v1/models` with the bearer, read for\n * **status only**: 2xx → \"valid\", 401/403 → \"invalid\", anything else (5xx,\n * 429, a network error, or the timeout) → \"unverified\" — a key we couldn't\n * confirm is not a key we condemn.\n */\nexport async function preflightOpenAiKey(opts: PreflightOptions = {}): Promise<OpenAiKeyStatus> {\n const {\n verify = true,\n env = process.env,\n fetchImpl = fetch,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n } = opts;\n\n const key = env.OPENAI_API_KEY?.trim();\n if (!key) {\n return \"missing\";\n }\n if (!verify) {\n return \"unverified\";\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetchImpl(MODELS_URL, {\n headers: { authorization: `Bearer ${key}` },\n signal: controller.signal,\n });\n if (res.ok) {\n return \"valid\";\n }\n if (res.status === 401 || res.status === 403) {\n return \"invalid\";\n }\n // 5xx / 429 / anything else: OpenAI didn't confirm *or* condemn the key.\n return \"unverified\";\n } catch {\n // Offline, DNS failure, TLS error, or our own abort (timeout): unverified.\n return \"unverified\";\n } finally {\n clearTimeout(timer);\n }\n}\n\ninterface PreflightMessage {\n level: \"warn\" | \"note\";\n title: string;\n detail: string;\n}\n\n/**\n * The user-facing message for a preflight status, or `null` when there's\n * nothing to say. A valid key is silent — the launcher's terminal stays quiet\n * until something's actually wrong (the same posture as {@link printNote} &c).\n * The copy is data (not printed here) so it can be unit-tested per case.\n */\nexport function openAiPreflightMessage(status: OpenAiKeyStatus): PreflightMessage | null {\n switch (status) {\n case \"valid\":\n return null;\n case \"missing\":\n return {\n level: \"warn\",\n title: \"OPENAI_API_KEY is not set — the intent pipeline will run degraded\",\n detail:\n \"Speech transcription and dictation correction fall back to mock/off mode. To enable \" +\n \"them, export the key in the shell you run `aiui claude` from:\\n\" +\n \" export OPENAI_API_KEY=sk-…\\n\" +\n \"It flows through to the channel process, which is where those calls happen.\",\n };\n case \"invalid\":\n return {\n level: \"warn\",\n title:\n \"OPENAI_API_KEY was rejected by OpenAI (401) — the intent pipeline will run degraded\",\n detail:\n \"The key in your environment isn't valid. The usual cause is a stale shell export \" +\n \"shadowing your real key — check what's actually set (this prints only a short prefix, \" +\n \"not the whole secret):\\n\" +\n \" echo $OPENAI_API_KEY | head -c 12\\n\" +\n \"and compare that against the start of your real key. Until it's fixed, transcription \" +\n \"and correction run in mock/off mode.\",\n };\n case \"unverified\":\n return {\n level: \"note\",\n title: \"couldn't verify OPENAI_API_KEY with OpenAI — continuing\",\n detail:\n \"The check didn't complete (offline, a timeout, or a transient OpenAI error), so the \" +\n \"key is unverified — not known-bad. Launch continues; if the intent pipeline degrades \" +\n \"to mock/off, an unreachable or invalid key may be why.\",\n };\n }\n}\n\n/** Print the preflight message for `status` (nothing, for a valid key). */\nexport function reportOpenAiPreflight(status: OpenAiKeyStatus): void {\n const message = openAiPreflightMessage(status);\n if (!message) {\n return;\n }\n if (message.level === \"warn\") {\n printWarning(message.title, message.detail);\n } else {\n printNote(message.title, message.detail);\n }\n}\n","// Injected at build time by Vite's `define` (see vite.config.ts). The `typeof`\n// guard is a no-op in the built CLI (where the define replaces it with a string\n// literal) but keeps this working anywhere the define isn't applied.\ndeclare const __AIUI_VERSION__: string;\n\n/** This aiui build's version. */\nexport const VERSION = typeof __AIUI_VERSION__ === \"string\" ? __AIUI_VERSION__ : \"0.0.0+dev\";\n","import { accessSync, constants } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\n\n/**\n * Return true if `command` resolves to an executable on the PATH.\n *\n * A dependency-free `which`: it scans each PATH entry for an executable file\n * (honouring `PATHEXT` on Windows). This is the first of what will be several\n * environment checks the launcher runs before shelling out to `claude`.\n */\nexport function commandExists(command: string): boolean {\n const dirs = (process.env.PATH ?? \"\").split(delimiter).filter(Boolean);\n const exts =\n process.platform === \"win32\" ? (process.env.PATHEXT ?? \".EXE;.CMD;.BAT;.COM\").split(\";\") : [\"\"];\n for (const dir of dirs) {\n for (const ext of exts) {\n try {\n accessSync(join(dir, command + ext), constants.X_OK);\n return true;\n } catch {\n // Not executable here — keep scanning.\n }\n }\n }\n return false;\n}\n","import { mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { ChromeDevtoolsInfo, LaunchInfo } from \"@habemus-papadum/aiui-claude-channel\";\nimport { execa } from \"execa\";\nimport { type AiuiArgs, infoFlag, splitAiuiArgs } from \"../util/aiui-args\";\nimport {\n discoverSessionBrowser,\n launchSessionBrowser,\n sessionBrowserBinary,\n} from \"../util/browser\";\nimport { syncChromeForTesting } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n CHROME_SERVER_ID,\n chromeDevtoolsEnabled,\n chromeMcpAttachServer,\n chromeMcpServer,\n devtoolsExtensionDir,\n isCi,\n maybeExtensionAutoloadHint,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { type AiuiConfig, loadAiuiConfig } from \"../util/config\";\nimport { nudgeChannelAck } from \"../util/enter-nudge\";\nimport { ensureLaunchChoices } from \"../util/first-run\";\nimport { preflightOpenAiKey, reportOpenAiPreflight } from \"../util/openai-preflight\";\nimport { packageRoot, resolvePackageCli } from \"../util/resolve-cli\";\nimport { printError, printWarning } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\nimport { commandExists } from \"../util/which\";\n\nconst CHANNEL_PKG = \"@habemus-papadum/aiui-claude-channel\";\nconst PLUGIN_PKG = \"@habemus-papadum/aiui-claude-plugin\";\n\n// The inline MCP server id for our custom channel. It is reused twice: as the\n// key under `mcpServers` in `--mcp-config`, and as the `server:<id>` entry that\n// opts the session into loading the development channel.\nconst CHANNEL_SERVER_ID = \"aiui\";\n\n/**\n * Launch Claude Code wired up with the aiui channel, plugin, and (by default)\n * the Chrome DevTools MCP.\n *\n * Builds a `claude` command line and hands the terminal over to it. The plugin\n * directories and the channel CLI are resolved from their dependencies to\n * absolute paths — no PATH lookups. In a dev checkout the channel runs straight\n * from its TypeScript source via tsx (no build step), and when installed from\n * npm it runs the built `dist` entry; see {@link resolvePackageCli}. Only\n * `claude` itself is checked on the PATH, since everything here launches it.\n *\n * Args are split into aiui's own options (those beginning with `--aiui-`) and\n * the rest, which forward verbatim to `claude`. So `aiui claude --resume` passes\n * `--resume` through, while `aiui claude --aiui-tag <uuid>` is consumed here to\n * tag the channel session (letting a test harness address the exact MCP server\n * it spawned via `quick --tag`). When no tag is given the channel server mints\n * its own UUID.\n *\n * `--help` and `--version` are inert: aiui's own answer prints first, then the\n * flag forwards to claude so its output follows — and none of the launch\n * machinery (config, Chrome for Testing, session browser, channel) runs.\n */\nexport async function runClaude(rawArgs: string[] = []): Promise<void> {\n const aiuiArgs = splitAiuiArgs(rawArgs);\n const { tag, passthrough } = aiuiArgs;\n\n // `--help` / `--version` are inert: print aiui's own answer, then forward\n // the flag so claude's follows — two outputs back to back, and none of the\n // launch machinery (config, browser, Chrome for Testing, channel) runs.\n const info = infoFlag(passthrough);\n if (info) {\n if (info === \"help\") {\n printClaudeWrapperHelp();\n } else {\n console.log(`aiui ${VERSION}`);\n }\n await forwardToClaude(passthrough);\n return;\n }\n // Settings from ~/.cache/aiui/config.json + .aiui-cache/config.json (project\n // wins per key; flags win over both) — see util/config and docs/guide/config.\n let config = loadAiuiConfig();\n\n // A real TTY on both ends, not print mode, not CI: the only context where\n // aiui may prompt (first-run choices, CfT offers) or type into the terminal.\n const interactive = isInteractiveSession(passthrough) && !isCi();\n if (interactive) {\n // Settings that deserve a deliberate answer — skip-permissions and the\n // enter nudge — are asked once, definitively, and persisted to the user\n // config; every later launch reads the choice silently.\n config = await ensureLaunchChoices(config);\n }\n\n // Preflight the OpenAI key the intent pipeline needs (transcription +\n // correction, which run in the spawned channel process — the key reaches\n // them through this environment). Interactive launches verify it against the\n // API and report any degradation once; CI/non-interactive only note presence\n // without touching the network. Either way the launch proceeds — a bad or\n // missing key means the modality degrades to mock/off, never a refusal. We\n // keep only the status (never the key) to thread into launch-info below.\n const openaiKey = await preflightOpenAiKey({ verify: interactive });\n if (interactive) {\n reportOpenAiPreflight(openaiKey);\n }\n\n if (!ensureClaudeOnPath()) {\n return;\n }\n\n // Plugins ship in the plugin package's marketplace/ (in both dev and\n // installed layouts). They're loaded directly with repeated `--plugin-dir`\n // flags — the marketplace manifest exists for marketplace installs later,\n // not as a required indirection here.\n const pluginsRoot = resolve(packageRoot(PLUGIN_PKG), \"marketplace\", \"plugins\");\n\n // Resolve how to run the channel CLI (tsx-from-source in dev, dist when\n // installed) and append its `mcp` subcommand. A user-supplied `--aiui-tag`\n // is forwarded as the server's `--tag`; without one the server generates its\n // own UUID.\n const channel = resolvePackageCli(CHANNEL_PKG);\n const mcpArgs = [...channel.args, \"mcp\"];\n if (tag) {\n mcpArgs.push(\"--tag\", tag);\n }\n const mcpServers: Record<string, { command: string; args: string[] }> = {\n [CHANNEL_SERVER_ID]: { command: channel.command, args: mcpArgs },\n };\n\n // By default the session also gets the Chrome DevTools MCP — off under CI,\n // `--aiui-no-chrome`, or `chrome.enabled: false`. In the default \"attach\"\n // mode the MCP shares a user-visible session browser (see util/browser);\n // \"launch\" mode keeps the browser private to the MCP and lazily started.\n let chromeInfo: ChromeDevtoolsInfo = { enabled: false };\n if (chromeDevtoolsEnabled(aiuiArgs, config.chrome)) {\n // `--aiui-browser-url` (printed by `aiui browser --tunnel` for the remote\n // side) beats a configured chrome.browserUrl for this launch.\n const chromeCfg = {\n ...config.chrome,\n ...(aiuiArgs.browserUrl ? { browserUrl: aiuiArgs.browserUrl } : {}),\n };\n const chrome = await chromeServerEntry(aiuiArgs, chromeCfg, interactive);\n mcpServers[CHROME_SERVER_ID] = chrome.entry;\n chromeInfo = chrome.info;\n }\n // Tell the channel server how this session was assembled. It surfaces this\n // at /debug/api/info, and the DevTools panel's Server tab renders it — the\n // first place to look when browser/MCP connectivity misbehaves.\n const launchInfo: LaunchInfo = {\n launcher: \"aiui claude\",\n chromeDevtools: chromeInfo,\n openaiKey,\n };\n mcpArgs.push(\"--launch-info\", JSON.stringify(launchInfo));\n const mcpConfig = JSON.stringify({ mcpServers });\n\n // The base aiui plugin and the frontend-design principles always load. The\n // session-browser skill is an add-on for the Chrome DevTools MCP — etiquette\n // for driving the *shared* browser — so the session is lightened by leaving\n // it out whenever that MCP isn't attached.\n const plugins = [join(pluginsRoot, \"aiui\"), join(pluginsRoot, \"frontend-design\")];\n if (chromeInfo.enabled) {\n plugins.push(join(pluginsRoot, \"session-browser\"));\n }\n\n // We don't add `--chrome` or `--no-chrome`: whether to use Claude's own\n // browser integration is the user's call, forwarded via passthrough (e.g.\n // `aiui claude --chrome`) — it is independent of the Chrome DevTools MCP\n // above. Automated/CI contexts pass `--no-chrome` themselves (see the e2e\n // test harness) to skip the browser-detection startup prompt.\n // Skipping permissions is still the (documented, dangerous) default, but no\n // longer hard-coded: `claude.skipPermissions: false` in config.json opts out\n // and leaves Claude's own permission behavior in charge.\n const skipPermissions = config.claude?.skipPermissions ?? true;\n const args = [\n ...(skipPermissions ? [\"--dangerously-skip-permissions\"] : []),\n \"--mcp-config\",\n mcpConfig,\n ...plugins.flatMap((dir) => [\"--plugin-dir\", dir]),\n // Custom channels are a research preview and not on the approved allowlist,\n // so opt this session into loading ours as a development channel.\n \"--dangerously-load-development-channels\",\n `server:${CHANNEL_SERVER_ID}`,\n ];\n\n // Loading our development channel makes Claude show a one-key acknowledgement\n // prompt at startup. Whether aiui best-effort presses Enter on the user's\n // behalf is their saved first-run choice (claude.enterNudge; see\n // nudgeChannelAck for the mechanism). Never outside an interactive TTY — the\n // prompt only appears in the interactive TUI, and the e2e harness drives its\n // own keypresses over tmux.\n if (interactive && (config.claude?.enterNudge ?? true)) {\n nudgeChannelAck();\n }\n\n // Hand the terminal over to Claude. stdio:\"inherit\" so the session owns the\n // terminal (and, when spawned by the test harness, so Claude's stdio is the\n // harness's captured pipes). reject:false so an interrupted/non-zero Claude\n // exit becomes our exit code rather than a thrown error.\n const result = await execa(\"claude\", [...args, ...passthrough], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** Check for `claude`; print the friendly install pointer when missing. */\nfunction ensureClaudeOnPath(): boolean {\n if (commandExists(\"claude\")) {\n return true;\n }\n printError(\n \"`claude` was not found on your PATH\",\n \"Install Claude Code and make sure the `claude` command is available, then try again.\",\n );\n process.exitCode = 1;\n return false;\n}\n\n/** Run claude with the args verbatim (the --help/--version forward). */\nasync function forwardToClaude(args: string[]): Promise<void> {\n if (!ensureClaudeOnPath()) {\n return;\n }\n const result = await execa(\"claude\", args, { stdio: \"inherit\", reject: false });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** The aiui half of `aiui claude --help` (claude's own --help follows it). */\nfunction printClaudeWrapperHelp(): void {\n console.log(`aiui claude — launch Claude Code wired with the aiui channel, plugin, and browser MCP\n\naiui's own flags (everything else forwards to claude verbatim):\n --aiui-tag <tag> tag the channel session (e.g. for \\`quick --tag\\`)\n --aiui-chrome force the Chrome DevTools MCP on (even under CI)\n --aiui-no-chrome launch without the Chrome DevTools MCP\n --aiui-chrome-profile <name> browser profile at .aiui-cache/chrome/<name>\n --aiui-chrome-data-dir <path> explicit browser user data dir\n --aiui-browser-url <url> attach to a browser at this DevTools endpoint\n (e.g. a tunnel from \\`aiui browser --tunnel\\`)\n\nDurable settings live in config.json (project .aiui-cache/ + user cache) — see the\nConfiguration guide. What follows is claude's own --help:\n`);\n}\n\n/**\n * Assemble the chrome-devtools MCP entry for this launch.\n *\n * The decision ladder:\n * 1. `chrome.browserUrl` configured → attach verbatim. The browser lives\n * elsewhere (typically another machine, tunneled — see docs/guide/remote);\n * nothing local is managed: no CfT sync, no profile, no extension.\n * 2. Attach mode with a session browser already running on this profile →\n * attach to it (works non-interactively too; discovery is read-only).\n * 3. Attach mode, interactive → start the session browser now (CfT-preferred,\n * devtools extension loaded, visible from t0) and attach. On failure, warn\n * and fall through.\n * 4. Otherwise — `mode: \"launch\"`, a non-interactive session with nothing\n * running, or a failed start — classic launch mode: chrome-devtools-mcp\n * starts its own private browser lazily, on the agent's first tool call.\n */\nasync function chromeServerEntry(\n aiuiArgs: AiuiArgs,\n chromeCfg: NonNullable<AiuiConfig[\"chrome\"]>,\n interactive: boolean,\n): Promise<{ entry: { command: string; args: string[] }; info: ChromeDevtoolsInfo }> {\n if (chromeCfg.browserUrl) {\n return {\n entry: chromeMcpAttachServer(chromeCfg.browserUrl),\n info: { enabled: true, connection: \"attach\", browserUrl: chromeCfg.browserUrl },\n };\n }\n\n let cfg = { ...chromeCfg };\n let settings = resolveChromeSettings(aiuiArgs, cfg);\n\n if (settings.mode === \"attach\") {\n const running = await discoverSessionBrowser(settings.userDataDir);\n if (running) {\n return {\n entry: chromeMcpAttachServer(running.browserUrl),\n info: {\n enabled: true,\n connection: \"attach\",\n browserUrl: running.browserUrl,\n userDataDir: settings.userDataDir,\n },\n };\n }\n }\n\n // From here a browser will be launched one way or the other — pick the\n // binary. Chrome for Testing is the recommended default: unless config\n // names a browser explicitly, prefer the managed install (and offer to\n // install/update it interactively — see syncChromeForTesting).\n if (!cfg.executablePath && !cfg.channel) {\n const cft = await syncChromeForTesting({ mode: cfg.forTesting ?? \"prompt\", interactive });\n if (cft) {\n cfg = { ...cfg, executablePath: cft };\n settings = resolveChromeSettings(aiuiArgs, cfg);\n }\n }\n mkdirSync(settings.userDataDir, { recursive: true });\n if (settings.buildExtension) {\n await buildDevtoolsExtension();\n }\n const extensionDir = devtoolsExtensionDir();\n if (interactive) {\n maybeExtensionAutoloadHint(settings, extensionDir);\n }\n const browserInfo = {\n userDataDir: settings.userDataDir,\n executablePath: settings.executablePath,\n channel: settings.channel,\n headless: settings.headless,\n extensionDir,\n };\n\n if (settings.mode === \"attach\" && interactive) {\n try {\n const session = await launchSessionBrowser({\n binary: sessionBrowserBinary(settings),\n userDataDir: settings.userDataDir,\n debugPort: settings.debugPort,\n extensionDir,\n headless: settings.headless,\n });\n return {\n entry: chromeMcpAttachServer(session.browserUrl),\n info: {\n enabled: true,\n connection: \"attach\",\n browserUrl: session.browserUrl,\n ...browserInfo,\n },\n };\n } catch (error) {\n printWarning(\n \"couldn't start the session browser — falling back to a browser private to the MCP\",\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n return {\n entry: chromeMcpServer(settings, extensionDir),\n info: { enabled: true, connection: \"launch\", ...browserInfo },\n };\n}\n\n/**\n * Whether this invocation will bring up Claude's interactive TUI — the only\n * context where the channel acknowledgement prompt appears. Requires a real\n * terminal on both ends and no print-mode flag.\n */\nexport function isInteractiveSession(passthrough: string[]): boolean {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return !passthrough.some((arg) => arg === \"-p\" || arg === \"--print\");\n}\n","/**\n * `aiui demo [dir]` — scaffold a disposable, runnable demo playground.\n *\n * The repo's `pnpm demo` serves `packages/aiui-demo` straight out of the\n * checkout — fine for developers, but every agent edit lands in the working\n * tree and wants to ride along upstream. This command is the outside-world\n * version: it copies a small sample app (Vite + the `aiuiDevOverlay()`\n * integration, real source) into a directory of the user's own, makes it a\n * standalone git repo, installs its dependencies, and prints how to run the\n * loop. Agent chaos stays in the sandbox, like a much-mutated notebook —\n * except versioned and nowhere near this repo.\n *\n * Designed for `npx @habemus-papadum/aiui demo my-demo` and for **re-running**:\n * the scaffold marks its package.json (`\"aiui\": { \"demo\": true }`), and a\n * marked directory is never re-scaffolded — a second run just tops up\n * `node_modules` if needed and reprints the next steps, so a demo in progress\n * (including everything the agent changed) continues exactly where it was.\n * The scaffold also lists `@habemus-papadum/aiui` as its own devDependency, so\n * after the one `npm install`, `npx aiui …` inside the directory resolves\n * locally — no repeated downloads.\n */\nimport {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execa } from \"execa\";\nimport { printError, printNote } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\nimport { commandExists } from \"../util/which\";\n\nexport interface DemoOptions {\n /** Scaffold only — skip `npm install` (used by the packaging test/CI). */\n skipInstall?: boolean;\n}\n\n/** What `aiui demo` finds at the target path. */\nexport type DemoTargetState = \"new\" | \"existing-demo\" | \"occupied\";\n\nexport async function runDemo(dir: string | undefined, opts: DemoOptions = {}): Promise<void> {\n const target = resolve(process.cwd(), dir ?? \"aiui-demo\");\n\n switch (classifyDemoTarget(target)) {\n case \"occupied\":\n printError(\n `${target} already exists and isn't an aiui demo`,\n \"Pick an empty (or new) directory — the scaffold never overwrites existing content.\",\n );\n process.exitCode = 1;\n return;\n case \"existing-demo\":\n printNote(`existing demo found at ${target} — continuing where it left off`);\n break;\n case \"new\": {\n const template = templateRoot();\n if (!template) {\n printError(\"the demo template did not ship with this aiui install\");\n process.exitCode = 1;\n return;\n }\n scaffoldDemo(template, target);\n console.log(`scaffolded the demo playground at ${target}`);\n await initGitRepo(target);\n break;\n }\n }\n\n if (!opts.skipInstall && !existsSync(join(target, \"node_modules\"))) {\n if (!commandExists(\"npm\")) {\n printNote(\"npm not found on PATH — run your package manager's install in the demo yourself\");\n } else {\n console.log(\"installing dependencies (one time)…\");\n const result = await execa(\"npm\", [\"install\", \"--no-audit\", \"--no-fund\"], {\n cwd: target,\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n printError(\"npm install failed — fix that, then re-run this command to continue\");\n process.exitCode = result.exitCode;\n return;\n }\n }\n }\n\n printNextSteps(target);\n}\n\n/**\n * Decide what to do with the target: `new` (missing or empty), an\n * `existing-demo` (package.json carries the scaffold marker — continue), or\n * `occupied` (anything else — refuse; never clobber unknown content).\n */\nexport function classifyDemoTarget(target: string): DemoTargetState {\n if (!existsSync(target)) {\n return \"new\";\n }\n let entries: string[];\n try {\n entries = readdirSync(target);\n } catch {\n return \"occupied\"; // a file, or unreadable — either way, not ours\n }\n if (entries.length === 0) {\n return \"new\";\n }\n try {\n const pkg = JSON.parse(readFileSync(join(target, \"package.json\"), \"utf8\")) as {\n aiui?: { demo?: boolean };\n };\n if (pkg.aiui?.demo === true) {\n return \"existing-demo\";\n }\n } catch {}\n return \"occupied\";\n}\n\n/**\n * The dependency range the scaffold pins aiui packages to: this build's exact\n * release line when it has one, `latest` from a dev build (whose `0.0.0+dev`\n * doesn't exist on the registry).\n */\nexport function demoDependencyRange(version: string): string {\n return /^\\d+\\.\\d+\\.\\d+$/.test(version) ? `^${version}` : \"latest\";\n}\n\n/** Copy the template, restore `.gitignore`, and pin dependency ranges. */\nexport function scaffoldDemo(template: string, target: string): void {\n mkdirSync(target, { recursive: true });\n cpSync(template, target, { recursive: true });\n // npm strips `.gitignore` files from tarballs, so the template ships it\n // undotted and the scaffold puts the dot back.\n if (existsSync(join(target, \"gitignore\"))) {\n renameSync(join(target, \"gitignore\"), join(target, \".gitignore\"));\n }\n const pkgFile = join(target, \"package.json\");\n writeFileSync(\n pkgFile,\n readFileSync(pkgFile, \"utf8\").replaceAll(\n \"__AIUI_VERSION_RANGE__\",\n demoDependencyRange(VERSION),\n ),\n );\n}\n\n/**\n * Make the sandbox its own git repo (best-effort): agent edits become\n * inspectable local history and can't wander into any surrounding project.\n * Skipped when the target already sits inside a work tree.\n */\nasync function initGitRepo(target: string): Promise<void> {\n if (!commandExists(\"git\")) {\n return;\n }\n const inside = await execa(\"git\", [\"-C\", target, \"rev-parse\", \"--is-inside-work-tree\"], {\n reject: false,\n });\n if (inside.exitCode === 0) {\n return;\n }\n const init = await execa(\"git\", [\"-C\", target, \"init\", \"--quiet\"], { reject: false });\n if (init.exitCode !== 0) {\n return;\n }\n await execa(\"git\", [\"-C\", target, \"add\", \"-A\"], { reject: false });\n // May fail without a user.name/email configured; the repo alone is enough.\n await execa(\"git\", [\"-C\", target, \"commit\", \"--quiet\", \"-m\", \"aiui demo scaffold\"], {\n reject: false,\n });\n}\n\n/**\n * The template directory shipped with this install. Probed upward from this\n * module because the relative depth differs between layouts: `dist/cli.js`\n * (bundled, installed) sits one level below the package root; the tsx-run\n * `src/commands/demo.ts` sits two below.\n */\nexport function templateRoot(): string | undefined {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 4; i++) {\n const candidate = join(dir, \"templates\", \"demo\");\n if (existsSync(join(candidate, \"package.json\"))) {\n return candidate;\n }\n dir = dirname(dir);\n }\n return undefined;\n}\n\nfunction printNextSteps(target: string): void {\n const rel = relative(process.cwd(), target) || \".\";\n const rerun = rel === \"aiui-demo\" ? \"aiui demo\" : `aiui demo ${rel}`;\n console.log(`\ndemo ready. Run the loop:\n\n cd ${rel}\n npm run claude # terminal 1 — Claude Code with the aiui channel + session browser\n npm run dev # terminal 2 — the demo app (Vite + the intent tool)\n\nthen open the app in the session browser (the window you share with the agent):\n\n npx aiui open http://localhost:5173\n\nClick the ✳ aiui button on the page and type an intent — it lands in the session\nas a prompt. Re-run \\`${rerun}\\` anytime to continue this sandbox.`);\n}\n","import { execa } from \"execa\";\nimport { type CliInvocation, resolvePackageCli } from \"../util/resolve-cli\";\nimport { printError } from \"../util/ui\";\n\nconst CHANNEL_PKG = \"@habemus-papadum/aiui-claude-channel\";\n\n/**\n * Forward to the aiui Claude channel CLI (`aiui-claude-channel`).\n *\n * `aiui mcp <args...>` runs the channel package's own CLI with `<args...>`, so\n * `aiui mcp quick --tag <t> --message \"...\"` is exactly\n * `aiui-claude-channel quick --tag <t> --message \"...\"`. This surfaces the\n * user-facing channel commands under `aiui` without moving them out of the\n * package that owns the MCP server Claude Code spawns.\n *\n * Like `aiui vite`, the channel CLI is a declared dependency, so we resolve it\n * from node_modules (tsx-from-source in a dev checkout, the built `dist` when\n * installed; see {@link resolvePackageCli}) rather than looking on the PATH.\n */\nexport async function runMcp(passthrough: string[] = []): Promise<void> {\n let channel: CliInvocation;\n try {\n channel = resolvePackageCli(CHANNEL_PKG);\n } catch {\n printError(\n \"The aiui Claude channel CLI is not available\",\n \"`@habemus-papadum/aiui-claude-channel` should be installed as a dependency of aiui — try reinstalling.\",\n );\n process.exitCode = 1;\n return;\n }\n\n // stdio inherit so interactive channel commands (e.g. `quick`'s selector) own\n // the terminal; reject:false so a non-zero child exit becomes our exit code.\n const result = await execa(channel.command, [...channel.args, ...passthrough], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n","import type { RunningServer } from \"@habemus-papadum/aiui-claude-channel\";\nimport { listMcpServers, selectMcpServer } from \"@habemus-papadum/aiui-claude-channel\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { infoFlag, splitAiuiArgs } from \"../util/aiui-args\";\nimport { type CliInvocation, resolvePackageCli } from \"../util/resolve-cli\";\nimport { printError } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\n\nconst VITE_PKG = \"vite\";\n\n// The environment variable that tells the Vite dev server which channel\n// server's web backend to talk to. Read in the dev-server process by the\n// aiuiDevOverlay() plugin (@habemus-papadum/aiui-dev-overlay/vite), which\n// mounts the intent tool and hands it the port; app source can also read it\n// via Vite's `import.meta.env`. It can NOT be read from inside the prebuilt\n// overlay bundle — see the overlay package's src/vite.ts for that subtlety.\nconst VITE_PORT_ENV = \"VITE_AIUI_PORT\";\n\n/** The channel server `runVite` should point Vite at, or why it couldn't. */\nexport interface ChannelTarget {\n /** The server resolved without prompting (by tag). */\n server?: RunningServer;\n /** Servers to offer in the interactive selector (no tag given, ≥1 running). */\n select?: RunningServer[];\n /** A human-readable reason a requested server couldn't be resolved. */\n error?: string;\n}\n\n/**\n * Decide which running channel server Vite should connect to.\n *\n * Pure so it can be unit-tested without spawning anything:\n * - With a `targetTag`, return the server whose `tag` matches exactly; if none\n * matches, return an `error` naming the tag and the tags that *are* running.\n * - Without a `targetTag`, don't guess: return `{}` when nothing is running, or\n * `{ select }` so the caller runs the same selector as `quick` (which\n * auto-picks a lone server and prompts when there are several).\n */\nexport function resolveChannelTarget(\n servers: RunningServer[],\n targetTag: string | undefined,\n): ChannelTarget {\n if (targetTag !== undefined) {\n const server = servers.find((s) => s.tag === targetTag);\n if (!server) {\n const running = servers.length > 0 ? servers.map((s) => s.tag).join(\", \") : \"(none running)\";\n return {\n error: `no running aiui channel with tag \"${targetTag}\" — running tags: ${running}`,\n };\n }\n return { server };\n }\n return servers.length > 0 ? { select: servers } : {};\n}\n\n/**\n * Launch Vite, forwarding any extra args (e.g. `aiui vite dev`,\n * `aiui vite --port 3000`, `aiui vite --version`).\n *\n * Before launching, resolve which running aiui channel server the dev server\n * should talk to — either the one named by `--aiui-mcp <tag>` (or `--aiui-tag`),\n * or, with no tag, the one you pick from the same selector `quick` uses (which\n * auto-selects when only one is running) — and inject its port as\n * {@link VITE_PORT_ENV} so the app can reach it. When a specific tag was asked\n * for but isn't running, we fail loudly instead of connecting to the wrong one.\n *\n * Unlike `claude` — an external tool we look up on the PATH — Vite is a declared\n * dependency of this package, so we resolve it straight out of node_modules and\n * run it via the current Node with an absolute path. Resolving it also doubles\n * as the \"is Vite available?\" check: if it isn't installed, we fail loudly\n * rather than shelling out to nothing. See {@link resolvePackageCli}.\n */\nexport async function runVite(rawArgs: string[] = []): Promise<void> {\n const { mcp, tag, passthrough } = splitAiuiArgs(rawArgs);\n\n // `--help` / `--version` are inert: aiui's own answer, then Vite's — with no\n // channel discovery (which could otherwise block on an interactive picker).\n const info = infoFlag(passthrough);\n if (info) {\n if (info === \"help\") {\n printViteWrapperHelp();\n } else {\n console.log(`aiui ${VERSION}`);\n }\n await forwardToVite(passthrough);\n return;\n }\n\n // `--aiui-mcp` is the purpose-built selector; `--aiui-tag` is accepted too.\n const targetTag = mcp ?? tag;\n\n const target = resolveChannelTarget(listMcpServers(), targetTag);\n if (target.error) {\n printError(\"Could not resolve an aiui channel\", target.error);\n process.exitCode = 1;\n return;\n }\n\n // A tag resolves directly; otherwise the selector (shared with `quick`) picks\n // — returning the lone server without prompting, or asking when there's more\n // than one.\n const server = target.select ? await selectMcpServer(target.select) : target.server;\n\n let port: string | undefined;\n if (server) {\n port = String(server.port);\n console.error(\n chalk.dim(\n `aiui: connecting vite to channel \"${server.tag}\" (${server.cwd}) on port ${port} via ${VITE_PORT_ENV}`,\n ),\n );\n } else {\n console.error(chalk.dim(`aiui: no running channel found — ${VITE_PORT_ENV} left unset`));\n }\n\n const vite = resolveVite();\n if (!vite) {\n return;\n }\n\n // stdio inherit so the dev server owns the terminal and Ctrl-C reaches it.\n // reject:false so a non-zero/interrupted Vite exit is propagated as our exit\n // code instead of throwing an error the user didn't cause. execa merges `env`\n // over process.env, so we only add VITE_AIUI_PORT when we actually resolved one.\n const result = await execa(vite.command, [...vite.args, ...passthrough], {\n stdio: \"inherit\",\n reject: false,\n ...(port ? { env: { [VITE_PORT_ENV]: port } } : {}),\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** Resolve the Vite CLI; print the friendly install pointer when missing. */\nfunction resolveVite(): CliInvocation | undefined {\n try {\n return resolvePackageCli(VITE_PKG);\n } catch {\n printError(\n \"Vite is not available\",\n \"`vite` should be installed as a dependency of aiui — try reinstalling.\",\n );\n process.exitCode = 1;\n return undefined;\n }\n}\n\n/** Run Vite with the args verbatim (the --help/--version forward). */\nasync function forwardToVite(args: string[]): Promise<void> {\n const vite = resolveVite();\n if (!vite) {\n return;\n }\n const result = await execa(vite.command, [...vite.args, ...args], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** The aiui half of `aiui vite --help` (vite's own --help follows it). */\nfunction printViteWrapperHelp(): void {\n console.log(`aiui vite — launch Vite connected to the running aiui channel\n\naiui's own flags (everything else forwards to vite verbatim):\n --aiui-mcp <tag> connect to the channel server with this tag\n --aiui-tag <tag> accepted alias for --aiui-mcp\n\nThe chosen channel's port is exported as VITE_AIUI_PORT; the aiuiDevOverlay()\nVite plugin picks it up there and wires the intent tool to it. What follows is\nvite's own --help:\n`);\n}\n","import { Command } from \"commander\";\nimport { type BrowserOptions, runBrowser, runOpen } from \"./commands/browser\";\nimport { runChrome } from \"./commands/chrome\";\nimport { runClaude } from \"./commands/claude\";\nimport { type DemoOptions, runDemo } from \"./commands/demo\";\nimport { runMcp } from \"./commands/mcp\";\nimport { runVite } from \"./commands/vite\";\n\nimport { VERSION } from \"./util/version\";\n\n/**\n * Build the `aiui` command tree.\n *\n * Kept separate from the executable entrypoint (cli.ts) so tests can construct\n * and inspect the program without actually running it.\n */\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"aiui\")\n .description(\"ai ui frontends — thin launchers for Claude, Vite, and the channel CLI\")\n .version(VERSION)\n // Only treat options as aiui's own when they come *before* the subcommand.\n // Without this, commander parses interspersed options and would swallow e.g.\n // `aiui vite --version` as aiui's own --version instead of forwarding it.\n .enablePositionalOptions();\n\n // Both subcommands are thin wrappers: everything after the subcommand name is\n // forwarded verbatim to the underlying tool. allowUnknownOption + helpOption(false)\n // stop commander from intercepting flags like `--resume` or `--help`, and the\n // variadic `[args...]` collects them for the action to pass through.\n program\n .command(\"claude\")\n .description(\"launch Claude (extra args are forwarded, e.g. `aiui claude --resume`)\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to claude\")\n .action((args: string[]) => runClaude(args));\n\n program\n .command(\"vite\")\n .description(\"launch Vite (extra args are forwarded, e.g. `aiui vite dev`)\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to vite\")\n .action((args: string[]) => runVite(args));\n\n // Unlike its siblings, `aiui chrome` is a real subcommand (not a forwarding\n // wrapper): it manages the agent's browser — the Chrome for Testing install,\n // launch status, and the devtools extension path.\n program\n .command(\"chrome\")\n .description(\"manage the agent's browser: install | update | status | extension\")\n .argument(\"<action>\", \"install | update | status | extension\")\n .action((action: string) => runChrome([action]));\n\n // The shared session browser (human + agent in one window). `browser` starts\n // or finds it — locally before/without a session, or on your local machine\n // for remote development (tunnel its debug port to the remote box).\n program\n .command(\"browser\")\n .description(\n \"start (or find) the shared session browser; --tunnel does the whole remote-dev local half\",\n )\n .option(\n \"--profile <name>\",\n \"named profile (project .aiui-cache/chrome/, or the user cache with --tunnel)\",\n )\n .option(\"--data-dir <path>\", \"explicit Chrome user data dir\")\n .option(\"--port <port>\", \"fixed local DevTools debug port (default: OS-assigned)\")\n .option(\"--headless\", \"launch with no UI\")\n .option(\"--open <url>\", \"also open this URL in it\")\n .option(\n \"--tunnel <[user@]host>\",\n \"reverse-tunnel the debug port to this host (Ctrl-C closes it)\",\n )\n .option(\"--remote-port <port>\", \"fixed port on the tunnel's remote side (default: 9222)\")\n .action((opts: BrowserOptions) => runBrowser(opts));\n\n // A disposable, npx-able playground: scaffolds a sample app into the user's\n // own directory (its own git repo — agent edits stay in the sandbox) and is\n // safe to re-run: an existing demo continues instead of being re-scaffolded.\n program\n .command(\"demo\")\n .description(\"scaffold a runnable demo playground (safe to re-run; default dir: aiui-demo)\")\n .argument(\"[dir]\", \"target directory (default: aiui-demo)\")\n .option(\"--skip-install\", \"scaffold only — don't run npm install\")\n .action((dir: string | undefined, opts: DemoOptions) => runDemo(dir, opts));\n\n program\n .command(\"open\")\n .description(\"open a URL in the session browser, e.g. `aiui open http://localhost:5173`\")\n .argument(\"<url>\", \"the URL to open\")\n .option(\"--profile <name>\", \"named profile under .aiui-cache/chrome/\")\n .option(\"--data-dir <path>\", \"explicit Chrome user data dir\")\n .action((url: string, opts: Pick<BrowserOptions, \"profile\" | \"dataDir\">) => runOpen(url, opts));\n\n // `aiui mcp <args...>` forwards to the aiui-claude-channel CLI, so the\n // user-facing channel commands live under `aiui` (e.g. `aiui mcp quick`)\n // without duplicating them or moving them off the package that owns the\n // in-process MCP server.\n program\n .command(\"mcp\")\n .description(\"run a channel command (forwards to aiui-claude-channel), e.g. `aiui mcp quick`\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to the aiui-claude-channel CLI\")\n .action((args: string[]) => runMcp(args));\n\n return program;\n}\n","import { buildProgram } from \"./program\";\n\n// Executable entrypoint for the `aiui` bin. The `#!/usr/bin/env node` shebang is\n// prepended to the built dist/cli.js by a rollup banner (see vite.config.ts), so\n// this source stays valid TypeScript. This file is only ever executed, never\n// imported — the testable logic lives in program.ts.\nbuildProgram()\n .parseAsync()\n .catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n });\n"],"names":["ACTIVE_PORT_FILE","LAUNCH_TIMEOUT_MS","discoverSessionBrowser","userDataDir","port","readActivePort","debugEndpointAlive","first","readFileSync","join","sessionBrowserBinary","settings","computeSystemExecutablePath","Browser","RELEASE_CHANNELS","ChromeReleaseChannel","launchSessionBrowser","opts","mkdirSync","rmSync","args","child","execa","exited","deadline","found","writeFileSync","sleep","openInSessionBrowser","browserUrl","url","base","res","ms","resolve","CONFIG_FILENAME","CHROME_CHANNELS","FOR_TESTING_MODES","CHROME_MODES","configPaths","cacheDir","projectCacheDir","loadAiuiConfig","paths","mergeAiuiConfig","readConfigFile","override","file","text","raw","error","validateConfig","root","asSection","rejectUnknownKeys","config","claude","prune","asOptional","chrome","channel","forTesting","mode","isHttpUrl","debugPort","value","updateUserConfig","mutate","dirname","section","v","where","known","key","type","choose","question","choices","defaultKey","rl","createInterface","menu","c","chalk","answer","hit","printError","title","detail","printWarning","printNote","CHECK_TTL_MS","RESOLVE_TIMEOUT_MS","cftCacheDir","create","STATE_FILE","readCftState","writeCftState","patch","dir","compareBuildIds","a","b","as","bs","i","diff","installedCft","existsSync","best","getInstalledBrowsers","latestStableCft","maxAgeMs","timeoutMs","now","state","platform","detectBrowserPlatform","buildId","withTimeout","resolveBuildId","installCft","fresh","install","others","old","uninstall","ensureLatestCft","report","latest","current","syncChromeForTesting","interactive","offerInstall","offerUpdate","promise","timer","_","reject","nodeRequire","createRequire","packageRoot","packageName","segments","manifest","resolvePackageCli","binName","pkg","binRel","firstValue","srcRel","bin","CHROME_SERVER_ID","DEVTOOLS_PKG","DEFAULT_CHROME_PROFILE","PROFILE_NAME","chromeDevtoolsEnabled","env","isCi","ci","resolveChromeSettings","flagged","dataDir","profile","chromeUserDataDir","ids","devtoolsExtensionDir","realpathSync","buildDevtoolsExtension","tsc","result","bundleScript","bundle","maybeExtensionAutoloadHint","extensionDir","marker","chromeMcpAttachServer","chromeMcpServer","DEFAULT_REMOTE_PORT","runBrowser","chromeCfg","remotePort","parsePort","cfg","flags","tunnelProfileDir","withPort","session","cft","binary","runTunnel","printNextSteps","target","sanitizeHostKey","sshTunnelArgs","localPort","remoteAttachCommand","flag","runOpen","_a","_b","runChrome","action","line","printStatus","freshness","effective","running","connection","browser","profilesDir","profiles","readdirSync","e","extension","AIUI_PREFIX","infoFlag","passthrough","splitAiuiArgs","tag","mcp","noChrome","chromeProfile","chromeDataDir","arg","eq","name","TIOCSTI_BY_PLATFORM","PERL_INJECT","DEFAULT_DELAYS_MS","nudgeChannelAck","delaysMs","tiocsti","spawn","SKIP_PERMISSIONS_QUESTION","ENTER_NUDGE_QUESTION","ensureLaunchChoices","ask","updated","persist","MODELS_URL","DEFAULT_TIMEOUT_MS","preflightOpenAiKey","verify","fetchImpl","controller","openAiPreflightMessage","status","reportOpenAiPreflight","message","VERSION","commandExists","command","dirs","delimiter","exts","ext","accessSync","constants","CHANNEL_PKG","PLUGIN_PKG","CHANNEL_SERVER_ID","runClaude","rawArgs","aiuiArgs","info","printClaudeWrapperHelp","forwardToClaude","isInteractiveSession","openaiKey","ensureClaudeOnPath","pluginsRoot","mcpArgs","mcpServers","chromeInfo","chromeServerEntry","launchInfo","mcpConfig","plugins","browserInfo","runDemo","classifyDemoTarget","template","templateRoot","scaffoldDemo","initGitRepo","entries","demoDependencyRange","version","cpSync","renameSync","pkgFile","fileURLToPath","candidate","rel","relative","rerun","runMcp","VITE_PKG","VITE_PORT_ENV","resolveChannelTarget","servers","targetTag","server","s","runVite","printViteWrapperHelp","forwardToVite","listMcpServers","selectMcpServer","vite","resolveVite","buildProgram","program","Command"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/util/config-schema.ts","../src/util/config.ts","../src/util/prompt.ts","../src/util/ui.ts","../src/util/cft.ts","../src/util/resolve-cli.ts","../src/util/chrome.ts","../src/commands/browser.ts","../src/commands/chrome.ts","../src/util/aiui-args.ts","../src/util/enter-nudge.ts","../src/util/first-run.ts","../src/util/openai-preflight.ts","../src/util/version.ts","../src/util/which.ts","../src/commands/claude.ts","../src/commands/clean.ts","../src/commands/config.ts","../src/commands/config-tui.ts","../src/commands/vite.ts","../src/commands/debug.ts","../src/commands/demo.ts","../src/commands/mcp.ts","../src/commands/paint.ts","../src/program.ts","../src/cli.ts"],"sourcesContent":["/**\n * The declarative schema for aiui's `config.json` — one table that drives\n * everything config-shaped:\n *\n * - validation (util/config.ts walks these sections instead of hand-rolled\n * per-key checks),\n * - the `aiui config` commands (show/get/set/unset use it to resolve keys,\n * parse CLI values, and report defaults),\n * - the `aiui config tui` browser (docs, defaults, and enum choices all\n * render from here).\n *\n * Because docs, defaults, and validation come from the same rows, they cannot\n * drift apart. `docs/guide/config.md` remains the long-form narrative; the\n * `doc` strings here are the terminal-sized versions.\n *\n * **How a component participates.** `CONFIG_SECTIONS` is the registry: a\n * section is plain data, so a future component that grows file-backed settings\n * (say, an `intent` section for the pipeline) joins by contributing one\n * `ConfigSectionSchema` object here and a matching optional section on\n * `AiuiConfig`. Nothing else — validation, show/get/set, and the TUI pick it\n * up from the table. (The intent pipeline's current config is deliberately\n * *not* here: it is per-app, passed as `aiuiDevOverlay({ intent: { … } })` in\n * the app's Vite config — see docs/guide/intent-overlay.md.)\n */\n\n// The channel list lives with the launch code (aiui-util's browser module,\n// whose RELEASE_CHANNELS map must stay exhaustive over it); re-exported here\n// so the config table keeps being the one import surface for config shapes.\nimport { CHROME_CHANNELS } from \"@habemus-papadum/aiui-util\";\n\nexport { CHROME_CHANNELS, type ChromeChannel } from \"@habemus-papadum/aiui-util\";\n\nexport const FOR_TESTING_MODES = [\"prompt\", \"auto\", \"off\"] as const;\nexport type ForTestingMode = (typeof FOR_TESTING_MODES)[number];\n\nexport const CHROME_MODES = [\"attach\", \"launch\"] as const;\nexport type ChromeMode = (typeof CHROME_MODES)[number];\n\nexport const CHANNEL_BINDS = [\"loopback\", \"host\"] as const;\nexport type ChannelBind = (typeof CHANNEL_BINDS)[number];\n\n/** Every config leaf is a JSON scalar; sections never nest further. */\nexport type ConfigValue = boolean | number | string;\n\nexport interface ConfigFieldSchema {\n /** Leaf key within its section, e.g. `\"skipPermissions\"`. */\n key: string;\n /** `\"enum\"` is a string constrained to {@link values}. */\n type: \"boolean\" | \"number\" | \"string\" | \"enum\";\n /** The allowed values when {@link type} is `\"enum\"`. */\n values?: readonly string[];\n /** The built-in default, when the fallback is a plain value. */\n default?: ConfigValue;\n /**\n * Human phrasing of the default when a bare value would mislead (dynamic\n * fallbacks, first-run prompts). Shown instead of {@link default}.\n */\n defaultText?: string;\n /** One-line summary — the TUI's list row. */\n summary: string;\n /** The rest of the documentation (terminal-sized; optional). */\n doc?: string;\n /**\n * Constraint beyond type/enum. Returns the \"expected …\" tail of the error\n * message, or undefined when the value is fine.\n */\n validate?: (value: ConfigValue) => string | undefined;\n}\n\nexport interface ConfigSectionSchema {\n /** Top-level key in config.json, e.g. `\"chrome\"`. */\n name: string;\n /** One-line summary of what the section configures. */\n summary: string;\n fields: ConfigFieldSchema[];\n}\n\nexport const CONFIG_SECTIONS: ConfigSectionSchema[] = [\n {\n name: \"claude\",\n summary: \"how `aiui claude` launches Claude Code\",\n fields: [\n {\n key: \"skipPermissions\",\n type: \"boolean\",\n default: true,\n defaultText: \"true (unset: the first interactive launch asks, then persists the answer)\",\n summary: \"Launch Claude Code with --dangerously-skip-permissions.\",\n doc:\n \"A personal preference with real consequences (docs/guide/warning): every agent \" +\n \"action — shell commands, file writes, network, the browser — runs without asking \" +\n \"first. aiui works fine either way. The first interactive launch asks and persists \" +\n \"the answer at the user level; when unset, non-interactive sessions fall back to true.\",\n },\n {\n key: \"enterNudge\",\n type: \"boolean\",\n default: true,\n defaultText: \"true (unset: the first interactive launch asks, then persists the answer)\",\n summary: \"Auto-dismiss Claude Code's development-channel acknowledgement prompt.\",\n doc:\n \"aiui loads a custom development channel, so Claude Code shows a one-key \" +\n \"acknowledgement at every startup; this injects a single Enter keystroke into the \" +\n \"terminal to dismiss it (best-effort TIOCSTI on /dev/tty — platforms that forbid it \" +\n \"harmlessly do nothing). Saying no just means pressing Enter yourself each launch.\",\n },\n ],\n },\n {\n name: \"channel\",\n summary: \"the channel server's web backend\",\n fields: [\n {\n key: \"bind\",\n type: \"enum\",\n values: CHANNEL_BINDS,\n default: \"loopback\",\n defaultText:\n '\"loopback\" (unset: the first interactive launch asks, then persists the answer)',\n summary: \"Which interface the channel web server binds: loopback, or host (LAN).\",\n doc:\n '\"host\" (0.0.0.0) makes the session\\'s whole web surface — the iPad paint page, but ' +\n \"also prompt injection, /debug, and every sidecar — reachable by anyone on your \" +\n \"network, UNAUTHENTICATED. That is the trusted-LAN posture (docs/guide/warning): \" +\n 'right on a network that is yours alone, wrong on shared Wi-Fi. \"loopback\" keeps ' +\n \"everything this-machine-only; reaching the paint page from an iPad is then up to \" +\n \"you — tunnel the channel port however you like (Tailscale, `ssh -L`). The first \" +\n \"interactive launch asks and persists the answer at the user level. Per-launch \" +\n \"flag: --aiui-bind.\",\n },\n ],\n },\n {\n name: \"sidecars\",\n summary: \"which session sidecars `aiui claude` asks the channel to host\",\n fields: [\n {\n key: \"paint\",\n type: \"boolean\",\n default: true,\n summary: \"Host the iPad paint sidecar (on the channel's own port).\",\n doc:\n \"The iPad paint stream (docs/guide/paint-stream) rides the channel's one port — no \" +\n \"extra process, no extra listener — so it is on by default; false turns it off. \" +\n \"Whether an iPad can actually reach it is channel.bind's call (host, or a tunnel \" +\n \"you own). Per-launch flags win: --aiui-sidecar paint / --aiui-no-sidecar paint. \" +\n \"`aiui paint url` prints the URL to open on the iPad.\",\n },\n ],\n },\n {\n name: \"chrome\",\n summary: \"the agent's browser and the Chrome DevTools MCP\",\n fields: [\n {\n key: \"enabled\",\n type: \"boolean\",\n default: true,\n summary: \"Attach the Chrome DevTools MCP.\",\n doc:\n \"false turns it off everywhere; true restates the default and does NOT override the \" +\n \"CI default-off — only the --aiui-chrome flag forces it on under CI.\",\n },\n {\n key: \"mode\",\n type: \"enum\",\n values: CHROME_MODES,\n default: \"attach\",\n summary: \"How the MCP reaches a browser: shared session browser, or its own.\",\n doc:\n '\"attach\" shares a user-visible session browser: an already-running one is ' +\n 'discovered by profile, or an interactive launch starts one eagerly. \"launch\" is ' +\n \"the hands-off mode: chrome-devtools-mcp launches its own private browser lazily, on \" +\n \"the agent's first browser tool call.\",\n },\n {\n key: \"browserUrl\",\n type: \"string\",\n defaultText: \"unset (manage a browser locally)\",\n summary: \"Attach to this DevTools endpoint instead of managing a browser at all.\",\n doc:\n \"The remote-development key (docs/guide/remote): the browser runs on another machine \" +\n \"(started there with `aiui browser`) and its debug port is tunneled over. Setting it \" +\n 'implies mode: \"attach\" and makes every local-browser setting (profile, ' +\n \"executablePath, channel, forTesting…) irrelevant. Per-launch flag: --aiui-browser-url.\",\n validate: (value) =>\n isHttpUrl(String(value))\n ? undefined\n : 'expected an http(s) URL like \"http://127.0.0.1:9222\"',\n },\n {\n key: \"debugPort\",\n type: \"number\",\n default: 0,\n defaultText: \"0 (an OS-assigned free port)\",\n summary: \"Fixed DevTools debug port for session browsers aiui launches.\",\n doc:\n \"Pin it (e.g. 9222) when something external must find the port — an ssh tunnel, a \" +\n \"VS Code attach-to-Chrome launch config. 0 means an OS-assigned free port.\",\n validate: (value) =>\n Number.isInteger(value) && (value as number) >= 0 && (value as number) <= 65535\n ? undefined\n : \"expected 0..65535\",\n },\n {\n key: \"profile\",\n type: \"string\",\n default: \"default\",\n summary: \"Named profile under .aiui-cache/chrome/.\",\n doc: \"Per-launch flag: --aiui-chrome-profile.\",\n },\n {\n key: \"dataDir\",\n type: \"string\",\n defaultText: \"unset (derived from chrome.profile)\",\n summary: \"Explicit Chrome user data dir; takes precedence over chrome.profile.\",\n doc: \"Per-launch flag: --aiui-chrome-data-dir.\",\n },\n {\n key: \"executablePath\",\n type: \"string\",\n defaultText: \"unset (managed Chrome for Testing when installed, else installed Chrome)\",\n summary: \"Chrome binary to launch — e.g. a Chrome for Testing install.\",\n doc:\n \"Chrome for Testing still honors --load-extension, so the aiui DevTools panel can \" +\n \"auto-load. Mutually exclusive with chrome.channel.\",\n },\n {\n key: \"channel\",\n type: \"enum\",\n values: CHROME_CHANNELS,\n defaultText: 'unset (\"stable\" when launching an installed Chrome)',\n summary: \"Installed Chrome release channel to launch.\",\n doc: \"Mutually exclusive with chrome.executablePath.\",\n },\n {\n key: \"forTesting\",\n type: \"enum\",\n values: FOR_TESTING_MODES,\n default: \"prompt\",\n summary: \"How `aiui claude` manages the recommended Chrome for Testing install.\",\n doc:\n '\"prompt\" asks before installing or updating it — interactive sessions only, never ' +\n 'under CI; \"auto\" installs/updates without asking; \"off\" never checks. Prompt ' +\n 'answers (\"automatically\", \"never ask again\") persist here at the user level. ' +\n \"Skipped entirely when executablePath or channel picks a browser explicitly.\",\n },\n {\n key: \"headless\",\n type: \"boolean\",\n default: false,\n summary: \"Launch Chrome with no UI.\",\n },\n {\n key: \"buildExtension\",\n type: \"boolean\",\n default: true,\n summary: \"Rebuild the aiui-devtools-extension whenever a browser starts in a dev checkout.\",\n doc:\n \"~0.3s of tsc so the auto-loaded DevTools panel is never stale. Only relevant in a \" +\n \"dev checkout of pdum_aiui.\",\n },\n ],\n },\n];\n\n/** A field paired with its section — what key-resolution hands back. */\nexport interface ResolvedField {\n section: ConfigSectionSchema;\n field: ConfigFieldSchema;\n /** The dotted path, e.g. `\"chrome.mode\"`. */\n path: string;\n}\n\n/** Every field in schema order, with dotted paths. */\nexport function allConfigFields(): ResolvedField[] {\n return CONFIG_SECTIONS.flatMap((section) =>\n section.fields.map((field) => ({ section, field, path: `${section.name}.${field.key}` })),\n );\n}\n\n/** Resolve a dotted path like `\"chrome.mode\"`; undefined when unknown. */\nexport function findConfigField(path: string): ResolvedField | undefined {\n return allConfigFields().find((entry) => entry.path === path);\n}\n\n/** The `typeof` a field's values (enums are strings). */\nexport function fieldRuntimeType(field: ConfigFieldSchema): \"boolean\" | \"number\" | \"string\" {\n return field.type === \"enum\" ? \"string\" : field.type;\n}\n\n/**\n * Constraint check beyond the runtime type: enum membership, then the field's\n * own `validate`. Returns the \"expected …\" tail for the error, or undefined.\n */\nexport function invalidReason(field: ConfigFieldSchema, value: ConfigValue): string | undefined {\n if (field.type === \"enum\" && !(field.values ?? []).includes(String(value))) {\n return `expected one of: ${(field.values ?? []).join(\", \")}`;\n }\n return field.validate?.(value);\n}\n\n/**\n * Parse a CLI-provided string into the field's type and validate it — how\n * `aiui config set` and the TUI turn text into a config value.\n */\nexport function parseFieldValue(\n field: ConfigFieldSchema,\n raw: string,\n): { value: ConfigValue } | { error: string } {\n let value: ConfigValue;\n switch (fieldRuntimeType(field)) {\n case \"boolean\": {\n if (raw !== \"true\" && raw !== \"false\") {\n return { error: \"expected true or false\" };\n }\n value = raw === \"true\";\n break;\n }\n case \"number\": {\n value = Number(raw);\n if (raw.trim() === \"\" || Number.isNaN(value)) {\n return { error: \"expected a number\" };\n }\n break;\n }\n default:\n value = raw;\n }\n const reason = invalidReason(field, value);\n return reason ? { error: reason } : { value };\n}\n\n/** Render a value the way config.json would hold it (strings quoted). */\nexport function formatConfigValue(value: ConfigValue): string {\n return typeof value === \"string\" ? JSON.stringify(value) : String(value);\n}\n\n/** The default, phrased for humans: `defaultText` wins, then the value, then \"unset\". */\nexport function describeDefault(field: ConfigFieldSchema): string {\n if (field.defaultText) {\n return field.defaultText;\n }\n return field.default === undefined ? \"unset\" : formatConfigValue(field.default);\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n","/**\n * aiui's two-level configuration file.\n *\n * Settings live in `config.json` at up to two places, merged per-key with the\n * more specific one winning:\n *\n * <user cache>/config.json e.g. ~/.cache/aiui/config.json (respects\n * AIUI_CACHE / XDG_CACHE_HOME)\n * <project>/.aiui-cache/config.json next to the traces and Chrome profiles\n *\n * CLI flags override both. Everything is optional; a missing file is an empty\n * config. A *malformed* file is a hard error, not a warning — these settings\n * gate security-relevant behavior (`claude.skipPermissions`), and a typo that\n * silently reverts to the dangerous default is worse than a failed launch. The\n * same reasoning rejects unknown keys.\n *\n * What the keys are — types, enums, defaults, and documentation — lives in one\n * declarative table, `config-schema.ts`. Validation here walks that schema, and\n * the `aiui config` commands (show/get/set/tui) render from it, so the checks\n * and the docs cannot drift apart.\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { projectCacheDir } from \"@habemus-papadum/aiui-claude-channel\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\nimport {\n type ChannelBind,\n type ChromeChannel,\n type ChromeMode,\n CONFIG_SECTIONS,\n type ConfigValue,\n type ForTestingMode,\n fieldRuntimeType,\n formatConfigValue,\n invalidReason,\n} from \"./config-schema\";\n\nexport {\n CHANNEL_BINDS,\n CHROME_CHANNELS,\n CHROME_MODES,\n type ChannelBind,\n type ChromeChannel,\n type ChromeMode,\n FOR_TESTING_MODES,\n type ForTestingMode,\n} from \"./config-schema\";\n\nexport const CONFIG_FILENAME = \"config.json\";\n\n/**\n * The typed shape of a config file. Must mirror `CONFIG_SECTIONS` in\n * config-schema.ts — the schema rows carry the full per-key documentation and\n * defaults; the comments here are just orientation.\n */\nexport interface AiuiConfig {\n claude?: {\n /** Launch Claude Code with `--dangerously-skip-permissions`. */\n skipPermissions?: boolean;\n /** Auto-dismiss Claude Code's development-channel prompt (util/enter-nudge.ts). */\n enterNudge?: boolean;\n };\n channel?: {\n /** Which interface the channel web server binds: loopback (default) or host (LAN). */\n bind?: ChannelBind;\n };\n sidecars?: {\n /** Host the iPad paint sidecar on the channel's port (default: true). */\n paint?: boolean;\n };\n chrome?: {\n /** Attach the Chrome DevTools MCP (default: true). */\n enabled?: boolean;\n /** How the MCP reaches a browser: shared session browser, or its own. */\n mode?: ChromeMode;\n /** Attach to this DevTools endpoint instead of managing a browser at all. */\n browserUrl?: string;\n /** Fixed DevTools debug port for session browsers aiui launches (0 = OS-assigned). */\n debugPort?: number;\n /** Named profile under `.aiui-cache/chrome/` (default: \"default\"). */\n profile?: string;\n /** Explicit Chrome user data dir; takes precedence over `profile`. */\n dataDir?: string;\n /** Chrome binary to launch. Mutually exclusive with `channel`. */\n executablePath?: string;\n /** Installed Chrome release channel to launch. */\n channel?: ChromeChannel;\n /** How `aiui claude` manages the Chrome for Testing install. */\n forTesting?: ForTestingMode;\n /** Launch Chrome headless (default: false). */\n headless?: boolean;\n /** Rebuild the aiui-devtools-extension on every launch in a dev checkout. */\n buildExtension?: boolean;\n };\n}\n\n/** The untyped view validation and merging work in: section → leaf values. */\ntype SectionValues = Record<string, ConfigValue>;\n\n/** The `config.json` paths consulted, user-level first (base: the project dir). */\nexport function configPaths(base: string = process.cwd()): { user: string; project: string } {\n return {\n user: join(cacheDir(undefined, { create: false }), CONFIG_FILENAME),\n project: join(projectCacheDir(base), CONFIG_FILENAME),\n };\n}\n\n/** Load and merge the user- and project-level configs (project wins per key). */\nexport function loadAiuiConfig(base: string = process.cwd()): AiuiConfig {\n const paths = configPaths(base);\n return mergeAiuiConfig(readConfigFile(paths.user) ?? {}, readConfigFile(paths.project) ?? {});\n}\n\n/** Merge two configs section-by-section; `override`'s keys win within a section. */\nexport function mergeAiuiConfig(base: AiuiConfig, override: AiuiConfig): AiuiConfig {\n const merged: Record<string, SectionValues> = {};\n for (const section of CONFIG_SECTIONS) {\n merged[section.name] = {\n ...(base as Record<string, SectionValues | undefined>)[section.name],\n ...(override as Record<string, SectionValues | undefined>)[section.name],\n };\n }\n return merged as AiuiConfig;\n}\n\n/**\n * Read one config file. Missing → undefined; unreadable JSON or an unexpected\n * shape → an error naming the file, so the fix is obvious.\n */\nexport function readConfigFile(file: string): AiuiConfig | undefined {\n let text: string;\n try {\n text = readFileSync(file, \"utf8\");\n } catch {\n return undefined;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error) {\n throw new Error(\n `invalid JSON in ${file}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return validateConfig(raw, file);\n}\n\n/** Walk `CONFIG_SECTIONS`, rejecting unknown keys, wrong types, and bad values. */\nfunction validateConfig(raw: unknown, file: string): AiuiConfig {\n const root = asSection(raw, file, \"the top level\");\n rejectUnknownKeys(\n root,\n CONFIG_SECTIONS.map((s) => s.name),\n file,\n \"the top level\",\n );\n\n const config: Record<string, SectionValues> = {};\n for (const section of CONFIG_SECTIONS) {\n if (root[section.name] === undefined) {\n continue;\n }\n const values = asSection(root[section.name], file, `\"${section.name}\"`);\n rejectUnknownKeys(\n values,\n section.fields.map((f) => f.key),\n file,\n `\"${section.name}\"`,\n );\n // Absent keys stay absent (never `undefined`), or merging would let them\n // clobber a value from the other config level.\n const out: SectionValues = {};\n for (const field of section.fields) {\n const value = values[field.key];\n if (value === undefined) {\n continue;\n }\n const path = `${section.name}.${field.key}`;\n const type = fieldRuntimeType(field);\n if (typeof value !== type) {\n throw new Error(`expected a ${type} for ${path} in ${file}`);\n }\n const reason = invalidReason(field, value as ConfigValue);\n if (reason) {\n throw new Error(\n `invalid ${path} ${formatConfigValue(value as ConfigValue)} in ${file} — ${reason}`,\n );\n }\n out[field.key] = value as ConfigValue;\n }\n config[section.name] = out;\n }\n return config as AiuiConfig;\n}\n\n/**\n * Persist a change to the **user-level** config (creating the file if needed).\n *\n * This is how interactive prompt answers like \"never ask again\" become\n * durable: they are per-user decisions, so they land in the user cache — never\n * in the project file, which may be shared/committed by a team.\n */\nexport function updateUserConfig(mutate: (config: AiuiConfig) => void): string {\n return updateConfigFile(configPaths().user, mutate);\n}\n\n/**\n * Persist a change to a specific config file — the general form behind\n * {@link updateUserConfig}, and how `aiui config set --project` writes the\n * project level deliberately.\n */\nexport function updateConfigFile(file: string, mutate: (config: AiuiConfig) => void): string {\n const config = readConfigFile(file) ?? {};\n mutate(config);\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, `${JSON.stringify(config, null, 2)}\\n`);\n return file;\n}\n\nfunction asSection(value: unknown, file: string, where: string): Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`expected an object at ${where} of ${file}`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction rejectUnknownKeys(\n section: Record<string, unknown>,\n known: string[],\n file: string,\n where: string,\n): void {\n for (const key of Object.keys(section)) {\n if (!known.includes(key)) {\n throw new Error(\n `unknown key \"${key}\" at ${where} of ${file} — known keys: ${known.join(\", \")}`,\n );\n }\n }\n}\n","/**\n * A minimal interactive chooser for the launcher's rare questions (CfT\n * install/update offers, first-run choices). Menus print to stderr so a piped\n * stdout stays clean; answers match a choice's key or any unambiguous label\n * prefix. With a `defaultKey`, Enter takes the default; without one the\n * question requires an explicit answer (for choices that should be definitive,\n * not waved through). Callers are responsible for only asking in a real\n * interactive session (TTY, not CI).\n */\nimport { createInterface } from \"node:readline/promises\";\nimport chalk from \"chalk\";\n\nexport interface Choice {\n key: string;\n label: string;\n}\n\nexport async function choose(\n question: string,\n choices: Choice[],\n defaultKey?: string,\n): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const menu = choices\n .map(\n (c) =>\n ` ${chalk.bold(`[${c.key === defaultKey ? c.key.toUpperCase() : c.key}]`)} ${c.label}`,\n )\n .join(\"\\n\");\n for (;;) {\n const raw = await rl.question(`${chalk.cyan(question)}\\n${menu}\\n> `);\n const answer = raw.trim().toLowerCase();\n if (!answer) {\n if (defaultKey !== undefined) {\n return defaultKey;\n }\n continue; // definitive questions have no default — ask again\n }\n const hit =\n choices.find((c) => c.key === answer) ??\n choices.find((c) => c.label.toLowerCase().startsWith(answer));\n if (hit) {\n return hit.key;\n }\n // Unrecognized — the loop re-prints the menu.\n }\n } finally {\n rl.close();\n }\n}\n","import chalk from \"chalk\";\n\n/**\n * Styled terminal output — our small stand-in for Python's `rich`.\n *\n * We deliberately keep the surface tiny: the launcher only ever needs to shout\n * about errors and warn about degraded launches. Routine progress (which CLIs\n * were found, what command is being assembled) is intentionally left unprinted\n * so the terminal stays quiet until something actually goes wrong.\n */\nexport function printError(title: string, detail?: string): void {\n console.error(`${chalk.bgRed.white.bold(\" ERROR \")} ${chalk.red.bold(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n\nexport function printWarning(title: string, detail?: string): void {\n console.error(`${chalk.bgYellow.black.bold(\" WARN \")} ${chalk.yellow.bold(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n\n/** A one-off informational aside (tips, config-written confirmations). */\nexport function printNote(title: string, detail?: string): void {\n console.error(`${chalk.bgCyan.black.bold(\" NOTE \")} ${chalk.cyan(title)}`);\n if (detail) {\n console.error(chalk.dim(detail));\n }\n}\n","/**\n * Managed Chrome for Testing (CfT) — the recommended browser for `aiui claude`.\n *\n * CfT is Google's automation build of Chrome: version-pinned, no auto-update,\n * and it still honors `--load-extension`, so the aiui DevTools panel loads\n * automatically (branded Chrome ≥ 137 ignores that flag). aiui keeps its own\n * CfT install in the **user-level** cache (`~/.cache/aiui/chrome/`, shared\n * across projects — these are ~160 MB downloads) via `@puppeteer/browsers`,\n * which manages `<cacheDir>/chrome/<platform>-<buildId>/` layouts for us.\n *\n * Because CfT never updates itself, staying current is our job: launches check\n * the latest stable build id (at most once per {@link CHECK_TTL_MS}, with a\n * short network timeout, silently skipped offline) and either prompt or\n * auto-update per `chrome.forTesting` in config. All prompt bookkeeping —\n * when we last checked, which update the user skipped, when an install offer\n * was declined — lives in one small state file next to the installs.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\nimport {\n Browser,\n detectBrowserPlatform,\n getInstalledBrowsers,\n install,\n resolveBuildId,\n uninstall,\n} from \"@puppeteer/browsers\";\nimport { type ForTestingMode, updateUserConfig } from \"./config\";\nimport { choose } from \"./prompt\";\nimport { printNote } from \"./ui\";\n\n/** Re-resolve the latest stable build id at most this often. */\nexport const CHECK_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Network budget for the version lookup on the launch path. */\nconst RESOLVE_TIMEOUT_MS = 4000;\n\n/** One managed CfT install. */\nexport interface CftInstall {\n buildId: string;\n executablePath: string;\n}\n\n/** Prompt/check bookkeeping, persisted in the CfT cache dir. */\nexport interface CftState {\n /** Epoch ms of the last successful latest-stable lookup. */\n checkedAt?: number;\n /** The latest stable build id as of `checkedAt`. */\n latestBuildId?: string;\n /** Update prompt answered \"skip\" for this build id — don't re-ask for it. */\n skippedBuildId?: string;\n /** Epoch ms when an install offer was declined — snooze re-asking for a day. */\n installDeclinedAt?: number;\n}\n\n/** Where managed CfT builds (and the state file) live. */\nexport function cftCacheDir(create = true): string {\n return cacheDir(\"chrome\", { create });\n}\n\nconst STATE_FILE = \"update-state.json\";\n\nexport function readCftState(): CftState {\n try {\n return JSON.parse(readFileSync(join(cftCacheDir(false), STATE_FILE), \"utf8\")) as CftState;\n } catch {\n return {};\n }\n}\n\nexport function writeCftState(patch: Partial<CftState>): void {\n const dir = cftCacheDir();\n mkdirSync(dir, { recursive: true });\n writeFileSync(join(dir, STATE_FILE), `${JSON.stringify({ ...readCftState(), ...patch })}\\n`);\n}\n\n/** Numeric, segment-wise build id comparison (\"138.0.7204.94\"-style). */\nexport function compareBuildIds(a: string, b: string): number {\n const as = a.split(\".\").map(Number);\n const bs = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(as.length, bs.length); i++) {\n const diff = (as[i] ?? 0) - (bs[i] ?? 0);\n if (diff) {\n return diff < 0 ? -1 : 1;\n }\n }\n return 0;\n}\n\n/** The newest managed CfT install, if any. Never touches the network. */\nexport async function installedCft(): Promise<CftInstall | undefined> {\n const dir = cftCacheDir(false);\n if (!existsSync(dir)) {\n return undefined;\n }\n const browsers = await getInstalledBrowsers({ cacheDir: dir });\n const chromes = browsers\n .filter((b) => b.browser === Browser.CHROME)\n .sort((a, b) => compareBuildIds(a.buildId, b.buildId));\n const best = chromes.at(-1);\n return best && { buildId: best.buildId, executablePath: best.executablePath };\n}\n\n/**\n * The latest stable CfT build id, freshness-limited and offline-tolerant.\n *\n * Consults the network at most once per `maxAgeMs` (persisted in the state\n * file) with a short timeout; on failure it falls back to the last known\n * value, or undefined — callers treat undefined as \"can't tell, don't nag\".\n */\nexport async function latestStableCft(\n opts: { maxAgeMs?: number; timeoutMs?: number; now?: number } = {},\n): Promise<string | undefined> {\n const { maxAgeMs = CHECK_TTL_MS, timeoutMs = RESOLVE_TIMEOUT_MS, now = Date.now() } = opts;\n const state = readCftState();\n if (state.latestBuildId && state.checkedAt && now - state.checkedAt < maxAgeMs) {\n return state.latestBuildId;\n }\n const platform = detectBrowserPlatform();\n if (!platform) {\n return undefined;\n }\n try {\n const buildId = await withTimeout(\n resolveBuildId(Browser.CHROME, platform, \"stable\"),\n timeoutMs,\n );\n writeCftState({ checkedAt: now, latestBuildId: buildId });\n return buildId;\n } catch {\n return state.latestBuildId;\n }\n}\n\n/**\n * Install the given CfT build into the managed cache and drop superseded\n * builds (they're ~160 MB each; the whole point of the managed dir is that\n * there's exactly one, current, browser in it).\n */\nexport async function installCft(buildId: string): Promise<CftInstall> {\n const dir = cftCacheDir();\n const fresh = await install({\n browser: Browser.CHROME,\n buildId,\n cacheDir: dir,\n downloadProgressCallback: \"default\",\n });\n const others = (await getInstalledBrowsers({ cacheDir: dir })).filter(\n (b) => b.browser === Browser.CHROME && b.buildId !== buildId,\n );\n for (const old of others) {\n await uninstall({ browser: Browser.CHROME, buildId: old.buildId, cacheDir: dir });\n }\n return { buildId, executablePath: fresh.executablePath };\n}\n\n/**\n * Bring the managed CfT to the latest stable (the `aiui chrome install` /\n * `update` implementation). Unlike the launch path this resolves with no\n * timeout — an explicit command is allowed to wait on the network — and\n * reports what it did.\n */\nexport async function ensureLatestCft(\n report: (line: string) => void,\n): Promise<CftInstall & { outcome: \"current\" | \"installed\" | \"updated\" }> {\n const platform = detectBrowserPlatform();\n if (!platform) {\n throw new Error(\"could not detect a supported platform for Chrome for Testing\");\n }\n const latest = await resolveBuildId(Browser.CHROME, platform, \"stable\");\n writeCftState({ checkedAt: Date.now(), latestBuildId: latest });\n const current = await installedCft();\n if (current && compareBuildIds(current.buildId, latest) >= 0) {\n report(`Chrome for Testing ${current.buildId} is up to date`);\n return { ...current, outcome: \"current\" };\n }\n report(\n current\n ? `updating Chrome for Testing ${current.buildId} → ${latest}…`\n : `installing Chrome for Testing ${latest}…`,\n );\n const fresh = await installCft(latest);\n report(`Chrome for Testing ${latest} installed at ${fresh.executablePath}`);\n return { ...fresh, outcome: current ? \"updated\" : \"installed\" };\n}\n\n/**\n * The launch-path CfT sync: decide which browser this session should use, and\n * (interactively, when allowed) offer to install or update the managed CfT.\n *\n * Returns the CfT executable path to prefer, or undefined to fall back to the\n * system Chrome. Callers only invoke this when config names no browser\n * explicitly (no `chrome.executablePath` / `chrome.channel`).\n *\n * The mode ladder (`chrome.forTesting`, default \"prompt\"):\n * - \"off\" — never check, never prompt; an already-installed managed CfT is\n * still used (install one deliberately with `aiui chrome install`).\n * - \"auto\" — install/update to latest stable without asking.\n * - \"prompt\" — offer to install when missing, offer to update when stale;\n * answers can rewrite the mode in the user config.\n *\n * Nothing here ever blocks a non-interactive session: without a TTY (or under\n * CI, or in print mode) this degrades to \"use whatever is already installed\".\n * Downloads are likewise interactive-only — even \"auto\" won't pull ~160 MB\n * into a headless one-shot.\n */\nexport async function syncChromeForTesting(opts: {\n mode: ForTestingMode;\n interactive: boolean;\n now?: number;\n}): Promise<string | undefined> {\n const { mode, interactive, now = Date.now() } = opts;\n const current = await installedCft();\n if (mode === \"off\" || !interactive) {\n return current?.executablePath;\n }\n\n if (!current) {\n return offerInstall(mode, now);\n }\n\n const latest = await latestStableCft({ now });\n if (!latest || compareBuildIds(latest, current.buildId) <= 0) {\n return current.executablePath;\n }\n return offerUpdate(mode, current, latest);\n}\n\n/** No managed CfT: install silently (auto) or ask (prompt). */\nasync function offerInstall(mode: \"prompt\" | \"auto\", now: number): Promise<string | undefined> {\n const latest = await latestStableCft({ now });\n if (!latest) {\n return undefined; // offline / undetectable platform — don't nag, don't block\n }\n if (mode === \"auto\") {\n printNote(`installing Chrome for Testing ${latest} (chrome.forTesting: \"auto\")…`);\n return (await installCft(latest)).executablePath;\n }\n const state = readCftState();\n if (state.installDeclinedAt && now - state.installDeclinedAt < CHECK_TTL_MS) {\n return undefined; // declined recently — don't re-ask every launch\n }\n const answer = await choose(\n \"Chrome for Testing isn't installed. It's the recommended browser for aiui — \" +\n \"version-pinned, separate from your real Chrome, and it auto-loads the aiui \" +\n `DevTools panel (branded Chrome can't). Download ${latest} (~160 MB) to ${cftCacheDir(false)}?`,\n [\n { key: \"y\", label: \"yes, install it\" },\n { key: \"n\", label: \"not now — use the regular Chrome (asks again tomorrow)\" },\n { key: \"never\", label: 'never — stop offering (writes chrome.forTesting: \"off\")' },\n ],\n \"y\",\n );\n if (answer === \"y\") {\n return (await installCft(latest)).executablePath;\n }\n if (answer === \"never\") {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"off\" };\n });\n printNote(`wrote chrome.forTesting: \"off\" to ${file}`);\n } else {\n writeCftState({ installDeclinedAt: now });\n }\n return undefined;\n}\n\n/** Managed CfT is stale: update silently (auto) or ask (prompt). */\nasync function offerUpdate(\n mode: \"prompt\" | \"auto\",\n current: CftInstall,\n latest: string,\n): Promise<string> {\n if (mode === \"auto\") {\n printNote(\n `updating Chrome for Testing ${current.buildId} → ${latest} (chrome.forTesting: \"auto\")…`,\n );\n return (await installCft(latest)).executablePath;\n }\n if (readCftState().skippedBuildId === latest) {\n return current.executablePath;\n }\n const answer = await choose(\n `Your Chrome for Testing (${current.buildId}) is out of date — latest stable is ${latest}. Update?`,\n [\n { key: \"y\", label: \"yes, just this once\" },\n { key: \"a\", label: 'automatically, now and from here on (writes chrome.forTesting: \"auto\")' },\n {\n key: \"s\",\n label: `skip ${latest} — keep ${current.buildId}, don't ask again for this version`,\n },\n { key: \"never\", label: 'never ask again (writes chrome.forTesting: \"off\")' },\n ],\n \"y\",\n );\n switch (answer) {\n case \"y\":\n return (await installCft(latest)).executablePath;\n case \"a\": {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"auto\" };\n });\n printNote(`wrote chrome.forTesting: \"auto\" to ${file}`);\n return (await installCft(latest)).executablePath;\n }\n case \"never\": {\n const file = updateUserConfig((c) => {\n c.chrome = { ...c.chrome, forTesting: \"off\" };\n });\n printNote(`wrote chrome.forTesting: \"off\" to ${file}`);\n return current.executablePath;\n }\n default: // \"s\"\n writeCftState({ skippedBuildId: latest });\n return current.executablePath;\n }\n}\n\nasync function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`timed out after ${ms}ms`)), ms);\n }),\n ]);\n } finally {\n clearTimeout(timer);\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { packageRoot, runningFromSource } from \"@habemus-papadum/aiui-util\";\n\n// `packageRoot` (and the \"still carries src/ → dev checkout\" heuristic below)\n// moved to aiui-util as shared provenance logic; re-exported here so existing\n// importers keep resolving it from this module.\nexport { packageRoot } from \"@habemus-papadum/aiui-util\";\n\n/** How to spawn a CLI: a program plus the args that precede any subcommand. */\nexport interface CliInvocation {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve how to run a dependency package's CLI, without ever needing it built\n * in a dev checkout.\n *\n * A package is considered \"in dev\" when it still carries its `src/` directory\n * (published tarballs ship only `dist/`). In that case we run the TypeScript\n * source directly through `tsx`, so edits take effect with no build step. Once\n * installed from npm, we run the built `dist` entry instead. Either way it is\n * spawned via the current Node with an absolute path, so it relies on neither\n * the PATH nor an executable bit.\n */\nexport function resolvePackageCli(packageName: string, binName?: string): CliInvocation {\n const root = packageRoot(packageName);\n const pkg = JSON.parse(readFileSync(join(root, \"package.json\"), \"utf8\")) as {\n bin?: string | Record<string, string>;\n };\n\n const binRel =\n typeof pkg.bin === \"string\" ? pkg.bin : binName ? pkg.bin?.[binName] : firstValue(pkg.bin);\n if (!binRel) {\n throw new Error(\n `package ${packageName} declares no bin${binName ? ` named \"${binName}\"` : \"\"}`,\n );\n }\n\n if (runningFromSource(root)) {\n // dev: dist/cli.js -> src/cli.ts, run through tsx (no build needed).\n const srcRel = binRel.replace(/^\\.?\\/?dist\\//, \"src/\").replace(/\\.js$/, \".ts\");\n return { command: process.execPath, args: [\"--import\", \"tsx\", resolve(root, srcRel)] };\n }\n // installed: run the built entry directly.\n return { command: process.execPath, args: [resolve(root, binRel)] };\n}\n\nfunction firstValue(bin: Record<string, string> | undefined): string | undefined {\n return bin ? Object.values(bin)[0] : undefined;\n}\n","/**\n * The Chrome DevTools MCP attachment for `aiui claude`.\n *\n * By default the session gets a second MCP server alongside the channel:\n * Google's [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp),\n * run via npx, giving the agent a Chrome to drive (navigate, click, screenshot,\n * evaluate). It reaches a browser one of two ways — the mode ladder itself\n * lives in commands/claude.ts (chromeServerEntry); the full user-facing story\n * is docs/guide/chrome:\n *\n * - **attach** (default): the MCP is pointed (`--browser-url`) at a shared,\n * user-visible *session browser* — discovered or eagerly launched via\n * aiui-util's browser module — so human and agent work in the same window.\n * - **launch**: the MCP launches its own private browser, lazily, on the\n * agent's first tool call ({@link chromeMcpServer} builds that entry).\n *\n * The deliberate choices common to both:\n *\n * - **A persistent, project-local profile.** Chrome's user data dir defaults to\n * `.aiui-cache/chrome/default` under the launch directory — the same\n * gitignored cache the lowering traces live in — so browser state (logins,\n * devtools settings, manually installed extensions) survives across sessions\n * and stays out of both git and the user's real browser profile. Named\n * profiles (`--aiui-chrome-profile <name>`) live as siblings and are created\n * on first use; `--aiui-chrome-data-dir <path>` escapes the convention\n * entirely.\n *\n * - **Best-effort auto-load of the aiui DevTools panel.** In a dev checkout we\n * rebuild the `aiui-devtools-extension` package (~0.3s of tsc, so it is never\n * stale) and pass `--load-extension` pointing at it. Chrome-branded builds\n * ≥ 137 ignore that flag (Chromium and Chrome for Testing still honor it), so\n * this is an attempt, not a guarantee — the reliable path is loading the\n * extension unpacked once at `chrome://extensions`, which the persistent\n * profile then remembers.\n *\n * - **Config picks the browser.** `chrome.executablePath` (e.g. a Chrome for\n * Testing binary) or `chrome.channel` choose what to launch;\n * `chrome.browserUrl` says \"don't launch anything, attach here\" (the remote\n * key); flags choose per-invocation things (profile, on/off).\n */\nimport { existsSync, realpathSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { projectCacheDir } from \"@habemus-papadum/aiui-claude-channel\";\nimport { isCi } from \"@habemus-papadum/aiui-util\";\nimport { execa } from \"execa\";\nimport type { AiuiArgs } from \"./aiui-args\";\nimport type { AiuiConfig, ChromeChannel, ChromeMode } from \"./config\";\nimport { packageRoot } from \"./resolve-cli\";\nimport { printNote, printWarning } from \"./ui\";\n\n/**\n * The id of the Chrome DevTools entry under `mcpServers`. Deliberately the\n * conventional name from the chrome-devtools-mcp docs: if the user's own Claude\n * config already registers `chrome-devtools`, the two entries collide by name —\n * one definition wins — instead of two MCP servers racing to launch two Chromes\n * with duplicate toolsets.\n */\nexport const CHROME_SERVER_ID = \"chrome-devtools\";\n\nconst DEVTOOLS_PKG = \"@habemus-papadum/aiui-devtools-extension\";\n\n/** The profile used when neither flags nor config name one. */\nexport const DEFAULT_CHROME_PROFILE = \"default\";\n\n/** Profile names must stay plain directory names — no separators, no leading dot. */\nconst PROFILE_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\ntype ChromeConfig = NonNullable<AiuiConfig[\"chrome\"]>;\ntype ChromeFlags = Pick<AiuiArgs, \"chrome\" | \"noChrome\" | \"chromeProfile\" | \"chromeDataDir\">;\n\n/**\n * Whether this launch should attach the Chrome DevTools MCP.\n *\n * On by default; off under CI (no display, and e2e sessions must not trigger an\n * npx download + Chrome launch), with `--aiui-no-chrome`, or with\n * `chrome.enabled: false` in config. `--aiui-chrome` forces it on even under\n * CI; `chrome.enabled: true` merely restates the default and does not.\n *\n * Deliberately gated on CI alone, not the wider `isHeadless` check from\n * aiui-util: on a headless-but-interactive box (SSH into a dev machine)\n * the MCP still works — it just launches/attaches a headless-capable Chrome —\n * whereas *opening a page for the user* would be pointless. Only commands that\n * put a window in front of someone consult the broader signals.\n */\nexport function chromeDevtoolsEnabled(\n args: Pick<ChromeFlags, \"chrome\" | \"noChrome\">,\n config: ChromeConfig = {},\n env: NodeJS.ProcessEnv = process.env,\n): boolean {\n if (args.noChrome) {\n return false;\n }\n if (args.chrome) {\n return true;\n }\n if (config.enabled === false) {\n return false;\n }\n return !isCi(env);\n}\n\n/** The launch-relevant Chrome settings after flags and config are reconciled. */\nexport interface ChromeSettings {\n /** Absolute user data dir for this launch. */\n userDataDir: string;\n /**\n * \"attach\" (default): share a session browser — discover or eagerly launch\n * one and point the MCP at its debug endpoint. \"launch\": chrome-devtools-mcp\n * owns a private browser, started lazily on first tool use.\n */\n mode: ChromeMode;\n /** Explicit attach endpoint from config; forces attach, disables management. */\n browserUrl?: string;\n /** Debug port for session browsers aiui launches (0 = OS-assigned). */\n debugPort: number;\n /** Chrome binary to launch instead of an installed channel, if configured. */\n executablePath?: string;\n /** Installed Chrome release channel to launch, if configured. */\n channel?: ChromeChannel;\n headless: boolean;\n buildExtension: boolean;\n}\n\n/**\n * Reconcile CLI flags with config into the settings for this launch.\n *\n * Flags beat config. The profile/data-dir pair is reconciled as a unit: a\n * `--aiui-chrome-profile` flag also suppresses a configured `dataDir` (and\n * vice versa), because whichever identity the user named at the prompt is the\n * one they mean. Within config alone, `dataDir` beats `profile`.\n */\nexport function resolveChromeSettings(\n args: Pick<ChromeFlags, \"chromeProfile\" | \"chromeDataDir\">,\n config: ChromeConfig = {},\n base: string = process.cwd(),\n): ChromeSettings {\n if (config.executablePath && config.channel) {\n throw new Error(\n \"config sets both chrome.executablePath and chrome.channel — they pick the browser \" +\n \"two different ways; keep exactly one\",\n );\n }\n const flagged = args.chromeProfile !== undefined || args.chromeDataDir !== undefined;\n const dataDir = args.chromeDataDir ?? (flagged ? undefined : config.dataDir);\n const profile = args.chromeProfile ?? (flagged ? undefined : config.profile);\n return {\n userDataDir: chromeUserDataDir({ dataDir, profile }, base),\n // A configured endpoint means the browser is managed elsewhere (usually\n // another machine) — that's always attach, whatever `mode` says.\n mode: config.browserUrl ? \"attach\" : (config.mode ?? \"attach\"),\n browserUrl: config.browserUrl,\n debugPort: config.debugPort ?? 0,\n executablePath: config.executablePath && resolve(base, config.executablePath),\n channel: config.channel,\n headless: config.headless ?? false,\n buildExtension: config.buildExtension ?? true,\n };\n}\n\n/**\n * The Chrome user data dir to use (absolute).\n *\n * An explicit `dataDir` wins, resolved against `base`. Otherwise the named\n * profile (default: {@link DEFAULT_CHROME_PROFILE}) maps to\n * `.aiui-cache/chrome/<name>` under `base` — alternate profiles are just other\n * names, created on first use by the caller's mkdir.\n */\nexport function chromeUserDataDir(\n ids: { dataDir?: string; profile?: string },\n base: string = process.cwd(),\n): string {\n if (ids.dataDir) {\n return resolve(base, ids.dataDir);\n }\n const profile = ids.profile ?? DEFAULT_CHROME_PROFILE;\n if (!PROFILE_NAME.test(profile)) {\n throw new Error(\n `invalid chrome profile name \"${profile}\" — use letters, digits, \".\", \"_\", \"-\" ` +\n \"(or --aiui-chrome-data-dir for an arbitrary path)\",\n );\n }\n return join(projectCacheDir(base), \"chrome\", profile);\n}\n\n/**\n * The built aiui-devtools-extension directory, if available.\n *\n * The package publishes `extension/` prebuilt, so this resolves both in a dev\n * workspace (where `extension/js` appears once built — see\n * {@link buildDevtoolsExtension}) and installed from npm. Without `js/` the\n * extension is an empty shell, so an unbuilt dev checkout returns undefined.\n */\nexport function devtoolsExtensionDir(): string | undefined {\n let dir: string;\n try {\n // realpath, not the pnpm symlink — the same canonical path a manual\n // \"Load unpacked\" would register in the profile.\n dir = realpathSync(join(packageRoot(DEVTOOLS_PKG), \"extension\"));\n } catch {\n return undefined;\n }\n return existsSync(join(dir, \"js\")) ? dir : undefined;\n}\n\n/**\n * Rebuild the aiui-devtools-extension package so the auto-loaded panel is never stale.\n *\n * A full tsc of the extension (plus the debug-ui esbuild bundle) is well\n * under a second — cheap enough to run on every launch rather than tracking\n * staleness. Best-effort by design: outside a dev\n * checkout (no devtools package, no typescript) it silently does nothing, and\n * a failing compile warns loudly but never blocks the launch — whatever\n * `extension/js` already holds is what gets loaded.\n */\nexport async function buildDevtoolsExtension(): Promise<void> {\n let root: string;\n let tsc: string;\n try {\n root = realpathSync(packageRoot(DEVTOOLS_PKG));\n tsc = join(packageRoot(\"typescript\"), \"bin\", \"tsc\");\n } catch {\n return;\n }\n // Only a dev checkout carries src/ — the published package ships the built\n // extension/js (no sources, nothing to compile), so installed-from-npm\n // layouts skip straight to loading it.\n if (!existsSync(join(root, \"src\"))) {\n return;\n }\n const result = await execa(process.execPath, [tsc, \"-p\", join(root, \"tsconfig.json\")], {\n cwd: root,\n reject: false,\n all: true,\n });\n if (result.exitCode) {\n printWarning(\n \"aiui-devtools-extension failed to compile — the DevTools panel will be stale or missing\",\n result.all || result.message,\n );\n return;\n }\n // The Intent pane's shared debug-ui is bundled (esbuild) from the overlay's\n // source — tsc alone can't produce it. Same best-effort posture: the script\n // is only present in a dev checkout, and a failure degrades exactly one pane\n // (the panel imports debug-ui.js lazily), never the launch.\n const bundleScript = join(root, \"build-debug-ui.mjs\");\n if (!existsSync(bundleScript)) {\n return;\n }\n const bundle = await execa(process.execPath, [bundleScript], {\n cwd: root,\n reject: false,\n all: true,\n });\n if (bundle.exitCode) {\n printWarning(\n \"aiui-devtools-extension debug-ui bundle failed — the Intent pane will be degraded\",\n bundle.all || bundle.message,\n );\n }\n}\n\n/**\n * One-time tip when the extension exists but the chosen browser won't\n * auto-load it (no `executablePath` means a branded Chrome — installed\n * stable or a `channel` build — and branded builds ≥ 137 ignore\n * `--load-extension`). Printed once per profile: the marker file lives in the\n * user data dir, so it survives exactly as long as the profile whose manual\n * install it recommends.\n */\nexport function maybeExtensionAutoloadHint(\n settings: ChromeSettings,\n extensionDir: string | undefined,\n): void {\n if (!extensionDir || settings.executablePath) {\n return;\n }\n const marker = join(settings.userDataDir, \"aiui-devtools-extension-hint\");\n if (existsSync(marker)) {\n return;\n }\n printNote(\n \"the aiui DevTools panel can't auto-load into regular Chrome (≥ 137 ignores --load-extension)\",\n `Load it once in the launched Chrome — chrome://extensions → Developer mode → Load unpacked →\\n` +\n `${extensionDir}\\n` +\n \"— and this profile remembers it. Or switch to Chrome for Testing (`aiui chrome install`),\\n\" +\n \"which auto-loads it. This note won't repeat for this profile.\",\n );\n try {\n writeFileSync(marker, `${new Date().toISOString()}\\n`);\n } catch {\n // Best-effort: an unwritable profile dir just means the note may repeat.\n }\n}\n\n/**\n * The `mcpServers` entry that launches chrome-devtools-mcp.\n *\n * Uses the documented `npx -y chrome-devtools-mcp@latest` invocation with the\n * user data dir pinned to ours. Puppeteer's default launch args include\n * `--disable-extensions`, which would neuter both the auto-loaded panel and\n * anything installed manually into the profile — so it is always stripped.\n * When an extension dir is given, additionally ask Chrome to load it.\n */\n/**\n * The `mcpServers` entry that *attaches* chrome-devtools-mcp to an existing\n * browser's DevTools endpoint (a session browser, or a tunneled remote one).\n * In this mode the MCP manages no browser: user-data-dir, extension, and\n * headless choices all belong to whoever launched it.\n */\nexport function chromeMcpAttachServer(browserUrl: string): { command: string; args: string[] } {\n return {\n command: \"npx\",\n args: [\"-y\", \"chrome-devtools-mcp@latest\", \"--browser-url\", browserUrl],\n };\n}\n\nexport function chromeMcpServer(\n settings: ChromeSettings,\n extensionDir?: string,\n): { command: string; args: string[] } {\n const args = [\n \"-y\",\n \"chrome-devtools-mcp@latest\",\n \"--userDataDir\",\n settings.userDataDir,\n \"--ignoreDefaultChromeArg=--disable-extensions\",\n ];\n if (settings.executablePath) {\n args.push(\"--executablePath\", settings.executablePath);\n }\n if (settings.channel) {\n args.push(\"--channel\", settings.channel);\n }\n if (settings.headless) {\n args.push(\"--headless\");\n }\n if (extensionDir) {\n args.push(`--chromeArg=--load-extension=${extensionDir}`);\n }\n return { command: \"npx\", args };\n}\n","/**\n * `aiui browser` — start (or find) the session browser; `aiui open <url>` —\n * open a page in it.\n *\n * `aiui browser` is how the session browser exists *independently* of a\n * Claude session: locally when you want the window up before (or without)\n * `aiui claude`, and — the headline use — on your **local machine in remote\n * development**, where one command does the whole local half:\n *\n * aiui browser --tunnel dev-box\n *\n * launches the browser (Chrome for Testing preferred, devtools panel loaded),\n * reverse-tunnels its debug port to `dev-box` on a **fixed remote port**\n * (default 9222 — the remote port is the one worth pinning: it's what the\n * remote session and any VS Code launch config reference; the local port can\n * float), and prints the exact command to run over there. Ctrl-C closes the\n * tunnel; the browser stays.\n *\n * Profiles: without `--tunnel`, the profile is project-local as usual\n * (`.aiui-cache/chrome/<name>` under the cwd). With `--tunnel` there is\n * usually no local checkout to anchor to, so the profile lives in the\n * **user-level cache**, keyed by the tunnel host —\n * `~/.cache/aiui/browser-profiles/<host>` — so reconnecting to the same box\n * reuses the same browser state. `--profile <name>` renames that key;\n * `--data-dir <path>` escapes the convention entirely.\n *\n * Both commands identify the browser the same way `aiui claude` does: by the\n * profile's user data dir, via Chrome's own `DevToolsActivePort` file.\n */\nimport { join } from \"node:path\";\nimport {\n cacheDir,\n discoverSessionBrowser,\n isCi,\n launchSessionBrowser,\n openInSessionBrowser,\n type SessionBrowser,\n sessionBrowserBinary,\n} from \"@habemus-papadum/aiui-util\";\nimport { execa } from \"execa\";\nimport type { AiuiArgs } from \"../util/aiui-args\";\nimport { syncChromeForTesting } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n type ChromeSettings,\n devtoolsExtensionDir,\n maybeExtensionAutoloadHint,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { type AiuiConfig, loadAiuiConfig } from \"../util/config\";\nimport { printError, printNote } from \"../util/ui\";\n\ntype ChromeConfig = NonNullable<AiuiConfig[\"chrome\"]>;\n\n/** The default *remote* port — the one worth keeping fixed (see module doc). */\nexport const DEFAULT_REMOTE_PORT = 9222;\n\nexport interface BrowserOptions {\n profile?: string;\n dataDir?: string;\n port?: string;\n headless?: boolean;\n open?: string;\n /** `[user@]host` to reverse-tunnel the debug port to (runs until Ctrl-C). */\n tunnel?: string;\n /** Fixed port on the tunnel's remote side (default {@link DEFAULT_REMOTE_PORT}). */\n remotePort?: string;\n}\n\nexport async function runBrowser(opts: BrowserOptions): Promise<void> {\n const config = loadAiuiConfig();\n const chromeCfg = { ...config.chrome };\n if (chromeCfg.browserUrl) {\n printNote(\n `config pins chrome.browserUrl to ${chromeCfg.browserUrl} — the browser is managed elsewhere`,\n \"Run `aiui browser` on the machine that should host it (and drop browserUrl there).\",\n );\n return;\n }\n\n let remotePort: number;\n let debugPort: number | undefined;\n try {\n remotePort = parsePort(opts.remotePort, \"--remote-port\") ?? DEFAULT_REMOTE_PORT;\n debugPort = parsePort(opts.port, \"--port\");\n } catch (error) {\n printError(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n return;\n }\n\n // With a tunnel, the profile anchors to the *remote host* in the user cache\n // (no local checkout to be project-local to); see the module doc.\n const flags = {\n chromeProfile: opts.tunnel ? undefined : opts.profile,\n chromeDataDir:\n opts.dataDir ?? (opts.tunnel ? tunnelProfileDir(opts.tunnel, opts.profile) : undefined),\n };\n let settings = resolveChromeSettings(flags, chromeCfg);\n if (debugPort !== undefined) {\n settings = { ...settings, debugPort };\n }\n\n let session = await discoverSessionBrowser(settings.userDataDir);\n if (session) {\n report(\"session browser already running\", settings, session);\n if (opts.open) {\n await openInSessionBrowser(session.browserUrl, opts.open);\n console.log(`opened ${opts.open}`);\n }\n } else {\n const interactive = !!process.stdin.isTTY && !!process.stdout.isTTY && !isCi();\n try {\n const started = await startSessionBrowser({\n flags,\n config: chromeCfg,\n interactive,\n debugPort,\n headless: opts.headless,\n startUrl: opts.open,\n });\n session = started.session;\n settings = started.settings;\n } catch (error) {\n printError(\n \"the session browser failed to start\",\n error instanceof Error ? error.message : String(error),\n );\n process.exitCode = 1;\n return;\n }\n report(\"session browser started\", settings, session);\n }\n\n if (opts.tunnel) {\n await runTunnel(opts.tunnel, remotePort, session.port);\n } else {\n printNextSteps(session, remotePort);\n }\n}\n\n/** Options for {@link startSessionBrowser}. */\nexport interface StartSessionBrowserOptions {\n /** CLI identity flags (profile / data dir), reconciled against config. */\n flags?: Pick<AiuiArgs, \"chromeProfile\" | \"chromeDataDir\">;\n /** The `chrome` section of the loaded config. */\n config?: ChromeConfig;\n /**\n * Whether prompting is allowed. Interactive launches may offer to\n * install/update Chrome for Testing and print the extension autoload hint;\n * non-interactive ones (CI, or a sidecar whose terminal belongs to another\n * process — `aiui vite`'s browser open) degrade to whatever browser is\n * already installed, silently.\n */\n interactive: boolean;\n /** Override the resolved DevTools debug port (`aiui browser --port`). */\n debugPort?: number;\n /** Launch headless regardless of config (`aiui browser --headless`). */\n headless?: boolean;\n /** Open this URL as the first tab instead of about:blank. */\n startUrl?: string;\n}\n\n/**\n * Launch the session browser the way `aiui browser` does — the shared\n * \"everything before the window exists\" sequence, extracted so other commands\n * (`aiui vite`'s browser sidecar) launch identically instead of re-deriving\n * it:\n *\n * 1. Resolve settings from flags + config.\n * 2. Prefer the managed Chrome for Testing unless config names a browser\n * explicitly (the sync may prompt to install/update — only when\n * `interactive`; otherwise it just reports what's installed).\n * 3. Rebuild and load the aiui devtools extension (dev checkouts only).\n * 4. Launch on the profile's user data dir and wait for the debug endpoint.\n *\n * Throws with a remediation-bearing message when no browser can be found or\n * the launch fails; callers decide whether that's fatal (`aiui browser`) or\n * merely a warning (`aiui vite`, where the dev server must keep running).\n */\nexport async function startSessionBrowser(\n opts: StartSessionBrowserOptions,\n): Promise<{ session: SessionBrowser; settings: ChromeSettings }> {\n let cfg = opts.config ?? {};\n const flags = opts.flags ?? {};\n const settle = () => {\n const settings = resolveChromeSettings(flags, cfg);\n return opts.debugPort === undefined ? settings : { ...settings, debugPort: opts.debugPort };\n };\n let settings = settle();\n\n if (!cfg.executablePath && !cfg.channel) {\n const cft = await syncChromeForTesting({\n mode: cfg.forTesting ?? \"prompt\",\n interactive: opts.interactive,\n });\n if (cft) {\n cfg = { ...cfg, executablePath: cft };\n settings = settle();\n }\n }\n if (settings.buildExtension) {\n await buildDevtoolsExtension();\n }\n const extensionDir = devtoolsExtensionDir();\n if (opts.interactive) {\n maybeExtensionAutoloadHint(settings, extensionDir);\n }\n\n let binary: string;\n try {\n binary = sessionBrowserBinary(settings);\n } catch (error) {\n throw new Error(\n `${error instanceof Error ? error.message : String(error)}\\n` +\n \"Install Chrome for Testing with `aiui chrome install`, or set chrome.executablePath.\",\n );\n }\n const session = await launchSessionBrowser({\n binary,\n userDataDir: settings.userDataDir,\n debugPort: settings.debugPort,\n extensionDir,\n headless: settings.headless || opts.headless,\n startUrl: opts.startUrl,\n });\n return { session, settings };\n}\n\n/**\n * `~/.cache/aiui/browser-profiles/<key>` for a tunneled session browser.\n * Path-only (no mkdir) — the browser launch creates it on first use.\n */\nexport function tunnelProfileDir(target: string, profile?: string): string {\n const key = profile ?? sanitizeHostKey(target);\n return join(cacheDir(\"browser-profiles\", { create: false }), key);\n}\n\n/** \"user@dev.example.com\" → \"dev.example.com\", made filesystem-safe. */\nexport function sanitizeHostKey(target: string): string {\n const host = target.includes(\"@\") ? target.slice(target.indexOf(\"@\") + 1) : target;\n const key = host.replace(/[^A-Za-z0-9._-]/g, \"-\");\n return key || \"remote\";\n}\n\n/** The ssh argv for the reverse tunnel (exported for tests). */\nexport function sshTunnelArgs(target: string, remotePort: number, localPort: number): string[] {\n return [\n // No remote command — the connection exists only to carry the forward...\n \"-N\",\n // ...so if the forward can't be established (remote port taken), fail\n // loudly instead of holding a useless connection open.\n \"-o\",\n \"ExitOnForwardFailure=yes\",\n \"-R\",\n `${remotePort}:localhost:${localPort}`,\n target,\n ];\n}\n\n/** The command to run on the remote side, given the tunnel's remote port. */\nexport function remoteAttachCommand(remotePort: number): string {\n return `aiui claude --aiui-browser-url http://127.0.0.1:${remotePort}`;\n}\n\n/**\n * Hold the reverse tunnel open in the foreground (stdio inherited, so ssh's\n * own auth prompts — passwords, host keys, 2FA — work normally). The browser\n * deliberately outlives the tunnel: Ctrl-C here only drops the forward.\n */\nasync function runTunnel(target: string, remotePort: number, localPort: number): Promise<void> {\n console.log(\n `\\ntunneling ${target}:${remotePort} → localhost:${localPort} — on ${target}, run:\\n\\n` +\n ` ${remoteAttachCommand(remotePort)}\\n\\n` +\n \"(Ctrl-C closes the tunnel; the browser stays running.)\",\n );\n const result = await execa(\"ssh\", sshTunnelArgs(target, remotePort, localPort), {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.failed && !result.isTerminated) {\n printError(\n `the ssh tunnel to ${target} exited (code ${result.exitCode})`,\n \"A taken remote port exits immediately (ExitOnForwardFailure) — try another --remote-port. \" +\n \"The browser is still running; rerun `aiui browser --tunnel …` to reconnect.\",\n );\n process.exitCode = 1;\n }\n}\n\nfunction parsePort(raw: string | undefined, flag: string): number | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const port = Number(raw);\n if (!(Number.isInteger(port) && port >= 0 && port <= 65535)) {\n throw new Error(`invalid ${flag} ${raw} — expected 0..65535`);\n }\n return port;\n}\n\nfunction report(title: string, settings: ChromeSettings, session: SessionBrowser): void {\n console.log(title);\n console.log(` profile: ${settings.userDataDir}`);\n console.log(` debug endpoint: ${session.browserUrl}`);\n}\n\n/** The no-tunnel closing hint: local attach is automatic; remote needs steps. */\nfunction printNextSteps(session: SessionBrowser, remotePort: number): void {\n console.log(\n \"\\nAn `aiui claude` in this profile's project attaches automatically. For a *remote*\\n\" +\n \"session, rerun with `--tunnel <[user@]host>` — or do it by hand:\\n\" +\n ` ssh -N -o ExitOnForwardFailure=yes -R ${remotePort}:localhost:${session.port} <host>\\n` +\n `then, on the remote: ${remoteAttachCommand(remotePort)}`,\n );\n}\n\n/** `aiui open <url>` — open a page in the running session browser. */\nexport async function runOpen(\n url: string,\n opts: Pick<BrowserOptions, \"profile\" | \"dataDir\">,\n): Promise<void> {\n const config = loadAiuiConfig();\n const settings = resolveChromeSettings(\n { chromeProfile: opts.profile, chromeDataDir: opts.dataDir },\n config.chrome ?? {},\n );\n // An explicitly configured endpoint (remote browser) is also openable.\n const browserUrl =\n config.chrome?.browserUrl ?? (await discoverSessionBrowser(settings.userDataDir))?.browserUrl;\n if (!browserUrl) {\n printError(\n \"no session browser is running for this profile\",\n `Start one with \\`aiui browser\\` (profile: ${settings.userDataDir}).`,\n );\n process.exitCode = 1;\n return;\n }\n try {\n await openInSessionBrowser(browserUrl, url);\n console.log(`opened ${url}`);\n } catch (error) {\n printError(`couldn't open ${url}`, error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n }\n}\n","/**\n * `aiui chrome <action>` — manage the agent's browser.\n *\n * install | update bring the managed Chrome for Testing to latest stable\n * status what would launch here, and is the devtools panel available\n * extension print the aiui-devtools-extension directory (for Load unpacked)\n *\n * `install` and `update` are the same operation (idempotent \"ensure latest\");\n * both names exist because both questions get asked. `status` is the\n * diagnostic: it reports per the *current directory's* merged config, so run\n * it from the project you're wondering about.\n */\nimport { existsSync, readdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { discoverSessionBrowser } from \"@habemus-papadum/aiui-util\";\nimport { ensureLatestCft, installedCft, latestStableCft } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n chromeDevtoolsEnabled,\n chromeUserDataDir,\n devtoolsExtensionDir,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { loadAiuiConfig } from \"../util/config\";\nimport { printError } from \"../util/ui\";\n\nexport async function runChrome(args: string[]): Promise<void> {\n const [action] = args;\n switch (action) {\n case \"install\":\n case \"update\":\n await ensureLatestCft((line) => console.log(line));\n return;\n case \"status\":\n await printStatus();\n return;\n case \"extension\": {\n await buildDevtoolsExtension();\n const dir = devtoolsExtensionDir();\n if (!dir) {\n printError(\n \"the aiui-devtools-extension is not available in this install\",\n \"In a dev checkout, build it first: pnpm --filter @habemus-papadum/aiui-devtools-extension build\",\n );\n process.exitCode = 1;\n return;\n }\n console.log(dir);\n return;\n }\n default:\n printError(\n action ? `unknown aiui chrome action: ${action}` : \"aiui chrome needs an action\",\n \"Usage: aiui chrome <install | update | status | extension>\",\n );\n process.exitCode = 1;\n return;\n }\n}\n\n/** The human-facing dump of every browser decision this directory would get. */\nasync function printStatus(): Promise<void> {\n const config = loadAiuiConfig();\n const chromeCfg = config.chrome ?? {};\n const flags = { chrome: false, noChrome: false };\n\n const cft = await installedCft();\n const latest = await latestStableCft();\n\n console.log(\"Chrome for Testing (managed):\");\n if (cft) {\n const freshness =\n latest === undefined\n ? \"(latest stable unknown — offline?)\"\n : latest === cft.buildId\n ? \"(latest stable)\"\n : `(latest stable is ${latest} — run \\`aiui chrome update\\`)`;\n console.log(` installed ${cft.buildId} ${freshness}`);\n console.log(` ${cft.executablePath}`);\n } else {\n console.log(\" not installed — `aiui chrome install` (recommended; auto-loads the panel)\");\n }\n console.log(` startup checks (chrome.forTesting): ${chromeCfg.forTesting ?? \"prompt\"}`);\n\n console.log(\"\\nThis directory would launch:\");\n if (!chromeDevtoolsEnabled(flags, chromeCfg)) {\n console.log(\" nothing — the Chrome DevTools MCP is disabled here\");\n return;\n }\n if (chromeCfg.browserUrl) {\n console.log(` connection: attach to ${chromeCfg.browserUrl} (chrome.browserUrl)`);\n console.log(\" the browser is managed elsewhere — nothing launches on this machine\");\n return;\n }\n const effective = { ...chromeCfg };\n if (!effective.executablePath && !effective.channel && cft) {\n effective.executablePath = cft.executablePath;\n }\n const settings = resolveChromeSettings({}, effective);\n const running = await discoverSessionBrowser(settings.userDataDir);\n const connection =\n settings.mode === \"attach\"\n ? running\n ? `attach to the running session browser at ${running.browserUrl}`\n : \"attach — a session browser starts with the next interactive launch (or `aiui browser`)\"\n : \"launch — chrome-devtools-mcp starts a private browser on the agent's first tool use\";\n console.log(` connection: ${connection}`);\n const browser = settings.executablePath\n ? settings.executablePath === cft?.executablePath\n ? `Chrome for Testing ${cft.buildId}`\n : settings.executablePath\n : settings.channel\n ? `installed Chrome (${settings.channel} channel)`\n : \"installed Chrome (stable)\";\n console.log(` browser: ${browser}${settings.headless ? \" — headless\" : \"\"}`);\n console.log(` user data dir: ${settings.userDataDir}`);\n const profilesDir = join(chromeUserDataDir({}, process.cwd()), \"..\");\n if (existsSync(profilesDir)) {\n const profiles = readdirSync(profilesDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n if (profiles.length) {\n console.log(` profiles here: ${profiles.join(\", \")}`);\n }\n }\n\n console.log(\"\\naiui DevTools panel:\");\n const extension = devtoolsExtensionDir();\n if (!extension) {\n console.log(\" not available (unbuilt dev checkout? run `aiui chrome extension` for help)\");\n return;\n }\n console.log(` ${extension}`);\n if (settings.executablePath) {\n console.log(\" auto-loads via --load-extension (honored by Chrome for Testing/Chromium)\");\n } else {\n console.log(\" can NOT auto-load into branded Chrome ≥ 137 — load it unpacked once\");\n console.log(\n \" (chrome://extensions → Developer mode → Load unpacked), or `aiui chrome install`\",\n );\n }\n}\n","/**\n * Splitting aiui's own flags out of an otherwise pass-through arg list.\n *\n * The `aiui claude` (and future) subcommands forward nearly everything to the\n * underlying tool. The exception is aiui's own options, which by convention all\n * begin with `--aiui-` so they're unambiguously distinguishable from flags meant\n * for the wrapped command (e.g. claude's `--resume`).\n */\nimport { CHANNEL_BINDS, type ChannelBind } from \"./config-schema\";\n\nexport interface AiuiArgs {\n /** The `--aiui-tag <tag>` value, if provided (the channel/MCP session tag). */\n tag?: string;\n /**\n * The `--aiui-mcp <tag>` value, if provided — the tag of the running channel\n * MCP server to target (e.g. so `aiui vite` connects to a specific session).\n */\n mcp?: string;\n /**\n * `--aiui-chrome` was passed: force the Chrome DevTools MCP on even where it\n * would default off (i.e. under CI).\n */\n chrome: boolean;\n /**\n * `--aiui-no-chrome` was passed: don't attach the Chrome DevTools MCP to the\n * session. (Distinct from claude's own `--no-chrome`, which disables Claude\n * Code's built-in browser integration and forwards via passthrough.)\n */\n noChrome: boolean;\n /**\n * `--aiui-browser` was passed: open the wrapped tool's page in the session\n * browser even where it would default off (CI / headless environments —\n * e.g. `aiui vite` on a machine whose port *is* getting forwarded to one\n * with a display).\n */\n browser: boolean;\n /**\n * `--aiui-no-browser` was passed: skip all session-browser activity (no\n * tab opened, no browser launched) for this run.\n */\n noBrowser: boolean;\n /**\n * The `--aiui-chrome-profile <name>` value, if provided — which named Chrome\n * profile (user data dir under `.aiui-cache/chrome/`) to launch with.\n */\n chromeProfile?: string;\n /**\n * The `--aiui-chrome-data-dir <path>` value, if provided — an explicit Chrome\n * user data dir, bypassing the named-profile convention entirely.\n */\n chromeDataDir?: string;\n /**\n * The `--aiui-browser-url <url>` value, if provided — attach the Chrome\n * DevTools MCP to this endpoint and manage no browser locally. This is the\n * flag `aiui browser --tunnel` prints for the remote side; it overrides\n * `chrome.browserUrl` in config for this launch.\n */\n browserUrl?: string;\n /**\n * Names collected from repeatable `--aiui-sidecar <name>` flags — session\n * sidecars to force-enable even when they wouldn't auto-detect for this\n * project (e.g. `--aiui-sidecar paint`).\n */\n sidecar: string[];\n /**\n * Names collected from repeatable `--aiui-no-sidecar <name>` flags — session\n * sidecars to disable for this run. Disable wins over enable.\n */\n noSidecar: string[];\n /**\n * The `--aiui-bind <loopback|host>` value, if provided — where the channel's\n * web backend binds for this launch, overriding `channel.bind` in config.\n * `host` is the trusted-LAN posture: the whole (unauthenticated) channel\n * surface, iPad paint page included, becomes reachable from the network.\n */\n bind?: ChannelBind;\n /** Everything else, to forward verbatim to the wrapped tool. */\n passthrough: string[];\n}\n\nconst AIUI_PREFIX = \"--aiui-\";\n\n/**\n * Detect a standalone `--help`/`-h` or `--version`/`-v` in a passthrough list.\n *\n * The wrapper commands treat these as **inert**: no config read, no browser or\n * Chrome-for-Testing activity, no channel discovery — just aiui's own\n * help/version, then the flag forwarded to the wrapped tool so its output\n * follows. Only exact argument matches count; a flag *value* that happens to\n * contain the text (e.g. `-p \"explain --help\"`) doesn't trigger it.\n */\nexport function infoFlag(passthrough: string[]): \"help\" | \"version\" | undefined {\n if (passthrough.includes(\"--help\") || passthrough.includes(\"-h\")) {\n return \"help\";\n }\n if (passthrough.includes(\"--version\") || passthrough.includes(\"-v\")) {\n return \"version\";\n }\n return undefined;\n}\n\n/**\n * Partition `args` into aiui's own options and the rest.\n *\n * Recognised aiui options:\n * - `--aiui-tag <tag>` / `--aiui-tag=<tag>` — the channel/MCP session tag,\n * forwarded to the channel server (and usable with `quick --tag`).\n * - `--aiui-mcp <tag>` / `--aiui-mcp=<tag>` — the tag of the running channel\n * MCP server to target (e.g. which session `aiui vite` should connect to).\n * - `--aiui-chrome` / `--aiui-no-chrome` — force the Chrome DevTools MCP on\n * (even under CI) / leave it off. Passing both is an error.\n * - `--aiui-browser` / `--aiui-no-browser` — force opening the page in the\n * session browser (even in CI/headless environments) / never open one.\n * Passing both is an error.\n * - `--aiui-chrome-profile <name>` — launch Chrome with the named profile\n * (created on first use under `.aiui-cache/chrome/<name>`).\n * - `--aiui-chrome-data-dir <path>` — launch Chrome with an explicit user data\n * dir instead of a named profile. Mutually exclusive with the above.\n * - `--aiui-browser-url <url>` — attach the Chrome DevTools MCP to this\n * endpoint (e.g. a tunneled remote browser) instead of managing one.\n * - `--aiui-sidecar <name>` / `--aiui-no-sidecar <name>` — force-enable /\n * disable a session sidecar by name. Both are repeatable and accumulate;\n * disable wins over enable when the same name appears in both.\n * - `--aiui-bind <loopback|host>` — where the channel's web backend binds for\n * this launch (see `channel.bind` in the config guide). Any other value is\n * an error.\n *\n * Any other `--aiui-*` flag throws, so a typo surfaces loudly instead of being\n * silently dropped or leaking into the child command.\n */\nexport function splitAiuiArgs(args: string[]): AiuiArgs {\n let tag: string | undefined;\n let mcp: string | undefined;\n let chrome = false;\n let noChrome = false;\n let browser = false;\n let noBrowser = false;\n let chromeProfile: string | undefined;\n let chromeDataDir: string | undefined;\n let browserUrl: string | undefined;\n const sidecar: string[] = [];\n const noSidecar: string[] = [];\n let bind: ChannelBind | undefined;\n const passthrough: string[] = [];\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (!arg.startsWith(AIUI_PREFIX)) {\n passthrough.push(arg);\n continue;\n }\n\n const eq = arg.indexOf(\"=\");\n const name = eq === -1 ? arg : arg.slice(0, eq);\n let value = eq === -1 ? undefined : arg.slice(eq + 1);\n\n switch (name) {\n case \"--aiui-tag\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-tag requires a non-empty value\");\n }\n tag = value;\n break;\n }\n case \"--aiui-mcp\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-mcp requires a non-empty value\");\n }\n mcp = value;\n break;\n }\n case \"--aiui-chrome\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-chrome takes no value\");\n }\n chrome = true;\n break;\n }\n case \"--aiui-no-chrome\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-no-chrome takes no value\");\n }\n noChrome = true;\n break;\n }\n case \"--aiui-browser\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-browser takes no value\");\n }\n browser = true;\n break;\n }\n case \"--aiui-no-browser\": {\n if (value !== undefined) {\n throw new Error(\"--aiui-no-browser takes no value\");\n }\n noBrowser = true;\n break;\n }\n case \"--aiui-chrome-profile\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-chrome-profile requires a non-empty value\");\n }\n chromeProfile = value;\n break;\n }\n case \"--aiui-chrome-data-dir\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-chrome-data-dir requires a non-empty value\");\n }\n chromeDataDir = value;\n break;\n }\n case \"--aiui-browser-url\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-browser-url requires a non-empty value\");\n }\n browserUrl = value;\n break;\n }\n case \"--aiui-sidecar\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-sidecar requires a non-empty value\");\n }\n sidecar.push(value);\n break;\n }\n case \"--aiui-no-sidecar\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value) {\n throw new Error(\"--aiui-no-sidecar requires a non-empty value\");\n }\n noSidecar.push(value);\n break;\n }\n case \"--aiui-bind\": {\n if (value === undefined) {\n value = args[++i];\n }\n if (!value || !(CHANNEL_BINDS as readonly string[]).includes(value)) {\n throw new Error(`--aiui-bind requires one of: ${CHANNEL_BINDS.join(\", \")}`);\n }\n bind = value as ChannelBind;\n break;\n }\n default:\n throw new Error(`unknown aiui option: ${name}`);\n }\n }\n\n if (chrome && noChrome) {\n throw new Error(\"--aiui-chrome and --aiui-no-chrome are mutually exclusive\");\n }\n if (browser && noBrowser) {\n throw new Error(\"--aiui-browser and --aiui-no-browser are mutually exclusive\");\n }\n if (chromeProfile !== undefined && chromeDataDir !== undefined) {\n throw new Error(\"--aiui-chrome-profile and --aiui-chrome-data-dir are mutually exclusive\");\n }\n if (browserUrl !== undefined && (chromeProfile !== undefined || chromeDataDir !== undefined)) {\n throw new Error(\n \"--aiui-browser-url means the browser is managed elsewhere — it can't be combined \" +\n \"with --aiui-chrome-profile or --aiui-chrome-data-dir\",\n );\n }\n\n return {\n tag,\n mcp,\n chrome,\n noChrome,\n browser,\n noBrowser,\n chromeProfile,\n chromeDataDir,\n browserUrl,\n sidecar,\n noSidecar,\n bind,\n passthrough,\n };\n}\n","/**\n * Best-effort auto-dismiss of Claude's custom-channel acknowledgement prompt.\n *\n * When `aiui claude` opts a session into our development channel\n * (`--dangerously-load-development-channels`), Claude shows a prompt at startup\n * asking the user to confirm they're using it for local development. It's the\n * same keypress every time, so we press Enter for them.\n *\n * How: `aiui` and the `claude` it spawns share one controlling terminal. A\n * short-lived `perl` helper opens that terminal (`/dev/tty`) and pushes a\n * carriage return into its input queue via the TIOCSTI ioctl — exactly as if\n * the user had typed Enter. Claude, the foreground reader, consumes it. We\n * target the terminal, not Claude's PID, which is both simpler and more robust.\n *\n * Deliberately best-effort and silent — in every failure the user just presses\n * Enter themselves, so we never surface an error:\n * - Only macOS and Linux have known TIOCSTI request numbers; elsewhere we skip.\n * - Modern Linux (≥6.2) disables TIOCSTI by default (`dev.tty.legacy_tiocsti=0`),\n * so the ioctl returns EPERM — a no-op.\n * - No `perl` on PATH, no controlling tty, etc. → also a no-op.\n *\n * Safety if the prompt ever goes away (say Claude adds a skip flag): a stray\n * Enter at Claude's empty main input is a no-op submit, so an unneeded nudge\n * does no harm. The attempts are also kept early (before a user could plausibly\n * have dismissed the prompt and started typing a real message) so we don't race\n * their input. Whether to nudge at all is the user's saved first-run choice\n * (`claude.enterNudge` in config.json) — the caller gates on it.\n */\nimport { spawn } from \"node:child_process\";\n\n// TIOCSTI ioctl request number by platform (\"push one byte into the tty input\n// queue as if typed\"). darwin: _IOW('t',114,char) = 0x80017472; linux: 0x5412.\nconst TIOCSTI_BY_PLATFORM: Partial<Record<NodeJS.Platform, number>> = {\n darwin: 0x80017472,\n linux: 0x5412,\n};\n\n// perl one-liner (perl ships with a builtin ioctl(), avoiding a native addon):\n// open the controlling terminal and inject a carriage return. `$ARGV[0]` is the\n// platform's TIOCSTI number. Any failure — a locked-down kernel, no tty — exits\n// quietly. Passed as a single argv entry (no shell), so `\\r` needs no escaping\n// beyond the JS string literal.\nconst PERL_INJECT = 'open(my $t,\"+<\",\"/dev/tty\") or exit 0; my $c=\"\\\\r\"; ioctl($t,$ARGV[0]+0,$c);';\n\n/** Delays (ms after spawn) at which to attempt the keypress. */\nconst DEFAULT_DELAYS_MS = [250, 750];\n\n/**\n * Schedule a couple of best-effort Enter keypresses into the controlling\n * terminal to dismiss the channel prompt. Returns immediately; the attempts run\n * on unref'd timers so they never hold the CLI open, and every error is\n * swallowed. Call this only for an interactive session (see the caller).\n */\nexport function nudgeChannelAck(delaysMs: number[] = DEFAULT_DELAYS_MS): void {\n const tiocsti = TIOCSTI_BY_PLATFORM[process.platform];\n if (tiocsti === undefined) {\n return;\n }\n\n for (const ms of delaysMs) {\n const timer = setTimeout(() => {\n try {\n const child = spawn(\"perl\", [\"-e\", PERL_INJECT, String(tiocsti)], { stdio: \"ignore\" });\n child.on(\"error\", () => {}); // no perl on PATH, etc. — ignore\n } catch {\n // ignore — the user can always press Enter themselves\n }\n }, ms);\n timer.unref();\n }\n}\n","/**\n * First-run choices: settings that deserve a deliberate answer, not a silent\n * default.\n *\n * Three of `aiui claude`'s behaviors are pure personal preference with real\n * consequences: whether to launch with `--dangerously-skip-permissions`,\n * whether to auto-dismiss the development-channel acknowledgement prompt by\n * typing into the user's terminal, and whether the channel's web server binds\n * loopback-only or the host interface (the trusted-LAN posture that makes the\n * whole unauthenticated surface — iPad paint page included — reachable from\n * the network). None should be something the user \"tagged along\" with because\n * a default existed — so the first interactive launch asks (definitively: the\n * prompts have no Enter-through default), and the answers persist to the\n * **user-level** config, after which nothing asks again. Non-interactive\n * sessions never prompt; unset values fall back to the documented defaults\n * (skip: true, nudge: true, bind: loopback).\n */\nimport { type AiuiConfig, type ChannelBind, updateUserConfig } from \"./config\";\nimport { type Choice, choose } from \"./prompt\";\nimport { printNote } from \"./ui\";\n\n/** Injectable for tests; matches {@link choose} without a default key. */\ntype Ask = (question: string, choices: Choice[]) => Promise<string>;\n\nconst SKIP_PERMISSIONS_QUESTION =\n \"One-time setup — how should aiui launch Claude Code?\\n\" +\n \"With --dangerously-skip-permissions, every agent action (shell commands, file writes,\\n\" +\n \"network, the browser) runs without asking you first. Fast, and dangerous. It's a personal\\n\" +\n \"preference — aiui works fine either way. Saved as claude.skipPermissions in your user\\n\" +\n \"config; edit or delete it there to change your mind.\";\n\nconst CHANNEL_BIND_QUESTION =\n \"One-time setup — where should the channel's web server bind?\\n\" +\n \"Binding the HOST interface puts the session's whole web surface on your network,\\n\" +\n \"UNAUTHENTICATED — the iPad paint page (`aiui paint url` prints its URL), but also prompt\\n\" +\n \"injection, /debug, and every sidecar. That's the simple, single-port way to use the iPad —\\n\" +\n \"on a network that is yours alone (a home LAN), not on café Wi-Fi. LOOPBACK keeps everything\\n\" +\n \"this-machine-only; reaching it from an iPad is then up to you — tunnel the channel port\\n\" +\n \"however you like (Tailscale, `ssh -L`). Saved as channel.bind in your user config;\\n\" +\n \"--aiui-bind wins per launch.\";\n\nconst ENTER_NUDGE_QUESTION =\n \"One-time setup — auto-dismiss Claude Code's channel prompt?\\n\" +\n \"aiui loads a custom development channel, so Claude Code shows a one-key acknowledgement\\n\" +\n \"prompt at every startup. aiui can dismiss it for you: shortly after launch it injects a\\n\" +\n \"single Enter keystroke into this terminal (a best-effort TIOCSTI ioctl on /dev/tty — it\\n\" +\n 'literally \"types\" the Enter for you; on platforms that forbid that, nothing happens and\\n' +\n \"you press it yourself). Saying no just means pressing Enter once per launch. Saved as\\n\" +\n \"claude.enterNudge in your user config.\";\n\n/**\n * Ask (once, ever) for any first-run choice that isn't already configured, and\n * return the config with the answers applied. Call only from an interactive\n * session.\n */\nexport async function ensureLaunchChoices(\n config: AiuiConfig,\n ask: Ask = choose,\n): Promise<AiuiConfig> {\n let updated = config;\n\n if (updated.claude?.skipPermissions === undefined) {\n const answer = await ask(SKIP_PERMISSIONS_QUESTION, [\n { key: \"y\", label: \"yes — skip permissions; nothing asks before acting\" },\n { key: \"n\", label: \"no — keep Claude Code's own permission prompts\" },\n ]);\n updated = persist(updated, \"skipPermissions\", answer === \"y\");\n }\n\n if (updated.claude?.enterNudge === undefined) {\n const answer = await ask(ENTER_NUDGE_QUESTION, [\n { key: \"y\", label: \"yes — press Enter for me at startup\" },\n { key: \"n\", label: \"no — I'll press it myself each launch\" },\n ]);\n updated = persist(updated, \"enterNudge\", answer === \"y\");\n }\n\n if (updated.channel?.bind === undefined) {\n const answer = await ask(CHANNEL_BIND_QUESTION, [\n { key: \"h\", label: \"host — reachable on my (trusted) network; the iPad just works\" },\n { key: \"l\", label: \"loopback — this machine only; I'll tunnel when I want the iPad\" },\n ]);\n updated = persistBind(updated, answer === \"h\" ? \"host\" : \"loopback\");\n }\n\n return updated;\n}\n\nfunction persist(\n config: AiuiConfig,\n key: \"skipPermissions\" | \"enterNudge\",\n value: boolean,\n): AiuiConfig {\n const file = updateUserConfig((c) => {\n c.claude = { ...c.claude, [key]: value };\n });\n printNote(`wrote claude.${key}: ${value} to ${file}`);\n return { ...config, claude: { ...config.claude, [key]: value } };\n}\n\nfunction persistBind(config: AiuiConfig, value: ChannelBind): AiuiConfig {\n const file = updateUserConfig((c) => {\n c.channel = { ...c.channel, bind: value };\n });\n printNote(`wrote channel.bind: ${value} to ${file}`);\n return { ...config, channel: { ...config.channel, bind: value } };\n}\n","/**\n * OpenAI key preflight for `aiui claude`.\n *\n * Once the multimodal intent modality is the overlay's default, its speech\n * transcription and correction-diff calls need an OpenAI key — and they run in\n * the *channel* process, which inherits the environment `aiui claude` launches\n * in. The adopted key story (see the multimodal-intent-graduation handoff) is\n * deliberately narrow: the key comes from **`OPENAI_API_KEY` in the\n * environment**, never from `config.json` (a shareable, eventually-committed\n * file must not hold secrets) and never from an `aiui claude` flag.\n *\n * So the launcher's whole job is to *preflight* that env var and tell the user\n * what it found — degradation, not refusal. A missing or rejected key never\n * blocks the launch; the modality still mounts, but transcription/correction are\n * unavailable until the key is set (the widget says so — `mock` is the explicit\n * offline choice, not a silent fallback). The most valuable case this catches is the one that\n * bit the bench twice (see workbench field-notes, \"Keys & config\"): a **stale\n * shell export** shadowing the real key, which surfaces as a confusing 401 deep\n * in the pipeline rather than a clear message up front.\n *\n * We record only a {@link OpenAiKeyStatus} — never the key or any prefix of it —\n * so the launch-info summary (and the DevTools panel that renders it) can\n * explain a degraded pipeline without ever seeing the secret.\n */\nimport type { OpenAiKeyStatus } from \"@habemus-papadum/aiui-claude-channel\";\nimport { printNote, printWarning } from \"./ui\";\n\nexport type { OpenAiKeyStatus };\n\n/** OpenAI's cheapest authenticated endpoint — we read the status, never the body. */\nconst MODELS_URL = \"https://api.openai.com/v1/models\";\n\n/** Keep the preflight off the critical path: a slow network can't stall launch. */\nconst DEFAULT_TIMEOUT_MS = 3000;\n\nexport interface PreflightOptions {\n /**\n * Perform the authenticated network check. True only for interactive,\n * non-CI launches (the same gate as the Chrome-for-Testing prompts). When\n * false — CI or any non-interactive session — the check is skipped silently\n * and a present key is reported as \"unverified\", never contacted.\n */\n verify?: boolean;\n /** Injectable for tests; defaults to the process environment. */\n env?: NodeJS.ProcessEnv;\n /** Injectable for tests; defaults to global `fetch`. */\n fetchImpl?: typeof fetch;\n /** Network budget for the status check (default {@link DEFAULT_TIMEOUT_MS}). */\n timeoutMs?: number;\n}\n\n/**\n * Determine the status of `OPENAI_API_KEY` for this launch.\n *\n * Never throws, never prints, never returns the key. Absent env var →\n * \"missing\". Present but `verify` off → \"unverified\" (no network touched).\n * Present and verifying → a single `GET /v1/models` with the bearer, read for\n * **status only**: 2xx → \"valid\", 401/403 → \"invalid\", anything else (5xx,\n * 429, a network error, or the timeout) → \"unverified\" — a key we couldn't\n * confirm is not a key we condemn.\n */\nexport async function preflightOpenAiKey(opts: PreflightOptions = {}): Promise<OpenAiKeyStatus> {\n const {\n verify = true,\n env = process.env,\n fetchImpl = fetch,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n } = opts;\n\n const key = env.OPENAI_API_KEY?.trim();\n if (!key) {\n return \"missing\";\n }\n if (!verify) {\n return \"unverified\";\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetchImpl(MODELS_URL, {\n headers: { authorization: `Bearer ${key}` },\n signal: controller.signal,\n });\n if (res.ok) {\n return \"valid\";\n }\n if (res.status === 401 || res.status === 403) {\n return \"invalid\";\n }\n // 5xx / 429 / anything else: OpenAI didn't confirm *or* condemn the key.\n return \"unverified\";\n } catch {\n // Offline, DNS failure, TLS error, or our own abort (timeout): unverified.\n return \"unverified\";\n } finally {\n clearTimeout(timer);\n }\n}\n\ninterface PreflightMessage {\n level: \"warn\" | \"note\";\n title: string;\n detail: string;\n}\n\n/**\n * The user-facing message for a preflight status, or `null` when there's\n * nothing to say. A valid key is silent — the launcher's terminal stays quiet\n * until something's actually wrong (the same posture as {@link printNote} &c).\n * The copy is data (not printed here) so it can be unit-tested per case.\n */\nexport function openAiPreflightMessage(status: OpenAiKeyStatus): PreflightMessage | null {\n switch (status) {\n case \"valid\":\n return null;\n case \"missing\":\n return {\n level: \"warn\",\n title: \"OPENAI_API_KEY is not set — the intent pipeline will run degraded\",\n detail:\n \"Speech transcription and dictation correction are unavailable — the overlay says so when \" +\n \"you try to dictate. To enable them, export the key in the shell you run `aiui claude` from:\\n\" +\n \" export OPENAI_API_KEY=sk-…\\n\" +\n \"It flows through to the channel process, which is where those calls happen. (For offline \" +\n \"work, switch the overlay to the mock backends — see the intent-overlay guide.)\",\n };\n case \"invalid\":\n return {\n level: \"warn\",\n title:\n \"OPENAI_API_KEY was rejected by OpenAI (401) — the intent pipeline will run degraded\",\n detail:\n \"The key in your environment isn't valid. The usual cause is a stale shell export \" +\n \"shadowing your real key — check what's actually set (this prints only a short prefix, \" +\n \"not the whole secret):\\n\" +\n \" echo $OPENAI_API_KEY | head -c 12\\n\" +\n \"and compare that against the start of your real key. Until it's fixed, transcription \" +\n \"and correction are unavailable.\",\n };\n case \"unverified\":\n return {\n level: \"note\",\n title: \"couldn't verify OPENAI_API_KEY with OpenAI — continuing\",\n detail:\n \"The check didn't complete (offline, a timeout, or a transient OpenAI error), so the \" +\n \"key is unverified — not known-bad. Launch continues; if transcription and correction \" +\n \"turn out unavailable, an unreachable or invalid key may be why.\",\n };\n }\n}\n\n/** Print the preflight message for `status` (nothing, for a valid key). */\nexport function reportOpenAiPreflight(status: OpenAiKeyStatus): void {\n const message = openAiPreflightMessage(status);\n if (!message) {\n return;\n }\n if (message.level === \"warn\") {\n printWarning(message.title, message.detail);\n } else {\n printNote(message.title, message.detail);\n }\n}\n","// Injected at build time by Vite's `define` (see vite.config.ts). The `typeof`\n// guard is a no-op in the built CLI (where the define replaces it with a string\n// literal) but keeps this working anywhere the define isn't applied.\ndeclare const __AIUI_VERSION__: string;\n\n/** This aiui build's version. */\nexport const VERSION = typeof __AIUI_VERSION__ === \"string\" ? __AIUI_VERSION__ : \"0.0.0+dev\";\n","import { accessSync, constants } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\n\n/**\n * Return true if `command` resolves to an executable on the PATH.\n *\n * A dependency-free `which`: it scans each PATH entry for an executable file\n * (honouring `PATHEXT` on Windows). This is the first of what will be several\n * environment checks the launcher runs before shelling out to `claude`.\n */\nexport function commandExists(command: string): boolean {\n const dirs = (process.env.PATH ?? \"\").split(delimiter).filter(Boolean);\n const exts =\n process.platform === \"win32\" ? (process.env.PATHEXT ?? \".EXE;.CMD;.BAT;.COM\").split(\";\") : [\"\"];\n for (const dir of dirs) {\n for (const ext of exts) {\n try {\n accessSync(join(dir, command + ext), constants.X_OK);\n return true;\n } catch {\n // Not executable here — keep scanning.\n }\n }\n }\n return false;\n}\n","import { mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { ChromeDevtoolsInfo, LaunchInfo } from \"@habemus-papadum/aiui-claude-channel\";\nimport {\n discoverSessionBrowser,\n isCi,\n launchSessionBrowser,\n sessionBrowserBinary,\n} from \"@habemus-papadum/aiui-util\";\nimport { execa } from \"execa\";\nimport { type AiuiArgs, infoFlag, splitAiuiArgs } from \"../util/aiui-args\";\nimport { syncChromeForTesting } from \"../util/cft\";\nimport {\n buildDevtoolsExtension,\n CHROME_SERVER_ID,\n chromeDevtoolsEnabled,\n chromeMcpAttachServer,\n chromeMcpServer,\n devtoolsExtensionDir,\n maybeExtensionAutoloadHint,\n resolveChromeSettings,\n} from \"../util/chrome\";\nimport { type AiuiConfig, loadAiuiConfig } from \"../util/config\";\nimport { nudgeChannelAck } from \"../util/enter-nudge\";\nimport { ensureLaunchChoices } from \"../util/first-run\";\nimport { preflightOpenAiKey, reportOpenAiPreflight } from \"../util/openai-preflight\";\nimport { packageRoot, resolvePackageCli } from \"../util/resolve-cli\";\nimport { resolveSidecars } from \"../util/sidecars\";\nimport { printError, printWarning } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\nimport { commandExists } from \"../util/which\";\n\nconst CHANNEL_PKG = \"@habemus-papadum/aiui-claude-channel\";\nconst PLUGIN_PKG = \"@habemus-papadum/aiui-claude-plugin\";\n\n// The inline MCP server id for our custom channel. It is reused twice: as the\n// key under `mcpServers` in `--mcp-config`, and as the `server:<id>` entry that\n// opts the session into loading the development channel.\nconst CHANNEL_SERVER_ID = \"aiui\";\n\n/**\n * Launch Claude Code wired up with the aiui channel, plugin, and (by default)\n * the Chrome DevTools MCP.\n *\n * Builds a `claude` command line and hands the terminal over to it. The plugin\n * directories and the channel CLI are resolved from their dependencies to\n * absolute paths — no PATH lookups. In a dev checkout the channel runs straight\n * from its TypeScript source via tsx (no build step), and when installed from\n * npm it runs the built `dist` entry; see {@link resolvePackageCli}. Only\n * `claude` itself is checked on the PATH, since everything here launches it.\n *\n * Args are split into aiui's own options (those beginning with `--aiui-`) and\n * the rest, which forward verbatim to `claude`. So `aiui claude --resume` passes\n * `--resume` through, while `aiui claude --aiui-tag <uuid>` is consumed here to\n * tag the channel session (letting a test harness address the exact MCP server\n * it spawned via `quick --tag`). When no tag is given the channel server mints\n * its own UUID.\n *\n * `--help` and `--version` are inert: aiui's own answer prints first, then the\n * flag forwards to claude so its output follows — and none of the launch\n * machinery (config, Chrome for Testing, session browser, channel) runs.\n */\nexport async function runClaude(rawArgs: string[] = []): Promise<void> {\n const aiuiArgs = splitAiuiArgs(rawArgs);\n const { tag, passthrough } = aiuiArgs;\n\n // `--help` / `--version` are inert: print aiui's own answer, then forward\n // the flag so claude's follows — two outputs back to back, and none of the\n // launch machinery (config, browser, Chrome for Testing, channel) runs.\n const info = infoFlag(passthrough);\n if (info) {\n if (info === \"help\") {\n printClaudeWrapperHelp();\n } else {\n console.log(`aiui ${VERSION}`);\n }\n await forwardToClaude(passthrough);\n return;\n }\n // Settings from ~/.cache/aiui/config.json + .aiui-cache/config.json (project\n // wins per key; flags win over both) — see util/config and docs/guide/config.\n let config = loadAiuiConfig();\n\n // A real TTY on both ends, not print mode, not CI: the only context where\n // aiui may prompt (first-run choices, CfT offers) or type into the terminal.\n const interactive = isInteractiveSession(passthrough) && !isCi();\n if (interactive) {\n // Settings that deserve a deliberate answer — skip-permissions, the enter\n // nudge, and the channel bind — are asked once, definitively, and persisted\n // to the user config; every later launch reads the choice silently.\n config = await ensureLaunchChoices(config);\n }\n\n // Preflight the OpenAI key the intent pipeline needs (transcription +\n // correction, which run in the spawned channel process — the key reaches\n // them through this environment). Interactive launches verify it against the\n // API and report any degradation once; CI/non-interactive only note presence\n // without touching the network. Either way the launch proceeds — a bad or\n // missing key leaves transcription/correction unavailable (the widget says\n // so; mock is the explicit offline choice), never a refusal. We keep only the\n // status (never the key) to thread into launch-info below.\n const openaiKey = await preflightOpenAiKey({ verify: interactive });\n if (interactive) {\n reportOpenAiPreflight(openaiKey);\n }\n\n if (!ensureClaudeOnPath()) {\n return;\n }\n\n // Plugins ship in the plugin package's marketplace/ (in both dev and\n // installed layouts). They're loaded directly with repeated `--plugin-dir`\n // flags — the marketplace manifest exists for marketplace installs later,\n // not as a required indirection here.\n const pluginsRoot = resolve(packageRoot(PLUGIN_PKG), \"marketplace\", \"plugins\");\n\n // Resolve how to run the channel CLI (tsx-from-source in dev, dist when\n // installed) and append its `mcp` subcommand. A user-supplied `--aiui-tag`\n // is forwarded as the server's `--tag`; without one the server generates its\n // own UUID.\n const channel = resolvePackageCli(CHANNEL_PKG);\n const mcpArgs = [...channel.args, \"mcp\"];\n if (tag) {\n mcpArgs.push(\"--tag\", tag);\n }\n // Where the channel's web backend binds. loopback (the default) keeps every\n // route this-machine-only; host puts the whole unauthenticated surface —\n // iPad paint page, prompt injection, /debug — on the network (the trusted-LAN\n // posture; asked at first run, see docs/guide/warning). Flag, then config.\n const bind = aiuiArgs.bind ?? config.channel?.bind ?? \"loopback\";\n mcpArgs.push(\"--bind\", bind);\n const mcpServers: Record<string, { command: string; args: string[] }> = {\n [CHANNEL_SERVER_ID]: { command: channel.command, args: mcpArgs },\n };\n\n // By default the session also gets the Chrome DevTools MCP — off under CI,\n // `--aiui-no-chrome`, or `chrome.enabled: false`. In the default \"attach\"\n // mode the MCP shares a user-visible session browser (see aiui-util's\n // browser module);\n // \"launch\" mode keeps the browser private to the MCP and lazily started.\n let chromeInfo: ChromeDevtoolsInfo = { enabled: false };\n if (chromeDevtoolsEnabled(aiuiArgs, config.chrome)) {\n // `--aiui-browser-url` (printed by `aiui browser --tunnel` for the remote\n // side) beats a configured chrome.browserUrl for this launch.\n const chromeCfg = {\n ...config.chrome,\n ...(aiuiArgs.browserUrl ? { browserUrl: aiuiArgs.browserUrl } : {}),\n };\n const chrome = await chromeServerEntry(aiuiArgs, chromeCfg, interactive);\n mcpServers[CHROME_SERVER_ID] = chrome.entry;\n chromeInfo = chrome.info;\n }\n // Tell the channel server how this session was assembled. It surfaces this\n // at /debug/api/info, and the DevTools panel's Server tab renders it — the\n // first place to look when browser/MCP connectivity misbehaves.\n const launchInfo: LaunchInfo = {\n launcher: \"aiui claude\",\n chromeDevtools: chromeInfo,\n openaiKey,\n };\n mcpArgs.push(\"--launch-info\", JSON.stringify(launchInfo));\n\n // Tell the channel which session sidecars to host. The channel process\n // inherits this session's cwd, so the project root is process.cwd(). Paint\n // is always on (it rides the channel port — reachability is the bind's\n // decision above). Three tiers, per name: `--aiui-sidecar` /\n // `--aiui-no-sidecar` flags win, then the `sidecars.*` config, then\n // auto-detection.\n const enable = [...aiuiArgs.sidecar];\n const disable = [...aiuiArgs.noSidecar];\n for (const [name, on] of Object.entries(config.sidecars ?? {})) {\n if (on === undefined || enable.includes(name) || disable.includes(name)) {\n continue; // a per-launch flag beats the durable setting\n }\n (on ? enable : disable).push(name);\n }\n const sidecars = resolveSidecars(process.cwd(), { enable, disable });\n if (sidecars.length > 0) {\n mcpArgs.push(\"--sidecars\", JSON.stringify(sidecars));\n }\n const mcpConfig = JSON.stringify({ mcpServers });\n\n // The base aiui plugin and the frontend-design principles always load. The\n // session-browser skill is an add-on for the Chrome DevTools MCP — etiquette\n // for driving the *shared* browser — so the session is lightened by leaving\n // it out whenever that MCP isn't attached.\n const plugins = [join(pluginsRoot, \"aiui\"), join(pluginsRoot, \"frontend-design\")];\n if (chromeInfo.enabled) {\n plugins.push(join(pluginsRoot, \"session-browser\"));\n }\n\n // We don't add `--chrome` or `--no-chrome`: whether to use Claude's own\n // browser integration is the user's call, forwarded via passthrough (e.g.\n // `aiui claude --chrome`) — it is independent of the Chrome DevTools MCP\n // above. Automated/CI contexts pass `--no-chrome` themselves (see the e2e\n // test harness) to skip the browser-detection startup prompt.\n // Skipping permissions is still the (documented, dangerous) default, but no\n // longer hard-coded: `claude.skipPermissions: false` in config.json opts out\n // and leaves Claude's own permission behavior in charge.\n const skipPermissions = config.claude?.skipPermissions ?? true;\n const args = [\n ...(skipPermissions ? [\"--dangerously-skip-permissions\"] : []),\n \"--mcp-config\",\n mcpConfig,\n ...plugins.flatMap((dir) => [\"--plugin-dir\", dir]),\n // Custom channels are a research preview and not on the approved allowlist,\n // so opt this session into loading ours as a development channel.\n \"--dangerously-load-development-channels\",\n `server:${CHANNEL_SERVER_ID}`,\n ];\n\n // Loading our development channel makes Claude show a one-key acknowledgement\n // prompt at startup. Whether aiui best-effort presses Enter on the user's\n // behalf is their saved first-run choice (claude.enterNudge; see\n // nudgeChannelAck for the mechanism). Never outside an interactive TTY — the\n // prompt only appears in the interactive TUI, and the e2e harness drives its\n // own keypresses over tmux.\n if (interactive && (config.claude?.enterNudge ?? true)) {\n nudgeChannelAck();\n }\n\n // Hand the terminal over to Claude. stdio:\"inherit\" so the session owns the\n // terminal (and, when spawned by the test harness, so Claude's stdio is the\n // harness's captured pipes). reject:false so an interrupted/non-zero Claude\n // exit becomes our exit code rather than a thrown error.\n const result = await execa(\"claude\", [...args, ...passthrough], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** Check for `claude`; print the friendly install pointer when missing. */\nfunction ensureClaudeOnPath(): boolean {\n if (commandExists(\"claude\")) {\n return true;\n }\n printError(\n \"`claude` was not found on your PATH\",\n \"Install Claude Code and make sure the `claude` command is available, then try again.\",\n );\n process.exitCode = 1;\n return false;\n}\n\n/** Run claude with the args verbatim (the --help/--version forward). */\nasync function forwardToClaude(args: string[]): Promise<void> {\n if (!ensureClaudeOnPath()) {\n return;\n }\n const result = await execa(\"claude\", args, { stdio: \"inherit\", reject: false });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** The aiui half of `aiui claude --help` (claude's own --help follows it). */\nfunction printClaudeWrapperHelp(): void {\n console.log(`aiui claude — launch Claude Code wired with the aiui channel, plugin, and browser MCP\n\naiui's own flags (everything else forwards to claude verbatim):\n --aiui-tag <tag> tag the channel session (e.g. for \\`quick --tag\\`)\n --aiui-chrome force the Chrome DevTools MCP on (even under CI)\n --aiui-no-chrome launch without the Chrome DevTools MCP\n --aiui-chrome-profile <name> browser profile at .aiui-cache/chrome/<name>\n --aiui-chrome-data-dir <path> explicit browser user data dir\n --aiui-browser-url <url> attach to a browser at this DevTools endpoint\n (e.g. a tunnel from \\`aiui browser --tunnel\\`)\n --aiui-bind <loopback|host> where the channel's web server binds: loopback\n (this machine only, the default) or host (your\n whole network can reach the session's web\n surface — the iPad paint page included;\n trusted networks only)\n --aiui-sidecar <name> host this session sidecar (repeatable);\n \\`paint\\` (iPad ink) is always on\n --aiui-no-sidecar <name> don't host this session sidecar (repeatable)\n\nDurable settings live in config.json (project .aiui-cache/ + user cache) — see the\nConfiguration guide. What follows is claude's own --help:\n`);\n}\n\n/**\n * Assemble the chrome-devtools MCP entry for this launch.\n *\n * The decision ladder:\n * 1. `chrome.browserUrl` configured → attach verbatim. The browser lives\n * elsewhere (typically another machine, tunneled — see docs/guide/remote);\n * nothing local is managed: no CfT sync, no profile, no extension.\n * 2. Attach mode with a session browser already running on this profile →\n * attach to it (works non-interactively too; discovery is read-only).\n * 3. Attach mode, interactive → start the session browser now (CfT-preferred,\n * devtools extension loaded, visible from t0) and attach. On failure, warn\n * and fall through.\n * 4. Otherwise — `mode: \"launch\"`, a non-interactive session with nothing\n * running, or a failed start — classic launch mode: chrome-devtools-mcp\n * starts its own private browser lazily, on the agent's first tool call.\n */\nasync function chromeServerEntry(\n aiuiArgs: AiuiArgs,\n chromeCfg: NonNullable<AiuiConfig[\"chrome\"]>,\n interactive: boolean,\n): Promise<{ entry: { command: string; args: string[] }; info: ChromeDevtoolsInfo }> {\n if (chromeCfg.browserUrl) {\n return {\n entry: chromeMcpAttachServer(chromeCfg.browserUrl),\n info: { enabled: true, connection: \"attach\", browserUrl: chromeCfg.browserUrl },\n };\n }\n\n let cfg = { ...chromeCfg };\n let settings = resolveChromeSettings(aiuiArgs, cfg);\n\n if (settings.mode === \"attach\") {\n const running = await discoverSessionBrowser(settings.userDataDir);\n if (running) {\n return {\n entry: chromeMcpAttachServer(running.browserUrl),\n info: {\n enabled: true,\n connection: \"attach\",\n browserUrl: running.browserUrl,\n userDataDir: settings.userDataDir,\n },\n };\n }\n }\n\n // From here a browser will be launched one way or the other — pick the\n // binary. Chrome for Testing is the recommended default: unless config\n // names a browser explicitly, prefer the managed install (and offer to\n // install/update it interactively — see syncChromeForTesting).\n if (!cfg.executablePath && !cfg.channel) {\n const cft = await syncChromeForTesting({ mode: cfg.forTesting ?? \"prompt\", interactive });\n if (cft) {\n cfg = { ...cfg, executablePath: cft };\n settings = resolveChromeSettings(aiuiArgs, cfg);\n }\n }\n mkdirSync(settings.userDataDir, { recursive: true });\n if (settings.buildExtension) {\n await buildDevtoolsExtension();\n }\n const extensionDir = devtoolsExtensionDir();\n if (interactive) {\n maybeExtensionAutoloadHint(settings, extensionDir);\n }\n const browserInfo = {\n userDataDir: settings.userDataDir,\n executablePath: settings.executablePath,\n channel: settings.channel,\n headless: settings.headless,\n extensionDir,\n };\n\n if (settings.mode === \"attach\" && interactive) {\n try {\n const session = await launchSessionBrowser({\n binary: sessionBrowserBinary(settings),\n userDataDir: settings.userDataDir,\n debugPort: settings.debugPort,\n extensionDir,\n headless: settings.headless,\n });\n return {\n entry: chromeMcpAttachServer(session.browserUrl),\n info: {\n enabled: true,\n connection: \"attach\",\n browserUrl: session.browserUrl,\n ...browserInfo,\n },\n };\n } catch (error) {\n printWarning(\n \"couldn't start the session browser — falling back to a browser private to the MCP\",\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n return {\n entry: chromeMcpServer(settings, extensionDir),\n info: { enabled: true, connection: \"launch\", ...browserInfo },\n };\n}\n\n/**\n * Whether this invocation will bring up Claude's interactive TUI — the only\n * context where the channel acknowledgement prompt appears. Requires a real\n * terminal on both ends and no print-mode flag.\n */\nexport function isInteractiveSession(passthrough: string[]): boolean {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n return false;\n }\n return !passthrough.some((arg) => arg === \"-p\" || arg === \"--print\");\n}\n","/**\n * `aiui clean` — reset aiui's on-disk state for a clean-slate demo.\n *\n * aiui keeps all of its state in two cache roots — never in `node_modules`, so a\n * plain reinstall of the package leaves everything below behind:\n *\n * <repo>/.aiui-cache/ traces, recordings, the session-browser profile(s),\n * and the project-level config.json (see projectCacheDir)\n * <user cache>/ ~/.cache/aiui — the user config.json (incl. the\n * persisted `claude.skipPermissions` first-run answer),\n * the running-server registry (mcp/), remote-tunnel\n * browser-profiles/, and the ~160 MB managed Chrome for\n * Testing install (chrome/)\n *\n * `clean` removes both so the next `aiui claude` behaves like a fresh install:\n * the first-run permission prompt returns, the Chrome for Testing offer returns,\n * and traces + browser logins are gone. Re-showing the CfT download is the whole\n * reason the browser is in scope by default — `--keep-browser` spares the\n * re-download when you only want to reset the cheap state. Because it deletes\n * hundreds of MB and login state, it confirms first (skip with `--yes`) and\n * `--dry-run` prints the plan without touching anything.\n */\nimport { existsSync, lstatSync, readdirSync, rmSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { listMcpServers, projectCacheDir } from \"@habemus-papadum/aiui-claude-channel\";\nimport { cacheDir } from \"@habemus-papadum/aiui-util\";\nimport chalk from \"chalk\";\nimport { cftCacheDir } from \"../util/cft\";\nimport { choose } from \"../util/prompt\";\nimport { printError, printNote, printWarning } from \"../util/ui\";\n\nexport interface CleanOptions {\n /** Limit the reset to this repo's `.aiui-cache/`. */\n projectOnly?: boolean;\n /** Limit the reset to the user cache (`~/.cache/aiui`). */\n userOnly?: boolean;\n /** Keep the managed Chrome for Testing install (skip the ~160 MB re-download). */\n keepBrowser?: boolean;\n /** Print what would be deleted, then stop. */\n dryRun?: boolean;\n /** Delete without the confirmation prompt. */\n yes?: boolean;\n}\n\n/** The three cache roots `clean` reasons about (resolved, never created). */\nexport interface CleanRoots {\n /** `<base>/.aiui-cache`. */\n project: string;\n /** `~/.cache/aiui` (respects `$AIUI_CACHE` / `$XDG_CACHE_HOME`). */\n user: string;\n /** The managed Chrome for Testing dir — a child of {@link CleanRoots.user}. */\n browser: string;\n}\n\n/** One thing `clean` will delete: a root path, optionally sparing some children. */\nexport interface CleanTarget {\n /** Short human label for the plan output. */\n label: string;\n /** The directory to remove. */\n path: string;\n /**\n * Absolute child paths to preserve instead of deleting `path` wholesale. Used\n * by `--keep-browser` to clear the user cache while keeping `chrome/`.\n */\n keep?: string[];\n}\n\n/** Resolve the cache roots for a given base dir (defaults to cwd). */\nexport function cleanRoots(base: string = process.cwd()): CleanRoots {\n return {\n project: projectCacheDir(base),\n user: cacheDir(undefined, { create: false }),\n browser: cftCacheDir(false),\n };\n}\n\n/**\n * Decide which roots to remove from the flags. Pure over `roots`: the fs is only\n * touched later, when the targets are resolved to concrete deletions.\n */\nexport function planCleanTargets(opts: CleanOptions, roots: CleanRoots): CleanTarget[] {\n const targets: CleanTarget[] = [];\n if (!opts.userOnly) {\n targets.push({ label: \"project cache\", path: roots.project });\n }\n if (!opts.projectOnly) {\n targets.push(\n opts.keepBrowser\n ? {\n label: \"user cache (keeping Chrome for Testing)\",\n path: roots.user,\n keep: [roots.browser],\n }\n : { label: \"user cache\", path: roots.user },\n );\n }\n return targets;\n}\n\n/**\n * The concrete absolute paths a target will delete. `[path]` for a plain target,\n * or its children minus the kept ones for a `--keep-browser` target; `[]` when\n * nothing there exists (so an already-clean root drops out of the plan).\n */\nexport function resolveDeletions(target: CleanTarget): string[] {\n if (!existsSync(target.path)) {\n return [];\n }\n if (!target.keep?.length) {\n return [target.path];\n }\n const keep = new Set(target.keep.map((p) => resolve(p)));\n let children: string[];\n try {\n children = readdirSync(target.path);\n } catch {\n return [];\n }\n return children.map((c) => resolve(join(target.path, c))).filter((p) => !keep.has(p));\n}\n\nexport async function runClean(opts: CleanOptions = {}): Promise<void> {\n if (opts.projectOnly && opts.userOnly) {\n printError(\n \"pass at most one of --project-only / --user-only\",\n \"with neither, `aiui clean` removes both\",\n );\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const targets = planCleanTargets(opts, cleanRoots(cwd));\n const planned = targets\n .map((target) => ({ target, paths: resolveDeletions(target) }))\n .filter((p) => p.paths.length > 0);\n\n if (planned.length === 0) {\n console.log(\"nothing to clean — no aiui state here or in the user cache.\");\n return;\n }\n\n // Show the plan (size per target, then the consequences).\n let total = 0;\n console.log(\"aiui clean will remove:\\n\");\n for (const { target, paths } of planned) {\n const size = paths.reduce((n, p) => n + pathSize(p), 0);\n total += size;\n console.log(` ${formatBytes(size).padStart(9)} ${target.label}`);\n console.log(` ${\" \".repeat(9)} ${chalk.dim(target.path)}`);\n }\n console.log(`\\n ${formatBytes(total).padStart(9)} total\\n`);\n\n const clearingUser = !opts.projectOnly;\n const removingBrowser = clearingUser && !opts.keepBrowser;\n const consequences = [\n \"traces and session-browser logins are cleared\",\n clearingUser && \"the Claude permission prompt (claude.skipPermissions) returns on next launch\",\n removingBrowser && \"Chrome for Testing re-downloads (~160 MB) on the next `aiui claude`\",\n ].filter((line): line is string => Boolean(line));\n console.log(\"This resets aiui toward a fresh install:\");\n for (const line of consequences) {\n console.log(` • ${line}`);\n }\n console.log(\"\");\n\n const running = listMcpServers(cwd);\n if (running.length) {\n printWarning(\n `${running.length} aiui session${running.length === 1 ? \"\" : \"s\"} still running`,\n \"stop them first — a live session rewrites its registry entry as it exits, and an open browser can lock files being deleted\",\n );\n }\n\n if (opts.dryRun) {\n printNote(\"dry run — nothing was deleted\");\n return;\n }\n\n if (!opts.yes) {\n if (!process.stdin.isTTY) {\n printError(\n \"refusing to delete without confirmation\",\n \"re-run with --yes for a non-interactive clean, or from a terminal\",\n );\n process.exitCode = 1;\n return;\n }\n const answer = await choose(\n \"Delete these now?\",\n [\n { key: \"y\", label: \"yes, delete\" },\n { key: \"n\", label: \"no, cancel\" },\n ],\n \"n\",\n );\n if (answer !== \"y\") {\n console.log(\"cancelled — nothing was deleted.\");\n return;\n }\n }\n\n let freed = 0;\n let failed = 0;\n for (const { paths } of planned) {\n for (const p of paths) {\n const size = pathSize(p);\n try {\n rmSync(p, { recursive: true, force: true });\n freed += size;\n } catch (error) {\n failed++;\n printWarning(\n `could not remove ${p}`,\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n }\n\n if (failed) {\n printWarning(\n `${failed} path${failed === 1 ? \"\" : \"s\"} could not be deleted`,\n \"a running browser can lock its files — close the session browser and re-run\",\n );\n }\n console.log(`clean complete — freed ~${formatBytes(freed)}.`);\n}\n\n/** Recursive on-disk size of a path, best-effort. Symlinks count as their own entry, never followed. */\nfunction pathSize(p: string): number {\n let st: ReturnType<typeof lstatSync>;\n try {\n st = lstatSync(p);\n } catch {\n return 0;\n }\n if (st.isSymbolicLink() || !st.isDirectory()) {\n return st.size;\n }\n let entries: string[];\n try {\n entries = readdirSync(p);\n } catch {\n return 0;\n }\n let total = 0;\n for (const entry of entries) {\n total += pathSize(join(p, entry));\n }\n return total;\n}\n\n/** Human-readable byte count (binary units), e.g. `346.0 MB`. */\nexport function formatBytes(n: number): string {\n if (n < 1024) {\n return `${n} B`;\n }\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let value = n / 1024;\n let i = 0;\n while (value >= 1024 && i < units.length - 1) {\n value /= 1024;\n i++;\n }\n return `${value.toFixed(1)} ${units[i]}`;\n}\n","/**\n * `aiui config` — inspect and edit the two-level config.json.\n *\n * aiui config the interactive browser (TTY), help otherwise\n * aiui config tui same, explicitly\n * aiui config show [--json] every key: effective value + which file set it\n * aiui config get <key> the effective value (provenance on stderr)\n * aiui config set <key> <value> validated write — user level by default\n * aiui config unset <key> remove a key (e.g. to be re-asked on first run)\n *\n * `set`/`unset` write the **user** file unless `--project` says otherwise: user\n * config holds personal preferences; the project file (.aiui-cache/config.json)\n * may be shared or committed by a team, so touching it is a deliberate act.\n * Keys, types, defaults, and docs all come from `util/config-schema.ts` — the\n * same table validation uses, so what these commands print is what the loader\n * enforces.\n */\nimport { existsSync } from \"node:fs\";\nimport chalk from \"chalk\";\nimport {\n type AiuiConfig,\n configPaths,\n mergeAiuiConfig,\n readConfigFile,\n updateConfigFile,\n} from \"../util/config\";\nimport {\n allConfigFields,\n type ConfigValue,\n describeDefault,\n formatConfigValue,\n parseFieldValue,\n type ResolvedField,\n} from \"../util/config-schema\";\nimport { printError, printNote } from \"../util/ui\";\n\n/** Which config file a write targets. */\nexport type ConfigLevel = \"user\" | \"project\";\n\n/** Both config files, read once — the raw material for every subcommand. */\nexport interface ConfigLevels {\n paths: { user: string; project: string };\n user: AiuiConfig;\n project: AiuiConfig;\n}\n\n/** One field with its per-level values and resolved provenance. */\nexport interface FieldState extends ResolvedField {\n userValue?: ConfigValue;\n projectValue?: ConfigValue;\n /** The value the launcher would see (project beats user); undefined = unset. */\n effective?: ConfigValue;\n /** Where {@link effective} comes from; \"default\"/\"unset\" mean no file sets it. */\n source: ConfigLevel | \"default\" | \"unset\";\n}\n\n/** Read both config levels (missing files read as empty configs). */\nexport function readLevels(base: string = process.cwd()): ConfigLevels {\n const paths = configPaths(base);\n return {\n paths,\n user: readConfigFile(paths.user) ?? {},\n project: readConfigFile(paths.project) ?? {},\n };\n}\n\n/** Resolve every schema field against the two levels. */\nexport function fieldStates(levels: ConfigLevels): FieldState[] {\n return allConfigFields().map((resolved) => {\n const userValue = valueIn(levels.user, resolved);\n const projectValue = valueIn(levels.project, resolved);\n const effective = projectValue ?? userValue;\n const source: FieldState[\"source\"] =\n projectValue !== undefined\n ? \"project\"\n : userValue !== undefined\n ? \"user\"\n : resolved.field.default !== undefined\n ? \"default\"\n : \"unset\";\n return { ...resolved, userValue, projectValue, effective, source };\n });\n}\n\nfunction valueIn(config: AiuiConfig, resolved: ResolvedField): ConfigValue | undefined {\n const sections = config as Record<string, Record<string, ConfigValue> | undefined>;\n return sections[resolved.section.name]?.[resolved.field.key];\n}\n\n/** Resolve a CLI key argument, reporting unknowns with the full key list. */\nfunction resolveKeyArg(key: string, levels: ConfigLevels): FieldState | undefined {\n const hit = fieldStates(levels).find((state) => state.path === key);\n if (!hit) {\n printError(\n `unknown config key: ${key}`,\n `Known keys:\\n ${allConfigFields()\n .map((f) => f.path)\n .join(\"\\n \")}`,\n );\n process.exitCode = 1;\n }\n return hit;\n}\n\n// ── show ─────────────────────────────────────────────────────────────────────\n\nexport interface ShowOptions {\n json?: boolean;\n}\n\nexport function runConfigShow(options: ShowOptions = {}, base: string = process.cwd()): void {\n const levels = readLevels(base);\n const states = fieldStates(levels);\n\n if (options.json) {\n console.log(\n JSON.stringify(\n {\n files: {\n user: { path: levels.paths.user, exists: existsSync(levels.paths.user) },\n project: { path: levels.paths.project, exists: existsSync(levels.paths.project) },\n },\n user: levels.user,\n project: levels.project,\n effective: mergeAiuiConfig(levels.user, levels.project),\n },\n null,\n 2,\n ),\n );\n return;\n }\n\n console.log(`user: ${levels.paths.user}${presenceSuffix(levels.paths.user)}`);\n console.log(`project: ${levels.paths.project}${presenceSuffix(levels.paths.project)}`);\n const width = Math.max(...states.map((state) => state.path.length));\n let section = \"\";\n for (const state of states) {\n if (state.section.name !== section) {\n section = state.section.name;\n console.log(`\\n${chalk.bold(section)} ${chalk.dim(`— ${state.section.summary}`)}`);\n }\n console.log(` ${state.path.padEnd(width + 2)}${valueCell(state)}`);\n }\n console.log(\n chalk.dim(\n \"\\nProject beats user per key; CLI flags beat both. `aiui config` browses the docs interactively.\",\n ),\n );\n}\n\nfunction presenceSuffix(path: string): string {\n return existsSync(path) ? \"\" : chalk.dim(\" (not present)\");\n}\n\n/** The value column of one `show`/TUI row: set value + source, or the default. */\nexport function valueCell(state: FieldState): string {\n if (state.effective !== undefined) {\n return `${chalk.cyan(formatConfigValue(state.effective))} ${chalk.dim(`(${state.source})`)}`;\n }\n return chalk.dim(\n state.field.default !== undefined\n ? `default: ${formatConfigValue(state.field.default)}`\n : \"unset\",\n );\n}\n\n// ── get ──────────────────────────────────────────────────────────────────────\n\nexport function runConfigGet(key: string, base: string = process.cwd()): void {\n const levels = readLevels(base);\n const state = resolveKeyArg(key, levels);\n if (!state) {\n return;\n }\n // Value on stdout (raw, script-friendly); provenance on stderr.\n if (state.effective !== undefined) {\n console.log(String(state.effective));\n const file = state.source === \"project\" ? levels.paths.project : levels.paths.user;\n console.error(chalk.dim(`# set in the ${state.source} config: ${file}`));\n return;\n }\n if (state.field.default !== undefined) {\n console.log(String(state.field.default));\n console.error(chalk.dim(`# built-in default — ${describeDefault(state.field)}`));\n return;\n }\n console.error(\n chalk.dim(`# not set in any config file — default: ${describeDefault(state.field)}`),\n );\n}\n\n// ── set / unset ──────────────────────────────────────────────────────────────\n\nexport interface WriteOptions {\n /** Target the project file (.aiui-cache/config.json) instead of the user file. */\n project?: boolean;\n}\n\nexport function runConfigSet(\n key: string,\n raw: string,\n options: WriteOptions = {},\n base: string = process.cwd(),\n): void {\n const levels = readLevels(base);\n const state = resolveKeyArg(key, levels);\n if (!state) {\n return;\n }\n const parsed = parseFieldValue(state.field, raw);\n if (\"error\" in parsed) {\n printError(`invalid value for ${state.path}: ${raw}`, parsed.error);\n process.exitCode = 1;\n return;\n }\n writeValue(levels, state, parsed.value, options.project ? \"project\" : \"user\");\n}\n\n/** The shared write path for `set` and the TUI. */\nexport function writeValue(\n levels: ConfigLevels,\n state: ResolvedField,\n value: ConfigValue,\n level: ConfigLevel,\n): void {\n const file = updateConfigFile(levels.paths[level], (config) => {\n const sections = config as Record<string, Record<string, ConfigValue> | undefined>;\n sections[state.section.name] = { ...sections[state.section.name], [state.field.key]: value };\n });\n printNote(`wrote ${state.path}: ${formatConfigValue(value)} to ${file}`);\n}\n\nexport function runConfigUnset(\n key: string,\n options: WriteOptions = {},\n base: string = process.cwd(),\n): void {\n const levels = readLevels(base);\n const state = resolveKeyArg(key, levels);\n if (!state) {\n return;\n }\n removeValue(levels, state, options.project ? \"project\" : \"user\");\n}\n\n/** The shared unset path for `unset` and the TUI. */\nexport function removeValue(levels: ConfigLevels, state: ResolvedField, level: ConfigLevel): void {\n const path = levels.paths[level];\n const current = readConfigFile(path);\n const sections = (current ?? {}) as Record<string, Record<string, ConfigValue> | undefined>;\n if (sections[state.section.name]?.[state.field.key] === undefined) {\n printNote(`${state.path} is not set in the ${level} config (${path})`);\n return;\n }\n const file = updateConfigFile(path, (config) => {\n const mutable = config as Record<string, Record<string, ConfigValue> | undefined>;\n const section = { ...mutable[state.section.name] };\n delete section[state.field.key];\n if (Object.keys(section).length === 0) {\n delete mutable[state.section.name];\n } else {\n mutable[state.section.name] = section;\n }\n });\n printNote(`removed ${state.path} from ${file}`);\n}\n","/**\n * `aiui config tui` (and bare `aiui config`) — the interactive config browser.\n *\n * A two-level @inquirer/prompts flow over the schema in util/config-schema.ts:\n * the main list shows every key with its effective value, and the description\n * panel under the list is the documentation card — what the key does, its\n * default, and what each config file says. Picking a key opens its actions:\n * set at either level (enums and booleans become menus, strings and numbers a\n * validated input), unset where it's set, or back. Writes reuse the same code\n * paths as `aiui config set`/`unset`, so the TUI can't drift from the CLI.\n *\n * Ctrl-C anywhere leaves quietly — it's a browser, not a wizard.\n */\nimport { homedir } from \"node:os\";\nimport { input, Separator, select } from \"@inquirer/prompts\";\nimport chalk from \"chalk\";\nimport {\n CONFIG_SECTIONS,\n type ConfigValue,\n describeDefault,\n formatConfigValue,\n parseFieldValue,\n} from \"../util/config-schema\";\nimport { printError } from \"../util/ui\";\nimport {\n type ConfigLevel,\n type ConfigLevels,\n type FieldState,\n fieldStates,\n readLevels,\n removeValue,\n valueCell,\n writeValue,\n} from \"./config\";\n\nexport async function runConfigTui(base: string = process.cwd()): Promise<void> {\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n printError(\n \"aiui config tui needs an interactive terminal\",\n \"In scripts and non-TTY sessions use `aiui config show` (or `show --json`).\",\n );\n process.exitCode = 1;\n return;\n }\n // Where the two levels live, once, above the prompt (it redraws below).\n const paths = readLevels(base).paths;\n console.log(chalk.dim(`user: ${tildify(paths.user)}`));\n console.log(chalk.dim(`project: ${tildify(paths.project)}`));\n try {\n for (;;) {\n // Re-read every lap so an edit (or a concurrent one) is visible immediately.\n const levels = readLevels(base);\n const picked = await pickField(levels);\n if (!picked) {\n return;\n }\n await editField(levels, picked);\n }\n } catch (error) {\n if (error instanceof Error && error.name === \"ExitPromptError\") {\n return; // Ctrl-C: leave quietly\n }\n throw error;\n }\n}\n\n/** The main list: every key, grouped by section, documented in the panel below. */\nasync function pickField(levels: ConfigLevels): Promise<FieldState | undefined> {\n const states = fieldStates(levels);\n const width = Math.max(...states.map((state) => state.path.length)) + 2;\n const choices: (\n | Separator\n | { name: string; value: FieldState | undefined; description: string }\n )[] = [];\n for (const section of CONFIG_SECTIONS) {\n choices.push(new Separator(chalk.dim(`── ${section.name} — ${section.summary}`)));\n for (const state of states.filter((s) => s.section.name === section.name)) {\n choices.push({\n name: `${state.path.padEnd(width)}${valueCell(state)}`,\n value: state,\n description: fieldCard(state),\n });\n }\n }\n choices.push(new Separator(\" \"));\n choices.push({ name: \"exit\", value: undefined, description: \"Leave the config browser.\" });\n return select({\n message: \"aiui config\",\n choices,\n pageSize: choices.length,\n loop: false,\n });\n}\n\n/** The documentation card for one key: doc, default, and both file levels. */\nfunction fieldCard(state: FieldState): string {\n const lines = [chalk.bold(state.field.summary)];\n if (state.field.doc) {\n lines.push(wrap(state.field.doc, 76));\n }\n lines.push(\"\");\n if (state.field.type === \"enum\") {\n lines.push(`allowed: ${(state.field.values ?? []).join(\" | \")}`);\n }\n lines.push(`default: ${describeDefault(state.field)}`);\n lines.push(`user: ${levelCell(state.userValue)}`);\n lines.push(`project: ${levelCell(state.projectValue)}`);\n lines.push(\n `effective: ${\n state.effective !== undefined\n ? `${formatConfigValue(state.effective)} (from the ${state.source} config)`\n : \"the built-in default\"\n }`,\n );\n return lines.join(\"\\n\");\n}\n\nfunction levelCell(value: ConfigValue | undefined): string {\n return value === undefined ? chalk.dim(\"(not set)\") : formatConfigValue(value);\n}\n\n/** The per-key action menu, then the edit itself. */\nasync function editField(levels: ConfigLevels, state: FieldState): Promise<void> {\n const actions: (Separator | { name: string; value: string; description?: string })[] = [\n {\n name: \"set in the user config\",\n value: \"set-user\",\n description: `${levels.paths.user}\\nPersonal preference — applies to every project.`,\n },\n {\n name: \"set in the project config\",\n value: \"set-project\",\n description: `${levels.paths.project}\\nThis project only; the file may be shared or committed by a team.`,\n },\n ];\n if (state.userValue !== undefined) {\n actions.push({\n name: `unset in the user config (currently ${formatConfigValue(state.userValue)})`,\n value: \"unset-user\",\n });\n }\n if (state.projectValue !== undefined) {\n actions.push({\n name: `unset in the project config (currently ${formatConfigValue(state.projectValue)})`,\n value: \"unset-project\",\n });\n }\n actions.push(new Separator());\n actions.push({ name: \"back\", value: \"back\" });\n\n const action = await select({\n message: `${state.path} — ${state.field.summary}`,\n choices: actions,\n });\n if (action === \"back\") {\n return;\n }\n const level: ConfigLevel = action.endsWith(\"project\") ? \"project\" : \"user\";\n if (action.startsWith(\"unset\")) {\n removeValue(levels, state, level);\n return;\n }\n const value = await askValue(state);\n if (value !== undefined) {\n writeValue(levels, state, value, level);\n }\n}\n\n/** Prompt for a new value: menus for enums/booleans, validated input otherwise. */\nasync function askValue(state: FieldState): Promise<ConfigValue | undefined> {\n const mark = (value: ConfigValue): string => {\n if (value === state.effective) {\n return \" (current)\";\n }\n return value === state.field.default ? \" (default)\" : \"\";\n };\n if (state.field.type === \"boolean\") {\n return select({\n message: `${state.path} =`,\n choices: [true, false].map((value) => ({\n name: `${value}${chalk.dim(mark(value))}`,\n value: value as ConfigValue,\n })),\n });\n }\n if (state.field.type === \"enum\") {\n return select({\n message: `${state.path} =`,\n choices: (state.field.values ?? []).map((value) => ({\n name: `${value}${chalk.dim(mark(value))}`,\n value: value as ConfigValue,\n })),\n });\n }\n const raw = await input({\n message: `${state.path} = `,\n default: state.effective !== undefined ? String(state.effective) : undefined,\n validate: (text) => {\n const parsed = parseFieldValue(state.field, text);\n return \"error\" in parsed ? parsed.error : true;\n },\n });\n const parsed = parseFieldValue(state.field, raw);\n return \"error\" in parsed ? undefined : parsed.value;\n}\n\n/** Shorten a home-dir prefix to `~` for display. */\nfunction tildify(path: string): string {\n const home = homedir();\n return path.startsWith(home) ? `~${path.slice(home.length)}` : path;\n}\n\n/** Greedy wrap at `width` columns — inquirer descriptions don't wrap themselves. */\nfunction wrap(text: string, width: number): string {\n const lines: string[] = [];\n let line = \"\";\n for (const word of text.split(/\\s+/)) {\n if (line && line.length + 1 + word.length > width) {\n lines.push(line);\n line = word;\n } else {\n line = line ? `${line} ${word}` : word;\n }\n }\n if (line) {\n lines.push(line);\n }\n return lines.join(\"\\n\");\n}\n","import type { RunningServer } from \"@habemus-papadum/aiui-claude-channel\";\nimport { listMcpServers, selectMcpServer } from \"@habemus-papadum/aiui-claude-channel\";\nimport {\n decideBrowserAction,\n discoverSessionBrowser,\n openInSessionBrowser,\n} from \"@habemus-papadum/aiui-util\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { type AiuiArgs, infoFlag, splitAiuiArgs } from \"../util/aiui-args\";\nimport { resolveChromeSettings } from \"../util/chrome\";\nimport { type AiuiConfig, loadAiuiConfig } from \"../util/config\";\nimport { type CliInvocation, resolvePackageCli } from \"../util/resolve-cli\";\nimport { printError, printNote, printWarning } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\nimport { startSessionBrowser } from \"./browser\";\n\nconst VITE_PKG = \"vite\";\n\n// The environment variable that tells the Vite dev server which channel\n// server's web backend to talk to. Read in the dev-server process by the\n// aiuiDevOverlay() plugin (@habemus-papadum/aiui-dev-overlay/vite), which\n// mounts the intent tool and hands it the port; app source can also read it\n// via Vite's `import.meta.env`. It can NOT be read from inside the prebuilt\n// overlay bundle — see the overlay package's src/vite.ts for that subtlety.\nconst VITE_PORT_ENV = \"VITE_AIUI_PORT\";\n\n/** The channel server `runVite` should point Vite at, or why it couldn't. */\nexport interface ChannelTarget {\n /** The server resolved without prompting (by tag). */\n server?: RunningServer;\n /** Servers to offer in the interactive selector (no tag given, ≥1 running). */\n select?: RunningServer[];\n /** A human-readable reason a requested server couldn't be resolved. */\n error?: string;\n}\n\n/**\n * Decide which running channel server Vite should connect to.\n *\n * Pure so it can be unit-tested without spawning anything:\n * - With a `targetTag`, return the server whose `tag` matches exactly; if none\n * matches, return an `error` naming the tag and the tags that *are* running.\n * - Without a `targetTag`, don't guess: return `{}` when nothing is running, or\n * `{ select }` so the caller runs the same selector as `quick` (which\n * auto-picks a lone server and prompts when there are several).\n */\nexport function resolveChannelTarget(\n servers: RunningServer[],\n targetTag: string | undefined,\n): ChannelTarget {\n if (targetTag !== undefined) {\n const server = servers.find((s) => s.tag === targetTag);\n if (!server) {\n const running = servers.length > 0 ? servers.map((s) => s.tag).join(\", \") : \"(none running)\";\n return {\n error: `no running aiui channel with tag \"${targetTag}\" — running tags: ${running}`,\n };\n }\n return { server };\n }\n return servers.length > 0 ? { select: servers } : {};\n}\n\n/**\n * Launch Vite, forwarding any extra args (e.g. `aiui vite dev`,\n * `aiui vite --port 3000`, `aiui vite --version`).\n *\n * Before launching, resolve which running aiui channel server the dev server\n * should talk to — either the one named by `--aiui-mcp <tag>` (or `--aiui-tag`),\n * or, with no tag, the one you pick from the same selector `quick` uses (which\n * auto-selects when only one is running) — and inject its port as\n * {@link VITE_PORT_ENV} so the app can reach it. When a specific tag was asked\n * for but isn't running, we fail loudly instead of connecting to the wrong one.\n *\n * Unlike `claude` — an external tool we look up on the PATH — Vite is a declared\n * dependency of this package, so we resolve it straight out of node_modules and\n * run it via the current Node with an absolute path. Resolving it also doubles\n * as the \"is Vite available?\" check: if it isn't installed, we fail loudly\n * rather than shelling out to nothing. See {@link resolvePackageCli}.\n *\n * Once the dev server is up, a *sidecar* opens it in the session browser (the\n * shared window `aiui claude` attaches the agent to). We never know up front\n * which port Vite will bind — the user runs many dev servers and Vite walks up\n * from 5173 — so instead of guessing, Vite's stdout is teed through us and\n * scanned for its own ready banner (see {@link parseViteLocalUrl}). stdin and\n * stderr stay inherited: Vite still owns the terminal, its interactive\n * shortcuts and Ctrl-C behave exactly as before. The sidecar is fire-and-forget\n * ({@link openAppInBrowser}): whatever the browser does, the dev server runs on.\n */\nexport async function runVite(rawArgs: string[] = []): Promise<void> {\n const aiuiArgs = splitAiuiArgs(rawArgs);\n const { mcp, tag, passthrough } = aiuiArgs;\n\n // `--help` / `--version` are inert: aiui's own answer, then Vite's — with no\n // channel discovery (which could otherwise block on an interactive picker).\n const info = infoFlag(passthrough);\n if (info) {\n if (info === \"help\") {\n printViteWrapperHelp();\n } else {\n console.log(`aiui ${VERSION}`);\n }\n await forwardToVite(passthrough);\n return;\n }\n\n // `--aiui-mcp` is the purpose-built selector; `--aiui-tag` is accepted too.\n const targetTag = mcp ?? tag;\n\n const target = resolveChannelTarget(listMcpServers(), targetTag);\n if (target.error) {\n printError(\"Could not resolve an aiui channel\", target.error);\n process.exitCode = 1;\n return;\n }\n\n // A tag resolves directly; otherwise the selector (shared with `quick`) picks\n // — returning the lone server without prompting, or asking when there's more\n // than one.\n const server = target.select ? await selectMcpServer(target.select) : target.server;\n\n let port: string | undefined;\n if (server) {\n port = String(server.port);\n console.error(\n chalk.dim(\n `aiui: connecting vite to channel \"${server.tag}\" (${server.cwd}) on port ${port} via ${VITE_PORT_ENV}`,\n ),\n );\n } else {\n console.error(chalk.dim(`aiui: no running channel found — ${VITE_PORT_ENV} left unset`));\n }\n\n const vite = resolveVite();\n if (!vite) {\n return;\n }\n\n // execa merges `env` over process.env, so we only add entries deliberately.\n const env: NodeJS.ProcessEnv = {};\n if (port) {\n env[VITE_PORT_ENV] = port;\n }\n // Piping stdout (below) makes Vite see a non-TTY and drop its colors. When\n // *our* stdout is a real terminal the tee lands there verbatim, so tell the\n // child to keep coloring — unless the user already voted (FORCE_COLOR /\n // NO_COLOR), in which case their setting passes through untouched.\n if (process.stdout.isTTY && !(\"FORCE_COLOR\" in process.env) && !(\"NO_COLOR\" in process.env)) {\n env.FORCE_COLOR = \"1\";\n }\n\n // stdin/stderr inherit so the dev server owns the terminal (Ctrl-C, the\n // h/r/q shortcuts); stdout is piped *only* to learn which port Vite bound —\n // every byte is teed straight back out. buffer:false because a dev server\n // runs for hours and execa would otherwise accumulate its whole output.\n // reject:false so a non-zero/interrupted Vite exit is propagated as our exit\n // code instead of throwing an error the user didn't cause.\n const child = execa(vite.command, [...vite.args, ...passthrough], {\n stdio: [\"inherit\", \"pipe\", \"inherit\"],\n buffer: false,\n reject: false,\n env,\n });\n if (child.stdout) {\n teeAndDetectLocalUrl(child.stdout, process.stdout, (url) => {\n // Fire-and-forget: openAppInBrowser catches everything itself, and Vite\n // must never wait on (or die with) the browser.\n void openAppInBrowser(url, aiuiArgs);\n });\n }\n const result = await child;\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/**\n * ANSI CSI escape sequences (colors, cursor movement, screen clears):\n * `ESC [`, parameter bytes, intermediate bytes, one final byte.\n */\n// biome-ignore lint/suspicious/noControlCharactersInRegex: matching the ESC byte is the point\nconst ANSI_CSI = /\\x1b\\[[0-9;?]*[ -/]*[@-~]/g;\n\n/**\n * Parse one line of Vite's stdout for the local dev-server URL.\n *\n * Vite's ready banner looks like (colored in a real terminal):\n *\n * ➜ Local: http://localhost:5174/\n * ➜ Network: use --host to expose\n *\n * Only the `Local:` line counts, and only with a loopback host (`localhost`,\n * `127.0.0.1`, `[::1]`) — that's the URL meaningful to open in a browser on\n * this machine, or to port-forward in the headless hint. ANSI codes are\n * stripped *first*: Vite colors the host and the port separately, so escape\n * sequences sit in the middle of the URL text and no pattern would survive\n * matching against the raw bytes. Returns undefined for anything that isn't\n * the ready line — the same shape as the workbench's parseServeReadyLine, the\n * house pattern for these one-line protocols.\n */\nexport function parseViteLocalUrl(line: string): string | undefined {\n const plain = line.replace(ANSI_CSI, \"\");\n const match = plain.match(\n /\\bLocal:\\s+(https?:\\/\\/(?:localhost|127\\.0\\.0\\.1|\\[::1\\])(?::\\d+)?\\/?\\S*)/,\n );\n return match?.[1];\n}\n\n/**\n * Tee a child's stdout to ours verbatim while watching for the dev-server URL.\n *\n * Chunks are forwarded untouched (colors, spinners, screen clears all pass\n * through); scanning happens on a parallel line-buffered copy so a URL split\n * across chunk boundaries is still seen. Once found, `onUrl` fires exactly\n * once and the scanner disengages — from then on this is a plain passthrough.\n * Exported for tests, which drive it with in-memory streams (no child\n * process).\n */\nexport function teeAndDetectLocalUrl(\n source: NodeJS.ReadableStream,\n sink: NodeJS.WritableStream,\n onUrl: (url: string) => void,\n): void {\n let buffer = \"\";\n let found = false;\n source.on(\"data\", (chunk: Buffer | string) => {\n sink.write(chunk);\n if (found) {\n return;\n }\n buffer += chunk.toString();\n for (;;) {\n const newline = buffer.indexOf(\"\\n\");\n if (newline === -1) {\n break;\n }\n const line = buffer.slice(0, newline);\n buffer = buffer.slice(newline + 1);\n const url = parseViteLocalUrl(line);\n if (url) {\n found = true;\n buffer = \"\";\n onUrl(url);\n return;\n }\n }\n // Guard the partial-line buffer against pathological unbroken output; the\n // banner line we're waiting for is short, so nothing real is lost.\n if (buffer.length > 8192) {\n buffer = buffer.slice(-8192);\n }\n });\n}\n\ntype ChromeConfig = NonNullable<AiuiConfig[\"chrome\"]>;\n\n/**\n * The browser sidecar: once the dev server's URL is known, put it in front of\n * the user — in the *session browser* (the shared window `aiui claude`\n * attaches the agent to; see aiui-util's browser module and `aiui open`),\n * never their default browser. A running session browser gets a new tab; none running\n * means launching one exactly the way `aiui browser` does\n * ({@link startSessionBrowser}), with the app as its first tab.\n *\n * Runs concurrently with Vite and must never interfere with it: everything is\n * caught, failures print a warning, and the dev server keeps the terminal and\n * keeps running either way. It is also deliberately non-interactive — Vite\n * owns stdin — so the Chrome for Testing sync never prompts here; it just\n * uses whatever browser is already available.\n */\nasync function openAppInBrowser(url: string, aiuiArgs: AiuiArgs): Promise<void> {\n try {\n // `--aiui-browser-url` beats a configured chrome.browserUrl for this run,\n // the same precedence `aiui claude` gives it.\n const chromeCfg: ChromeConfig = {\n ...loadAiuiConfig().chrome,\n ...(aiuiArgs.browserUrl ? { browserUrl: aiuiArgs.browserUrl } : {}),\n };\n const action = decideBrowserAction(aiuiArgs, chromeCfg);\n if (action.kind === \"skip\") {\n return;\n }\n if (action.kind === \"hint\") {\n printNote(\n `detected a headless environment (${action.reason}) — not opening a browser`,\n `Assuming the dev server's port is already forwarded, open ${url} in the browser\\n` +\n \"on your local machine. (Pass --aiui-browser to open one here anyway.)\",\n );\n return;\n }\n\n if (chromeCfg.browserUrl) {\n await openInSessionBrowser(chromeCfg.browserUrl, url);\n console.error(chalk.dim(`aiui: opened ${url} in the browser at ${chromeCfg.browserUrl}`));\n return;\n }\n const settings = resolveChromeSettings(aiuiArgs, chromeCfg);\n const running = await discoverSessionBrowser(settings.userDataDir);\n if (running) {\n await openInSessionBrowser(running.browserUrl, url);\n console.error(chalk.dim(`aiui: opened ${url} in the session browser`));\n } else {\n await startSessionBrowser({\n flags: aiuiArgs,\n config: chromeCfg,\n interactive: false,\n startUrl: url,\n });\n console.error(chalk.dim(`aiui: opened ${url} in a new session browser`));\n }\n } catch (error) {\n printWarning(\n \"couldn't open the app in the session browser — the dev server is unaffected\",\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\n/** Resolve the Vite CLI; print the friendly install pointer when missing. */\nfunction resolveVite(): CliInvocation | undefined {\n try {\n return resolvePackageCli(VITE_PKG);\n } catch {\n printError(\n \"Vite is not available\",\n \"`vite` should be installed as a dependency of aiui — try reinstalling.\",\n );\n process.exitCode = 1;\n return undefined;\n }\n}\n\n/** Run Vite with the args verbatim (the --help/--version forward). */\nasync function forwardToVite(args: string[]): Promise<void> {\n const vite = resolveVite();\n if (!vite) {\n return;\n }\n const result = await execa(vite.command, [...vite.args, ...args], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n\n/** The aiui half of `aiui vite --help` (vite's own --help follows it). */\nfunction printViteWrapperHelp(): void {\n console.log(`aiui vite — launch Vite connected to the running aiui channel\n\naiui's own flags (everything else forwards to vite verbatim):\n --aiui-mcp <tag> connect to the channel server with this tag\n --aiui-tag <tag> accepted alias for --aiui-mcp\n --aiui-browser open the app in the session browser even when\n the environment looks headless (CI, SSH, no display)\n --aiui-no-browser never open a browser for this run\n --aiui-chrome-profile <name> browser profile at .aiui-cache/chrome/<name>\n --aiui-chrome-data-dir <path> explicit browser user data dir\n\nThe chosen channel's port is exported as VITE_AIUI_PORT; the aiuiDevOverlay()\nVite plugin picks it up there and wires the intent tool to it. When Vite prints\nits Local: URL, aiui opens it in the shared session browser (the one \\`aiui\nclaude\\` and \\`aiui open\\` use); in headless environments it prints the URL to\nopen on your own machine instead. What follows is vite's own --help:\n`);\n}\n","/**\n * `aiui debug` — the standalone trace-debugger frontend.\n *\n * The channel serves **no HTML** (it is a JSON/data server); every viewer is a\n * frontend process. This command is that frontend when there's no app dev\n * server to piggyback on: it picks a running channel (the same registry +\n * selector `aiui vite` uses — a lone channel is taken directly, several\n * prompt), then runs a small Vite dev server whose only job is the\n * `aiuiDevOverlay()` plugin's `/__aiui/debug` page — the shared debug-ui\n * viewer (trace list + live-followed TraceView). The page's header offers a\n * **channel switcher** fed by the channel's `/debug/api/channels` route, so\n * one command inspects every channel on the machine, hopping between them\n * mid-session.\n *\n * Vite (a real dependency of this package) is used as the module server so the\n * viewer is served exactly the way the in-app `/__aiui/debug` page is — one\n * implementation, no prebuilt bundle to keep in sync. The server root is this\n * package's own directory: that is where `@habemus-papadum/aiui-dev-overlay`\n * (the virtual mount module's import) resolves from, in both the workspace\n * (source-first) and installed (dist) shapes.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { listMcpServers, selectMcpServer } from \"@habemus-papadum/aiui-claude-channel\";\nimport { aiuiDevOverlay } from \"@habemus-papadum/aiui-dev-overlay/vite\";\nimport chalk from \"chalk\";\nimport { createServer, type Plugin } from \"vite\";\nimport { printError } from \"../util/ui\";\nimport { resolveChannelTarget } from \"./vite\";\n\n/** The route the overlay plugin serves the viewer at (its contract). */\nconst DEBUG_ROUTE = \"/__aiui/debug\";\n\n/** Default UI port; Vite walks up from here when it's taken. */\nconst DEFAULT_UI_PORT = 4747;\n\nexport interface DebugOptions {\n /** Target a channel by its registry tag instead of the interactive selector. */\n mcp?: string;\n /** UI port for the viewer's dev server (default {@link DEFAULT_UI_PORT}). */\n port?: string;\n /** Open the browser at the viewer (default true; `--no-open` skips). */\n open?: boolean;\n}\n\n/**\n * This package's root — the Vite server root, found by walking up from this\n * module to the `@habemus-papadum/aiui` package.json (two levels in the\n * source tree, but the walk keeps it honest against dist layouts too).\n */\nexport function packageRoot(): string | undefined {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n const candidate = join(dir, \"package.json\");\n if (existsSync(candidate)) {\n try {\n const name = (JSON.parse(readFileSync(candidate, \"utf8\")) as { name?: string }).name;\n if (name === \"@habemus-papadum/aiui\") {\n return dir;\n }\n } catch {\n // unreadable package.json — keep walking\n }\n }\n dir = dirname(dir);\n }\n return undefined;\n}\n\nexport async function runDebug(opts: DebugOptions = {}): Promise<void> {\n const target = resolveChannelTarget(listMcpServers(), opts.mcp);\n if (target.error) {\n printError(\"Could not resolve an aiui channel\", target.error);\n process.exitCode = 1;\n return;\n }\n const server = target.select ? await selectMcpServer(target.select) : target.server;\n if (!server) {\n console.log(\"No running aiui channel to debug — start one with `aiui claude`.\");\n process.exitCode = 1;\n return;\n }\n\n const root = packageRoot();\n if (!root) {\n printError(\"Could not locate the aiui package root to serve the viewer from\");\n process.exitCode = 1;\n return;\n }\n\n // \"/\" is not a page here — send it to the viewer.\n const home: Plugin = {\n name: \"aiui:debug-home\",\n configureServer(viteServer) {\n viteServer.middlewares.use((req, res, next) => {\n if ((req.url ?? \"/\").split(\"?\")[0] === \"/\") {\n res.statusCode = 302;\n res.setHeader(\"location\", DEBUG_ROUTE);\n res.end();\n return;\n }\n next();\n });\n },\n };\n\n const uiPort = Number(opts.port);\n const ui = await createServer({\n root,\n configFile: false,\n // `mount: false`: this server hosts the viewer, not an app — nothing to\n // arm, so the intent tool stays out of it. The plugin still serves the\n // DEBUG_ROUTE page and seeds the picked channel's port into it.\n plugins: [home, aiuiDevOverlay({ port: server.port, mount: false }) as Plugin],\n server: {\n port: Number.isInteger(uiPort) && uiPort > 0 ? uiPort : DEFAULT_UI_PORT,\n open: opts.open === false ? false : DEBUG_ROUTE,\n },\n logLevel: \"warn\",\n });\n await ui.listen();\n\n const local = ui.resolvedUrls?.local[0] ?? `http://localhost:${DEFAULT_UI_PORT}/`;\n const url = `${local.replace(/\\/$/, \"\")}${DEBUG_ROUTE}`;\n console.log(`${chalk.cyan(\"aiui debug\")} — the lowering-trace viewer`);\n console.log(` viewing ${chalk.bold(url)}`);\n console.log(\n chalk.dim(` channel \"${server.tag}\" (${server.cwd}) on port ${server.port}`) +\n chalk.dim(\" — switch channels from the page's header. Ctrl-C to stop.\"),\n );\n}\n","/**\n * `aiui demo [dir]` — scaffold a disposable, runnable demo playground.\n *\n * The repo's `pnpm demo` serves `packages/aiui-demo` straight out of the\n * checkout — fine for developers, but every agent edit lands in the working\n * tree and wants to ride along upstream. This command is the outside-world\n * version: it copies a small sample app (Vite + the `aiuiDevOverlay()`\n * integration, real source) into a directory of the user's own, makes it a\n * standalone git repo, installs its dependencies, and prints how to run the\n * loop. Agent chaos stays in the sandbox, like a much-mutated notebook —\n * except versioned and nowhere near this repo.\n *\n * Designed for `npx @habemus-papadum/aiui demo my-demo` and for **re-running**:\n * the scaffold marks its package.json (`\"aiui\": { \"demo\": true }`), and a\n * marked directory is never re-scaffolded — a second run just tops up\n * `node_modules` if needed and reprints the next steps, so a demo in progress\n * (including everything the agent changed) continues exactly where it was.\n * The scaffold also lists `@habemus-papadum/aiui` as its own devDependency, so\n * after the one `npm install`, `npx aiui …` inside the directory resolves\n * locally — no repeated downloads.\n */\nimport {\n cpSync,\n existsSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { execa } from \"execa\";\nimport { printError, printNote } from \"../util/ui\";\nimport { VERSION } from \"../util/version\";\nimport { commandExists } from \"../util/which\";\n\nexport interface DemoOptions {\n /** Scaffold only — skip `npm install` (used by the packaging test/CI). */\n skipInstall?: boolean;\n}\n\n/** What `aiui demo` finds at the target path. */\nexport type DemoTargetState = \"new\" | \"existing-demo\" | \"occupied\";\n\nexport async function runDemo(dir: string | undefined, opts: DemoOptions = {}): Promise<void> {\n const target = resolve(process.cwd(), dir ?? \"aiui-demo\");\n\n switch (classifyDemoTarget(target)) {\n case \"occupied\":\n printError(\n `${target} already exists and isn't an aiui demo`,\n \"Pick an empty (or new) directory — the scaffold never overwrites existing content.\",\n );\n process.exitCode = 1;\n return;\n case \"existing-demo\":\n printNote(`existing demo found at ${target} — continuing where it left off`);\n break;\n case \"new\": {\n const template = templateRoot();\n if (!template) {\n printError(\"the demo template did not ship with this aiui install\");\n process.exitCode = 1;\n return;\n }\n scaffoldDemo(template, target);\n console.log(`scaffolded the demo playground at ${target}`);\n await initGitRepo(target);\n break;\n }\n }\n\n if (!opts.skipInstall && !existsSync(join(target, \"node_modules\"))) {\n if (!commandExists(\"npm\")) {\n printNote(\"npm not found on PATH — run your package manager's install in the demo yourself\");\n } else {\n console.log(\"installing dependencies (one time)…\");\n const result = await execa(\"npm\", [\"install\", \"--no-audit\", \"--no-fund\"], {\n cwd: target,\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n printError(\"npm install failed — fix that, then re-run this command to continue\");\n process.exitCode = result.exitCode;\n return;\n }\n }\n }\n\n printNextSteps(target);\n}\n\n/**\n * Decide what to do with the target: `new` (missing or empty), an\n * `existing-demo` (package.json carries the scaffold marker — continue), or\n * `occupied` (anything else — refuse; never clobber unknown content).\n */\nexport function classifyDemoTarget(target: string): DemoTargetState {\n if (!existsSync(target)) {\n return \"new\";\n }\n let entries: string[];\n try {\n entries = readdirSync(target);\n } catch {\n return \"occupied\"; // a file, or unreadable — either way, not ours\n }\n if (entries.length === 0) {\n return \"new\";\n }\n try {\n const pkg = JSON.parse(readFileSync(join(target, \"package.json\"), \"utf8\")) as {\n aiui?: { demo?: boolean };\n };\n if (pkg.aiui?.demo === true) {\n return \"existing-demo\";\n }\n } catch {}\n return \"occupied\";\n}\n\n/**\n * The dependency range the scaffold pins aiui packages to: this build's exact\n * release line when it has one, `latest` from a dev build (whose `0.0.0+dev`\n * doesn't exist on the registry).\n */\nexport function demoDependencyRange(version: string): string {\n return /^\\d+\\.\\d+\\.\\d+$/.test(version) ? `^${version}` : \"latest\";\n}\n\n/** Copy the template, restore dot-paths npm strips, and pin dependency ranges. */\nexport function scaffoldDemo(template: string, target: string): void {\n mkdirSync(target, { recursive: true });\n cpSync(template, target, { recursive: true });\n // `npm install` strips dot-files/-dirs from an installed tarball, so the\n // template ships them undotted and the scaffold puts the dot back (the\n // `.gitignore`).\n if (existsSync(join(target, \"gitignore\"))) {\n renameSync(join(target, \"gitignore\"), join(target, \".gitignore\"));\n }\n const pkgFile = join(target, \"package.json\");\n writeFileSync(\n pkgFile,\n readFileSync(pkgFile, \"utf8\").replaceAll(\n \"__AIUI_VERSION_RANGE__\",\n demoDependencyRange(VERSION),\n ),\n );\n}\n\n/**\n * Make the sandbox its own git repo (best-effort): agent edits become\n * inspectable local history and can't wander into any surrounding project.\n * Skipped when the target already sits inside a work tree.\n */\nasync function initGitRepo(target: string): Promise<void> {\n if (!commandExists(\"git\")) {\n return;\n }\n const inside = await execa(\"git\", [\"-C\", target, \"rev-parse\", \"--is-inside-work-tree\"], {\n reject: false,\n });\n if (inside.exitCode === 0) {\n return;\n }\n const init = await execa(\"git\", [\"-C\", target, \"init\", \"--quiet\"], { reject: false });\n if (init.exitCode !== 0) {\n return;\n }\n await execa(\"git\", [\"-C\", target, \"add\", \"-A\"], { reject: false });\n // May fail without a user.name/email configured; the repo alone is enough.\n await execa(\"git\", [\"-C\", target, \"commit\", \"--quiet\", \"-m\", \"aiui demo scaffold\"], {\n reject: false,\n });\n}\n\n/**\n * The template directory shipped with this install. Probed upward from this\n * module because the relative depth differs between layouts: `dist/cli.js`\n * (bundled, installed) sits one level below the package root; the tsx-run\n * `src/commands/demo.ts` sits two below.\n */\nexport function templateRoot(): string | undefined {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 4; i++) {\n const candidate = join(dir, \"templates\", \"demo\");\n if (existsSync(join(candidate, \"package.json\"))) {\n return candidate;\n }\n dir = dirname(dir);\n }\n return undefined;\n}\n\nfunction printNextSteps(target: string): void {\n const rel = relative(process.cwd(), target) || \".\";\n const rerun = rel === \"aiui-demo\" ? \"aiui demo\" : `aiui demo ${rel}`;\n console.log(`\ndemo ready. Run the loop:\n\n cd ${rel}\n npm run claude # terminal 1 — Claude Code with the aiui channel + session browser\n npm run dev # terminal 2 — the demo app (Vite + the intent tool)\n\nthen open the app in the session browser (the window you share with the agent):\n\n npx aiui open http://localhost:5173\n\nClick the ✳ aiui button on the page and type an intent — it lands in the session\nas a prompt. Re-run \\`${rerun}\\` anytime to continue this sandbox.`);\n}\n","import { execa } from \"execa\";\nimport { type CliInvocation, resolvePackageCli } from \"../util/resolve-cli\";\nimport { printError } from \"../util/ui\";\n\nconst CHANNEL_PKG = \"@habemus-papadum/aiui-claude-channel\";\n\n/**\n * Forward to the aiui Claude channel CLI (`aiui-claude-channel`).\n *\n * `aiui mcp <args...>` runs the channel package's own CLI with `<args...>`, so\n * `aiui mcp quick --tag <t> --message \"...\"` is exactly\n * `aiui-claude-channel quick --tag <t> --message \"...\"`. This surfaces the\n * user-facing channel commands under `aiui` without moving them out of the\n * package that owns the MCP server Claude Code spawns.\n *\n * Like `aiui vite`, the channel CLI is a declared dependency, so we resolve it\n * from node_modules (tsx-from-source in a dev checkout, the built `dist` when\n * installed; see {@link resolvePackageCli}) rather than looking on the PATH.\n */\nexport async function runMcp(passthrough: string[] = []): Promise<void> {\n let channel: CliInvocation;\n try {\n channel = resolvePackageCli(CHANNEL_PKG);\n } catch {\n printError(\n \"The aiui Claude channel CLI is not available\",\n \"`@habemus-papadum/aiui-claude-channel` should be installed as a dependency of aiui — try reinstalling.\",\n );\n process.exitCode = 1;\n return;\n }\n\n // stdio inherit so interactive channel commands (e.g. `quick`'s selector) own\n // the terminal; reject:false so a non-zero child exit becomes our exit code.\n const result = await execa(channel.command, [...channel.args, ...passthrough], {\n stdio: \"inherit\",\n reject: false,\n });\n if (result.exitCode) {\n process.exitCode = result.exitCode;\n }\n}\n","/**\n * `aiui paint url` — where should the iPad point its browser?\n *\n * The paint surface rides each channel's one web server, so the URL is just\n * `http://<address>:<channelPort>/paint/` — the open question is which\n * addresses can reach it. This command finds every running channel via the\n * on-disk registry, confirms the paint sidecar answers `/paint/info`, and reads\n * the channel's bind from `/health`: a host-bound channel gets its LAN URL(s)\n * printed (open one on the iPad — macOS Universal Clipboard pastes straight\n * across); a loopback-bound channel gets the loopback URL plus a reminder that\n * reaching it from the iPad is a tunnel of the user's own making (Tailscale,\n * `ssh -L` — see docs/guide/paint-stream).\n *\n * A channel without the paint sidecar simply doesn't answer `/paint/info`.\n */\nimport { networkInterfaces } from \"node:os\";\nimport { listMcpServers } from \"@habemus-papadum/aiui-claude-channel\";\nimport chalk from \"chalk\";\n\n/** One channel's paint surface, as printed. */\ninterface PaintTarget {\n cwd: string;\n pid: number;\n port: number;\n /** Whether the channel is bound beyond loopback (LAN-reachable). */\n lan: boolean;\n urls: string[];\n hosts: number;\n clients: number;\n}\n\n/** Non-internal IPv4 addresses — the URLs an iPad on the LAN can open. (The\n * demo server keeps its own copy; the sidecar itself no longer needs one.) */\nfunction lanAddresses(): string[] {\n const out: string[] = [];\n for (const addrs of Object.values(networkInterfaces())) {\n for (const addr of addrs ?? []) {\n if (addr.family === \"IPv4\" && !addr.internal) {\n out.push(addr.address);\n }\n }\n }\n return out;\n}\n\n/** Fetch a channel route's JSON; undefined on any failure (stale registry\n * entries, a channel predating the route). */\nasync function getJson<T>(port: number, path: string): Promise<T | undefined> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}${path}`, {\n signal: AbortSignal.timeout(1500),\n });\n if (!res.ok) {\n return undefined;\n }\n return (await res.json()) as T;\n } catch {\n return undefined;\n }\n}\n\n/** Ask one channel about its paint surface; undefined when it has no paint\n * sidecar (or is not actually listening any more). */\nasync function queryChannel(\n port: number,\n): Promise<{ lan: boolean; urls: string[]; hosts: number; clients: number } | undefined> {\n const info = await getJson<{ ok?: boolean; hosts?: number; clients?: number }>(\n port,\n \"/paint/info\",\n );\n if (!info?.ok) {\n return undefined;\n }\n // The bind rides on /health; a channel too old to report it is loopback-only.\n const health = await getJson<{ host?: string }>(port, \"/health\");\n const lan = health?.host !== undefined && health.host !== \"127.0.0.1\";\n return {\n lan,\n urls: lan\n ? lanAddresses().map((ip) => `http://${ip}:${port}/paint/`)\n : [`http://127.0.0.1:${port}/paint/`],\n hosts: info.hosts ?? 0,\n clients: info.clients ?? 0,\n };\n}\n\nexport async function runPaintUrl(opts: { json?: boolean } = {}): Promise<void> {\n const servers = listMcpServers();\n const targets: PaintTarget[] = [];\n for (const server of servers) {\n const paint = await queryChannel(server.port);\n if (paint) {\n targets.push({ cwd: server.cwd, pid: server.pid, port: server.port, ...paint });\n }\n }\n\n if (opts.json) {\n console.log(JSON.stringify({ targets }, null, 2));\n return;\n }\n\n if (targets.length === 0) {\n console.log(\"No running channel is hosting the paint surface.\");\n console.log(\"\");\n console.log(\n `It is on by default — start a session with ${chalk.cyan(\"aiui claude\")}. If it's off here,`,\n );\n console.log(\n `check for ${chalk.cyan(\"sidecars.paint false\")} in config or a --aiui-no-sidecar flag.`,\n );\n process.exitCode = 1;\n return;\n }\n\n for (const target of targets) {\n console.log(\"\");\n console.log(`${chalk.bold(target.cwd)} ${chalk.dim(`(channel :${target.port})`)}`);\n for (const url of target.urls.length ? target.urls : [\"(no LAN address found)\"]) {\n console.log(` ${chalk.cyan(url)}`);\n }\n const reach = target.lan\n ? \"open the URL on the iPad (same Wi-Fi)\"\n : \"loopback-only — the iPad needs a tunnel to this port (Tailscale, ssh -L), \" +\n \"or relaunch with --aiui-bind host\";\n console.log(\n chalk.dim(` ${target.hosts} browser host(s), ${target.clients} viewer(s) — ${reach}`),\n );\n }\n console.log(\"\");\n}\n","import { Command } from \"commander\";\nimport { type BrowserOptions, runBrowser, runOpen } from \"./commands/browser\";\nimport { runChrome } from \"./commands/chrome\";\nimport { runClaude } from \"./commands/claude\";\nimport { type CleanOptions, runClean } from \"./commands/clean\";\nimport {\n runConfigGet,\n runConfigSet,\n runConfigShow,\n runConfigUnset,\n type ShowOptions,\n type WriteOptions,\n} from \"./commands/config\";\nimport { runConfigTui } from \"./commands/config-tui\";\nimport { type DebugOptions, runDebug } from \"./commands/debug\";\nimport { type DemoOptions, runDemo } from \"./commands/demo\";\nimport { runMcp } from \"./commands/mcp\";\nimport { runPaintUrl } from \"./commands/paint\";\nimport { runVite } from \"./commands/vite\";\n\nimport { VERSION } from \"./util/version\";\n\n/**\n * Build the `aiui` command tree.\n *\n * Kept separate from the executable entrypoint (cli.ts) so tests can construct\n * and inspect the program without actually running it.\n */\nexport function buildProgram(): Command {\n const program = new Command();\n\n program\n .name(\"aiui\")\n .description(\"ai ui frontends — thin launchers for Claude, Vite, and the channel CLI\")\n .version(VERSION)\n // Only treat options as aiui's own when they come *before* the subcommand.\n // Without this, commander parses interspersed options and would swallow e.g.\n // `aiui vite --version` as aiui's own --version instead of forwarding it.\n .enablePositionalOptions();\n\n // Both subcommands are thin wrappers: everything after the subcommand name is\n // forwarded verbatim to the underlying tool. allowUnknownOption + helpOption(false)\n // stop commander from intercepting flags like `--resume` or `--help`, and the\n // variadic `[args...]` collects them for the action to pass through.\n program\n .command(\"claude\")\n .description(\"launch Claude (extra args are forwarded, e.g. `aiui claude --resume`)\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to claude\")\n .action((args: string[]) => runClaude(args));\n\n program\n .command(\"vite\")\n .description(\"launch Vite (extra args are forwarded, e.g. `aiui vite dev`)\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to vite\")\n .action((args: string[]) => runVite(args));\n\n // The standalone trace-debugger frontend: the channel serves no HTML, so\n // this serves the shared debug-ui viewer against a picked running channel\n // (with an in-page switcher for the rest of the machine's channels).\n program\n .command(\"debug\")\n .description(\"open the lowering-trace viewer for a running channel (switchable in-page)\")\n .option(\"--mcp <tag>\", \"target a channel by registry tag (skips the selector)\")\n .option(\"--port <port>\", \"UI port for the viewer's dev server (default 4747)\")\n .option(\"--no-open\", \"don't open the browser\")\n .action((opts: DebugOptions) => runDebug(opts));\n\n // Unlike its siblings, `aiui chrome` is a real subcommand (not a forwarding\n // wrapper): it manages the agent's browser — the Chrome for Testing install,\n // launch status, and the devtools extension path.\n program\n .command(\"chrome\")\n .description(\"manage the agent's browser: install | update | status | extension\")\n .argument(\"<action>\", \"install | update | status | extension\")\n .action((action: string) => runChrome([action]));\n\n // The shared session browser (human + agent in one window). `browser` starts\n // or finds it — locally before/without a session, or on your local machine\n // for remote development (tunnel its debug port to the remote box).\n program\n .command(\"browser\")\n .description(\n \"start (or find) the shared session browser; --tunnel does the whole remote-dev local half\",\n )\n .option(\n \"--profile <name>\",\n \"named profile (project .aiui-cache/chrome/, or the user cache with --tunnel)\",\n )\n .option(\"--data-dir <path>\", \"explicit Chrome user data dir\")\n .option(\"--port <port>\", \"fixed local DevTools debug port (default: OS-assigned)\")\n .option(\"--headless\", \"launch with no UI\")\n .option(\"--open <url>\", \"also open this URL in it\")\n .option(\n \"--tunnel <[user@]host>\",\n \"reverse-tunnel the debug port to this host (Ctrl-C closes it)\",\n )\n .option(\"--remote-port <port>\", \"fixed port on the tunnel's remote side (default: 9222)\")\n .action((opts: BrowserOptions) => runBrowser(opts));\n\n // A disposable, npx-able playground: scaffolds a sample app into the user's\n // own directory (its own git repo — agent edits stay in the sandbox) and is\n // safe to re-run: an existing demo continues instead of being re-scaffolded.\n program\n .command(\"demo\")\n .description(\"scaffold a runnable demo playground (safe to re-run; default dir: aiui-demo)\")\n .argument(\"[dir]\", \"target directory (default: aiui-demo)\")\n .option(\"--skip-install\", \"scaffold only — don't run npm install\")\n .action((dir: string | undefined, opts: DemoOptions) => runDemo(dir, opts));\n\n // Reset aiui's on-disk state to a fresh-install slate — the two cache roots\n // (this repo's .aiui-cache/ and the user cache, including the managed Chrome\n // for Testing). For clean demos of the install/first-run flow.\n program\n .command(\"clean\")\n .description(\n \"reset aiui state (project + user cache, incl. Chrome for Testing) for a clean-slate demo\",\n )\n .option(\"--project-only\", \"only this repo's .aiui-cache/\")\n .option(\"--user-only\", \"only the user cache (~/.cache/aiui)\")\n .option(\"--keep-browser\", \"keep Chrome for Testing (skip the ~160 MB re-download)\")\n .option(\"-n, --dry-run\", \"print what would be deleted, then stop\")\n .option(\"-y, --yes\", \"delete without the confirmation prompt\")\n .action((opts: CleanOptions) => runClean(opts));\n\n program\n .command(\"open\")\n .description(\"open a URL in the session browser, e.g. `aiui open http://localhost:5173`\")\n .argument(\"<url>\", \"the URL to open\")\n .option(\"--profile <name>\", \"named profile under .aiui-cache/chrome/\")\n .option(\"--data-dir <path>\", \"explicit Chrome user data dir\")\n .action((url: string, opts: Pick<BrowserOptions, \"profile\" | \"dataDir\">) => runOpen(url, opts));\n\n // The two-level config.json, self-documenting: every subcommand renders from\n // the same schema table validation uses (util/config-schema.ts). Bare\n // `aiui config` opens the interactive browser.\n const config = program\n .command(\"config\")\n .description(\"inspect and edit aiui's config.json — tui | show | get | set | unset\")\n .action(() => runConfigTui());\n config\n .command(\"tui\")\n .description(\"browse every setting interactively: docs, defaults, current values, editing\")\n .action(() => runConfigTui());\n config\n .command(\"show\")\n .description(\"every key with its effective value and which file set it\")\n .option(\"--json\", \"machine-readable: file paths, per-level values, effective merge\")\n .action((opts: ShowOptions) => runConfigShow(opts));\n config\n .command(\"get\")\n .description(\"print a key's effective value (provenance goes to stderr)\")\n .argument(\"<key>\", 'dotted key, e.g. \"chrome.mode\"')\n .action((key: string) => runConfigGet(key));\n config\n .command(\"set\")\n .description(\"set a key in the user config (or the project's with --project)\")\n .argument(\"<key>\", 'dotted key, e.g. \"chrome.mode\"')\n .argument(\"<value>\", \"the new value, validated against the schema\")\n .option(\"--project\", \"write .aiui-cache/config.json here instead of the user config\")\n .action((key: string, value: string, opts: WriteOptions) => runConfigSet(key, value, opts));\n config\n .command(\"unset\")\n .description(\"remove a key from the user config (or the project's with --project)\")\n .argument(\"<key>\", 'dotted key, e.g. \"claude.skipPermissions\"')\n .option(\"--project\", \"remove from .aiui-cache/config.json here instead of the user config\")\n .action((key: string, opts: WriteOptions) => runConfigUnset(key, opts));\n\n // `aiui mcp <args...>` forwards to the aiui-claude-channel CLI, so the\n // user-facing channel commands live under `aiui` (e.g. `aiui mcp quick`)\n // without duplicating them or moving them off the package that owns the\n // in-process MCP server.\n program\n .command(\"mcp\")\n .description(\"run a channel command (forwards to aiui-claude-channel), e.g. `aiui mcp quick`\")\n .allowUnknownOption()\n .allowExcessArguments()\n .helpOption(false)\n .argument(\"[args...]\", \"arguments forwarded to the aiui-claude-channel CLI\")\n .action((args: string[]) => runMcp(args));\n\n // `aiui paint …` — the iPad paint stream. `url` prints where the iPad should\n // point its browser: the paint surface rides each channel's one web server\n // (`/paint/` on the channel port), so this resolves every running channel\n // that answers `/paint/info` — plus whether its bind makes it LAN-reachable —\n // so you can copy-paste the URL.\n const paint = program\n .command(\"paint\")\n .description(\"the iPad paint stream — url (where the iPad should connect)\");\n paint\n .command(\"url\")\n .description(\"print the URL(s) an iPad should open, per running paint-enabled channel\")\n .option(\"--json\", \"machine-readable targets\")\n .action((opts: { json?: boolean }) => runPaintUrl(opts));\n\n return program;\n}\n","import { buildProgram } from \"./program\";\n\n// Executable entrypoint for the `aiui` bin. The `#!/usr/bin/env node` shebang is\n// prepended to the built dist/cli.js by a rollup banner (see vite.config.ts), so\n// this source stays valid TypeScript. This file is only ever executed, never\n// imported — the testable logic lives in program.ts.\nbuildProgram()\n .parseAsync()\n .catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n });\n"],"names":["FOR_TESTING_MODES","CHROME_MODES","CHANNEL_BINDS","CONFIG_SECTIONS","value","isHttpUrl","CHROME_CHANNELS","allConfigFields","section","field","fieldRuntimeType","invalidReason","_a","parseFieldValue","raw","reason","formatConfigValue","describeDefault","url","CONFIG_FILENAME","configPaths","base","join","cacheDir","projectCacheDir","loadAiuiConfig","paths","mergeAiuiConfig","readConfigFile","override","merged","file","text","readFileSync","error","validateConfig","root","asSection","rejectUnknownKeys","s","config","values","f","out","path","type","updateUserConfig","mutate","updateConfigFile","mkdirSync","dirname","writeFileSync","where","known","key","choose","question","choices","defaultKey","rl","createInterface","menu","c","chalk","answer","hit","printError","title","detail","printWarning","printNote","CHECK_TTL_MS","RESOLVE_TIMEOUT_MS","cftCacheDir","create","STATE_FILE","readCftState","writeCftState","patch","dir","compareBuildIds","a","b","as","bs","i","diff","installedCft","existsSync","best","getInstalledBrowsers","Browser","latestStableCft","opts","maxAgeMs","timeoutMs","now","state","platform","detectBrowserPlatform","buildId","withTimeout","resolveBuildId","installCft","fresh","install","others","old","uninstall","ensureLatestCft","report","latest","current","syncChromeForTesting","mode","interactive","offerInstall","offerUpdate","promise","ms","timer","_","reject","resolvePackageCli","packageName","binName","packageRoot","pkg","binRel","firstValue","runningFromSource","srcRel","resolve","bin","CHROME_SERVER_ID","DEVTOOLS_PKG","DEFAULT_CHROME_PROFILE","PROFILE_NAME","chromeDevtoolsEnabled","args","env","isCi","resolveChromeSettings","flagged","dataDir","profile","chromeUserDataDir","ids","devtoolsExtensionDir","realpathSync","buildDevtoolsExtension","tsc","result","execa","bundleScript","bundle","maybeExtensionAutoloadHint","settings","extensionDir","marker","chromeMcpAttachServer","browserUrl","chromeMcpServer","DEFAULT_REMOTE_PORT","runBrowser","chromeCfg","remotePort","debugPort","parsePort","flags","tunnelProfileDir","session","discoverSessionBrowser","openInSessionBrowser","started","startSessionBrowser","runTunnel","printNextSteps","cfg","settle","cft","binary","sessionBrowserBinary","launchSessionBrowser","target","sanitizeHostKey","sshTunnelArgs","localPort","remoteAttachCommand","flag","port","runOpen","_b","runChrome","action","line","printStatus","freshness","effective","running","connection","browser","profilesDir","profiles","readdirSync","e","extension","AIUI_PREFIX","infoFlag","passthrough","splitAiuiArgs","tag","mcp","chrome","noChrome","noBrowser","chromeProfile","chromeDataDir","sidecar","noSidecar","bind","arg","eq","name","TIOCSTI_BY_PLATFORM","PERL_INJECT","DEFAULT_DELAYS_MS","nudgeChannelAck","delaysMs","tiocsti","spawn","SKIP_PERMISSIONS_QUESTION","CHANNEL_BIND_QUESTION","ENTER_NUDGE_QUESTION","ensureLaunchChoices","ask","updated","persist","_c","persistBind","MODELS_URL","DEFAULT_TIMEOUT_MS","preflightOpenAiKey","verify","fetchImpl","controller","res","openAiPreflightMessage","status","reportOpenAiPreflight","message","VERSION","commandExists","command","dirs","delimiter","exts","ext","accessSync","constants","CHANNEL_PKG","PLUGIN_PKG","CHANNEL_SERVER_ID","runClaude","rawArgs","aiuiArgs","info","printClaudeWrapperHelp","forwardToClaude","isInteractiveSession","openaiKey","ensureClaudeOnPath","pluginsRoot","channel","mcpArgs","mcpServers","chromeInfo","chromeServerEntry","launchInfo","enable","disable","on","sidecars","resolveSidecars","mcpConfig","plugins","browserInfo","cleanRoots","planCleanTargets","roots","targets","resolveDeletions","keep","p","children","runClean","cwd","planned","total","size","n","pathSize","formatBytes","clearingUser","removingBrowser","consequences","listMcpServers","freed","failed","rmSync","st","lstatSync","entries","entry","units","readLevels","fieldStates","levels","resolved","userValue","valueIn","projectValue","source","resolveKeyArg","runConfigShow","options","states","presenceSuffix","width","valueCell","runConfigGet","runConfigSet","parsed","writeValue","level","sections","runConfigUnset","removeValue","mutable","runConfigTui","tildify","picked","pickField","editField","Separator","fieldCard","select","lines","wrap","levelCell","actions","askValue","mark","input","home","homedir","word","VITE_PKG","VITE_PORT_ENV","resolveChannelTarget","servers","targetTag","server","runVite","printViteWrapperHelp","forwardToVite","selectMcpServer","vite","resolveVite","child","teeAndDetectLocalUrl","openAppInBrowser","ANSI_CSI","parseViteLocalUrl","match","sink","onUrl","buffer","found","chunk","newline","decideBrowserAction","DEBUG_ROUTE","DEFAULT_UI_PORT","fileURLToPath","candidate","runDebug","viteServer","req","next","uiPort","ui","createServer","aiuiDevOverlay","runDemo","classifyDemoTarget","template","templateRoot","scaffoldDemo","initGitRepo","demoDependencyRange","version","cpSync","renameSync","pkgFile","rel","relative","rerun","runMcp","lanAddresses","addrs","networkInterfaces","addr","getJson","queryChannel","health","lan","ip","runPaintUrl","paint","reach","buildProgram","program","Command"],"mappings":"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"}
|