@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
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ggui-ai/cli
2
2
 
3
- > Open `ggui` CLI — the local dev + serve + pair + bench binary for the `ggui` protocol. OSS and self-hosted workflows end-to-end. **No account required.**
3
+ > Open `ggui` CLI — the local dev + serve + authoring binary for the `ggui` protocol. OSS and self-hosted workflows end-to-end. **No account required.**
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@ggui-ai/cli.svg)](https://www.npmjs.com/package/@ggui-ai/cli)
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE)
@@ -32,7 +32,7 @@ To declare a project (blueprints, primitives, theme, agent entry), drop a `ggui.
32
32
 
33
33
  ### `ggui serve`
34
34
 
35
- Run the OSS self-hosted personal-mode server: MCP at `/mcp`, session viewer at `/s/<shortCode>`, pairing endpoints, and a live-channel WebSocket. Supervises the agent declared in `ggui.json#agent.entry`.
35
+ Run the OSS self-hosted personal-mode server: MCP at `/mcp`, render viewer at `/s/<shortCode>`, pairing endpoints, and a live-channel WebSocket. Supervises the agent declared in `ggui.json#agent.entry`.
36
36
 
37
37
  ```bash
38
38
  ggui serve # default: http://127.0.0.1:6781
@@ -45,12 +45,41 @@ ggui serve --mcp-only # skip agent supervision
45
45
 
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
- - ✅ Component-code generation is wired on the OSS path via `createUiGenerator()` from `@ggui-ai/ui-gen` — the same harness the hosted runtime uses. `ggui_render` returns `codeReady: false` only when no BYOK credentials resolve (no `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / etc.); supply a key to get full generation locally.
49
- - 🔒 Default auth is dev-mode (any bearer → `builder`). Swap in a real `AuthAdapter` via `createGguiServer({ auth })` before exposing beyond `127.0.0.1`.
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` still lands a real render — a connect-your-key onboarding card with `codeReady: true` that walks the operator through supplying a key. `codeReady: false` is reserved for failures (generation errors, malformed credentials at boot).
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 })`.
50
50
 
51
51
  ### `ggui dev`
52
52
 
53
- Start the local UI registry + compile-on-demand dev hub, optionally supervising a local agent runtime. See `ggui dev --help` for the full flag list (agent adapter, tunnel provider, browser auto-open).
53
+ Start the local UI registry + compile-on-demand dev hub, optionally supervising a local agent runtime. Run `ggui --help` for the flag list (agent adapter, tunnel provider, browser auto-open).
54
+
55
+ #### Tunnel-provider plugins
56
+
57
+ `ggui dev` exposes the local stack through a `TunnelProvider` seam. Real providers (cloudflared bindings, ngrok, hosted tunnels) plug in without changing the CLI:
58
+
59
+ 1. Set `GGUI_TUNNEL_PROVIDER=<module specifier>` (a package name or path resolvable from your project).
60
+ 2. The module exports a `createTunnelProvider()` factory (preferred) or a default export returning a `TunnelProvider`.
61
+ 3. Type the module against the published contract: `import type { TunnelProvider, TunnelProviderModule } from '@ggui-ai/cli/tunnel-provider'`.
62
+
63
+ When the variable is unset, or the module fails shape validation, the CLI falls back to the null provider (`status: 'unavailable'` with a reason) instead of crashing the dev loop.
64
+
65
+ ### Everything else
66
+
67
+ | Command | What it does |
68
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
69
+ | `ggui login` | Sign into ggui.ai (device flow); tokens stored in `~/.ggui/auth.json`. |
70
+ | `ggui logout` | Discard the local ggui.ai session. |
71
+ | `ggui whoami` | Print the authenticated user. |
72
+ | `ggui keys` | Manage `ggui_user_*` connector keys (list / create / revoke). |
73
+ | `ggui create` | Create cloud resources tied to this project (`create app` writes `appId` into `ggui.json`). |
74
+ | `ggui gadget` | Author gadgets for the ggui marketplace (`gadget create <scope/name>` scaffolds a repo). |
75
+ | `ggui blueprint` | Author UI blueprints for the ggui marketplace (`blueprint create <scope/name>` scaffolds a repo). |
76
+ | `ggui theme` | Validate + inspect operator-authored DTCG themes (`theme validate <path>`). |
77
+ | `ggui export-pool` | Export this deployment's reusable blueprints as a shareable pool directory; load elsewhere via `ggui serve --seed-pool <dir>`. |
78
+ | `ggui push` | Compile + bulk-push blueprints to a ggui.ai cloud app (requires `ggui login`). |
79
+ | `ggui deploy` | Provision + wire a ggui.ai cloud app for this project (idempotent). |
80
+ | `ggui provider-key` | Manage provider API keys for a cloud ggui app (`provider-key set --app <appId>`). |
81
+
82
+ Run `ggui --help` for per-command flags.
54
83
 
55
84
  ## Configuration
