@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
@@ -0,0 +1,386 @@
1
+ /**
2
+ * `ggui deploy` — idempotent orchestrator that provisions and wires a
3
+ * ggui.ai cloud app for the current project.
4
+ *
5
+ * Sequencing (ggui-side only):
6
+ * 1. login — device-flow auth (if not already signed in)
7
+ * 2. create-app — POST /v1/apps, persist appId into ggui.json
8
+ * 3. mint-key — POST /v1/keys, write GGUI_MCP_BEARER into .env.local
9
+ * 4. push — export local blueprint pool + upload to the app
10
+ * 5. push-config — PATCH /v1/apps/{appId}: gadgets + publicEnv from ggui.json
11
+ * 6. wire-env — write GGUI_MCP_URL into .env.local
12
+ *
13
+ * Each step is skipped when its gate is already satisfied, making the
14
+ * command idempotent — run it again after a partial failure and it
15
+ * resumes from the first unsatisfied step.
16
+ *
17
+ * Agent / MCP HOSTING (guuey.com) is out of scope for `ggui deploy`.
18
+ * This command wires only the ggui UI layer.
19
+ */
20
+ /* eslint-disable no-console */
21
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
22
+ import { dirname, join } from 'node:path';
23
+ import { tryLoadAuthSession } from './auth-store.js';
24
+ import { runLoginCommand } from './auth-login.js';
25
+ import { createApp, createKey } from './api-client.js';
26
+ import { findGguiJson, readGguiJson, writeGguiJson } from './internal/ggui-json.js';
27
+ import { runPushCommand } from './push-command.js';
28
+ import { runConfigPushStep } from './config-push.js';
29
+ import { runProviderKeyCommand } from './provider-key-command.js';
30
+ /**
31
+ * Derive the ordered list of steps to execute given the current state.
32
+ * Pure function — no I/O.
33
+ *
34
+ * Step order:
35
+ * login? → create-app? → mint-key? → push → push-config → push-keys? → wire-env
36
+ */
37
+ export function planDeploySteps(input) {
38
+ const { state: s, pushKeys } = input;
39
+ const steps = [];
40
+ if (!s.authed)
41
+ steps.push({ kind: 'login' });
42
+ if (!s.appId)
43
+ steps.push({ kind: 'create-app' });
44
+ if (!s.hasKey)
45
+ steps.push({ kind: 'mint-key' });
46
+ steps.push({ kind: 'push' }, { kind: 'push-config' });
47
+ if (pushKeys)
48
+ steps.push({ kind: 'push-keys' });
49
+ steps.push({ kind: 'wire-env' });
50
+ return steps;
51
+ }
52
+ /**
53
+ * Resolve the `GGUI_MCP_URL` value `ggui deploy` wires into `.env.local`.
54
+ *
55
+ * The per-app cloud pod serves the MCP transport at the BARE per-app endpoint
56
+ * `<base>/apps/<appId>` (the `connectUrl` the backend advertises) — NOT a
57
+ * `/mcp` sub-path. The `/mcp` suffix is the LOCAL `ggui serve` convention;
58
+ * appending it here produced a URL the per-app pod 404s, so the agent (which
59
+ * reads GGUI_MCP_URL verbatim) could never connect to a deployed app.
60
+ *
61
+ * - `connectUrl` present (a create-app ran this session) → use it VERBATIM —
62
+ * the deployment-correct base (prod `mcp.ggui.ai`, sandbox
63
+ * `<env>.mcp.sandbox.ggui.ai`, derived from whichever backend `GGUI_API_URL`
64
+ * pointed at).
65
+ * - absent → the PRODUCTION default `https://mcp.ggui.ai/apps/<appId>`.
66
+ *
67
+ * The caller leaves any pre-existing `GGUI_MCP_URL` untouched (env override).
68
+ */
69
+ export function resolveDeployMcpUrl(connectUrl, appId) {
70
+ return connectUrl ?? `https://mcp.ggui.ai/apps/${appId}`;
71
+ }
72
+ // ─────────────────────────────────────────────────────────────────────────────
73
+ // .env.local upsert helper
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+ /**
76
+ * Read-modify-write `.env.local` at `envLocalPath`, upserting `key=value`.
77
+ *
78
+ * - If the file doesn't exist, creates it with only this line.
79
+ * - If the key already exists, replaces its value in-place.
80
+ * - Other keys/comments are preserved unchanged.
81
+ * - No trailing newline is added beyond the final `\n`.
82
+ */
83
+ export function upsertEnvLocal(envLocalPath, key, value) {
84
+ const line = `${key}=${value}`;
85
+ if (!existsSync(envLocalPath)) {
86
+ writeFileSync(envLocalPath, `${line}\n`, 'utf-8');
87
+ return;
88
+ }
89
+ const raw = readFileSync(envLocalPath, 'utf-8');
90
+ const lines = raw.split('\n');
91
+ let found = false;
92
+ const updated = lines.map((l) => {
93
+ // Match `KEY=...` or `export KEY=...` (no leading spaces per convention).
94
+ const trimmed = l.startsWith('export ') ? l.slice(7) : l;
95
+ const eq = trimmed.indexOf('=');
96
+ if (eq !== -1 && trimmed.slice(0, eq).trim() === key) {
97
+ found = true;
98
+ return line;
99
+ }
100
+ return l;
101
+ });
102
+ if (!found) {
103
+ // Append: ensure one blank line before the new entry if the file is
104
+ // non-empty and doesn't already end with a blank line.
105
+ const last = updated[updated.length - 1];
106
+ if (last !== undefined && last.trim() !== '') {
107
+ updated.push('');
108
+ }
109
+ updated.push(line);
110
+ }
111
+ // Rejoin preserving the original trailing-newline behaviour.
112
+ const result = updated.join('\n');
113
+ writeFileSync(envLocalPath, result.endsWith('\n') ? result : `${result}\n`, 'utf-8');
114
+ }
115
+ /**
116
+ * Read the non-empty value of `key` from `.env.local`, or `undefined` if
117
+ * the file doesn't exist, the key is absent, or its value is empty.
118
+ * Strips surrounding quotes + `export ` prefix the same way `upsertEnvLocal`
119
+ * recognises them.
120
+ */
121
+ export function readEnvLocalValue(envLocalPath, key) {
122
+ if (!existsSync(envLocalPath))
123
+ return undefined;
124
+ const raw = readFileSync(envLocalPath, 'utf-8');
125
+ for (const rawLine of raw.split('\n')) {
126
+ const l = rawLine.trim().startsWith('export ')
127
+ ? rawLine.trim().slice(7)
128
+ : rawLine.trim();
129
+ const eq = l.indexOf('=');
130
+ if (eq !== -1 && l.slice(0, eq).trim() === key) {
131
+ const val = l
132
+ .slice(eq + 1)
133
+ .replace(/^["']|["']$/g, '')
134
+ .trim();
135
+ if (val.length > 0)
136
+ return val;
137
+ }
138
+ }
139
+ return undefined;
140
+ }
141
+ /**
142
+ * Read the current deploy state from disk:
143
+ * - authed : ~/.ggui/auth.json is present + valid
144
+ * - appId : ggui.json#appId if found
145
+ * - hasKey : GGUI_MCP_BEARER present in .env.local (not empty)
146
+ */
147
+ function readDeployContext(cwd) {
148
+ const gguiJsonPath = findGguiJson(cwd);
149
+ // .env.local lives next to ggui.json when one is found, else at cwd.
150
+ const projectRoot = gguiJsonPath ? dirname(gguiJsonPath) : cwd;
151
+ const envLocalPath = join(projectRoot, '.env.local');
152
+ const authed = tryLoadAuthSession() !== null;
153
+ let appId;
154
+ if (gguiJsonPath) {
155
+ const result = readGguiJson(gguiJsonPath);
156
+ if ('value' in result) {
157
+ const v = result.value['appId'];
158
+ if (typeof v === 'string' && v.length > 0)
159
+ appId = v;
160
+ }
161
+ }
162
+ const hasKey = readEnvLocalValue(envLocalPath, 'GGUI_MCP_BEARER') !== undefined;
163
+ return {
164
+ gguiJsonPath,
165
+ envLocalPath,
166
+ state: { authed, appId, hasKey },
167
+ };
168
+ }
169
+ // ─────────────────────────────────────────────────────────────────────────────
170
+ // Orchestrator
171
+ // ─────────────────────────────────────────────────────────────────────────────
172
+ export const DEPLOY_HELP = `ggui deploy — provision + wire a ggui.ai cloud app for this project
173
+
174
+ Idempotent: run it again after a partial failure to resume.
175
+
176
+ Steps (ggui-side only):
177
+ 1. login Sign in (skipped if already authed)
178
+ 2. create-app Create a cloud app + write appId into ggui.json
179
+ 3. mint-key Create a connector key + write GGUI_MCP_BEARER into .env.local
180
+ 4. push Export + upload blueprints to the app
181
+ 5. push-config Push declared gadgets + publicEnv to the app
182
+ 6. push-keys Push provider API key from env to the app (only with --push-keys)
183
+ 7. wire-env Write GGUI_MCP_URL into .env.local
184
+
185
+ Agent / MCP HOSTING (guuey.com) is handled separately.
186
+
187
+ Usage:
188
+ ggui deploy [--push-keys]
189
+
190
+ Options:
191
+ --push-keys Read the provider API key from the appropriate env var
192
+ (e.g. ANTHROPIC_API_KEY) and push it to the cloud app so
193
+ keySource=own renders work. Derives the provider from
194
+ ggui.json#generation.model (or pass --provider to
195
+ \`ggui provider-key set\` directly).
196
+ `;
197
+ /**
198
+ * Orchestrate a full ggui-side deploy. Returns a process exit code.
199
+ */
200
+ export async function runDeployCommand(_args) {
201
+ if (_args.includes('--help') || _args.includes('-h')) {
202
+ process.stdout.write(DEPLOY_HELP);
203
+ return 0;
204
+ }
205
+ const pushKeys = _args.includes('--push-keys');
206
+ const cwd = process.cwd();
207
+ // Read current state.
208
+ const ctx = readDeployContext(cwd);
209
+ const { gguiJsonPath, envLocalPath, state } = ctx;
210
+ if (!gguiJsonPath) {
211
+ process.stderr.write('ggui deploy: no ggui.json found in this directory or its parents.\n' +
212
+ ' Run from a scaffolded project directory (one created by `npx @ggui-ai/create-agentic-app`).\n');
213
+ return 1;
214
+ }
215
+ const steps = planDeploySteps({ state, pushKeys });
216
+ // Mutable state for values discovered mid-run that subsequent steps need.
217
+ let appId = state.appId;
218
+ let connectUrl;
219
+ for (const step of steps) {
220
+ switch (step.kind) {
221
+ case 'login': {
222
+ process.stdout.write('\n[ggui deploy] Step: login\n');
223
+ const code = await runLoginCommand([]);
224
+ if (code !== 0) {
225
+ process.stderr.write('ggui deploy: login failed. Aborting.\n');
226
+ return code;
227
+ }
228
+ // runLoginCommand persisted the session to ~/.ggui/auth.json;
229
+ // createApp / createKey load it internally, so nothing to thread
230
+ // forward here.
231
+ break;
232
+ }
233
+ case 'create-app': {
234
+ process.stdout.write('\n[ggui deploy] Step: create-app\n');
235
+ let app;
236
+ try {
237
+ app = await createApp({});
238
+ }
239
+ catch (err) {
240
+ process.stderr.write(`ggui deploy: create-app failed — ${err instanceof Error ? err.message : String(err)}\n`);
241
+ return 1;
242
+ }
243
+ appId = app.appId;
244
+ connectUrl = app.connectUrl;
245
+ // Persist appId into ggui.json. The app already exists server-side
246
+ // now, so a persist failure is LOUD: without the appId on disk a
247
+ // re-run would call create-app again and create a DUPLICATE app.
248
+ const read = readGguiJson(gguiJsonPath);
249
+ if ('error' in read) {
250
+ process.stderr.write(`ggui deploy: create-app: app ${app.appId} was created, but ggui.json ` +
251
+ `could not be read to persist appId — ${read.error}\n` +
252
+ ` Add "appId": "${app.appId}" to ggui.json manually before re-running, ` +
253
+ `or a re-run will create a DUPLICATE app.\n`);
254
+ return 1;
255
+ }
256
+ writeGguiJson(gguiJsonPath, { ...read.value, appId });
257
+ process.stdout.write(` appId: ${app.appId}\n`);
258
+ process.stdout.write(` connectUrl: ${app.connectUrl}\n`);
259
+ break;
260
+ }
261
+ case 'mint-key': {
262
+ process.stdout.write('\n[ggui deploy] Step: mint-key\n');
263
+ let key;
264
+ try {
265
+ key = await createKey({ name: 'ggui deploy' });
266
+ }
267
+ catch (err) {
268
+ process.stderr.write(`ggui deploy: mint-key failed — ${err instanceof Error ? err.message : String(err)}\n`);
269
+ return 1;
270
+ }
271
+ // Write the one-time-reveal secret into .env.local.
272
+ upsertEnvLocal(envLocalPath, 'GGUI_MCP_BEARER', key.apiKey);
273
+ process.stdout.write(` Key created (prefix: ${key.prefix}). Written to .env.local.\n`);
274
+ break;
275
+ }
276
+ case 'push': {
277
+ process.stdout.write('\n[ggui deploy] Step: push\n');
278
+ if (!appId) {
279
+ process.stderr.write('ggui deploy: push: no appId resolved. This is a bug.\n');
280
+ return 1;
281
+ }
282
+ const code = await runPushCommand(['--app', appId]);
283
+ if (code !== 0) {
284
+ process.stderr.write('ggui deploy: push failed. Aborting.\n');
285
+ return code;
286
+ }
287
+ break;
288
+ }
289
+ case 'push-config': {
290
+ process.stdout.write('\n[ggui deploy] Step: push-config\n');
291
+ if (!appId) {
292
+ process.stderr.write('ggui deploy: push-config: no appId resolved. This is a bug.\n');
293
+ return 1;
294
+ }
295
+ const code = await runConfigPushStep(appId);
296
+ if (code !== 0) {
297
+ process.stderr.write('ggui deploy: push-config failed. Aborting.\n');
298
+ return code;
299
+ }
300
+ break;
301
+ }
302
+ case 'push-keys': {
303
+ process.stdout.write('\n[ggui deploy] Step: push-keys\n');
304
+ if (!appId) {
305
+ process.stderr.write('ggui deploy: push-keys: no appId resolved. This is a bug.\n');
306
+ return 1;
307
+ }
308
+ const code = await runProviderKeyCommand(['set', '--app', appId]);
309
+ if (code !== 0) {
310
+ process.stderr.write('ggui deploy: push-keys failed. Aborting.\n');
311
+ return code;
312
+ }
313
+ break;
314
+ }
315
+ case 'wire-env': {
316
+ process.stdout.write('\n[ggui deploy] Step: wire-env\n');
317
+ if (!appId) {
318
+ process.stderr.write('ggui deploy: wire-env: no appId resolved. This is a bug.\n');
319
+ return 1;
320
+ }
321
+ // NEVER clobber an existing GGUI_MCP_URL. The FIRST deploy writes
322
+ // the deployment-correct base derived from create-app's connectUrl
323
+ // (e.g. sandbox `<env>.mcp.sandbox.ggui.ai`, prod `mcp.ggui.ai`).
324
+ // On REDEPLOY we have only the appId — reconstructing a hardcoded
325
+ // prod URL here would silently break a sandbox/non-prod deployment.
326
+ // So: leave any existing value alone; only write when absent.
327
+ const existing = readEnvLocalValue(envLocalPath, 'GGUI_MCP_URL');
328
+ if (existing) {
329
+ process.stdout.write(` GGUI_MCP_URL already set (${existing}) — leaving it unchanged.\n`);
330
+ break;
331
+ }
332
+ // No prior value → resolve the deployment-correct BARE per-app URL
333
+ // (see resolveDeployMcpUrl: the per-app pod serves MCP at the bare
334
+ // endpoint, never `/mcp`). The fallback to the production default only
335
+ // fires when create-app didn't run this session — a first deploy always
336
+ // has connectUrl, so it never reaches the fallback in practice.
337
+ const mcpUrl = resolveDeployMcpUrl(connectUrl, appId);
338
+ upsertEnvLocal(envLocalPath, 'GGUI_MCP_URL', mcpUrl);
339
+ process.stdout.write(` GGUI_MCP_URL=${mcpUrl}\n`);
340
+ break;
341
+ }
342
+ default: {
343
+ // Exhaustiveness check — DeployStepKind is a union; this branch
344
+ // is unreachable in correct code.
345
+ const _exhaustive = step.kind;
346
+ process.stderr.write(`ggui deploy: unknown step "${_exhaustive}"\n`);
347
+ return 1;
348
+ }
349
+ }
350
+ }
351
+ // keySource=own WARN — if the project declared keySource='own' but the
352
+ // user didn't pass --push-keys this deploy, remind them that cloud renders
353
+ // will reject until a key is set.
354
+ if (!pushKeys && appId) {
355
+ const gguiReadResult = readGguiJson(gguiJsonPath);
356
+ if ('value' in gguiReadResult) {
357
+ const { z } = await import('zod');
358
+ const schema = z.object({
359
+ generation: z.object({ keySource: z.string().optional() }).optional(),
360
+ });
361
+ const parsed = schema.safeParse(gguiReadResult.value);
362
+ if (parsed.success && parsed.data.generation?.keySource === 'own') {
363
+ process.stderr.write(`\n WARN: keySource=own but no provider key pushed — run\n` +
364
+ ` ggui deploy --push-keys\n` +
365
+ ` or\n` +
366
+ ` ggui provider-key set --app ${appId}\n` +
367
+ ` else cloud renders will reject.\n`);
368
+ }
369
+ }
370
+ }
371
+ // Report the URL actually in .env.local — after wire-env that file is
372
+ // authoritative (a preserved sandbox/non-prod value is reflected
373
+ // accurately rather than re-derived as the prod default).
374
+ const finalMcpUrl = readEnvLocalValue(envLocalPath, 'GGUI_MCP_URL') ?? '(unknown)';
375
+ process.stdout.write('\n');
376
+ process.stdout.write(' ggui deploy complete.\n');
377
+ process.stdout.write(`\n`);
378
+ process.stdout.write(` App MCP endpoint: ${finalMcpUrl}\n`);
379
+ process.stdout.write(` Bearer key: in .env.local (GGUI_MCP_BEARER)\n`);
380
+ process.stdout.write('\n');
381
+ process.stdout.write(' The agent will use the cloud ggui endpoint on next `pnpm dev`.\n' +
382
+ ' Local ggui service is skipped automatically when GGUI_MCP_URL is remote.\n' +
383
+ '\n' +
384
+ ' Agent / MCP hosting (guuey.com) is handled separately.\n');
385
+ return 0;
386
+ }
@@ -28,14 +28,18 @@ export interface ParsedDevFlags {
28
28
  */
29
29
  tunnel: boolean;
30
30
  agent?: string;
31
+ /** `'__help__'` for `--help` / `-h`; other strings = usage error. */
31
32
  error?: string;
32
33
  }
33
34
  /**
34
35
  * Parse the raw `ggui dev` argv tail. Returns a discriminated
35
36
  * shape — `error` field is non-undefined when the input was
36
- * malformed; callers render it and bail with exit code 1.
37
+ * malformed (`'__help__'` is the help sentinel, mirroring
38
+ * `parseServeFlags`); callers render it and bail with exit code 1.
37
39
  */
38
40
  export declare function parseDevFlags(args: readonly string[]): ParsedDevFlags;
41
+ /** Help text for `ggui dev` — printed on `--help` / `-h`. */
42
+ export declare const DEV_HELP = "ggui dev \u2014 run the local development stack\n\nBoots the local dev hub (UI registry + dev server) and, when\n`--agent` is supplied, supervises your agent entry alongside it.\nSets GGUI_MODE=dev (unless already set) so the server mounts the\n/devtools/* namespace and the SPA shows the link. In an interactive\nterminal the hub URL auto-opens in your browser (skipped in CI, when\nstdout is piped, with BROWSER=none, or with --no-open).\n\nUsage:\n ggui dev [options]\n\nOptions:\n --port <n> Bind port (default: 6780, 0 = OS-assigned).\n --host <addr> Bind host (default: 127.0.0.1).\n --agent <entry> Agent entry file to supervise (e.g. src/agent.ts).\n Resolved before the socket binds \u2014 bad paths fail fast.\n --no-serve Do not bind the HTTP dev hub; run registry-only.\n --no-open Skip the browser auto-open.\n --tunnel Opt into managed-mode: after the local stack is\n listening, the configured tunnel provider exposes it\n remotely. The local loop runs unchanged either way.\n --help, -h Show this help.\n";
39
43
  export { resolveAgentCommand, buildAgentRuntime };
40
44
  export type { AgentCommandResolution };
41
45
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"dev-command.d.ts","sourceRoot":"","sources":["../src/dev-command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-command.d.ts","sourceRoot":"","sources":["../src/dev-command.ts"],"names":[],"mappings":"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"}
@@ -13,11 +13,13 @@
13
13
  * tunnel, a test harness) pick their own launch policy.
14
14
  */
15
15
  import { spawn as nodeSpawn, } from 'node:child_process';
16
+ import { DEFAULT_DEV_HOST, DEFAULT_DEV_PORT } from '@ggui-ai/dev-stack';
16
17
  import { buildAgentRuntime, resolveAgentCommand, } from './agent-resolution.js';
17
18
  /**
18
19
  * Parse the raw `ggui dev` argv tail. Returns a discriminated
19
20
  * shape — `error` field is non-undefined when the input was
20
- * malformed; callers render it and bail with exit code 1.
21
+ * malformed (`'__help__'` is the help sentinel, mirroring
22
+ * `parseServeFlags`); callers render it and bail with exit code 1.
21
23
  */
22
24
  export function parseDevFlags(args) {
23
25
  const out = { noServe: false, noOpen: false, tunnel: false };
@@ -61,10 +63,38 @@ export function parseDevFlags(args) {
61
63
  out.agent = v;
62
64
  continue;
63
65
  }
66
+ if (arg === '--help' || arg === '-h') {
67
+ return { ...out, error: '__help__' };
68
+ }
64
69
  return { ...out, error: `unknown flag "${arg ?? ''}"` };
65
70
  }
66
71
  return out;
67
72
  }
