@habemus-papadum/aiui 0.3.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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * ai ui frontends\n *\n * The `aiui` CLI's library surface. Launch *policy* the CLI applies lives here\n * too, so sibling supervisors can reuse it instead of re-deriving it — the\n * first case is {@link resolveSidecars}, the `aiui claude` logic deciding\n * which session sidecars (e.g. the paint stream) a channel server should host for a\n * project root. The workbench imports it to give its debug channel the same\n * sidecar set a real session would get.\n *\n * @packageDocumentation\n */\n\nexport {\n type ResolveSidecarsDeps,\n resolveSidecars,\n type SidecarDescriptor,\n} from \"./util/sidecars\";\n\n/** The published package name — handy for smoke tests. */\nexport const name = \"@habemus-papadum/aiui\";\n\n/** Greet someone. Replace with your library's real API. */\nexport function greet(who: string): string {\n return `Hello, ${who}!`;\n}\n"],"names":["name","greet","who"],"mappings":";AAoBO,MAAMA,IAAO;AAGb,SAASC,EAAMC,GAAqB;AACzC,SAAO,UAAUA,CAAG;AACtB;"}
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * ai ui frontends\n *\n * The `aiui` CLI's library surface. Launch *policy* the CLI applies lives here\n * too, so sibling supervisors can reuse it instead of re-deriving it — e.g. the\n * layered config loader below, so a debug-channel supervisor binds the channel\n * the same way a real `aiui claude` launch would.\n *\n * @packageDocumentation\n */\n\n// The layered config loader (user config project config), for supervisors\n// that must obey the same settings a real `aiui claude` launch would the\n// supervisors read `channel.bind` through this so a debug channel binds the\n// way the user configured the real one.\nexport { type AiuiConfig, loadAiuiConfig } from \"./util/config\";\n\n/** The published package name — handy for smoke tests. */\nexport const name = \"@habemus-papadum/aiui\";\n\n/** Greet someone. Replace with your library's real API. */\nexport function greet(who: string): string {\n return `Hello, ${who}!`;\n}\n"],"names":["name","greet","who"],"mappings":";AAkBO,MAAMA,IAAO;AAGb,SAASC,EAAMC,GAAqB;AACzC,SAAO,UAAUA,CAAG;AACtB;"}
@@ -55,17 +55,6 @@ export interface AiuiArgs {
55
55
  * `chrome.browserUrl` in config for this launch.
56
56
  */
57
57
  browserUrl?: string;
58
- /**
59
- * Names collected from repeatable `--aiui-sidecar <name>` flags — session
60
- * sidecars to force-enable even when they wouldn't auto-detect for this
61
- * project (e.g. `--aiui-sidecar paint`).
62
- */
63
- sidecar: string[];
64
- /**
65
- * Names collected from repeatable `--aiui-no-sidecar <name>` flags — session
66
- * sidecars to disable for this run. Disable wins over enable.
67
- */
68
- noSidecar: string[];
69
58
  /**
70
59
  * The `--aiui-bind <loopback|host>` value, if provided — where the channel's
71
60
  * web backend binds for this launch, overriding `channel.bind` in config.
@@ -105,9 +94,6 @@ export declare function infoFlag(passthrough: string[]): "help" | "version" | un
105
94
  * dir instead of a named profile. Mutually exclusive with the above.
106
95
  * - `--aiui-browser-url <url>` — attach the Chrome DevTools MCP to this
107
96
  * endpoint (e.g. a tunneled remote browser) instead of managing one.
108
- * - `--aiui-sidecar <name>` / `--aiui-no-sidecar <name>` — force-enable /
109
- * disable a session sidecar by name. Both are repeatable and accumulate;
110
- * disable wins over enable when the same name appears in both.
111
97
  * - `--aiui-bind <loopback|host>` — where the channel's web backend binds for
112
98
  * this launch (see `channel.bind` in the config guide). Any other value is
113
99
  * an error.
@@ -0,0 +1,67 @@
1
+ /**
2
+ * How a channel process gets configured, in one place.
3
+ *
4
+ * There are two ways the channel server comes up, and they must agree:
5
+ *
6
+ * - **`aiui claude`** builds an argv for the channel's `mcp` subcommand and
7
+ * hands it to Claude Code inside `--mcp-config`, which spawns it.
8
+ * - **`aiui mcp serve` / `aiui mcp mcp`** run the very same channel CLI
9
+ * directly, with no Claude Code anywhere in the loop.
10
+ *
11
+ * The one durable setting they must both derive from config is where the web
12
+ * backend binds (`channel.bind`) — the trusted-LAN posture. The second path
13
+ * used to forward its arguments verbatim and so never computed it; this module
14
+ * is what makes both paths resolve the same {@link ChannelLaunch} and emit the
15
+ * same `--bind` flag, so a standalone channel binds exactly like a session's.
16
+ *
17
+ * Which sidecars a channel hosts is NOT decided here anymore. The channel
18
+ * imports and mounts its own standard set (intent, bar, pencil — see the
19
+ * channel's standard-sidecars.ts), so there is nothing to pass: every channel
20
+ * hosts all four, and `channel.bind` alone decides whether a remote device can
21
+ * reach them.
22
+ *
23
+ * What else deliberately does NOT live here: `--launch-info` (a summary of *how
24
+ * a Claude Code session was assembled* — browser wiring, key preflight — which
25
+ * a direct launch has nothing to say about, and which `serve` does not even
26
+ * accept), and `--tag` / `--name` / `--port`, which are per-invocation identity
27
+ * rather than durable configuration.
28
+ */
29
+ import type { AiuiConfig, ChannelBind } from "./config";
30
+ /** Everything a launcher knows that can influence the channel's configuration. */
31
+ export interface ChannelLaunchInput {
32
+ /** Merged user + project config (`util/config`). */
33
+ config: AiuiConfig;
34
+ /** An explicit per-launch bind (`--aiui-bind`, or `serve`'s own `--bind`). */
35
+ bind?: ChannelBind;
36
+ }
37
+ /** The resolved settings, ready to be rendered as channel CLI flags. */
38
+ export interface ChannelLaunch {
39
+ bind: ChannelBind;
40
+ }
41
+ /**
42
+ * Resolve the channel's bind for a launch. Follows the three-tier precedence
43
+ * the docs promise: per-launch flag, then durable config, then the built-in
44
+ * default (`loopback`).
45
+ */
46
+ export declare function resolveChannelLaunch(input: ChannelLaunchInput): ChannelLaunch;
47
+ /**
48
+ * Render a {@link ChannelLaunch} as flags for the channel CLI. Both the `mcp`
49
+ * and `serve` subcommands accept `--bind` (see the channel's program.ts).
50
+ */
51
+ export declare function channelLaunchFlags(launch: ChannelLaunch): string[];
52
+ /**
53
+ * Whether `aiui mcp <args...>` will start a channel process, and so wants the
54
+ * config-derived settings. Callers use this to leave the subcommands that
55
+ * merely talk to a channel someone else is running (`quick`, `config`)
56
+ * untouched.
57
+ */
58
+ export declare function isChannelLaunch(args: string[]): boolean;
59
+ /**
60
+ * Apply the config-derived channel settings to a raw `aiui mcp <args...>`
61
+ * invocation.
62
+ *
63
+ * Returns `args` untouched unless its subcommand actually launches a channel.
64
+ * A setting the caller named explicitly is never overridden — `aiui mcp serve
65
+ * --bind host` means `host` no matter what config says.
66
+ */
67
+ export declare function applyChannelLaunchArgs(args: string[], launch: ChannelLaunch): string[];
@@ -0,0 +1,33 @@
1
+ /**
2
+ * channel-target.ts — resolve which running channel a channel-CONNECTING
3
+ * command should talk to.
4
+ *
5
+ * Used by the commands that genuinely reach into a channel: `aiui debug` (the
6
+ * trace viewer, bound to one channel's port) and, in spirit, the intent
7
+ * client's standalone `pnpm dev` launcher. Deliberately NOT used by `aiui
8
+ * vite` any more: an app it serves reaches the channel through the intent
9
+ * client at `/intent/`, not a build-time port, so there is nothing to resolve.
10
+ * (This helper lived in `commands/vite.ts` while vite was that connection;
11
+ * it moved here when vite stopped connecting — owner, 2026-07-17.)
12
+ */
13
+ import type { RunningServer } from "@habemus-papadum/aiui-claude-channel";
14
+ /** The channel a command should point at, or why it couldn't be resolved. */
15
+ export interface ChannelTarget {
16
+ /** The server resolved without prompting (by tag). */
17
+ server?: RunningServer;
18
+ /** Servers to offer in the interactive selector (no tag given, ≥1 running). */
19
+ select?: RunningServer[];
20
+ /** A human-readable reason a requested server couldn't be resolved. */
21
+ error?: string;
22
+ }
23
+ /**
24
+ * Decide which running channel server a command should connect to.
25
+ *
26
+ * Pure so it can be unit-tested without spawning anything:
27
+ * - With a `targetTag`, return the server whose `tag` matches exactly; if none
28
+ * matches, return an `error` naming the tag and the tags that *are* running.
29
+ * - Without a `targetTag`, don't guess: return `{}` when nothing is running, or
30
+ * `{ select }` so the caller runs the same selector as `quick` (which
31
+ * auto-picks a lone server and prompts when there are several).
32
+ */
33
+ export declare function resolveChannelTarget(servers: RunningServer[], targetTag: string | undefined): ChannelTarget;
@@ -46,7 +46,6 @@ export interface ChromeSettings {
46
46
  /** Installed Chrome release channel to launch, if configured. */
47
47
  channel?: ChromeChannel;
48
48
  headless: boolean;
49
- buildExtension: boolean;
50
49
  }
51
50
  /**
52
51
  * Reconcile CLI flags with config into the settings for this launch.
@@ -69,43 +68,61 @@ export declare function chromeUserDataDir(ids: {
69
68
  dataDir?: string;
70
69
  profile?: string;
71
70
  }, base?: string): string;
71
+ /** The intent client's MV3 bundle directory name (see its build-ext.ts: a
72
+ * THIRD shape of that package — dist/ is the library, dist-ext/ the unpacked
73
+ * extension). */
74
+ export declare const INTENT_CLIENT_OUT_DIR = "dist-ext";
72
75
  /**
73
- * The built aiui-devtools-extension directory, if available.
76
+ * What we found when looking for the intent client's extension the one
77
+ * launches AUTO-LOAD.
74
78
  *
75
- * The package publishes `extension/` prebuilt, so this resolves both in a dev
76
- * workspace (where `extension/js` appears once built see
77
- * {@link buildDevtoolsExtension}) and installed from npm. Without `js/` the
78
- * extension is an empty shell, so an unbuilt dev checkout returns undefined.
79
+ * Deliberately simple: the bundle is a static build with no dev server and no
80
+ * dev/prod split the client's hot-iteration surface is the channel-served
81
+ * plain page, so the extension only ever needs to be BUILT.
79
82
  */
80
- export declare function devtoolsExtensionDir(): string | undefined;
81
- /**
82
- * Rebuild the aiui-devtools-extension package so the auto-loaded panel is never stale.
83
- *
84
- * A full tsc of the extension (plus the debug-ui esbuild bundle) is well
85
- * under a second — cheap enough to run on every launch rather than tracking
86
- * staleness. Best-effort by design: outside a dev
87
- * checkout (no devtools package, no typescript) it silently does nothing, and
88
- * a failing compile warns loudly but never blocks the launch — whatever
89
- * `extension/js` already holds is what gets loaded.
90
- */
91
- export declare function buildDevtoolsExtension(): Promise<void>;
83
+ export type IntentClientExtension = {
84
+ state: "absent";
85
+ } | {
86
+ state: "unbuilt";
87
+ root: string;
88
+ } | {
89
+ state: "ready";
90
+ dir: string;
91
+ };
92
+ /** Absolute paths for the intent client's bundle, in a checkout. */
93
+ export declare function intentClientExtensionPaths(): {
94
+ root: string;
95
+ outDir: string;
96
+ } | undefined;
97
+ /** {@link findIntentClientExtension}'s decision, against explicit paths. */
98
+ export declare function resolveIntentClientExtension(paths: {
99
+ root: string;
100
+ outDir: string;
101
+ } | undefined): IntentClientExtension;
102
+ /** The intent client extension, as this checkout can load it. No build-on-
103
+ * launch (the same rule as the frozen extension's ladder): the bundle is a
104
+ * deliberate act — `pnpm -C packages/aiui-intent-client build:ext`. */
105
+ export declare function findIntentClientExtension(): IntentClientExtension;
106
+ /** One launch-time note when the client is resolvable but unbuilt — printed
107
+ * every launch while true (actionable; it disappears once fixed). */
108
+ export declare function warnIntentClientState(intent: IntentClientExtension): void;
92
109
  /**
93
- * One-time tip when the extension exists but the chosen browser won't
94
- * auto-load it (no `executablePath` means a branded Chrome — installed
110
+ * One-time tip when extensions exist but the chosen browser won't
111
+ * auto-load them (no `executablePath` means a branded Chrome — installed
95
112
  * stable or a `channel` build — and branded builds ≥ 137 ignore
96
113
  * `--load-extension`). Printed once per profile: the marker file lives in the
97
114
  * user data dir, so it survives exactly as long as the profile whose manual
98
115
  * install it recommends.
99
116
  */
100
- export declare function maybeExtensionAutoloadHint(settings: ChromeSettings, extensionDir: string | undefined): void;
117
+ export declare function maybeExtensionAutoloadHint(settings: ChromeSettings, extensionDirs: string[]): void;
101
118
  /**
102
119
  * The `mcpServers` entry that launches chrome-devtools-mcp.
103
120
  *
104
121
  * Uses the documented `npx -y chrome-devtools-mcp@latest` invocation with the
105
122
  * user data dir pinned to ours. Puppeteer's default launch args include
106
- * `--disable-extensions`, which would neuter both the auto-loaded panel and
107
- * anything installed manually into the profile — so it is always stripped.
108
- * When an extension dir is given, additionally ask Chrome to load it.
123
+ * `--disable-extensions`, which would neuter both the auto-loaded extensions
124
+ * and anything installed manually into the profile — so it is always stripped.
125
+ * When extension dirs are given, additionally ask Chrome to load them.
109
126
  */
110
127
  /**
111
128
  * The `mcpServers` entry that *attaches* chrome-devtools-mcp to an existing
@@ -117,7 +134,7 @@ export declare function chromeMcpAttachServer(browserUrl: string): {
117
134
  command: string;
118
135
  args: string[];
119
136
  };
120
- export declare function chromeMcpServer(settings: ChromeSettings, extensionDir?: string): {
137
+ export declare function chromeMcpServer(settings: ChromeSettings, extensionDirs?: string[]): {
121
138
  command: string;
122
139
  args: string[];
123
140
  };
@@ -17,10 +17,6 @@ export interface AiuiConfig {
17
17
  /** Which interface the channel web server binds: loopback (default) or host (LAN). */
18
18
  bind?: ChannelBind;
19
19
  };