56
85
 
@@ -67,7 +96,7 @@ Start the local UI registry + compile-on-demand dev hub, optionally supervising
67
96
 
68
97
  Set `protocol` to the value of `PROTOCOL_VERSION` exported by `@ggui-ai/protocol` in the version you install (or omit the field — the CLI will fall back to the installed protocol version).
69
98
 
70
- Optional blocks include `storage` (sessions / vectors / threads via `memory` or `sqlite`), `primitives`, `theme`, `adapters`, and `blueprints`. See [`@ggui-ai/project-config`](../project-config) for the full schema.
99
+ Optional blocks include `storage` (renders / vectors / threads via `memory` or `sqlite`), `primitives`, `theme`, `blueprints`, `generation`, and `mcpMounts`. See [`@ggui-ai/project-config`](../project-config) for the full schema.
71
100
 
72
101
  ## MCP config for your agent runtime
73
102
 
@@ -78,12 +107,14 @@ Point any MCP-compatible agent at the local endpoint `ggui serve` exposes:
78
107
  "mcpServers": {
79
108
  "ggui": {
80
109
  "url": "http://127.0.0.1:6781/mcp",
81
- "headers": { "Authorization": "Bearer dev" }
110
+ "headers": { "Authorization": "Bearer <paired-bearer>" }
82
111
  }
83
112
  }
84
113
  }
