@ggui-ai/cli 0.2.0-alpha.3 → 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.
- package/README.md +38 -7
- package/dist/api-client.d.ts +42 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +30 -2
- package/dist/auth-keys.d.ts +1 -1
- package/dist/auth-keys.d.ts.map +1 -1
- package/dist/auth-keys.js +22 -7
- package/dist/auth-store.d.ts +1 -1
- package/dist/auth-store.d.ts.map +1 -1
- package/dist/cli.js +112 -30
- package/dist/config-push.d.ts +79 -0
- package/dist/config-push.d.ts.map +1 -0
- package/dist/config-push.js +301 -0
- package/dist/create-app-command.d.ts +17 -0
- package/dist/create-app-command.d.ts.map +1 -0
- package/dist/create-app-command.js +58 -0
- package/dist/deploy-command.d.ts +67 -0
- package/dist/deploy-command.d.ts.map +1 -0
- package/dist/deploy-command.js +386 -0
- package/dist/dev-command.d.ts +5 -1
- package/dist/dev-command.d.ts.map +1 -1
- package/dist/dev-command.js +31 -1
- package/dist/export-pool-command.d.ts +25 -0
- package/dist/export-pool-command.d.ts.map +1 -0
- package/dist/export-pool-command.js +116 -0
- package/dist/filesystem-seed-pool-source.d.ts +13 -0
- package/dist/filesystem-seed-pool-source.d.ts.map +1 -0
- package/dist/filesystem-seed-pool-source.js +22 -0
- package/dist/generation-probe.d.ts +53 -7
- package/dist/generation-probe.d.ts.map +1 -1
- package/dist/generation-probe.js +37 -1
- package/dist/internal/artifact-install.d.ts +4 -2
- package/dist/internal/artifact-install.d.ts.map +1 -1
- package/dist/internal/artifact-install.js +11 -10
- package/dist/internal/artifact-publish.d.ts +34 -31
- package/dist/internal/artifact-publish.d.ts.map +1 -1
- package/dist/internal/artifact-publish.js +133 -241
- package/dist/internal/artifact-search.d.ts +2 -28
- package/dist/internal/artifact-search.d.ts.map +1 -1
- package/dist/internal/artifact-search.js +5 -4
- package/dist/internal/auth-strategy.d.ts +21 -31
- package/dist/internal/auth-strategy.d.ts.map +1 -1
- package/dist/internal/auth-strategy.js +15 -23
- package/dist/internal/register-author-key.d.ts +7 -1
- package/dist/internal/register-author-key.d.ts.map +1 -1
- package/dist/internal/register-author-key.js +56 -19
- package/dist/mcp-backend.d.ts +25 -25
- package/dist/mcp-backend.d.ts.map +1 -1
- package/dist/mcp-backend.js +65 -61
- package/dist/paths.d.ts +3 -3
- package/dist/paths.js +3 -3
- package/dist/persistent-secrets.js +1 -1
- package/dist/persistent-stores.d.ts +2 -2
- package/dist/persistent-stores.d.ts.map +1 -1
- package/dist/persistent-stores.js +5 -5
- package/dist/pool-artifact.d.ts +19 -0
- package/dist/pool-artifact.d.ts.map +1 -0
- package/dist/pool-artifact.js +59 -0
- package/dist/provider-key-command.d.ts +51 -0
- package/dist/provider-key-command.d.ts.map +1 -0
- package/dist/provider-key-command.js +222 -0
- package/dist/push-command.d.ts +47 -0
- package/dist/push-command.d.ts.map +1 -0
- package/dist/push-command.js +230 -0
- package/dist/serve-command.d.ts +23 -19
- package/dist/serve-command.d.ts.map +1 -1
- package/dist/serve-command.js +13 -26
- package/dist/theme-command.js +0 -2
- package/dist/tunnel-provider.d.ts +8 -7
- package/dist/tunnel-provider.d.ts.map +1 -1
- package/package.json +25 -21
- package/dist/internal/auth-cache.d.ts +0 -40
- package/dist/internal/auth-cache.d.ts.map +0 -1
- package/dist/internal/auth-cache.js +0 -133
- package/dist/persistent-short-code-index.d.ts +0 -14
- package/dist/persistent-short-code-index.d.ts.map +0 -1
- package/dist/persistent-short-code-index.js +0 -39
- package/dist/spawn-cloudflared.d.ts +0 -61
- package/dist/spawn-cloudflared.d.ts.map +0 -1
- 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 +
|
|
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
|
[](https://www.npmjs.com/package/@ggui-ai/cli)
|
|
6
6
|
[](./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`,
|
|
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.
|
|
49
|
-
- 🔒 Default auth is
|
|
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.
|
|
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` (
|
|
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
|
|
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
|
package/dist/api-client.d.ts
CHANGED
|
@@ -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
|
package/dist/api-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/api-client.js
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/dist/auth-keys.d.ts
CHANGED
|
@@ -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;
|
package/dist/auth-keys.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-keys.d.ts","sourceRoot":"","sources":["../src/auth-keys.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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
|
|
357
|
-
// `
|
|
358
|
-
// `Date.now()` returns milliseconds; passing it raw
|
|
359
|
-
//
|
|
360
|
-
//
|
|
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) {
|
package/dist/auth-store.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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;
|
package/dist/auth-store.d.ts.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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 {
|
|
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 ({
|
|
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-
|
|
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
|
|
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 +
|
|
283
|
+
// sqlite-backed VectorStore + GguiSessionStore persist across restarts
|
|
239
284
|
// — note this means the install-to-cache bridge can serve stale
|
|
240
|
-
//
|
|
241
|
-
//
|
|
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
|
|
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
|
|
380
|
-
// ggui.json" in the picker reaches the next
|
|
381
|
-
// envelope without a server restart. Without the cell the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
//
|
|
469
|
-
|
|
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
|
|
490
|
-
|
|
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
|
-
//
|
|
670
|
+
// render. `onThemeConfigChange` mutates the cell from
|
|
594
671
|
// the console-theme route's POST handler. Together:
|
|
595
|
-
// console save → cell update → next
|
|
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
|
|
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
|
|
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;
|