@ggui-ai/cli 0.6.3 → 0.8.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 (39) hide show
  1. package/dist/auth-keys.d.ts +3 -1
  2. package/dist/auth-keys.d.ts.map +1 -1
  3. package/dist/auth-keys.js +42 -2
  4. package/dist/blueprint-command.d.ts.map +1 -1
  5. package/dist/blueprint-command.js +13 -13
  6. package/dist/cli.js +10 -0
  7. package/dist/config-push.d.ts +0 -8
  8. package/dist/config-push.d.ts.map +1 -1
  9. package/dist/config-push.js +10 -35
  10. package/dist/gadget-command.d.ts +1 -1
  11. package/dist/gadget-command.d.ts.map +1 -1
  12. package/dist/gadget-command.js +35 -12
  13. package/dist/internal/artifact-install.d.ts.map +1 -1
  14. package/dist/internal/artifact-install.js +132 -105
  15. package/dist/internal/artifact-publish.d.ts +37 -35
  16. package/dist/internal/artifact-publish.d.ts.map +1 -1
  17. package/dist/internal/artifact-publish.js +33 -97
  18. package/dist/internal/artifact-search.d.ts +27 -21
  19. package/dist/internal/artifact-search.d.ts.map +1 -1
  20. package/dist/internal/artifact-search.js +14 -128
  21. package/dist/internal/artifact-uninstall.d.ts +8 -5
  22. package/dist/internal/artifact-uninstall.d.ts.map +1 -1
  23. package/dist/internal/artifact-uninstall.js +133 -26
  24. package/dist/internal/ggui-json.d.ts +35 -4
  25. package/dist/internal/ggui-json.d.ts.map +1 -1
  26. package/dist/internal/ggui-json.js +58 -7
  27. package/dist/internal/register-author-key.d.ts +10 -0
  28. package/dist/internal/register-author-key.d.ts.map +1 -1
  29. package/dist/internal/register-author-key.js +14 -7
  30. package/dist/internal/registry-url.d.ts +49 -0
  31. package/dist/internal/registry-url.d.ts.map +1 -0
  32. package/dist/internal/registry-url.js +137 -0
  33. package/dist/mcp-backend.d.ts +6 -0
  34. package/dist/mcp-backend.d.ts.map +1 -1
  35. package/dist/mcp-backend.js +9 -0
  36. package/dist/serve-command.d.ts +14 -1
  37. package/dist/serve-command.d.ts.map +1 -1
  38. package/dist/serve-command.js +29 -0
  39. package/package.json +17 -17
@@ -1,8 +1,10 @@
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";
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> [--label <name>] [--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 --label attaches a display name shown by the registry's key\n management surfaces (e.g. \"ci laptop\").\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;
5
5
  readonly registry?: string;
6
+ /** Optional display name stored on the registered key ([E]). */
7
+ readonly label?: string;
6
8
  readonly help: boolean;
7
9
  readonly error?: string;
8
10
  }
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"auth-keys.d.ts","sourceRoot":"","sources":["../src/auth-keys.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,SAAS,qkEA6BrB,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,gEAAgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,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,CAuEzE"}
package/dist/auth-keys.js CHANGED
@@ -21,7 +21,7 @@ Usage:
21
21
  ggui keys list [--keys-file <path>]
22
22
  ggui keys create [--name <label>] [--expires-at <iso8601>] [--keys-file <path>]
23
23
  ggui keys revoke <id> [--keys-file <path>]
24
- ggui keys register --scope <@scope> [--registry <url>] [--auth=bearer [--token <token>]]
24
+ ggui keys register --scope <@scope> [--label <name>] [--registry <url>] [--auth=bearer [--token <token>]]
25
25
 
26
26
  list Print the caller's keys (id, prefix, name, status, dates).
27
27
  create Mint a new connector key. The full secret is printed ONCE — copy it.
@@ -32,6 +32,8 @@ register Register a per-scope Ed25519 PUBLISHER key with the marketplace
32
32
  auto-generated on first publish under
33
33
  ~/.ggui/keys/<scope>/{private,public}.key — this verb ships
34
34
  the public half to the registry's POST /author-keys.
35
+ --label attaches a display name shown by the registry's key
36
+ management surfaces (e.g. "ci laptop").
35
37
 
36
38
  register-only ${AUTH_HELP_FRAGMENT}
37
39
 