85
114
  ```
86
115
 
116
+ Mint the bearer by pairing (landing page or `POST /pair`). Under `ggui serve --dev-allow-all`, any non-empty bearer (e.g. `dev`) authenticates.
117
+
87
118
  ## Links
88
119
 
89
120
  - [Repo](https://github.com/ggui-ai/ggui) — source, examples, issue tracker
@@ -1,3 +1,5 @@
1
+ import type { PushRecord } from './push-command.js';
2
+ import type { GadgetDescriptor, AppTheme } from '@ggui-ai/protocol';
1
3
  export interface DeviceCodeResponse {
2
4
  device_code: string;
3
5
  user_code: string;
@@ -48,12 +50,51 @@ export declare class ApiError extends Error {
48
50
  }
49
51
  export declare function postAuthDevice(endpoint: string, clientName: string): Promise<DeviceCodeResponse>;
50
52
  export declare function postAuthPoll(endpoint: string, deviceCode: string): Promise<TokenResponse>;
51
- export declare function postAuthRefresh(endpoint: string, refreshToken: string): Promise<TokenResponse>;
53
+ export declare function postAuthRefresh(endpoint: string, refreshToken: string, fetchImpl?: typeof fetch): Promise<TokenResponse>;
52
54
  export declare function getMe(): Promise<MeResponse>;
53
55
  export declare function listKeys(): Promise<KeysListResponse>;
54
56
  export declare function createKey(args: {
55
57
  name?: string;
56
58
  expiresAt?: string;
57
59
  }): Promise<KeysCreateResponse>;
60
+ export interface AppsCreateResponse {
61
+ appId: string;
62
+ /**
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
65
+ * bare path; appending `/mcp` (a local `ggui serve` convention) 404s. Carries
66
+ * the deployment-correct host (prod `mcp.ggui.ai`, sandbox `<env>.mcp.sandbox…`),
67
+ * so prefer it over any reconstructed/hardcoded URL. See `resolveDeployMcpUrl`.
68
+ */
69
+ connectUrl: string;
70
+ isDefault: boolean;
71
+ createdAt: string;
72
+ }
73
+ export declare function createApp(args: {
74
+ displayName?: string;
75
+ }): Promise<AppsCreateResponse>;
58
76
  export declare function revokeKey(id: string): Promise<void>;
77
+ export interface PushResponse {
78
+ pushed: number;
79
+ appId: string;
80
+ }
81
+ export declare function pushAppBlueprints(appId: string, blueprints: PushRecord[]): Promise<PushResponse>;
82
+ export interface SetProviderKeyResponse {
83
+ provider: string;
84
+ lastFour: string;
85
+ appId: string;
86
+ }
87
+ export declare function setAppProviderKey(appId: string, provider: string, plaintextKey: string, label?: string): Promise<SetProviderKeyResponse>;
88
+ export interface PatchAppConfigResponse {
89
+ updated: string[];
90
+ }
91
+ export declare function patchAppConfig(appId: string, patch: {
92
+ gadgets?: GadgetDescriptor[];
93
+ publicEnv?: Record<string, string>;
94
+ generation?: {
95
+ model: string;
96
+ keySource: 'own' | 'managed';
97
+ };
98
+ theme?: AppTheme;
99
+ }): Promise<PatchAppConfigResponse>;
59
100
  //# sourceMappingURL=api-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAM1D;AAqBD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,kBAAkB,CAAC,CAQ7B;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAQxB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC,CAQxB;AA0ED,wBAAsB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAKjD;AAED,wBAAsB,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAK1D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAa9B;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASzD"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -59,8 +59,8 @@ export async function postAuthPoll(endpoint, deviceCode) {
59
59
  throw await parseError(res);
60
60
  return (await res.json());
61
61
  }
62
- export async function postAuthRefresh(endpoint, refreshToken) {
63
- const res = await fetch(`${endpoint}/v1/auth/refresh`, {
62
+ export async function postAuthRefresh(endpoint, refreshToken, fetchImpl = globalThis.fetch.bind(globalThis)) {
63
+ const res = await fetchImpl(`${endpoint}/v1/auth/refresh`, {
64
64
  method: 'POST',
65
65
  headers: { 'Content-Type': 'application/json' },
66
66
  body: JSON.stringify({ refresh_token: refreshToken }),
@@ -137,9 +137,37 @@ export async function createKey(args) {
137
137
  },
138
138
  }), (r) => r.json());
139
139
  }
140
+ export async function createApp(args) {
141
+ return withAuthRetry((s) => authedFetch(s, {
142
+ method: 'POST',
143
+ path: '/v1/apps',
144
+ body: { displayName: args.displayName },
145
+ }), (r) => r.json());
146
+ }
140
147
  export async function revokeKey(id) {
141
148
  await withAuthRetry((s) => authedFetch(s, {
142
149
  method: 'DELETE',
143
150
  path: `/v1/keys/${encodeURIComponent(id)}`,
144
151
  }), async () => undefined);
145
152
  }
153
+ export async function pushAppBlueprints(appId, blueprints) {
154
+ return withAuthRetry((s) => authedFetch(s, {
155
+ method: 'POST',
156
+ path: `/v1/apps/${encodeURIComponent(appId)}/blueprints`,
157
+ body: { blueprints },
158
+ }), (res) => res.json());
159
+ }
160
+ export async function setAppProviderKey(appId, provider, plaintextKey, label) {
161
+ return withAuthRetry((s) => authedFetch(s, {
162
+ method: 'POST',
163
+ path: `/v1/apps/${encodeURIComponent(appId)}/provider-keys`,
164
+ body: { provider, plaintextKey, ...(label ? { label } : {}) },
165
+ }), (r) => r.json());
166
+ }
167
+ export async function patchAppConfig(appId, patch) {
168
+ return withAuthRetry((s) => authedFetch(s, {
169
+ method: 'PATCH',
170
+ path: `/v1/apps/${encodeURIComponent(appId)}`,
171
+ body: patch,
172
+ }), (r) => r.json());
173
+ }
@@ -1,4 +1,4 @@
1
- export declare const KEYS_HELP = "ggui keys \u2014 manage CLI keys\n\nUsage:\n ggui keys list [--keys-file <path>]\n ggui keys create [--name <label>] [--expires-at <iso8601>] [--keys-file <path>]\n ggui keys revoke <id> [--keys-file <path>]\n ggui keys register --scope <@scope> [--registry <url>]\n\nlist Print the caller's keys (id, prefix, name, status, dates).\ncreate Mint a new connector key. The full secret is printed ONCE \u2014 copy it.\nrevoke Soft-revoke a connector key by id (the row stays for audit).\nregister Register a per-scope Ed25519 PUBLISHER key with the marketplace\n registry so future `ggui gadget publish` / `ggui blueprint publish`\n signed by the matching private key validate. The keypair is\n auto-generated on first publish under\n ~/.ggui/keys/<scope>/{private,public}.key \u2014 this verb ships\n the public half to the registry's POST /author-keys.\n\nPass --keys-file <path> to operate on the LOCAL JSON file instead of\nthe cloud (api.ggui.ai). The file format is the one written by\n`ggui serve --keys-file` \u2014 the two commands share the same store, so\nyou can mint a bearer here and have `ggui serve` accept it on next\nboot. Use this for self-host / personal-mode workflows. --keys-file\napplies to list/create/revoke only; `register` always talks to the\nmarketplace registry.\n";
1
+ export declare const KEYS_HELP = "ggui keys \u2014 manage CLI keys\n\nUsage:\n ggui keys list [--keys-file <path>]\n ggui keys create [--name <label>] [--expires-at <iso8601>] [--keys-file <path>]\n ggui keys revoke <id> [--keys-file <path>]\n ggui keys register --scope <@scope> [--registry <url>] [--auth=bearer [--token <token>]]\n\nlist Print the caller's keys (id, prefix, name, status, dates).\ncreate Mint a new connector key. The full secret is printed ONCE \u2014 copy it.\nrevoke Soft-revoke a connector key by id (the row stays for audit).\nregister Register a per-scope Ed25519 PUBLISHER key with the marketplace\n registry so future `ggui gadget publish` / `ggui blueprint publish`\n signed by the matching private key validate. The keypair is\n auto-generated on first publish under\n ~/.ggui/keys/<scope>/{private,public}.key \u2014 this verb ships\n the public half to the registry's POST /author-keys.\n\nregister-only Auth:\n --auth=bearer Use a bearer token (for self-hosted or local\n registries). Pair with --token <token> or set\n GGUI_REGISTRY_TOKEN in the environment.\n --token <token> Bearer token (paired with --auth=bearer).\n (default) Your `ggui login` session \u2014 the CLI reads\n ~/.ggui/auth.json and refreshes the token\n automatically when it has expired. Run\n `ggui login` first.\n\n\nPass --keys-file <path> to operate on the LOCAL JSON file instead of\nthe cloud (api.ggui.ai). The file format is the one written by\n`ggui serve --keys-file` \u2014 the two commands share the same store, so\nyou can mint a bearer here and have `ggui serve` accept it on next\nboot. Use this for self-host / personal-mode workflows. --keys-file\napplies to list/create/revoke only; `register` always talks to the\nmarketplace registry.\n";
2
2
  export declare function runKeysCommand(args: readonly string[]): Promise<number>;
3
3
  export interface RegisterFlags {
4
4
  readonly scope?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-keys.d.ts","sourceRoot":"","sources":["../src/auth-keys.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,SAAS,43CAyBrB,CAAC;AAEF,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB7E;AA6PD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,CA2CzE"}
1
+ {"version":3,"file":"auth-keys.d.ts","sourceRoot":"","sources":["../src/auth-keys.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,SAAS,w7DA2BrB,CAAC;AAEF,wBAAsB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB7E;AA6PD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,CA2CzE"}
package/dist/auth-keys.js CHANGED
@@ -13,6 +13,7 @@
13
13
  import process from 'node:process';
14
14
  import { ApiError, createKey, listKeys, revokeKey } from './api-client.js';
15
15
  import { formatLocalKeyTable, loadLocalKeys, mintLocalKey, revokeLocalKey, saveLocalKeys, } from './local-keys.js';
16
+ import { AUTH_HELP_FRAGMENT, parseAuthFlags } from './internal/auth-strategy.js';
16
17
  import { runRegisterAuthorKey } from './internal/register-author-key.js';
17
18
  export const KEYS_HELP = `ggui keys — manage CLI keys
