@habemus-papadum/aiui 0.1.0 → 0.2.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 +427 -350
- package/dist/cli.js.map +1 -1
- package/dist/util/chrome.d.ts +3 -2
- package/dist/util/openai-preflight.d.ts +65 -0
- package/package.json +5 -5
- package/templates/demo/package.json +1 -0
- package/templates/demo/vite.config.ts +7 -1
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/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 is ~0.3s — cheap enough to run on every launch\n * rather than tracking 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 }\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","// 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 { 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 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 = { launcher: \"aiui claude\", chromeDevtools: chromeInfo };\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","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","VERSION","commandExists","command","dirs","delimiter","exts","ext","accessSync","constants","CHANNEL_PKG","PLUGIN_PKG","CHANNEL_SERVER_ID","runClaude","rawArgs","aiuiArgs","info","printClaudeWrapperHelp","forwardToClaude","isInteractiveSession","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/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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"}
|