73
+ /** Help text for `ggui dev` — printed on `--help` / `-h`. */
74
+ export const DEV_HELP = `ggui dev — run the local development stack
75
+
76
+ Boots the local dev hub (UI registry + dev server) and, when
77
+ \`--agent\` is supplied, supervises your agent entry alongside it.
78
+ Sets GGUI_MODE=dev (unless already set) so the server mounts the
79
+ /devtools/* namespace and the SPA shows the link. In an interactive
80
+ terminal the hub URL auto-opens in your browser (skipped in CI, when
81
+ stdout is piped, with BROWSER=none, or with --no-open).
82
+
83
+ Usage:
84
+ ggui dev [options]
85
+
86
+ Options:
87
+ --port <n> Bind port (default: ${DEFAULT_DEV_PORT}, 0 = OS-assigned).
88
+ --host <addr> Bind host (default: ${DEFAULT_DEV_HOST}).
89
+ --agent <entry> Agent entry file to supervise (e.g. src/agent.ts).
90
+ Resolved before the socket binds — bad paths fail fast.
91
+ --no-serve Do not bind the HTTP dev hub; run registry-only.
92
+ --no-open Skip the browser auto-open.
93
+ --tunnel Opt into managed-mode: after the local stack is
94
+ listening, the configured tunnel provider exposes it
95
+ remotely. The local loop runs unchanged either way.
96
+ --help, -h Show this help.
97
+ `;
68
98
  // Agent-entry resolution + adapter construction live in