18
19
 
@@ -20,7 +21,7 @@ Usage:
20
21
  ggui keys list [--keys-file <path>]
21
22
  ggui keys create [--name <label>] [--expires-at <iso8601>] [--keys-file <path>]
22
23
  ggui keys revoke <id> [--keys-file <path>]
23
- ggui keys register --scope <@scope> [--registry <url>]
24
+ ggui keys register --scope <@scope> [--registry <url>] [--auth=bearer [--token <token>]]
24
25
 
25
26
  list Print the caller's keys (id, prefix, name, status, dates).
26
27
  create Mint a new connector key. The full secret is printed ONCE — copy it.
@@ -32,6 +33,8 @@ register Register a per-scope Ed25519 PUBLISHER key with the marketplace
32
33
  ~/.ggui/keys/<scope>/{private,public}.key — this verb ships
33
34
  the public half to the registry's POST /author-keys.
34
35
 
36
+ register-only ${AUTH_HELP_FRAGMENT}
37
+
35
38
  Pass --keys-file <path> to operate on the LOCAL JSON file instead of
36
39
  the cloud (api.ggui.ai). The file format is the one written by
37
40
  \`ggui serve --keys-file\` — the two commands share the same store, so
@@ -329,7 +332,15 @@ export function parseRegisterFlags(args) {
329
332
  };
330
333
  }
331
334
  async function runKeysRegister(args) {
332
- const flags = parseRegisterFlags(args);
335
+ // Peel the shared `--auth=bearer` / `--token` flags first (same
336
+ // router the publish verbs use), then parse the register-specific
337
+ // flags from the residue.
338
+ const authParsed = parseAuthFlags(args);
339
+ if ('error' in authParsed) {
340
+ process.stderr.write(`ggui keys register: ${authParsed.error}\n`);
341
+ return 2;
342
+ }
343
+ const flags = parseRegisterFlags(authParsed.rest);
333
344
  if (flags.help) {
334
345
  process.stdout.write(KEYS_HELP);
335
346
  return 0;
@@ -346,18 +357,22 @@ async function runKeysRegister(args) {
346
357
  process.stderr.write(`ggui keys register: --scope must start with \`@\` (got "${flags.scope}")\n`);
347
358
  return 2;
348
359
  }
