@ggui-ai/cli 0.14.0 → 0.16.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 CHANGED
@@ -46,7 +46,7 @@ ggui serve --mcp-only # skip agent supervision
46
46
  - ✅ Local server, viewer, cookie-authenticated WebSocket subscribe/ack all work end-to-end.
47
47
  - ✅ `ggui_render` mints shortCodes and lands on the same-origin viewer.
48
48
  - ✅ Component-code generation is wired on the OSS path via `createUiGenerator()` from `@ggui-ai/ui-gen` — the same harness the hosted runtime uses. When no LLM credentials resolve (no `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / etc.), `ggui_render` returns an `isError: true` tool result whose `structuredContent.error.code` is `NO_CREDENTIALS`, with a message naming which key to set; supply a key to get full generation locally.
49
- - 🔒 Default auth is strict pairing — only pair-minted bearer tokens authenticate `/mcp` (pair via the landing page or `POST /pair`). `--dev-allow-all` (local dev / tunnel smoke) and `--public-demo` (rate-limited, operator-paid public demo) relax `/mcp` to any-bearer; `--multi-tenant` keeps strict auth with per-user provider-key scoping; `--oauth` mounts the OAuth 2.1 + DCR routes MCP custom-connector hosts (claude.ai, ChatGPT) need. Custom adapters via `createGguiServer({ auth })`.
49
+ - 🔒 Default auth is strict pairing — only pair-minted bearer tokens authenticate `/mcp` (pair via the landing page or `POST /pair`). `--dev-allow-all` (local dev / tunnel smoke) and `--public-demo` (rate-limited, operator-paid public demo) relax `/mcp` to any-bearer; `--multi-user` keeps strict auth with per-user provider-key scoping; `--oauth` mounts the OAuth 2.1 + DCR routes MCP custom-connector hosts (claude.ai, ChatGPT) need. Custom adapters via `createGguiServer({ auth })`.
50
50
 
51
51
  ### `ggui dev`
52
52
 
@@ -61,7 +61,7 @@ export interface AppsCreateResponse {
61
61
  appId: string;
62
62
  /**
63
63
  * The app's MCP endpoint, used VERBATIM as `GGUI_MCP_URL` — `<base>/apps/<appId>`,
64
- * with NO `/mcp` suffix. The per-app cloud pod serves the MCP transport at this
64
+ * with NO `/mcp` suffix. The hosted per-app server serves the MCP transport at this
65
65
  * bare path; appending `/mcp` (a local `ggui serve` convention) 404s. Carries
66
66
  * the deployment-correct host (prod `mcp.ggui.ai`, sandbox `<env>.mcp.sandbox…`),
67
67
  * so prefer it over any reconstructed/hardcoded URL. See `resolveDeployMcpUrl`.
@@ -29,7 +29,7 @@
29
29
  * 2. **`~/.ggui/credentials.json`** — operator-persistent personal
30
30
  * store. Backed by `PlaintextFileProviderKeyStore` from
31
31
  * `@ggui-ai/mcp-server-core/plaintext` at the **global** app
32
- * scope. Single-user, single-tenant; multi-app or multi-user
32
+ * scope. Single-user, single-app; multi-app or multi-user
33
33
  * deployments swap in a different `ProviderKeyStore` binding at
34
34
  * composition time and bypass this resolver entirely.
35
35
  *
@@ -75,7 +75,7 @@ export function createByokResolver(opts = {}) {
75
75
  return globalHit;
76
76
  // Step 3: credentials file at the end-user's identity scope.
77
77
  // Only consulted when the operator opted out of paying (no
78
- // global key). The /settings UI under --multi-tenant writes
78
+ // global key). The /settings UI under --multi-user writes
79
79
  // here.
80
80
  if (resolveOpts?.userScope &&
81
81
  resolveOpts.userScope !== BYOK_GLOBAL_APP_SCOPE) {
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'node:url';
12
12
  import { GguiDevError, LocalUiRegistry, runDev } from '@ggui-ai/dev-stack';
13
13
  import { discoverLocalUis, discoverMcpMounts, discoverPrimitives, GguiJsonLoadError, loadTheme, } from '@ggui-ai/project-config/node';
14
14
  import { resolveAgentPlan } from './serve-fallback.js';
15
+ import { DRAIN_DEADLINE_MS, armDrainDeadline } from './drain-deadline.js';
15
16
  import { buildAgentRuntime, describeTunnelSession, DEV_HELP, launchBrowser, openTunnel, parseDevFlags, resolveAgentCommand, resolveHubUrl, shouldAutoOpen, } from './dev-command.js';
16
17
  import { createNullTunnelProvider, discoverTunnelProvider, } from './tunnel-provider.js';
17
18
  import { ManifestBlueprintProvider, resolveStorageFromConfig, buildNoCredentialsGguiSession, } from '@ggui-ai/mcp-server';
@@ -632,7 +633,7 @@ async function runServeCommand(args) {
632
633
  devAllowAll: parsed.devAllowAll,
633
634
  withholdResultMeta: parsed.withholdResultMeta,
634
635
  publicDemo: parsed.publicDemo,
635
- multiTenant: parsed.multiTenant,
636
+ multiUser: parsed.multiUser,
636
637
  oauth: parsed.oauth,
637
638
  browserOrigins,
638
639
  ...(parsed.publicBaseUrl !== undefined
@@ -650,7 +651,7 @@ async function runServeCommand(args) {
650
651
  port: effectivePort,
651
652
  devAllowAll: parsed.devAllowAll,
652
653
  publicDemo: parsed.publicDemo,
653
- multiTenant: parsed.multiTenant,
654
+ multiUser: parsed.multiUser,
654
655
  oauth: parsed.oauth,
655
656
  ...(parsed.publicBaseUrl !== undefined
656
657
  ? { publicBaseUrl: parsed.publicBaseUrl }
@@ -1203,7 +1204,25 @@ function readVersion() {
1203
1204
  }
1204
1205
  }
1205
1206
  main(process.argv).then((code) => {
1206
- process.exit(code);
1207
+ // Set the code and let the event loop drain — never `process.exit()`
1208
+ // here. Every command closes its own handles before resolving, so the
1209
+ // process ends on its own; a hard exit while the local embedding
1210
+ // model is loaded runs ONNX Runtime's static teardown under its
1211
+ // still-parked worker threads and aborts (`libc++abi … mutex lock
1212
+ // failed`, SIGABRT on every Ctrl-C — #855). The journeys spec
1213
+ // `serve-teardown.spec.ts` pins the clean exit on every serve shape.
1214
+ process.exitCode = code;
1215
+ // Bounded: a leaked handle must not turn Ctrl-C into a hang. If the
1216
+ // loop has not drained by the deadline, the live resources are named
1217
+ // on stderr and the process hard-exits with the same code.
1218
+ armDrainDeadline({
1219
+ exitCode: code,
1220
+ deadlineMs: DRAIN_DEADLINE_MS,
1221
+ exit: (c) => process.exit(c),
1222
+ stderr: (line) => process.stderr.write(line),
1223
+ setTimer: (fn, ms) => setTimeout(fn, ms),
1224
+ liveResources: () => process.getActiveResourcesInfo(),
1225
+ });
1207
1226
  }, (error) => {
1208
1227
  const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
1209
1228
  process.stderr.write(`ggui: ${message}\n`);
@@ -39,7 +39,19 @@ export declare function readGenerationFromGguiJson(gguiJson: unknown): {
39
39
  * backend enforces, so a deploy fails loud LOCALLY rather than with a
40
40
  * confusing 422.
41
41
  */
42
- export declare function readThemeFromGguiJson(projectRoot: string, gguiJson: unknown): AppTheme | undefined;
42
+ /** What `ggui.json#theme` resolves to for a deploy. */
43
+ export interface ThemeRead {
44
+ /** The projection the PATCH ships. */
45
+ readonly theme: AppTheme;
46
+ /**
47
+ * The families the document's `typography.faces` declare, deduped in
48
+ * declaration order. The hosted path does not deliver them yet
49
+ * (ggui#990): the deploy names them so the fallback is visible, not
50
+ * silent.
51
+ */
52
+ readonly declaredFaceFamilies: readonly string[];
53
+ }
54
+ export declare function readThemeFromGguiJson(projectRoot: string, gguiJson: unknown): Promise<ThemeRead | undefined>;
43
55
  /**
44
56
  * Assert that none of the gadget bundle references point at a loopback host.
45
57
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config-push.d.ts","sourceRoot":"","sources":["../src/config-push.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAmB3B;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,GAAG,SAAS,CAkB7D;AA4BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,GAChB,QAAQ,GAAG,SAAS,CA4CtB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAiB9E;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA2FjB"}
1
+ {"version":3,"file":"config-push.d.ts","sourceRoot":"","sources":["../src/config-push.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAmB3B;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,GAAG,SAAS,CAkB7D;AAGD;;;;;;;;;;;;;;;;;GAiBG;AACH,uDAAuD;AACvD,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAoDhC;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAiB9E;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAqGjB"}
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import process from 'node:process';
10
10
  import { dirname } from 'node:path';
11
- import { appPublicEnvSchema, appThemeSchema, LOOPBACK_HOST_RE, } from '@ggui-ai/protocol';
11
+ import { appPublicEnvSchema, appThemeSchema, canonicalOverlayHash, LOOPBACK_HOST_RE, } from '@ggui-ai/protocol';
12
12
  import { loadTheme, parseGguiJson } from '@ggui-ai/project-config/node';
13
13
  import { z } from 'zod';
14
14
  import { patchAppConfig } from './api-client.js';
@@ -73,53 +73,7 @@ export function readGenerationFromGguiJson(gguiJson) {
73
73
  return undefined;
74
74
  return { model, keySource: result.data.generation?.keySource ?? 'managed' };
75
75
  }
76
- /**
77
- * Parse a generated `:root { --ggui-x: val; … }` block into a flat
78
- * `{ '--ggui-x': 'val' }` map.
79
- *
80
- * The block is machine-generated by `@ggui-ai/design`'s theme parser
81
- * (one `--ggui-*: value;` declaration per line, wrapped in a `:root {…}`
82
- * selector), so a line-based parse is deterministic:
83
- * - split on newlines;
84
- * - on each line, split at the FIRST `:` (custom-property names never
85
- * contain `:`, so the key is unambiguous even when the value does);
86
- * - keep only `--ggui-*`-prefixed keys with a non-empty value. The
87
- * `:root {` and `}` wrapper lines have no such key and drop out.
88
- */
89
- function parseCssVarBlock(css) {
90
- const out = {};
91
- for (const line of css.split('\n')) {
92
- const i = line.indexOf(':');
93
- if (i === -1)
94
- continue;
95
- const key = line.slice(0, i).trim();
96
- let val = line.slice(i + 1).trim();
97
- if (val.endsWith(';'))
98
- val = val.slice(0, -1).trim();
99
- if (key.startsWith('--ggui-') && val.length > 0)
100
- out[key] = val;
101
- }
102
- return out;
103
- }
104
- /**
105
- * Resolve the developer's local `ggui.json#theme` into a concrete,
106
- * injection-safe {@link AppTheme} ready to PATCH to the cloud.
107
- *
108
- * Returns `undefined` when no `theme` field is declared (deploy leaves the
109
- * app's theme untouched).
110
- *
111
- * When a theme IS declared we parse the FULL manifest (via `parseGguiJson`)
112
- * — `loadTheme` requires the typed `GguiJsonV1` shape and resolves relative
113
- * `theme.file` paths against `projectRoot`, so a partial slice will not do.
114
- *
115
- * Throws with a clear, local message when:
116
- * - the manifest is structurally invalid (so deploy fails before the wire);
117
- * - the theme cannot be loaded (e.g. an unregistered preset id);
118
- * - the resolved overlay fails `appThemeSchema` — the SAME validator the
119
- * backend enforces, so a deploy fails loud LOCALLY rather than with a
120
- * confusing 422.
121
- */
122
- export function readThemeFromGguiJson(projectRoot, gguiJson) {
76
+ export async function readThemeFromGguiJson(projectRoot, gguiJson) {
123
77
  // Cheap pre-check on the raw value: no `theme` field → nothing to push,
124
78
  // and we avoid forcing a full-manifest parse on theme-less deploys.
125
79
  const presence = z
@@ -140,9 +94,14 @@ export function readThemeFromGguiJson(projectRoot, gguiJson) {
140
94
  throw new Error(`ggui.json: ${result.issue.message}`);
141
95
  }
142
96
  const loaded = result.theme;
97
+ // The wire's theme is the PROJECTION (ggui#987 §3): both modes'
98
+ // derived sets, attested; the declared mode is the default the card
99
+ // paints when no host announces one; the preset id is the label.
100
+ const overlays = loaded.overlays;
143
101
  const theme = {
102
+ overlayHash: await canonicalOverlayHash({ overlays }),
103
+ overlays,
144
104
  mode: loaded.mode,
145
- cssVariables: parseCssVarBlock(loaded.cssVariables),
146
105
  ...(loaded.source === 'preset' ? { name: loaded.preset } : {}),
147
106
  };
148
107
  // Defense + clear local error: the resolved overlay must satisfy the same
@@ -152,7 +111,10 @@ export function readThemeFromGguiJson(projectRoot, gguiJson) {
152
111
  if (!parsed.success) {
153
112
  throw new Error(`ggui.json: resolved theme is invalid — ${parsed.error.issues[0]?.message ?? 'unexpected css value'}`);
154
113
  }
155
- return parsed.data;
114
+ const declaredFaceFamilies = [
115
+ ...new Set((loaded.document.typography?.faces ?? []).map((face) => face.family)),
116
+ ];
117
+ return { theme: parsed.data, declaredFaceFamilies };
156
118
  }
157
119
  // ─────────────────────────────────────────────────────────────────────────────
158
120
  // Loopback guard
@@ -238,7 +200,15 @@ export async function runConfigPushStep(appId, startDir = process.cwd()) {
238
200
  try {
239
201
  // `loadTheme` resolves relative `theme.file` paths against the directory
240
202
  // containing ggui.json — the same root convention `storage.*.path` uses.
241
- theme = readThemeFromGguiJson(dirname(gguiJsonPath), readResult.value);
203
+ const read = await readThemeFromGguiJson(dirname(gguiJsonPath), readResult.value);
204
+ theme = read?.theme;
205
+ if (read !== undefined && read.declaredFaceFamilies.length > 0) {
206
+ // ggui#990: the PATCH carries the projection only — no faces slot
207
+ // on the wire, nothing composes them on the hosted path — so the
208
+ // declared families fall back to their stacks there. One line,
209
+ // every deploy, until #990 lands.
210
+ process.stderr.write(`ggui deploy: theme declares font faces (${read.declaredFaceFamilies.join(', ')}) that are not delivered on the hosted path yet — the fallback stack applies (ggui-ai/ggui#990).\n`);
211
+ }
242
212
  }
243
213
  catch (err) {
244
214
  process.stderr.write(`ggui deploy: ${err instanceof Error ? err.message : String(err)}\n`);
@@ -28,10 +28,10 @@ export declare function planDeploySteps(input: DeployInput): DeployStep[];
28
28
  /**
29
29
  * Resolve the `GGUI_MCP_URL` value `ggui deploy` wires into `.env.local`.
30
30
  *
31
- * The per-app cloud pod serves the MCP transport at the BARE per-app endpoint
31
+ * The hosted per-app server serves the MCP transport at the BARE per-app endpoint
32
32
  * `<base>/apps/<appId>` (the `connectUrl` the backend advertises) — NOT a
33
33
  * `/mcp` sub-path. The `/mcp` suffix is the LOCAL `ggui serve` convention;
34
- * appending it here produced a URL the per-app pod 404s, so the agent (which
34
+ * appending it here produced a URL the per-app server 404s, so the agent (which
35
35
  * reads GGUI_MCP_URL verbatim) could never connect to a deployed app.
36
36
  *
37
37
  * - `connectUrl` present (a create-app ran this session) → use it VERBATIM —
@@ -52,10 +52,10 @@ export function planDeploySteps(input) {
52
52
  /**
53
53
  * Resolve the `GGUI_MCP_URL` value `ggui deploy` wires into `.env.local`.
54
54
  *
55
- * The per-app cloud pod serves the MCP transport at the BARE per-app endpoint
55
+ * The hosted per-app server serves the MCP transport at the BARE per-app endpoint
56
56
  * `<base>/apps/<appId>` (the `connectUrl` the backend advertises) — NOT a
57
57
  * `/mcp` sub-path. The `/mcp` suffix is the LOCAL `ggui serve` convention;
58
- * appending it here produced a URL the per-app pod 404s, so the agent (which
58
+ * appending it here produced a URL the per-app server 404s, so the agent (which
59
59
  * reads GGUI_MCP_URL verbatim) could never connect to a deployed app.
60
60
  *
61
61
  * - `connectUrl` present (a create-app ran this session) → use it VERBATIM —
@@ -330,7 +330,7 @@ export async function runDeployCommand(_args) {
330
330
  break;
331
331
  }
332
332
  // No prior value → resolve the deployment-correct BARE per-app URL
333
- // (see resolveDeployMcpUrl: the per-app pod serves MCP at the bare
333
+ // (see resolveDeployMcpUrl: the per-app server serves MCP at the bare
334
334
  // endpoint, never `/mcp`). The fallback to the production default only
335
335
  // fires when create-app didn't run this session — a first deploy always
336
336
  // has connectUrl, so it never reaches the fallback in practice.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Bounded drain for the CLI's success path.
3
+ *
4
+ * `main()` resolves with an exit code and the process is expected to end on
5
+ * its own: every command closes its handles, and a hard `process.exit()`
6
+ * while the local embedding model is loaded aborts under ONNX Runtime's
7
+ * parked worker threads (#855). The drain is the contract — this is what
8
+ * happens if a future change leaks a handle: instead of a process that
9
+ * never ends (strictly worse for an operator than the abort it replaced),
10
+ * the deadline names the live resources on stderr and hard-exits with the
11
+ * code. The timer is unref'd so the deadline itself never keeps the loop
12
+ * alive; a drained loop exits before it fires and nothing is written.
13
+ */
14
+ export declare const DRAIN_DEADLINE_MS = 5000;
15
+ export interface DrainDeadlineDeps {
16
+ readonly exitCode: number;
17
+ readonly deadlineMs: number;
18
+ readonly exit: (code: number) => void;
19
+ readonly stderr: (line: string) => void;
20
+ readonly setTimer: (fn: () => void, ms: number) => {
21
+ unref: () => void;
22
+ };
23
+ /** `process.getActiveResourcesInfo()` — what still keeps the loop alive. */
24
+ readonly liveResources: () => readonly string[];
25
+ }
26
+ export declare function armDrainDeadline(deps: DrainDeadlineDeps): void;
27
+ //# sourceMappingURL=drain-deadline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain-deadline.d.ts","sourceRoot":"","sources":["../src/drain-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACzE,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;CACjD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAS9D"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Bounded drain for the CLI's success path.
3
+ *
4
+ * `main()` resolves with an exit code and the process is expected to end on
5
+ * its own: every command closes its handles, and a hard `process.exit()`
6
+ * while the local embedding model is loaded aborts under ONNX Runtime's
7
+ * parked worker threads (#855). The drain is the contract — this is what
8
+ * happens if a future change leaks a handle: instead of a process that
9
+ * never ends (strictly worse for an operator than the abort it replaced),
10
+ * the deadline names the live resources on stderr and hard-exits with the
11
+ * code. The timer is unref'd so the deadline itself never keeps the loop
12
+ * alive; a drained loop exits before it fires and nothing is written.
13
+ */
14
+ export const DRAIN_DEADLINE_MS = 5_000;
15
+ export function armDrainDeadline(deps) {
16
+ const timer = deps.setTimer(() => {
17
+ const live = deps.liveResources();
18
+ deps.stderr(`ggui: shutdown did not drain within ${deps.deadlineMs}ms — live resources: ${live.length > 0 ? live.join(', ') : '(none reported)'}; exiting ${deps.exitCode}\n`);
19
+ deps.exit(deps.exitCode);
20
+ }, deps.deadlineMs);
21
+ timer.unref();
22
+ }
@@ -14,7 +14,7 @@
14
14
  * re-runs the resolver for the chosen provider with
15
15
  * `userScope: ctx.appId`, so per-end-user keys stored at the
16
16
  * caller's identity scope flip in at request time without a
17
- * server restart. Multi-tenant `ggui serve` lives off this
17
+ * server restart. Multi-user `ggui serve` lives off this
18
18
  * seam: each authenticated user manages their OWN provider
19
19
  * key via `/settings`, the resolver picks it up on the next
20
20
  * `ggui_render`.
@@ -166,7 +166,7 @@ export interface GenerationBinding {
166
166
  * `true` when the boot scan resolved a key at the operator's
167
167
  * `'global'` scope (env or credentials-file). `false` means
168
168
  * generation will fall back to either the per-end-user key
169
- * (multi-tenant) or the {@link GenerationDeps.onNoCredentials}
169
+ * (multi-user) or the {@link GenerationDeps.onNoCredentials}
170
170
  * card on first call.
171
171
  */
172
172
  readonly bootResolved: boolean;
@@ -119,7 +119,7 @@ export async function probeGenerationBinding(opts) {
119
119
  const resolveLlm = async (ctx) => {
120
120
  // Per-call BYOK with `userScope: ctx.appId` — env + global
121
121
  // (operator) checked first, then the caller's identity scope
122
- // (end-user). Multi-tenant + Anthropic-OAuth flows store
122
+ // (end-user). Multi-user + Anthropic-OAuth flows store
123
123
  // user-supplied bundles under `userId` here.
124
124
  const resolution = await opts.resolver.resolve(defaultProvider, {
125
125
  userScope: ctx.appId,
@@ -110,7 +110,7 @@ export interface BuildMcpServerBackendOptions {
110
110
  * {@link probeGenerationBinding} — env (`ANTHROPIC_API_KEY` etc.)
111
111
  * first, then `~/.ggui/credentials.json`. Programmatic hosts
112
112
  * embedding `buildMcpServerBackend` directly supply their own
113
- * `GenerationDeps` bundle (real BYOK in a hosted multi-tenant
113
+ * `GenerationDeps` bundle (real BYOK in a hosted multi-user
114
114
  * setting, a test double, etc.).
115
115
  */
116
116
  readonly generation?: GenerationDeps;
@@ -188,20 +188,20 @@ export interface BuildMcpServerBackendOptions {
188
188
  */
189
189
  readonly publicDemo?: boolean;
190
190
  /**
191
- * Multi-tenant posture. Switches the `/ggui/console/llm-keys` gate
191
+ * Multi-user posture. Switches the `/ggui/console/llm-keys` gate
192
192
  * from admin-token to auth-adapter — each authenticated end-user
193
193
  * manages their OWN provider keys (scope = `userId` for `kind:'user'`
194
194
  * identities, `appId` for `kind:'app'`). `kind:'builder'` identities
195
- * are rejected at the gate; multi-tenant is meaningless without a
195
+ * are rejected at the gate; multi-user is meaningless without a
196
196
  * real per-caller id.
197
197
  *
198
198
  * Strict-auth shape — every bearer must clear the `AuthAdapter`
199
199
  * (pairing-minted, OIDC, Cognito, or whatever the embedding host
200
200
  * binds). Mutually exclusive with `devAllowAll` and `publicDemo`;
201
201
  * the CLI rejects combinations at parse time. Surface =
202
- * `--multi-tenant` CLI flag.
202
+ * `--multi-user` CLI flag.
203
203
  */
204
- readonly multiTenant?: boolean;
204
+ readonly multiUser?: boolean;
205
205
  /**
206
206
  * Public base URL that replaces `http://<host>:<port>` when
207
207
  * composing `mcpApps.wsUrl` and `runtime.url`. Set this when the
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"AA0CA,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAE/B,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAO7B,OAAO,EAIL,KAAK,aAAa,EAKnB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAMvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAC/C;;;;;;;;;OASG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACnE;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MACrB;QACE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;KAClC,GACD,SAAS,CAAC;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAClE;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC;IAErC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAE9C;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAEhD;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE;QAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;YAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;YACpB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;YAC9B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;SAC/B,CAAC,CAAC;KACJ,CAAC;IAEF;;;;;;;;;OASG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAEnD;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAE/B;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAE9B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAE/B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;;OASG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;IACnF;;;;;;;OAOG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IAEzC;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;QACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IAEF;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAE/C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAEtD;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;CAChD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,SAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBhE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,YAAY,CA6kBtF"}
1
+ {"version":3,"file":"mcp-backend.d.ts","sourceRoot":"","sources":["../src/mcp-backend.ts"],"names":[],"mappings":"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"}
@@ -166,7 +166,14 @@ export function buildMcpServerBackend(opts) {
166
166
  // fallback warning rather than a silent first-lookup degradation.
167
167
  // eslint-disable-next-line no-console -- one-shot boot signal, mirrors banner output
168
168
  console.warn(`[ggui:embedding] local ${embedding.id} (cache: ${embeddingCacheDir})`);
169
- embedding.embed("warmup").catch((err) => {
169
+ const warmupStartedAt = Date.now();
170
+ embedding.embed("warmup").then(() => {
171
+ // One-shot boot signal: the model is loaded and semantic search is
172
+ // live from here. Harnesses wait on this line before exercising
173
+ // (or tearing down) the loaded-model path (#855).
174
+ // eslint-disable-next-line no-console -- one-shot boot signal, mirrors banner output
175
+ console.warn(`[ggui:embedding] warm — local model loaded in ${Date.now() - warmupStartedAt}ms`);
176
+ }, (err) => {
170
177
  const reason = err instanceof Error ? err.message : String(err);
171
178
  // No provider swap happens here — the local provider stays bound and
172
179
  // every embed() keeps failing, which search handlers absorb per-call
@@ -247,7 +254,7 @@ export function buildMcpServerBackend(opts) {
247
254
  }
248
255
  }
249
256
  // Per-app metadata store seeded from `ggui.json#theme.preset` (when
250
- // the manifest declares a preset variant). Single-tenant OSS: any
257
+ // the manifest declares a preset variant). Single-app: any
251
258
  // appId the handlers see picks up this default theme via
252
259
  // `InMemoryAppMetadataStore.get`'s defaults fall-through. File-mode
253
260
  // themes don't have a registry id to surface here — agents pick from
@@ -405,12 +412,12 @@ export function buildMcpServerBackend(opts) {
405
412
  : {}),
406
413
  ...(resolvedMcpInstructions !== undefined ? { mcpInstructions: resolvedMcpInstructions } : {}),
407
414
  ...(resolvedWithhold ? { withholdResultMeta: true } : {}),
408
- // `--multi-tenant` flips the `/ggui/console/llm-keys` gate from
415
+ // `--multi-user` flips the `/ggui/console/llm-keys` gate from
409
416
  // admin-token to auth-adapter. Default scope derivation in the
410
417
  // server picks up `userId` / `appId` from the resolved identity;
411
418
  // operators with composite scopes pass their own `providerKeyScope`
412
419
  // by composing `createGguiServer` directly.
413
- ...(opts.multiTenant ? { providerKeysGate: "auth-adapter" } : {}),
420
+ ...(opts.multiUser ? { providerKeysGate: "auth-adapter" } : {}),
414
421
  // Gate OAuth login routes on publicBaseUrl. Without it the
415
422
  // redirect_uri can't be composed; the admin transport still mounts
416
423
  // so operators can paste credentials in advance.
@@ -538,6 +545,14 @@ export function buildMcpServerBackend(opts) {
538
545
  // `ggui.json#theme`; absent = `createGguiServer` falls back to its
539
546
  // `lightTheme`-backed default internally.
540
547
  ...(opts.theme ? { theme: opts.theme } : {}),
548
+ // The theme document's declared font faces (ggui#987 §5): the
549
+ // server unions their origins into every served shell's CSP and
550
+ // inlines the rules, so the faces load without host cooperation.
551
+ ...(opts.theme !== undefined &&
552
+ opts.theme.document.typography?.faces !== undefined &&
553
+ opts.theme.document.typography.faces.length > 0
554
+ ? { fontFaces: opts.theme.document.typography.faces }
555
+ : {}),
541
556
  // Console theme picker writer — paired with the admin-gated
542
557
  // `/ggui/console/theme` POST. Absent = picker UI surfaces a
543
558
  // read-only banner + POST returns 501. CLI sets this when
@@ -20,7 +20,7 @@ export interface PushRecord {
20
20
  /** Tool-identity catalog hash at export — feeds the import gate's re-key check. */
21
21
  readonly toolIdentityCatalogHash?: string;
22
22
  };
23
- /** Compiled ESM JS, ready for the pod to evaluate directly (no pod-side esbuild). */
23
+ /** Compiled ESM JS, ready for the hosted server to evaluate directly (no server-side esbuild). */
24
24
  readonly compiledBytes: string;
25
25
  }
26
26
  export interface ParsedPushFlags {
@@ -1 +1 @@
1
- {"version":3,"file":"push-command.d.ts","sourceRoot":"","sources":["../src/push-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWtD,kFAAkF;AAClF,MAAM,WAAW,UAAU;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;QAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B;;;;;WAKG;QACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,8EAA8E;QAC9E,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAC3C,mFAAmF;QACnF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAC3C,CAAC;IACF,qFAAqF;IACrF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CA+BvE;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B9D;AAMD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAyC1F;AAMD,eAAO,MAAM,SAAS,onBAerB,CAAC;AAEF,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4E7E"}
1
+ {"version":3,"file":"push-command.d.ts","sourceRoot":"","sources":["../src/push-command.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWtD,kFAAkF;AAClF,MAAM,WAAW,UAAU;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;QAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAC9B;;;;;WAKG;QACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,8EAA8E;QAC9E,QAAQ,CAAC,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAC3C,mFAAmF;QACnF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAC3C,CAAC;IACF,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CA+BvE;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B9D;AAMD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAyC1F;AAMD,eAAO,MAAM,SAAS,onBAerB,CAAC;AAEF,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4E7E"}
@@ -120,7 +120,7 @@ export interface ParsedServeFlags {
120
120
  */
121
121
  publicDemo: boolean;
122
122
  /**
123
- * Multi-tenant posture. Switches the `/ggui/console/llm-keys` gate
123
+ * Multi-user posture. Switches the `/ggui/console/llm-keys` gate
124
124
  * from admin-token to auth-adapter — each authenticated end-user
125
125
  * manages their OWN provider keys (scope = `userId` for `kind:'user'`
126
126
  * identities, `appId` for `kind:'app'`). `kind:'builder'` identities
@@ -129,14 +129,14 @@ export interface ParsedServeFlags {
129
129
  * Strict-auth shape — every bearer must clear the `AuthAdapter`.
130
130
  * Mutually exclusive with {@link devAllowAll} and {@link publicDemo};
131
131
  * those modes collapse every identity to `kind:'builder'` which the
132
- * multi-tenant gate rejects, so the combination would produce a
133
- * server that's wired multi-tenant but can't authenticate anyone.
132
+ * multi-user gate rejects, so the combination would produce a
133
+ * server that's wired multi-user but can't authenticate anyone.
134
134
  *
135
135
  * Use case: a single `ggui serve` instance fronting many users (a
136
136
  * shared dev box, a small team server) where each user pastes their
137
137
  * own LLM key via /settings using their paired bearer.
138
138
  */
139
- multiTenant: boolean;
139
+ multiUser: boolean;
140
140
  /**
141
141
  * Server-level MCP instructions preset (the string injected into
142
142
  * the LLM's system prompt above the tool catalog). Operator-tunable
@@ -312,13 +312,13 @@ export interface ServeBannerInputs {
312
312
  */
313
313
  readonly publicDemo?: boolean;
314
314
  /**
315
- * When true, the auth section adds a "MULTI-TENANT" line below the
315
+ * When true, the auth section adds a "MULTI-USER" line below the
316
316
  * strict-auth blurb explaining that each user manages their own LLM
317
317
  * keys via /settings under their paired bearer. Set by
318
- * `--multi-tenant`. Mutually exclusive with `devAllowAll` and
318
+ * `--multi-user`. Mutually exclusive with `devAllowAll` and
319
319
  * `publicDemo` at parse time.
320
320
  */
321
- readonly multiTenant?: boolean;
321
+ readonly multiUser?: boolean;
322
322
  /**
323
323
  * Public base URL displayed under the local URLs when set via
324
324
  * `--public-base-url`. Reminds the operator which URL their tunnel
@@ -525,5 +525,5 @@ export interface RunServeOptions {
525
525
  * {@link describeServeBanner} / {@link describeAgentStatus}.
526
526
  */
527
527
  export declare function runServe(opts: RunServeOptions): Promise<number>;
528
- export declare const SERVE_HELP = "ggui serve \u2014 run the open self-hosted personal-mode app\n\nBoots the @ggui-ai/mcp-server binding with persistent defaults AND\n(by default) supervises your agent alongside it. The current MCP\nhandler surface is the blueprint-read family (search / list_featured\n/ render); more tools come online as @ggui-ai/mcp-server-handlers\ngrows.\n\nFirst-run bundle (all on by default):\n - Landing page at / \u2014 operator opens a browser and sees\n server identity + pair-code card.\n - Render viewer at /r/<code> \u2014 same-origin viewer for renders\n minted by ggui_render; same-origin\n HTTP-only cookie authenticates the\n live-channel /ws upgrade.\n - POST /pair + POST /admin/pair/init \u2014 pairing endpoint for MCP\n hosts and third-party clients.\n - Live-channel /ws \u2014 live render plane for MCP Apps\n iframes and the console.\n\nEmbedding hosts that want a different shape (no landing page, no\npairing, programmatic control) should compose createGguiServer()\ndirectly in code rather than invoking `ggui serve`.\n\nUsage:\n ggui serve [options]\n\nOptions:\n --port <n> Bind port (default: 6781, 0 = OS-assigned).\n --host <addr> Bind host (default: 127.0.0.1).\n --mcp-only Run just the MCP server; do not supervise an agent.\n --dev-allow-all Accept any non-empty bearer (incl. no bearer) as\n builder. Local-dev / tunnel smoke ONLY \u2014 never\n expose to the open internet.\n --public-demo Public-facing demo posture: same any-bearer auth\n as --dev-allow-all, plus per-IP rate limit on\n ggui_render (30 generations / 10 min) and a\n \"PUBLIC DEMO \u2014 operator pays\" banner. Mutually\n exclusive with --dev-allow-all.\n --multi-tenant Multi-tenant posture: switches the /settings LLM-\n keys gate from admin-token to auth-adapter so\n each authenticated end-user manages their OWN\n key plane (scope = userId/appId). kind:'builder'\n identities are rejected at the gate. Strict-auth\n only; mutually exclusive with --dev-allow-all\n and --public-demo. (Generation-time BYOK still\n resolves at the operator 'global' scope \u2014 a\n follow-up will thread per-user BYOK through\n ggui_render.)\n --public-base-url <u> Override the public base URL used to compose\n iframe-runtime + shortCode URLs. Set this to a\n tunnel URL (https://<random>.trycloudflare.com)\n when testing against a remote MCP host so URLs\n resolve from the host's perspective. Without\n this, URLs derive from --host:--port and only\n work from the same machine.\n --browser-origin <o> Allow pages at this origin past MCP-wire Origin\n validation (repeatable; env GGUI_BROWSER_ORIGINS,\n comma-separated). Pages on localhost need no\n entry. NOT authentication \u2014 /mcp still requires\n a bearer. Non-browser clients are unaffected.\n --oauth Mount OAuth 2.1 + PKCE + Dynamic Client\n Registration routes (.well-known/oauth-* +\n /oauth/{authorize,token,register}). Required\n for MCP custom-connector hosts (claude.ai,\n ChatGPT) whose Add Connector form has no field\n for a pre-shared bearer; OAuth lets them do\n a discovery \u2192 DCR \u2192 consent dance ending in\n the same paired bearer the strict-auth flow\n already mints. Pure-bearer clients (Claude\n Desktop with bearer in config) work without it.\n --admin-token <t> Pin the admin bearer that gates the console\n /keys plane (list / mint / revoke pairings +\n /admin-login route). Without this, the server\n mints a fresh ggui_admin_* token per boot and\n prints it on the banner. Pin a stable value\n when you want the bearer to survive restarts\n (e.g. paste once into a password manager).\n --ephemeral Disable cross-restart persistence. Without\n this flag, the HMAC secrets that sign MCP\n Apps bootstrap tokens and signed render URLs\n are read or minted under .ggui/persistent/\n (project-local if a ggui.json was resolved,\n else ~/.ggui/persistent/) so cached tokens\n survive a server restart. Pass this flag to\n keep restarts ephemeral (tests / CI / nuclear-\n revoke). Override the dir via GGUI_PERSISTENT_DIR.\n --seed-pool <dir> Load a read-only shared blueprint pool from a\n directory artifact (repeatable). Blueprints in it\n are reused by exact contract match, after your own.\n --help, -h Show this help.\n\nAgent runtime:\n The default boots your agent alongside MCP. The agent's entry file\n comes from ggui.json:\n\n { \"agent\": { \"entry\": \"./agent.ts\" } }\n\n Supported extensions: .js / .mjs / .cjs (node), .ts / .tsx / .mts\n (node --import=tsx). No ggui.json, or no agent.entry, falls back\n to MCP-only with a warning. Malformed ggui.json or a bad entry\n is a hard error.\n\n Agent crashes after startup are logged; the MCP server keeps\n running. No auto-restart \u2014 compose that with your own supervisor.\n\nPersistent storage (default-on; opt-out via --ephemeral):\n By default `ggui serve` writes a small bundle under .ggui/persistent/\n (project-local if a ggui.json was resolved, else ~/.ggui/persistent/)\n so cached MCP Apps tokens, signed render URLs, shortCodes, renders,\n vectors, and paired bearers survive a restart \u2014 claude.ai chat-history\n revisits keep working.\n\n Bundle layout:\n\n .ggui/persistent/\n \u251C\u2500\u2500 ws-token-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 render-signer-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 short-codes.sqlite (signed render-URL resolution)\n \u251C\u2500\u2500 sessions.sqlite (GguiSessionStore \u2014 renders + event history)\n \u251C\u2500\u2500 vectors.sqlite (RAG corpus)\n \u2514\u2500\u2500 keys.json (paired bearers)\n\n Override the dir with `GGUI_PERSISTENT_DIR`. Pass `--ephemeral` to\n skip the bundle entirely (tests, CI loops, nuclear-revoke).\n\n Explicit ggui.json#storage declarations always win \u2014 declare\n `{ \"renders\": { \"driver\": \"memory\" } }` to opt renders back to\n in-memory while keeping other surfaces persistent. Custom sqlite\n paths via ggui.json#storage are honored verbatim:\n\n {\n \"storage\": {\n \"renders\": { \"driver\": \"sqlite\", \"path\": \"./ggui-sessions.sqlite\" },\n \"vectors\": { \"driver\": \"sqlite\", \"path\": \"./ggui-vectors.sqlite\" },\n \"threads\": { \"driver\": \"sqlite\", \"path\": \"./ggui-threads.sqlite\" }\n }\n }\n\n Threads: the persistent-chat route family (/threads) is opt-in.\n Absent `storage.threads` leaves the routes unmounted \u2014 pairs\n with Portal's self-hosted origins only when declared. Driver\n semantics:\n - `driver: \"sqlite\"` \u2014 durable. Threads survive restart.\n /ggui/health reports `threads.durability: \"durable\"`;\n Portal hides its non-durable caveat.\n - `driver: \"memory\"` \u2014 routes mount but data is lost on\n restart. /ggui/health reports `threads.durability:\n \"ephemeral\"`; Portal shows its caveat.\n\n Paths resolve relative to the ggui.json directory. The sqlite\n driver needs `better-sqlite3` installed in your project.\n\nRecommended setups:\n\n Local-only smoke (no tunnel, no remote MCP host):\n ggui serve\n\n claude.ai custom connector (over a public tunnel):\n 1. Tunnel: cloudflared tunnel --url http://localhost:6781\n 2. ggui serve --oauth \\\n --public-base-url https://<tunnel>.trycloudflare.com\n 3. Note PAIR_CODE from boot. Visit the landing page (the public URL\n above), complete pairing, save the bearer.\n 4. claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector.\n URL: https://<tunnel>.trycloudflare.com/mcp\n Client ID / Secret: leave empty (server uses Dynamic Client\n Registration; no pre-shared secret needed).\n 5. Click Connect \u2192 claude.ai redirects you to a paste-key page.\n Paste the bearer from step 3. Done.\n\n Quick local-dev WITHOUT auth (NEVER over a public tunnel):\n ggui serve --dev-allow-all\n\nCurrent limits:\n - Strict-auth only: /mcp rejects any bearer that isn't pair-minted.\n The CLI pre-mints one pairing code on boot (printed as\n `PAIR_CODE <code>` and shown in the banner + on the landing\n page). Complete `POST /pair {code, deviceName}` to receive a\n bearer you can use with MCP. Embed @ggui-ai/mcp-server in your\n own entrypoint to inject a different AuthAdapter (OIDC, Cognito,\n etc.) for production.\n - Single-tenant: every request scopes to one `builder` app id.\n";
528
+ export declare const SERVE_HELP = "ggui serve \u2014 run the open self-hosted personal-mode app\n\nBoots the @ggui-ai/mcp-server binding with persistent defaults AND\n(by default) supervises your agent alongside it. The current MCP\nhandler surface is the blueprint-read family (search / list_featured\n/ render); more tools come online as @ggui-ai/mcp-server-handlers\ngrows.\n\nFirst-run bundle (all on by default):\n - Landing page at / \u2014 operator opens a browser and sees\n server identity + pair-code card.\n - Render viewer at /r/<code> \u2014 same-origin viewer for renders\n minted by ggui_render; same-origin\n HTTP-only cookie authenticates the\n live-channel /ws upgrade.\n - POST /pair + POST /admin/pair/init \u2014 pairing endpoint for MCP\n hosts and third-party clients.\n - Live-channel /ws \u2014 live render plane for MCP Apps\n iframes and the console.\n\nEmbedding hosts that want a different shape (no landing page, no\npairing, programmatic control) should compose createGguiServer()\ndirectly in code rather than invoking `ggui serve`.\n\nUsage:\n ggui serve [options]\n\nOptions:\n --port <n> Bind port (default: 6781, 0 = OS-assigned).\n --host <addr> Bind host (default: 127.0.0.1).\n --mcp-only Run just the MCP server; do not supervise an agent.\n --dev-allow-all Accept any non-empty bearer (incl. no bearer) as\n builder. Local-dev / tunnel smoke ONLY \u2014 never\n expose to the open internet.\n --public-demo Public-facing demo posture: same any-bearer auth\n as --dev-allow-all, plus per-IP rate limit on\n ggui_render (30 generations / 10 min) and a\n \"PUBLIC DEMO \u2014 operator pays\" banner. Mutually\n exclusive with --dev-allow-all.\n --multi-user Multi-user posture: switches the /settings LLM-\n keys gate from admin-token to auth-adapter so\n each authenticated end-user manages their OWN\n key plane (scope = userId/appId). kind:'builder'\n identities are rejected at the gate. Strict-auth\n only; mutually exclusive with --dev-allow-all\n and --public-demo. (Generation-time BYOK still\n resolves at the operator 'global' scope \u2014 a\n follow-up will thread per-user BYOK through\n ggui_render.)\n --public-base-url <u> Override the public base URL used to compose\n iframe-runtime + shortCode URLs. Set this to a\n tunnel URL (https://<random>.trycloudflare.com)\n when testing against a remote MCP host so URLs\n resolve from the host's perspective. Without\n this, URLs derive from --host:--port and only\n work from the same machine.\n --browser-origin <o> Allow pages at this origin past MCP-wire Origin\n validation (repeatable; env GGUI_BROWSER_ORIGINS,\n comma-separated). Pages on localhost need no\n entry. NOT authentication \u2014 /mcp still requires\n a bearer. Non-browser clients are unaffected.\n --oauth Mount OAuth 2.1 + PKCE + Dynamic Client\n Registration routes (.well-known/oauth-* +\n /oauth/{authorize,token,register}). Required\n for MCP custom-connector hosts (claude.ai,\n ChatGPT) whose Add Connector form has no field\n for a pre-shared bearer; OAuth lets them do\n a discovery \u2192 DCR \u2192 consent dance ending in\n the same paired bearer the strict-auth flow\n already mints. Pure-bearer clients (Claude\n Desktop with bearer in config) work without it.\n --admin-token <t> Pin the admin bearer that gates the console\n /keys plane (list / mint / revoke pairings +\n /admin-login route). Without this, the server\n mints a fresh ggui_admin_* token per boot and\n prints it on the banner. Pin a stable value\n when you want the bearer to survive restarts\n (e.g. paste once into a password manager).\n --ephemeral Disable cross-restart persistence. Without\n this flag, the HMAC secrets that sign MCP\n Apps bootstrap tokens and signed render URLs\n are read or minted under .ggui/persistent/\n (project-local if a ggui.json was resolved,\n else ~/.ggui/persistent/) so cached tokens\n survive a server restart. Pass this flag to\n keep restarts ephemeral (tests / CI / nuclear-\n revoke). Override the dir via GGUI_PERSISTENT_DIR.\n --seed-pool <dir> Load a read-only shared blueprint pool from a\n directory artifact (repeatable). Blueprints in it\n are reused by exact contract match, after your own.\n --help, -h Show this help.\n\nAgent runtime:\n The default boots your agent alongside MCP. The agent's entry file\n comes from ggui.json:\n\n { \"agent\": { \"entry\": \"./agent.ts\" } }\n\n Supported extensions: .js / .mjs / .cjs (node), .ts / .tsx / .mts\n (node --import=tsx). No ggui.json, or no agent.entry, falls back\n to MCP-only with a warning. Malformed ggui.json or a bad entry\n is a hard error.\n\n Agent crashes after startup are logged; the MCP server keeps\n running. No auto-restart \u2014 compose that with your own supervisor.\n\nPersistent storage (default-on; opt-out via --ephemeral):\n By default `ggui serve` writes a small bundle under .ggui/persistent/\n (project-local if a ggui.json was resolved, else ~/.ggui/persistent/)\n so cached MCP Apps tokens, signed render URLs, shortCodes, renders,\n vectors, and paired bearers survive a restart \u2014 claude.ai chat-history\n revisits keep working.\n\n Bundle layout:\n\n .ggui/persistent/\n \u251C\u2500\u2500 ws-token-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 render-signer-secret.hex (HMAC, 0600)\n \u251C\u2500\u2500 short-codes.sqlite (signed render-URL resolution)\n \u251C\u2500\u2500 sessions.sqlite (GguiSessionStore \u2014 renders + event history)\n \u251C\u2500\u2500 vectors.sqlite (RAG corpus)\n \u2514\u2500\u2500 keys.json (paired bearers)\n\n Override the dir with `GGUI_PERSISTENT_DIR`. Pass `--ephemeral` to\n skip the bundle entirely (tests, CI loops, nuclear-revoke).\n\n Explicit ggui.json#storage declarations always win \u2014 declare\n `{ \"renders\": { \"driver\": \"memory\" } }` to opt renders back to\n in-memory while keeping other surfaces persistent. Custom sqlite\n paths via ggui.json#storage are honored verbatim:\n\n {\n \"storage\": {\n \"renders\": { \"driver\": \"sqlite\", \"path\": \"./ggui-sessions.sqlite\" },\n \"vectors\": { \"driver\": \"sqlite\", \"path\": \"./ggui-vectors.sqlite\" },\n \"threads\": { \"driver\": \"sqlite\", \"path\": \"./ggui-threads.sqlite\" }\n }\n }\n\n Threads: the persistent-chat route family (/threads) is opt-in.\n Absent `storage.threads` leaves the routes unmounted \u2014 pairs\n with Portal's self-hosted origins only when declared. Driver\n semantics:\n - `driver: \"sqlite\"` \u2014 durable. Threads survive restart.\n /ggui/health reports `threads.durability: \"durable\"`;\n Portal hides its non-durable caveat.\n - `driver: \"memory\"` \u2014 routes mount but data is lost on\n restart. /ggui/health reports `threads.durability:\n \"ephemeral\"`; Portal shows its caveat.\n\n Paths resolve relative to the ggui.json directory. The sqlite\n driver needs `better-sqlite3` installed in your project.\n\nRecommended setups:\n\n Local-only smoke (no tunnel, no remote MCP host):\n ggui serve\n\n claude.ai custom connector (over a public tunnel):\n 1. Tunnel: cloudflared tunnel --url http://localhost:6781\n 2. ggui serve --oauth \\\n --public-base-url https://<tunnel>.trycloudflare.com\n 3. Note PAIR_CODE from boot. Visit the landing page (the public URL\n above), complete pairing, save the bearer.\n 4. claude.ai \u2192 Settings \u2192 Connectors \u2192 Add custom connector.\n URL: https://<tunnel>.trycloudflare.com/mcp\n Client ID / Secret: leave empty (server uses Dynamic Client\n Registration; no pre-shared secret needed).\n 5. Click Connect \u2192 claude.ai redirects you to a paste-key page.\n Paste the bearer from step 3. Done.\n\n Quick local-dev WITHOUT auth (NEVER over a public tunnel):\n ggui serve --dev-allow-all\n\nCurrent limits:\n - Strict-auth only: /mcp rejects any bearer that isn't pair-minted.\n The CLI pre-mints one pairing code on boot (printed as\n `PAIR_CODE <code>` and shown in the banner + on the landing\n page). Complete `POST /pair {code, deviceName}` to receive a\n bearer you can use with MCP. Embed @ggui-ai/mcp-server in your\n own entrypoint to inject a different AuthAdapter (OIDC, Cognito,\n etc.) for production.\n - Single-user: every request scopes to one `builder` app id.\n";
529
529
  //# sourceMappingURL=serve-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serve-command.d.ts","sourceRoot":"","sources":["../src/serve-command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve-command.d.ts","sourceRoot":"","sources":["../src/serve-command.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,7 @@ export function parseServeFlags(args) {
25
25
  devAllowAll: false,
26
26
  withholdResultMeta: false,
27
27
  publicDemo: false,
28
- multiTenant: false,
28
+ multiUser: false,
29
29
  oauth: false,
30
30
  seedPools: [],
31
31
  browserOrigins: [],
@@ -71,8 +71,8 @@ export function parseServeFlags(args) {
71
71
  out.publicDemo = true;
72
72
  continue;
73
73
  }
74
- if (arg === '--multi-tenant') {
75
- out.multiTenant = true;
74
+ if (arg === '--multi-user') {
75
+ out.multiUser = true;
76
76
  continue;
77
77
  }
78
78
  if (arg === '--oauth') {
@@ -179,13 +179,13 @@ export function parseServeFlags(args) {
179
179
  'public demo (rate-limited, "PUBLIC DEMO" copy). Pick one.',
180
180
  };
181
181
  }
182
- if (out.multiTenant && (out.devAllowAll || out.publicDemo)) {
182
+ if (out.multiUser && (out.devAllowAll || out.publicDemo)) {
183
183
  return {
184
184
  ...out,
185
- error: '--multi-tenant is incompatible with --dev-allow-all and --public-demo. ' +
186
- 'Multi-tenant requires real per-user identities (kind:"user" / kind:"app"); ' +
185
+ error: '--multi-user is incompatible with --dev-allow-all and --public-demo. ' +
186
+ 'Multi-user requires real per-user identities (kind:"user" / kind:"app"); ' +
187
187
  'the any-bearer modes collapse every caller to kind:"builder" which the ' +
188
- 'multi-tenant gate rejects. Drop the other flag and pair with real bearers.',
188
+ 'multi-user gate rejects. Drop the other flag and pair with real bearers.',
189
189
  };
190
190
  }
191
191
  return out;
@@ -256,8 +256,8 @@ export function describeServeBanner(input) {
256
256
  }
257
257
  else {
258
258
  lines.push(` auth: strict — only pair-minted bearer tokens authenticate /mcp.`, ` Pair via the code above (landing page or \`POST /pair\`).`, ` Override the adapter via GguiServer({ auth }) in code for`, ` custom deployments (OIDC, Cognito, etc.).`);
259
- if (input.multiTenant) {
260
- lines.push(``, ` multi-tenant: each authenticated user manages their OWN LLM`, ` keys at /settings — the gate uses the request's bearer`, ` (Authorization: Bearer their-paired-token), NOT the`, ` admin token. Per-user scope is auto-derived from the`, ` identity (userId / appId).`, ` Note: ggui_render generation still resolves BYOK at the`, ` operator scope (env + credentials-file 'global'). Per-`, ` user generation-time BYOK is a follow-up — for now,`, ` multi-tenant lets each user MANAGE their key plane`, ` without granting them the operator's admin token.`);
259
+ if (input.multiUser) {
260
+ lines.push(``, ` multi-user: each authenticated user manages their OWN LLM`, ` keys at /settings — the gate uses the request's bearer`, ` (Authorization: Bearer their-paired-token), NOT the`, ` admin token. Per-user scope is auto-derived from the`, ` identity (userId / appId).`, ` Note: ggui_render generation still resolves BYOK at the`, ` operator scope (env + credentials-file 'global'). Per-`, ` user generation-time BYOK is a follow-up — for now,`, ` multi-user lets each user MANAGE their key plane`, ` without granting them the operator's admin token.`);
261
261
  }
262
262
  if (input.oauth) {
263
263
  lines.push(``, ` oauth: enabled — MCP custom-connector hosts (claude.ai etc.)`, ` can discover via /.well-known/oauth-* and complete the`, ` auth dance against /oauth/{authorize,token,register}.`, ` Paste the paired bearer on the consent page.`);
@@ -381,7 +381,7 @@ export async function runServe(opts) {
381
381
  agent: opts.agentStatus,
382
382
  devAllowAll: opts.flags.devAllowAll,
383
383
  publicDemo: opts.flags.publicDemo,
384
- multiTenant: opts.flags.multiTenant,
384
+ multiUser: opts.flags.multiUser,
385
385
  oauth: opts.flags.oauth,
386
386
  ...(opts.flags.publicBaseUrl !== undefined
387
387
  ? { publicBaseUrl: opts.flags.publicBaseUrl }
@@ -490,7 +490,7 @@ Options:
490
490
  ggui_render (30 generations / 10 min) and a
491
491
  "PUBLIC DEMO — operator pays" banner. Mutually
492
492
  exclusive with --dev-allow-all.
493
- --multi-tenant Multi-tenant posture: switches the /settings LLM-
493
+ --multi-user Multi-user posture: switches the /settings LLM-
494
494
  keys gate from admin-token to auth-adapter so
495
495
  each authenticated end-user manages their OWN
496
496
  key plane (scope = userId/appId). kind:'builder'
@@ -633,5 +633,5 @@ Current limits:
633
633
  bearer you can use with MCP. Embed @ggui-ai/mcp-server in your
634
634
  own entrypoint to inject a different AuthAdapter (OIDC, Cognito,
635
635
  etc.) for production.
636
- - Single-tenant: every request scopes to one \`builder\` app id.
636
+ - Single-user: every request scopes to one \`builder\` app id.
637
637
  `;
@@ -1,4 +1,4 @@
1
- export declare const THEME_HELP = "ggui theme \u2014 validate + inspect operator-authored DTCG themes\n\nUsage:\n ggui theme <subcommand> [options]\n\nSubcommands:\n validate <path> Validate a JSON theme file against the ThemeDocumentV1 schema.\n\nRun `ggui theme <subcommand> --help` for subcommand-specific options.\n";
1
+ export declare const THEME_HELP = "ggui theme \u2014 validate + inspect operator-authored DTCG themes\n\nUsage:\n ggui theme <subcommand> [options]\n\nSubcommands:\n validate <path> Validate a JSON theme file against the ThemeDocumentV2 schema.\n\nRun `ggui theme <subcommand> --help` for subcommand-specific options.\n";
2
2
  export declare const THEME_VALIDATE_HELP = "ggui theme validate \u2014 validate a DTCG theme file\n\nUsage:\n ggui theme validate <path>\n\nArguments:\n <path> Path to a JSON theme document (e.g. `./theme.json`).\n\nExit codes:\n 0 theme is valid\n 1 theme file is unreadable, not JSON, or fails schema validation\n 2 bad CLI usage (missing path argument)\n";
3
3
  export declare function runThemeCommand(args: readonly string[]): Promise<number>;
4
4
  //# sourceMappingURL=theme-command.d.ts.map
@@ -27,7 +27,7 @@ Usage:
27
27
  ggui theme <subcommand> [options]
28
28
 
29
29
  Subcommands:
30
- validate <path> Validate a JSON theme file against the ThemeDocumentV1 schema.
30
+ validate <path> Validate a JSON theme file against the ThemeDocumentV2 schema.
31
31
 
32
32
  Run \`ggui theme <subcommand> --help\` for subcommand-specific options.
33
33
  `;
@@ -127,7 +127,7 @@ async function runValidate(args) {
127
127
  */
128
128
  function describeValidTheme(absPath, theme) {
129
129
  const lines = [];
130
- const mark = process.stdout.isTTY ? '✓' : '✓';
130
+ const mark = process.stdout.isTTY ? '\x1b[32m✓\x1b[0m' : '✓';
131
131
  const nameSuffix = theme.$name ? ` (${theme.$name})` : '';
132
132
  lines.push(`${mark} Theme valid${nameSuffix}`);
133
133
  lines.push(` file: ${absPath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/cli",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "The `ggui` CLI — run the ggui protocol locally: dev server, self-hosted MCP server, and gadget / blueprint authoring. End-to-end OSS and self-hosted workflows; no account required.",
5
5
  "keywords": [
6
6
  "ggui",
@@ -37,22 +37,22 @@
37
37
  "@noble/hashes": "^1.6.0",
38
38
  "esbuild": "^0.28.2",
39
39
  "zod": "^4.3.6",
40
- "@ggui-ai/agent-runtime": "0.14.0",
41
- "@ggui-ai/artifact-manifest": "0.14.0",
42
- "@ggui-ai/embedding-local": "0.14.0",
43
- "@ggui-ai/dev-stack": "0.14.0",
44
- "@ggui-ai/gadget-signing": "0.14.0",
45
- "@ggui-ai/mcp-server": "0.14.0",
46
- "@ggui-ai/mcp-server-handlers": "0.14.0",
47
- "@ggui-ai/project-config": "0.14.0",
48
- "@ggui-ai/preview-a2ui": "0.14.0",
49
- "@ggui-ai/mcp-server-core": "0.14.0",
50
- "@ggui-ai/registry-core": "0.14.0",
51
- "@ggui-ai/design": "0.14.0",
52
- "@ggui-ai/ui-gen": "0.14.0",
53
- "@ggui-ai/iframe-runtime": "0.14.0",
54
- "@ggui-ai/ui-registry": "0.14.0",
55
- "@ggui-ai/protocol": "0.14.0"
40
+ "@ggui-ai/agent-runtime": "0.16.0",
41
+ "@ggui-ai/artifact-manifest": "0.16.0",
42
+ "@ggui-ai/dev-stack": "0.16.0",
43
+ "@ggui-ai/design": "0.16.0",
44
+ "@ggui-ai/gadget-signing": "0.16.0",
45
+ "@ggui-ai/mcp-server": "0.16.0",
46
+ "@ggui-ai/mcp-server-handlers": "0.16.0",
47
+ "@ggui-ai/mcp-server-core": "0.16.0",
48
+ "@ggui-ai/preview-a2ui": "0.16.0",
49
+ "@ggui-ai/project-config": "0.16.0",
50
+ "@ggui-ai/iframe-runtime": "0.16.0",
51
+ "@ggui-ai/ui-gen": "0.16.0",
52
+ "@ggui-ai/embedding-local": "0.16.0",
53
+ "@ggui-ai/protocol": "0.16.0",
54
+ "@ggui-ai/registry-core": "0.16.0",
55
+ "@ggui-ai/ui-registry": "0.16.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/node": "^24.0.0",