@ggui-ai/cli 0.1.0-rc.1

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 (117) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +96 -0
  3. package/dist/agent-resolution.d.ts +41 -0
  4. package/dist/agent-resolution.d.ts.map +1 -0
  5. package/dist/agent-resolution.js +80 -0
  6. package/dist/api-client.d.ts +59 -0
  7. package/dist/api-client.d.ts.map +1 -0
  8. package/dist/api-client.js +145 -0
  9. package/dist/auth-keys.d.ts +10 -0
  10. package/dist/auth-keys.d.ts.map +1 -0
  11. package/dist/auth-keys.js +382 -0
  12. package/dist/auth-login.d.ts +11 -0
  13. package/dist/auth-login.d.ts.map +1 -0
  14. package/dist/auth-login.js +231 -0
  15. package/dist/auth-logout.d.ts +3 -0
  16. package/dist/auth-logout.d.ts.map +1 -0
  17. package/dist/auth-logout.js +32 -0
  18. package/dist/auth-store.d.ts +67 -0
  19. package/dist/auth-store.d.ts.map +1 -0
  20. package/dist/auth-store.js +150 -0
  21. package/dist/auth-whoami.d.ts +3 -0
  22. package/dist/auth-whoami.d.ts.map +1 -0
  23. package/dist/auth-whoami.js +52 -0
  24. package/dist/blueprint-command.d.ts +3 -0
  25. package/dist/blueprint-command.d.ts.map +1 -0
  26. package/dist/blueprint-command.js +203 -0
  27. package/dist/blueprint-create.d.ts +71 -0
  28. package/dist/blueprint-create.d.ts.map +1 -0
  29. package/dist/blueprint-create.js +377 -0
  30. package/dist/byok-resolver.d.ts +141 -0
  31. package/dist/byok-resolver.d.ts.map +1 -0
  32. package/dist/byok-resolver.js +89 -0
  33. package/dist/cli.d.ts +3 -0
  34. package/dist/cli.d.ts.map +1 -0
  35. package/dist/cli.js +1066 -0
  36. package/dist/dev-command.d.ts +169 -0
  37. package/dist/dev-command.d.ts.map +1 -0
  38. package/dist/dev-command.js +222 -0
  39. package/dist/gadget-command.d.ts +3 -0
  40. package/dist/gadget-command.d.ts.map +1 -0
  41. package/dist/gadget-command.js +181 -0
  42. package/dist/gadget-create.d.ts +106 -0
  43. package/dist/gadget-create.d.ts.map +1 -0
  44. package/dist/gadget-create.js +492 -0
  45. package/dist/generation-probe.d.ts +135 -0
  46. package/dist/generation-probe.d.ts.map +1 -0
  47. package/dist/generation-probe.js +132 -0
  48. package/dist/internal/artifact-install.d.ts +150 -0
  49. package/dist/internal/artifact-install.d.ts.map +1 -0
  50. package/dist/internal/artifact-install.js +1019 -0
  51. package/dist/internal/artifact-publish.d.ts +204 -0
  52. package/dist/internal/artifact-publish.d.ts.map +1 -0
  53. package/dist/internal/artifact-publish.js +1141 -0
  54. package/dist/internal/artifact-search.d.ts +155 -0
  55. package/dist/internal/artifact-search.d.ts.map +1 -0
  56. package/dist/internal/artifact-search.js +539 -0
  57. package/dist/internal/artifact-uninstall.d.ts +30 -0
  58. package/dist/internal/artifact-uninstall.d.ts.map +1 -0
  59. package/dist/internal/artifact-uninstall.js +205 -0
  60. package/dist/internal/auth-cache.d.ts +40 -0
  61. package/dist/internal/auth-cache.d.ts.map +1 -0
  62. package/dist/internal/auth-cache.js +133 -0
  63. package/dist/internal/auth-strategy.d.ts +89 -0
  64. package/dist/internal/auth-strategy.d.ts.map +1 -0
  65. package/dist/internal/auth-strategy.js +132 -0
  66. package/dist/internal/ggui-json.d.ts +53 -0
  67. package/dist/internal/ggui-json.d.ts.map +1 -0
  68. package/dist/internal/ggui-json.js +92 -0
  69. package/dist/internal/key-store.d.ts +43 -0
  70. package/dist/internal/key-store.d.ts.map +1 -0
  71. package/dist/internal/key-store.js +128 -0
  72. package/dist/internal/oidc-token.d.ts +52 -0
  73. package/dist/internal/oidc-token.d.ts.map +1 -0
  74. package/dist/internal/oidc-token.js +351 -0
  75. package/dist/internal/register-author-key.d.ts +44 -0
  76. package/dist/internal/register-author-key.d.ts.map +1 -0
  77. package/dist/internal/register-author-key.js +212 -0
  78. package/dist/local-keys.d.ts +47 -0
  79. package/dist/local-keys.d.ts.map +1 -0
  80. package/dist/local-keys.js +115 -0
  81. package/dist/mcp-backend.d.ts +356 -0
  82. package/dist/mcp-backend.d.ts.map +1 -0
  83. package/dist/mcp-backend.js +668 -0
  84. package/dist/paths.d.ts +77 -0
  85. package/dist/paths.d.ts.map +1 -0
  86. package/dist/paths.js +119 -0
  87. package/dist/persistent-secrets.d.ts +28 -0
  88. package/dist/persistent-secrets.d.ts.map +1 -0
  89. package/dist/persistent-secrets.js +94 -0
  90. package/dist/persistent-short-code-index.d.ts +14 -0
  91. package/dist/persistent-short-code-index.d.ts.map +1 -0
  92. package/dist/persistent-short-code-index.js +39 -0
  93. package/dist/persistent-stores.d.ts +12 -0
  94. package/dist/persistent-stores.d.ts.map +1 -0
  95. package/dist/persistent-stores.js +42 -0
  96. package/dist/serve-command.d.ts +500 -0
  97. package/dist/serve-command.d.ts.map +1 -0
  98. package/dist/serve-command.js +616 -0
  99. package/dist/serve-fallback.d.ts +64 -0
  100. package/dist/serve-fallback.d.ts.map +1 -0
  101. package/dist/serve-fallback.js +122 -0
  102. package/dist/spawn-cloudflared.d.ts +61 -0
  103. package/dist/spawn-cloudflared.d.ts.map +1 -0
  104. package/dist/spawn-cloudflared.js +167 -0
  105. package/dist/theme-command.d.ts +4 -0
  106. package/dist/theme-command.d.ts.map +1 -0
  107. package/dist/theme-command.js +154 -0
  108. package/dist/theme-file-uploader.d.ts +8 -0
  109. package/dist/theme-file-uploader.d.ts.map +1 -0
  110. package/dist/theme-file-uploader.js +58 -0
  111. package/dist/theme-writer.d.ts +8 -0
  112. package/dist/theme-writer.d.ts.map +1 -0
  113. package/dist/theme-writer.js +103 -0
  114. package/dist/tunnel-provider.d.ts +273 -0
  115. package/dist/tunnel-provider.d.ts.map +1 -0
  116. package/dist/tunnel-provider.js +142 -0
  117. package/package.json +81 -0