69
99
  // `./agent-resolution.ts`. They are re-exported here so call sites
70
100
  // that reach for these from `dev-command.js` keep working — and
@@ -0,0 +1,25 @@
1
+ export declare const EXPORT_POOL_HELP = "ggui export-pool \u2014 export this app's reusable blueprints as a shared pool\n\nWrites a directory artifact (manifest.json + codes/) that another ggui\ndeployment can load with `ggui serve --seed-pool <dir>`.\n\nUsage:\n ggui export-pool [--out <dir>]\n\nOptions:\n --out <dir> Output directory (default: ./ggui-pool).\n\nRequires a persistent vectors store (ggui.json: storage.vectors.driver = \"sqlite\").\n";
2
+ /** Result of {@link exportLocalPool}: the artifact directory + how many records it holds. */
3
+ export interface ExportLocalPoolResult {
4
+ /** Absolute path of the written artifact directory. */
5
+ readonly dir: string;
6
+ /** Number of blueprint records written (may be 0 — the artifact dir is still written). */
7
+ readonly count: number;
8
+ }
9
+ /**
10
+ * Resolve the storage stack → list blueprints → write a pool artifact to disk.
11
+ *
12
+ * When `outDir` is omitted a fresh temp directory is created (suitable for
13
+ * a subsequent `ggui push` that immediately reads the artifact). When provided,
14
+ * the artifact is written to `resolve(process.cwd(), outDir)`.
15
+ *
16
+ * Throws on hard errors (no vectors store, list failure). An empty pool is
17
+ * NOT an error here — the artifact directory is written regardless (possibly
18
+ * holding zero records) so callers can decide whether "nothing to export /
19
+ * push" is graceful (push) or an error (explicit `export-pool`).
20
+ *
21
+ * Returns the artifact directory + record count.
22
+ */
23
+ export declare function exportLocalPool(outDir?: string): Promise<ExportLocalPoolResult>;
24
+ export declare function runExportPoolCommand(args: readonly string[]): Promise<number>;
25
+ //# sourceMappingURL=export-pool-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export-pool-command.d.ts","sourceRoot":"","sources":["../src/export-pool-command.ts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,gBAAgB,oaAY5B,CAAC;AAEF,6FAA6F;AAC7F,MAAM,WAAW,qBAAqB;IACpC,uDAAuD;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0CrF;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBnF"}
@@ -0,0 +1,116 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { mkdtemp } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { listRegistryBlueprintsForExport, } from '@ggui-ai/mcp-server-handlers';
6
+ import { toPortableBlueprint } from '@ggui-ai/protocol/blueprint-key';
7
+ import { resolveStorageFromConfig, DEFAULT_BUILDER_APP_ID } from '@ggui-ai/mcp-server';
8
+ import { findGguiJson, safeLoadGguiJson, } from '@ggui-ai/project-config/node';
9
+ import { writePoolArtifact } from './pool-artifact.js';
10
+ function parseExportPoolFlags(args) {
11
+ let out = './ggui-pool';
12
+ for (let i = 0; i < args.length; i++) {
13
+ const a = args[i];
14
+ if (a === '--out' || a === '-o') {
15
+ const v = args[++i];
16
+ if (v === undefined)
17
+ return { out, error: '--out requires a path' };
18
+ out = v;
19
+ }
20
+ else if (a === '--help' || a === '-h') {
21
+ return { out, error: '__help__' };
22
+ }
23
+ }
24
+ return { out };
25
+ }
26
+ export const EXPORT_POOL_HELP = `ggui export-pool — export this app's reusable blueprints as a shared pool
27
+
28
+ Writes a directory artifact (manifest.json + codes/) that another ggui
29
+ deployment can load with \`ggui serve --seed-pool <dir>\`.
30
+
31
+ Usage:
32
+ ggui export-pool [--out <dir>]
33
+
34
+ Options:
35
+ --out <dir> Output directory (default: ./ggui-pool).
36
+
37
+ Requires a persistent vectors store (ggui.json: storage.vectors.driver = "sqlite").
38
+ `;
39
+ /**
40
+ * Resolve the storage stack → list blueprints → write a pool artifact to disk.
41
+ *
42
+ * When `outDir` is omitted a fresh temp directory is created (suitable for
43
+ * a subsequent `ggui push` that immediately reads the artifact). When provided,
44
+ * the artifact is written to `resolve(process.cwd(), outDir)`.
45
+ *
46
+ * Throws on hard errors (no vectors store, list failure). An empty pool is
47
+ * NOT an error here — the artifact directory is written regardless (possibly
48
+ * holding zero records) so callers can decide whether "nothing to export /
49
+ * push" is graceful (push) or an error (explicit `export-pool`).
50
+ *
51
+ * Returns the artifact directory + record count.
52
+ */
53
+ export async function exportLocalPool(outDir) {
54
+ // Resolve the SAME storage stack `ggui serve` uses, from ggui.json in cwd.
55
+ // A missing manifest is fine (MCP-only / default storage); a manifest that
56
+ // exists but fails schema validation is a hard error — surface it rather
57
+ // than silently falling through to the generic "no vectors store" message.
58
+ const gguiJsonPath = findGguiJson(process.cwd());
59
+ let manifest;
60
+ if (gguiJsonPath) {
61
+ const loaded = safeLoadGguiJson(gguiJsonPath);
62
+ if (!loaded.success) {
63
+ throw new Error(loaded.error.message);
64
+ }
65
+ manifest = loaded.data;
66
+ }
67
+ const projectRoot = gguiJsonPath ? dirname(gguiJsonPath) : process.cwd();
68
+ const storage = await resolveStorageFromConfig(manifest?.storage, { baseDir: projectRoot });
69
+ if (!storage.vectors) {
70
+ throw new Error('no persistent vectors store. Set storage.vectors.driver="sqlite" in ggui.json (nothing to export from an in-memory store).');
71
+ }
72
+ const rows = await listRegistryBlueprintsForExport(storage.vectors, DEFAULT_BUILDER_APP_ID);
73
+ // Tool-identity catalog is NOT available here: `export-pool` is a
74
+ // batch offline export with no live MCP connections. The catalog is
75
+ // a runtime artifact built from active `initialize` + `tools/list`
76
+ // handshakes during `ggui serve`. Passing it here would require
77
+ // either persisting it to disk or starting the server just for export
78
+ // — both are the wrong trade-off for an offline command. The
79
+ // `generatorProtocolVersion` stamp (always set by toPortableBlueprint)
80
+ // is sufficient for era-compatibility gating; the `toolIdentityCatalogHash`
81
+ // remains absent, triggering an "unstamped → warn" policy on importers.
82
+ const records = rows.map((r) => toPortableBlueprint(r));
83
+ const resolvedDir = outDir !== undefined
84
+ ? resolve(process.cwd(), outDir)
85
+ : await mkdtemp(join(tmpdir(), 'ggui-pool-'));
86
+ await writePoolArtifact(resolvedDir, records);
87
+ return { dir: resolvedDir, count: records.length };
88
+ }
89
+ export async function runExportPoolCommand(args) {
90
+ const flags = parseExportPoolFlags(args);
91
+ if (flags.error === '__help__') {
92
+ process.stdout.write(EXPORT_POOL_HELP);
93
+ return 0;
94
+ }
95
+ if (flags.error) {
96
+ process.stderr.write(`ggui export-pool: ${flags.error}\n`);
97
+ return 1;
98
+ }
99
+ let result;
100
+ try {
101
+ result = await exportLocalPool(flags.out);
102
+ }
103
+ catch (err) {
104
+ process.stderr.write(`ggui export-pool: ${err instanceof Error ? err.message : String(err)}\n`);
105
+ return 1;
106
+ }
107
+ // Explicit `export-pool` of an empty pool is an error: the operator asked
108
+ // to export something and there is nothing. (The on-the-fly export path
109
+ // used by `ggui push` treats 0 as a graceful "nothing to push" instead.)
110
+ if (result.count === 0) {
111
+ process.stderr.write('ggui export-pool: 0 blueprints found for this app; nothing written.\n');
112
+ return 1;
113
+ }
114
+ process.stdout.write(`ggui export-pool: wrote ${result.count} blueprint(s) to ${result.dir}\n`);
115
+ return 0;
116
+ }
@@ -0,0 +1,13 @@
1
+ import type { SeedPoolSource } from '@ggui-ai/mcp-server-handlers';
2
+ /**
3
+ * Reads a seed-pool directory artifact (B2 OSS adapter). Records come
4
+ * back raw/unvalidated — `buildSeedPool` runs each one through the
5
+ * `fromPortableBlueprint` trust boundary.
6
+ */
7
+ export declare class FileSystemSeedPoolSource implements SeedPoolSource {
8
+ private readonly dir;
9
+ readonly label: string;
10
+ constructor(dir: string);
11
+ loadAll(): Promise<readonly unknown[]>;
12
+ }
13
+ //# sourceMappingURL=filesystem-seed-pool-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem-seed-pool-source.d.ts","sourceRoot":"","sources":["../src/filesystem-seed-pool-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE;;;;GAIG;AACH,qBAAa,wBAAyB,YAAW,cAAc;IAEjD,OAAO,CAAC,QAAQ,CAAC,GAAG;IADhC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACM,GAAG,EAAE,MAAM;IAGlC,OAAO,IAAI,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC;CAQ7C"}
@@ -0,0 +1,22 @@
1
+ import { readPoolArtifact } from './pool-artifact.js';
2
+ /**
3
+ * Reads a seed-pool directory artifact (B2 OSS adapter). Records come
4
+ * back raw/unvalidated — `buildSeedPool` runs each one through the
5
+ * `fromPortableBlueprint` trust boundary.
6
+ */
7
+ export class FileSystemSeedPoolSource {
8
+ dir;
9
+ label;
10
+ constructor(dir) {
11
+ this.dir = dir;
12
+ this.label = `seed:${dir}`;
13
+ }
14
+ async loadAll() {
15
+ const { records, issues } = await readPoolArtifact(this.dir);
16
+ for (const issue of issues) {
17
+ // eslint-disable-next-line no-console -- operator-visible load warning
18
+ console.warn(`[ggui] ${issue}`);
19
+ }
20
+ return records;
21
+ }
22
+ }