360
+ const authFlags = authParsed.flags;
349
361
  const outcome = await runRegisterAuthorKey({
350
362
  scope: flags.scope,
351
363
  ...(flags.registry !== undefined ? { registry: flags.registry } : {}),
364
+ ...(authFlags.auth !== undefined || authFlags.token !== undefined
365
+ ? { auth: authFlags }
366
+ : {}),
352
367
  }, {
353
368
  cwd: process.cwd(),
354
369
  env: process.env,
355
370
  fetch,
356
- // Unix-epoch SECONDS (matches the convention publish.ts uses for
357
- // `expiresAt` in the registry-token cache — see auth-cache.ts:31).
358
- // `Date.now()` returns milliseconds; passing it raw caused
359
- // `isTokenFresh` to always return false against a real Cognito
360
- // exp claim (also in seconds), forcing a re-auth on every run.
371
+ // Unix-epoch SECONDS — the login-session expiry fields
372
+ // (`accessExpiresAt` / `refreshExpiresAt` in auth-store.ts) are
373
+ // in seconds. `Date.now()` returns milliseconds; passing it raw
374
+ // would make every freshness check fail and force a refresh (or
375
+ // a spurious "session expired") on every run.
361
376
  now: () => Math.floor(Date.now() / 1000),
362
377
  });