20
- sidecars?: {
21
- /** Host the iPad paint sidecar on the channel's port (default: true). */
22
- paint?: boolean;
23
- };
24
20
  chrome?: {
25
21
  /** Attach the Chrome DevTools MCP (default: true). */
26
22
  enabled?: boolean;
@@ -42,8 +38,12 @@ export interface AiuiConfig {
42
38
  forTesting?: ForTestingMode;
43
39
  /** Launch Chrome headless (default: false). */
44
40
  headless?: boolean;
45
- /** Rebuild the aiui-devtools-extension on every launch in a dev checkout. */
41
+ /** OBSOLETE (the devtools extension is deleted): parsed and ignored so old
42
+ * configs stay valid. */
46
43
  buildExtension?: boolean;
44
+ /** OBSOLETE (page-side getDisplayMedia capture is gone): parsed and
45
+ * ignored so old configs stay valid. */
46
+ autoCapture?: boolean;
47
47
  };
48
48
  }
49
49
  /** The `config.json` paths consulted, user-level first (base: the project dir). */
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Gemini key preflight for `aiui claude` — the GEMINI_API_KEY twin of
3
+ * {@link ./openai-preflight}.
4
+ *
5
+ * The realtime submode's reference engine is Gemini Live, and it runs in the
6
+ * *channel* process, which inherits the environment `aiui claude` launches in.
7
+ * Same key story as OpenAI: **`GEMINI_API_KEY` in the environment**, never
8
+ * config, never a flag. And the same failure this catches: a missing or stale
9
+ * key that would otherwise surface as an opaque closed WebSocket deep in a
10
+ * live session ("gemini live session closed") rather than a clear message up
11
+ * front. Degradation, not refusal — a bad key never blocks the launch; the
12
+ * realtime tier is simply unavailable until it's fixed.
13
+ *
14
+ * We record only a status — never the key or any prefix of it — so the
15
+ * launch-info summary can explain a degraded pipeline without seeing the
16
+ * secret.
17
+ */
18
+ import type { OpenAiKeyStatus } from "@habemus-papadum/aiui-claude-channel";
19
+ export interface GeminiPreflightOptions {
20
+ /**
21
+ * Perform the authenticated network check. True only for interactive,
22
+ * non-CI launches. When false the check is skipped silently and a present
23
+ * key is reported as "unverified", never contacted.
24
+ */
25
+ verify?: boolean;
26
+ /** Injectable for tests; defaults to the process environment. */
27
+ env?: NodeJS.ProcessEnv;
28
+ /** Injectable for tests; defaults to global `fetch`. */
29
+ fetchImpl?: typeof fetch;
30
+ /** Network budget for the status check (default {@link DEFAULT_TIMEOUT_MS}). */
31
+ timeoutMs?: number;
32
+ }
33
+ /**
34
+ * Determine the status of `GEMINI_API_KEY` for this launch. Same contract as
35
+ * `preflightOpenAiKey`: never throws, never prints, never returns the key.
36
+ * The key rides the query string (Gemini's auth shape — no bearer header) and
37
+ * a rejected key answers 400/401/403 depending on how it's malformed, so all
38
+ * three read as "invalid".
39
+ */
40
+ export declare function preflightGeminiKey(opts?: GeminiPreflightOptions): Promise<OpenAiKeyStatus>;
41
+ interface PreflightMessage {
42
+ level: "warn" | "note";
43
+ title: string;
44
+ detail: string;
45
+ }
46
+ /**
47
+ * The user-facing message for a preflight status, or `null` when there's
48
+ * nothing to say. Unlike the OpenAI twin, a **missing** Gemini key is a quiet
49
+ * `note`, not a warning: the default transcription tiers don't need it — only
50
+ * the realtime (Gemini Live) submode does — so its absence degrades one
51
+ * opt-in tier, not the pipeline's default path.
52
+ */
53
+ export declare function geminiPreflightMessage(status: OpenAiKeyStatus): PreflightMessage | null;
54
+ /** Print the preflight message for `status` (nothing, for a valid key). */
55
+ export declare function reportGeminiPreflight(status: OpenAiKeyStatus): void;
56
+ export {};
@@ -14,7 +14,7 @@
14
14
  * blocks the launch; the modality still mounts, but transcription/correction are
