@habemus-papadum/aiui 0.3.0 → 0.6.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.
Files changed (41) hide show
  1. package/README.md +6 -1
  2. package/dist/cli.js +1121 -1149
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/browser.d.ts +6 -4
  5. package/dist/commands/clean.d.ts +5 -4
  6. package/dist/commands/debug.d.ts +1 -9
  7. package/dist/commands/env.d.ts +36 -0
  8. package/dist/commands/extension.d.ts +46 -0
  9. package/dist/commands/mcp.d.ts +10 -0
  10. package/dist/commands/native-host.d.ts +16 -0
  11. package/dist/commands/pencil-url.d.ts +18 -0
  12. package/dist/commands/vite.d.ts +17 -29
  13. package/dist/config-xLcoLiLT.js +315 -0
  14. package/dist/config-xLcoLiLT.js.map +1 -0
  15. package/dist/index.d.ts +4 -6
  16. package/dist/index.js +4 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/util/aiui-args.d.ts +0 -14
  19. package/dist/util/channel-launch.d.ts +67 -0
  20. package/dist/util/channel-target.d.ts +33 -0
  21. package/dist/util/chrome.d.ts +70 -27
  22. package/dist/util/config-schema.d.ts +23 -1
  23. package/dist/util/config.d.ts +21 -11
  24. package/dist/util/gemini-preflight.d.ts +56 -0
  25. package/dist/util/managed-browser.d.ts +109 -0
  26. package/dist/util/openai-preflight.d.ts +1 -1
  27. package/package.json +9 -9
  28. package/dist/commands/demo.d.ts +0 -28
  29. package/dist/commands/paint.d.ts +0 -3
  30. package/dist/sidecars-BKlhUA0V.js +0 -48
  31. package/dist/sidecars-BKlhUA0V.js.map +0 -1
  32. package/dist/util/cft.d.ts +0 -79
  33. package/dist/util/sidecars.d.ts +0 -62
  34. package/templates/demo/CLAUDE.md +0 -16
  35. package/templates/demo/README.md +0 -36
  36. package/templates/demo/gitignore +0 -4
  37. package/templates/demo/index.html +0 -11
  38. package/templates/demo/package.json +0 -23
  39. package/templates/demo/src/main.ts +0 -51
  40. package/templates/demo/tsconfig.json +0 -11
  41. package/templates/demo/vite.config.ts +0 -17
package/dist/cli.js CHANGED
@@ -1,302 +1,143 @@
1
1
  #!/usr/bin/env node
2
- import { Command as gt } from "commander";
3
- import { join as f, dirname as R, resolve as P, delimiter as bt, relative as yt } from "node:path";
4
- import { CHROME_CHANNELS as vt, cacheDir as Y, packageRoot as L, runningFromSource as kt, isCi as ue, discoverSessionBrowser as F, openInSessionBrowser as H, sessionBrowserBinary as Fe, launchSessionBrowser as Ve, decideBrowserAction as Ct } from "@habemus-papadum/aiui-util";
5
- import { execa as k } from "execa";
6
- import { readFileSync as U, mkdirSync as W, writeFileSync as q, existsSync as v, realpathSync as He, readdirSync as G, accessSync as $t, constants as xt, rmSync as Tt, lstatSync as Pt, cpSync as Et, renameSync as Dt } from "node:fs";
7
- import { detectBrowserPlatform as Ke, resolveBuildId as Ye, Browser as I, getInstalledBrowsers as We, install as It, uninstall as St } from "@puppeteer/browsers";
8
- import { projectCacheDir as de, listMcpServers as J, selectMcpServer as qe } from "@habemus-papadum/aiui-claude-channel";
9
- import { createInterface as Ot } from "node:readline/promises";
2
+ import { Command as yt } from "commander";
3
+ import { join as h, resolve as x, dirname as Oe, delimiter as vt } from "node:path";
4
+ import { packageRoot as K, runningFromSource as $t, isCi as ce, cacheDir as Y, discoverSessionBrowser as U, openInSessionBrowser as H, sessionBrowserBinary as Ue, launchSessionBrowser as Me, decideBrowserAction as kt } from "@habemus-papadum/aiui-util";
5
+ import { execa as E } from "execa";
6
+ import { readFileSync as M, existsSync as v, writeFileSync as W, realpathSync as Ct, mkdirSync as z, readdirSync as q, chmodSync as xt, accessSync as It, constants as Et, rmSync as Pt, lstatSync as Dt } from "node:fs";
7
+ import { projectCacheDir as Le, listMcpServers as L, selectMcpServer as Be, agentsByPid as Re, listClaudeAgents as He } from "@habemus-papadum/aiui-claude-channel";
8
+ import { r as B, D as St, M as Fe, u as N, l as P, a as le, C as $e, m as Tt, d as oe, p as re, c as At, b as ie, e as Ve, f as I, g as Ge, h as Nt } from "./config-xLcoLiLT.js";
10
9
  import l from "chalk";