@@ -290,6 +292,7 @@ function printApiError(label, err) {
290
292
  export function parseRegisterFlags(args) {
291
293
  let scope;
292
294
  let registry;
295
+ let label;
293
296
  let help = false;
294
297
  for (let i = 0; i < args.length; i++) {
295
298
  const arg = args[i];
@@ -323,12 +326,39 @@ export function parseRegisterFlags(args) {
323
326
  registry = arg.slice('--registry='.length);
324
327
  continue;
325
328
  }
329
+ if (arg === '--label') {
330
+ const value = args[i + 1];
331
+ if (typeof value !== 'string' || value.trim().length === 0) {
332
+ return {
333
+ help,
334
+ error: '--label requires a non-empty value (e.g. `ci laptop`)',
335
+ };
336
+ }
337
+ label = value;
338
+ i += 1;
339
+ continue;
340
+ }
341
+ if (arg && arg.startsWith('--label=')) {
342
+ // Same rule as the space form — an explicit empty (`--label=`)
343
+ // or whitespace-only value is a usage error, never a silent
344
+ // no-label register.
345
+ const value = arg.slice('--label='.length);
346
+ if (value.trim().length === 0) {
347
+ return {
348
+ help,
349
+ error: '--label requires a non-empty value (e.g. `ci laptop`)',
350
+ };
351
+ }
352
+ label = value;
353
+ continue;
354
+ }
326
355
  return { help, error: `unknown flag: ${arg}` };
327
356
  }
328
357
  return {
329
358
  help,
330
359
  ...(scope !== undefined ? { scope } : {}),
331
360
  ...(registry !== undefined ? { registry } : {}),
361
+ ...(label !== undefined ? { label } : {}),
332
362
  };
333
363
  }
334
364
  async function runKeysRegister(args) {
@@ -361,6 +391,7 @@ async function runKeysRegister(args) {
361
391
  const outcome = await runRegisterAuthorKey({
362
392
  scope: flags.scope,
363
393
  ...(flags.registry !== undefined ? { registry: flags.registry } : {}),
394
+ ...(flags.label !== undefined ? { label: flags.label } : {}),
364
395
  ...(authFlags.auth !== undefined || authFlags.token !== undefined
365
396
  ? { auth: authFlags }
366
397
  : {}),
@@ -380,7 +411,16 @@ async function runKeysRegister(args) {
380
411
  process.stdout.write(`${verb} publisher key for ${flags.scope}.\n` +
381
412
  ` registry: ${outcome.registryUrl}\n` +
382
413
  ` subject: ${outcome.subject}\n` +
383
- ` keyId: ${outcome.keyId}\n`);
414
+ ` keyId: ${outcome.keyId}\n` +
415
+ (outcome.label !== undefined ? ` label: ${outcome.label}\n` : ''));
416
+ // Idempotent re-register keeps the STORED label — say so when the
417
+ // caller asked for a different one, so a silent "why didn't my
418
+ // label change" mystery never happens.
419
+ if (flags.label !== undefined && outcome.label !== flags.label) {
420
+ process.stdout.write(` note: the registry kept the key's existing label` +
421
+ `${outcome.label !== undefined ? ` ("${outcome.label}")` : ''} — ` +
422
+ `re-registering never rewrites it.\n`);
423
+ }
384
424
  return 0;
385
425
  }
386
426
  process.stderr.write(`ggui keys register: ${outcome.code}: ${outcome.message}\n`);
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint-command.d.ts","sourceRoot":"","sources":["../src/blueprint-command.ts"],"names":[],"mappings":"AA6CA,eAAO,MAAM,cAAc,uiBAa1B,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
1
+ {"version":3,"file":"blueprint-command.d.ts","sourceRoot":"","sources":["../src/blueprint-command.ts"],"names":[],"mappings":"AA+CA,eAAO,MAAM,cAAc,uiBAa1B,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
@@ -1,20 +1,21 @@
1
1
  /**
2
- * `ggui blueprint <create|publish|install|search>` — kind-discriminated
3
- * sibling of `gadget-command.ts`.
2
+ * `ggui blueprint <create|publish|install|uninstall|search>` —
3
+ * kind-discriminated sibling of `gadget-command.ts`.
4
4
  *
5
- * - `create` — scaffold a new blueprint repo (TSX + contract stub)
6
- * - `publish` — build, sign, and push to a registry; manifest-kind
7
- * mismatch errors with a friendly redirect.
8
- * - `install` — fetch, verify, and materialize a blueprint into
9
- * `.ggui/installed-blueprints/<id>/`.
10
- * - `search` — query the registry, hard-locked to `kind=blueprint`.
5
+ * - `create` — scaffold a new blueprint repo (TSX + contract stub)
6
+ * - `publish` — build, sign, and push to a registry; manifest-kind
7
+ * mismatch errors with a friendly redirect.
8
+ * - `install` — fetch, verify, and materialize a blueprint into
9
+ * `.ggui/installed-blueprints/<id>/`.
10
+ * - `uninstall` — remove the materialized directory (reverse of install).
11
+ * - `search` — query the registry, hard-locked to `kind=blueprint`.
11
12
  *
12
13
  * A thin router that passes `kind: 'blueprint'` to the shared
13
14
  * `internal/artifact-*` implementations. Per-kind state (scaffolders,
14
15
  * help text) lives in sibling files; the rest is shared.
15
16
  */
16
17
  import { BLUEPRINT_CREATE_HELP, parseBlueprintCreateFlags, runBlueprintCreate, } from './blueprint-create.js';
17
- import { buildPublishHelp, parseArtifactPublishFlags, runArtifactPublish, } from './internal/artifact-publish.js';
18
+ import { buildPublishHelp, parseArtifactPublishFlags, publishOptionsFromFlags, runArtifactPublish, } from './internal/artifact-publish.js';
18
19
  import { buildInstallHelp, parseArtifactInstallFlags, runArtifactInstall, } from './internal/artifact-install.js';
19
20
  import { buildUninstallHelp, parseArtifactUninstallFlags, runArtifactUninstall, } from './internal/artifact-uninstall.js';
20
21
  import { buildSearchHelp, parseArtifactSearchFlags, runArtifactSearch, } from './internal/artifact-search.js';
@@ -110,12 +111,11 @@ async function runPublish(args) {
110
111
  process.stderr.write(`ggui blueprint publish: ${flags.error}\n`);
111
112
  return 2;
112
113
  }
114
+ // Forward the WHOLE parsed-flag bundle — no per-field spreads (a
115
+ // hand-written spread is how `--identity-token` got dropped once).
113
116
  const result = await runArtifactPublish({
114
117
  kind: KIND,
115
- ...(flags.registry !== undefined ? { registry: flags.registry } : {}),
116
- dryRun: flags.dryRun,
117
- ...(flags.key !== undefined ? { key: flags.key } : {}),
118
- ...(flags.auth !== undefined ? { auth: flags.auth } : {}),
118
+ ...publishOptionsFromFlags(flags),
119
119
  });
120
120
  return result.exitCode;
121
121
  }
package/dist/cli.js CHANGED
@@ -615,6 +615,14 @@ async function runServeCommand(args) {
615
615
  return 1;
616
616
  }
617
617
  }
618
+ // Browser-origin allowlist (ggui#438). CLI flags win; the env var is
619
+ // the container/compose ergonomic. Resolved HERE so the banner, the
620
+ // backend, and the server all see the same effective list.
621
+ const envBrowserOrigins = (process.env['GGUI_BROWSER_ORIGINS'] ?? '')
622
+ .split(',')
623
+ .map((o) => o.trim())
624
+ .filter((o) => o.length > 0);
625
+ const browserOrigins = parsed.browserOrigins.length > 0 ? parsed.browserOrigins : envBrowserOrigins;
618
626
  try {
619
627
  return await runServe({
620
628
  flags: {
@@ -625,6 +633,7 @@ async function runServeCommand(args) {
625
633
  publicDemo: parsed.publicDemo,
626
634
  multiTenant: parsed.multiTenant,
627
635
  oauth: parsed.oauth,
636
+ browserOrigins,
628
637
  ...(parsed.publicBaseUrl !== undefined
629
638
  ? { publicBaseUrl: parsed.publicBaseUrl }
630
639
  : {}),
@@ -746,6 +755,7 @@ async function runServeCommand(args) {
746
755
  generation: generationBinding.generation,
747
756
  ...(mcpMounts.length > 0 ? { mcpMounts } : {}),
748
757
  ...(seedPools.length > 0 ? { seedPools } : {}),
758
+ ...(browserOrigins.length > 0 ? { browserOrigins } : {}),
749
759
  // Marketplace-install bridge. Pass the projectRoot +
750
760
  // filtered installed-blueprint subset so `buildMcpServerBackend`
751
761
  // can construct an `InstalledBlueprintsProvider` rooted on the
@@ -1,12 +1,4 @@
1
1
  import { type GadgetDescriptor, type AppTheme } from '@ggui-ai/protocol';
2
- /**
3
- * Extract and validate `app.gadgets` from a decoded ggui.json value.
4
- *
5
- * Returns `[]` when the field is absent.
6
- * Throws with a descriptive message when any descriptor is malformed
7
- * (schema parse fails).
8
- */
9
- export declare function readGadgetsFromGguiJson(gguiJson: unknown): GadgetDescriptor[];
10
2
  /**
11
3
  * Extract and validate `app.publicEnv` from a decoded ggui.json value.
12
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config-push.d.ts","sourceRoot":"","sources":["../src/config-push.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAU3B;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,GAAG,gBAAgB,EAAE,CAqB7E;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,GAAG,SAAS,CAkB7D;AA4BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,GAChB,QAAQ,GAAG,SAAS,CA4CtB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAiB9E;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA8FjB"}
1
+ {"version":3,"file":"config-push.d.ts","sourceRoot":"","sources":["../src/config-push.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAmB3B;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,OAAO,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,GAAG,SAAS,CAkB7D;AA4BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,GAChB,QAAQ,GAAG,SAAS,CA4CtB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAiB9E;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAsB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA2FjB"}
@@ -8,41 +8,18 @@
8
8
  */
9
9
  import process from 'node:process';
10
10
  import { dirname } from 'node:path';
11
- import { strictGadgetDescriptorSchema, appPublicEnvSchema, appThemeSchema, LOOPBACK_HOST_RE, } from '@ggui-ai/protocol';
11
+ import { appPublicEnvSchema, appThemeSchema, LOOPBACK_HOST_RE, } from '@ggui-ai/protocol';
12
12
  import { loadTheme, parseGguiJson } from '@ggui-ai/project-config/node';
13
13
  import { z } from 'zod';
14
14
  import { patchAppConfig } from './api-client.js';
15
- import { findGguiJson, readGguiJson } from './internal/ggui-json.js';
15
+ import { findGguiJson, readGadgetsFromGguiJson, readGguiJson, } from './internal/ggui-json.js';
16
16
  // ─────────────────────────────────────────────────────────────────────────────
17
17
  // Readers
18
18
  // ─────────────────────────────────────────────────────────────────────────────
19
- /**
20
- * Extract and validate `app.gadgets` from a decoded ggui.json value.
21
- *
22
- * Returns `[]` when the field is absent.
23
- * Throws with a descriptive message when any descriptor is malformed
24
- * (schema parse fails).
25
- */
26
- export function readGadgetsFromGguiJson(gguiJson) {
27
- // Navigate to `app.gadgets` via a nested schema rather than casting +
28
- // index access. `z.object` is non-strict by default, so ggui.json's other
29
- // fields (schema / protocol / generation / app.publicEnv / …) ride through
30
- // as allowed-and-stripped extras — only `app.gadgets` is validated here.
31
- const schema = z.object({
32
- app: z
33
- .object({
34
- gadgets: z.array(strictGadgetDescriptorSchema).optional(),
35
- })
36
- .optional(),
37
- });
38
- const result = schema.safeParse(gguiJson);
39
- if (!result.success) {
40
- throw new Error(`ggui.json: app.gadgets is invalid — ${result.error.issues[0]?.message ?? 'malformed descriptor'}`);
41
- }
42
- // result.data.app?.gadgets is GadgetDescriptor[] — strictGadgetDescriptorSchema
43
- // parses to the same shape as GadgetDescriptor. Spread to a fresh mutable array.
44
- return [...(result.data.app?.gadgets ?? [])];
45
- }
19
+ // The gadget-catalog reader lives in `./internal/ggui-json.ts`
20
+ // (`readGadgetsFromGguiJson`) — ONE reader shared with the install
21
+ // write gate so deploy and install cannot drift on optionality or
22
+ // error shape.
46
23
  /**
47
24
  * Extract and validate `app.publicEnv` from a decoded ggui.json value.
48
25
  *
@@ -235,17 +212,15 @@ export async function runConfigPushStep(appId, startDir = process.cwd()) {
235
212
  process.stderr.write(`ggui deploy: ${readResult.error}\n`);
236
213
  return 1;
237
214
  }
238
- let gadgets;
239
215
  let publicEnv;
240
216
  let generation;
241
217
  let theme;
242
- try {
243
- gadgets = readGadgetsFromGguiJson(readResult.value);
244
- }
245
- catch (err) {
246
- process.stderr.write(`ggui deploy: ${err instanceof Error ? err.message : String(err)}\n`);
218
+ const gadgetsRead = readGadgetsFromGguiJson(readResult.value);
219
+ if (!gadgetsRead.ok) {
220
+ process.stderr.write(`ggui deploy: ${gadgetsRead.error}\n`);
247
221
  return 1;
248
222
  }
223
+ const gadgets = gadgetsRead.gadgets;
249
224
  try {
250
225
  publicEnv = readPublicEnvFromGguiJson(readResult.value);
251
226
  }
@@ -1,3 +1,3 @@
1
- export declare const GADGET_HELP = "ggui gadget \u2014 author + manage marketplace gadgets\n\nUsage:\n ggui gadget <subcommand> [options]\n\nSubcommands:\n create Scaffold a new gadget repo.\n publish Build, sign, and push the gadget in CWD to a marketplace registry.\n install Fetch, verify, and register a gadget in ggui.json.\n search Search the marketplace registry for gadgets.\n\nRun `ggui gadget <subcommand> --help` for subcommand-specific options.\n";
1
+ export declare const GADGET_HELP = "ggui gadget \u2014 author + manage marketplace gadgets\n\nUsage:\n ggui gadget <subcommand> [options]\n\nSubcommands:\n create Scaffold a new gadget repo.\n publish Build, sign, and push the gadget in CWD to a marketplace registry.\n install Fetch, verify, and register a gadget in ggui.json.\n uninstall Remove an installed gadget from ggui.json (reverse of install).\n search Search the marketplace registry for gadgets.\n\nRun `ggui gadget <subcommand> --help` for subcommand-specific options.\n";
2
2
  export declare function runGadgetCommand(args: readonly string[]): Promise<number>;
3
3
  //# sourceMappingURL=gadget-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gadget-command.d.ts","sourceRoot":"","sources":["../src/gadget-command.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,WAAW,scAYvB,CAAC;AAEF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
1
+ {"version":3,"file":"gadget-command.d.ts","sourceRoot":"","sources":["../src/gadget-command.ts"],"names":[],"mappings":"AA+CA,eAAO,MAAM,WAAW,shBAavB,CAAC;AAEF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
@@ -1,21 +1,23 @@
1
1
  /**
2
- * `ggui gadget <create|publish|install|search>` — top-level router for
3
- * the marketplace author + consumer surface.
2
+ * `ggui gadget <create|publish|install|uninstall|search>` — top-level
3
+ * router for the marketplace author + consumer surface.
4
4
  *
5
5
  * A thin sibling of `blueprint-command.ts`. Both commands dispatch into
6
6
  * the shared `internal/artifact-*` modules with their own `kind`
7
7
  * discriminator; per-kind state (scaffolders, help text) lives in
8
8
  * sibling files.
9
9
  *
10
- * - `create` — scaffold a new gadget repo (hook + bundle stub).
11
- * - `publish` — build, sign, and push to a registry.
12
- * Manifest-kind mismatch surfaces a redirect.
13
- * - `install` — fetch, verify, append to ggui.json.
14
- * - `search` — query the registry, hard-locked to `kind=gadget`.
10
+ * - `create` — scaffold a new gadget repo (hook + bundle stub).
11
+ * - `publish` — build, sign, and push to a registry.
12
+ * Manifest-kind mismatch surfaces a redirect.
13
+ * - `install` — fetch, verify, append to ggui.json.
14
+ * - `uninstall` — remove the ggui.json#app.gadgets row (reverse of install).
15
+ * - `search` — query the registry, hard-locked to `kind=gadget`.
15
16
  */
16
17
  import { GADGET_CREATE_HELP, parseGadgetCreateFlags, runGadgetCreate, } from './gadget-create.js';
17
- import { buildPublishHelp, parseArtifactPublishFlags, runArtifactPublish, } from './internal/artifact-publish.js';
18
+ import { buildPublishHelp, parseArtifactPublishFlags, publishOptionsFromFlags, runArtifactPublish, } from './internal/artifact-publish.js';
18
19
  import { buildInstallHelp, parseArtifactInstallFlags, runArtifactInstall, } from './internal/artifact-install.js';
20
+ import { buildUninstallHelp, parseArtifactUninstallFlags, runArtifactUninstall, } from './internal/artifact-uninstall.js';
19
21
  import { buildSearchHelp, parseArtifactSearchFlags, runArtifactSearch, } from './internal/artifact-search.js';
20
22
  const KIND = 'gadget';
21
23
  export const GADGET_HELP = `ggui gadget — author + manage marketplace gadgets
@@ -27,6 +29,7 @@ Subcommands:
27
29
  create Scaffold a new gadget repo.
28
30
  publish Build, sign, and push the gadget in CWD to a marketplace registry.
29
31
  install Fetch, verify, and register a gadget in ggui.json.
32
+ uninstall Remove an installed gadget from ggui.json (reverse of install).
30
33
  search Search the marketplace registry for gadgets.
31
34
 
32
35
  Run \`ggui gadget <subcommand> --help\` for subcommand-specific options.
@@ -44,6 +47,8 @@ export async function runGadgetCommand(args) {
44
47
  return runPublish(rest);
45
48
  case 'install':
46
49
  return runInstall(rest);
50
+ case 'uninstall':
51
+ return runUninstall(rest);
47
52
  case 'search':
48
53
  return runSearch(rest);
49
54
  default:
@@ -107,12 +112,11 @@ async function runPublish(args) {
107
112
  process.stderr.write(`ggui gadget publish: ${flags.error}\n`);
108
113
  return 2;
109
114
  }
115
+ // Forward the WHOLE parsed-flag bundle — no per-field spreads (a
116
+ // hand-written spread is how `--identity-token` got dropped once).
110
117
  const result = await runArtifactPublish({
111
118
  kind: KIND,
112
- ...(flags.registry !== undefined ? { registry: flags.registry } : {}),
113
- dryRun: flags.dryRun,
114
- ...(flags.key !== undefined ? { key: flags.key } : {}),
115
- ...(flags.auth !== undefined ? { auth: flags.auth } : {}),
119
+ ...publishOptionsFromFlags(flags),
116
120
  });
117
121
  return result.exitCode;
118
122
  }
@@ -137,6 +141,25 @@ async function runInstall(args) {
137
141
  });
138
142
  }
139
143
  /* -------------------------------------------------------------------------- */
144
+ /* uninstall */
145
+ /* -------------------------------------------------------------------------- */
146
+ async function runUninstall(args) {
147
+ const parsed = parseArtifactUninstallFlags(KIND, args);
148
+ if ('error' in parsed) {
149
+ if (parsed.error === '__help__') {
150
+ process.stdout.write(buildUninstallHelp(KIND));
151
+ return 0;
152
+ }
153
+ process.stderr.write(`ggui gadget uninstall: ${parsed.error}\n\n`);
154
+ process.stderr.write(buildUninstallHelp(KIND));
155
+ return 2;
156
+ }
157
+ const result = await runArtifactUninstall(parsed, {
158
+ cwd: process.cwd(),
159
+ });
160
+ return result.exitCode;
161
+ }
162
+ /* -------------------------------------------------------------------------- */
140
163
  /* search */
141
164
  /* -------------------------------------------------------------------------- */
142
165
  async function runSearch(args) {
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-install.d.ts","sourceRoot":"","sources":["../../src/internal/artifact-install.ts"],"names":[],"mappings":"AA6EA,OAAO,EAAoB,KAAK,YAAY,EAAsB,MAAM,wBAAwB,CAAC;AAQjG;;;sCAGsC;AACtC,YAAY,EAAE,YAAY,EAAE,CAAC;AAqB7B;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAgD3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,kBAAkB,CAsGpB;AA6DD;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kCAAkC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACrE,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACjE;AAmMD;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,MAAM,CAAC,CA8XjB;AAmID;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,+BAA+B,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,8CACqB,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAOtE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAGT"}
1
+ {"version":3,"file":"artifact-install.d.ts","sourceRoot":"","sources":["../../src/internal/artifact-install.ts"],"names":[],"mappings":"AAyFA,OAAO,EAAoB,KAAK,YAAY,EAAsB,MAAM,wBAAwB,CAAC;AAWjG;;;sCAGsC;AACtC,YAAY,EAAE,YAAY,EAAE,CAAC;AAM7B;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAgD3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,kBAAkB,CAsGpB;AA6DD;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kCAAkC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACrE,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CACjE;AA8ID;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,MAAM,CAAC,CAidjB;AA0JD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,+BAA+B,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,8CACqB,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAOtE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAGT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAGT"}