15
15
  * unavailable until the key is set (the widget says so — `mock` is the explicit
16
16
  * offline choice, not a silent fallback). The most valuable case this catches is the one that
17
- * bit the bench twice (see workbench field-notes, "Keys & config"): a **stale
17
+ * bit the bench twice (archive/workbench/field-notes.md, "Keys & config"): a **stale
18
18
  * shell export** shadowing the real key, which surfaces as a confusing 401 deep
19
19
  * in the pipeline rather than a clear message up front.
20
20
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@habemus-papadum/aiui",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "ai ui frontends",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,8 +25,7 @@
25
25
  "aiui": "./dist/cli.js"
26
26
  },
27
27
  "files": [
28
- "dist",
29
- "templates"
28
+ "dist"
30
29
  ],
31
30
  "publishConfig": {
32
31
  "access": "public"
@@ -38,12 +37,13 @@
38
37
  "commander": "^15.0.0",
39
38
  "execa": "^9.6.1",
40
39
  "vite": "^6.4.3",
41
- "@habemus-papadum/aiui-claude-plugin": "^0.3.0",
42
- "@habemus-papadum/aiui-devtools-extension": "^0.3.0",
43
- "@habemus-papadum/aiui-dev-overlay": "^0.3.0",
44
- "@habemus-papadum/aiui-paint": "^0.3.0",
45
- "@habemus-papadum/aiui-claude-channel": "^0.3.0",
46
- "@habemus-papadum/aiui-util": "^0.3.0"
40
+ "@habemus-papadum/aiui-claude-channel": "^0.5.0",
41
+ "@habemus-papadum/aiui-trace-ui": "^0.5.0",
42
+ "@habemus-papadum/aiui-claude-plugin": "^0.5.0",
43
+ "@habemus-papadum/aiui-util": "^0.5.0"
44
+ },
45
+ "devDependencies": {
46
+ "@habemus-papadum/aiui-lowering-pipeline": "^0.5.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc --emitDeclarationOnly -p tsconfig.json && vite build",
@@ -1,28 +0,0 @@
1
- export interface DemoOptions {
2
- /** Scaffold only — skip `npm install` (used by the packaging test/CI). */
3
- skipInstall?: boolean;
4
- }
5
- /** What `aiui demo` finds at the target path. */
6
- export type DemoTargetState = "new" | "existing-demo" | "occupied";
7
- export declare function runDemo(dir: string | undefined, opts?: DemoOptions): Promise<void>;
8
- /**
9
- * Decide what to do with the target: `new` (missing or empty), an
10
- * `existing-demo` (package.json carries the scaffold marker — continue), or
11
- * `occupied` (anything else — refuse; never clobber unknown content).
12
- */
13
- export declare function classifyDemoTarget(target: string): DemoTargetState;
14
- /**
15
- * The dependency range the scaffold pins aiui packages to: this build's exact
16
- * release line when it has one, `latest` from a dev build (whose `0.0.0+dev`
17
- * doesn't exist on the registry).
18
- */
19
- export declare function demoDependencyRange(version: string): string;
20
- /** Copy the template, restore dot-paths npm strips, and pin dependency ranges. */
21
- export declare function scaffoldDemo(template: string, target: string): void;
22
- /**
23
- * The template directory shipped with this install. Probed upward from this
24
- * module because the relative depth differs between layouts: `dist/cli.js`
25
- * (bundled, installed) sits one level below the package root; the tsx-run
26
- * `src/commands/demo.ts` sits two below.
27
- */
28
- export declare function templateRoot(): string | undefined;
@@ -1,3 +0,0 @@
1
- export declare function runPaintUrl(opts?: {
2
- json?: boolean;
3
- }): Promise<void>;
@@ -1,48 +0,0 @@
1
- import { createRequire as c } from "node:module";
2
- const l = c(import.meta.url), n = [
3
- {
4
- // The iPad paint stream. Always on: it rides the channel's own port (no
5
- // process, no extra listener), so hosting it costs nothing. Whether an
6
- // iPad can actually REACH it is the channel bind's decision (`channel.bind`
7
- // / `--aiui-bind`, asked at first run) — the security posture lives there,
8
- // not here. `--aiui-no-sidecar paint` / `sidecars.paint: false` turn it off.
9
- name: "paint",
10
- autoEnable: () => !0,
11
- descriptor: (a, o) => ({
12
- name: "paint",
13
- module: o("@habemus-papadum/aiui-paint/sidecar"),
14
- export: "paintSidecar",
15
- options: { root: a }
16
- })
17
- }
18
- ];
19
- function u(a, o, i = {}) {
20
- const d = i.resolveModule ?? ((e) => l.resolve(e)), s = i.log ?? ((e) => process.stderr.write(`[aiui] warning: ${e}
21
- `)), t = /* @__PURE__ */ new Set();
22
- for (const e of n)
23
- try {
24
- e.autoEnable(a) && t.add(e.name);
25
- } catch (r) {
26
- s(
27
- `sidecar "${e.name}" auto-detect failed: ${r instanceof Error ? r.message : String(r)}`
28
- );
29
- }
30
- for (const e of o.enable)
31
- n.some((r) => r.name === e) && t.add(e);
32
- for (const e of o.disable)
33
- t.delete(e);
34
- return n.filter((e) => t.has(e.name)).map((e) => {
35
- try {
36
- return e.descriptor(a, d);
37
- } catch (r) {
38
- s(
39
- `sidecar "${e.name}" disabled — its module failed to resolve: ${r instanceof Error ? r.message : String(r)}`
40
- );
41
- return;
42
- }
43
- }).filter((e) => e !== void 0);
44
- }
45
- export {
46
- u as r
47
- };
48
- //# sourceMappingURL=sidecars-BKlhUA0V.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sidecars-BKlhUA0V.js","sources":["../src/util/sidecars.ts"],"sourcesContent":["/**\n * Deciding which **session sidecars** the channel should host for a launch.\n *\n * A sidecar is an extra backend the channel mounts alongside the intent\n * pipeline — the concrete one today is the iPad paint stream. The channel takes\n * no dependency on any sidecar: `aiui claude` hands it a JSON array of\n * {@link SidecarDescriptor}s on `--sidecars`, and the channel dynamic-imports\n * each `module`, calls `mod[export ?? \"default\"](options)`, and mounts the\n * result (a bad descriptor is logged + skipped there).\n *\n * This module is the CLI's half: a small registry of the sidecars the launcher\n * knows how to construct, plus a pure resolver that decides which are on for a\n * given project root and set of `--aiui-sidecar` / `--aiui-no-sidecar` flags.\n */\n\nimport { createRequire } from \"node:module\";\n\nconst nodeRequire = createRequire(import.meta.url);\n\n/**\n * The contract the channel's `--sidecars` argument expects, mirrored here so\n * the CLI can emit it. The channel imports `module`, calls its `export` (or\n * `default`) with `options`, and mounts the returned sidecar.\n */\nexport interface SidecarDescriptor {\n /** Stable identifier, used for `--aiui-sidecar`/`--aiui-no-sidecar` and logs. */\n name: string;\n /** Importable specifier the channel `import()`s (e.g. a package subpath). */\n module: string;\n /** Named export to call as the factory. Defaults to `\"default\"`. */\n export?: string;\n /** Passed opaquely to the factory (e.g. `{ root: \"/proj\" }`). */\n options?: unknown;\n}\n\n/** Resolves a package specifier to an absolute path the channel can `import()`. */\ntype ResolveModule = (specifier: string) => string;\n\n/** Injectable seams, so the resolver is unit-testable without on-disk state. */\nexport interface ResolveSidecarsDeps {\n /**\n * Resolves a sidecar's package specifier to an ABSOLUTE path. Defaults to\n * `createRequire(import.meta.url).resolve`. This matters: the channel\n * dynamic-imports the descriptor's `module`, but it does NOT depend on any\n * sidecar package — so a bare specifier resolves from the channel's own\n * node_modules and fails (pnpm's isolated layout). Resolving here (from the\n * `aiui` CLI, which DOES depend on the sidecar package) to an absolute path\n * makes the import work in both the source-first workspace and an install.\n */\n resolveModule?: ResolveModule;\n /** Warning sink for a sidecar that had to be dropped (defaults to stderr). */\n log?: (message: string) => void;\n}\n\n/** A sidecar the CLI knows how to enable and construct, keyed by name. */\ninterface KnownSidecar {\n name: string;\n /** Whether this sidecar auto-enables for the given project root. */\n autoEnable: (root: string) => boolean;\n /** Build the descriptor the channel will mount for this root. */\n descriptor: (root: string, resolveModule: ResolveModule) => SidecarDescriptor;\n}\n\n/**\n * The registry of sidecars the launcher can enable. Emit order follows this\n * list, so the resolver's output is stable regardless of flag order. The paint\n * stream is always on — see its entry.\n */\nconst KNOWN_SIDECARS: KnownSidecar[] = [\n {\n // The iPad paint stream. Always on: it rides the channel's own port (no\n // process, no extra listener), so hosting it costs nothing. Whether an\n // iPad can actually REACH it is the channel bind's decision (`channel.bind`\n // / `--aiui-bind`, asked at first run) — the security posture lives there,\n // not here. `--aiui-no-sidecar paint` / `sidecars.paint: false` turn it off.\n name: \"paint\",\n autoEnable: () => true,\n descriptor: (root, resolveModule) => ({\n name: \"paint\",\n module: resolveModule(\"@habemus-papadum/aiui-paint/sidecar\"),\n export: \"paintSidecar\",\n options: { root },\n }),\n },\n];\n\n/**\n * Decide which session sidecars to host for `root`.\n *\n * Starts from the auto-detected set (each known sidecar whose `autoEnable`\n * predicate is truthy for this root), then applies the flags: `opts.enable`\n * force-adds a known sidecar by name even when it wouldn't auto-detect, and\n * `opts.disable` removes one. Disable wins over enable. Enable names the CLI\n * doesn't know how to construct are ignored (the launcher can only build the\n * sidecars in {@link KNOWN_SIDECARS}). Descriptors come back in the registry's\n * stable order.\n */\nexport function resolveSidecars(\n root: string,\n opts: { enable: string[]; disable: string[] },\n deps: ResolveSidecarsDeps = {},\n): SidecarDescriptor[] {\n const resolveModule =\n deps.resolveModule ?? ((specifier: string) => nodeRequire.resolve(specifier));\n const log =\n deps.log ?? ((message: string) => process.stderr.write(`[aiui] warning: ${message}\\n`));\n const enabled = new Set<string>();\n\n // Auto-detected sidecars. A throwing detector must not kill the launch —\n // warn and treat as not detected.\n for (const known of KNOWN_SIDECARS) {\n try {\n if (known.autoEnable(root)) {\n enabled.add(known.name);\n }\n } catch (err) {\n log(\n `sidecar \"${known.name}\" auto-detect failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n // Force-enable, but only sidecars the CLI knows how to construct.\n for (const name of opts.enable) {\n if (KNOWN_SIDECARS.some((k) => k.name === name)) {\n enabled.add(name);\n }\n }\n // Disable wins over enable.\n for (const name of opts.disable) {\n enabled.delete(name);\n }\n\n return KNOWN_SIDECARS.filter((k) => enabled.has(k.name))\n .map((k) => {\n try {\n return k.descriptor(root, resolveModule);\n } catch (err) {\n // The sidecar's package isn't resolvable (e.g. not installed) — skip it\n // rather than failing the whole launch, but say so: silently dropping it\n // would surface as \"the sidecar is mysteriously absent\".\n log(\n `sidecar \"${k.name}\" disabled — its module failed to resolve: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n return undefined;\n }\n })\n .filter((d): d is SidecarDescriptor => d !== undefined);\n}\n"],"names":["nodeRequire","createRequire","KNOWN_SIDECARS","root","resolveModule","resolveSidecars","opts","deps","specifier","log","message","enabled","known","err","name","k","d"],"mappings":";AAiBA,MAAMA,IAAcC,EAAc,YAAY,GAAG,GAmD3CC,IAAiC;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAME,MAAM;AAAA,IACN,YAAY,MAAM;AAAA,IAClB,YAAY,CAACC,GAAMC,OAAmB;AAAA,MACpC,MAAM;AAAA,MACN,QAAQA,EAAc,qCAAqC;AAAA,MAC3D,QAAQ;AAAA,MACR,SAAS,EAAE,MAAAD,EAAA;AAAA,IAAK;AAAA,EAClB;AAEJ;AAaO,SAASE,EACdF,GACAG,GACAC,IAA4B,CAAA,GACP;AACrB,QAAMH,IACJG,EAAK,kBAAkB,CAACC,MAAsBR,EAAY,QAAQQ,CAAS,IACvEC,IACJF,EAAK,QAAQ,CAACG,MAAoB,QAAQ,OAAO,MAAM,mBAAmBA,CAAO;AAAA,CAAI,IACjFC,wBAAc,IAAA;AAIpB,aAAWC,KAASV;AAClB,QAAI;AACF,MAAIU,EAAM,WAAWT,CAAI,KACvBQ,EAAQ,IAAIC,EAAM,IAAI;AAAA,IAE1B,SAASC,GAAK;AACZ,MAAAJ;AAAA,QACE,YAAYG,EAAM,IAAI,yBACpBC,aAAe,QAAQA,EAAI,UAAU,OAAOA,CAAG,CACjD;AAAA,MAAA;AAAA,IAEJ;AAGF,aAAWC,KAAQR,EAAK;AACtB,IAAIJ,EAAe,KAAK,CAACa,MAAMA,EAAE,SAASD,CAAI,KAC5CH,EAAQ,IAAIG,CAAI;AAIpB,aAAWA,KAAQR,EAAK;AACtB,IAAAK,EAAQ,OAAOG,CAAI;AAGrB,SAAOZ,EAAe,OAAO,CAACa,MAAMJ,EAAQ,IAAII,EAAE,IAAI,CAAC,EACpD,IAAI,CAACA,MAAM;AACV,QAAI;AACF,aAAOA,EAAE,WAAWZ,GAAMC,CAAa;AAAA,IACzC,SAASS,GAAK;AAIZ,MAAAJ;AAAA,QACE,YAAYM,EAAE,IAAI,8CAChBF,aAAe,QAAQA,EAAI,UAAU,OAAOA,CAAG,CACjD;AAAA,MAAA;AAEF;AAAA,IACF;AAAA,EACF,CAAC,EACA,OAAO,CAACG,MAA8BA,MAAM,MAAS;AAC1D;"}
@@ -1,62 +0,0 @@
1
- /**
2
- * Deciding which **session sidecars** the channel should host for a launch.
3
- *
4
- * A sidecar is an extra backend the channel mounts alongside the intent
5
- * pipeline — the concrete one today is the iPad paint stream. The channel takes
6
- * no dependency on any sidecar: `aiui claude` hands it a JSON array of
7
- * {@link SidecarDescriptor}s on `--sidecars`, and the channel dynamic-imports
8
- * each `module`, calls `mod[export ?? "default"](options)`, and mounts the
9
- * result (a bad descriptor is logged + skipped there).
10
- *
11
- * This module is the CLI's half: a small registry of the sidecars the launcher
12
- * knows how to construct, plus a pure resolver that decides which are on for a
13
- * given project root and set of `--aiui-sidecar` / `--aiui-no-sidecar` flags.
14
- */
15
- /**
16
- * The contract the channel's `--sidecars` argument expects, mirrored here so
17
- * the CLI can emit it. The channel imports `module`, calls its `export` (or
18
- * `default`) with `options`, and mounts the returned sidecar.
19
- */
20
- export interface SidecarDescriptor {
21
- /** Stable identifier, used for `--aiui-sidecar`/`--aiui-no-sidecar` and logs. */
22
- name: string;
23
- /** Importable specifier the channel `import()`s (e.g. a package subpath). */
24
- module: string;
25
- /** Named export to call as the factory. Defaults to `"default"`. */
26
- export?: string;
27
- /** Passed opaquely to the factory (e.g. `{ root: "/proj" }`). */
28
- options?: unknown;
29
- }
30
- /** Resolves a package specifier to an absolute path the channel can `import()`. */
31
- type ResolveModule = (specifier: string) => string;
32
- /** Injectable seams, so the resolver is unit-testable without on-disk state. */
33
- export interface ResolveSidecarsDeps {
34
- /**
35
- * Resolves a sidecar's package specifier to an ABSOLUTE path. Defaults to
36
- * `createRequire(import.meta.url).resolve`. This matters: the channel
37
- * dynamic-imports the descriptor's `module`, but it does NOT depend on any
38
- * sidecar package — so a bare specifier resolves from the channel's own
39
- * node_modules and fails (pnpm's isolated layout). Resolving here (from the
40
- * `aiui` CLI, which DOES depend on the sidecar package) to an absolute path
41
- * makes the import work in both the source-first workspace and an install.
42
- */
43
- resolveModule?: ResolveModule;
44
- /** Warning sink for a sidecar that had to be dropped (defaults to stderr). */
45
- log?: (message: string) => void;
46
- }
47
- /**
48
- * Decide which session sidecars to host for `root`.
49
- *
50
- * Starts from the auto-detected set (each known sidecar whose `autoEnable`
51
- * predicate is truthy for this root), then applies the flags: `opts.enable`
52
- * force-adds a known sidecar by name even when it wouldn't auto-detect, and
53
- * `opts.disable` removes one. Disable wins over enable. Enable names the CLI
54
- * doesn't know how to construct are ignored (the launcher can only build the
55
- * sidecars in {@link KNOWN_SIDECARS}). Descriptors come back in the registry's
56
- * stable order.
57
- */
58
- export declare function resolveSidecars(root: string, opts: {
59
- enable: string[];
60
- disable: string[];
61
- }, deps?: ResolveSidecarsDeps): SidecarDescriptor[];
62
- export {};
@@ -1,16 +0,0 @@
1
- # aiui demo playground
2
-
3
- This directory is a scaffolded, disposable aiui demo — a sandbox the user is exploring the aiui
4
- workflow in. Ground rules for working here:
5
-
6
- - **The app is scenery.** `src/main.ts` renders a fake "spectra" viewer purely so there's
7
- something to point at and modify. Redesign it, extend it, replace it — that's the point of the
8
- demo. Prefer small, visible changes the user can watch land.
9
- - **Don't remove the integration.** The `aiuiDevOverlay()` plugin in `vite.config.ts` is what
10
- mounts the intent tool and connects it to this session's channel. The demo stops working
11
- without it.
12
- - The dev server runs via `npm run dev` (which is `aiui vite dev` — it injects the channel port
13
- as `VITE_AIUI_PORT`). Plain `vite` also serves the app, but the intent tool won't find the
14
- channel.
15
- - This is a standalone git repo scaffolded by `aiui demo`; commit freely — history here belongs
16
- to the user's sandbox and goes nowhere else.
@@ -1,36 +0,0 @@
1
- # aiui demo playground
2
-
3
- A disposable sandbox for trying [aiui](https://habemus-papadum.github.io/pdum_aiui/): a Claude
4
- Code session with a custom channel, a shared agent+human browser, and a web intent tool floating
5
- over a demo app. It's a local git repo of its own — let the agent redesign, break, and rebuild
6
- the app; nothing flows back anywhere.
7
-
8
- > ⚠️ First read *Read before running* in the aiui docs: `aiui claude` can skip permissions and
9
- > gives the agent a browser. This sandbox assumes you've decided to trust it.
10
-
11
- ## Run it
12
-
13
- ```sh
14
- npm run claude # terminal 1 — Claude Code with the aiui channel + session browser
15
- npm run dev # terminal 2 — the demo app (Vite + the intent tool overlay)
16
- ```
17
-
18
- Then open the app **in the session browser** (the window you share with the agent):
19
-
20
- ```sh
21
- npx aiui open http://localhost:5173
22
- ```
23
-
24
- Click the floating **✳ aiui** button, type an intent — *"make the baseline curve red"* — and
25
- watch it land in the Claude session as a prompt. The 🔍 button shows how your input was lowered
26
- into that prompt.
27
-
28
- ## What's what
29
-
30
- - `vite.config.ts` — the **entire** aiui integration: one `aiuiDevOverlay()` plugin. This is the
31
- part you'd copy into your own app.
32
- - `src/main.ts` — throwaway scenery (a fake spectrum viewer). No aiui code in it.
33
- - Re-running the scaffold command in this directory just picks up where you left off — it never
34
- overwrites your (or the agent's) changes.
35
-
36
- Docs: <https://habemus-papadum.github.io/pdum_aiui/guide/getting-started>
@@ -1,4 +0,0 @@
1
- node_modules/
2
- dist/
3
- .aiui-cache/
4
- *.log
@@ -1,11 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>spectra — aiui demo playground</title>
7
- </head>
8
- <body>
9
- <script type="module" src="/src/main.ts"></script>
10
- </body>
11
- </html>
@@ -1,23 +0,0 @@
1
- {
2
- "name": "aiui-demo-playground",
3
- "private": true,
4
- "type": "module",
5
- "aiui": {
6
- "demo": true
7
- },
8
- "scripts": {
9
- "claude": "aiui claude",
10
- "dev": "aiui vite dev",
11
- "open": "aiui open"
12
- },
13
- "dependencies": {
14
- "@habemus-papadum/aiui-dev-overlay": "__AIUI_VERSION_RANGE__"
15
- },
16
- "devDependencies": {
17
- "@babel/core": "^7.29.7",
18
- "@habemus-papadum/aiui": "__AIUI_VERSION_RANGE__",
19
- "typescript": "^5.6.0",
20
- "typescript-language-server": "^5.3.0",
21
- "vite": "^6.4.1"
22
- }
23
- }