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