@@ -0,0 +1,67 @@
1
+ /**
2
+ * v1 on-disk document. Bumped on breaking changes; loaders that
3
+ * encounter `version > 1` should refuse to read and prompt the user
4
+ * to upgrade the CLI.
5
+ */
6
+ export interface AuthSessionDocument {
7
+ readonly version: 1;
8
+ /** API endpoint these tokens are bound to (origin only, no path). */
9
+ readonly endpoint: string;
10
+ /** Cognito sub of the authenticated user. */
11
+ readonly userId: string;
12
+ /** Stable session id grouping access + refresh on the server. */
13
+ readonly sessionId: string;
14
+ /** `cli_at_*` bearer. Send as `Authorization: Bearer <accessToken>`. */
15
+ readonly accessToken: string;
16
+ /** Unix epoch SECONDS — used to decide when to proactively refresh. */
17
+ readonly accessExpiresAt: number;
18
+ /** `cli_rt_*` bearer. Used by /v1/auth/refresh. */
19
+ readonly refreshToken: string;
20
+ /** Unix epoch SECONDS — past this, the user must `ggui login` again. */
21
+ readonly refreshExpiresAt: number;
22
+ /** Free-form label the CLI provided at login (e.g. host name). */
23
+ readonly clientName: string;
24
+ /** ISO 8601 timestamp of when this document was written. */
25
+ readonly writtenAt: string;
26
+ }
27
+ /** Resolution of the API endpoint, with explicit precedence. */
28
+ export interface ResolvedEndpoint {
29
+ readonly url: string;
30
+ readonly source: 'env' | 'auth.json' | 'default';
31
+ }
32
+ /**
33
+ * Resolve which endpoint a command should use.
34
+ *
35
+ * Precedence:
36
+ * 1. `GGUI_API_URL` env var — operator override (dev / sandbox testing).
37
+ * 2. `~/.ggui/auth.json#endpoint` — captured at last `ggui login`.
38
+ * 3. `https://api.ggui.ai` — production default.
39
+ *
40
+ * Used by `ggui login` (where there's no auth.json yet) and by every
41
+ * authenticated command (where auth.json holds the canonical value).
42
+ */
43
+ export declare function resolveEndpoint(): ResolvedEndpoint;
44
+ /**
45
+ * Load the auth.json document. Returns null if missing OR malformed
46
+ * (unknown version, missing fields, parse error). Callers print
47
+ * "ggui login" prompt on null rather than crash.
48
+ */
49
+ export declare function tryLoadAuthSession(): AuthSessionDocument | null;
50
+ /**
51
+ * Stricter loader — throws with a friendly message for the
52
+ * authenticated-command path. CLI surfaces the message + exit 1.
53
+ */
54
+ export declare function loadAuthSession(): AuthSessionDocument;
55
+ /**
56
+ * Atomically write the document with mode `0o600`. Uses the
57
+ * write-temp + rename pattern so a crash mid-write doesn't leave a
58
+ * partial document.
59
+ */
60
+ export declare function saveAuthSession(doc: AuthSessionDocument): void;
61
+ /**
62
+ * Delete the auth.json file. No-op if missing. Used by `ggui logout`.
63
+ */
64
+ export declare function deleteAuthSession(): {
65
+ deleted: boolean;
66
+ };
67
+ //# sourceMappingURL=auth-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-store.d.ts","sourceRoot":"","sources":["../src/auth-store.ts"],"names":[],"mappings":"AAoCA;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;CAClD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAUlD;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,mBAAmB,GAAG,IAAI,CAiB/D;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,mBAAmB,CAQrD;AAmBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,mBAAmB,GAAG,IAAI,CA2B9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAKxD"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `~/.ggui/auth.json` reader / writer for the OSS `ggui` CLI.
3
+ *
4
+ * The file is written by `ggui login` after a successful device-flow
5
+ * exchange against `api.ggui.ai` (S2 endpoints). All authenticated
6
+ * subcommands (`whoami`, `keys *`) read it via {@link loadAuthSession}.
7
+ *
8
+ * On-disk shape is `AuthSessionDocument` v1 — a small JSON document
9
+ * with the access + refresh bearers, their expiry timestamps, the
10
+ * userId the tokens are bound to, and the API endpoint used at login
11
+ * time. Mode `0o600` is enforced on every write — the tokens are
12
+ * bearer secrets equivalent to a short-lived password.
13
+ *
14
+ * This file is distinct from `~/.ggui/credentials.json` (BYOK
15
+ * provider keys, see `paths.ts`). The two coexist; one stores
16
+ * provider-API keys for local generation, the other stores ggui.ai
17
+ * session bearers for hosted-key management.
18
+ */
19
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
20
+ import { getAuthFile, getConfigDir } from './paths.js';
21
+ /**
22
+ * Default endpoint URL — `api.ggui.ai` once Route53 + ACM land (S6).
23
+ * Until then the operator overrides via `GGUI_API_URL` env to point
24
+ * at the deployed exec-api URL surfaced in `amplify_outputs.json`.
25
+ */
26
+ const DEFAULT_API_URL = 'https://api.ggui.ai';
27
+ /**
28
+ * Resolve which endpoint a command should use.
29
+ *
30
+ * Precedence:
31
+ * 1. `GGUI_API_URL` env var — operator override (dev / sandbox testing).
32
+ * 2. `~/.ggui/auth.json#endpoint` — captured at last `ggui login`.
33
+ * 3. `https://api.ggui.ai` — production default.
34
+ *
35
+ * Used by `ggui login` (where there's no auth.json yet) and by every
36
+ * authenticated command (where auth.json holds the canonical value).
37
+ */
38
+ export function resolveEndpoint() {
39
+ const envOverride = process.env['GGUI_API_URL'];
40
+ if (envOverride && envOverride.length > 0) {
41
+ return { url: stripTrailingSlash(envOverride), source: 'env' };
42
+ }
43
+ const session = tryLoadAuthSession();
44
+ if (session) {
45
+ return { url: stripTrailingSlash(session.endpoint), source: 'auth.json' };
46
+ }
47
+ return { url: DEFAULT_API_URL, source: 'default' };
48
+ }
49
+ function stripTrailingSlash(url) {
50
+ return url.endsWith('/') ? url.slice(0, -1) : url;
51
+ }
52
+ /**
53
+ * Load the auth.json document. Returns null if missing OR malformed
54
+ * (unknown version, missing fields, parse error). Callers print
55
+ * "ggui login" prompt on null rather than crash.
56
+ */
57
+ export function tryLoadAuthSession() {
58
+ const path = getAuthFile();
59
+ if (!existsSync(path))
60
+ return null;
61
+ let raw;
62
+ try {
63
+ raw = readFileSync(path, 'utf8');
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(raw);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ if (!isAuthSessionDocument(parsed))
76
+ return null;
77
+ return parsed;
78
+ }
79
+ /**
80
+ * Stricter loader — throws with a friendly message for the
81
+ * authenticated-command path. CLI surfaces the message + exit 1.
82
+ */
83
+ export function loadAuthSession() {
84
+ const session = tryLoadAuthSession();
85
+ if (!session) {
86
+ throw new Error('No active ggui session. Run `ggui login` first.');
87
+ }
88
+ return session;
89
+ }
90
+ function isAuthSessionDocument(x) {
91
+ if (typeof x !== 'object' || x === null)
92
+ return false;
93
+ const r = x;
94
+ return (r['version'] === 1 &&
95
+ typeof r['endpoint'] === 'string' &&
96
+ typeof r['userId'] === 'string' &&
97
+ typeof r['sessionId'] === 'string' &&
98
+ typeof r['accessToken'] === 'string' &&
99
+ typeof r['accessExpiresAt'] === 'number' &&
100
+ typeof r['refreshToken'] === 'string' &&
101
+ typeof r['refreshExpiresAt'] === 'number' &&
102
+ typeof r['clientName'] === 'string' &&
103
+ typeof r['writtenAt'] === 'string');
104
+ }
105
+ /**
106
+ * Atomically write the document with mode `0o600`. Uses the
107
+ * write-temp + rename pattern so a crash mid-write doesn't leave a
108
+ * partial document.
109
+ */
110
+ export function saveAuthSession(doc) {
111
+ const dir = getConfigDir();
112
+ if (!existsSync(dir)) {
113
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
114
+ }
115
+ else {
116
+ chmodSync(dir, 0o700);
117
+ }
118
+ const target = getAuthFile();
119
+ const tmp = `${target}.tmp-${process.pid}-${Date.now()}`;
120
+ // Write JSON pretty-printed for human-readable inspection.
121
+ writeFileSync(tmp, `${JSON.stringify(doc, null, 2)}\n`, {
122
+ encoding: 'utf8',
123
+ mode: 0o600,
124
+ });
125
+ // chmod the tmp file in case the writeFileSync mode arg was ignored
126
+ // by the platform (Windows tolerates 0o600 silently as 0o666).
127
+ try {
128
+ chmodSync(tmp, 0o600);
129
+ }
130
+ catch {
131
+ // best-effort
132
+ }
133
+ renameSync(tmp, target);
134
+ try {
135
+ chmodSync(target, 0o600);
136
+ }
137
+ catch {
138
+ // best-effort
139
+ }
140
+ }
141
+ /**
142
+ * Delete the auth.json file. No-op if missing. Used by `ggui logout`.
143
+ */
144
+ export function deleteAuthSession() {
145
+ const path = getAuthFile();
146
+ if (!existsSync(path))
147
+ return { deleted: false };
148
+ unlinkSync(path);
149
+ return { deleted: true };
150
+ }
@@ -0,0 +1,3 @@
1
+ export declare const WHOAMI_HELP = "ggui whoami \u2014 print the authenticated user\n\nUsage:\n ggui whoami\n\nReads the local session from ~/.ggui/auth.json and calls /v1/me.\n";
2
+ export declare function runWhoamiCommand(args: readonly string[]): Promise<number>;
3
+ //# sourceMappingURL=auth-whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-whoami.d.ts","sourceRoot":"","sources":["../src/auth-whoami.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,WAAW,mJAMvB,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC/E"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `ggui whoami` — print the authenticated user's profile.
3
+ *
4
+ * Reads `~/.ggui/auth.json`, calls GET /v1/me with the access bearer,
5
+ * and pretty-prints the result. On 401, the api-client transparently
6
+ * refreshes via /v1/auth/refresh; if that also fails the user gets
7
+ * a "Run `ggui login` again" message and exit 1.
8
+ */
9
+ import { ApiError, getMe } from './api-client.js';
10
+ import { tryLoadAuthSession } from './auth-store.js';
11
+ export const WHOAMI_HELP = `ggui whoami — print the authenticated user
12
+
13
+ Usage:
14
+ ggui whoami
15
+
16
+ Reads the local session from ~/.ggui/auth.json and calls /v1/me.
17
+ `;
18
+ export async function runWhoamiCommand(args) {
19
+ if (args.includes('--help') || args.includes('-h')) {
20
+ process.stdout.write(WHOAMI_HELP);
21
+ return 0;
22
+ }
23
+ const session = tryLoadAuthSession();
24
+ if (!session) {
25
+ process.stderr.write(`Not signed in. Run \`ggui login\` first.\n`);
26
+ return 1;
27
+ }
28
+ try {
29
+ const me = await getMe();
30
+ process.stdout.write(`User ID: ${me.userId}\n`);
31
+ process.stdout.write(`Session ID: ${me.sessionId}\n`);
32
+ if (me.clientName) {
33
+ process.stdout.write(`Client: ${me.clientName}\n`);
34
+ }
35
+ const expiresAt = new Date(me.accessExpiresAt * 1000).toISOString();
36
+ process.stdout.write(`Access expires: ${expiresAt}\n`);
37
+ process.stdout.write(`Endpoint: ${session.endpoint}\n`);
38
+ return 0;
39
+ }
40
+ catch (err) {
41
+ if (err instanceof ApiError) {
42
+ process.stderr.write(`ggui whoami: ${err.code}: ${err.message}\n`);
43
+ }
44
+ else if (err instanceof Error) {
45
+ process.stderr.write(`ggui whoami: ${err.message}\n`);
46
+ }
47
+ else {
48
+ process.stderr.write(`ggui whoami: unknown error\n`);
49
+ }
50
+ return 1;
51
+ }
52
+ }
@@ -0,0 +1,3 @@
1
+ export declare const BLUEPRINT_HELP = "ggui blueprint \u2014 author + manage marketplace blueprints\n\nUsage:\n ggui blueprint <subcommand> [options]\n\nSubcommands:\n create Scaffold a new blueprint repo.\n publish Build, sign, and push the blueprint in CWD to a marketplace registry.\n install Fetch, verify, and materialize a blueprint locally.\n uninstall Remove a locally-installed blueprint (reverse of install).\n search Search the marketplace registry for blueprints.\n\nRun `ggui blueprint <subcommand> --help` for subcommand-specific options.\n";
2
+ export declare function runBlueprintCommand(args: readonly string[]): Promise<number>;
3
+ //# sourceMappingURL=blueprint-command.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * `ggui blueprint <create|publish|install|search>` — kind-discriminated
3
+ * sibling of `gadget-command.ts`.
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`.
11
+ *
12
+ * A thin router that passes `kind: 'blueprint'` to the shared
13
+ * `internal/artifact-*` implementations. Per-kind state (scaffolders,
14
+ * help text) lives in sibling files; the rest is shared.
15
+ */
16
+ import { BLUEPRINT_CREATE_HELP, parseBlueprintCreateFlags, runBlueprintCreate, } from './blueprint-create.js';
17
+ import { buildPublishHelp, parseArtifactPublishFlags, runArtifactPublish, } from './internal/artifact-publish.js';
18
+ import { buildInstallHelp, parseArtifactInstallFlags, runArtifactInstall, } from './internal/artifact-install.js';
19
+ import { buildUninstallHelp, parseArtifactUninstallFlags, runArtifactUninstall, } from './internal/artifact-uninstall.js';
20
+ import { buildSearchHelp, parseArtifactSearchFlags, runArtifactSearch, } from './internal/artifact-search.js';
21
+ const KIND = 'blueprint';
22
+ export const BLUEPRINT_HELP = `ggui blueprint — author + manage marketplace blueprints
23
+
24
+ Usage:
25
+ ggui blueprint <subcommand> [options]
26
+
27
+ Subcommands:
28
+ create Scaffold a new blueprint repo.
29
+ publish Build, sign, and push the blueprint in CWD to a marketplace registry.
30
+ install Fetch, verify, and materialize a blueprint locally.
31
+ uninstall Remove a locally-installed blueprint (reverse of install).
32
+ search Search the marketplace registry for blueprints.
33
+
34
+ Run \`ggui blueprint <subcommand> --help\` for subcommand-specific options.
35
+ `;
36
+ export async function runBlueprintCommand(args) {
37
+ const [sub, ...rest] = args;
38
+ if (!sub || sub === '--help' || sub === '-h') {
39
+ process.stdout.write(BLUEPRINT_HELP);
40
+ return sub ? 0 : 2;
41
+ }
42
+ switch (sub) {
43
+ case 'create':
44
+ return runCreate(rest);
45
+ case 'publish':
46
+ return runPublish(rest);
47
+ case 'install':
48
+ return runInstall(rest);
49
+ case 'uninstall':
50
+ return runUninstall(rest);
51
+ case 'search':
52
+ return runSearch(rest);
53
+ default:
54
+ process.stderr.write(`ggui blueprint: unknown subcommand "${sub}"\n\n`);
55
+ process.stderr.write(BLUEPRINT_HELP);
56
+ return 2;
57
+ }
58
+ }
59
+ /* -------------------------------------------------------------------------- */
60
+ /* create */
61
+ /* -------------------------------------------------------------------------- */
62
+ async function runCreate(args) {
63
+ const parsed = parseBlueprintCreateFlags(args);
64
+ if (parsed.error === '__help__') {
65
+ process.stdout.write(BLUEPRINT_CREATE_HELP);
66
+ return 0;
67
+ }
68
+ if (parsed.error) {
69
+ process.stderr.write(`ggui blueprint create: ${parsed.error}\n\n`);
70
+ process.stderr.write(BLUEPRINT_CREATE_HELP);
71
+ return 2;
72
+ }
73
+ if (!parsed.flags) {
74
+ process.stderr.write(`ggui blueprint create: failed to parse flags\n`);
75
+ return 2;
76
+ }
77
+ const result = await runBlueprintCreate(parsed.flags, { cwd: process.cwd() });
78
+ return renderCreateResult(result);
79
+ }
80
+ function renderCreateResult(result) {
81
+ if (!result.ok) {
82
+ process.stderr.write(`ggui blueprint create: ${result.message}\n`);
83
+ return 1;
84
+ }
85
+ process.stdout.write(`\n`);
86
+ process.stdout.write(`Scaffolded blueprint at ${result.targetDir}\n\n`);
87
+ process.stdout.write(`Files written:\n`);
88
+ for (const f of result.files) {
89
+ process.stdout.write(` - ${f}\n`);
90
+ }
91
+ process.stdout.write(`\n`);
92
+ process.stdout.write(`Identifier: ${result.manifest.scope}/${result.manifest.name}@${result.manifest.version}\n`);
93
+ process.stdout.write(`\nNext steps:\n`);
94
+ process.stdout.write(` cd ${result.targetDir}\n`);
95
+ process.stdout.write(` pnpm install\n`);
96
+ process.stdout.write(` # implement the component in src/blueprint.tsx, then:\n`);
97
+ process.stdout.write(` ggui blueprint publish\n\n`);
98
+ return 0;
99
+ }
100
+ /* -------------------------------------------------------------------------- */
101
+ /* publish */
102
+ /* -------------------------------------------------------------------------- */
103
+ async function runPublish(args) {
104
+ const flags = parseArtifactPublishFlags(args);
105
+ if (flags.help) {
106
+ process.stdout.write(buildPublishHelp(KIND));
107
+ return 0;
108
+ }
109
+ if (flags.error) {
110
+ process.stderr.write(`ggui blueprint publish: ${flags.error}\n`);
111
+ return 2;
112
+ }
113
+ const result = await runArtifactPublish({
114
+ 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 } : {}),
119
+ });
120
+ return result.exitCode;
121
+ }
122
+ /* -------------------------------------------------------------------------- */
123
+ /* install */
124
+ /* -------------------------------------------------------------------------- */
125
+ async function runInstall(args) {
126
+ const parsed = parseArtifactInstallFlags(KIND, args);
127
+ if ('error' in parsed) {
128
+ if (parsed.error === '__help__') {
129
+ process.stdout.write(buildInstallHelp(KIND));
130
+ return 0;
131
+ }
132
+ process.stderr.write(`ggui blueprint install: ${parsed.error}\n\n`);
133
+ process.stderr.write(buildInstallHelp(KIND));
134
+ return 2;
135
+ }
136
+ return runArtifactInstall(parsed, {
137
+ cwd: process.cwd(),
138
+ env: process.env,
139
+ fetch: globalThis.fetch.bind(globalThis),
140
+ });
141
+ }
142
+ /* -------------------------------------------------------------------------- */
143
+ /* uninstall */
144
+ /* -------------------------------------------------------------------------- */
145
+ async function runUninstall(args) {
146
+ const parsed = parseArtifactUninstallFlags(KIND, args);
147
+ if ('error' in parsed) {
148
+ if (parsed.error === '__help__') {
149
+ process.stdout.write(buildUninstallHelp(KIND));
150
+ return 0;
151
+ }
152
+ process.stderr.write(`ggui blueprint uninstall: ${parsed.error}\n\n`);
153
+ process.stderr.write(buildUninstallHelp(KIND));
154
+ return 2;
155
+ }
156
+ const result = await runArtifactUninstall(parsed, {
157
+ cwd: process.cwd(),
158
+ });
159
+ return result.exitCode;
160
+ }
161
+ /* -------------------------------------------------------------------------- */
162
+ /* search */
163
+ /* -------------------------------------------------------------------------- */
164
+ async function runSearch(args) {
165
+ const parsed = parseArtifactSearchFlags(KIND, args);
166
+ if (parsed.error === '__help__') {
167
+ process.stdout.write(buildSearchHelp(KIND));
168
+ return 0;
169
+ }
170
+ if (parsed.error) {
171
+ process.stderr.write(`ggui blueprint search: ${parsed.error}\n\n`);
172
+ process.stderr.write(buildSearchHelp(KIND));
173
+ return 2;
174
+ }
175
+ if (!parsed.flags) {
176
+ process.stderr.write(`ggui blueprint search: failed to parse flags\n`);
177
+ return 2;
178
+ }
179
+ const result = await runArtifactSearch(parsed.flags, {
180
+ cwd: process.cwd(),
181
+ env: { GGUI_REGISTRY: process.env['GGUI_REGISTRY'] },
182
+ fetch: globalThis.fetch.bind(globalThis),
183
+ });
184
+ return renderSearchResult(result, parsed.flags.json === true);
185
+ }
186
+ function renderSearchResult(result, json) {
187
+ if (!result.ok) {
188
+ process.stderr.write(`ggui blueprint search: ${result.message}\n`);
189
+ return 1;
190
+ }
191
+ if (json) {
192
+ process.stdout.write(`${result.json}\n`);
193
+ return 0;
194
+ }
195
+ if (result.response.results.length === 0) {
196
+ process.stderr.write('No blueprints found matching the supplied criteria.\n');
197
+ return 0;
198
+ }
199
+ for (const line of result.lines) {
200
+ process.stdout.write(`${line}\n`);
201
+ }
202
+ return 0;
203
+ }
@@ -0,0 +1,71 @@
1
+ import { type BlueprintManifest } from '@ggui-ai/artifact-manifest';
2
+ /**
3
+ * Flag bundle accepted by {@link runBlueprintCreate}. Mirrors the CLI's
4
+ * positional + flag surface; pulled out so `blueprint-command.ts` can
5
+ * build it from `process.argv` and tests can build it from object literals.
6
+ */
7
+ export interface BlueprintCreateFlags {
8
+ /** Positional `<scope>/<name>`. Validated in {@link runBlueprintCreate}. */
9
+ readonly scopeName: string;
10
+ /**
11
+ * Target directory. Defaults to the `name` portion of `scopeName`
12
+ * (i.e. `@my-org/login-form` → `./login-form`). Resolved relative
13
+ * to `cwd` at write time.
14
+ */
15
+ readonly dir?: string;
16
+ /** Optional one-line `description` for the manifest. */
17
+ readonly description?: string;
18
+ /** Manifest `visibility`. Defaults to {@link DEFAULT_VISIBILITY}. */
19
+ readonly visibility?: BlueprintManifest['visibility'];
20
+ }
21
+ export interface ParsedBlueprintCreateFlags {
22
+ readonly flags?: BlueprintCreateFlags;
23
+ /** `'__help__'` for `--help`; other strings = usage error. */
24
+ readonly error?: string;
25
+ }
26
+ /**
27
+ * Parse the `ggui blueprint create` flag tail. Returns a discriminated
28
+ * `{error}` vs. successful flags so the caller doesn't throw on user
29
+ * input.
30
+ */
31
+ export declare function parseBlueprintCreateFlags(args: readonly string[]): ParsedBlueprintCreateFlags;
32
+ /**
33
+ * Build + validate the {@link BlueprintManifest} BEFORE any write.
34
+ * A template regression that drifts (e.g. drops `source`) blows up
35
+ * here, before any file lands on disk, so authors don't see a corrupt
36
+ * scaffold.
37
+ */
38
+ export declare function buildScaffoldManifest(args: {
39
+ scope: string;
40
+ name: string;
41
+ visibility: BlueprintManifest['visibility'];
42
+ description?: string;
43
+ source: string;
44
+ }): BlueprintManifest;
45
+ /** Top-level scaffolder result — mirror of `GadgetCreateResult`. */
46
+ export interface BlueprintCreateSuccess {
47
+ readonly ok: true;
48
+ /** Absolute path to the scaffolded blueprint directory. */
49
+ readonly targetDir: string;
50
+ /** Files written, relative to `targetDir`. */
51
+ readonly files: readonly string[];
52
+ /** Resolved manifest — useful for callers that want to chain on it. */
53
+ readonly manifest: BlueprintManifest;
54
+ }
55
+ export interface BlueprintCreateFailure {
56
+ readonly ok: false;
57
+ readonly code: 'invalid-scope-name' | 'invalid-name' | 'target-not-empty' | 'manifest-invalid' | 'fs-error';
58
+ readonly message: string;
59
+ }
60
+ export type BlueprintCreateResult = BlueprintCreateSuccess | BlueprintCreateFailure;
61
+ /**
62
+ * Run the blueprint scaffold end-to-end. Resolves the target dir,
63
+ * builds + validates the manifest, writes files. Returns a structured
64
+ * result so the CLI driver can render copy + exit code without
65
+ * re-deriving paths.
66
+ */
67
+ export declare function runBlueprintCreate(flags: BlueprintCreateFlags, options: {
68
+ cwd: string;
69
+ }): Promise<BlueprintCreateResult>;
70
+ export declare const BLUEPRINT_CREATE_HELP = "ggui blueprint create \u2014 scaffold a new blueprint repo\n\nUsage:\n ggui blueprint create <scope/name> [options]\n\nArguments:\n <scope/name> Required. `@my-org/login-form` style identifier.\n The scope (with leading `@`) becomes the manifest's\n `scope`; the segment after `/` becomes the `name`\n (must match `^[a-z0-9][a-z0-9_-]{0,63}$`).\n\nOptions:\n --dir <path> Target directory. Defaults to `./<name>`.\n --description <text> One-line description threaded into the\n manifest + README.\n --visibility <p> `public` (default, sigstore-signed) or\n `private` (Ed25519-signed, org-only).\n --help, -h Show this help.\n\nOutput:\n <dir>/ggui.blueprint.json Strict-schema manifest (validated before write).\n <dir>/package.json Workspace shell.\n <dir>/src/blueprint.tsx Default-exported React component stub.\n <dir>/src/contract.ts Matching `DataContract` stub.\n <dir>/README.md One-paragraph orientation.\n\nAfter scaffold:\n cd <dir> && pnpm install\n # implement the component in src/blueprint.tsx, then:\n ggui blueprint publish # publishes to the resolved registry.\n";
71
+ //# sourceMappingURL=blueprint-create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blueprint-create.d.ts","sourceRoot":"","sources":["../src/blueprint-create.ts"],"names":[],"mappings":"AA2BA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,4BAA4B,CAAC;AASpC;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IACtC,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,0BAA0B,CA0D5B;AAkED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,iBAAiB,CAepB;AAED,oEAAoE;AACpE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,IAAI,EACT,oBAAoB,GACpB,cAAc,GACd,kBAAkB,GAClB,kBAAkB,GAClB,UAAU,CAAC;IACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,qBAAqB,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;AAiFpF;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACvB,OAAO,CAAC,qBAAqB,CAAC,CAwIhC;AAED,eAAO,MAAM,qBAAqB,owCA8BjC,CAAC"}