@ggui-ai/cli 0.2.0-alpha.4 → 0.3.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +38 -7
  2. package/dist/api-client.d.ts +42 -1
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +30 -2
  5. package/dist/auth-keys.d.ts +1 -1
  6. package/dist/auth-keys.d.ts.map +1 -1
  7. package/dist/auth-keys.js +22 -7
  8. package/dist/auth-store.d.ts +1 -1
  9. package/dist/auth-store.d.ts.map +1 -1
  10. package/dist/cli.js +112 -30
  11. package/dist/config-push.d.ts +79 -0
  12. package/dist/config-push.d.ts.map +1 -0
  13. package/dist/config-push.js +301 -0
  14. package/dist/create-app-command.d.ts +17 -0
  15. package/dist/create-app-command.d.ts.map +1 -0
  16. package/dist/create-app-command.js +58 -0
  17. package/dist/deploy-command.d.ts +67 -0
  18. package/dist/deploy-command.d.ts.map +1 -0
  19. package/dist/deploy-command.js +386 -0
  20. package/dist/dev-command.d.ts +5 -1
  21. package/dist/dev-command.d.ts.map +1 -1
  22. package/dist/dev-command.js +31 -1
  23. package/dist/export-pool-command.d.ts +25 -0
  24. package/dist/export-pool-command.d.ts.map +1 -0
  25. package/dist/export-pool-command.js +116 -0
  26. package/dist/filesystem-seed-pool-source.d.ts +13 -0
  27. package/dist/filesystem-seed-pool-source.d.ts.map +1 -0
  28. package/dist/filesystem-seed-pool-source.js +22 -0
  29. package/dist/generation-probe.d.ts +53 -7
  30. package/dist/generation-probe.d.ts.map +1 -1
  31. package/dist/generation-probe.js +37 -1
  32. package/dist/internal/artifact-install.d.ts +4 -2
  33. package/dist/internal/artifact-install.d.ts.map +1 -1
  34. package/dist/internal/artifact-install.js +11 -10
  35. package/dist/internal/artifact-publish.d.ts +34 -31
  36. package/dist/internal/artifact-publish.d.ts.map +1 -1
  37. package/dist/internal/artifact-publish.js +133 -241
  38. package/dist/internal/artifact-search.d.ts +2 -28
  39. package/dist/internal/artifact-search.d.ts.map +1 -1
  40. package/dist/internal/artifact-search.js +5 -4
  41. package/dist/internal/auth-strategy.d.ts +21 -31
  42. package/dist/internal/auth-strategy.d.ts.map +1 -1
  43. package/dist/internal/auth-strategy.js +15 -23
  44. package/dist/internal/register-author-key.d.ts +7 -1
  45. package/dist/internal/register-author-key.d.ts.map +1 -1
  46. package/dist/internal/register-author-key.js +56 -19
  47. package/dist/mcp-backend.d.ts +25 -25
  48. package/dist/mcp-backend.d.ts.map +1 -1
  49. package/dist/mcp-backend.js +65 -61
  50. package/dist/paths.d.ts +3 -3
  51. package/dist/paths.js +3 -3
  52. package/dist/persistent-secrets.js +1 -1
  53. package/dist/persistent-stores.d.ts +2 -2
  54. package/dist/persistent-stores.d.ts.map +1 -1
  55. package/dist/persistent-stores.js +5 -5
  56. package/dist/pool-artifact.d.ts +19 -0
  57. package/dist/pool-artifact.d.ts.map +1 -0
  58. package/dist/pool-artifact.js +59 -0
  59. package/dist/provider-key-command.d.ts +51 -0
  60. package/dist/provider-key-command.d.ts.map +1 -0
  61. package/dist/provider-key-command.js +222 -0
  62. package/dist/push-command.d.ts +47 -0
  63. package/dist/push-command.d.ts.map +1 -0
  64. package/dist/push-command.js +230 -0
  65. package/dist/serve-command.d.ts +23 -19
  66. package/dist/serve-command.d.ts.map +1 -1
  67. package/dist/serve-command.js +13 -26
  68. package/dist/theme-command.js +0 -2
  69. package/dist/tunnel-provider.d.ts +8 -7
  70. package/dist/tunnel-provider.d.ts.map +1 -1
  71. package/package.json +25 -21
  72. package/dist/internal/auth-cache.d.ts +0 -40
  73. package/dist/internal/auth-cache.d.ts.map +0 -1
  74. package/dist/internal/auth-cache.js +0 -133
  75. package/dist/persistent-short-code-index.d.ts +0 -14
  76. package/dist/persistent-short-code-index.d.ts.map +0 -1
  77. package/dist/persistent-short-code-index.js +0 -39
  78. package/dist/spawn-cloudflared.d.ts +0 -61
  79. package/dist/spawn-cloudflared.d.ts.map +0 -1
  80. package/dist/spawn-cloudflared.js +0 -167
@@ -10,8 +10,8 @@
10
10
  * What gets wired by default — see OSS-split §2 (the five server
11
11
  * responsibilities):