363
378
  if (outcome.ok) {
@@ -7,7 +7,7 @@ export interface AuthSessionDocument {
7
7
  readonly version: 1;
8
8
  /** API endpoint these tokens are bound to (origin only, no path). */
9
9
  readonly endpoint: string;
10
- /** Cognito sub of the authenticated user. */
10
+ /** Server-side id of the authenticated user the tokens are bound to. */
11
11
  readonly userId: string;
12
12
  /** Stable session id grouping access + refresh on the server. */
13
13
  readonly sessionId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-store.d.ts","sourceRoot":"","sources":["../src/auth-store.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;CAClD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAUlD;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,mBAAmB,GAAG,IAAI,CAiB/D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CAQrD;AAmBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CA2B9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAKxD"}
1
+ {"version":3,"file":"auth-store.d.ts","sourceRoot":"","sources":["../src/auth-store.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;CAClD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAUlD;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,mBAAmB,GAAG,IAAI,CAiB/D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CAQrD;AAmBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CA2B9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAKxD"}
package/dist/cli.js CHANGED
@@ -12,18 +12,20 @@ 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 { buildAgentRuntime, describeTunnelSession, launchBrowser, openTunnel, parseDevFlags, resolveAgentCommand, resolveHubUrl, shouldAutoOpen, } from './dev-command.js';
15
+ import { buildAgentRuntime, describeTunnelSession, DEV_HELP, launchBrowser, openTunnel, parseDevFlags, resolveAgentCommand, resolveHubUrl, shouldAutoOpen, } from './dev-command.js';
16
16
  import { createNullTunnelProvider, discoverTunnelProvider, } from './tunnel-provider.js';
17
- import { ManifestBlueprintProvider, resolveStorageFromConfig, buildNoCredentialsRender, } from '@ggui-ai/mcp-server';
17
+ import { ManifestBlueprintProvider, resolveStorageFromConfig, buildNoCredentialsGguiSession, } from '@ggui-ai/mcp-server';
18
18
  import { InMemoryBlueprintProvider } from '@ggui-ai/mcp-server-core/in-memory';
19
19
  import { createByokResolver } from './byok-resolver.js';
20
20
  import { isInstalledBlueprintPath } from './internal/artifact-install.js';
21
- import { DEFAULT_ROUTE_BY_PROVIDER, describeGenerationBinding, probeGenerationBinding, } from './generation-probe.js';
21
+ import { DEFAULT_ROUTE_BY_PROVIDER, GENERATION_MODEL_ENV, describeGenerationBinding, probeGenerationBinding, resolveConfiguredRoute, } from './generation-probe.js';
22
22
  import { buildMcpServerBackend, pickFreePort } from './mcp-backend.js';
23
+ import { FileSystemSeedPoolSource } from './filesystem-seed-pool-source.js';
24
+ import { buildSeedPool } from '@ggui-ai/mcp-server-handlers';
23
25
  import { createThemeWriter } from './theme-writer.js';
24
26
  import { createThemeFileUploader } from './theme-file-uploader.js';
25
27
  import { getPersistentDir } from './paths.js';
26
- import { createPersistentRenderStore, createPersistentVectorStore, } from './persistent-stores.js';
28
+ import { createPersistentGguiSessionStore, createPersistentVectorStore, } from './persistent-stores.js';
27
29
  import { parseServeFlags, runServe, SERVE_HELP, } from './serve-command.js';
28
30
  import { runLoginCommand } from './auth-login.js';
29
31
  import { runLogoutCommand } from './auth-logout.js';
@@ -32,6 +34,11 @@ import { runKeysCommand } from './auth-keys.js';
32
34
  import { runGadgetCommand } from './gadget-command.js';
33
35
  import { runBlueprintCommand } from './blueprint-command.js';
34
36
  import { runThemeCommand } from './theme-command.js';
37
+ import { runExportPoolCommand } from './export-pool-command.js';
38
+ import { runCreateCommand } from './create-app-command.js';
39
+ import { runPushCommand } from './push-command.js';
40
+ import { runDeployCommand } from './deploy-command.js';
41
+ import { runProviderKeyCommand } from './provider-key-command.js';
35
42
  const HELP = `ggui — open CLI for the ggui protocol
36
43
 
37
44
  Usage:
@@ -71,6 +78,11 @@ Commands:
71
78
  logout Discard the local ggui.ai session.
72
79
  whoami Print the authenticated user.
73
80
  keys Manage ggui_user_* connector keys (list/create/revoke).
81
+ create Create cloud resources tied to this project.
82
+
83
+ create app [--name <displayName>] Create a new ggui.ai app
84
+ and write appId into ggui.json.
85
+
74
86
  gadget Author gadgets for the ggui marketplace.
75
87
 
76
88
  gadget create <scope/name> Scaffold a new gadget repo.
@@ -84,6 +96,29 @@ Commands:
84
96
  theme validate <path> Validate a JSON theme file against
85
97
  the ThemeDocumentV1 schema.
86
98
 
99
+ export-pool Export this deployment's reusable blueprints as a shareable
100
+ pool directory (manifest.json + codes/). The pool can be
101
+ loaded by another deployment via \`ggui serve --seed-pool <dir>\`.
102
+
103
+ --out <dir> Output directory (default: ./ggui-pool).
104
+
105
+ deploy Provision + wire a ggui.ai cloud app for this project
106
+ (idempotent: safe to re-run after a partial failure).
107
+ Requires a ggui.json in the current directory tree.
108
+
109
+ --push-keys Also push provider API key from env to the app.
110
+
111
+ provider-key Manage provider API keys for a cloud ggui app.
112
+
113
+ provider-key set --app <appId> [--provider <name>]
114
+ Push a provider API key from env to the cloud app.
115
+
116
+ push Compile + bulk-push blueprints to a ggui.ai cloud app.
117
+ Requires \`ggui login\`.
118
+
119
+ --app <appId> Target app ID (default: ggui.json#appId).
120
+ --from <dir> Read from an existing pool artifact directory.
121
+
87
122
  Global options:
88
123
  --help, -h Show this help.
89
124
  --version Show installed version.
@@ -122,6 +157,16 @@ async function main(argv) {
122
157
  return runBlueprintCommand(rest);
123
158
  case 'theme':
124
159
  return runThemeCommand(rest);
160
+ case 'export-pool':
161
+ return runExportPoolCommand(rest);
162
+ case 'push':
163
+ return runPushCommand(rest);
164
+ case 'create':
165
+ return runCreateCommand(rest);
166
+ case 'deploy':
167
+ return runDeployCommand(rest);
168
+ case 'provider-key':
169
+ return runProviderKeyCommand(rest);
125
170
  default:
126
171
  process.stderr.write(`ggui: unknown command "${command}"\n\n`);
127
172
  process.stderr.write(HELP);
@@ -129,7 +174,7 @@ async function main(argv) {
129
174
  }
130
175
  }
131
176
  // `buildMcpServerBackend` moved to `./mcp-backend.ts` so tests can
132
- // exercise the full opt-in bundle ({sessionChannel, pairing,
177
+ // exercise the full opt-in bundle ({renderChannel, pairing,
133
178
  // console.sessionCookie, shortCodeIndex}) without triggering
134
179
  // `main()` at import. See module-level doc there for the bundle
135
180
  // rationale.
@@ -211,7 +256,7 @@ async function runServeCommand(args) {
211
256
  // Resolve `ggui.json#storage` → concrete adapters BEFORE binding.
212
257
  // Absent storage block → empty bundle → server keeps in-memory
213
258
  // defaults. Sqlite paths resolve relative to the manifest directory
214
- // (not CWD) so `"path": "./ggui-renders.sqlite"` lands next to
259
+ // (not CWD) so `"path": "./ggui-sessions.sqlite"` lands next to
215
260
  // `ggui.json` regardless of where the operator invoked `ggui serve`
216
261
  // from. Failure to instantiate (e.g. better-sqlite3 peer dep
217
262
  // missing, permission denied) propagates to the exit-1 path below.
@@ -223,7 +268,7 @@ async function runServeCommand(args) {
223
268
  process.stderr.write(`ggui serve: storage: ${err instanceof Error ? err.message : String(err)}\n`);
224
269
  return 1;
225
270
  }
226
- // Persistent default for RenderStore + VectorStore — layered AFTER
271
+ // Persistent default for GguiSessionStore + VectorStore — layered AFTER
227
272
  // the manifest resolver so explicit `ggui.json#storage` declarations
228
273
  // always win, and BEFORE the banner so `describeStorageStatus`
229
274
  // reflects the actual wired state. Without this layer, ggui-default
@@ -235,10 +280,10 @@ async function runServeCommand(args) {
235
280
  // `--ephemeral` skips the whole persistent layering. Operators who
236
281
  // pass the flag get pure in-memory defaults across the board (HMAC
237
282
  // secrets mint fresh per process). Without the flag, the
238
- // sqlite-backed VectorStore + RenderStore persist across restarts
283
+ // sqlite-backed VectorStore + GguiSessionStore persist across restarts
239
284
  // — note this means the install-to-cache bridge can serve stale
240
- // install-provenance rows after a boot if the source TSX behind a
241
- // cached row has been edited or uninstalled in the meantime.
285
+ // installed rows after a boot if the source TSX behind a cached
286
+ // row has been edited or uninstalled in the meantime.
242
287
  if (!parsed.ephemeral) {
243
288
  const persistentDir = getPersistentDir(plan.projectRoot);
244
289
  const manifestDeclaresRenders = plan.manifest?.storage?.renders !== undefined;
@@ -253,7 +298,7 @@ async function runServeCommand(args) {
253
298
  // resolved above) — the operator asked for it by name.
254
299
  if (!manifestDeclaresRenders && storage.renderStore === undefined) {
255
300
  try {
256
- const renderStore = await createPersistentRenderStore(persistentDir);
301
+ const renderStore = await createPersistentGguiSessionStore(persistentDir);
257
302
  storage = { ...storage, renderStore };
258
303
  }
259
304
  catch (err) {
@@ -376,16 +421,16 @@ async function runServeCommand(args) {
376
421
  }
377
422
  // Live-theme state cell. Shared between the console-theme route's
378
423
  // POST handler (mountDevtoolThemeRoutes' `onConfigChange` callback)
379
- // and the push handler's `themeProvider` getter so a "Save to
380
- // ggui.json" in the picker reaches the next push's bootstrap
381
- // envelope without a server restart. Without the cell the push
424
+ // and the render handler's `themeProvider` getter so a "Save to
425
+ // ggui.json" in the picker reaches the next render's bootstrap
426
+ // envelope without a server restart. Without the cell the render
382
427
  // handler would capture `themeId` once at boot, and any subsequent
383
428
  // edit would be silently ignored until restart.
384
429
  //
385
430
  // Initial value derives from the boot-time LoadedTheme so the
386
- // first push (before any save happens) embeds the manifest's
431
+ // first render (before any save happens) embeds the manifest's
387
432
  // declared theme. POST writes mutate `themeStateCell.current`;
388
- // the closure below reads it on every push.
433
+ // the closure below reads it on every render.
389
434
  const themeStateCell = {
390
435
  current: theme !== undefined && theme.source === 'preset'
391
436
  ? { id: theme.preset, mode: theme.mode }
@@ -458,15 +503,33 @@ async function runServeCommand(args) {
458
503
  const settingsUrl = `${baseUrlForCard}/settings`;
459
504
  // No-credentials cards live for an hour from mint — long enough for
460
505
  // the operator to follow the Connect-Claude link, paste a key, and
461
- // re-prompt before the persisted render is GC'd.
506
+ // re-prompt before the persisted GguiSession is GC'd.
462
507
  const NO_CREDENTIALS_CARD_TTL_MS = 60 * 60 * 1000;
463
- // Resolve the operator's explicit `generation.model` route, if any.
464
- // The schema-side `parseAnyLlmRoute` transform yields a typed
465
- // `LlmRoute` directly — no string parsing here. Bedrock is
466
- // hosted-only on the OSS path (no IAM resolver chain wired); reject
467
- // explicitly with a clear error rather than silently failing at
468
- // dispatch time.
469
- const configuredRoute = plan.manifest?.generation?.model;
508
+ // Resolve the operator's explicit generation route, applying the
509
+ // precedence rule `GGUI_GENERATION_MODEL` env > `ggui.json#
510
+ // generation.model`. The env override is the ops escape hatch: it
511
+ // repoints a `ggui serve` instance at a different model without
512
+ // editing the manifest (and lets the e2e provider matrix boot the
513
+ // SAME sample under different providers). The manifest leg is
514
+ // already a typed `LlmRoute` from the schema transform; the env leg
515
+ // is parsed via `parseAnyLlmRoute` inside `resolveConfiguredRoute`.
516
+ const routeResolution = resolveConfiguredRoute({
517
+ envModel: process.env[GENERATION_MODEL_ENV],
518
+ manifestRoute: plan.manifest?.generation?.model,
519
+ });
520
+ if (routeResolution.kind === 'invalid-env') {
521
+ process.stderr.write(`ggui serve: ${GENERATION_MODEL_ENV}="${routeResolution.raw}" is not a ` +
522
+ `valid model route. Accepted forms:\n` +
523
+ ` - canonical: "openai:gpt-5.4-mini"\n` +
524
+ ` - LiteLLM: "gemini/gemini-3.1-flash-lite"\n` +
525
+ `See docs/principles/model-string-convention.md.\n`);
526
+ return 1;
527
+ }
528
+ const configuredRoute = routeResolution.kind === 'route' ? routeResolution.route : undefined;
529
+ // Bedrock is hosted-only on the OSS path (no IAM resolver chain
530
+ // wired); reject explicitly with a clear error rather than silently
531
+ // failing at dispatch time. Covers both env + manifest sources since
532
+ // the check runs on the resolved route.
470
533
  if (configuredRoute?.provider === 'bedrock') {
471
534
  process.stderr.write(`ggui serve: ggui.json#generation.model declared a bedrock route ` +
472
535
  `("${configuredRoute.model}"), but the OSS BYOK resolver chain ` +
@@ -486,8 +549,8 @@ async function runServeCommand(args) {
486
549
  ...(configuredRoute ? { configuredRoute } : {}),
487
550
  onNoCredentials: (ctx, story) => {
488
551
  const nowEpochMs = Date.parse(story.nowIso);
489
- return buildNoCredentialsRender({
490
- renderId: story.renderId,
552
+ return buildNoCredentialsGguiSession({
553
+ sessionId: story.sessionId,
491
554
  appId: ctx.appId,
492
555
  intent: story.intent,
493
556
  nowEpochMs,
@@ -538,6 +601,20 @@ async function runServeCommand(args) {
538
601
  return 1;
539
602
  }
540
603
  process.stdout.write(`${describeGenerationBinding(generationBinding)}\n`);
604
+ // Build seed pools from --seed-pool flags BEFORE composing the backend
605
+ // factory. `buildSeedPool` is async (reads + indexes the directory
606
+ // artifact) so it must run here in the async outer scope rather than
607
+ // inside the synchronous `backendFactory` closure below.
608
+ const seedPools = [];
609
+ for (const dir of parsed.seedPools) {
610
+ try {
611
+ seedPools.push(await buildSeedPool(new FileSystemSeedPoolSource(dir), { scope: 'shared' }));
612
+ }
613
+ catch (err) {
614
+ process.stderr.write(`ggui serve: --seed-pool "${dir}": ${err instanceof Error ? err.message : String(err)}\n`);
615
+ return 1;
616
+ }
617
+ }
541
618
  try {
542
619
  return await runServe({
543
620
  flags: {
@@ -590,9 +667,9 @@ async function runServeCommand(args) {
590
667
  themeWriter: createThemeWriter(join(plan.projectRoot, 'ggui.json')),
591
668
  themeFileUploader: createThemeFileUploader(join(plan.projectRoot, 'ggui.json')),
592
669
  // Live-theme: closure reads `themeStateCell` on every
593
- // push. `onThemeConfigChange` mutates the cell from
670
+ // render. `onThemeConfigChange` mutates the cell from
594
671
  // the console-theme route's POST handler. Together:
595
- // console save → cell update → next push's bootstrap
672
+ // console save → cell update → next render's bootstrap
596
673
  // carries the new themeId. No restart required.
597
674
  themeProvider,
598
675
  onThemeConfigChange: (next) => {
@@ -605,7 +682,7 @@ async function runServeCommand(args) {
605
682
  // - { preset, mode?, overrides? }: explicit preset.
606
683
  // - { file, mode? }: custom DTCG document.
607
684
  // For preset shapes, propagate id+mode to the live
608
- // cell so the next push's bootstrap envelope picks
685
+ // cell so the next render's bootstrap envelope picks
609
686
  // them up. For the file branch the bootstrap's
610
687
  // `themeId` field has no useful value (the runtime
611
688
  // bundles only registered presets), so we clear
@@ -657,7 +734,7 @@ async function runServeCommand(args) {
657
734
  : {}),
658
735
  // Pass through `app.publicEnv`. The server projects only keys
659
736
  // that some registered wrapper's `requires` lists onto
660
- // `App.publicEnv` for the push-gate validator + the iframe
737
+ // `App.publicEnv` for the render-gate validator + the iframe
661
738
  // runtime's `getPublicEnv()`.
662
739
  ...(plan.manifest?.app?.publicEnv &&
663
740
  Object.keys(plan.manifest.app.publicEnv).length > 0
@@ -668,6 +745,7 @@ async function runServeCommand(args) {
668
745
  : {}),
669
746
  generation: generationBinding.generation,
670
747
  ...(mcpMounts.length > 0 ? { mcpMounts } : {}),
748
+ ...(seedPools.length > 0 ? { seedPools } : {}),
671
749
  // Marketplace-install bridge. Pass the projectRoot +
672
750
  // filtered installed-blueprint subset so `buildMcpServerBackend`
673
751
  // can construct an `InstalledBlueprintsProvider` rooted on the
@@ -899,6 +977,10 @@ function narrowMcpMounts(discovered) {
899
977
  }
900
978
  async function runDevCommand(args) {
901
979
  const parsed = parseDevFlags(args);
980
+ if (parsed.error === '__help__') {
981
+ process.stdout.write(DEV_HELP);
982
+ return 0;
983
+ }
902
984
  if (parsed.error) {
903
985
  process.stderr.write(`ggui dev: ${parsed.error}\n`);
904
986
  return 1;