@habemus-papadum/aiui 0.3.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/cli.js +1121 -1149
- package/dist/cli.js.map +1 -1
- package/dist/commands/browser.d.ts +6 -4
- package/dist/commands/clean.d.ts +5 -4
- package/dist/commands/debug.d.ts +1 -9
- package/dist/commands/env.d.ts +36 -0
- package/dist/commands/extension.d.ts +46 -0
- package/dist/commands/mcp.d.ts +10 -0
- package/dist/commands/native-host.d.ts +16 -0
- package/dist/commands/pencil-url.d.ts +18 -0
- package/dist/commands/vite.d.ts +17 -29
- package/dist/config-xLcoLiLT.js +315 -0
- package/dist/config-xLcoLiLT.js.map +1 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/util/aiui-args.d.ts +0 -14
- package/dist/util/channel-launch.d.ts +67 -0
- package/dist/util/channel-target.d.ts +33 -0
- package/dist/util/chrome.d.ts +70 -27
- package/dist/util/config-schema.d.ts +23 -1
- package/dist/util/config.d.ts +21 -11
- package/dist/util/gemini-preflight.d.ts +56 -0
- package/dist/util/managed-browser.d.ts +109 -0
- package/dist/util/openai-preflight.d.ts +1 -1
- package/package.json +9 -9
- package/dist/commands/demo.d.ts +0 -28
- package/dist/commands/paint.d.ts +0 -3
- package/dist/sidecars-BKlhUA0V.js +0 -48
- package/dist/sidecars-BKlhUA0V.js.map +0 -1
- package/dist/util/cft.d.ts +0 -79
- package/dist/util/sidecars.d.ts +0 -62
- package/templates/demo/CLAUDE.md +0 -16
- package/templates/demo/README.md +0 -36
- package/templates/demo/gitignore +0 -4
- package/templates/demo/index.html +0 -11
- package/templates/demo/package.json +0 -23
- package/templates/demo/src/main.ts +0 -51
- package/templates/demo/tsconfig.json +0 -11
- package/templates/demo/vite.config.ts +0 -17
|
@@ -45,10 +45,12 @@ export interface StartSessionBrowserOptions {
|
|
|
45
45
|
* it:
|
|
46
46
|
*
|
|
47
47
|
* 1. Resolve settings from flags + config.
|
|
48
|
-
* 2. Prefer the managed
|
|
49
|
-
*
|
|
50
|
-
* `interactive`; otherwise it just
|
|
51
|
-
*
|
|
48
|
+
* 2. Prefer the managed browser (Chromium by default, or Chrome for Testing
|
|
49
|
+
* per chrome.managed) unless config names a browser explicitly (the sync
|
|
50
|
+
* may prompt to install/update — only when `interactive`; otherwise it just
|
|
51
|
+
* reports what's installed).
|
|
52
|
+
* 3. Load the intent client's MV3 bundle if it has been built (dev checkouts
|
|
53
|
+
* only).
|
|
52
54
|
* 4. Launch on the profile's user data dir and wait for the debug endpoint.
|
|
53
55
|
*
|
|
54
56
|
* Throws with a remediation-bearing message when no browser can be found or
|
package/dist/commands/clean.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface CleanOptions {
|
|
|
3
3
|
projectOnly?: boolean;
|
|
4
4
|
/** Limit the reset to the user cache (`~/.cache/aiui`). */
|
|
5
5
|
userOnly?: boolean;
|
|
6
|
-
/** Keep the managed
|
|
6
|
+
/** Keep the managed browser installs (skip the ~150-160 MB re-download). */
|
|
7
7
|
keepBrowser?: boolean;
|
|
8
8
|
/** Print what would be deleted, then stop. */
|
|
9
9
|
dryRun?: boolean;
|
|
@@ -16,8 +16,8 @@ export interface CleanRoots {
|
|
|
16
16
|
project: string;
|
|
17
17
|
/** `~/.cache/aiui` (respects `$AIUI_CACHE` / `$XDG_CACHE_HOME`). */
|
|
18
18
|
user: string;
|
|
19
|
-
/** The managed
|
|
20
|
-
|
|
19
|
+
/** The managed-browser dirs (chromium/, chrome/) — children of {@link CleanRoots.user}. */
|
|
20
|
+
browsers: string[];
|
|
21
21
|
}
|
|
22
22
|
/** One thing `clean` will delete: a root path, optionally sparing some children. */
|
|
23
23
|
export interface CleanTarget {
|
|
@@ -27,7 +27,8 @@ export interface CleanTarget {
|
|
|
27
27
|
path: string;
|
|
28
28
|
/**
|
|
29
29
|
* Absolute child paths to preserve instead of deleting `path` wholesale. Used
|
|
30
|
-
* by `--keep-browser` to clear the user cache while keeping
|
|
30
|
+
* by `--keep-browser` to clear the user cache while keeping the managed
|
|
31
|
+
* browser dirs (`chromium/`, `chrome/`).
|
|
31
32
|
*/
|
|
32
33
|
keep?: string[];
|
|
33
34
|
}
|
package/dist/commands/debug.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export interface DebugOptions {
|
|
2
2
|
/** Target a channel by its registry tag instead of the interactive selector. */
|
|
3
3
|
mcp?: string;
|
|
4
|
-
/**
|
|
5
|
-
port?: string;
|
|
6
|
-
/** Open the browser at the viewer (default true; `--no-open` skips). */
|
|
4
|
+
/** Open the browser at the console (default true; `--no-open` skips). */
|
|
7
5
|
open?: boolean;
|
|
8
6
|
}
|
|
9
|
-
/**
|
|
10
|
-
* This package's root — the Vite server root, found by walking up from this
|
|
11
|
-
* module to the `@habemus-papadum/aiui` package.json (two levels in the
|
|
12
|
-
* source tree, but the walk keeps it honest against dist layouts too).
|
|
13
|
-
*/
|
|
14
|
-
export declare function packageRoot(): string | undefined;
|
|
15
7
|
export declare function runDebug(opts?: DebugOptions): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The env files read from the workspace root, in load order (later wins). */
|
|
2
|
+
export declare const ENV_FILES: readonly [".env", ".env.local", ".env.dev", ".env.dev.local"];
|
|
3
|
+
/**
|
|
4
|
+
* Find the workspace root: the nearest ancestor of `start` carrying a
|
|
5
|
+
* `pnpm-workspace.yaml` or a `.git`. Falls back to `start` itself (a bare
|
|
6
|
+
* consumer project without either still gets its own ./node_modules/.bin).
|
|
7
|
+
*/
|
|
8
|
+
export declare function findWorkspaceRoot(start: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Parse one dotenv file's text into ordered KEY → value entries. Deliberately
|
|
11
|
+
* minimal (the files here hold API keys, not programs): `KEY=VALUE` lines, an
|
|
12
|
+
* optional `export ` prefix, `#` comment lines and blanks skipped, one pair of
|
|
13
|
+
* matching single or double outer quotes stripped. No escape processing, no
|
|
14
|
+
* multiline values, no inline comments after unquoted values.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseDotenv(text: string): Map<string, string>;
|
|
17
|
+
/** Single-quote `value` for POSIX shells (embedded `'` becomes `'\''`). */
|
|
18
|
+
export declare function shQuote(value: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Render the activation script. Pure — the command resolves dirs and reads
|
|
21
|
+
* files, this just writes shell. Every line ends in `;` (see the module
|
|
22
|
+
* header: unquoted `eval` survival), and each PATH prepend is wrapped in a
|
|
23
|
+
* containment check so activation is idempotent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildEnvScript(input: {
|
|
26
|
+
/** Directories to prepend to PATH, highest priority first. */
|
|
27
|
+
pathDirs: string[];
|
|
28
|
+
/** Env entries to export (already merged across files, later-file wins). */
|
|
29
|
+
vars: Map<string, string>;
|
|
30
|
+
}): string;
|
|
31
|
+
/**
|
|
32
|
+
* The command: resolve the workspace root from the cwd, collect the PATH dirs
|
|
33
|
+
* that exist, merge the env files, and print the script (stdout) + a summary
|
|
34
|
+
* of what it does (stderr, names only — never a value).
|
|
35
|
+
*/
|
|
36
|
+
export declare function runEnv(): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** The NM host name (lowercase alphanumerics, dots, underscores only). */
|
|
2
|
+
export declare const NATIVE_HOST_NAME = "com.habemus_papadum.aiui";
|
|
3
|
+
/**
|
|
4
|
+
* The intent client's stable unpacked-extension id (see
|
|
5
|
+
* `packages/aiui-intent-client/src/ext/manifest.ts`, which owns the key this
|
|
6
|
+
* is derived from). Duplicated rather than imported: `aiui` does not depend on
|
|
7
|
+
* the intent client, and this is a 32-char constant that changes only if the
|
|
8
|
+
* key does.
|
|
9
|
+
*/
|
|
10
|
+
export declare const INTENT_CLIENT_EXTENSION_ID = "cdpbfpcelmifhagikjlfpgfipggcmdeg";
|
|
11
|
+
export interface ExtensionOptions {
|
|
12
|
+
extensionId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Browser NM manifest directories, user-level, per platform.
|
|
16
|
+
*
|
|
17
|
+
* Deliberately NO Chrome for Testing entry: CfT does not read a fixed
|
|
18
|
+
* user-level directory — it looks in `<user-data-dir>/NativeMessagingHosts`
|
|
19
|
+
* (measured on macOS; an earlier `Google/ChromeForTesting` guess here was
|
|
20
|
+
* never read by anything). aiui-launched browsers get the manifest written
|
|
21
|
+
* into their profile at launch instead ({@link installProfileNativeHost}).
|
|
22
|
+
*/
|
|
23
|
+
export declare function nativeHostManifestDirs(platform: NodeJS.Platform, home: string): string[];
|
|
24
|
+
/** The wrapper script body. Exported for tests. */
|
|
25
|
+
export declare function wrapperScript(cwd: string, command: string, args: string[]): string;
|
|
26
|
+
export declare function runExtension(action: string, options?: ExtensionOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Install the NM manifest into a session-browser profile
|
|
29
|
+
* (`<user-data-dir>/NativeMessagingHosts/`) — the directory Chrome for
|
|
30
|
+
* Testing actually consults (measured; see the module doc). Called by the
|
|
31
|
+
* launchers whenever they load the intent client's extension: the profile
|
|
32
|
+
* lives in aiui's own project-local cache, so unlike the global install this
|
|
33
|
+
* needs no user decision — it is the same class of write as creating the
|
|
34
|
+
* profile. Quiet and idempotent; no browser restart needed (NM manifests are
|
|
35
|
+
* read per `connectNative`/`sendNativeMessage` call).
|
|
36
|
+
*/
|
|
37
|
+
export declare function installProfileNativeHost(userDataDir: string, options?: ExtensionOptions): void;
|
|
38
|
+
/**
|
|
39
|
+
* {@link installProfileNativeHost} as the launchers call it: only when the
|
|
40
|
+
* intent client's extension is actually loadable (no point granting a host to
|
|
41
|
+
* an extension that won't be there), and never fatal — a failed write
|
|
42
|
+
* degrades to the panel's type-a-port fallback, with a note saying so. Called
|
|
43
|
+
* on both the launch and the attach-to-running paths, so a profile whose
|
|
44
|
+
* browser outlives many sessions still converges on a current manifest.
|
|
45
|
+
*/
|
|
46
|
+
export declare function ensureProfileNativeHost(userDataDir: string, intentReady: boolean, warn: (title: string, detail: string) => void): void;
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
* user-facing channel commands under `aiui` without moving them out of the
|
|
8
8
|
* package that owns the MCP server Claude Code spawns.
|
|
9
9
|
*
|
|
10
|
+
* **One thing is not verbatim.** The subcommands that *are* a channel process —
|
|
11
|
+
* `serve` (standalone debug channel) and `mcp` (the stdio MCP server) — get the
|
|
12
|
+
* same config-derived `--bind` that `aiui claude` computes when it tells Claude
|
|
13
|
+
* Code how to spawn the channel (see util/channel-launch), so both ways of
|
|
14
|
+
* starting a channel honor `channel.bind` identically. (Sidecars need no such
|
|
15
|
+
* plumbing: the channel imports and mounts its own standard set — paint, intent,
|
|
16
|
+
* bar, pencil — so every channel hosts all four.) Flags you pass explicitly
|
|
17
|
+
* always win. Every other subcommand (`quick`, `config`) talks to a channel
|
|
18
|
+
* someone *else* is running and forwards untouched.
|
|
19
|
+
*
|
|
10
20
|
* Like `aiui vite`, the channel CLI is a declared dependency, so we resolve it
|
|
11
21
|
* from node_modules (tsx-from-source in a dev checkout, the built `dist` when
|
|
12
22
|
* installed; see {@link resolvePackageCli}) rather than looking on the PATH.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Encode one native-messaging frame (native-endian u32 length + JSON). Pure. */
|
|
2
|
+
export declare function encodeNativeFrame(message: unknown): Buffer;
|
|
3
|
+
/**
|
|
4
|
+
* Split complete frames off an accumulating buffer. Returns the parsed
|
|
5
|
+
* messages and the unconsumed remainder (a partial frame stays buffered).
|
|
6
|
+
* Unparseable JSON inside a complete frame yields `undefined` in its slot —
|
|
7
|
+
* the caller answers with an error rather than dying. Pure.
|
|
8
|
+
*/
|
|
9
|
+
export declare function decodeNativeFrames(buffer: Buffer): {
|
|
10
|
+
messages: unknown[];
|
|
11
|
+
rest: Buffer;
|
|
12
|
+
};
|
|
13
|
+
/** Answer one request. Exported for tests. */
|
|
14
|
+
export declare function handleNativeRequest(message: unknown): Record<string, unknown>;
|
|
15
|
+
/** The stdio loop. Never resolves until stdin ends (Chrome closes it). */
|
|
16
|
+
export declare function runNativeHost(): Promise<void>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** A network interface the channel's bind makes reachable, as offered in the picker. */
|
|
2
|
+
interface HostInterface {
|
|
3
|
+
/** The OS interface name (`en0`, `lo0`) or a synthetic label (`loopback`). */
|
|
4
|
+
name: string;
|
|
5
|
+
address: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The interfaces the channel is actually reachable on, given its bind:
|
|
9
|
+
* - loopback (`127.0.0.1`) → only the loopback address; an iPad can't reach it
|
|
10
|
+
* without a tunnel, but we still offer it (the user may have one).
|
|
11
|
+
* - host (`0.0.0.0`) → every non-internal IPv4 the machine holds, named by its
|
|
12
|
+
* OS interface, plus loopback last for on-machine testing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function boundInterfaces(bindHost: string | undefined): HostInterface[];
|
|
15
|
+
export declare function runPencilUrl(opts?: {
|
|
16
|
+
json?: boolean;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
export {};
|
package/dist/commands/vite.d.ts
CHANGED
|
@@ -1,35 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
/** The channel server `runVite` should point Vite at, or why it couldn't. */
|
|
3
|
-
export interface ChannelTarget {
|
|
4
|
-
/** The server resolved without prompting (by tag). */
|
|
5
|
-
server?: RunningServer;
|
|
6
|
-
/** Servers to offer in the interactive selector (no tag given, ≥1 running). */
|
|
7
|
-
select?: RunningServer[];
|
|
8
|
-
/** A human-readable reason a requested server couldn't be resolved. */
|
|
9
|
-
error?: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Decide which running channel server Vite should connect to.
|
|
13
|
-
*
|
|
14
|
-
* Pure so it can be unit-tested without spawning anything:
|
|
15
|
-
* - With a `targetTag`, return the server whose `tag` matches exactly; if none
|
|
16
|
-
* matches, return an `error` naming the tag and the tags that *are* running.
|
|
17
|
-
* - Without a `targetTag`, don't guess: return `{}` when nothing is running, or
|
|
18
|
-
* `{ select }` so the caller runs the same selector as `quick` (which
|
|
19
|
-
* auto-picks a lone server and prompts when there are several).
|
|
20
|
-
*/
|
|
21
|
-
export declare function resolveChannelTarget(servers: RunningServer[], targetTag: string | undefined): ChannelTarget;
|
|
1
|
+
import { type AiuiArgs } from "../util/aiui-args";
|
|
22
2
|
/**
|
|
23
3
|
* Launch Vite, forwarding any extra args (e.g. `aiui vite dev`,
|
|
24
4
|
* `aiui vite --port 3000`, `aiui vite --version`).
|
|
25
5
|
*
|
|
26
|
-
* Before launching, resolve which running aiui channel server the dev server
|
|
27
|
-
* should talk to — either the one named by `--aiui-mcp <tag>` (or `--aiui-tag`),
|
|
28
|
-
* or, with no tag, the one you pick from the same selector `quick` uses (which
|
|
29
|
-
* auto-selects when only one is running) — and inject its port as
|
|
30
|
-
* {@link VITE_PORT_ENV} so the app can reach it. When a specific tag was asked
|
|
31
|
-
* for but isn't running, we fail loudly instead of connecting to the wrong one.
|
|
32
|
-
*
|
|
33
6
|
* Unlike `claude` — an external tool we look up on the PATH — Vite is a declared
|
|
34
7
|
* dependency of this package, so we resolve it straight out of node_modules and
|
|
35
8
|
* run it via the current Node with an absolute path. Resolving it also doubles
|
|
@@ -60,7 +33,7 @@ export declare function runVite(rawArgs?: string[]): Promise<void>;
|
|
|
60
33
|
* stripped *first*: Vite colors the host and the port separately, so escape
|
|
61
34
|
* sequences sit in the middle of the URL text and no pattern would survive
|
|
62
35
|
* matching against the raw bytes. Returns undefined for anything that isn't
|
|
63
|
-
* the ready line — the same shape
|
|
36
|
+
* the ready line — the same shape the retired workbench lab used, the
|
|
64
37
|
* house pattern for these one-line protocols.
|
|
65
38
|
*/
|
|
66
39
|
export declare function parseViteLocalUrl(line: string): string | undefined;
|
|
@@ -75,3 +48,18 @@ export declare function parseViteLocalUrl(line: string): string | undefined;
|
|
|
75
48
|
* process).
|
|
76
49
|
*/
|
|
77
50
|
export declare function teeAndDetectLocalUrl(source: NodeJS.ReadableStream, sink: NodeJS.WritableStream, onUrl: (url: string) => void): void;
|
|
51
|
+
/**
|
|
52
|
+
* The browser sidecar: once the dev server's URL is known, put it in front of
|
|
53
|
+
* the user — in the *session browser* (the shared window `aiui claude`
|
|
54
|
+
* attaches the agent to; see aiui-util's browser module and `aiui open`),
|
|
55
|
+
* never their default browser. A running session browser gets a new tab; none running
|
|
56
|
+
* means launching one exactly the way `aiui browser` does
|
|
57
|
+
* ({@link startSessionBrowser}), with the app as its first tab.
|
|
58
|
+
*
|
|
59
|
+
* Runs concurrently with Vite and must never interfere with it: everything is
|
|
60
|
+
* caught, failures print a warning, and the dev server keeps the terminal and
|
|
61
|
+
* keeps running either way. It is also deliberately non-interactive — Vite
|
|
62
|
+
* owns stdin — so the Chrome for Testing sync never prompts here; it just
|
|
63
|
+
* uses whatever browser is already available.
|
|
64
|
+
*/
|
|
65
|
+
export declare function openAppInBrowser(url: string, aiuiArgs: AiuiArgs): Promise<void>;
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { readFileSync as E, mkdirSync as x, writeFileSync as S } from "node:fs";
|
|
2
|
+
import { join as p, dirname as A } from "node:path";
|
|
3
|
+
import { projectCacheDir as N } from "@habemus-papadum/aiui-claude-channel";
|
|
4
|
+
import { CHROME_CHANNELS as $, cacheDir as D } from "@habemus-papadum/aiui-util";
|
|
5
|
+
const O = ["chromium", "chrome-for-testing"], b = "chromium", y = ["prompt", "auto", "off"], P = ["attach", "launch"], M = ["loopback", "host"], u = [
|
|
6
|
+
{
|
|
7
|
+
name: "claude",
|
|
8
|
+
summary: "how `aiui claude` launches Claude Code",
|
|
9
|
+
fields: [
|
|
10
|
+
{
|
|
11
|
+
key: "skipPermissions",
|
|
12
|
+
type: "boolean",
|
|
13
|
+
default: !0,
|
|
14
|
+
defaultText: "true (unset: the first interactive launch asks, then persists the answer)",
|
|
15
|
+
summary: "Launch Claude Code with --dangerously-skip-permissions.",
|
|
16
|
+
doc: "A personal preference with real consequences (docs/guide/warning): every agent action — shell commands, file writes, network, the browser — runs without asking first. aiui works fine either way. The first interactive launch asks and persists the answer at the user level; when unset, non-interactive sessions fall back to true."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: "enterNudge",
|
|
20
|
+
type: "boolean",
|
|
21
|
+
default: !0,
|
|
22
|
+
defaultText: "true (unset: the first interactive launch asks, then persists the answer)",
|
|
23
|
+
summary: "Auto-dismiss Claude Code's development-channel acknowledgement prompt.",
|
|
24
|
+
doc: "aiui loads a custom development channel, so Claude Code shows a one-key acknowledgement at every startup; this injects a single Enter keystroke into the terminal to dismiss it (best-effort TIOCSTI on /dev/tty — platforms that forbid it harmlessly do nothing). Saying no just means pressing Enter yourself each launch."
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "channel",
|
|
30
|
+
summary: "the channel server's web backend",
|
|
31
|
+
fields: [
|
|
32
|
+
{
|
|
33
|
+
key: "bind",
|
|
34
|
+
type: "enum",
|
|
35
|
+
values: M,
|
|
36
|
+
default: "loopback",
|
|
37
|
+
defaultText: '"loopback" (unset: the first interactive launch asks, then persists the answer)',
|
|
38
|
+
summary: "Which interface the channel web server binds: loopback, or host (LAN).",
|
|
39
|
+
doc: '"host" (0.0.0.0) makes the session\'s whole web surface — the iPad paint page, but also prompt injection, /debug, and every sidecar — reachable by anyone on your network, UNAUTHENTICATED. That is the trusted-LAN posture (docs/guide/warning): right on a network that is yours alone, wrong on shared Wi-Fi. "loopback" keeps everything this-machine-only; reaching the paint page from an iPad is then up to you — tunnel the channel port however you like (Tailscale, `ssh -L`). The first interactive launch asks and persists the answer at the user level. Per-launch flag: --aiui-bind.'
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "chrome",
|
|
45
|
+
summary: "the agent's browser and the Chrome DevTools MCP",
|
|
46
|
+
fields: [
|
|
47
|
+
{
|
|
48
|
+
key: "enabled",
|
|
49
|
+
type: "boolean",
|
|
50
|
+
default: !0,
|
|
51
|
+
summary: "Attach the Chrome DevTools MCP.",
|
|
52
|
+
doc: "false turns it off everywhere; true restates the default and does NOT override the CI default-off — only the --aiui-chrome flag forces it on under CI."
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "mode",
|
|
56
|
+
type: "enum",
|
|
57
|
+
values: P,
|
|
58
|
+
default: "attach",
|
|
59
|
+
summary: "How the MCP reaches a browser: shared session browser, or its own.",
|
|
60
|
+
doc: `"attach" shares a user-visible session browser: an already-running one is discovered by profile, or an interactive launch starts one eagerly. "launch" is the hands-off mode: chrome-devtools-mcp launches its own private browser lazily, on the agent's first browser tool call.`
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
key: "browserUrl",
|
|
64
|
+
type: "string",
|
|
65
|
+
defaultText: "unset (manage a browser locally)",
|
|
66
|
+
summary: "Attach to this DevTools endpoint instead of managing a browser at all.",
|
|
67
|
+
doc: 'The remote-development key (docs/guide/remote): the browser runs on another machine (started there with `aiui browser`) and its debug port is tunneled over. Setting it implies mode: "attach" and makes every local-browser setting (profile, executablePath, channel, forTesting…) irrelevant. Per-launch flag: --aiui-browser-url.',
|
|
68
|
+
validate: (e) => j(String(e)) ? void 0 : 'expected an http(s) URL like "http://127.0.0.1:9222"'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "debugPort",
|
|
72
|
+
type: "number",
|
|
73
|
+
default: 0,
|
|
74
|
+
defaultText: "0 (an OS-assigned free port)",
|
|
75
|
+
summary: "Fixed DevTools debug port for session browsers aiui launches.",
|
|
76
|
+
doc: "Pin it (e.g. 9222) when something external must find the port — an ssh tunnel, a VS Code attach-to-Chrome launch config. 0 means an OS-assigned free port.",
|
|
77
|
+
validate: (e) => Number.isInteger(e) && e >= 0 && e <= 65535 ? void 0 : "expected 0..65535"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "profile",
|
|
81
|
+
type: "string",
|
|
82
|
+
default: "default",
|
|
83
|
+
summary: "Named profile under .aiui-cache/chrome/.",
|
|
84
|
+
doc: "Per-launch flag: --aiui-chrome-profile."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "dataDir",
|
|
88
|
+
type: "string",
|
|
89
|
+
defaultText: "unset (derived from chrome.profile)",
|
|
90
|
+
summary: "Explicit Chrome user data dir; takes precedence over chrome.profile.",
|
|
91
|
+
doc: "Per-launch flag: --aiui-chrome-data-dir."
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
key: "executablePath",
|
|
95
|
+
type: "string",
|
|
96
|
+
defaultText: "unset (the managed browser — see chrome.managed — else installed Chrome)",
|
|
97
|
+
summary: "Explicit browser binary to launch — e.g. a Chrome for Testing install.",
|
|
98
|
+
doc: "Overrides chrome.managed. Chrome for Testing and Chromium both honor --load-extension, so the intent client auto-loads. Mutually exclusive with chrome.channel."
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
key: "channel",
|
|
102
|
+
type: "enum",
|
|
103
|
+
values: $,
|
|
104
|
+
defaultText: 'unset ("stable" when launching an installed Chrome)',
|
|
105
|
+
summary: "Installed Chrome release channel to launch.",
|
|
106
|
+
doc: "Overrides chrome.managed. Mutually exclusive with chrome.executablePath."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: "managed",
|
|
110
|
+
type: "enum",
|
|
111
|
+
values: O,
|
|
112
|
+
default: b,
|
|
113
|
+
summary: "Which browser aiui downloads and manages: Chromium, or Chrome for Testing.",
|
|
114
|
+
doc: `"chromium" (default) is the open-source build — it dodges the "verify you're human" reCAPTCHA that Google serves to the Chrome-for-Testing automation build, at the cost of Widevine DRM, some proprietary codecs, and Google account sign-in. "chrome-for-testing" is Google's branded automation build. Both auto-load the intent client and take the media auto-accept flags. Ignored when executablePath or channel names a browser explicitly. Each flavor keeps its own project profile under .aiui-cache/chrome/<flavor>/.`
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: "manage",
|
|
118
|
+
type: "enum",
|
|
119
|
+
values: y,
|
|
120
|
+
default: "prompt",
|
|
121
|
+
summary: "How `aiui claude` keeps the managed browser (chrome.managed) installed/current.",
|
|
122
|
+
doc: '"prompt" asks before installing or updating it — interactive sessions only, never under CI; "auto" installs/updates without asking; "off" never checks. Prompt answers ("automatically", "never ask again") persist here at the user level. Skipped entirely when executablePath or channel picks a browser explicitly. (Was chrome.forTesting.)'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "forTesting",
|
|
126
|
+
type: "enum",
|
|
127
|
+
values: y,
|
|
128
|
+
defaultText: "unset (deprecated alias for chrome.manage)",
|
|
129
|
+
summary: "DEPRECATED — old name for chrome.manage; still honored when manage is unset.",
|
|
130
|
+
doc: "Renamed to chrome.manage now that Chromium is also a managed flavor. Old configs keep working: when chrome.manage is unset, this value is used."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
key: "headless",
|
|
134
|
+
type: "boolean",
|
|
135
|
+
default: !1,
|
|
136
|
+
summary: "Launch Chrome with no UI."
|
|
137
|
+
}
|
|
138
|
+
// NOTE: `chrome.buildExtension` and `chrome.autoCapture` were removed here
|
|
139
|
+
// (owner, 2026-07-17) — both had long been parsed-and-ignored (the DevTools
|
|
140
|
+
// extension is deleted; page-side getDisplayMedia capture is gone). A config
|
|
141
|
+
// still carrying either is tolerated and dropped by validation, not a hard
|
|
142
|
+
// error — see DEPRECATED_FIELDS in util/config.ts.
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
];
|
|
146
|
+
function J() {
|
|
147
|
+
return u.flatMap(
|
|
148
|
+
(e) => e.fields.map((t) => ({ section: e, field: t, path: `${e.name}.${t.key}` }))
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function k(e) {
|
|
152
|
+
return e.type === "enum" ? "string" : e.type;
|
|
153
|
+
}
|
|
154
|
+
function v(e, t) {
|
|
155
|
+
var n;
|
|
156
|
+
return e.type === "enum" && !(e.values ?? []).includes(String(t)) ? `expected one of: ${(e.values ?? []).join(", ")}` : (n = e.validate) == null ? void 0 : n.call(e, t);
|
|
157
|
+
}
|
|
158
|
+
function B(e, t) {
|
|
159
|
+
let n;
|
|
160
|
+
switch (k(e)) {
|
|
161
|
+
case "boolean": {
|
|
162
|
+
if (t !== "true" && t !== "false")
|
|
163
|
+
return { error: "expected true or false" };
|
|
164
|
+
n = t === "true";
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
case "number": {
|
|
168
|
+
if (n = Number(t), t.trim() === "" || Number.isNaN(n))
|
|
169
|
+
return { error: "expected a number" };
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
n = t;
|
|
174
|
+
}
|
|
175
|
+
const a = v(e, n);
|
|
176
|
+
return a ? { error: a } : { value: n };
|
|
177
|
+
}
|
|
178
|
+
function C(e) {
|
|
179
|
+
return typeof e == "string" ? JSON.stringify(e) : String(e);
|
|
180
|
+
}
|
|
181
|
+
function q(e) {
|
|
182
|
+
return e.defaultText ? e.defaultText : e.default === void 0 ? "unset" : C(e.default);
|
|
183
|
+
}
|
|
184
|
+
function j(e) {
|
|
185
|
+
try {
|
|
186
|
+
const t = new URL(e);
|
|
187
|
+
return t.protocol === "http:" || t.protocol === "https:";
|
|
188
|
+
} catch {
|
|
189
|
+
return !1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const g = "config.json", I = /* @__PURE__ */ new Set(["sidecars"]), F = {
|
|
193
|
+
chrome: ["buildExtension", "autoCapture"]
|
|
194
|
+
};
|
|
195
|
+
function T(e = process.cwd()) {
|
|
196
|
+
return {
|
|
197
|
+
user: p(D(void 0, { create: !1 }), g),
|
|
198
|
+
project: p(N(e), g)
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function z(e = process.cwd()) {
|
|
202
|
+
const t = T(e);
|
|
203
|
+
return L(c(t.user) ?? {}, c(t.project) ?? {});
|
|
204
|
+
}
|
|
205
|
+
function L(e, t) {
|
|
206
|
+
const n = {};
|
|
207
|
+
for (const a of u)
|
|
208
|
+
n[a.name] = {
|
|
209
|
+
...e[a.name],
|
|
210
|
+
...t[a.name]
|
|
211
|
+
};
|
|
212
|
+
return n;
|
|
213
|
+
}
|
|
214
|
+
function c(e) {
|
|
215
|
+
let t;
|
|
216
|
+
try {
|
|
217
|
+
t = E(e, "utf8");
|
|
218
|
+
} catch {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
let n;
|
|
222
|
+
try {
|
|
223
|
+
n = JSON.parse(t);
|
|
224
|
+
} catch (a) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`invalid JSON in ${e}: ${a instanceof Error ? a.message : String(a)}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return R(n, e);
|
|
230
|
+
}
|
|
231
|
+
function R(e, t) {
|
|
232
|
+
const n = w(e, t, "the top level"), a = u.map((r) => r.name);
|
|
233
|
+
for (const r of Object.keys(n))
|
|
234
|
+
if (!(a.includes(r) || I.has(r)))
|
|
235
|
+
throw new Error(
|
|
236
|
+
`unknown key "${r}" at the top level of ${t} — known keys: ${a.join(", ")}`
|
|
237
|
+
);
|
|
238
|
+
const s = {};
|
|
239
|
+
for (const r of u) {
|
|
240
|
+
if (n[r.name] === void 0)
|
|
241
|
+
continue;
|
|
242
|
+
const l = w(n[r.name], t, `"${r.name}"`);
|
|
243
|
+
U(
|
|
244
|
+
l,
|
|
245
|
+
// Known leaf keys PLUS any retired-but-tolerated ones for this section;
|
|
246
|
+
// the copy loop below only carries `section.fields`, so a deprecated key
|
|
247
|
+
// is accepted here and then dropped.
|
|
248
|
+
[...r.fields.map((o) => o.key), ...F[r.name] ?? []],
|
|
249
|
+
t,
|
|
250
|
+
`"${r.name}"`
|
|
251
|
+
);
|
|
252
|
+
const h = {};
|
|
253
|
+
for (const o of r.fields) {
|
|
254
|
+
const i = l[o.key];
|
|
255
|
+
if (i === void 0)
|
|
256
|
+
continue;
|
|
257
|
+
const d = `${r.name}.${o.key}`, m = k(o);
|
|
258
|
+
if (typeof i !== m)
|
|
259
|
+
throw new Error(`expected a ${m} for ${d} in ${t}`);
|
|
260
|
+
const f = v(o, i);
|
|
261
|
+
if (f)
|
|
262
|
+
throw new Error(
|
|
263
|
+
`invalid ${d} ${C(i)} in ${t} — ${f}`
|
|
264
|
+
);
|
|
265
|
+
h[o.key] = i;
|
|
266
|
+
}
|
|
267
|
+
s[r.name] = h;
|
|
268
|
+
}
|
|
269
|
+
return s;
|
|
270
|
+
}
|
|
271
|
+
function K(e) {
|
|
272
|
+
return _(T().user, e);
|
|
273
|
+
}
|
|
274
|
+
function _(e, t) {
|
|
275
|
+
const n = c(e) ?? {};
|
|
276
|
+
return t(n), x(A(e), { recursive: !0 }), S(e, `${JSON.stringify(n, null, 2)}
|
|
277
|
+
`), e;
|
|
278
|
+
}
|
|
279
|
+
function Q(e = {}) {
|
|
280
|
+
return e.managed ?? b;
|
|
281
|
+
}
|
|
282
|
+
function X(e = {}) {
|
|
283
|
+
return e.manage ?? e.forTesting ?? "prompt";
|
|
284
|
+
}
|
|
285
|
+
function w(e, t, n) {
|
|
286
|
+
if (typeof e != "object" || e === null || Array.isArray(e))
|
|
287
|
+
throw new Error(`expected an object at ${n} of ${t}`);
|
|
288
|
+
return e;
|
|
289
|
+
}
|
|
290
|
+
function U(e, t, n, a) {
|
|
291
|
+
for (const s of Object.keys(e))
|
|
292
|
+
if (!t.includes(s))
|
|
293
|
+
throw new Error(
|
|
294
|
+
`unknown key "${s}" at ${a} of ${n} — known keys: ${t.join(", ")}`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
M as C,
|
|
299
|
+
b as D,
|
|
300
|
+
O as M,
|
|
301
|
+
X as a,
|
|
302
|
+
c as b,
|
|
303
|
+
T as c,
|
|
304
|
+
q as d,
|
|
305
|
+
J as e,
|
|
306
|
+
C as f,
|
|
307
|
+
_ as g,
|
|
308
|
+
u as h,
|
|
309
|
+
z as l,
|
|
310
|
+
L as m,
|
|
311
|
+
B as p,
|
|
312
|
+
Q as r,
|
|
313
|
+
K as u
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=config-xLcoLiLT.js.map
|