12
12
  *
13
- * - `sessionChannel: true` — live-channel `/ws` endpoint. Required for
14
- * MCP Apps iframes AND the console session viewer.
13
+ * - `renderChannel: true` — live-channel `/ws` endpoint. Required for
14
+ * MCP Apps iframes AND the console render viewer.
15
15
  * - `pairing: true` — `POST /pair` + `POST /admin/pair/init` so
16
16
  * remote clients (Portal, third-party) can pair with this server.
17
17
  * The default `InMemoryAuthAdapter` registers pairing tokens
@@ -19,8 +19,8 @@
19
19
  * - `console: { sessionCookie: true }` — landing page at `/` +
20
20
  * `/s/<shortCode>` viewer + same-origin HTTP-only cookie flow.
21
21
  * - `shortCodeIndex: new InMemoryShortCodeIndex()` — required by
22
- * the `sessionCookie` flow so `POST /ggui/console/render-cookie`
23
- * can resolve a posted shortCode to the bound `{renderId, appId}`.
22
+ * the `sessionCookie` flow so `POST /ggui/console/session-cookie`
23
+ * can resolve a posted shortCode to the bound `{sessionId, appId}`.
24
24
  * In-memory is correct for the OSS first-run: render state lives
25
25
  * in memory unless the operator opts into sqlite, so a matching
26
26
  * index lifetime is what operators expect.
@@ -40,12 +40,11 @@ import { listThemes } from "@ggui-ai/design/themes";
40
40
  import { compileUiOnDemand } from "@ggui-ai/dev-stack";
41
41
  import { createLocalEmbeddingProvider } from "@ggui-ai/embedding-local";
42
42
  import { RUNTIME_BUNDLE_URL_PATH } from "@ggui-ai/iframe-runtime/server";
43
- import { composeWiredActionRouterFromMounts, createGguiServer, DEFAULT_BUILDER_APP_ID, FileSystemCodeStore, FixedWindowRateLimiter, InMemoryAuthAdapter, InMemoryQuotaStore, InMemoryShortCodeIndex, selectEmailSenderFromEnv, } from "@ggui-ai/mcp-server";
44
- import { InMemoryAppMetadataStore, InMemoryVectorStore } from "@ggui-ai/mcp-server-core/in-memory";
43
+ import { createGguiServer, FileSystemCodeStore, FixedWindowRateLimiter, InMemoryAuthAdapter, InMemoryQuotaStore, InMemoryShortCodeIndex, selectEmailSenderFromEnv, } from "@ggui-ai/mcp-server";
44
+ import { InMemoryAppMetadataStore, InMemoryBlueprintIndex, InMemoryVectorStore, } from "@ggui-ai/mcp-server-core/in-memory";
45
45
  import { PlaintextFileProviderKeyStore } from "@ggui-ai/mcp-server-core/plaintext";
46
- import { createInstalledBlueprintsProvider, } from "@ggui-ai/mcp-server-handlers/renders";
46
+ import { createInstalledBlueprintsProvider, createStderrCacheTraceSink, setCacheTraceSink, } from "@ggui-ai/mcp-server-handlers/renders";
47
47
  import { createDeterministicPreviewEmitter } from "@ggui-ai/preview-a2ui/emitters";
48
- import { randomUUID } from "node:crypto";
49
48
  import { existsSync } from "node:fs";
50
49
  import { createServer as createNetServer } from "node:net";
51
50
  import { join } from "node:path";