11
- import { spawn as jt } from "node:child_process";
12
- import { r as Ut } from "./sidecars-BKlhUA0V.js";
13
- import { homedir as At, networkInterfaces as Nt } from "node:os";
14
- import { Separator as ie, select as K, input as _t } from "@inquirer/prompts";
15
- import { fileURLToPath as Ge } from "node:url";
16
- import { aiuiDevOverlay as Rt } from "@habemus-papadum/aiui-dev-overlay/vite";
17
- import { createServer as Lt } from "vite";
18
- const Mt = ["prompt", "auto", "off"], Bt = ["attach", "launch"], se = ["loopback", "host"], M = [
19
- {
20
- name: "claude",
21
- summary: "how `aiui claude` launches Claude Code",
22
- fields: [
23
- {
24
- key: "skipPermissions",
25
- type: "boolean",
26
- default: !0,
27
- defaultText: "true (unset: the first interactive launch asks, then persists the answer)",
28
- summary: "Launch Claude Code with --dangerously-skip-permissions.",
29
- doc: "A personal preference with real consequences (docs/guide/warning): every agent action — shell commands, file writes, network, the browser — runs without asking first. aiui works fine either way. The first interactive launch asks and persists the answer at the user level; when unset, non-interactive sessions fall back to true."
30
- },
31
- {
32
- key: "enterNudge",
33
- type: "boolean",
34
- default: !0,
35
- defaultText: "true (unset: the first interactive launch asks, then persists the answer)",
36
- summary: "Auto-dismiss Claude Code's development-channel acknowledgement prompt.",
37
- doc: "aiui loads a custom development channel, so Claude Code shows a one-key acknowledgement at every startup; this injects a single Enter keystroke into the terminal to dismiss it (best-effort TIOCSTI on /dev/tty — platforms that forbid it harmlessly do nothing). Saying no just means pressing Enter yourself each launch."
38
- }
39
- ]
40
- },
41
- {
42
- name: "channel",
43
- summary: "the channel server's web backend",
44
- fields: [
45
- {
46
- key: "bind",
47
- type: "enum",
48
- values: se,
49
- default: "loopback",
50
- defaultText: '"loopback" (unset: the first interactive launch asks, then persists the answer)',
51
- summary: "Which interface the channel web server binds: loopback, or host (LAN).",
52
- doc: '"host" (0.0.0.0) makes the session\'s whole web surface — the iPad paint page, but also prompt injection, /debug, and every sidecar — reachable by anyone on your network, UNAUTHENTICATED. That is the trusted-LAN posture (docs/guide/warning): right on a network that is yours alone, wrong on shared Wi-Fi. "loopback" keeps everything this-machine-only; reaching the paint page from an iPad is then up to you — tunnel the channel port however you like (Tailscale, `ssh -L`). The first interactive launch asks and persists the answer at the user level. Per-launch flag: --aiui-bind.'
53
- }
54
- ]
55
- },
56
- {
57
- name: "sidecars",
58
- summary: "which session sidecars `aiui claude` asks the channel to host",
59
- fields: [
60
- {
61
- key: "paint",
62
- type: "boolean",
63
- default: !0,
64
- summary: "Host the iPad paint sidecar (on the channel's own port).",
65
- doc: "The iPad paint stream (docs/guide/paint-stream) rides the channel's one port — no extra process, no extra listener — so it is on by default; false turns it off. Whether an iPad can actually reach it is channel.bind's call (host, or a tunnel you own). Per-launch flags win: --aiui-sidecar paint / --aiui-no-sidecar paint. `aiui paint url` prints the URL to open on the iPad."
66
- }
67
- ]
68
- },
69
- {
70
- name: "chrome",
71
- summary: "the agent's browser and the Chrome DevTools MCP",
72
- fields: [
73
- {
74
- key: "enabled",
75
- type: "boolean",
76
- default: !0,
77
- summary: "Attach the Chrome DevTools MCP.",
78
- doc: "false turns it off everywhere; true restates the default and does NOT override the CI default-off — only the --aiui-chrome flag forces it on under CI."
79
- },
80
- {
81
- key: "mode",
82
- type: "enum",
83
- values: Bt,
84
- default: "attach",
85
- summary: "How the MCP reaches a browser: shared session browser, or its own.",
86
- doc: `"attach" shares a user-visible session browser: an already-running one is discovered by profile, or an interactive launch starts one eagerly. "launch" is the hands-off mode: chrome-devtools-mcp launches its own private browser lazily, on the agent's first browser tool call.`
87
- },
88
- {
89
- key: "browserUrl",
90
- type: "string",
91
- defaultText: "unset (manage a browser locally)",
92
- summary: "Attach to this DevTools endpoint instead of managing a browser at all.",
93
- doc: 'The remote-development key (docs/guide/remote): the browser runs on another machine (started there with `aiui browser`) and its debug port is tunneled over. Setting it implies mode: "attach" and makes every local-browser setting (profile, executablePath, channel, forTesting…) irrelevant. Per-launch flag: --aiui-browser-url.',
94
- validate: (e) => Ft(String(e)) ? void 0 : 'expected an http(s) URL like "http://127.0.0.1:9222"'
95
- },
96
- {
97
- key: "debugPort",
98
- type: "number",
99
- default: 0,
100
- defaultText: "0 (an OS-assigned free port)",
101
- summary: "Fixed DevTools debug port for session browsers aiui launches.",
102
- doc: "Pin it (e.g. 9222) when something external must find the port — an ssh tunnel, a VS Code attach-to-Chrome launch config. 0 means an OS-assigned free port.",
103
- validate: (e) => Number.isInteger(e) && e >= 0 && e <= 65535 ? void 0 : "expected 0..65535"
104
- },
105
- {
106
- key: "profile",
107
- type: "string",
108
- default: "default",
109
- summary: "Named profile under .aiui-cache/chrome/.",
110
- doc: "Per-launch flag: --aiui-chrome-profile."
111
- },
112
- {
113
- key: "dataDir",
114
- type: "string",
115
- defaultText: "unset (derived from chrome.profile)",
116
- summary: "Explicit Chrome user data dir; takes precedence over chrome.profile.",
117
- doc: "Per-launch flag: --aiui-chrome-data-dir."
118
- },
119
- {
120
- key: "executablePath",
121
- type: "string",
122
- defaultText: "unset (managed Chrome for Testing when installed, else installed Chrome)",
123
- summary: "Chrome binary to launch — e.g. a Chrome for Testing install.",
124
- doc: "Chrome for Testing still honors --load-extension, so the aiui DevTools panel can auto-load. Mutually exclusive with chrome.channel."
125
- },
126
- {
127
- key: "channel",
128
- type: "enum",
129
- values: vt,
130
- defaultText: 'unset ("stable" when launching an installed Chrome)',
131
- summary: "Installed Chrome release channel to launch.",
132
- doc: "Mutually exclusive with chrome.executablePath."
133
- },
134
- {
135
- key: "forTesting",
136
- type: "enum",
137
- values: Mt,
138
- default: "prompt",
139
- summary: "How `aiui claude` manages the recommended Chrome for Testing install.",
140
- doc: '"prompt" asks before installing or updating it — interactive sessions only, never under CI; "auto" installs/updates without asking; "off" never checks. Prompt answers ("automatically", "never ask again") persist here at the user level. Skipped entirely when executablePath or channel picks a browser explicitly.'
141
- },
142
- {
143
- key: "headless",
144
- type: "boolean",
145
- default: !1,
146
- summary: "Launch Chrome with no UI."
147
- },
148
- {
149
- key: "buildExtension",
150
- type: "boolean",
151
- default: !0,
152
- summary: "Rebuild the aiui-devtools-extension whenever a browser starts in a dev checkout.",
153
- doc: "~0.3s of tsc so the auto-loaded DevTools panel is never stale. Only relevant in a dev checkout of pdum_aiui."
154
- }
155
- ]
10
+ import { detectBrowserPlatform as Ke, resolveBuildId as Ye, Browser as ke, getInstalledBrowsers as We, install as _t, uninstall as jt } from "@puppeteer/browsers";
11
+ import { createInterface as Ot } from "node:readline/promises";
12
+ import { homedir as ue, networkInterfaces as ze } from "node:os";
13
+ import { Separator as se, select as _, input as Ut } from "@inquirer/prompts";
14
+ function J(e, t) {
15
+ const n = K(e), o = JSON.parse(M(h(n, "package.json"), "utf8")), r = typeof o.bin == "string" ? o.bin : Mt(o.bin);
16
+ if (!r)
17
+ throw new Error(
18
+ `package ${e} declares no bin`
19
+ );
20
+ if ($t(n)) {
21
+ const i = r.replace(/^\.?\/?dist\//, "src/").replace(/\.js$/, ".ts");
22
+ return { command: process.execPath, args: ["--import", "tsx", x(n, i)] };
156
23
  }
157
- ];
158
- function Je() {
159
- return M.flatMap(
160
- (e) => e.fields.map((t) => ({ section: e, field: t, path: `${e.name}.${t.key}` }))
161
- );
24
+ return { command: process.execPath, args: [x(n, r)] };
162
25
  }
163
- function ze(e) {
164
- return e.type === "enum" ? "string" : e.type;
165
- }
166
- function Xe(e, t) {
167
- var n;
168
- return e.type === "enum" && !(e.values ?? []).includes(String(t)) ? `expected one of: ${(e.values ?? []).join(", ")}` : (n = e.validate) == null ? void 0 : n.call(e, t);
26
+ function Mt(e) {
27
+ return e ? Object.values(e)[0] : void 0;
169
28
  }
170
- function ae(e, t) {
171
- let n;
172
- switch (ze(e)) {
173
- case "boolean": {
174
- if (t !== "true" && t !== "false")
175
- return { error: "expected true or false" };
176
- n = t === "true";
177
- break;
178
- }
179
- case "number": {
180
- if (n = Number(t), t.trim() === "" || Number.isNaN(n))
181
- return { error: "expected a number" };
182
- break;
183
- }
184
- default:
185
- n = t;
186
- }
187
- const o = Xe(e, n);
188
- return o ? { error: o } : { value: n };
29
+ function y(e, t) {
30
+ console.error(`${l.bgRed.white.bold(" ERROR ")} ${l.red.bold(e)}`), t && console.error(l.dim(t));
189
31
  }
190
- function $(e) {
191
- return typeof e == "string" ? JSON.stringify(e) : String(e);
32
+ function C(e, t) {
33
+ console.error(`${l.bgYellow.black.bold(" WARN ")} ${l.yellow.bold(e)}`), t && console.error(l.dim(t));
192
34
  }
193
- function ce(e) {
194
- return e.defaultText ? e.defaultText : e.default === void 0 ? "unset" : $(e.default);
35
+ function g(e, t) {
36
+ console.error(`${l.bgCyan.black.bold(" NOTE ")} ${l.cyan(e)}`), t && console.error(l.dim(t));
195
37
  }
196
- function Ft(e) {
197
- try {
198
- const t = new URL(e);
199
- return t.protocol === "http:" || t.protocol === "https:";
200
- } catch {
201
- return !1;
202
- }
38
+ const Lt = "chrome-devtools", Bt = "@habemus-papadum/aiui-intent-client", Rt = "default", Ht = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
39
+ function qe(e, t = {}, n = process.env) {
40
+ return e.noChrome ? !1 : e.chrome ? !0 : t.enabled === !1 ? !1 : !ce(n);
203
41
  }
204
- const Ee = "config.json";
205
- function he(e = process.cwd()) {
42
+ function T(e, t = {}, n = process.cwd()) {
43
+ if (t.executablePath && t.channel)
44
+ throw new Error(
45
+ "config sets both chrome.executablePath and chrome.channel — they pick the browser two different ways; keep exactly one"
46
+ );
47
+ const o = e.chromeProfile !== void 0 || e.chromeDataDir !== void 0, r = e.chromeDataDir ?? (o ? void 0 : t.dataDir), i = e.chromeProfile ?? (o ? void 0 : t.profile), s = Ft(t);
206
48
  return {
207
- user: f(Y(void 0, { create: !1 }), Ee),
208
- project: f(de(e), Ee)
49
+ userDataDir: Gt({ dataDir: r, profile: i, variant: s }, n),
50
+ variant: s,
51
+ // A configured endpoint means the browser is managed elsewhere (usually
52
+ // another machine) — that's always attach, whatever `mode` says.
53
+ mode: t.browserUrl ? "attach" : t.mode ?? "attach",
54
+ browserUrl: t.browserUrl,
55
+ debugPort: t.debugPort ?? 0,
56
+ executablePath: t.executablePath && x(n, t.executablePath),
57
+ channel: t.channel,
58
+ headless: t.headless ?? !1
209
59
  };
210
60
  }
211
- function V(e = process.cwd()) {
212
- const t = he(e);
213
- return Qe(O(t.user) ?? {}, O(t.project) ?? {});
61
+ function Ft(e) {
62
+ return e.channel ? `chrome-${e.channel}` : e.executablePath ? `custom-${Vt(e.executablePath)}` : B(e);
214
63
  }
215
- function Qe(e, t) {
216
- const n = {};
217
- for (const o of M)
218
- n[o.name] = {
219
- ...e[o.name],
220
- ...t[o.name]
221
- };
222
- return n;
64
+ function Vt(e) {
65
+ let t = 5381;
66
+ for (let n = 0; n < e.length; n++)
67
+ t = (t * 33 ^ e.charCodeAt(n)) >>> 0;
68
+ return t.toString(36);
223
69
  }
224
- function O(e) {
225
- let t;
70
+ function Gt(e, t = process.cwd()) {
71
+ if (e.dataDir)
72
+ return x(t, e.dataDir);
73
+ const n = e.profile ?? Rt;
74
+ if (!Ht.test(n))
75
+ throw new Error(
76
+ `invalid chrome profile name "${n}" — use letters, digits, ".", "_", "-" (or --aiui-chrome-data-dir for an arbitrary path)`
77
+ );
78
+ const o = e.variant ?? St;
79
+ return h(Le(t), "chrome", o, n);
80
+ }
81
+ const Kt = "dist-ext";
82
+ function Yt() {
226
83
  try {
227
- t = U(e, "utf8");
84
+ const e = Ct(K(Bt));
85
+ return { root: e, outDir: h(e, Kt) };
228
86
  } catch {
229
87
  return;
230
88
  }
231
- let n;
232
- try {
233
- n = JSON.parse(t);
234
- } catch (o) {
235
- throw new Error(
236
- `invalid JSON in ${e}: ${o instanceof Error ? o.message : String(o)}`
237
- );
238
- }
239
- return Vt(n, e);
240
89
  }
241
- function Vt(e, t) {
242
- const n = De(e, t, "the top level");
243
- Ie(
244
- n,
245
- M.map((r) => r.name),
246
- t,
247
- "the top level"
90
+ function Wt(e) {
91
+ return e === void 0 ? { state: "absent" } : v(h(e.outDir, "manifest.json")) ? { state: "ready", dir: e.outDir } : { state: "unbuilt", root: e.root };
92
+ }
93
+ function j() {
94
+ return Wt(Yt());
95
+ }
96
+ function Je(e) {
97
+ e.state === "unbuilt" && g(
98
+ "the aiui intent client has no MV3 bundle yet, so this launch won't load it",
99
+ `Build it once: pnpm -C packages/aiui-intent-client build:ext
100
+ then relaunch — or load it into the RUNNING browser:
101
+ pnpm -C packages/aiui-intent-client ext`
248
102
  );
249
- const o = {};
250
- for (const r of M) {
251
- if (n[r.name] === void 0)
252
- continue;
253
- const i = De(n[r.name], t, `"${r.name}"`);
254
- Ie(
255
- i,
256
- r.fields.map((a) => a.key),
257
- t,
258
- `"${r.name}"`
103
+ }
104
+ function Xe(e, t) {
105
+ if (!t.length || e.executablePath)
106
+ return;
107
+ const n = h(e.userDataDir, "aiui-extension-autoload-hint");
108
+ if (!v(n)) {
109
+ g(
110
+ "the aiui intent client can't auto-load into regular Chrome ( 137 ignores --load-extension)",
111
+ `Load it once in the launched Chrome — chrome://extensions → Developer mode → Load unpacked →
112
+ ${t.join(`
113
+ `)}
114
+ — and this profile remembers it. Or use the managed browser (\`aiui chrome install\`),
115
+ which auto-loads it. This note won't repeat for this profile.`
259
116
  );
260
- const s = {};
261
- for (const a of r.fields) {
262
- const c = i[a.key];
263
- if (c === void 0)
264
- continue;
265
- const d = `${r.name}.${a.key}`, p = ze(a);
266
- if (typeof c !== p)
267
- throw new Error(`expected a ${p} for ${d} in ${t}`);
268
- const h = Xe(a, c);
269
- if (h)
270
- throw new Error(
271
- `invalid ${d} ${$(c)} in ${t} — ${h}`
272
- );
273
- s[a.key] = c;
117
+ try {
118
+ W(n, `${(/* @__PURE__ */ new Date()).toISOString()}
119
+ `);
120
+ } catch {
274
121
  }
275
- o[r.name] = s;
276
122
  }
277
- return o;
278
- }
279
- function B(e) {
280
- return fe(he().user, e);
281
- }
282
- function fe(e, t) {
283
- const n = O(e) ?? {};
284
- return t(n), W(R(e), { recursive: !0 }), q(e, `${JSON.stringify(n, null, 2)}
285
- `), e;
286
123
  }
287
- function De(e, t, n) {
288
- if (typeof e != "object" || e === null || Array.isArray(e))
289
- throw new Error(`expected an object at ${n} of ${t}`);
290
- return e;
124
+ function te(e) {
125
+ return {
126
+ command: "npx",
127
+ args: ["-y", "chrome-devtools-mcp@latest", "--browser-url", e]
128
+ };
291
129
  }
292
- function Ie(e, t, n, o) {
293
- for (const r of Object.keys(e))
294
- if (!t.includes(r))
295
- throw new Error(
296
- `unknown key "${r}" at ${o} of ${n} — known keys: ${t.join(", ")}`
297
- );
130
+ function zt(e, t = []) {
131
+ const n = [
132
+ "-y",
133
+ "chrome-devtools-mcp@latest",
134
+ "--userDataDir",
135
+ e.userDataDir,
136
+ "--ignoreDefaultChromeArg=--disable-extensions"
137
+ ];
138
+ return e.executablePath && n.push("--executablePath", e.executablePath), e.channel && n.push("--channel", e.channel), e.headless && n.push("--headless"), t.length && n.push(`--chromeArg=--load-extension=${t.join(",")}`), { command: "npx", args: n };
298
139
  }
299
- async function z(e, t, n) {
140
+ async function X(e, t, n) {
300
141
  const o = Ot({ input: process.stdin, output: process.stderr });
301
142
  try {
302
143
  const r = t.map(
@@ -312,41 +153,60 @@ ${r}
312
153
  return n;
313
154
  continue;
314
155
  }
315
- const a = t.find((c) => c.key === s) ?? t.find((c) => c.label.toLowerCase().startsWith(s));
316
- if (a)
317
- return a.key;
156
+ const c = t.find((a) => a.key === s) ?? t.find((a) => a.label.toLowerCase().startsWith(s));
157
+ if (c)
158
+ return c.key;
318
159
  }
319
160
  } finally {
320
161
  o.close();
321
162
  }
322
163
  }
323
- function g(e, t) {
324
- console.error(`${l.bgRed.white.bold(" ERROR ")} ${l.red.bold(e)}`), t && console.error(l.dim(t));
325
- }
326
- function T(e, t) {
327
- console.error(`${l.bgYellow.black.bold(" WARN ")} ${l.yellow.bold(e)}`), t && console.error(l.dim(t));
328
- }
329
- function y(e, t) {
330
- console.error(`${l.bgCyan.black.bold(" NOTE ")} ${l.cyan(e)}`), t && console.error(l.dim(t));
331
- }
332
- const Ze = 1440 * 60 * 1e3, Ht = 4e3;
333
- function A(e = !0) {
334
- return Y("chrome", { create: e });
335
- }
336
- const et = "update-state.json";
337
- function X() {
164
+ const Ze = 1440 * 60 * 1e3, qt = 4e3, Jt = {
165
+ chromium: {
166
+ flavor: "chromium",
167
+ browser: ke.CHROMIUM,
168
+ latestTag: "latest",
169
+ displayName: "Chromium",
170
+ cacheSubdir: "chromium",
171
+ approxSizeMb: 150
172
+ },
173
+ "chrome-for-testing": {
174
+ flavor: "chrome-for-testing",
175
+ browser: ke.CHROME,
176
+ latestTag: "stable",
177
+ displayName: "Chrome for Testing",
178
+ cacheSubdir: "chrome",
179
+ approxSizeMb: 160
180
+ }
181
+ };
182
+ function k(e) {
183
+ return Jt[e];
184
+ }
185
+ function A(e, t = !0) {
186
+ return Y(k(e).cacheSubdir, { create: t });
187
+ }
188
+ function Xt(e = !1) {
189
+ return Fe.map((t) => A(t, e));
190
+ }
191
+ const Qe = "update-state.json";
192
+ function Z(e) {
338
193
  try {
339
- return JSON.parse(U(f(A(!1), et), "utf8"));
194
+ return JSON.parse(
195
+ M(h(A(e, !1), Qe), "utf8")
196
+ );
340
197
  } catch {
341
198
  return {};
342
199
  }
343
200
  }
344
- function Q(e) {
345
- const t = A();
346
- W(t, { recursive: !0 }), q(f(t, et), `${JSON.stringify({ ...X(), ...e })}
347
- `);
201
+ function Q(e, t) {
202
+ const n = A(e);
203
+ z(n, { recursive: !0 }), W(
204
+ h(n, Qe),
205
+ `${JSON.stringify({ ...Z(e), ...t })}
206
+ `
207
+ );
348
208
  }
349
- function pe(e, t) {
209
+ function de(e, t) {
350
210
  const n = e.split(".").map(Number), o = t.split(".").map(Number);
351
211
  for (let r = 0; r < Math.max(n.length, o.length); r++) {
352
212
  const i = (n[r] ?? 0) - (o[r] ?? 0);
@@ -355,133 +215,134 @@ function pe(e, t) {
355
215
  }
356
216
  return 0;
357
217
  }
358
- async function me() {
359
- const e = A(!1);
360
- if (!v(e))
218
+ async function F(e) {
219
+ const t = k(e), n = A(e, !1);
220
+ if (!v(n))
361
221
  return;
362
- const o = (await We({ cacheDir: e })).filter((r) => r.browser === I.CHROME).sort((r, i) => pe(r.buildId, i.buildId)).at(-1);
363
- return o && { buildId: o.buildId, executablePath: o.executablePath };
364
- }
365
- async function we(e = {}) {
366
- const { maxAgeMs: t = Ze, timeoutMs: n = Ht, now: o = Date.now() } = e, r = X();
367
- if (r.latestBuildId && r.checkedAt && o - r.checkedAt < t)
368
- return r.latestBuildId;
369
- const i = Ke();
370
- if (i)
222
+ const r = (await We({ cacheDir: n })).filter((i) => i.browser === t.browser).sort((i, s) => de(i.buildId, s.buildId)).at(-1);
223
+ return r && { flavor: e, buildId: r.buildId, executablePath: r.executablePath };
224
+ }
225
+ async function he(e, t = {}) {
226
+ const { maxAgeMs: n = Ze, timeoutMs: o = qt, now: r = Date.now() } = t, i = Z(e);
227
+ if (i.latestBuildId && i.checkedAt && r - i.checkedAt < n)
228
+ return i.latestBuildId;
229
+ const s = Ke();
230
+ if (s)
371
231
  try {
372
- const s = await qt(
373
- Ye(I.CHROME, i, "stable"),
374
- n
232
+ const c = k(e), a = await tn(
233
+ Ye(c.browser, s, c.latestTag),
234
+ o
375
235
  );
376
- return Q({ checkedAt: o, latestBuildId: s }), s;
236
+ return Q(e, { checkedAt: r, latestBuildId: a }), a;
377
237
  } catch {
378
- return r.latestBuildId;
238
+ return i.latestBuildId;
379
239
  }
380
240
  }
381
- async function S(e) {
382
- const t = A(), n = await It({
383
- browser: I.CHROME,
384
- buildId: e,
385
- cacheDir: t,
241
+ async function D(e, t) {
242
+ const n = k(e), o = A(e), r = await _t({
243
+ browser: n.browser,
244
+ buildId: t,
245
+ cacheDir: o,
386
246
  downloadProgressCallback: "default"
387
- }), o = (await We({ cacheDir: t })).filter(
388
- (r) => r.browser === I.CHROME && r.buildId !== e
247
+ }), i = (await We({ cacheDir: o })).filter(
248
+ (s) => s.browser === n.browser && s.buildId !== t
389
249
  );
390
- for (const r of o)
391
- await St({ browser: I.CHROME, buildId: r.buildId, cacheDir: t });
392
- return { buildId: e, executablePath: n.executablePath };
393
- }
394
- async function Kt(e) {
395
- const t = Ke();
396
- if (!t)
397
- throw new Error("could not detect a supported platform for Chrome for Testing");
398
- const n = await Ye(I.CHROME, t, "stable");
399
- Q({ checkedAt: Date.now(), latestBuildId: n });
400
- const o = await me();
401
- if (o && pe(o.buildId, n) >= 0)
402
- return e(`Chrome for Testing ${o.buildId} is up to date`), { ...o, outcome: "current" };
403
- e(
404
- o ? `updating Chrome for Testing ${o.buildId} → ${n}…` : `installing Chrome for Testing ${n}…`
250
+ for (const s of i)
251
+ await jt({ browser: n.browser, buildId: s.buildId, cacheDir: o });
252
+ return { flavor: e, buildId: t, executablePath: r.executablePath };
253
+ }
254
+ async function Zt(e, t) {
255
+ const n = k(e), o = Ke();
256
+ if (!o)
257
+ throw new Error(`could not detect a supported platform for ${n.displayName}`);
258
+ const r = await Ye(n.browser, o, n.latestTag);
259
+ Q(e, { checkedAt: Date.now(), latestBuildId: r });
260
+ const i = await F(e);
261
+ if (i && de(i.buildId, r) >= 0)
262
+ return t(`${n.displayName} ${i.buildId} is up to date`), { ...i, outcome: "current" };
263
+ t(
264
+ i ? `updating ${n.displayName} ${i.buildId} → ${r}…` : `installing ${n.displayName} ${r}…`
405
265
  );
406
- const r = await S(n);
407
- return e(`Chrome for Testing ${n} installed at ${r.executablePath}`), { ...r, outcome: o ? "updated" : "installed" };
266
+ const s = await D(e, r);
267
+ return t(`${n.displayName} ${r} installed at ${s.executablePath}`), { ...s, outcome: i ? "updated" : "installed" };
408
268
  }
409
- async function tt(e) {
410
- const { mode: t, interactive: n, now: o = Date.now() } = e, r = await me();
411
- if (t === "off" || !n)
412
- return r == null ? void 0 : r.executablePath;
413
- if (!r)
414
- return Yt(t, o);
415
- const i = await we({ now: o });
416
- return !i || pe(i, r.buildId) <= 0 ? r.executablePath : Wt(t, r, i);
269
+ async function et(e) {
270
+ const { flavor: t, mode: n, interactive: o, now: r = Date.now() } = e, i = await F(t);
271
+ if (n === "off" || !o)
272
+ return i == null ? void 0 : i.executablePath;
273
+ if (!i)
274
+ return Qt(t, n, r);
275
+ const s = await he(t, { now: r });
276
+ return !s || de(s, i.buildId) <= 0 ? i.executablePath : en(t, n, i, s);
417
277
  }
418
- async function Yt(e, t) {
419
- const n = await we({ now: t });
420
- if (!n)
278
+ async function Qt(e, t, n) {
279
+ const o = k(e), r = await he(e, { now: n });
280
+ if (!r)
421
281
  return;
422
- if (e === "auto")
423
- return y(`installing Chrome for Testing ${n} (chrome.forTesting: "auto")…`), (await S(n)).executablePath;
424
- const o = X();
425
- if (o.installDeclinedAt && t - o.installDeclinedAt < Ze)
282
+ if (t === "auto")
283
+ return g(`installing ${o.displayName} ${r} (chrome.manage: "auto")…`), (await D(e, r)).executablePath;
284
+ const i = Z(e);
285
+ if (i.installDeclinedAt && n - i.installDeclinedAt < Ze)
426
286
  return;
427
- const r = await z(
428
- `Chrome for Testing isn't installed. It's the recommended browser for aiui — version-pinned, separate from your real Chrome, and it auto-loads the aiui DevTools panel (branded Chrome can't). Download ${n} (~160 MB) to ${A(!1)}?`,
287
+ const s = await X(
288
+ `${o.displayName} isn't installed. It's the recommended browser for aiui — version-pinned, separate from your real Chrome, and it auto-loads the aiui intent client (branded Chrome can't). Download ${r} (~${o.approxSizeMb} MB) to ${A(e, !1)}?`,
429
289
  [
430
290
  { key: "y", label: "yes, install it" },
431
291
  { key: "n", label: "not now — use the regular Chrome (asks again tomorrow)" },
432
- { key: "never", label: 'never — stop offering (writes chrome.forTesting: "off")' }
292
+ { key: "never", label: 'never — stop offering (writes chrome.manage: "off")' }
433
293
  ],
434
294
  "y"
435
295
  );
436
- if (r === "y")
437
- return (await S(n)).executablePath;
438
- if (r === "never") {
439
- const i = B((s) => {
440
- s.chrome = { ...s.chrome, forTesting: "off" };
296
+ if (s === "y")
297
+ return (await D(e, r)).executablePath;
298
+ if (s === "never") {
299
+ const c = N((a) => {
300
+ a.chrome = { ...a.chrome, manage: "off" };
441
301
  });
442
- y(`wrote chrome.forTesting: "off" to ${i}`);
302
+ g(`wrote chrome.manage: "off" to ${c}`);
443
303
  } else
444
- Q({ installDeclinedAt: t });
445
- }
446
- async function Wt(e, t, n) {
447
- if (e === "auto")
448
- return y(
449
- `updating Chrome for Testing ${t.buildId} → ${n} (chrome.forTesting: "auto")…`
450
- ), (await S(n)).executablePath;
451
- if (X().skippedBuildId === n)
452
- return t.executablePath;
453
- switch (await z(
454
- `Your Chrome for Testing (${t.buildId}) is out of date — latest stable is ${n}. Update?`,
304
+ Q(e, { installDeclinedAt: n });
305
+ }
306
+ async function en(e, t, n, o) {
307
+ const r = k(e);
308
+ if (t === "auto")
309
+ return g(
310
+ `updating ${r.displayName} ${n.buildId} → ${o} (chrome.manage: "auto")…`
311
+ ), (await D(e, o)).executablePath;
312
+ if (Z(e).skippedBuildId === o)
313
+ return n.executablePath;
314
+ switch (await X(
315
+ `Your ${r.displayName} (${n.buildId}) is out of date — latest is ${o}. Update?`,
455
316
  [
456
317
  { key: "y", label: "yes, just this once" },
457
- { key: "a", label: 'automatically, now and from here on (writes chrome.forTesting: "auto")' },
318
+ { key: "a", label: 'automatically, now and from here on (writes chrome.manage: "auto")' },
458
319
  {
459
320
  key: "s",
460
- label: `skip ${n} — keep ${t.buildId}, don't ask again for this version`
321
+ label: `skip ${o} — keep ${n.buildId}, don't ask again for this version`
461
322
  },
462
- { key: "never", label: 'never ask again (writes chrome.forTesting: "off")' }
323
+ { key: "never", label: 'never ask again (writes chrome.manage: "off")' }
463
324
  ],
464
325
  "y"
465
326
  )) {
466
327
  case "y":
467
- return (await S(n)).executablePath;
328
+ return (await D(e, o)).executablePath;
468
329
  case "a": {
469
- const r = B((i) => {
470
- i.chrome = { ...i.chrome, forTesting: "auto" };
330
+ const s = N((c) => {
331
+ c.chrome = { ...c.chrome, manage: "auto" };
471
332
  });
472
- return y(`wrote chrome.forTesting: "auto" to ${r}`), (await S(n)).executablePath;
333
+ return g(`wrote chrome.manage: "auto" to ${s}`), (await D(e, o)).executablePath;
473
334
  }
474
335
  case "never": {
475
- const r = B((i) => {
476
- i.chrome = { ...i.chrome, forTesting: "off" };
336
+ const s = N((c) => {
337
+ c.chrome = { ...c.chrome, manage: "off" };
477
338
  });
478
- return y(`wrote chrome.forTesting: "off" to ${r}`), t.executablePath;
339
+ return g(`wrote chrome.manage: "off" to ${s}`), n.executablePath;
479
340
  }
480
341
  default:
481
- return Q({ skippedBuildId: n }), t.executablePath;
342
+ return Q(e, { skippedBuildId: o }), n.executablePath;
482
343
  }
483
344
  }
484
- async function qt(e, t) {
345
+ async function tn(e, t) {
485
346
  let n;
486
347
  try {
487
348
  return await Promise.race([
@@ -494,137 +355,158 @@ async function qt(e, t) {
494
355
  clearTimeout(n);
495
356
  }
496
357
  }
497
- function ge(e, t) {
498
- const n = L(e), o = JSON.parse(U(f(n, "package.json"), "utf8")), r = typeof o.bin == "string" ? o.bin : Gt(o.bin);
499
- if (!r)
500
- throw new Error(
501
- `package ${e} declares no bin`
502
- );
503
- if (kt(n)) {
504
- const i = r.replace(/^\.?\/?dist\//, "src/").replace(/\.js$/, ".ts");
505
- return { command: process.execPath, args: ["--import", "tsx", P(n, i)] };
506
- }
507
- return { command: process.execPath, args: [P(n, r)] };
358
+ const O = "com.habemus_papadum.aiui", fe = "cdpbfpcelmifhagikjlfpgfipggcmdeg";
359
+ function tt(e, t) {
360
+ if (e === "darwin") {
361
+ const n = h(t, "Library", "Application Support");
362
+ return [
363
+ h(n, "Google", "Chrome", "NativeMessagingHosts"),
364
+ h(n, "Chromium", "NativeMessagingHosts"),
365
+ h(n, "Microsoft Edge", "NativeMessagingHosts")
366
+ ];
367
+ }
368
+ if (e === "linux")
369
+ return [
370
+ h(t, ".config", "google-chrome", "NativeMessagingHosts"),
371
+ h(t, ".config", "chromium", "NativeMessagingHosts"),
372
+ h(t, ".config", "microsoft-edge", "NativeMessagingHosts")
373
+ ];
374
+ throw new Error(`aiui extension: unsupported platform ${e} (macOS/Linux only for now)`);
375
+ }
376
+ function nn(e, t, n) {
377
+ const o = (r) => `"${r.replace(/(["\\$`])/g, "\\$1")}"`;
378
+ return [
379
+ "#!/bin/sh",
380
+ "# aiui native-messaging host wrapper — generated by `aiui extension install-native-host`.",
381
+ "# Chrome spawns this with a minimal env and cwd /; everything below is absolute.",
382
+ `cd ${o(e)} || exit 1`,
383
+ `exec ${[t, ...n].map(o).join(" ")} native-host`,
384
+ ""
385
+ ].join(`
386
+ `);
508
387
  }
509
- function Gt(e) {
510
- return e ? Object.values(e)[0] : void 0;
388
+ const on = ["install-native-host", "status"];
389
+ async function rn(e, t = {}) {
390
+ switch (e) {
391
+ case "install-native-host":
392
+ an(t);
393
+ return;
394
+ case "status":
395
+ cn();
396
+ return;
397
+ case "dev":
398
+ case "reload":
399
+ y(
400
+ `aiui extension ${e} is gone — the frozen aiui-extension was deleted`,
401
+ `The intent client's extension is a static build:
402
+ pnpm -C packages/aiui-intent-client ext (build + load into the session browser)`
403
+ ), process.exitCode = 1;
404
+ return;
405
+ default:
406
+ throw new Error(`aiui extension: unknown action "${e}" (${on.join(" | ")})`);
407
+ }
511
408
  }
512
- const Jt = "chrome-devtools", nt = "@habemus-papadum/aiui-devtools-extension", zt = "default", Xt = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
513
- function ot(e, t = {}, n = process.env) {
514
- return e.noChrome ? !1 : e.chrome ? !0 : t.enabled === !1 ? !1 : !ue(n);
409
+ function nt() {
410
+ return h(Y("native-host", { create: !0 }), "aiui-native-host.sh");
515
411
  }
516
- function D(e, t = {}, n = process.cwd()) {
517
- if (t.executablePath && t.channel)
518
- throw new Error(
519
- "config sets both chrome.executablePath and chrome.channel — they pick the browser two different ways; keep exactly one"
520
- );
521
- const o = e.chromeProfile !== void 0 || e.chromeDataDir !== void 0, r = e.chromeDataDir ?? (o ? void 0 : t.dataDir), i = e.chromeProfile ?? (o ? void 0 : t.profile);
522
- return {
523
- userDataDir: rt({ dataDir: r, profile: i }, n),
524
- // A configured endpoint means the browser is managed elsewhere (usually
525
- // another machine) — that's always attach, whatever `mode` says.
526
- mode: t.browserUrl ? "attach" : t.mode ?? "attach",
527
- browserUrl: t.browserUrl,
528
- debugPort: t.debugPort ?? 0,
529
- executablePath: t.executablePath && P(n, t.executablePath),
530
- channel: t.channel,
531
- headless: t.headless ?? !1,
532
- buildExtension: t.buildExtension ?? !0
412
+ function ot(e) {
413
+ const t = J("@habemus-papadum/aiui"), n = K("@habemus-papadum/aiui"), o = nt();
414
+ rt(o, nn(n, t.command, t.args)), xt(o, 493);
415
+ const r = {
416
+ name: O,
417
+ description: "aiui native-messaging host: channel discovery for the aiui intent client",
418
+ path: o,
419
+ type: "stdio",
420
+ // The host answers one question "which channels are up?" — for the
421
+ // intent client's cold start. An overridden id rides alongside the stable
422
+ // one so a custom build never locks the shipped client out.
423
+ allowed_origins: [
424
+ .../* @__PURE__ */ new Set([
425
+ `chrome-extension://${e}/`,
426
+ `chrome-extension://${fe}/`
427
+ ])
428
+ ]
533
429
  };
430
+ return { wrapper: o, body: `${JSON.stringify(r, null, 2)}
431
+ ` };
534
432
  }
535
- function rt(e, t = process.cwd()) {
536
- if (e.dataDir)
537
- return P(t, e.dataDir);
538
- const n = e.profile ?? zt;
539
- if (!Xt.test(n))
540
- throw new Error(
541
- `invalid chrome profile name "${n}" — use letters, digits, ".", "_", "-" (or --aiui-chrome-data-dir for an arbitrary path)`
542
- );
543
- return f(de(t), "chrome", n);
544
- }
545
- function Z() {
546
- let e;
433
+ function rt(e, t) {
547
434
  try {
548
- e = He(f(L(nt), "extension"));
435
+ if (M(e, "utf8") === t)
436
+ return !1;
549
437
  } catch {
550
- return;
551
438
  }
552
- return v(f(e, "js")) ? e : void 0;
439
+ return W(e, t), !0;
440
+ }
441
+ function sn(e, t = {}) {
442
+ const { body: n } = ot(t.extensionId ?? fe), o = h(e, "NativeMessagingHosts");
443
+ z(o, { recursive: !0 }), rt(h(o, `${O}.json`), n);
444
+ }
445
+ function V(e, t, n) {
446
+ if (t)
447
+ try {
448
+ sn(e);
449
+ } catch (o) {
450
+ n(
451
+ "couldn't install the native-messaging host into the browser profile — the intent extension will need a manually typed port",
452
+ o instanceof Error ? o.message : String(o)
453
+ );
454
+ }
553
455
  }
554
- async function be() {
555
- let e, t;
456
+ function an(e) {
457
+ const t = e.extensionId ?? fe, { wrapper: n, body: o } = ot(t);
458
+ process.stdout.write(`wrote ${n}
459
+ `);
460
+ for (const r of tt(process.platform, ue())) {
461
+ z(r, { recursive: !0 });
462
+ const i = h(r, `${O}.json`);
463
+ W(i, o), process.stdout.write(`wrote ${i}
464
+ `);
465
+ }
466
+ process.stdout.write(
467
+ `native host installed for extension ${t}.
468
+ No browser restart needed; reload the extension if it was already running.
469
+ (Session browsers launched by aiui get the manifest in their own profile
470
+ automatically — this global install is for browsers aiui does not manage.)
471
+ `
472
+ );
473
+ }
474
+ function cn() {
475
+ const e = nt();
476
+ process.stdout.write(`wrapper: ${e} ${v(e) ? "(present)" : "(MISSING)"}
477
+ `);
478
+ for (const o of tt(process.platform, ue()))
479
+ Ce(h(o, `${O}.json`));
480
+ const t = h(process.cwd(), ".aiui-cache", "chrome");
481
+ let n = [];
556
482
  try {
557
- e = He(L(nt)), t = f(L("typescript"), "bin", "tsc");
483
+ n = q(t, { withFileTypes: !0 }).filter((o) => o.isDirectory()).map((o) => o.name);
558
484
  } catch {
559
- return;
560
- }
561
- if (!v(f(e, "src")))
562
- return;
563
- const n = await k(process.execPath, [t, "-p", f(e, "tsconfig.json")], {
564
- cwd: e,
565
- reject: !1,
566
- all: !0
567
- });
568
- if (n.exitCode) {
569
- T(
570
- "aiui-devtools-extension failed to compile — the DevTools panel will be stale or missing",
571
- n.all || n.message
572
- );
573
- return;
574
485
  }
575
- const o = f(e, "build-debug-ui.mjs");
576
- if (!v(o))
577
- return;
578
- const r = await k(process.execPath, [o], {
579
- cwd: e,
580
- reject: !1,
581
- all: !0
582
- });
583
- r.exitCode && T(
584
- "aiui-devtools-extension debug-ui bundle failed — the Intent pane will be degraded",
585
- r.all || r.message
586
- );
486
+ for (const o of n)
487
+ Ce(h(t, o, "NativeMessagingHosts", `${O}.json`));
587
488
  }
588
- function it(e, t) {
589
- if (!t || e.executablePath)
489
+ function Ce(e) {
490
+ var t;
491
+ if (!v(e)) {
492
+ process.stdout.write(`absent: ${e}
493
+ `);
590
494
  return;
591
- const n = f(e.userDataDir, "aiui-devtools-extension-hint");
592
- if (!v(n)) {
593
- y(
594
- "the aiui DevTools panel can't auto-load into regular Chrome (≥ 137 ignores --load-extension)",
595
- `Load it once in the launched Chrome — chrome://extensions → Developer mode → Load unpacked →
596
- ${t}
597
- — and this profile remembers it. Or switch to Chrome for Testing (\`aiui chrome install\`),
598
- which auto-loads it. This note won't repeat for this profile.`
599
- );
600
- try {
601
- q(n, `${(/* @__PURE__ */ new Date()).toISOString()}
495
+ }
496
+ try {
497
+ const n = JSON.parse(M(e, "utf8"));
498
+ process.stdout.write(`present: ${e} ${(t = n.allowed_origins) == null ? void 0 : t.join(", ")}
499
+ `);
500
+ } catch {
501
+ process.stdout.write(`present: ${e} (unparseable)
602
502
  `);
603
- } catch {
604
- }
605
503
  }
606
504
  }
607
- function te(e) {
608
- return {
609
- command: "npx",
610
- args: ["-y", "chrome-devtools-mcp@latest", "--browser-url", e]
611
- };
612
- }
613
- function Qt(e, t) {
614
- const n = [
615
- "-y",
616
- "chrome-devtools-mcp@latest",
617
- "--userDataDir",
618
- e.userDataDir,
619
- "--ignoreDefaultChromeArg=--disable-extensions"
620
- ];
621
- return e.executablePath && n.push("--executablePath", e.executablePath), e.channel && n.push("--channel", e.channel), e.headless && n.push("--headless"), t && n.push(`--chromeArg=--load-extension=${t}`), { command: "npx", args: n };
622
- }
623
- const Zt = 9222;
624
- async function en(e) {
625
- const n = { ...V().chrome };
505
+ const ln = 9222;
506
+ async function un(e) {
507
+ const n = { ...P().chrome };
626
508
  if (n.browserUrl) {
627
- y(
509
+ g(
628
510
  `config pins chrome.browserUrl to ${n.browserUrl} — the browser is managed elsewhere`,
629
511
  "Run `aiui browser` on the machine that should host it (and drop browserUrl there)."
630
512
  );
@@ -632,86 +514,86 @@ async function en(e) {
632
514
  }
633
515
  let o, r;
634
516
  try {
635
- o = Se(e.remotePort, "--remote-port") ?? Zt, r = Se(e.port, "--port");
636
- } catch (c) {
637
- g(c instanceof Error ? c.message : String(c)), process.exitCode = 1;
517
+ o = xe(e.remotePort, "--remote-port") ?? ln, r = xe(e.port, "--port");
518
+ } catch (a) {
519
+ y(a instanceof Error ? a.message : String(a)), process.exitCode = 1;
638
520
  return;
639
521
  }
640
522
  const i = {
641
523
  chromeProfile: e.tunnel ? void 0 : e.profile,
642
- chromeDataDir: e.dataDir ?? (e.tunnel ? tn(e.tunnel, e.profile) : void 0)
524
+ chromeDataDir: e.dataDir ?? (e.tunnel ? dn(e.tunnel, e.profile) : void 0)
643
525
  };
644
- let s = D(i, n);
526
+ let s = T(i, n);
645
527
  r !== void 0 && (s = { ...s, debugPort: r });
646
- let a = await F(s.userDataDir);
647
- if (a)
648
- Oe("session browser already running", s, a), e.open && (await H(a.browserUrl, e.open), console.log(`opened ${e.open}`));
528
+ let c = await U(s.userDataDir);
529
+ if (c)
530
+ V(
531
+ s.userDataDir,
532
+ j().state === "ready",
533
+ g
534
+ ), Ie("session browser already running", s, c), e.open && (await H(c.browserUrl, e.open), console.log(`opened ${e.open}`));
649
535
  else {
650
- const c = !!process.stdin.isTTY && !!process.stdout.isTTY && !ue();
536
+ const a = !!process.stdin.isTTY && !!process.stdout.isTTY && !ce();
651
537
  try {
652
- const d = await st({
538
+ const u = await it({
653
539
  flags: i,
654
540
  config: n,
655
- interactive: c,
541
+ interactive: a,
656
542
  debugPort: r,
657
543
  headless: e.headless,
658
544
  startUrl: e.open
659
545
  });
660
- a = d.session, s = d.settings;
661
- } catch (d) {
662
- g(
546
+ c = u.session, s = u.settings;
547
+ } catch (u) {
548
+ y(
663
549
  "the session browser failed to start",
664
- d instanceof Error ? d.message : String(d)
550
+ u instanceof Error ? u.message : String(u)
665
551
  ), process.exitCode = 1;
666
552
  return;
667
553
  }
668
- Oe("session browser started", s, a);
554
+ Ie("session browser started", s, c);
669
555
  }
670
- e.tunnel ? await rn(e.tunnel, o, a.port) : sn(a, o);
556
+ e.tunnel ? await pn(e.tunnel, o, c.port) : mn(c, o);
671
557
  }
672
- async function st(e) {
673
- let t = e.config ?? {};
674
- const n = e.flags ?? {}, o = () => {
675
- const c = D(n, t);
676
- return e.debugPort === void 0 ? c : { ...c, debugPort: e.debugPort };
677
- };
678
- let r = o();
679
- if (!t.executablePath && !t.channel) {
680
- const c = await tt({
681
- mode: t.forTesting ?? "prompt",
558
+ async function it(e) {
559
+ const t = e.config ?? {}, n = e.flags ?? {};
560
+ let o = T(n, t);
561
+ if (e.debugPort !== void 0 && (o = { ...o, debugPort: e.debugPort }), !t.executablePath && !t.channel) {
562
+ const a = await et({
563
+ flavor: B(t),
564
+ mode: le(t),
682
565
  interactive: e.interactive
683
566
  });
684
- c && (t = { ...t, executablePath: c }, r = o());
567
+ a && (o = { ...o, executablePath: a });
685
568
  }
686
- r.buildExtension && await be();
687
- const i = Z();
688
- e.interactive && it(r, i);
569
+ const r = j(), i = r.state === "ready" ? [r.dir] : [];
570
+ V(o.userDataDir, r.state === "ready", g), e.interactive && (Xe(o, i), Je(r));
689
571
  let s;
690
572
  try {
691
- s = Fe(r);
692
- } catch (c) {
573
+ s = Ue(o);
574
+ } catch (a) {
693
575
  throw new Error(
694
- `${c instanceof Error ? c.message : String(c)}
695
- Install Chrome for Testing with \`aiui chrome install\`, or set chrome.executablePath.`
576
+ `${a instanceof Error ? a.message : String(a)}
577
+ Install the managed browser with \`aiui chrome install\`, or set chrome.executablePath.`
696
578
  );
697
579
  }
698
- return { session: await Ve({
580
+ return { session: await Me({
699
581
  binary: s,
700
- userDataDir: r.userDataDir,
701
- debugPort: r.debugPort,
702
- extensionDir: i,
703
- headless: r.headless || e.headless,
582
+ userDataDir: o.userDataDir,
583
+ debugPort: o.debugPort,
584
+ extensionDirs: i,
585
+ headless: o.headless || e.headless,
704
586
  startUrl: e.startUrl
705
- }), settings: r };
587
+ }), settings: o };
706
588
  }
707
- function tn(e, t) {
708
- const n = t ?? nn(e);
709
- return f(Y("browser-profiles", { create: !1 }), n);
589
+ function dn(e, t) {
590
+ const n = t ?? hn(e);
591
+ return h(Y("browser-profiles", { create: !1 }), n);
710
592
  }
711
- function nn(e) {
593
+ function hn(e) {
712
594
  return (e.includes("@") ? e.slice(e.indexOf("@") + 1) : e).replace(/[^A-Za-z0-9._-]/g, "-") || "remote";
713
595
  }
714
- function on(e, t, n) {
596
+ function fn(e, t, n) {
715
597
  return [
716
598
  // No remote command — the connection exists only to carry the forward...
717
599
  "-N",
@@ -724,28 +606,28 @@ function on(e, t, n) {
724
606
  e
725
607
  ];
726
608
  }
727
- function at(e) {
609
+ function st(e) {
728
610
  return `aiui claude --aiui-browser-url http://127.0.0.1:${e}`;
729
611
  }
730
- async function rn(e, t, n) {
612
+ async function pn(e, t, n) {
731
613
  console.log(
732
614
  `
733
615
  tunneling ${e}:${t} → localhost:${n} — on ${e}, run:
734
616
 
735
- ${at(t)}
617
+ ${st(t)}
736
618
 
737
619
  (Ctrl-C closes the tunnel; the browser stays running.)`
738
620
  );
739
- const o = await k("ssh", on(e, t, n), {
621
+ const o = await E("ssh", fn(e, t, n), {
740
622
  stdio: "inherit",
741
623
  reject: !1
742
624
  });
743
- o.failed && !o.isTerminated && (g(
625
+ o.failed && !o.isTerminated && (y(
744
626
  `the ssh tunnel to ${e} exited (code ${o.exitCode})`,
745
627
  "A taken remote port exits immediately (ExitOnForwardFailure) — try another --remote-port. The browser is still running; rerun `aiui browser --tunnel …` to reconnect."
746
628
  ), process.exitCode = 1);
747
629
  }
748
- function Se(e, t) {
630
+ function xe(e, t) {
749
631
  if (e === void 0)
750
632
  return;
751
633
  const n = Number(e);
@@ -753,26 +635,26 @@ function Se(e, t) {
753
635
  throw new Error(`invalid ${t} ${e} — expected 0..65535`);
754
636
  return n;
755
637
  }
756
- function Oe(e, t, n) {
638
+ function Ie(e, t, n) {
757
639
  console.log(e), console.log(` profile: ${t.userDataDir}`), console.log(` debug endpoint: ${n.browserUrl}`);
758
640
  }
759
- function sn(e, t) {
641
+ function mn(e, t) {
760
642
  console.log(
761
643
  `
762
644
  An \`aiui claude\` in this profile's project attaches automatically. For a *remote*
763
645
  session, rerun with \`--tunnel <[user@]host>\` — or do it by hand:
764
646
  ssh -N -o ExitOnForwardFailure=yes -R ${t}:localhost:${e.port} <host>
765
- then, on the remote: ${at(t)}`
647
+ then, on the remote: ${st(t)}`
766
648
  );
767
649
  }
768
- async function an(e, t) {
650
+ async function wn(e, t) {
769
651
  var i, s;
770
- const n = V(), o = D(
652
+ const n = P(), o = T(
771
653
  { chromeProfile: t.profile, chromeDataDir: t.dataDir },
772
654
  n.chrome ?? {}
773
- ), r = ((i = n.chrome) == null ? void 0 : i.browserUrl) ?? ((s = await F(o.userDataDir)) == null ? void 0 : s.browserUrl);
655
+ ), r = ((i = n.chrome) == null ? void 0 : i.browserUrl) ?? ((s = await U(o.userDataDir)) == null ? void 0 : s.browserUrl);
774
656
  if (!r) {
775
- g(
657
+ y(
776
658
  "no session browser is running for this profile",
777
659
  `Start one with \`aiui browser\` (profile: ${o.userDataDir}).`
778
660
  ), process.exitCode = 1;
@@ -780,50 +662,59 @@ async function an(e, t) {
780
662
  }
781
663
  try {
782
664
  await H(r, e), console.log(`opened ${e}`);
783
- } catch (a) {
784
- g(`couldn't open ${e}`, a instanceof Error ? a.message : String(a)), process.exitCode = 1;
665
+ } catch (c) {
666
+ y(`couldn't open ${e}`, c instanceof Error ? c.message : String(c)), process.exitCode = 1;
785
667
  }
786
668
  }
787
- async function cn(e) {
788
- const [t] = e;
669
+ async function gn(e) {
670
+ const [t, n] = e;
789
671
  switch (t) {
790
672
  case "install":
791
- case "update":
792
- await Kt((n) => console.log(n));
793
- return;
794
- case "status":
795
- await ln();
796
- return;
797
- case "extension": {
798
- await be();
799
- const n = Z();
800
- if (!n) {
801
- g(
802
- "the aiui-devtools-extension is not available in this install",
803
- "In a dev checkout, build it first: pnpm --filter @habemus-papadum/aiui-devtools-extension build"
804
- ), process.exitCode = 1;
673
+ case "update": {
674
+ const o = bn(n);
675
+ if ("error" in o) {
676
+ y(o.error, "Usage: aiui chrome install [chromium | chrome-for-testing]"), process.exitCode = 1;
805
677
  return;
806
678
  }
807
- console.log(n);
679
+ await Zt(o.flavor, (r) => console.log(r));
808
680
  return;
809
681
  }
682
+ case "status":
683
+ await yn();
684
+ return;
810
685
  default:
811
- g(
686
+ y(
812
687
  t ? `unknown aiui chrome action: ${t}` : "aiui chrome needs an action",
813
- "Usage: aiui chrome <install | update | status | extension>"
688
+ "Usage: aiui chrome <install | update [flavor] | status>"
814
689
  ), process.exitCode = 1;
815
690
  return;
816
691
  }
817
692
  }
818
- async function ln() {
819
- const t = V().chrome ?? {}, n = { chrome: !1, noChrome: !1 }, o = await me(), r = await we();
820
- if (console.log("Chrome for Testing (managed):"), o) {
821
- const m = r === void 0 ? "(latest stable unknown — offline?)" : r === o.buildId ? "(latest stable)" : `(latest stable is ${r} — run \`aiui chrome update\`)`;
822
- console.log(` installed ${o.buildId} ${m}`), console.log(` ${o.executablePath}`);
823
- } else
824
- console.log(" not installed — `aiui chrome install` (recommended; auto-loads the panel)");
825
- if (console.log(` startup checks (chrome.forTesting): ${t.forTesting ?? "prompt"}`), console.log(`
826
- This directory would launch:`), !ot(n, t)) {
693
+ function bn(e) {
694
+ if (e === void 0)
695
+ return { flavor: B(P().chrome) };
696
+ const n = {
697
+ chromium: "chromium",
698
+ "chrome-for-testing": "chrome-for-testing",
699
+ cft: "chrome-for-testing"
700
+ }[e];
701
+ return n ? { flavor: n } : { error: `unknown browser "${e}" — use chromium or chrome-for-testing` };
702
+ }
703
+ async function yn() {
704
+ const t = P().chrome ?? {}, n = { chrome: !1, noChrome: !1 }, o = B(t);
705
+ console.log("Managed browsers:"), console.log(` preferred (chrome.managed): ${k(o).displayName}`);
706
+ for (const p of Fe) {
707
+ const w = k(p), b = p === o ? " *preferred*" : "", $ = await F(p);
708
+ if ($) {
709
+ const d = await he(p), R = d === void 0 ? "(latest unknown — offline?)" : d === $.buildId ? "(latest)" : `(latest is ${d} — \`aiui chrome update ${p}\`)`;
710
+ console.log(` ${w.displayName}${b}: ${$.buildId} ${R}`), console.log(` ${$.executablePath}`);
711
+ } else {
712
+ const d = p === o ? "aiui chrome install" : `aiui chrome install ${p}`;
713
+ console.log(` ${w.displayName}${b}: not installed — \`${d}\``);
714
+ }
715
+ }
716
+ if (console.log(` startup checks (chrome.manage): ${le(t)}`), console.log(`
717
+ This directory would launch:`), !qe(n, t)) {
827
718
  console.log(" nothing — the Chrome DevTools MCP is disabled here");
828
719
  return;
829
720
  }
@@ -831,132 +722,129 @@ This directory would launch:`), !ot(n, t)) {
831
722
  console.log(` connection: attach to ${t.browserUrl} (chrome.browserUrl)`), console.log(" the browser is managed elsewhere — nothing launches on this machine");
832
723
  return;
833
724
  }
834
- const i = { ...t };
835
- !i.executablePath && !i.channel && o && (i.executablePath = o.executablePath);
836
- const s = D({}, i), a = await F(s.userDataDir), c = s.mode === "attach" ? a ? `attach to the running session browser at ${a.browserUrl}` : "attach — a session browser starts with the next interactive launch (or `aiui browser`)" : "launch — chrome-devtools-mcp starts a private browser on the agent's first tool use";
725
+ const r = T({}, t), i = r.executablePath || r.channel ? void 0 : await F(o), s = await U(r.userDataDir), c = r.mode === "attach" ? s ? `attach to the running session browser at ${s.browserUrl}` : "attach — a session browser starts with the next interactive launch (or `aiui browser`)" : "launch — chrome-devtools-mcp starts a private browser on the agent's first tool use";
837
726
  console.log(` connection: ${c}`);
838
- const d = s.executablePath ? s.executablePath === (o == null ? void 0 : o.executablePath) ? `Chrome for Testing ${o.buildId}` : s.executablePath : s.channel ? `installed Chrome (${s.channel} channel)` : "installed Chrome (stable)";
839
- console.log(` browser: ${d}${s.headless ? " — headless" : ""}`), console.log(` user data dir: ${s.userDataDir}`);
840
- const p = f(rt({}, process.cwd()), "..");
841
- if (v(p)) {
842
- const m = G(p, { withFileTypes: !0 }).filter((b) => b.isDirectory()).map((b) => b.name);
843
- m.length && console.log(` profiles here: ${m.join(", ")}`);
727
+ const a = r.executablePath ? r.executablePath : r.channel ? `installed Chrome (${r.channel} channel)` : i ? `${k(o).displayName} ${i.buildId} (managed)` : `${k(o).displayName} (managed — not yet installed)`;
728
+ console.log(` browser: ${a}${r.headless ? " — headless" : ""}`), console.log(` profile variant: ${r.variant}`), console.log(` user data dir: ${r.userDataDir}`);
729
+ const u = !r.channel, m = Oe(r.userDataDir);
730
+ if (v(m)) {
731
+ const p = q(m, { withFileTypes: !0 }).filter((w) => w.isDirectory()).map((w) => w.name);
732
+ p.length && console.log(` profiles here (${r.variant}): ${p.join(", ")}`);
844
733
  }
845
734
  console.log(`
846
- aiui DevTools panel:`);
847
- const h = Z();
848
- if (!h) {
849
- console.log(" not available (unbuilt dev checkout? run `aiui chrome extension` for help)");
850
- return;
735
+ aiui intent client (the extension launches auto-load):`);
736
+ const f = j();
737
+ switch (f.state) {
738
+ case "absent":
739
+ console.log(" not available in this install (aiui-intent-client is not resolvable)");
740
+ break;
741
+ case "unbuilt":
742
+ console.log(
743
+ ` no MV3 bundle yet (${f.root})
744
+ build it: pnpm -C packages/aiui-intent-client build:ext
745
+ build + load into the running browser: pnpm -C packages/aiui-intent-client ext`
746
+ );
747
+ break;
748
+ case "ready":
749
+ console.log(` ${f.dir}`), vn(u);
750
+ break;
851
751
  }
852
- console.log(` ${h}`), s.executablePath ? console.log(" auto-loads via --load-extension (honored by Chrome for Testing/Chromium)") : (console.log(" can NOT auto-load into branded Chrome ≥ 137 — load it unpacked once"), console.log(
752
+ }
753
+ function vn(e) {
754
+ e ? console.log(" auto-loads via --load-extension (honored by Chromium/Chrome for Testing)") : (console.log(" can NOT auto-load into branded Chrome ≥ 137 — load it unpacked once"), console.log(
853
755
  " (chrome://extensions → Developer mode → Load unpacked), or `aiui chrome install`"
854
756
  ));
855
757
  }
856
- const un = "--aiui-";
857
- function ct(e) {
758
+ const $n = "--aiui-";
759
+ function at(e) {
858
760
  if (e.includes("--help") || e.includes("-h"))
859
761
  return "help";
860
762
  if (e.includes("--version") || e.includes("-v"))
861
763
  return "version";
862
764
  }
863
- function lt(e) {
864
- let t, n, o = !1, r = !1, i = !1, s = !1, a, c, d;
865
- const p = [], h = [];
866
- let m;
867
- const b = [];
868
- for (let w = 0; w < e.length; w++) {
869
- const C = e[w];
870
- if (!C.startsWith(un)) {
871
- b.push(C);
765
+ function pe(e) {
766
+ let t, n, o = !1, r = !1, i = !1, s = !1, c, a, u, m;
767
+ const f = [];
768
+ for (let p = 0; p < e.length; p++) {
769
+ const w = e[p];
770
+ if (!w.startsWith($n)) {
771
+ f.push(w);
872
772
  continue;
873
773
  }
874
- const x = C.indexOf("="), N = x === -1 ? C : C.slice(0, x);
875
- let u = x === -1 ? void 0 : C.slice(x + 1);
876
- switch (N) {
774
+ const b = w.indexOf("="), $ = b === -1 ? w : w.slice(0, b);
775
+ let d = b === -1 ? void 0 : w.slice(b + 1);
776
+ switch ($) {
877
777
  case "--aiui-tag": {
878
- if (u === void 0 && (u = e[++w]), !u)
778
+ if (d === void 0 && (d = e[++p]), !d)
879
779
  throw new Error("--aiui-tag requires a non-empty value");
880
- t = u;
780
+ t = d;
881
781
  break;
882
782
  }
883
783
  case "--aiui-mcp": {
884
- if (u === void 0 && (u = e[++w]), !u)
784
+ if (d === void 0 && (d = e[++p]), !d)
885
785
  throw new Error("--aiui-mcp requires a non-empty value");
886
- n = u;
786
+ n = d;
887
787
  break;
888
788
  }
889
789
  case "--aiui-chrome": {
890
- if (u !== void 0)
790
+ if (d !== void 0)
891
791
  throw new Error("--aiui-chrome takes no value");
892
792
  o = !0;
893
793
  break;
894
794
  }
895
795
  case "--aiui-no-chrome": {
896
- if (u !== void 0)
796
+ if (d !== void 0)
897
797
  throw new Error("--aiui-no-chrome takes no value");
898
798
  r = !0;
899
799
  break;
900
800
  }
901
801
  case "--aiui-browser": {
902
- if (u !== void 0)
802
+ if (d !== void 0)
903
803
  throw new Error("--aiui-browser takes no value");
904
804
  i = !0;
905
805
  break;
906
806
  }
907
807
  case "--aiui-no-browser": {
908
- if (u !== void 0)
808
+ if (d !== void 0)
909
809
  throw new Error("--aiui-no-browser takes no value");
910
810
  s = !0;
911
811
  break;
912
812
  }
913
813
  case "--aiui-chrome-profile": {
914
- if (u === void 0 && (u = e[++w]), !u)
814
+ if (d === void 0 && (d = e[++p]), !d)
915
815
  throw new Error("--aiui-chrome-profile requires a non-empty value");
916
- a = u;
816
+ c = d;
917
817
  break;
918
818
  }
919
819
  case "--aiui-chrome-data-dir": {
920
- if (u === void 0 && (u = e[++w]), !u)
820
+ if (d === void 0 && (d = e[++p]), !d)
921
821
  throw new Error("--aiui-chrome-data-dir requires a non-empty value");
922
- c = u;
822
+ a = d;
923
823
  break;
924
824
  }
925
825
  case "--aiui-browser-url": {
926
- if (u === void 0 && (u = e[++w]), !u)
826
+ if (d === void 0 && (d = e[++p]), !d)
927
827
  throw new Error("--aiui-browser-url requires a non-empty value");
928
- d = u;
929
- break;
930
- }
931
- case "--aiui-sidecar": {
932
- if (u === void 0 && (u = e[++w]), !u)
933
- throw new Error("--aiui-sidecar requires a non-empty value");
934
- p.push(u);
935
- break;
936
- }
937
- case "--aiui-no-sidecar": {
938
- if (u === void 0 && (u = e[++w]), !u)
939
- throw new Error("--aiui-no-sidecar requires a non-empty value");
940
- h.push(u);
828
+ u = d;
941
829
  break;
942
830
  }
943
831
  case "--aiui-bind": {
944
- if (u === void 0 && (u = e[++w]), !u || !se.includes(u))
945
- throw new Error(`--aiui-bind requires one of: ${se.join(", ")}`);
946
- m = u;
832
+ if (d === void 0 && (d = e[++p]), !d || !$e.includes(d))
833
+ throw new Error(`--aiui-bind requires one of: ${$e.join(", ")}`);
834
+ m = d;
947
835
  break;
948
836
  }
949
837
  default:
950
- throw new Error(`unknown aiui option: ${N}`);
838
+ throw new Error(`unknown aiui option: ${$}`);
951
839
  }
952
840
  }
953
841
  if (o && r)
954
842
  throw new Error("--aiui-chrome and --aiui-no-chrome are mutually exclusive");
955
843
  if (i && s)
956
844
  throw new Error("--aiui-browser and --aiui-no-browser are mutually exclusive");
957
- if (a !== void 0 && c !== void 0)
845
+ if (c !== void 0 && a !== void 0)
958
846
  throw new Error("--aiui-chrome-profile and --aiui-chrome-data-dir are mutually exclusive");
959
- if (d !== void 0 && (a !== void 0 || c !== void 0))
847
+ if (u !== void 0 && (c !== void 0 || a !== void 0))
960
848
  throw new Error(
961
849
  "--aiui-browser-url means the browser is managed elsewhere — it can't be combined with --aiui-chrome-profile or --aiui-chrome-data-dir"
962
850
  );
@@ -967,114 +855,174 @@ function lt(e) {
967
855
  noChrome: r,
968
856
  browser: i,
969
857
  noBrowser: s,
970
- chromeProfile: a,
971
- chromeDataDir: c,
972
- browserUrl: d,
973
- sidecar: p,
974
- noSidecar: h,
858
+ chromeProfile: c,
859
+ chromeDataDir: a,
860
+ browserUrl: u,
975
861
  bind: m,
976
- passthrough: b
862
+ passthrough: f
977
863
  };
978
864
  }
979
- const dn = {
980
- darwin: 2147578994,
981
- linux: 21522
982
- }, hn = 'open(my $t,"+<","/dev/tty") or exit 0; my $c="\\r"; ioctl($t,$ARGV[0]+0,$c);', fn = [250, 750];
983
- function pn(e = fn) {
984
- const t = dn[process.platform];
985
- if (t !== void 0)
986
- for (const n of e)
987
- setTimeout(() => {
988
- try {
989
- jt("perl", ["-e", hn, String(t)], { stdio: "ignore" }).on("error", () => {
990
- });
991
- } catch {
992
- }
993
- }, n).unref();
865
+ function ct(e) {
866
+ var t;
867
+ return { bind: e.bind ?? ((t = e.config.channel) == null ? void 0 : t.bind) ?? "loopback" };
868
+ }
869
+ function lt(e) {
870
+ return ["--bind", e.bind];
871
+ }
872
+ const kn = /* @__PURE__ */ new Set(["serve", "mcp"]);
873
+ function Cn(e, t) {
874
+ return e.some((n) => n === t || n.startsWith(`${t}=`));
994
875
  }
995
- const mn = `One-time setup — how should aiui launch Claude Code?
876
+ function ut(e) {
877
+ const t = e.find((n) => !n.startsWith("-"));
878
+ return t !== void 0 && kn.has(t);
879
+ }
880
+ function xn(e, t) {
881
+ if (!ut(e))
882
+ return e;
883
+ const n = lt(t), o = [...e];
884
+ for (let r = 0; r < n.length; r += 2) {
885
+ const i = n[r], s = n[r + 1];
886
+ Cn(e, i) || o.push(i, s);
887
+ }
888
+ return o;
889
+ }
890
+ const In = `One-time setup — how should aiui launch Claude Code?
996
891
  With --dangerously-skip-permissions, every agent action (shell commands, file writes,
997
892
  network, the browser) runs without asking you first. Fast, and dangerous. It's a personal
998
893
  preference — aiui works fine either way. Saved as claude.skipPermissions in your user
999
- config; edit or delete it there to change your mind.`, wn = `One-time setup — where should the channel's web server bind?
894
+ config; edit or delete it there to change your mind.`, En = `One-time setup — where should the channel's web server bind?
1000
895
  Binding the HOST interface puts the session's whole web surface on your network,
1001
896
  UNAUTHENTICATED — the iPad paint page (\`aiui paint url\` prints its URL), but also prompt
1002
897
  injection, /debug, and every sidecar. That's the simple, single-port way to use the iPad —
1003
898
  on a network that is yours alone (a home LAN), not on café Wi-Fi. LOOPBACK keeps everything
1004
899
  this-machine-only; reaching it from an iPad is then up to you — tunnel the channel port
1005
900
  however you like (Tailscale, \`ssh -L\`). Saved as channel.bind in your user config;
1006
- --aiui-bind wins per launch.`, gn = `One-time setup — auto-dismiss Claude Code's channel prompt?
901
+ --aiui-bind wins per launch.`, Pn = `One-time setup — auto-dismiss Claude Code's channel prompt?
1007
902
  aiui loads a custom development channel, so Claude Code shows a one-key acknowledgement
1008
903
  prompt at every startup. aiui can dismiss it for you: shortly after launch it injects a
1009
904
  single Enter keystroke into this terminal (a best-effort TIOCSTI ioctl on /dev/tty — it
1010
905
  literally "types" the Enter for you; on platforms that forbid that, nothing happens and
1011
906
  you press it yourself). Saying no just means pressing Enter once per launch. Saved as
1012
907
  claude.enterNudge in your user config.`;
1013
- async function bn(e, t = z) {
908
+ async function Dn(e, t = X) {
1014
909
  var o, r, i;
1015
910
  let n = e;
1016
911
  if (((o = n.claude) == null ? void 0 : o.skipPermissions) === void 0) {
1017
- const s = await t(mn, [
912
+ const s = await t(In, [
1018
913
  { key: "y", label: "yes — skip permissions; nothing asks before acting" },
1019
914
  { key: "n", label: "no — keep Claude Code's own permission prompts" }
1020
915
  ]);
1021
- n = je(n, "skipPermissions", s === "y");
916
+ n = Ee(n, "skipPermissions", s === "y");
1022
917
  }
1023
918
  if (((r = n.claude) == null ? void 0 : r.enterNudge) === void 0) {
1024
- const s = await t(gn, [
919
+ const s = await t(Pn, [
1025
920
  { key: "y", label: "yes — press Enter for me at startup" },
1026
921
  { key: "n", label: "no — I'll press it myself each launch" }
1027
922
  ]);
1028
- n = je(n, "enterNudge", s === "y");
923
+ n = Ee(n, "enterNudge", s === "y");
1029
924
  }
1030
925
  if (((i = n.channel) == null ? void 0 : i.bind) === void 0) {
1031
- const s = await t(wn, [
926
+ const s = await t(En, [
1032
927
  { key: "h", label: "host — reachable on my (trusted) network; the iPad just works" },
1033
928
  { key: "l", label: "loopback — this machine only; I'll tunnel when I want the iPad" }
1034
929
  ]);
1035
- n = yn(n, s === "h" ? "host" : "loopback");
930
+ n = Sn(n, s === "h" ? "host" : "loopback");
1036
931
  }
1037
932
  return n;
1038
933
  }
1039
- function je(e, t, n) {
1040
- const o = B((r) => {
934
+ function Ee(e, t, n) {
935
+ const o = N((r) => {
1041
936
  r.claude = { ...r.claude, [t]: n };
1042
937
  });
1043
- return y(`wrote claude.${t}: ${n} to ${o}`), { ...e, claude: { ...e.claude, [t]: n } };
938
+ return g(`wrote claude.${t}: ${n} to ${o}`), { ...e, claude: { ...e.claude, [t]: n } };
1044
939
  }
1045
- function yn(e, t) {
1046
- const n = B((o) => {
940
+ function Sn(e, t) {
941
+ const n = N((o) => {
1047
942
  o.channel = { ...o.channel, bind: t };
1048
943
  });
1049
- return y(`wrote channel.bind: ${t} to ${n}`), { ...e, channel: { ...e.channel, bind: t } };
944
+ return g(`wrote channel.bind: ${t} to ${n}`), { ...e, channel: { ...e.channel, bind: t } };
1050
945
  }
1051
- const vn = "https://api.openai.com/v1/models", kn = 3e3;
1052
- async function Cn(e = {}) {
1053
- var c;
946
+ const Tn = "https://generativelanguage.googleapis.com/v1beta/models", An = 3e3;
947
+ async function Nn(e = {}) {
948
+ var a;
949
+ const {
950
+ verify: t = !0,
951
+ env: n = process.env,
952
+ fetchImpl: o = fetch,
953
+ timeoutMs: r = An
954
+ } = e, i = (a = n.GEMINI_API_KEY) == null ? void 0 : a.trim();
955
+ if (!i)
956
+ return "missing";
957
+ if (!t)
958
+ return "unverified";
959
+ const s = new AbortController(), c = setTimeout(() => s.abort(), r);
960
+ try {
961
+ const u = await o(`${Tn}?key=${encodeURIComponent(i)}`, {
962
+ signal: s.signal
963
+ });
964
+ return u.ok ? "valid" : u.status === 400 || u.status === 401 || u.status === 403 ? "invalid" : "unverified";
965
+ } catch {
966
+ return "unverified";
967
+ } finally {
968
+ clearTimeout(c);
969
+ }
970
+ }
971
+ function _n(e) {
972
+ switch (e) {
973
+ case "valid":
974
+ return null;
975
+ case "missing":
976
+ return {
977
+ level: "note",
978
+ title: "GEMINI_API_KEY is not set — the realtime (Gemini Live) tier is unavailable",
979
+ detail: "Only the realtime conversational submode needs it; transcription tiers are unaffected. To enable it, export the key in the shell you run `aiui claude` from:\n export GEMINI_API_KEY=…\nIt flows through to the channel process, where the Gemini Live session runs."
980
+ };
981
+ case "invalid":
982
+ return {
983
+ level: "warn",
984
+ title: "GEMINI_API_KEY was rejected by Google — the realtime tier will fail",
985
+ detail: "The key in your environment isn't valid, so every Gemini Live session will close immediately. The usual cause is a stale shell export shadowing your real key — check what's actually set (this prints only a short prefix, not the whole secret):\n echo $GEMINI_API_KEY | head -c 8\nFix the export and relaunch `aiui claude`."
986
+ };
987
+ case "unverified":
988
+ return {
989
+ level: "note",
990
+ title: "couldn't verify GEMINI_API_KEY with Google — continuing",
991
+ detail: "The check didn't complete (offline, a timeout, or a transient error), so the key is unverified — not known-bad. Launch continues; if the realtime tier turns out unavailable, an unreachable or invalid key may be why."
992
+ };
993
+ }
994
+ }
995
+ function jn(e) {
996
+ const t = _n(e);
997
+ t && (t.level === "warn" ? C(t.title, t.detail) : g(t.title, t.detail));
998
+ }
999
+ const On = "https://api.openai.com/v1/models", Un = 3e3;
1000
+ async function Mn(e = {}) {
1001
+ var a;
1054
1002
  const {
1055
1003
  verify: t = !0,
1056
1004
  env: n = process.env,
1057
1005
  fetchImpl: o = fetch,
1058
- timeoutMs: r = kn
1059
- } = e, i = (c = n.OPENAI_API_KEY) == null ? void 0 : c.trim();
1006
+ timeoutMs: r = Un
1007
+ } = e, i = (a = n.OPENAI_API_KEY) == null ? void 0 : a.trim();
1060
1008
  if (!i)
1061
1009
  return "missing";
1062
1010
  if (!t)
1063
1011
  return "unverified";
1064
- const s = new AbortController(), a = setTimeout(() => s.abort(), r);
1012
+ const s = new AbortController(), c = setTimeout(() => s.abort(), r);
1065
1013
  try {
1066
- const d = await o(vn, {
1014
+ const u = await o(On, {
1067
1015
  headers: { authorization: `Bearer ${i}` },
1068
1016
  signal: s.signal
1069
1017
  });
1070
- return d.ok ? "valid" : d.status === 401 || d.status === 403 ? "invalid" : "unverified";
1018
+ return u.ok ? "valid" : u.status === 401 || u.status === 403 ? "invalid" : "unverified";
1071
1019
  } catch {
1072
1020
  return "unverified";
1073
1021
  } finally {
1074
- clearTimeout(a);
1022
+ clearTimeout(c);
1075
1023
  }
1076
1024
  }
1077
- function $n(e) {
1025
+ function Ln(e) {
1078
1026
  switch (e) {
1079
1027
  case "valid":
1080
1028
  return null;
@@ -1100,93 +1048,89 @@ and compare that against the start of your real key. Until it's fixed, transcrip
1100
1048
  };
1101
1049
  }
1102
1050
  }
1103
- function xn(e) {
1104
- const t = $n(e);
1105
- t && (t.level === "warn" ? T(t.title, t.detail) : y(t.title, t.detail));
1051
+ function Bn(e) {
1052
+ const t = Ln(e);
1053
+ t && (t.level === "warn" ? C(t.title, t.detail) : g(t.title, t.detail));
1106
1054
  }
1107
- const ee = "0.3.0";
1108
- function ye(e) {
1109
- const t = (process.env.PATH ?? "").split(bt).filter(Boolean), n = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";") : [""];
1055
+ const me = "0.6.0";
1056
+ function Rn(e) {
1057
+ const t = (process.env.PATH ?? "").split(vt).filter(Boolean), n = process.platform === "win32" ? (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";") : [""];
1110
1058
  for (const o of t)
1111
1059
  for (const r of n)
1112
1060
  try {
1113
- return $t(f(o, e + r), xt.X_OK), !0;
1061
+ return It(h(o, e + r), Et.X_OK), !0;
1114
1062
  } catch {
1115
1063
  }
1116
1064
  return !1;
1117
1065
  }
1118
- const Tn = "@habemus-papadum/aiui-claude-channel", Pn = "@habemus-papadum/aiui-claude-plugin", Ue = "aiui";
1119
- async function En(e = []) {
1120
- var xe, Te, Pe;
1121
- const t = lt(e), { tag: n, passthrough: o } = t, r = ct(o);
1066
+ const Hn = "@habemus-papadum/aiui-claude-channel", Fn = "@habemus-papadum/aiui-claude-plugin", Pe = "aiui";
1067
+ async function Vn(e = []) {
1068
+ var ye;
1069
+ const t = pe(e), { tag: n, passthrough: o } = t, r = at(o);
1122
1070
  if (r) {
1123
- r === "help" ? In() : console.log(`aiui ${ee}`), await Dn(o);
1071
+ r === "help" ? Kn() : console.log(`aiui ${me}`), await Gn(o);
1124
1072
  return;
1125
1073
  }
1126
- let i = V();
1127
- const s = On(o) && !ue();
1128
- s && (i = await bn(i));
1129
- const a = await Cn({ verify: s });
1130
- if (s && xn(a), !ut())
1074
+ let i = P();
1075
+ const s = Wn(o) && !ce();
1076
+ s && (i = await Dn(i));
1077
+ const c = await Mn({ verify: s });
1078
+ s && Bn(c);
1079
+ const a = await Nn({ verify: s });
1080
+ if (s && jn(a), !dt())
1131
1081
  return;
1132
- const c = P(L(Pn), "marketplace", "plugins"), d = ge(Tn), p = [...d.args, "mcp"];
1133
- n && p.push("--tag", n);
1134
- const h = t.bind ?? ((xe = i.channel) == null ? void 0 : xe.bind) ?? "loopback";
1135
- p.push("--bind", h);
1136
- const m = {
1137
- [Ue]: { command: d.command, args: p }
1082
+ const u = x(K(Fn), "marketplace", "plugins"), m = J(Hn), f = [...m.args, "mcp"];
1083
+ n && f.push("--tag", n);
1084
+ const p = ct({ config: i, bind: t.bind });
1085
+ f.push(...lt(p));
1086
+ const w = {
1087
+ [Pe]: { command: m.command, args: f }
1138
1088
  };
1139
1089
  let b = { enabled: !1 };
1140
- if (ot(t, i.chrome)) {
1141
- const E = {
1090
+ if (qe(t, i.chrome)) {
1091
+ const ee = {
1142
1092
  ...i.chrome,
1143
1093
  ...t.browserUrl ? { browserUrl: t.browserUrl } : {}
1144
- }, _ = await Sn(t, E, s);
1145
- m[Jt] = _.entry, b = _.info;
1094
+ }, ve = await Yn(t, ee, s);
1095
+ w[Lt] = ve.entry, b = ve.info;
1146
1096
  }
1147
- const w = {
1097
+ const $ = {
1148
1098
  launcher: "aiui claude",
1149
1099
  chromeDevtools: b,
1150
- openaiKey: a
1100
+ openaiKey: c,
1101
+ geminiKey: a
1151
1102
  };
1152
- p.push("--launch-info", JSON.stringify(w));
1153
- const C = [...t.sidecar], x = [...t.noSidecar];
1154
- for (const [E, _] of Object.entries(i.sidecars ?? {}))
1155
- _ === void 0 || C.includes(E) || x.includes(E) || (_ ? C : x).push(E);
1156
- const N = Ut(process.cwd(), { enable: C, disable: x });
1157
- N.length > 0 && p.push("--sidecars", JSON.stringify(N));
1158
- const u = JSON.stringify({ mcpServers: m }), Ce = [f(c, "aiui"), f(c, "frontend-design")];
1159
- b.enabled && Ce.push(f(c, "session-browser"));
1160
- const wt = [
1161
- ...((Te = i.claude) == null ? void 0 : Te.skipPermissions) ?? !0 ? ["--dangerously-skip-permissions"] : [],
1103
+ f.push("--launch-info", JSON.stringify($));
1104
+ const d = JSON.stringify({ mcpServers: w }), R = [h(u, "aiui"), h(u, "frontend-design")];
1105
+ b.enabled && R.push(h(u, "session-browser"));
1106
+ const bt = [
1107
+ ...((ye = i.claude) == null ? void 0 : ye.skipPermissions) ?? !0 ? ["--dangerously-skip-permissions"] : [],
1162
1108
  "--mcp-config",
1163
- u,
1164
- ...Ce.flatMap((E) => ["--plugin-dir", E]),
1109
+ d,
1110
+ ...R.flatMap((ee) => ["--plugin-dir", ee]),
1165
1111
  // Custom channels are a research preview and not on the approved allowlist,
1166
1112
  // so opt this session into loading ours as a development channel.
1167
1113
  "--dangerously-load-development-channels",
1168
- `server:${Ue}`
1169
- ];
1170
- s && (((Pe = i.claude) == null ? void 0 : Pe.enterNudge) ?? !0) && pn();
1171
- const $e = await k("claude", [...wt, ...o], {
1114
+ `server:${Pe}`
1115
+ ], be = await E("claude", [...bt, ...o], {
1172
1116
  stdio: "inherit",
1173
1117
  reject: !1
1174
1118
  });
1175
- $e.exitCode && (process.exitCode = $e.exitCode);
1119
+ be.exitCode && (process.exitCode = be.exitCode);
1176
1120
  }
1177
- function ut() {
1178
- return ye("claude") ? !0 : (g(
1121
+ function dt() {
1122
+ return Rn("claude") ? !0 : (y(
1179
1123
  "`claude` was not found on your PATH",
1180
1124
  "Install Claude Code and make sure the `claude` command is available, then try again."
1181
1125
  ), process.exitCode = 1, !1);
1182
1126
  }
1183
- async function Dn(e) {
1184
- if (!ut())
1127
+ async function Gn(e) {
1128
+ if (!dt())
1185
1129
  return;
1186
- const t = await k("claude", e, { stdio: "inherit", reject: !1 });
1130
+ const t = await E("claude", e, { stdio: "inherit", reject: !1 });
1187
1131
  t.exitCode && (process.exitCode = t.exitCode);
1188
1132
  }
1189
- function In() {
1133
+ function Kn() {
1190
1134
  console.log(`aiui claude — launch Claude Code wired with the aiui channel, plugin, and browser MCP
1191
1135
 
1192
1136
  aiui's own flags (everything else forwards to claude verbatim):
@@ -1202,25 +1146,29 @@ aiui's own flags (everything else forwards to claude verbatim):
1202
1146
  whole network can reach the session's web
1203
1147
  surface — the iPad paint page included;
1204
1148
  trusted networks only)
1205
- --aiui-sidecar <name> host this session sidecar (repeatable);
1206
- \`paint\` (iPad ink) is always on
1207
- --aiui-no-sidecar <name> don't host this session sidecar (repeatable)
1208
1149
 
1150
+ Every channel hosts the same session sidecars — paint (iPad ink), the intent panel
1151
+ at /intent/, the remote bar, and the remote pencil — reachable per --aiui-bind.
1209
1152
  Durable settings live in config.json (project .aiui-cache/ + user cache) — see the
1210
1153
  Configuration guide. What follows is claude's own --help:
1211
1154
  `);
1212
1155
  }
1213
- async function Sn(e, t, n) {
1156
+ async function Yn(e, t, n) {
1214
1157
  if (t.browserUrl)
1215
1158
  return {
1216
1159
  entry: te(t.browserUrl),
1217
1160
  info: { enabled: !0, connection: "attach", browserUrl: t.browserUrl }
1218
1161
  };
1219
- let o = { ...t }, r = D(e, o);
1162
+ const o = t;
1163
+ let r = T(e, o);
1220
1164
  if (r.mode === "attach") {
1221
- const a = await F(r.userDataDir);
1165
+ const a = await U(r.userDataDir);
1222
1166
  if (a)
1223
- return {
1167
+ return V(
1168
+ r.userDataDir,
1169
+ j().state === "ready",
1170
+ C
1171
+ ), {
1224
1172
  entry: te(a.browserUrl),
1225
1173
  info: {
1226
1174
  enabled: !0,
@@ -1231,26 +1179,30 @@ async function Sn(e, t, n) {
1231
1179
  };
1232
1180
  }
1233
1181
  if (!o.executablePath && !o.channel) {
1234
- const a = await tt({ mode: o.forTesting ?? "prompt", interactive: n });
1235
- a && (o = { ...o, executablePath: a }, r = D(e, o));
1182
+ const a = await et({
1183
+ flavor: B(o),
1184
+ mode: le(o),
1185
+ interactive: n
1186
+ });
1187
+ a && (r = { ...r, executablePath: a });
1236
1188
  }
1237
- W(r.userDataDir, { recursive: !0 }), r.buildExtension && await be();
1238
- const i = Z();
1239
- n && it(r, i);
1240
- const s = {
1189
+ z(r.userDataDir, { recursive: !0 });
1190
+ const i = j(), s = i.state === "ready" ? [i.dir] : [];
1191
+ V(r.userDataDir, i.state === "ready", C), n && (Xe(r, s), Je(i));
1192
+ const c = {
1241
1193
  userDataDir: r.userDataDir,
1242
1194
  executablePath: r.executablePath,
1243
1195
  channel: r.channel,
1244
1196
  headless: r.headless,
1245
- extensionDir: i
1197
+ extensionDirs: s
1246
1198
  };
1247
1199
  if (r.mode === "attach" && n)
1248
1200
  try {
1249
- const a = await Ve({
1250
- binary: Fe(r),
1201
+ const a = await Me({
1202
+ binary: Ue(r),
1251
1203
  userDataDir: r.userDataDir,
1252
1204
  debugPort: r.debugPort,
1253
- extensionDir: i,
1205
+ extensionDirs: s,
1254
1206
  headless: r.headless
1255
1207
  });
1256
1208
  return {
@@ -1259,64 +1211,64 @@ async function Sn(e, t, n) {
1259
1211
  enabled: !0,
1260
1212
  connection: "attach",
1261
1213
  browserUrl: a.browserUrl,
1262
- ...s
1214
+ ...c
1263
1215
  }
1264
1216
  };
1265
1217
  } catch (a) {
1266
- T(
1218
+ C(
1267
1219
  "couldn't start the session browser — falling back to a browser private to the MCP",
1268
1220
  a instanceof Error ? a.message : String(a)
1269
1221
  );
1270
1222
  }
1271
1223
  return {
1272
- entry: Qt(r, i),
1273
- info: { enabled: !0, connection: "launch", ...s }
1224
+ entry: zt(r, s),
1225
+ info: { enabled: !0, connection: "launch", ...c }
1274
1226
  };
1275
1227
  }
1276
- function On(e) {
1228
+ function Wn(e) {
1277
1229
  return !process.stdin.isTTY || !process.stdout.isTTY ? !1 : !e.some((t) => t === "-p" || t === "--print");
1278
1230
  }
1279
- function jn(e = process.cwd()) {
1231
+ function zn(e = process.cwd()) {
1280
1232
  return {
1281
- project: de(e),
1233
+ project: Le(e),
1282
1234
  user: Y(void 0, { create: !1 }),
1283
- browser: A(!1)
1235
+ browsers: Xt(!1)
1284
1236
  };
1285
1237
  }
1286
- function Un(e, t) {
1238
+ function qn(e, t) {
1287
1239
  const n = [];
1288
1240
  return e.userOnly || n.push({ label: "project cache", path: t.project }), e.projectOnly || n.push(
1289
1241
  e.keepBrowser ? {
1290
- label: "user cache (keeping Chrome for Testing)",
1242
+ label: "user cache (keeping the managed browsers)",
1291
1243
  path: t.user,
1292
- keep: [t.browser]
1244
+ keep: t.browsers
1293
1245
  } : { label: "user cache", path: t.user }
1294
1246
  ), n;
1295
1247
  }
1296
- function An(e) {
1248
+ function Jn(e) {
1297
1249
  var o;
1298
1250
  if (!v(e.path))
1299
1251
  return [];
1300
1252
  if (!((o = e.keep) != null && o.length))
1301
1253
  return [e.path];
1302
- const t = new Set(e.keep.map((r) => P(r)));
1254
+ const t = new Set(e.keep.map((r) => x(r)));
1303
1255
  let n;
1304
1256
  try {
1305
- n = G(e.path);
1257
+ n = q(e.path);
1306
1258
  } catch {
1307
1259
  return [];
1308
1260
  }
1309
- return n.map((r) => P(f(e.path, r))).filter((r) => !t.has(r));
1261
+ return n.map((r) => x(h(e.path, r))).filter((r) => !t.has(r));
1310
1262
  }
1311
- async function Nn(e = {}) {
1263
+ async function Xn(e = {}) {
1312
1264
  if (e.projectOnly && e.userOnly) {
1313
- g(
1265
+ y(
1314
1266
  "pass at most one of --project-only / --user-only",
1315
1267
  "with neither, `aiui clean` removes both"
1316
1268
  ), process.exitCode = 1;
1317
1269
  return;
1318
1270
  }
1319
- const t = process.cwd(), o = Un(e, jn(t)).map((h) => ({ target: h, paths: An(h) })).filter((h) => h.paths.length > 0);
1271
+ const t = process.cwd(), o = qn(e, zn(t)).map((f) => ({ target: f, paths: Jn(f) })).filter((f) => f.paths.length > 0);
1320
1272
  if (o.length === 0) {
1321
1273
  console.log("nothing to clean — no aiui state here or in the user cache.");
1322
1274
  return;
@@ -1324,39 +1276,39 @@ async function Nn(e = {}) {
1324
1276
  let r = 0;
1325
1277
  console.log(`aiui clean will remove:
1326
1278
  `);
1327
- for (const { target: h, paths: m } of o) {
1328
- const b = m.reduce((w, C) => w + le(C), 0);
1329
- r += b, console.log(` ${ne(b).padStart(9)} ${h.label}`), console.log(` ${" ".repeat(9)} ${l.dim(h.path)}`);
1279
+ for (const { target: f, paths: p } of o) {
1280
+ const w = p.reduce((b, $) => b + ae($), 0);
1281
+ r += w, console.log(` ${ne(w).padStart(9)} ${f.label}`), console.log(` ${" ".repeat(9)} ${l.dim(f.path)}`);
1330
1282
  }
1331
1283
  console.log(`
1332
1284
  ${ne(r).padStart(9)} total
1333
1285
  `);
1334
- const i = !e.projectOnly, s = i && !e.keepBrowser, a = [
1286
+ const i = !e.projectOnly, s = i && !e.keepBrowser, c = [
1335
1287
  "traces and session-browser logins are cleared",
1336
1288
  i && "the Claude permission prompt (claude.skipPermissions) returns on next launch",
1337
- s && "Chrome for Testing re-downloads (~160 MB) on the next `aiui claude`"
1338
- ].filter((h) => !!h);
1289
+ s && "the managed browser re-downloads (~150-160 MB) on the next `aiui claude`"
1290
+ ].filter((f) => !!f);
1339
1291
  console.log("This resets aiui toward a fresh install:");
1340
- for (const h of a)
1341
- console.log(` • ${h}`);
1292
+ for (const f of c)
1293
+ console.log(` • ${f}`);
1342
1294
  console.log("");
1343
- const c = J(t);
1344
- if (c.length && T(
1345
- `${c.length} aiui session${c.length === 1 ? "" : "s"} still running`,
1295
+ const a = L(t);
1296
+ if (a.length && C(
1297
+ `${a.length} aiui session${a.length === 1 ? "" : "s"} still running`,
1346
1298
  "stop them first — a live session rewrites its registry entry as it exits, and an open browser can lock files being deleted"
1347
1299
  ), e.dryRun) {
1348
- y("dry run — nothing was deleted");
1300
+ g("dry run — nothing was deleted");
1349
1301
  return;
1350
1302
  }
1351
1303
  if (!e.yes) {
1352
1304
  if (!process.stdin.isTTY) {
1353
- g(
1305
+ y(
1354
1306
  "refusing to delete without confirmation",
1355
1307
  "re-run with --yes for a non-interactive clean, or from a terminal"
1356
1308
  ), process.exitCode = 1;
1357
1309
  return;
1358
1310
  }
1359
- if (await z(
1311
+ if (await X(
1360
1312
  "Delete these now?",
1361
1313
  [
1362
1314
  { key: "y", label: "yes, delete" },
@@ -1368,28 +1320,28 @@ async function Nn(e = {}) {
1368
1320
  return;
1369
1321
  }
1370
1322
  }
1371
- let d = 0, p = 0;
1372
- for (const { paths: h } of o)
1373
- for (const m of h) {
1374
- const b = le(m);
1323
+ let u = 0, m = 0;
1324
+ for (const { paths: f } of o)
1325
+ for (const p of f) {
1326
+ const w = ae(p);
1375
1327
  try {
1376
- Tt(m, { recursive: !0, force: !0 }), d += b;
1377
- } catch (w) {
1378
- p++, T(
1379
- `could not remove ${m}`,
1380
- w instanceof Error ? w.message : String(w)
1328
+ Pt(p, { recursive: !0, force: !0 }), u += w;
1329
+ } catch (b) {
1330
+ m++, C(
1331
+ `could not remove ${p}`,
1332
+ b instanceof Error ? b.message : String(b)
1381
1333
  );
1382
1334
  }
1383
1335
  }
1384
- p && T(
1385
- `${p} path${p === 1 ? "" : "s"} could not be deleted`,
1336
+ m && C(
1337
+ `${m} path${m === 1 ? "" : "s"} could not be deleted`,
1386
1338
  "a running browser can lock its files — close the session browser and re-run"
1387
- ), console.log(`clean complete — freed ~${ne(d)}.`);
1339
+ ), console.log(`clean complete — freed ~${ne(u)}.`);
1388
1340
  }
1389
- function le(e) {
1341
+ function ae(e) {
1390
1342
  let t;
1391
1343
  try {
1392
- t = Pt(e);
1344
+ t = Dt(e);
1393
1345
  } catch {
1394
1346
  return 0;
1395
1347
  }
@@ -1397,13 +1349,13 @@ function le(e) {
1397
1349
  return t.size;
1398
1350
  let n;
1399
1351
  try {
1400
- n = G(e);
1352
+ n = q(e);
1401
1353
  } catch {
1402
1354
  return 0;
1403
1355
  }
1404
1356
  let o = 0;
1405
1357
  for (const r of n)
1406
- o += le(f(e, r));
1358
+ o += ae(h(e, r));
1407
1359
  return o;
1408
1360
  }
1409
1361
  function ne(e) {
@@ -1415,35 +1367,35 @@ function ne(e) {
1415
1367
  n /= 1024, o++;
1416
1368
  return `${n.toFixed(1)} ${t[o]}`;
1417
1369
  }
1418
- function j(e = process.cwd()) {
1419
- const t = he(e);
1370
+ function S(e = process.cwd()) {
1371
+ const t = At(e);
1420
1372
  return {
1421
1373
  paths: t,
1422
- user: O(t.user) ?? {},
1423
- project: O(t.project) ?? {}
1374
+ user: ie(t.user) ?? {},
1375
+ project: ie(t.project) ?? {}
1424
1376
  };
1425
1377
  }
1426
- function ve(e) {
1427
- return Je().map((t) => {
1428
- const n = Ae(e.user, t), o = Ae(e.project, t), r = o ?? n, i = o !== void 0 ? "project" : n !== void 0 ? "user" : t.field.default !== void 0 ? "default" : "unset";
1378
+ function we(e) {
1379
+ return Ve().map((t) => {
1380
+ const n = De(e.user, t), o = De(e.project, t), r = o ?? n, i = o !== void 0 ? "project" : n !== void 0 ? "user" : t.field.default !== void 0 ? "default" : "unset";
1429
1381
  return { ...t, userValue: n, projectValue: o, effective: r, source: i };
1430
1382
  });
1431
1383
  }
1432
- function Ae(e, t) {
1384
+ function De(e, t) {
1433
1385
  var o;
1434
1386
  return (o = e[t.section.name]) == null ? void 0 : o[t.field.key];
1435
1387
  }
1436
- function ke(e, t) {
1437
- const n = ve(t).find((o) => o.path === e);
1438
- return n || (g(
1388
+ function ge(e, t) {
1389
+ const n = we(t).find((o) => o.path === e);
1390
+ return n || (y(
1439
1391
  `unknown config key: ${e}`,
1440
1392
  `Known keys:
1441
- ${Je().map((o) => o.path).join(`
1393
+ ${Ve().map((o) => o.path).join(`
1442
1394
  `)}`
1443
1395
  ), process.exitCode = 1), n;
1444
1396
  }
1445
- function _n(e = {}, t = process.cwd()) {
1446
- const n = j(t), o = ve(n);
1397
+ function Zn(e = {}, t = process.cwd()) {
1398
+ const n = S(t), o = we(n);
1447
1399
  if (e.json) {
1448
1400
  console.log(
1449
1401
  JSON.stringify(
@@ -1454,7 +1406,7 @@ function _n(e = {}, t = process.cwd()) {
1454
1406
  },
1455
1407
  user: n.user,
1456
1408
  project: n.project,
1457
- effective: Qe(n.user, n.project)
1409
+ effective: Tt(n.user, n.project)
1458
1410
  },
1459
1411
  null,
1460
1412
  2
@@ -1462,28 +1414,28 @@ function _n(e = {}, t = process.cwd()) {
1462
1414
  );
1463
1415
  return;
1464
1416
  }
1465
- console.log(`user: ${n.paths.user}${Ne(n.paths.user)}`), console.log(`project: ${n.paths.project}${Ne(n.paths.project)}`);
1417
+ console.log(`user: ${n.paths.user}${Se(n.paths.user)}`), console.log(`project: ${n.paths.project}${Se(n.paths.project)}`);
1466
1418
  const r = Math.max(...o.map((s) => s.path.length));
1467
1419
  let i = "";
1468
1420
  for (const s of o)
1469
1421
  s.section.name !== i && (i = s.section.name, console.log(`
1470
- ${l.bold(i)} ${l.dim(`— ${s.section.summary}`)}`)), console.log(` ${s.path.padEnd(r + 2)}${dt(s)}`);
1422
+ ${l.bold(i)} ${l.dim(`— ${s.section.summary}`)}`)), console.log(` ${s.path.padEnd(r + 2)}${ht(s)}`);
1471
1423
  console.log(
1472
1424
  l.dim(
1473
1425
  "\nProject beats user per key; CLI flags beat both. `aiui config` browses the docs interactively."
1474
1426
  )
1475
1427
  );
1476
1428
  }
1477
- function Ne(e) {
1429
+ function Se(e) {
1478
1430
  return v(e) ? "" : l.dim(" (not present)");
1479
1431
  }
1480
- function dt(e) {
1481
- return e.effective !== void 0 ? `${l.cyan($(e.effective))} ${l.dim(`(${e.source})`)}` : l.dim(
1482
- e.field.default !== void 0 ? `default: ${$(e.field.default)}` : "unset"
1432
+ function ht(e) {
1433
+ return e.effective !== void 0 ? `${l.cyan(I(e.effective))} ${l.dim(`(${e.source})`)}` : l.dim(
1434
+ e.field.default !== void 0 ? `default: ${I(e.field.default)}` : "unset"
1483
1435
  );
1484
1436
  }
1485
- function Rn(e, t = process.cwd()) {
1486
- const n = j(t), o = ke(e, n);
1437
+ function Qn(e, t = process.cwd()) {
1438
+ const n = S(t), o = ge(e, n);
1487
1439
  if (o) {
1488
1440
  if (o.effective !== void 0) {
1489
1441
  console.log(String(o.effective));
@@ -1492,65 +1444,65 @@ function Rn(e, t = process.cwd()) {
1492
1444
  return;
1493
1445
  }
1494
1446
  if (o.field.default !== void 0) {
1495
- console.log(String(o.field.default)), console.error(l.dim(`# built-in default — ${ce(o.field)}`));
1447
+ console.log(String(o.field.default)), console.error(l.dim(`# built-in default — ${oe(o.field)}`));
1496
1448
  return;
1497
1449
  }
1498
1450
  console.error(
1499
- l.dim(`# not set in any config file — default: ${ce(o.field)}`)
1451
+ l.dim(`# not set in any config file — default: ${oe(o.field)}`)
1500
1452
  );
1501
1453
  }
1502
1454
  }
1503
- function Ln(e, t, n = {}, o = process.cwd()) {
1504
- const r = j(o), i = ke(e, r);
1455
+ function eo(e, t, n = {}, o = process.cwd()) {
1456
+ const r = S(o), i = ge(e, r);
1505
1457
  if (!i)
1506
1458
  return;
1507
- const s = ae(i.field, t);
1459
+ const s = re(i.field, t);
1508
1460
  if ("error" in s) {
1509
- g(`invalid value for ${i.path}: ${t}`, s.error), process.exitCode = 1;
1461
+ y(`invalid value for ${i.path}: ${t}`, s.error), process.exitCode = 1;
1510
1462
  return;
1511
1463
  }
1512
- ht(r, i, s.value, n.project ? "project" : "user");
1464
+ ft(r, i, s.value, n.project ? "project" : "user");
1513
1465
  }
1514
- function ht(e, t, n, o) {
1515
- const r = fe(e.paths[o], (i) => {
1466
+ function ft(e, t, n, o) {
1467
+ const r = Ge(e.paths[o], (i) => {
1516
1468
  const s = i;
1517
1469
  s[t.section.name] = { ...s[t.section.name], [t.field.key]: n };
1518
1470
  });
1519
- y(`wrote ${t.path}: ${$(n)} to ${r}`);
1471
+ g(`wrote ${t.path}: ${I(n)} to ${r}`);
1520
1472
  }
1521
- function Mn(e, t = {}, n = process.cwd()) {
1522
- const o = j(n), r = ke(e, o);
1523
- r && ft(o, r, t.project ? "project" : "user");
1473
+ function to(e, t = {}, n = process.cwd()) {
1474
+ const o = S(n), r = ge(e, o);
1475
+ r && pt(o, r, t.project ? "project" : "user");
1524
1476
  }
1525
- function ft(e, t, n) {
1526
- var a;
1477
+ function pt(e, t, n) {
1478
+ var c;
1527
1479
  const o = e.paths[n];
1528
- if (((a = (O(o) ?? {})[t.section.name]) == null ? void 0 : a[t.field.key]) === void 0) {
1529
- y(`${t.path} is not set in the ${n} config (${o})`);
1480
+ if (((c = (ie(o) ?? {})[t.section.name]) == null ? void 0 : c[t.field.key]) === void 0) {
1481
+ g(`${t.path} is not set in the ${n} config (${o})`);
1530
1482
  return;
1531
1483
  }
1532
- const s = fe(o, (c) => {
1533
- const d = c, p = { ...d[t.section.name] };
1534
- delete p[t.field.key], Object.keys(p).length === 0 ? delete d[t.section.name] : d[t.section.name] = p;
1484
+ const s = Ge(o, (a) => {
1485
+ const u = a, m = { ...u[t.section.name] };
1486
+ delete m[t.field.key], Object.keys(m).length === 0 ? delete u[t.section.name] : u[t.section.name] = m;
1535
1487
  });
1536
- y(`removed ${t.path} from ${s}`);
1488
+ g(`removed ${t.path} from ${s}`);
1537
1489
  }
1538
- async function _e(e = process.cwd()) {
1490
+ async function Te(e = process.cwd()) {
1539
1491
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
1540
- g(
1492
+ y(
1541
1493
  "aiui config tui needs an interactive terminal",
1542
1494
  "In scripts and non-TTY sessions use `aiui config show` (or `show --json`)."
1543
1495
  ), process.exitCode = 1;
1544
1496
  return;
1545
1497
  }
1546
- const t = j(e).paths;
1547
- console.log(l.dim(`user: ${Le(t.user)}`)), console.log(l.dim(`project: ${Le(t.project)}`));
1498
+ const t = S(e).paths;
1499
+ console.log(l.dim(`user: ${Ne(t.user)}`)), console.log(l.dim(`project: ${Ne(t.project)}`));
1548
1500
  try {
1549
1501
  for (; ; ) {
1550
- const n = j(e), o = await Bn(n);
1502
+ const n = S(e), o = await no(n);
1551
1503
  if (!o)
1552
1504
  return;
1553
- await Vn(n, o);
1505
+ await ro(n, o);
1554
1506
  }
1555
1507
  } catch (n) {
1556
1508
  if (n instanceof Error && n.name === "ExitPromptError")
@@ -1558,35 +1510,35 @@ async function _e(e = process.cwd()) {
1558
1510
  throw n;
1559
1511
  }
1560
1512
  }
1561
- async function Bn(e) {
1562
- const t = ve(e), n = Math.max(...t.map((r) => r.path.length)) + 2, o = [];
1563
- for (const r of M) {
1564
- o.push(new ie(l.dim(`── ${r.name} — ${r.summary}`)));
1513
+ async function no(e) {
1514
+ const t = we(e), n = Math.max(...t.map((r) => r.path.length)) + 2, o = [];
1515
+ for (const r of Nt) {
1516
+ o.push(new se(l.dim(`── ${r.name} — ${r.summary}`)));
1565
1517
  for (const i of t.filter((s) => s.section.name === r.name))
1566
1518
  o.push({
1567
- name: `${i.path.padEnd(n)}${dt(i)}`,
1519
+ name: `${i.path.padEnd(n)}${ht(i)}`,
1568
1520
  value: i,
1569
- description: Fn(i)
1521
+ description: oo(i)
1570
1522
  });
1571
1523
  }
1572
- return o.push(new ie(" ")), o.push({ name: "exit", value: void 0, description: "Leave the config browser." }), K({
1524
+ return o.push(new se(" ")), o.push({ name: "exit", value: void 0, description: "Leave the config browser." }), _({
1573
1525
  message: "aiui config",
1574
1526
  choices: o,
1575
1527
  pageSize: o.length,
1576
1528
  loop: !1
1577
1529
  });
1578
1530
  }
1579
- function Fn(e) {
1531
+ function oo(e) {
1580
1532
  const t = [l.bold(e.field.summary)];
1581
- return e.field.doc && t.push(Kn(e.field.doc, 76)), t.push(""), e.field.type === "enum" && t.push(`allowed: ${(e.field.values ?? []).join(" | ")}`), t.push(`default: ${ce(e.field)}`), t.push(`user: ${Re(e.userValue)}`), t.push(`project: ${Re(e.projectValue)}`), t.push(
1582
- `effective: ${e.effective !== void 0 ? `${$(e.effective)} (from the ${e.source} config)` : "the built-in default"}`
1533
+ return e.field.doc && t.push(so(e.field.doc, 76)), t.push(""), e.field.type === "enum" && t.push(`allowed: ${(e.field.values ?? []).join(" | ")}`), t.push(`default: ${oe(e.field)}`), t.push(`user: ${Ae(e.userValue)}`), t.push(`project: ${Ae(e.projectValue)}`), t.push(
1534
+ `effective: ${e.effective !== void 0 ? `${I(e.effective)} (from the ${e.source} config)` : "the built-in default"}`
1583
1535
  ), t.join(`
1584
1536
  `);
1585
1537
  }
1586
- function Re(e) {
1587
- return e === void 0 ? l.dim("(not set)") : $(e);
1538
+ function Ae(e) {
1539
+ return e === void 0 ? l.dim("(not set)") : I(e);
1588
1540
  }
1589
- async function Vn(e, t) {
1541
+ async function ro(e, t) {
1590
1542
  const n = [
1591
1543
  {
1592
1544
  name: "set in the user config",
@@ -1602,13 +1554,13 @@ This project only; the file may be shared or committed by a team.`
1602
1554
  }
1603
1555
  ];
1604
1556
  t.userValue !== void 0 && n.push({
1605
- name: `unset in the user config (currently ${$(t.userValue)})`,
1557
+ name: `unset in the user config (currently ${I(t.userValue)})`,
1606
1558
  value: "unset-user"
1607
1559
  }), t.projectValue !== void 0 && n.push({
1608
- name: `unset in the project config (currently ${$(t.projectValue)})`,
1560
+ name: `unset in the project config (currently ${I(t.projectValue)})`,
1609
1561
  value: "unset-project"
1610
- }), n.push(new ie()), n.push({ name: "back", value: "back" });
1611
- const o = await K({
1562
+ }), n.push(new se()), n.push({ name: "back", value: "back" });
1563
+ const o = await _({
1612
1564
  message: `${t.path} — ${t.field.summary}`,
1613
1565
  choices: n
1614
1566
  });
@@ -1616,16 +1568,16 @@ This project only; the file may be shared or committed by a team.`
1616
1568
  return;
1617
1569
  const r = o.endsWith("project") ? "project" : "user";
1618
1570
  if (o.startsWith("unset")) {
1619
- ft(e, t, r);
1571
+ pt(e, t, r);
1620
1572
  return;
1621
1573
  }
1622
- const i = await Hn(t);
1623
- i !== void 0 && ht(e, t, i, r);
1574
+ const i = await io(t);
1575
+ i !== void 0 && ft(e, t, i, r);
1624
1576
  }
1625
- async function Hn(e) {
1577
+ async function io(e) {
1626
1578
  const t = (r) => r === e.effective ? " (current)" : r === e.field.default ? " (default)" : "";
1627
1579
  if (e.field.type === "boolean")
1628
- return K({
1580
+ return _({
1629
1581
  message: `${e.path} =`,
1630
1582
  choices: [!0, !1].map((r) => ({
1631
1583
  name: `${r}${l.dim(t(r))}`,
@@ -1633,28 +1585,28 @@ async function Hn(e) {
1633
1585
  }))
1634
1586
  });
1635
1587
  if (e.field.type === "enum")
1636
- return K({
1588
+ return _({
1637
1589
  message: `${e.path} =`,
1638
1590
  choices: (e.field.values ?? []).map((r) => ({
1639
1591
  name: `${r}${l.dim(t(r))}`,
1640
1592
  value: r
1641
1593
  }))
1642
1594
  });
1643
- const n = await _t({
1595
+ const n = await Ut({
1644
1596
  message: `${e.path} = `,
1645
1597
  default: e.effective !== void 0 ? String(e.effective) : void 0,
1646
1598
  validate: (r) => {
1647
- const i = ae(e.field, r);
1599
+ const i = re(e.field, r);
1648
1600
  return "error" in i ? i.error : !0;
1649
1601
  }
1650
- }), o = ae(e.field, n);
1602
+ }), o = re(e.field, n);
1651
1603
  return "error" in o ? void 0 : o.value;
1652
1604
  }
1653
- function Le(e) {
1654
- const t = At();
1605
+ function Ne(e) {
1606
+ const t = ue();
1655
1607
  return e.startsWith(t) ? `~${e.slice(t.length)}` : e;
1656
1608
  }
1657
- function Kn(e, t) {
1609
+ function so(e, t) {
1658
1610
  const n = [];
1659
1611
  let o = "";
1660
1612
  for (const r of e.split(/\s+/))
@@ -1662,8 +1614,7 @@ function Kn(e, t) {
1662
1614
  return o && n.push(o), n.join(`
1663
1615
  `);
1664
1616
  }
1665
- const Yn = "vite", oe = "VITE_AIUI_PORT";
1666
- function pt(e, t) {
1617
+ function ao(e, t) {
1667
1618
  if (t !== void 0) {
1668
1619
  const n = e.find((o) => o.tag === t);
1669
1620
  if (!n) {
@@ -1676,49 +1627,42 @@ function pt(e, t) {
1676
1627
  }
1677
1628
  return e.length > 0 ? { select: e } : {};
1678
1629
  }
1679
- async function Wn(e = []) {
1680
- const t = lt(e), { mcp: n, tag: o, passthrough: r } = t, i = ct(r);
1630
+ const co = "vite";
1631
+ async function lo(e = []) {
1632
+ const t = pe(e), { mcp: n, tag: o, passthrough: r } = t, i = at(r);
1681
1633
  if (i) {
1682
- i === "help" ? Qn() : console.log(`aiui ${ee}`), await Xn(r);
1634
+ i === "help" ? mo() : console.log(`aiui ${me}`), await po(r);
1683
1635
  return;
1684
1636
  }
1685
- const s = n ?? o, a = pt(J(), s);
1686
- if (a.error) {
1687
- g("Could not resolve an aiui channel", a.error), process.exitCode = 1;
1688
- return;
1689
- }
1690
- const c = a.select ? await qe(a.select) : a.server;
1691
- let d;
1692
- c ? (d = String(c.port), console.error(
1693
- l.dim(
1694
- `aiui: connecting vite to channel "${c.tag}" (${c.cwd}) on port ${d} via ${oe}`
1695
- )
1696
- )) : console.error(l.dim(`aiui: no running channel found — ${oe} left unset`));
1697
- const p = mt();
1698
- if (!p)
1637
+ (n !== void 0 || o !== void 0) && C(
1638
+ "aiui vite no longer connects the app to a channel",
1639
+ "--aiui-mcp / --aiui-tag are ignored. The app reaches the channel through the intent client served at /intent/, not a build-time port. (The standalone panel's own `pnpm dev` still selects a channel — that page is served on Vite's origin, not the channel's.)"
1640
+ );
1641
+ const s = wt();
1642
+ if (!s)
1699
1643
  return;
1700
- const h = {};
1701
- d && (h[oe] = d), process.stdout.isTTY && !("FORCE_COLOR" in process.env) && !("NO_COLOR" in process.env) && (h.FORCE_COLOR = "1");
1702
- const m = k(p.command, [...p.args, ...r], {
1644
+ const c = {};
1645
+ process.stdout.isTTY && !("FORCE_COLOR" in process.env) && !("NO_COLOR" in process.env) && (c.FORCE_COLOR = "1");
1646
+ const a = E(s.command, [...s.args, ...r], {
1703
1647
  stdio: ["inherit", "pipe", "inherit"],
1704
1648
  buffer: !1,
1705
1649
  reject: !1,
1706
- env: h
1650
+ env: c
1707
1651
  });
1708
- m.stdout && Jn(m.stdout, process.stdout, (w) => {
1709
- zn(w, t);
1652
+ a.stdout && fo(a.stdout, process.stdout, (m) => {
1653
+ mt(m, t);
1710
1654
  });
1711
- const b = await m;
1712
- b.exitCode && (process.exitCode = b.exitCode);
1655
+ const u = await a;
1656
+ u.exitCode && (process.exitCode = u.exitCode);
1713
1657
  }
1714
- const qn = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
1715
- function Gn(e) {
1716
- const n = e.replace(qn, "").match(
1658
+ const uo = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
1659
+ function ho(e) {
1660
+ const n = e.replace(uo, "").match(
1717
1661
  /\bLocal:\s+(https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?\/?\S*)/
1718
1662
  );
1719
1663
  return n == null ? void 0 : n[1];
1720
1664
  }
1721
- function Jn(e, t, n) {
1665
+ function fo(e, t, n) {
1722
1666
  let o = "", r = !1;
1723
1667
  e.on("data", (i) => {
1724
1668
  if (t.write(i), !r) {
@@ -1727,11 +1671,11 @@ function Jn(e, t, n) {
1727
1671
  `);
1728
1672
  if (s === -1)
1729
1673
  break;
1730
- const a = o.slice(0, s);
1674
+ const c = o.slice(0, s);
1731
1675
  o = o.slice(s + 1);
1732
- const c = Gn(a);
1733
- if (c) {
1734
- r = !0, o = "", n(c);
1676
+ const a = ho(c);
1677
+ if (a) {
1678
+ r = !0, o = "", n(a);
1735
1679
  return;
1736
1680
  }
1737
1681
  }
@@ -1739,16 +1683,16 @@ function Jn(e, t, n) {
1739
1683
  }
1740
1684
  });
1741
1685
  }
1742
- async function zn(e, t) {
1686
+ async function mt(e, t) {
1743
1687
  try {
1744
1688
  const n = {
1745
- ...V().chrome,
1689
+ ...P().chrome,
1746
1690
  ...t.browserUrl ? { browserUrl: t.browserUrl } : {}
1747
- }, o = Ct(t, n);
1691
+ }, o = kt(t, n);
1748
1692
  if (o.kind === "skip")
1749
1693
  return;
1750
1694
  if (o.kind === "hint") {
1751
- y(
1695
+ g(
1752
1696
  `detected a headless environment (${o.reason}) — not opening a browser`,
1753
1697
  `Assuming the dev server's port is already forwarded, open ${e} in the browser
1754
1698
  on your local machine. (Pass --aiui-browser to open one here anyway.)`
@@ -1759,251 +1703,229 @@ on your local machine. (Pass --aiui-browser to open one here anyway.)`
1759
1703
  await H(n.browserUrl, e), console.error(l.dim(`aiui: opened ${e} in the browser at ${n.browserUrl}`));
1760
1704
  return;
1761
1705
  }
1762
- const r = D(t, n), i = await F(r.userDataDir);
1763
- i ? (await H(i.browserUrl, e), console.error(l.dim(`aiui: opened ${e} in the session browser`))) : (await st({
1706
+ const r = T(t, n), i = await U(r.userDataDir);
1707
+ i ? (await H(i.browserUrl, e), console.error(l.dim(`aiui: opened ${e} in the session browser`))) : (await it({
1764
1708
  flags: t,
1765
1709
  config: n,
1766
1710
  interactive: !1,
1767
1711
  startUrl: e
1768
1712
  }), console.error(l.dim(`aiui: opened ${e} in a new session browser`)));
1769
1713
  } catch (n) {
1770
- T(
1714
+ C(
1771
1715
  "couldn't open the app in the session browser — the dev server is unaffected",
1772
1716
  n instanceof Error ? n.message : String(n)
1773
1717
  );
1774
1718
  }
1775
1719
  }
1776
- function mt() {
1720
+ function wt() {
1777
1721
  try {
1778
- return ge(Yn);
1722
+ return J(co);
1779
1723
  } catch {
1780
- g(
1724
+ y(
1781
1725
  "Vite is not available",
1782
1726
  "`vite` should be installed as a dependency of aiui — try reinstalling."
1783
1727
  ), process.exitCode = 1;
1784
1728
  return;
1785
1729
  }
1786
1730
  }
1787
- async function Xn(e) {
1788
- const t = mt();
1731
+ async function po(e) {
1732
+ const t = wt();
1789
1733
  if (!t)
1790
1734
  return;
1791
- const n = await k(t.command, [...t.args, ...e], {
1735
+ const n = await E(t.command, [...t.args, ...e], {
1792
1736
  stdio: "inherit",
1793
1737
  reject: !1
1794
1738
  });
1795
1739
  n.exitCode && (process.exitCode = n.exitCode);
1796
1740
  }
1797
- function Qn() {
1798
- console.log(`aiui vite — launch Vite connected to the running aiui channel
1741
+ function mo() {
1742
+ console.log(`aiui vite — launch Vite and open the app in the session browser
1799
1743
 
1800
1744
  aiui's own flags (everything else forwards to vite verbatim):
1801
- --aiui-mcp <tag> connect to the channel server with this tag
1802
- --aiui-tag <tag> accepted alias for --aiui-mcp
1803
1745
  --aiui-browser open the app in the session browser even when
1804
1746
  the environment looks headless (CI, SSH, no display)
1805
1747
  --aiui-no-browser never open a browser for this run
1806
1748
  --aiui-chrome-profile <name> browser profile at .aiui-cache/chrome/<name>
1807
1749
  --aiui-chrome-data-dir <path> explicit browser user data dir
1808
1750
 
1809
- The chosen channel's port is exported as VITE_AIUI_PORT; the aiuiDevOverlay()
1810
- Vite plugin picks it up there and wires the intent tool to it. When Vite prints
1811
- its Local: URL, aiui opens it in the shared session browser (the one \`aiui
1812
- claude\` and \`aiui open\` use); in headless environments it prints the URL to
1813
- open on your own machine instead. What follows is vite's own --help:
1751
+ When Vite prints its Local: URL, aiui opens it in the shared session browser
1752
+ (the one \`aiui claude\` and \`aiui open\` use); in headless environments it
1753
+ prints the URL to open on your own machine instead.
1754
+
1755
+ The app reaches the aiui channel through the intent client served at /intent/,
1756
+ not through this command — so there is no channel to pick here. (The standalone
1757
+ intent panel, served on Vite's own origin, is the exception: its \`pnpm dev\`
1758
+ selects a channel itself.) What follows is vite's own --help:
1814
1759
  `);
1815
1760
  }
1816
- const re = "/__aiui/debug", Me = 4747;
1817
- function Zn() {
1818
- let e = R(Ge(import.meta.url));
1819
- for (let t = 0; t < 5; t++) {
1820
- const n = f(e, "package.json");
1821
- if (v(n))
1822
- try {
1823
- if (JSON.parse(U(n, "utf8")).name === "@habemus-papadum/aiui")
1824
- return e;
1825
- } catch {
1826
- }
1827
- e = R(e);
1828
- }
1829
- }
1830
- async function eo(e = {}) {
1831
- var d;
1832
- const t = pt(J(), e.mcp);
1761
+ async function wo(e = {}) {
1762
+ const t = ao(L(), e.mcp);
1833
1763
  if (t.error) {
1834
- g("Could not resolve an aiui channel", t.error), process.exitCode = 1;
1764
+ y("Could not resolve an aiui channel", t.error), process.exitCode = 1;
1835
1765
  return;
1836
1766
  }
1837
- const n = t.select ? await qe(t.select) : t.server;
1767
+ const n = t.select ? await Be(t.select) : t.server;
1838
1768
  if (!n) {
1839
- console.log("No running aiui channel to debug — start one with `aiui claude`."), process.exitCode = 1;
1769
+ console.log("No running aiui channel to open — start one with `aiui claude`."), process.exitCode = 1;
1840
1770
  return;
1841
1771
  }
1842
- const o = Zn();
1843
- if (!o) {
1844
- g("Could not locate the aiui package root to serve the viewer from"), process.exitCode = 1;
1845
- return;
1772
+ const o = `http://127.0.0.1:${n.port}/`;
1773
+ console.log(`${l.cyan("aiui debug")} — the channel console`), console.log(` ${l.bold(o)}`), console.log(
1774
+ l.dim(` channel "${n.tag}" (${n.cwd}) on port ${n.port}`) + l.dim(" — the dashboard links to the trace debugger at /__aiui/debug.")
1775
+ ), e.open !== !1 && await mt(o, pe([]));
1776
+ }
1777
+ const _e = [".env", ".env.local", ".env.dev", ".env.dev.local"];
1778
+ function go(e) {
1779
+ let t = e;
1780
+ for (; ; ) {
1781
+ if (v(h(t, "pnpm-workspace.yaml")) || v(h(t, ".git")))
1782
+ return t;
1783
+ const n = Oe(t);
1784
+ if (n === t)
1785
+ return e;
1786
+ t = n;
1787
+ }
1788
+ }
1789
+ function bo(e) {
1790
+ const t = /* @__PURE__ */ new Map();
1791
+ for (const n of e.split(`
1792
+ `)) {
1793
+ const o = n.trim();
1794
+ if (o === "" || o.startsWith("#"))
1795
+ continue;
1796
+ const r = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(o);
1797
+ if (!r)
1798
+ continue;
1799
+ let i = r[2].trim();
1800
+ i.length >= 2 && (i[0] === '"' || i[0] === "'") && i[i.length - 1] === i[0] && (i = i.slice(1, -1)), t.set(r[1], i);
1846
1801
  }
1847
- const r = {
1848
- name: "aiui:debug-home",
1849
- configureServer(p) {
1850
- p.middlewares.use((h, m, b) => {
1851
- if ((h.url ?? "/").split("?")[0] === "/") {
1852
- m.statusCode = 302, m.setHeader("location", re), m.end();
1853
- return;
1854
- }
1855
- b();
1856
- });
1857
- }
1858
- }, i = Number(e.port), s = await Lt({
1859
- root: o,
1860
- configFile: !1,
1861
- // `mount: false`: this server hosts the viewer, not an app — nothing to
1862
- // arm, so the intent tool stays out of it. The plugin still serves the
1863
- // DEBUG_ROUTE page and seeds the picked channel's port into it.
1864
- plugins: [r, Rt({ port: n.port, mount: !1 })],
1865
- server: {
1866
- port: Number.isInteger(i) && i > 0 ? i : Me,
1867
- open: e.open === !1 ? !1 : re
1868
- },
1869
- logLevel: "warn"
1870
- });
1871
- await s.listen();
1872
- const c = `${(((d = s.resolvedUrls) == null ? void 0 : d.local[0]) ?? `http://localhost:${Me}/`).replace(/\/$/, "")}${re}`;
1873
- console.log(`${l.cyan("aiui debug")} — the lowering-trace viewer`), console.log(` viewing ${l.bold(c)}`), console.log(
1874
- l.dim(` channel "${n.tag}" (${n.cwd}) on port ${n.port}`) + l.dim(" — switch channels from the page's header. Ctrl-C to stop.")
1875
- );
1802
+ return t;
1876
1803
  }
1877
- async function to(e, t = {}) {
1878
- const n = P(process.cwd(), e ?? "aiui-demo");
1879
- switch (no(n)) {
1880
- case "occupied":
1881
- g(
1882
- `${n} already exists and isn't an aiui demo`,
1883
- "Pick an empty (or new) directory — the scaffold never overwrites existing content."
1884
- ), process.exitCode = 1;
1885
- return;
1886
- case "existing-demo":
1887
- y(`existing demo found at ${n} — continuing where it left off`);
1888
- break;
1889
- case "new": {
1890
- const o = so();
1891
- if (!o) {
1892
- g("the demo template did not ship with this aiui install"), process.exitCode = 1;
1893
- return;
1894
- }
1895
- ro(o, n), console.log(`scaffolded the demo playground at ${n}`), await io(n);
1896
- break;
1897
- }
1898
- }
1899
- if (!t.skipInstall && !v(f(n, "node_modules")))
1900
- if (!ye("npm"))
1901
- y("npm not found on PATH — run your package manager's install in the demo yourself");
1902
- else {
1903
- console.log("installing dependencies (one time)…");
1904
- const o = await k("npm", ["install", "--no-audit", "--no-fund"], {
1905
- cwd: n,
1906
- stdio: "inherit",
1907
- reject: !1
1908
- });
1909
- if (o.exitCode) {
1910
- g("npm install failed — fix that, then re-run this command to continue"), process.exitCode = o.exitCode;
1911
- return;
1912
- }
1913
- }
1914
- ao(n);
1804
+ function je(e) {
1805
+ return `'${e.replace(/'/g, "'\\''")}'`;
1915
1806
  }
1916
- function no(e) {
1917
- var n;
1918
- if (!v(e))
1919
- return "new";
1920
- let t;
1921
- try {
1922
- t = G(e);
1923
- } catch {
1924
- return "occupied";
1925
- }
1926
- if (t.length === 0)
1927
- return "new";
1928
- try {
1929
- if (((n = JSON.parse(U(f(e, "package.json"), "utf8")).aiui) == null ? void 0 : n.demo) === !0)
1930
- return "existing-demo";
1931
- } catch {
1932
- }
1933
- return "occupied";
1934
- }
1935
- function oo(e) {
1936
- return /^\d+\.\d+\.\d+$/.test(e) ? `^${e}` : "latest";
1937
- }
1938
- function ro(e, t) {
1939
- W(t, { recursive: !0 }), Et(e, t, { recursive: !0 }), v(f(t, "gitignore")) && Dt(f(t, "gitignore"), f(t, ".gitignore"));
1940
- const n = f(t, "package.json");
1941
- q(
1942
- n,
1943
- U(n, "utf8").replaceAll(
1944
- "__AIUI_VERSION_RANGE__",
1945
- oo(ee)
1807
+ function yo(e) {
1808
+ const t = [];
1809
+ t.push(
1810
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: shell parameter expansion, not a JS template
1811
+ 'if [ -z "${_AIUI_OLD_PATH:-}" ]; then _AIUI_OLD_PATH="$PATH"; export _AIUI_OLD_PATH; fi;'
1812
+ );
1813
+ for (const o of [...e.pathDirs].reverse()) {
1814
+ const r = je(o);
1815
+ t.push(`case ":$PATH:" in *":"${r}":"*) : ;; *) PATH=${r}":$PATH"; export PATH ;; esac;`);
1816
+ }
1817
+ for (const [o, r] of e.vars)
1818
+ t.push(`export ${o}=${je(r)};`);
1819
+ const n = e.vars.size > 0 ? ` unset ${[...e.vars.keys()].join(" ")};` : "";
1820
+ return t.push(
1821
+ `aiui_deactivate () { if [ -n "\${_AIUI_OLD_PATH:-}" ]; then PATH="$_AIUI_OLD_PATH"; export PATH; unset _AIUI_OLD_PATH; fi;${n} unset -f aiui_deactivate; };`
1822
+ ), `${t.join(`
1823
+ `)}
1824
+ `;
1825
+ }
1826
+ function vo() {
1827
+ const e = process.cwd(), t = go(e), n = [h(t, "bin"), h(t, "node_modules", ".bin")];
1828
+ e !== t && n.push(h(e, "node_modules", ".bin"));
1829
+ const o = n.filter((c) => v(c)), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
1830
+ for (const c of _e) {
1831
+ const a = h(t, c);
1832
+ if (v(a))
1833
+ for (const [u, m] of bo(M(a, "utf8")))
1834
+ r.set(u, m), i.set(u, c);
1835
+ }
1836
+ process.stdout.write(yo({ pathDirs: o, vars: r }));
1837
+ const s = [...r.keys()].map((c) => `${c} (${i.get(c)})`);
1838
+ process.stderr.write(
1839
+ l.dim(
1840
+ `aiui env: root ${t}
1841
+ aiui env: PATH + ${o.length > 0 ? o.join(", ") : "(nothing found)"}
1842
+ aiui env: export ${s.length > 0 ? s.join(", ") : `(none — no ${_e.join("/")} at root)`}
1843
+ aiui env: activate with eval "$(aiui env)" — undo with aiui_deactivate
1844
+ `
1946
1845
  )
1947
1846
  );
1948
1847
  }
1949
- async function io(e) {
1950
- !ye("git") || (await k("git", ["-C", e, "rev-parse", "--is-inside-work-tree"], {
1951
- reject: !1
1952
- })).exitCode === 0 || (await k("git", ["-C", e, "init", "--quiet"], { reject: !1 })).exitCode !== 0 || (await k("git", ["-C", e, "add", "-A"], { reject: !1 }), await k("git", ["-C", e, "commit", "--quiet", "-m", "aiui demo scaffold"], {
1953
- reject: !1
1954
- }));
1955
- }
1956
- function so() {
1957
- let e = R(Ge(import.meta.url));
1958
- for (let t = 0; t < 4; t++) {
1959
- const n = f(e, "templates", "demo");
1960
- if (v(f(n, "package.json")))
1961
- return n;
1962
- e = R(e);
1963
- }
1964
- }
1965
- function ao(e) {
1966
- const t = yt(process.cwd(), e) || ".", n = t === "aiui-demo" ? "aiui demo" : `aiui demo ${t}`;
1967
- console.log(`
1968
- demo ready. Run the loop:
1969
-
1970
- cd ${t}
1971
- npm run claude # terminal 1 — Claude Code with the aiui channel + session browser
1972
- npm run dev # terminal 2 — the demo app (Vite + the intent tool)
1973
-
1974
- then open the app in the session browser (the window you share with the agent):
1975
-
1976
- npx aiui open http://localhost:5173
1977
-
1978
- Click the ✳ aiui button on the page and type an intent — it lands in the session
1979
- as a prompt. Re-run \`${n}\` anytime to continue this sandbox.`);
1980
- }
1981
- const co = "@habemus-papadum/aiui-claude-channel";
1982
- async function lo(e = []) {
1848
+ const $o = "@habemus-papadum/aiui-claude-channel";
1849
+ async function ko(e = []) {
1983
1850
  let t;
1984
1851
  try {
1985
- t = ge(co);
1852
+ t = J($o);
1986
1853
  } catch {
1987
- g(
1854
+ y(
1988
1855
  "The aiui Claude channel CLI is not available",
1989
1856
  "`@habemus-papadum/aiui-claude-channel` should be installed as a dependency of aiui — try reinstalling."
1990
1857
  ), process.exitCode = 1;
1991
1858
  return;
1992
1859
  }
1993
- const n = await k(t.command, [...t.args, ...e], {
1860
+ const n = ut(e) ? xn(e, ct({ config: P() })) : e, o = await E(t.command, [...t.args, ...n], {
1994
1861
  stdio: "inherit",
1995
1862
  reject: !1
1996
1863
  });
1997
- n.exitCode && (process.exitCode = n.exitCode);
1864
+ o.exitCode && (process.exitCode = o.exitCode);
1865
+ }
1866
+ function Co(e) {
1867
+ const t = Buffer.from(JSON.stringify(e), "utf8"), n = Buffer.allocUnsafe(4 + t.length);
1868
+ return gt() ? n.writeUInt32LE(t.length, 0) : n.writeUInt32BE(t.length, 0), t.copy(n, 4), n;
1869
+ }
1870
+ function xo(e) {
1871
+ const t = [];
1872
+ let n = 0;
1873
+ for (; e.length - n >= 4; ) {
1874
+ const o = gt() ? e.readUInt32LE(n) : e.readUInt32BE(n);
1875
+ if (e.length - n - 4 < o)
1876
+ break;
1877
+ const r = e.subarray(n + 4, n + 4 + o);
1878
+ n += 4 + o;
1879
+ try {
1880
+ t.push(JSON.parse(r.toString("utf8")));
1881
+ } catch {
1882
+ t.push(void 0);
1883
+ }
1884
+ }
1885
+ return { messages: t, rest: e.subarray(n) };
1886
+ }
1887
+ function gt() {
1888
+ return new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
1889
+ }
1890
+ function Io(e) {
1891
+ const t = e !== null && typeof e == "object" ? e.cmd : void 0;
1892
+ return t === "ping" ? { ok: !0, at: (/* @__PURE__ */ new Date()).toISOString() } : t === "version" ? { ok: !0, version: 1 } : t === "listChannels" ? { ok: !0, channels: L().map((o) => ({
1893
+ tag: o.tag,
1894
+ port: o.port,
1895
+ pid: o.pid,
1896
+ cwd: o.cwd,
1897
+ startedAt: o.startedAt,
1898
+ ...o.name !== void 0 ? { name: o.name } : {},
1899
+ ...o.debug === !0 ? { debug: !0 } : {}
1900
+ })) } : { ok: !1, error: `unknown cmd ${JSON.stringify(t)}` };
1901
+ }
1902
+ async function Eo() {
1903
+ let e = Buffer.alloc(0);
1904
+ process.stdin.on("data", (t) => {
1905
+ e = Buffer.concat([e, t]);
1906
+ const { messages: n, rest: o } = xo(e);
1907
+ e = o;
1908
+ for (const r of n) {
1909
+ let i;
1910
+ try {
1911
+ i = Io(r);
1912
+ } catch (s) {
1913
+ i = { ok: !1, error: s instanceof Error ? s.message : String(s) };
1914
+ }
1915
+ process.stdout.write(Co(i));
1916
+ }
1917
+ }), await new Promise((t) => {
1918
+ process.stdin.on("end", t), process.stdin.on("close", t);
1919
+ });
1998
1920
  }
1999
- function uo() {
1921
+ function Po() {
2000
1922
  const e = [];
2001
- for (const t of Object.values(Nt()))
1923
+ for (const t of Object.values(ze()))
2002
1924
  for (const n of t ?? [])
2003
1925
  n.family === "IPv4" && !n.internal && e.push(n.address);
2004
1926
  return e;
2005
1927
  }
2006
- async function Be(e, t) {
1928
+ async function G(e, t) {
2007
1929
  try {
2008
1930
  const n = await fetch(`http://127.0.0.1:${e}${t}`, {
2009
1931
  signal: AbortSignal.timeout(1500)
@@ -2013,53 +1935,103 @@ async function Be(e, t) {
2013
1935
  return;
2014
1936
  }
2015
1937
  }
2016
- async function ho(e) {
2017
- const t = await Be(
1938
+ async function Do(e, t) {
1939
+ const n = await G(
2018
1940
  e,
2019
- "/paint/info"
1941
+ `${t}/info`
2020
1942
  );
2021
- if (!(t != null && t.ok))
1943
+ if (!(n != null && n.ok))
2022
1944
  return;
2023
- const n = await Be(e, "/health"), o = (n == null ? void 0 : n.host) !== void 0 && n.host !== "127.0.0.1";
1945
+ const o = await G(e, "/health"), r = (o == null ? void 0 : o.host) !== void 0 && o.host !== "127.0.0.1";
2024
1946
  return {
2025
- lan: o,
2026
- urls: o ? uo().map((r) => `http://${r}:${e}/paint/`) : [`http://127.0.0.1:${e}/paint/`],
2027
- hosts: t.hosts ?? 0,
2028
- clients: t.clients ?? 0
1947
+ lan: r,
1948
+ urls: r ? Po().map((i) => `http://${i}:${e}${t}/`) : [`http://127.0.0.1:${e}${t}/`],
1949
+ hosts: n.hosts ?? 0,
1950
+ clients: n.clients ?? 0
2029
1951
  };
2030
1952
  }
2031
- async function fo(e = {}) {
2032
- const t = J(), n = [];
2033
- for (const o of t) {
2034
- const r = await ho(o.port);
2035
- r && n.push({ cwd: o.cwd, pid: o.pid, port: o.port, ...r });
1953
+ function So(e) {
1954
+ const t = { name: "loopback", address: "127.0.0.1" };
1955
+ if (e === void 0 || e === "127.0.0.1")
1956
+ return [t];
1957
+ const n = [];
1958
+ for (const [o, r] of Object.entries(ze()))
1959
+ for (const i of r ?? [])
1960
+ i.family === "IPv4" && !i.internal && n.push({ name: o, address: i.address });
1961
+ return [...n, t];
1962
+ }
1963
+ async function To(e) {
1964
+ if (process.platform !== "darwin")
1965
+ return !1;
1966
+ try {
1967
+ return await E("pbcopy", { input: e }), !0;
1968
+ } catch {
1969
+ return !1;
2036
1970
  }
2037
- if (e.json) {
2038
- console.log(JSON.stringify({ targets: n }, null, 2));
1971
+ }
1972
+ function Ao(e, t) {
1973
+ var n;
1974
+ return e.name ?? ((n = t.get(e.ppid)) == null ? void 0 : n.name) ?? e.cwd;
1975
+ }
1976
+ function No(e = {}) {
1977
+ return e.json ? Oo("/pencil") : _o("/pencil", "pencil");
1978
+ }
1979
+ async function _o(e, t) {
1980
+ const n = L();
1981
+ if (n.length === 0) {
1982
+ jo(t);
2039
1983
  return;
2040
1984
  }
2041
- if (n.length === 0) {
2042
- console.log("No running channel is hosting the paint surface."), console.log(""), console.log(
2043
- `It is on by default — start a session with ${l.cyan("aiui claude")}. If it's off here,`
2044
- ), console.log(
2045
- `check for ${l.cyan("sidecars.paint false")} in config or a --aiui-no-sidecar flag.`
1985
+ const o = await Be(n), r = await G(o.port, `${e}/info`);
1986
+ if (!(r != null && r.ok)) {
1987
+ console.log(
1988
+ `${l.bold(Ao(o, Re(He())))} is not hosting the ${t} surface.`
2046
1989
  ), process.exitCode = 1;
2047
1990
  return;
2048
1991
  }
2049
- for (const o of n) {
2050
- console.log(""), console.log(`${l.bold(o.cwd)} ${l.dim(`(channel :${o.port})`)}`);
2051
- for (const i of o.urls.length ? o.urls : ["(no LAN address found)"])
2052
- console.log(` ${l.cyan(i)}`);
2053
- const r = o.lan ? "open the URL on the iPad (same Wi-Fi)" : "loopback-only — the iPad needs a tunnel to this port (Tailscale, ssh -L), or relaunch with --aiui-bind host";
2054
- console.log(
2055
- l.dim(` ${o.hosts} browser host(s), ${o.clients} viewer(s) ${r}`)
2056
- );
1992
+ const i = await G(o.port, "/health"), s = So(i == null ? void 0 : i.host), c = (i == null ? void 0 : i.host) !== void 0 && i.host !== "127.0.0.1", u = `http://${(s.length === 1 ? s[0] : await _({
1993
+ message: "Which host interface should the iPad reach the channel on?",
1994
+ choices: s.map((f) => ({
1995
+ name: `${f.address} ${l.dim(`(${f.name})`)}`,
1996
+ value: f
1997
+ }))
1998
+ })).address}:${o.port}${e}/`, m = await To(u);
1999
+ console.log(""), console.log(` ${l.cyan(u)}`), console.log(""), m ? (console.log(l.dim(" Copied to your clipboard.")), console.log(
2000
+ l.dim(" Use Universal Clipboard (⌘V on the iPad) to paste it into Safari there.")
2001
+ )) : console.log(l.dim(" (Copy it to the iPad however you like.)")), c || console.log(
2002
+ l.dim(
2003
+ " This channel is loopback-only: the iPad needs a tunnel to this port (Tailscale, ssh -L), or relaunch with --aiui-bind host."
2004
+ )
2005
+ ), console.log("");
2006
+ }
2007
+ function jo(e) {
2008
+ console.log(`No running channel is hosting the ${e} surface.`), console.log(""), console.log(`Every channel hosts it — start a session with ${l.cyan("aiui claude")} (or a`), console.log(
2009
+ `standalone ${l.cyan("aiui mcp serve")}), then run this again to reach the ${e} surface.`
2010
+ ), process.exitCode = 1;
2011
+ }
2012
+ async function Oo(e, t) {
2013
+ var i;
2014
+ const n = L(), o = n.length > 0 ? Re(He()) : /* @__PURE__ */ new Map(), r = [];
2015
+ for (const s of n) {
2016
+ const c = await Do(s.port, e);
2017
+ if (c) {
2018
+ const a = s.name ?? ((i = o.get(s.ppid)) == null ? void 0 : i.name);
2019
+ r.push({
2020
+ cwd: s.cwd,
2021
+ pid: s.pid,
2022
+ port: s.port,
2023
+ ...a !== void 0 ? { session: a } : {},
2024
+ ...c
2025
+ });
2026
+ }
2057
2027
  }
2058
- console.log("");
2028
+ console.log(JSON.stringify({ targets: r }, null, 2));
2059
2029
  }
2060
- function po() {
2061
- const e = new gt();
2062
- e.name("aiui").description("ai ui frontends — thin launchers for Claude, Vite, and the channel CLI").version(ee).enablePositionalOptions(), e.command("claude").description("launch Claude (extra args are forwarded, e.g. `aiui claude --resume`)").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to claude").action((o) => En(o)), e.command("vite").description("launch Vite (extra args are forwarded, e.g. `aiui vite dev`)").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to vite").action((o) => Wn(o)), e.command("debug").description("open the lowering-trace viewer for a running channel (switchable in-page)").option("--mcp <tag>", "target a channel by registry tag (skips the selector)").option("--port <port>", "UI port for the viewer's dev server (default 4747)").option("--no-open", "don't open the browser").action((o) => eo(o)), e.command("chrome").description("manage the agent's browser: install | update | status | extension").argument("<action>", "install | update | status | extension").action((o) => cn([o])), e.command("browser").description(
2030
+ function Uo() {
2031
+ const e = new yt();
2032
+ e.name("aiui").description("ai ui frontends — thin launchers for Claude, Vite, and the channel CLI").version(me).enablePositionalOptions(), e.command("claude").description("launch Claude (extra args are forwarded, e.g. `aiui claude --resume`)").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to claude").action((o) => Vn(o)), e.command("vite").description("launch Vite (extra args are forwarded, e.g. `aiui vite dev`)").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to vite").action((o) => lo(o)), e.command("debug").description("open the channel console (dashboard + trace debugger) in the session browser").option("--mcp <tag>", "target a channel by registry tag (skips the selector)").option("--no-open", "print the URL but don't open the browser").action((o) => wo(o)), e.command("chrome").description("manage the agent's browser: install | update | status").argument("<action>", "install | update | status").argument("[flavor]", "for install/update: chromium | chrome-for-testing (default: configured)").action(
2033
+ (o, r) => gn(r === void 0 ? [o] : [o, r])
2034
+ ), e.command("browser").description(
2063
2035
  "start (or find) the shared session browser; --tunnel does the whole remote-dev local half"
2064
2036
  ).option(
2065
2037
  "--profile <name>",
@@ -2067,13 +2039,13 @@ function po() {
2067
2039
  ).option("--data-dir <path>", "explicit Chrome user data dir").option("--port <port>", "fixed local DevTools debug port (default: OS-assigned)").option("--headless", "launch with no UI").option("--open <url>", "also open this URL in it").option(
2068
2040
  "--tunnel <[user@]host>",
2069
2041
  "reverse-tunnel the debug port to this host (Ctrl-C closes it)"
2070
- ).option("--remote-port <port>", "fixed port on the tunnel's remote side (default: 9222)").action((o) => en(o)), e.command("demo").description("scaffold a runnable demo playground (safe to re-run; default dir: aiui-demo)").argument("[dir]", "target directory (default: aiui-demo)").option("--skip-install", "scaffold only don't run npm install").action((o, r) => to(o, r)), e.command("clean").description(
2071
- "reset aiui state (project + user cache, incl. Chrome for Testing) for a clean-slate demo"
2072
- ).option("--project-only", "only this repo's .aiui-cache/").option("--user-only", "only the user cache (~/.cache/aiui)").option("--keep-browser", "keep Chrome for Testing (skip the ~160 MB re-download)").option("-n, --dry-run", "print what would be deleted, then stop").option("-y, --yes", "delete without the confirmation prompt").action((o) => Nn(o)), e.command("open").description("open a URL in the session browser, e.g. `aiui open http://localhost:5173`").argument("<url>", "the URL to open").option("--profile <name>", "named profile under .aiui-cache/chrome/").option("--data-dir <path>", "explicit Chrome user data dir").action((o, r) => an(o, r));
2073
- const t = e.command("config").description("inspect and edit aiui's config.json — tui | show | get | set | unset").action(() => _e());
2074
- return t.command("tui").description("browse every setting interactively: docs, defaults, current values, editing").action(() => _e()), t.command("show").description("every key with its effective value and which file set it").option("--json", "machine-readable: file paths, per-level values, effective merge").action((o) => _n(o)), t.command("get").description("print a key's effective value (provenance goes to stderr)").argument("<key>", 'dotted key, e.g. "chrome.mode"').action((o) => Rn(o)), t.command("set").description("set a key in the user config (or the project's with --project)").argument("<key>", 'dotted key, e.g. "chrome.mode"').argument("<value>", "the new value, validated against the schema").option("--project", "write .aiui-cache/config.json here instead of the user config").action((o, r, i) => Ln(o, r, i)), t.command("unset").description("remove a key from the user config (or the project's with --project)").argument("<key>", 'dotted key, e.g. "claude.skipPermissions"').option("--project", "remove from .aiui-cache/config.json here instead of the user config").action((o, r) => Mn(o, r)), e.command("mcp").description("run a channel command (forwards to aiui-claude-channel), e.g. `aiui mcp quick`").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to the aiui-claude-channel CLI").action((o) => lo(o)), e.command("paint").description("the iPad paint stream — url (where the iPad should connect)").command("url").description("print the URL(s) an iPad should open, per running paint-enabled channel").option("--json", "machine-readable targets").action((o) => fo(o)), e;
2042
+ ).option("--remote-port <port>", "fixed port on the tunnel's remote side (default: 9222)").action((o) => un(o)), e.command("extension").description("the intent client extension's native host: install-native-host | status").argument("<action>", "install-native-host | status").option("--extension-id <id>", "extension id for allowed_origins (default: the pinned id)").action((o, r) => rn(o, r)), e.command("native-host", { hidden: !0 }).description("(internal) Chrome native-messaging host — spawned by the browser").action(() => Eo()), e.command("clean").description(
2043
+ "reset aiui state (project + user cache, incl. the managed browser) for a clean-slate demo"
2044
+ ).option("--project-only", "only this repo's .aiui-cache/").option("--user-only", "only the user cache (~/.cache/aiui)").option("--keep-browser", "keep the managed browser (skip the ~150-160 MB re-download)").option("-n, --dry-run", "print what would be deleted, then stop").option("-y, --yes", "delete without the confirmation prompt").action((o) => Xn(o)), e.command("open").description("open a URL in the session browser, e.g. `aiui open http://localhost:5173`").argument("<url>", "the URL to open").option("--profile <name>", "named profile under .aiui-cache/chrome/").option("--data-dir <path>", "explicit Chrome user data dir").action((o, r) => wn(o, r)), e.command("env").description('print shell code to activate this checkout — use as: eval "$(aiui env)"').action(() => vo());
2045
+ const t = e.command("config").description("inspect and edit aiui's config.json — tui | show | get | set | unset").action(() => Te());
2046
+ return t.command("tui").description("browse every setting interactively: docs, defaults, current values, editing").action(() => Te()), t.command("show").description("every key with its effective value and which file set it").option("--json", "machine-readable: file paths, per-level values, effective merge").action((o) => Zn(o)), t.command("get").description("print a key's effective value (provenance goes to stderr)").argument("<key>", 'dotted key, e.g. "chrome.mode"').action((o) => Qn(o)), t.command("set").description("set a key in the user config (or the project's with --project)").argument("<key>", 'dotted key, e.g. "chrome.mode"').argument("<value>", "the new value, validated against the schema").option("--project", "write .aiui-cache/config.json here instead of the user config").action((o, r, i) => eo(o, r, i)), t.command("unset").description("remove a key from the user config (or the project's with --project)").argument("<key>", 'dotted key, e.g. "claude.skipPermissions"').option("--project", "remove from .aiui-cache/config.json here instead of the user config").action((o, r) => to(o, r)), e.command("mcp").description("run a channel command (forwards to aiui-claude-channel), e.g. `aiui mcp quick`").allowUnknownOption().allowExcessArguments().helpOption(!1).argument("[args...]", "arguments forwarded to the aiui-claude-channel CLI").action((o) => ko(o)), e.command("pencil").description("the remote pencil — url (where the iPad should connect)").command("url").description("pick a channel + interface, print the iPad URL and copy it to the clipboard").option("--json", "machine-readable targets (every hosting channel, no prompts)").action((o) => No(o)), e;
2075
2047
  }
2076
- po().parseAsync().catch((e) => {
2048
+ Uo().parseAsync().catch((e) => {
2077
2049
  console.error(e instanceof Error ? e.message : e), process.exitCode = 1;
2078
2050
  });
2079
2051
  //# sourceMappingURL=cli.js.map