@@ -104,13 +103,6 @@ export function buildMcpServerBackend(opts) {
104
103
  // derive the live-channel URL.
105
104
  const baseUrl = opts.publicBaseUrl ?? `http://${opts.host}:${opts.port}`;
106
105
  const wsBaseUrl = baseUrl.replace(/^http(s?):\/\//, "ws$1://");
107
- // Router over the mount handler bundle. Computed once; absent
108
- // (`null`) when no mounts are declared so the server falls through
109
- // to agent-routed behavior.
110
- const wiredActionRouter = composeWiredActionRouterFromMounts(opts.mcpMounts, () => ({
111
- appId: DEFAULT_BUILDER_APP_ID,
112
- requestId: randomUUID(),
113
- }));
114
106
  // Strict-auth OSS default (`devAllowAll: false`). `createGguiServer`
115
107
  // would otherwise fall back to `InMemoryAuthAdapter({ devAllowAll:
116
108
  // true })`, which authenticates any non-empty bearer as builder — a
@@ -161,8 +153,23 @@ export function buildMcpServerBackend(opts) {
161
153
  // get `MockEmbeddingProvider` if they don't bind an embedder — that
162
154
  // keeps the 60MB `@huggingface/transformers` install surface off
163
155
  // the unit-test path.
156
+ const embeddingCacheDir = getEmbeddingCacheDir();
164
157
  const embedding = createLocalEmbeddingProvider({
165
- cacheDir: getEmbeddingCacheDir(),
158
+ cacheDir: embeddingCacheDir,
159
+ });
160
+ // One-line boot log of which embedding provider is live, so a captured
161
+ // serve log makes real-model-vs-mock unambiguous. The local provider
162
+ // loads `bge-small-en-v1.5` lazily; we then fire a best-effort warmup
163
+ // probe (one `embed`) so the model is downloaded/loaded before the
164
+ // first real lookup AND a load failure (e.g. missing
165
+ // `@huggingface/transformers` peer dep) is surfaced as a clear
166
+ // fallback warning rather than a silent first-lookup degradation.
167
+ // eslint-disable-next-line no-console -- one-shot boot signal, mirrors banner output
168
+ console.warn(`[ggui:embedding] local ${embedding.id} (cache: ${embeddingCacheDir})`);
169
+ embedding.embed("warmup").catch((err) => {
170
+ const reason = err instanceof Error ? err.message : String(err);
171
+ // eslint-disable-next-line no-console -- one-shot boot signal; local model unavailable
172
+ console.warn(`[ggui:embedding] MockEmbeddingProvider fallback — local model unavailable: ${reason}`);
166
173
  });
167
174
  // BYOK provider-key store — same `~/.ggui/credentials.json` that
168
175
  // `byok-resolver.ts` reads on every `resolve()`. Threading the store
@@ -240,7 +247,7 @@ export function buildMcpServerBackend(opts) {
240
247
  // themes don't have a registry id to surface here — agents pick from
241
248
  // `listThemes()` either way; the file-mode override only affects the
242
249
  // server's bound `theme` (rendered CSS variables), not the
243
- // session/handler-side `themeId` chain.
250
+ // render/handler-side `themeId` chain.
244
251
  const manifestThemePreset = opts.theme?.source === "preset" ? opts.theme.preset : undefined;
245
252
  const appMetadataStore = new InMemoryAppMetadataStore({
246
253
  ...(manifestThemePreset !== undefined ? { defaultThemeId: manifestThemePreset } : {}),
@@ -253,13 +260,13 @@ export function buildMcpServerBackend(opts) {
253
260
  ? { defaultGadgets: opts.gadgets }
254
261
  : {}),
255
262
  // `ggui.json#app.publicEnv` operator-stamped values for wrapper
256
- // hooks to read via `getPublicEnv()`. The push gate
263
+ // hooks to read via `getPublicEnv()`. The render gate
257
264
  // (`assertPublicEnvSatisfied`) verifies every declared wrapper's
258
265
  // `requires` keys are present in this map BEFORE the iframe boots.
259
266
  ...(opts.publicEnv !== undefined && Object.keys(opts.publicEnv).length > 0
260
267
  ? { defaultPublicEnv: opts.publicEnv }
261
268
  : {}),
262
- // `ggui.json#app.defaultDisplayMode`. Stamped per-push as
269
+ // `ggui.json#app.defaultDisplayMode`. Stamped per-render as
263
270
  // `_meta.ui.displayMode`; controls only host-side presentation.
264
271
  ...(opts.defaultDisplayMode !== undefined
265
272
  ? { defaultDisplayMode: opts.defaultDisplayMode }
@@ -274,6 +281,14 @@ export function buildMcpServerBackend(opts) {
274
281
  // here on the absent-storage path; threaded back as `vectors` to
275
282
  // `createGguiServer` so server + provider share state.
276
283
  const vectorStore = storage.vectors ?? new InMemoryVectorStore();
284
+ // Blueprint identity index — minted here so the install bridge's
285
+ // provider deps and the server share ONE instance (the server's
286
+ // ref-equality guard enforces this, same as `vectorStore`). The index
287
+ // is rebuildable from vector-store metadata and self-heals at the read
288
+ // site, so an in-memory index is safe even alongside a persistent
289
+ // `storage.vectors`; a persistent index can be threaded later via the
290
+ // same seam.
291
+ const blueprintIndex = new InMemoryBlueprintIndex();
277
292
  // Construct the marketplace-install bridge BEFORE `createGguiServer`
278
293
  // so we can fold it into `opts.generation.installedBlueprints` below.
279
294
  // Per design lock (project_slices_5_6_7_plan.md), the provider
@@ -319,14 +334,14 @@ export function buildMcpServerBackend(opts) {
319
334
  errors: result.errors.map((m) => m.text),
320
335
  };
321
336
  },
322
- deps: { embedding, vectorStore },
337
+ deps: { embedding, vectorStore, index: blueprintIndex },
323
338
  onIssue: (issue) => {
324
339
  process.stderr.write(`[ggui serve] installed-blueprint ${issue.id}: ${issue.kind}: ${issue.message}\n`);
325
340
  },
326
341
  };
327
342
  installedBlueprintsProvider = createInstalledBlueprintsProvider(providerOptions);
328
343
  }
329
- // Fold the bridge into the generation deps so the push handler +
344
+ // Fold the bridge into the generation deps so the render handler +
330
345
  // handshake negotiator both consume it via `MatchBlueprintDeps.
331
346
  // installedBlueprints`. Two-step pattern keeps the createGguiServer
332
347
  // call below unchanged when no provider is wired.
@@ -338,6 +353,17 @@ export function buildMcpServerBackend(opts) {
338
353
  }
339
354
  : opts.generation
340
355
  : undefined;
356
+ // Env-gated blueprint-cache trace sink. When `GGUI_CACHE_TRACE_STDERR`
357
+ // is truthy, every `matchBlueprint` decision (and the reason it landed
358
+ // there) is written as a single JSON line to stderr prefixed
359
+ // `[ggui:cache-trace] ` — a diagnostic for seeing WHY a semantic match
360
+ // missed (RAG retrieved nothing / cosine below floor / judge declined)
361
+ // in a captured server log. Registered BEFORE `createGguiServer` so the
362
+ // first lookup already routes through it. Absent env = no sink (the
363
+ // matcher emits nothing and spends no CPU on the top-K probe).
364
+ if (process.env["GGUI_CACHE_TRACE_STDERR"]) {
365
+ setCacheTraceSink(createStderrCacheTraceSink());
366
+ }
341
367
  const server = createGguiServer({
342
368
  auth,
343
369
  embedding,
@@ -355,7 +381,7 @@ export function buildMcpServerBackend(opts) {
355
381
  // Cross-restart HMAC secrets. Present iff the caller declared a
356
382
  // persistent dir. Both threaded so the next restart can verify
357
383
  // tokens minted by the previous run instead of regenerating per
358
- // process. Render-signer.secret rides on the `renderSigning`
384
+ // process. render-signer.secret rides on the `renderSigning`
359
385
  // discriminated union (the `false` shape disables the layer
360
386
  // entirely; we always want it on here, so build the object form).
361
387
  ...(persistedWsTokenSecret !== undefined ? { wsTokenSecret: persistedWsTokenSecret } : {}),
@@ -381,6 +407,10 @@ export function buildMcpServerBackend(opts) {
381
407
  // declaration, leave the server to mint its own internal
382
408
  // InMemoryVectorStore that the bridge couldn't see.
383
409
  vectors: vectorStore,
410
+ // Share the one minted index instance with the server so the install
411
+ // bridge + matcher resolve identity through the SAME index (the
412
+ // server's ref-equality guard enforces this).
413
+ index: blueprintIndex,
384
414
  // `threads:` is an opt-in on createGguiServer (no zero-config
385
415
  // fallback — threads without an explicit store wouldn't mount
386
416
  // routes at all). We only pass it when the manifest declares a
@@ -397,7 +427,7 @@ export function buildMcpServerBackend(opts) {
397
427
  },
398
428
  }
399
429
  : {}),
400
- sessionChannel: true,
430
+ renderChannel: true,
401
431
  pairing: opts.keysFile ? { persistencePath: opts.keysFile } : true,
402
432
  // Email magic-link login. OSS first-run default mirrors
403
433
  // `provisionalPreview` — wire a sender so magic links can be
@@ -472,7 +502,7 @@ export function buildMcpServerBackend(opts) {
472
502
  // `GGUI_CODE_CACHE_DIR`). Survives `ggui serve` restart so
473
503
  // claude.ai's iframe cache still resolves URLs minted before the
474
504
  // restart. Operators can `rm -rf` the cache at any time —
475
- // immutable URLs guarantee a fresh push repopulates as needed.
505
+ // immutable URLs guarantee a fresh render repopulates as needed.
476
506
  codeStore: new FileSystemCodeStore({ root: getCodeCacheDir() }),
477
507
  // Manifest-declared blueprints. Only present when `ggui serve`
478
508
  // found UIs in `blueprints.include`; absent = the server's
@@ -499,7 +529,7 @@ export function buildMcpServerBackend(opts) {
499
529
  ...(opts.themeWriter ? { themeWriter: opts.themeWriter } : {}),
500
530
  // Live-theme wiring — `themeProvider` reads from a shared cell
501
531
  // that `onThemeConfigChange` writes to on every console save,
502
- // so the picker's "Save to ggui.json" reaches the next push's
532
+ // so the picker's "Save to ggui.json" reaches the next render's
503
533
  // bootstrap envelope without a restart. Both threaded through
504
534
  // when present; absent = boot-baked behaviour (legacy).
505
535
  ...(opts.themeProvider ? { themeProvider: opts.themeProvider } : {}),
@@ -508,8 +538,8 @@ export function buildMcpServerBackend(opts) {
508
538
  // is the live-channel URL published on `_meta["ai.ggui/render"]`
509
539
  // so iframes opened by an MCP Apps host (Claude Desktop, Claude
510
540
  // Code, claude.ai) can subscribe back to this same process.
511
- // First-party hosts (Studio, Portal, console) consume
512
- // `_meta.ui.resourceUri` instead and never hit the URL directly.
541
+ // First-party hosted UIs consume `_meta.ui.resourceUri`
542
+ // instead and never hit the URL directly.
513
543
  mcpApps: {
514
544
  wsUrl: `${wsBaseUrl}/ws`,
515
545
  },
@@ -543,31 +573,27 @@ export function buildMcpServerBackend(opts) {
543
573
  },
544
574
  // Generation wiring. Threaded through to
545
575
  // `defaultHandlers.render.generation` → `createGguiRenderHandler`.
546
- // Absent = placeholder mode: render mints renderIds + shortCodes
576
+ // Absent = placeholder mode: render mints sessionIds + shortCodes
547
577
  // + preview but does NOT produce componentCode. The CLI's
548
578
  // `runServeCommand` probes BYOK (env → credentials file) at
549
579
  // boot and only supplies this opt on a hit. See
550
580
  // `./generation-probe.ts`.
551
581
  ...(generationWithInstalled ? { generation: generationWithInstalled } : {}),
582
+ // Seed pools from `--seed-pool` artifacts. Threaded into the
583
+ // handshake negotiator so cross-deployment blueprints are
584
+ // accessible without being in the local cache.
585
+ ...(opts.seedPools && opts.seedPools.length > 0 ? { seedPools: opts.seedPools } : {}),
552
586
  // Mount aggregation. External SharedHandler bundles appended onto
553
587
  // `/mcp` alongside ggui-native tools. No CLI config loader yet —
554
588
  // programmatic hosts + integration tests compose mounts through
555
589
  // this opt directly.
556
590
  ...(opts.mcpMounts && opts.mcpMounts.length > 0 ? { mcpMounts: opts.mcpMounts } : {}),
557
- // wiredActionRouter composed from the same mount handlers. With
558
- // mounts present, every mounted tool becomes wire-dispatchable
559
- // from a generated UI's `actionSpec[name].dispatch.tool` (when
560
- // `dispatch.kind === 'tool'`) — the "zero agent code" path.
561
- // Without mounts, no router is wired and inbound actions stay on
562
- // the agent-routed path.
563
- ...(wiredActionRouter ? { wiredActionRouter } : {}),
564
591
  // Schema-compat mode. Defaults to `'reject'` inside
565
592
  // `createGguiServer`; the env override is the operator's escape
566
593
  // hatch for scenarios that intentionally ship a blueprint with a
567
- // schema mismatch (e.g. contract-probe fixtures that test runtime
568
- // TOOL_THREW / SCHEMA_VIOLATION error paths — `'reject'` would
569
- // otherwise block the stack item at push/try-live time before
570
- // runtime gets to emit the envelope under test).
594
+ // schema mismatch (e.g. contract-probe fixtures that exercise
595
+ // mismatch handling downstream — `'reject'` would otherwise block
596
+ // the render at render/try-live time before the scenario starts).
571
597
  //
572
598
  // Accepted values match `SchemaCompatMode`: `'reject' | 'warn' |
573
599
  // 'off'`. Unrecognized values fall through to the server's
@@ -580,28 +606,6 @@ export function buildMcpServerBackend(opts) {
580
606
  schemaCompatCheck: process.env["GGUI_SCHEMA_COMPAT_MODE"],
581
607
  }
582
608
  : {}),
583
- // Wired-tool per-call timeout override (default 30 s in
584
- // `session-channel.ts::DEFAULT_WIRED_TOOL_TIMEOUT_MS`). Mirrors the
585
- // `GGUI_SCHEMA_COMPAT_MODE` escape-hatch shape: env-only; absent or
586
- // unparseable falls through to the server's default. Test harnesses
587
- // exercising TOOL_TIMEOUT envelopes lower this to keep per-test
588
- // budgets small (the conformance fixture
589
- // `wired-action-tool-timeout.json` deliberately wires a hanging
590
- // tool; the default 30 s wait would dominate a Lane-1 spec budget).
591
- //
592
- // Parse uses `Number()` + finite + positive-integer checks; a typo
593
- // (`"abc"`, `"-100"`, `"NaN"`) falls through silently to default.
594
- // Operators that need a different production timeout SHOULD compose
595
- // `createGguiServer` directly rather than rely on env coupling.
596
- ...(() => {
597
- const raw = process.env["GGUI_WIRED_TIMEOUT_MS"];
598
- if (raw === undefined || raw === "")
599
- return {};
600
- const parsed = Number(raw);
601
- if (!Number.isFinite(parsed) || parsed <= 0)
602
- return {};
603
- return { wiredActionTimeoutMs: Math.floor(parsed) };
604
- })(),
605
609
  });
606
610
  return {
607
611
  listen: async (port, host) => {
package/dist/paths.d.ts CHANGED
@@ -51,7 +51,7 @@ export declare function getEmbeddingCacheDir(): string;
51
51
  * safe to return even when nothing has been written yet. Operators
52
52
  * who want to reset the cache can `rm -rf` this directory at any
53
53
  * time — every entry is content-addressable + immutable, so a
54
- * re-fetch repopulates from upstream code on next push.
54
+ * re-fetch repopulates from upstream code on next render.
55
55
  */
56
56
  export declare function getCodeCacheDir(): string;
57
57
  /**
@@ -59,14 +59,14 @@ export declare function getCodeCacheDir(): string;
59
59
  * short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
60
60
  * keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
61
61
  * revisits can rehydrate the iframe instead of seeing four cascading
62
- * failures (HMAC + shortCode + session + pairing).
62
+ * failures (HMAC + shortCode + render + pairing).
63
63
  *
64
64
  * Resolution order:
65
65
  * 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
66
66
  * shared mount).
67
67
  * 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
68
68
  * ggui.json. Per-project isolation; different projects get
69
- * separate sessions even on the same machine.
69
+ * separate renders even on the same machine.
70
70
  * 3. `~/.ggui/persistent` — fallback when there's no manifest
71
71
  * (`ggui serve` from a bare directory).
72
72
  *
package/dist/paths.js CHANGED
@@ -81,7 +81,7 @@ export function getEmbeddingCacheDir() {
81
81
  * safe to return even when nothing has been written yet. Operators
82
82
  * who want to reset the cache can `rm -rf` this directory at any
83
83
  * time — every entry is content-addressable + immutable, so a
84
- * re-fetch repopulates from upstream code on next push.
84
+ * re-fetch repopulates from upstream code on next render.
85
85
  */
86
86
  export function getCodeCacheDir() {
87
87
  const override = process.env['GGUI_CODE_CACHE_DIR'];
@@ -94,14 +94,14 @@ export function getCodeCacheDir() {
94
94
  * short-codes.sqlite, bootstrap-secret.hex, render-signer-secret.hex,
95
95
  * keys.json …). Survives `ggui serve` restarts so claude.ai chat-history
96
96
  * revisits can rehydrate the iframe instead of seeing four cascading
97
- * failures (HMAC + shortCode + session + pairing).
97
+ * failures (HMAC + shortCode + render + pairing).
98
98
  *
99
99
  * Resolution order:
100
100
  * 1. `GGUI_PERSISTENT_DIR` env — operator override (tests, clean-room,
101
101
  * shared mount).
102
102
  * 2. `<projectRoot>/.ggui/persistent` — when the CLI resolved a
103
103
  * ggui.json. Per-project isolation; different projects get
104
- * separate sessions even on the same machine.
104
+ * separate renders even on the same machine.
105
105
  * 3. `~/.ggui/persistent` — fallback when there's no manifest
106
106
  * (`ggui serve` from a bare directory).
107
107
  *
@@ -20,7 +20,7 @@
20
20
  * Security posture: these secrets are a render-token signing key, NOT
21
21
  * a long-lived credential like an OAuth client secret. Losing them
22
22
  * means an attacker can mint MCP Apps bootstrap tokens for arbitrary
23
- * `(renderId, appId)` pairs — same blast radius as before, just
23
+ * `(sessionId, appId)` pairs — same blast radius as before, just
24
24
  * surviving restart. Mode 0600 on the file matches `~/.ggui/credentials
25
25
  * .json` (BYOK keys) which has the same threat model.
26
26
  *
@@ -1,5 +1,5 @@
1
- import type { RenderStore, VectorStore } from '@ggui-ai/mcp-server-core';
2
- export declare function createPersistentRenderStore(persistentDir: string): Promise<RenderStore>;
1
+ import type { GguiSessionStore, VectorStore } from '@ggui-ai/mcp-server-core';
2
+ export declare function createPersistentGguiSessionStore(persistentDir: string): Promise<GguiSessionStore>;
3
3
  export declare function createPersistentVectorStore(persistentDir: string): Promise<VectorStore>;
4
4
  /**
5
5
  * Compute the auto-default `keysFile` path. Returns `undefined` when
@@ -1 +1 @@
1
- {"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
1
+ {"version":3,"file":"persistent-stores.d.ts","sourceRoot":"","sources":["../src/persistent-stores.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACZ,MAAM,0BAA0B,CAAC;AAElC,wBAAsB,gCAAgC,CACpD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED,wBAAsB,2BAA2B,CAC/C,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Default-on persistent stores under `<persistentDir>/`. Used when the
3
3
  * operator hasn't declared `storage.renders` / `storage.vectors` in
4
- * `ggui.json` AND hasn't passed `--ephemeral`. Closes the RenderStore
4
+ * `ggui.json` AND hasn't passed `--ephemeral`. Closes the GguiSessionStore
5
5
  * leg of the rehydrate problem: without this, a restart drops every
6
6
  * render row even though the HMAC secret + ShortCodeIndex now persist.
7
7
  *
@@ -16,11 +16,11 @@
16
16
  */
17
17
  import { mkdirSync } from 'node:fs';
18
18
  import { join } from 'node:path';
19
- export async function createPersistentRenderStore(persistentDir) {
19
+ export async function createPersistentGguiSessionStore(persistentDir) {
20
20
  mkdirSync(persistentDir, { recursive: true });
21
- const { SqliteRenderStore } = await import('@ggui-ai/mcp-server-core/sqlite');
22
- return new SqliteRenderStore({
23
- filename: join(persistentDir, 'renders.sqlite'),
21
+ const { SqliteGguiSessionStore } = await import('@ggui-ai/mcp-server-core/sqlite');
22
+ return new SqliteGguiSessionStore({
23
+ filename: join(persistentDir, 'sessions.sqlite'),
24
24
  });
25
25
  }
26
26
  export async function createPersistentVectorStore(persistentDir) {
@@ -0,0 +1,19 @@
1
+ import type { PortableBlueprint } from '@ggui-ai/protocol';
2
+ /** Write records to a directory: manifest.json + codes/<hash>.tsx. */
3
+ export declare function writePoolArtifact(dir: string, records: readonly PortableBlueprint[]): Promise<void>;
4
+ export interface ReadPoolResult {
5
+ /**
6
+ * Raw, UNVALIDATED records (manifest entry + reassembled
7
+ * componentCode). The artifact bytes are an untrusted input; the
8
+ * trust boundary is `fromPortableBlueprint` at the loader
9
+ * (`buildSeedPool`), which validates each record and skips
10
+ * rejections with a log line. This reader only owns the directory
11
+ * IO; the layout/format is the shared codec's.
12
+ */
13
+ readonly records: readonly unknown[];
14
+ /** Per-record problems (malformed entry, bad ref, missing/unreadable code body); never throws on these. */
15
+ readonly issues: readonly string[];
16
+ }
17
+ /** Read + reassemble records. Throws on a missing/invalid manifest or unsupported schemaVersion (v1 included — re-export, no migration shim). */
18
+ export declare function readPoolArtifact(dir: string): Promise<ReadPoolResult>;
19
+ //# sourceMappingURL=pool-artifact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool-artifact.d.ts","sourceRoot":"","sources":["../src/pool-artifact.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK3D,sEAAsE;AACtE,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,SAAS,iBAAiB,EAAE,GACpC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,iJAAiJ;AACjJ,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B3E"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Filesystem IO for the seed-pool wrapper artifact. The byte FORMAT
3
+ * (schema version, layout names, codeRef shape, rejection/issue
4
+ * messages) is owned by `@ggui-ai/artifact-manifest`'s pool-artifact
5
+ * codec — this module owns only the directory IO: writing/reading the
6
+ * manifest + code bodies and splicing `componentCode` back in.
7
+ */
8
+ import { mkdir, readFile, writeFile, rm } from 'node:fs/promises';
9
+ import { createHash } from 'node:crypto';
10
+ import { join } from 'node:path';
11
+ import { POOL_ARTIFACT_CODES_DIR, POOL_ARTIFACT_MANIFEST_FILENAME, buildPoolArtifactManifest, parsePoolArtifactManifest, poolArtifactCodeRef, serializePoolArtifactManifest, } from '@ggui-ai/artifact-manifest';
12
+ const codeHash = (code) => createHash('sha256').update(code, 'utf-8').digest('hex');
13
+ /** Write records to a directory: manifest.json + codes/<hash>.tsx. */
14
+ export async function writePoolArtifact(dir, records) {
15
+ // Clear ONLY the files this codec owns, never the whole dir — the caller's
16
+ // path (e.g. `--out .`) is passed through verbatim, so a recursive wipe of
17
+ // `dir` would delete unrelated files. Removing `codes/` + the manifest still
18
+ // guarantees no stale code bodies survive a rewrite.
19
+ await mkdir(dir, { recursive: true });
20
+ await rm(join(dir, POOL_ARTIFACT_CODES_DIR), { recursive: true, force: true });
21
+ await rm(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), { force: true });
22
+ await mkdir(join(dir, POOL_ARTIFACT_CODES_DIR), { recursive: true });
23
+ const entries = [];
24
+ for (const r of records) {
25
+ const codeRef = poolArtifactCodeRef(codeHash(r.componentCode));
26
+ await writeFile(join(dir, POOL_ARTIFACT_CODES_DIR, codeRef), r.componentCode, 'utf-8');
27
+ const { componentCode: _omit, ...rest } = r;
28
+ entries.push({ record: rest, codeRef });
29
+ }
30
+ await writeFile(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), serializePoolArtifactManifest(buildPoolArtifactManifest(entries)), 'utf-8');
31
+ }
32
+ /** Read + reassemble records. Throws on a missing/invalid manifest or unsupported schemaVersion (v1 included — re-export, no migration shim). */
33
+ export async function readPoolArtifact(dir) {
34
+ let raw;
35
+ try {
36
+ raw = await readFile(join(dir, POOL_ARTIFACT_MANIFEST_FILENAME), 'utf-8');
37
+ }
38
+ catch (cause) {
39
+ throw new Error(`seed pool: cannot read ${join(dir, POOL_ARTIFACT_MANIFEST_FILENAME)}: ${cause instanceof Error ? cause.message : String(cause)}`);
40
+ }
41
+ const parsed = parsePoolArtifactManifest(raw);
42
+ if (!parsed.ok) {
43
+ throw new Error(`seed pool: ${parsed.reason}`);
44
+ }
45
+ const records = [];
46
+ const issues = parsed.issues.map((issue) => `seed pool: ${issue}`);
47
+ for (const entry of parsed.manifest.blueprints) {
48
+ let componentCode;
49
+ try {
50
+ componentCode = await readFile(join(dir, POOL_ARTIFACT_CODES_DIR, entry.codeRef), 'utf-8');
51
+ }
52
+ catch {
53
+ issues.push(`seed pool: missing code body ${entry.codeRef}; skipping blueprint`);
54
+ continue;
55
+ }
56
+ records.push({ ...entry.record, componentCode });
57
+ }
58
+ return { records, issues };
59
+ }
@@ -0,0 +1,51 @@
1
+ /** Providers that accept a user-supplied plaintext API key. */
2
+ export declare const BYOK_PROVIDERS: readonly ["anthropic", "openai", "google", "openrouter"];
3
+ /** Provider name subset that supports BYOK. */
4
+ export type ByokProvider = (typeof BYOK_PROVIDERS)[number];
5
+ /**
6
+ * Resolve which LLM provider to push a key for.
7
+ *
8
+ * Priority:
9
+ * 1. `flagProvider` (explicit `--provider` flag) — validated against BYOK set.
10
+ * Bedrock → descriptive error. Unknown provider → error.
11
+ * 2. `ggui.json#generation.model` — parsed via `parseAnyLlmRoute` from
12
+ * `@ggui-ai/protocol`. Bedrock → error. Unparseable / absent → error.
13
+ *
14
+ * **Does NOT hand-roll a provider parser.** All model-string parsing goes
15
+ * through `parseAnyLlmRoute` (covers both canonical `provider:model` and
16
+ * LiteLLM `provider/model` forms).
17
+ */
18
+ export declare function inferProvider(gguiJson: unknown, flagProvider: string | undefined): ByokProvider | {
19
+ error: string;
20
+ };
21
+ /**
22
+ * Return the primary env-var name for a BYOK provider.
23
+ * For google the primary is `GOOGLE_API_KEY` (with `GEMINI_API_KEY` as
24
+ * fallback checked in `readKeyFromEnv`). Order matches `byok-resolver.ts`
25
+ * so that the local-dev key and the pushed key always agree.
26
+ */
27
+ export declare function envVarForProvider(provider: ByokProvider): string;
28
+ /**
29
+ * Read the API key for a BYOK provider from the given env.
30
+ *
31
+ * - `anthropic` → `ANTHROPIC_API_KEY`
32
+ * - `openai` → `OPENAI_API_KEY`
33
+ * - `google` → `GOOGLE_API_KEY` (primary), then `GEMINI_API_KEY` (fallback)
34
+ * - `openrouter` → `OPENROUTER_API_KEY`
35
+ *
36
+ * Order matches `byok-resolver.ts` so that the key the cloud receives is
37
+ * always the same one local generation uses.
38
+ *
39
+ * Returns `undefined` when no non-empty value is found.
40
+ */
41
+ export declare function readKeyFromEnv(provider: ByokProvider, env?: Readonly<Record<string, string | undefined>>): string | undefined;
42
+ export declare const PROVIDER_KEY_HELP = "ggui provider-key \u2014 manage cloud provider API keys for a ggui app\n\nUsage:\n ggui provider-key set --app <appId> [--provider <name>]\n\nSubcommands:\n set Push a provider API key from the appropriate env var to the cloud app.\n The provider is derived from ggui.json#generation.model unless\n --provider is given explicitly.\n\n Supported providers: anthropic, openai, google, openrouter.\n (bedrock is IAM-only \u2014 no plaintext key to push.)\n\n Reads the key from env:\n ANTHROPIC_API_KEY (anthropic)\n OPENAI_API_KEY (openai)\n GOOGLE_API_KEY (google, primary); GEMINI_API_KEY (fallback)\n OPENROUTER_API_KEY (openrouter)\n\nOptions:\n --app <appId> Target app ID (required).\n --provider <name> Override provider (default: derived from ggui.json).\n --help, -h Show this help.\n";
43
+ /**
44
+ * Entry point for `ggui provider-key <subcommand> [options]`.
45
+ *
46
+ * @param args - argv slice after `provider-key` (e.g. `['set', '--app', 'app123']`)
47
+ * @param startDir - optional cwd override (primarily for tests)
48
+ * @returns exit code (0 = success, 1 = error)
49
+ */
50
+ export declare function runProviderKeyCommand(args: readonly string[], startDir?: string): Promise<number>;
51
+ //# sourceMappingURL=provider-key-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-key-command.d.ts","sourceRoot":"","sources":["../src/provider-key-command.ts"],"names":[],"mappings":"AAqBA,+DAA+D;AAC/D,eAAO,MAAM,cAAc,0DAA2D,CAAC;AAEvF,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAU3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EACjB,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CA8ClC;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,SAAS,CAWpB;AAMD,eAAO,MAAM,iBAAiB,o4BAuB7B,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA+EjB"}