@sprigr/cli 0.1.2

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.
@@ -0,0 +1,539 @@
1
+ /**
2
+ * `sprigr app publish` and `sprigr app validate`.
3
+ *
4
+ * Phase 2.1b of the ASCS platform implementation plan.
5
+ *
6
+ * sprigr app publish --dir <bundle-dir> [--manifest <path>]
7
+ * Reads the manifest, runs publish-time validation locally, bundles
8
+ * the directory the same way `sprigr deploy` does, and POSTs to
9
+ * /api/v1/data/marketplace/apps/publish. The server re-validates +
10
+ * stores the version + triggers the build-runner. We intentionally
11
+ * run validation client-side too — the publish API is slow (uploads
12
+ * a bundle to R2) so a clear local error beats a server round-trip.
13
+ *
14
+ * sprigr app validate --dir <bundle-dir> [--manifest <path>]
15
+ * Same validation as publish, no upload. Suitable for CI / local dev.
16
+ *
17
+ * Manifest format note (v1):
18
+ * The plan calls for sprigr-app.yaml. The CLI v1 ships with JSON
19
+ * support only (sprigr-app.json) so the npm-published bundle stays
20
+ * dependency-light. YAML support is a follow-up that adds the `yaml`
21
+ * package and detects sprigr-app.yaml in addition to sprigr-app.json.
22
+ * Server-side handling is unchanged either way — the wire payload is
23
+ * always JSON.
24
+ *
25
+ * Why the manifest is read separately from the bundle dir:
26
+ * The bundler walks the dir and uploads every file under it, so the
27
+ * manifest naturally lands in the bundle too — but we ALSO need the
28
+ * parsed manifest object to validate locally and to send as a separate
29
+ * field on the publish payload (the server reads it from there to
30
+ * route the build, not by parsing the bundle). Hence a separate read.
31
+ */
32
+ import { promises as fs } from 'node:fs';
33
+ import path from 'node:path';
34
+ import { validateAppDependencies, validateCrossTenantTools, validateMigrations, validateRuntimeTier, validateSchedules, validateAgentSchedules, validateSecrets, validateWebhooks, } from '../manifest.js';
35
+ import { bundleDirectory } from '../bundler.js';
36
+ import { ApiClient, ApiError } from '../api.js';
37
+ /**
38
+ * Read + parse the manifest. Defaults to `<dir>/sprigr-app.json`. Returns
39
+ * a structured error object on failure so the caller can surface a clean
40
+ * message rather than a stack trace.
41
+ */
42
+ export async function loadManifest(args) {
43
+ const resolvedPath = args.manifestPath
44
+ ? path.resolve(args.manifestPath)
45
+ : path.resolve(args.dir, 'sprigr-app.json');
46
+ let raw;
47
+ try {
48
+ raw = await fs.readFile(resolvedPath, 'utf-8');
49
+ }
50
+ catch (err) {
51
+ return {
52
+ error: `Could not read manifest at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`,
53
+ };
54
+ }
55
+ let parsed;
56
+ try {
57
+ parsed = JSON.parse(raw);
58
+ }
59
+ catch (err) {
60
+ return {
61
+ error: `Manifest at ${resolvedPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
62
+ };
63
+ }
64
+ if (!parsed || typeof parsed !== 'object') {
65
+ return { error: `Manifest at ${resolvedPath} must be a JSON object` };
66
+ }
67
+ return { manifest: parsed, resolvedPath };
68
+ }
69
+ /**
70
+ * Run the publish-time validators against a parsed manifest. Mirrors
71
+ * the server's validateManifest in workers/provisioning so that local
72
+ * `app validate` produces the same verdict the publish API would. The
73
+ * legacy fields (sprigr_app, metadata, etc.) are checked here too —
74
+ * the shared package's per-field validators only cover the new
75
+ * marketplace-hosting fields.
76
+ */
77
+ export function validateManifestLocally(manifest) {
78
+ if (!manifest.sprigr_app?.version)
79
+ return 'Missing sprigr_app.version';
80
+ if (!manifest.metadata?.name)
81
+ return 'Missing metadata.name';
82
+ if (!manifest.metadata?.slug)
83
+ return 'Missing metadata.slug';
84
+ if (!manifest.metadata?.version)
85
+ return 'Missing metadata.version';
86
+ if (!manifest.metadata?.description)
87
+ return 'Missing metadata.description';
88
+ if (!manifest.metadata?.author?.name)
89
+ return 'Missing metadata.author.name';
90
+ // Tier + framework are part of the new hosting schema; their absence is
91
+ // accepted (defaults applied later).
92
+ const tierErr = validateRuntimeTier(manifest.runtime?.tier);
93
+ if (tierErr)
94
+ return tierErr;
95
+ // Cross-tenant tools / app deps / migrations / secrets / schedules /
96
+ // webhooks share validators with the server.
97
+ const ctErr = validateCrossTenantTools(manifest.cross_tenant_tools);
98
+ if (ctErr)
99
+ return ctErr;
100
+ const depsErr = validateAppDependencies(manifest.app_dependencies);
101
+ if (depsErr)
102
+ return depsErr;
103
+ const migErr = validateMigrations(manifest.migrations);
104
+ if (migErr)
105
+ return migErr;
106
+ const secErr = validateSecrets(manifest.secrets);
107
+ if (secErr)
108
+ return secErr;
109
+ const declaredSecretKeys = new Set((manifest.secrets ?? []).map((s) => s.key));
110
+ const whErr = validateWebhooks(manifest.webhooks, declaredSecretKeys);
111
+ if (whErr)
112
+ return whErr;
113
+ const schErr = validateSchedules(manifest.schedules);
114
+ if (schErr)
115
+ return schErr;
116
+ const agentSchErr = validateAgentSchedules(manifest.agent_schedules);
117
+ if (agentSchErr)
118
+ return agentSchErr;
119
+ // Cross-validate that every declared migration file actually exists in
120
+ // the bundle is the publish flow's job (it has the file map). At
121
+ // validate time we don't have that, so we stop here.
122
+ return null;
123
+ }
124
+ /**
125
+ * Per-framework filesystem checks (#1022). Runs after manifest validation
126
+ * and BEFORE bundle upload, so authors hear about missing required files
127
+ * locally instead of after the round-trip through publish → build → install
128
+ * → failure. Currently only Next.js has a checkable contract; other
129
+ * frameworks return null.
130
+ *
131
+ * Async because the check stats the customer's bundle dir for the
132
+ * required config files. Returns null on success or an error message
133
+ * suitable for stderr.
134
+ */
135
+ export async function validateFrameworkFiles(manifest, dir) {
136
+ if (manifest.runtime?.framework !== 'next')
137
+ return null;
138
+ const exists = async (relPath) => {
139
+ try {
140
+ await fs.access(path.join(dir, relPath));
141
+ return true;
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ };
147
+ // open-next.config.ts is non-optional in @opennextjs/cloudflare: the
148
+ // build dies with `No \`open-next.config.ts\` file was found in the
149
+ // project root.` when it's missing. There is no .js/.mjs fallback.
150
+ if (!(await exists('open-next.config.ts'))) {
151
+ return 'missing open-next.config.ts — required by the OpenNext-Cloudflare build pipeline. Run `npx opennextjs-cloudflare migrate` in your app dir to generate one.';
152
+ }
153
+ // next.config can be .js / .mjs / .ts (Next.js accepts any of the three).
154
+ // Require at least one.
155
+ const nextConfigVariants = ['next.config.js', 'next.config.mjs', 'next.config.ts'];
156
+ const found = await Promise.all(nextConfigVariants.map(exists));
157
+ if (!found.some(Boolean)) {
158
+ return `missing next.config.{js,mjs,ts} — Next.js requires one at the project root. Got none of: ${nextConfigVariants.join(', ')}.`;
159
+ }
160
+ return null;
161
+ }
162
+ export async function runAppValidate(args) {
163
+ const loaded = await loadManifest({ dir: args.dir, manifestPath: args.manifestPath });
164
+ if ('error' in loaded) {
165
+ process.stderr.write(`${loaded.error}\n`);
166
+ return { exitCode: 2 };
167
+ }
168
+ const err = validateManifestLocally(loaded.manifest);
169
+ if (err) {
170
+ process.stderr.write(`Manifest validation failed: ${err}\n`);
171
+ return { exitCode: 1 };
172
+ }
173
+ const fsErr = await validateFrameworkFiles(loaded.manifest, args.dir);
174
+ if (fsErr) {
175
+ process.stderr.write(`Framework file check failed: ${fsErr}\n`);
176
+ return { exitCode: 1 };
177
+ }
178
+ process.stdout.write(`Manifest OK — ${loaded.manifest.metadata.slug}@${loaded.manifest.metadata.version}\n`);
179
+ return { exitCode: 0 };
180
+ }
181
+ /**
182
+ * Cross-validate that every file referenced by manifest.migrations[]
183
+ * actually rides in the bundle. Mirrors what the server does at upload
184
+ * time so the CLI can surface a clear local error before the round trip.
185
+ */
186
+ function validateMigrationFilesInBundle(manifest, bundle) {
187
+ if (!manifest.migrations || manifest.migrations.length === 0)
188
+ return null;
189
+ for (const m of manifest.migrations) {
190
+ if (!(m.file in bundle.files)) {
191
+ return `Manifest references migration file "${m.file}" but it is not in the bundle`;
192
+ }
193
+ }
194
+ return null;
195
+ }
196
+ export async function runAppPublish(args) {
197
+ const loaded = await loadManifest({ dir: args.dir, manifestPath: args.manifestPath });
198
+ if ('error' in loaded) {
199
+ process.stderr.write(`${loaded.error}\n`);
200
+ return { exitCode: 2 };
201
+ }
202
+ const validationErr = validateManifestLocally(loaded.manifest);
203
+ if (validationErr) {
204
+ process.stderr.write(`Manifest validation failed: ${validationErr}\n`);
205
+ return { exitCode: 1 };
206
+ }
207
+ const fsErr = await validateFrameworkFiles(loaded.manifest, args.dir);
208
+ if (fsErr) {
209
+ process.stderr.write(`Framework file check failed: ${fsErr}\n`);
210
+ return { exitCode: 1 };
211
+ }
212
+ // The bundler treats the manifest like any other file (it gets
213
+ // uploaded), but we also send the parsed object as a top-level field
214
+ // on the publish payload so the server doesn't have to re-parse the
215
+ // bundle to route the build.
216
+ //
217
+ // Framework is resolved from manifest.runtime.framework: a Next.js / Astro
218
+ // / Remix bundle is the SOURCE (the build container produces index.html),
219
+ // so the bundler enforces `package.json` instead of `index.html`. Without
220
+ // this read the bundler always treated the bundle as static and rejected
221
+ // any SSR app for "Missing index.html" — caught during the hello-
222
+ // marketplace shakedown 2026-05-11.
223
+ const framework = loaded.manifest.runtime?.framework ?? 'static';
224
+ const bundle = await bundleDirectory(args.dir, framework);
225
+ if ('errors' in bundle) {
226
+ for (const e of bundle.errors)
227
+ process.stderr.write(`${e}\n`);
228
+ return { exitCode: 1 };
229
+ }
230
+ const migsErr = validateMigrationFilesInBundle(loaded.manifest, bundle);
231
+ if (migsErr) {
232
+ process.stderr.write(`${migsErr}\n`);
233
+ return { exitCode: 1 };
234
+ }
235
+ process.stdout.write(`Publishing ${loaded.manifest.metadata.slug}@${loaded.manifest.metadata.version} ` +
236
+ `(${bundle.fileCount} files, ${(bundle.totalBytes / 1024).toFixed(1)} KiB` +
237
+ `${bundle.binaryCount > 0 ? `, ${bundle.binaryCount} binary` : ''})…\n`);
238
+ const client = new ApiClient({
239
+ endpoint: args.endpoint,
240
+ apiKey: args.apiKey,
241
+ fetchImpl: args.fetchImpl,
242
+ });
243
+ try {
244
+ const result = await client.publishMarketplaceApp({
245
+ manifest: loaded.manifest,
246
+ files: bundle.files,
247
+ });
248
+ process.stdout.write(`Published version ${result.version} of ${result.slug}` +
249
+ (result.buildId ? ` (build ${result.buildId})` : '') +
250
+ '\n');
251
+ // Partial-fanout warning. Surfaced here because the publish 201 still
252
+ // succeeds even when some tracking installs failed to enqueue a build —
253
+ // those installs are now pinned to the new version but still serving the
254
+ // old WFP bundle. Recovery is `sprigr app upgrade <slug> --force` on the
255
+ // affected tenant (or for cross-publisher recovery, grep system_logs AE
256
+ // for `marketplace.upgrade.fanout_failed` / `*.fanout_threw` filtered by
257
+ // `app_slug`). We don't fail the exit code — the version itself
258
+ // published successfully, the publisher just needs to follow up.
259
+ if (result.fanout) {
260
+ if (result.fanout.threw) {
261
+ process.stderr.write(`Warning: auto-upgrade fan-out threw: ${result.fanout.threw}. ` +
262
+ `Check system_logs AE for marketplace.upgrade.fanout_threw, ` +
263
+ `then run \`sprigr app upgrade ${result.slug} --force\` ` +
264
+ `on affected tenants.\n`);
265
+ }
266
+ else if (result.fanout.failed > 0) {
267
+ process.stderr.write(`Warning: auto-upgrade fan-out partial — ` +
268
+ `${result.fanout.upgraded} upgraded, ${result.fanout.failed} failed. ` +
269
+ `Failed installs are pinned to ${result.version} but still serving the old bundle. ` +
270
+ `Recover with \`sprigr app upgrade ${result.slug} --force\` ` +
271
+ `or grep system_logs AE for marketplace.upgrade.fanout_failed.\n`);
272
+ }
273
+ }
274
+ return { exitCode: 0 };
275
+ }
276
+ catch (err) {
277
+ if (err instanceof ApiError) {
278
+ process.stderr.write(`Publish failed: HTTP ${err.status} — ${err.message}\n`);
279
+ return { exitCode: 1 };
280
+ }
281
+ process.stderr.write(`Publish failed: ${err instanceof Error ? err.message : String(err)}\n`);
282
+ return { exitCode: 1 };
283
+ }
284
+ }
285
+ /**
286
+ * `sprigr app upgrade <slug>` — manually bump this company's install of
287
+ * a marketplace app to the latest approved version. Hits
288
+ * POST /api/v1/data/apps/:slug/install/upgrade which:
289
+ * 1. Bumps `pinned_version_id` to current_version
290
+ * 2. Refreshes the integration row's snapshot manifest
291
+ * 3. Syncs the company's integration KV cache + reinits agent DOs
292
+ * 4. Enqueues a fresh build via `enqueueBuildForMarketplaceVersion`
293
+ *
294
+ * Returns the buildId for status polling. Idempotent — returns HTTP 400
295
+ * "Already on latest version" if the install is already pinned to the
296
+ * current version.
297
+ *
298
+ * Use this to recover from a silent publish-fanout enqueue failure (the
299
+ * symptom: a fresh version exists in `current_version` but no
300
+ * publish-fanout build appears in website_status after the publish).
301
+ */
302
+ export async function runAppUpgrade(args) {
303
+ if (!args.slug) {
304
+ process.stderr.write('Missing app slug. Usage: sprigr app upgrade <slug>\n');
305
+ return { exitCode: 2 };
306
+ }
307
+ const client = new ApiClient({
308
+ endpoint: args.endpoint,
309
+ apiKey: args.apiKey,
310
+ fetchImpl: args.fetchImpl,
311
+ });
312
+ try {
313
+ const result = await client.upgradeMarketplaceInstall({ appSlug: args.slug, force: args.force });
314
+ process.stdout.write(`Upgrading ${args.slug} install to ${result.newVersion} (build ${result.buildId}). ` +
315
+ `Poll \`sprigr builds get <siteId> ${result.buildId}\` for completion.\n`);
316
+ return { exitCode: 0 };
317
+ }
318
+ catch (err) {
319
+ if (err instanceof ApiError) {
320
+ // 400 "Already on latest version" is a benign no-op; surface it
321
+ // explicitly but exit non-zero so scripts can detect it.
322
+ process.stderr.write(`Upgrade failed: HTTP ${err.status} — ${err.message}\n`);
323
+ return { exitCode: err.status === 400 ? 0 : 1 };
324
+ }
325
+ process.stderr.write(`Upgrade failed: ${err instanceof Error ? err.message : String(err)}\n`);
326
+ return { exitCode: 1 };
327
+ }
328
+ }
329
+ /**
330
+ * `sprigr app delete <slug>` — hard-delete a marketplace app + all its
331
+ * versions + any leftover install rows owned by the publisher. Releases
332
+ * the slug for re-publication.
333
+ *
334
+ * Use this when migrating an app between publishers (personal staging
335
+ * → sprigr-hq, etc.). The runbook lives at
336
+ * sprigr-apps/docs/migrate-app-publisher.md — uninstall on every
337
+ * installer tenant first, THEN delete from the old publisher, THEN
338
+ * re-publish under the new publisher.
339
+ *
340
+ * The endpoint hard-deletes everything: all marketplace_app_versions
341
+ * rows, R2 bundle objects, any orphaned app_installations. Only the
342
+ * current publisher can call it (403 otherwise).
343
+ *
344
+ * Pass --yes to skip the confirmation prompt. CI / non-interactive
345
+ * callers must always pass --yes.
346
+ */
347
+ export async function runAppDelete(args) {
348
+ if (!args.slug) {
349
+ process.stderr.write('Missing app slug. Usage: sprigr app delete <slug> [--yes]\n');
350
+ return { exitCode: 2 };
351
+ }
352
+ if (!args.yes) {
353
+ process.stderr.write(`Refusing to delete "${args.slug}" without --yes. This is a hard delete: every version, every R2 bundle, every install row this publisher owns is destroyed.\n`);
354
+ return { exitCode: 2 };
355
+ }
356
+ const client = new ApiClient({
357
+ endpoint: args.endpoint,
358
+ apiKey: args.apiKey,
359
+ fetchImpl: args.fetchImpl,
360
+ });
361
+ try {
362
+ await client.deleteMarketplaceApp({ appSlug: args.slug });
363
+ process.stdout.write(`Deleted ${args.slug}. Slug released; you can now republish under a different publisher.\n`);
364
+ return { exitCode: 0 };
365
+ }
366
+ catch (err) {
367
+ if (err instanceof ApiError) {
368
+ const hint = err.status === 403
369
+ ? ' (caller is not the publisher; check `sprigr whoami --profile <name>`)'
370
+ : err.status === 404
371
+ ? ' (slug already released)'
372
+ : '';
373
+ process.stderr.write(`Delete failed: HTTP ${err.status} — ${err.message}${hint}\n`);
374
+ return { exitCode: 1 };
375
+ }
376
+ process.stderr.write(`Delete failed: ${err instanceof Error ? err.message : String(err)}\n`);
377
+ return { exitCode: 1 };
378
+ }
379
+ }
380
+ /**
381
+ * `sprigr app share <slug>` — generate a share token + auto-bump
382
+ * trust_tier from `private` to `shared`.
383
+ *
384
+ * Publisher-only. Lets other tenants in the same env install the app
385
+ * via their own CLI/MCP/portal without sharing the publisher's
386
+ * credentials. Calling again rotates the token but the trust tier
387
+ * stays at whatever it already is.
388
+ *
389
+ * The share URL prints to stdout (paste into a tenant browser to
390
+ * install via the share-link flow), but the more common path post-
391
+ * share is `sprigr app install <slug> --profile <other-tenant>` since
392
+ * trust_tier='shared' allows any tenant to install.
393
+ */
394
+ export async function runAppShare(args) {
395
+ if (!args.slug) {
396
+ process.stderr.write('Missing app slug. Usage: sprigr app share <slug>\n');
397
+ return { exitCode: 2 };
398
+ }
399
+ const client = new ApiClient({
400
+ endpoint: args.endpoint,
401
+ apiKey: args.apiKey,
402
+ fetchImpl: args.fetchImpl,
403
+ });
404
+ try {
405
+ const result = await client.shareMarketplaceApp({ appSlug: args.slug });
406
+ process.stdout.write(`Shared ${args.slug}. trust_tier bumped to 'shared' (if previously 'private').\n` +
407
+ ` share_token: ${result.share_token}\n` +
408
+ ` share_url: ${result.share_url}\n`);
409
+ return { exitCode: 0 };
410
+ }
411
+ catch (err) {
412
+ if (err instanceof ApiError) {
413
+ const hint = err.status === 404
414
+ ? ' (caller is not the publisher OR slug does not exist; run `sprigr whoami --profile <name>`)'
415
+ : '';
416
+ process.stderr.write(`Share failed: HTTP ${err.status} — ${err.message}${hint}\n`);
417
+ return { exitCode: 1 };
418
+ }
419
+ process.stderr.write(`Share failed: ${err instanceof Error ? err.message : String(err)}\n`);
420
+ return { exitCode: 1 };
421
+ }
422
+ }
423
+ /**
424
+ * `sprigr app install <slug> --scopes ... [--secret KEY=VALUE ...]`
425
+ *
426
+ * Install a marketplace app on the calling tenant. Publisher-migration
427
+ * step 5 from sprigr-apps/docs/migrate-app-publisher.md.
428
+ *
429
+ * Body shape gotcha (documented at the runbook level too): the API is
430
+ * snake_case (granted_scopes, install_secrets). Sending camelCase
431
+ * silently parses + ignores the unknown keys → 400 "Missing required
432
+ * secrets". The CLI passes the right shape so callers don't have to
433
+ * remember.
434
+ */
435
+ export async function runAppInstall(args) {
436
+ if (!args.slug) {
437
+ process.stderr.write('Missing app slug. Usage: sprigr app install <slug> --scopes <list> [--secret KEY=VALUE]\n');
438
+ return { exitCode: 2 };
439
+ }
440
+ if (!args.scopes) {
441
+ process.stderr.write(`Missing --scopes. Pass a comma-separated list, e.g. --scopes tools:register,knowledge:write\n`);
442
+ return { exitCode: 2 };
443
+ }
444
+ const grantedScopes = args.scopes.split(',').map((s) => s.trim()).filter(Boolean);
445
+ if (grantedScopes.length === 0) {
446
+ process.stderr.write('Empty --scopes list after splitting on commas.\n');
447
+ return { exitCode: 2 };
448
+ }
449
+ const client = new ApiClient({
450
+ endpoint: args.endpoint,
451
+ apiKey: args.apiKey,
452
+ fetchImpl: args.fetchImpl,
453
+ });
454
+ try {
455
+ const result = await client.installMarketplaceApp({
456
+ appSlug: args.slug,
457
+ grantedScopes,
458
+ installSecrets: args.secrets,
459
+ });
460
+ const ins = result.installation;
461
+ process.stdout.write(`Installed ${args.slug} → install ${ins.id}` +
462
+ (ins.website_slug ? ` (site ${ins.website_slug})` : '') +
463
+ (ins.build_id ? ` (build ${ins.build_id})` : '') +
464
+ '\n');
465
+ return { exitCode: 0 };
466
+ }
467
+ catch (err) {
468
+ if (err instanceof ApiError) {
469
+ const hint = err.status === 400 && /missing required secret/i.test(err.message)
470
+ ? ' (the API wants snake_case secret KEYS that match manifest.secrets[].key — case-sensitive)'
471
+ : err.status === 403
472
+ ? ' (caller tenant cannot install this app; private apps require publisher self-install or shared trust tier)'
473
+ : err.status === 404
474
+ ? ' (app slug not found — check publisher with `sprigr whoami` then list apps via portal/MCP)'
475
+ : '';
476
+ process.stderr.write(`Install failed: HTTP ${err.status} — ${err.message}${hint}\n`);
477
+ return { exitCode: 1 };
478
+ }
479
+ process.stderr.write(`Install failed: ${err instanceof Error ? err.message : String(err)}\n`);
480
+ return { exitCode: 1 };
481
+ }
482
+ }
483
+ /**
484
+ * `sprigr app set-publisher-secrets <slug> --secrets '<json>'`
485
+ *
486
+ * Seed or rotate the publisher-provided secret bag on the app row.
487
+ * The publisher's OAuth-app credentials (SHOPIFY_CLIENT_ID,
488
+ * SHOPIFY_CLIENT_SECRET, etc.) flow through this command — every
489
+ * install of the app picks them up automatically at install time.
490
+ *
491
+ * Auth: caller must be the app's publisher (the API checks
492
+ * publisher_company_id matches the calling tenant). Errors:
493
+ * - 404: caller doesn't own the app, OR slug doesn't exist
494
+ * - 400: a key isn't declared / isn't `publisher_provides: true` /
495
+ * value isn't a non-empty string
496
+ * - 500: WEBSITE_SECRETS_MASTER_KEY not bound on the platform
497
+ *
498
+ * No value is ever returned in plaintext from the API. Rotation =
499
+ * "set a new value"; there's no "show me the current value" path.
500
+ */
501
+ export async function runAppSetPublisherSecrets(args) {
502
+ if (!args.slug) {
503
+ process.stderr.write('Missing app slug. Usage: sprigr app set-publisher-secrets <slug> --secrets \'<json>\'\n');
504
+ return { exitCode: 2 };
505
+ }
506
+ if (!args.secrets || Object.keys(args.secrets).length === 0) {
507
+ process.stderr.write('--secrets must contain at least one key (the no-op case is unsupported on the CLI to avoid silent misuse)\n');
508
+ return { exitCode: 2 };
509
+ }
510
+ const client = new ApiClient({
511
+ endpoint: args.endpoint,
512
+ apiKey: args.apiKey,
513
+ fetchImpl: args.fetchImpl,
514
+ });
515
+ try {
516
+ const result = await client.setMarketplaceAppPublisherSecrets({
517
+ appSlug: args.slug,
518
+ secrets: args.secrets,
519
+ });
520
+ process.stdout.write(`Set publisher secrets for ${args.slug}.\n` +
521
+ ` updated keys: ${result.updated_keys.join(', ') || '(none)'}\n` +
522
+ ` total stored: ${result.total_keys}\n`);
523
+ return { exitCode: 0 };
524
+ }
525
+ catch (err) {
526
+ if (err instanceof ApiError) {
527
+ const hint = err.status === 404
528
+ ? ' (caller is not the publisher OR slug does not exist; run `sprigr whoami`)'
529
+ : err.status === 400 && /publisher_provides/i.test(err.message)
530
+ ? ' (the manifest must declare each key with `publisher_provides: true` for this endpoint)'
531
+ : '';
532
+ process.stderr.write(`set-publisher-secrets failed: HTTP ${err.status} — ${err.message}${hint}\n`);
533
+ return { exitCode: 1 };
534
+ }
535
+ process.stderr.write(`set-publisher-secrets failed: ${err instanceof Error ? err.message : String(err)}\n`);
536
+ return { exitCode: 1 };
537
+ }
538
+ }
539
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/commands/app.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface BuildsListArgs {
2
+ siteId: string;
3
+ endpoint: string;
4
+ apiKey: string;
5
+ limit?: number;
6
+ log?: (line: string) => void;
7
+ fetchImpl?: typeof fetch;
8
+ }
9
+ export declare function runBuildsList(args: BuildsListArgs): Promise<number>;
10
+ export interface BuildsGetArgs {
11
+ siteId: string;
12
+ buildId: string;
13
+ endpoint: string;
14
+ apiKey: string;
15
+ /** When true, also fetch the captured stdout/stderr and append it after
16
+ * the build JSON. Default true — debugging is the dominant use case for
17
+ * `builds get`. Set false when scripting against the JSON shape. */
18
+ showLog?: boolean;
19
+ log?: (line: string) => void;
20
+ fetchImpl?: typeof fetch;
21
+ }
22
+ export declare function runBuildsGet(args: BuildsGetArgs): Promise<number>;
@@ -0,0 +1,76 @@
1
+ import { ApiClient, ApiError } from '../api.js';
2
+ export async function runBuildsList(args) {
3
+ const log = args.log ?? ((line) => console.log(line));
4
+ const client = new ApiClient({ endpoint: args.endpoint, apiKey: args.apiKey, fetchImpl: args.fetchImpl });
5
+ try {
6
+ const { builds } = await client.listBuilds(args.siteId, args.limit ?? 20);
7
+ if (builds.length === 0) {
8
+ log('no builds found');
9
+ return 0;
10
+ }
11
+ log(formatBuildsTable(builds));
12
+ return 0;
13
+ }
14
+ catch (err) {
15
+ log(formatApiError(err));
16
+ return 1;
17
+ }
18
+ }
19
+ const TERMINAL_STATUSES = new Set([
20
+ 'succeeded',
21
+ 'failed',
22
+ 'cancelled',
23
+ 'timeout',
24
+ ]);
25
+ export async function runBuildsGet(args) {
26
+ const log = args.log ?? ((line) => console.log(line));
27
+ const showLog = args.showLog ?? true;
28
+ const client = new ApiClient({ endpoint: args.endpoint, apiKey: args.apiKey, fetchImpl: args.fetchImpl });
29
+ try {
30
+ const { build } = await client.getBuild(args.siteId, args.buildId);
31
+ log(JSON.stringify(build, null, 2));
32
+ // Pull the build log too once the build has reached a terminal
33
+ // state and the runner produced one (logR2Key is set). Ignored for
34
+ // pending/running builds — they don't have a log yet.
35
+ if (showLog && TERMINAL_STATUSES.has(build.status) && build.logR2Key) {
36
+ try {
37
+ const text = await client.getBuildLog(args.siteId, args.buildId);
38
+ if (text !== null) {
39
+ log('');
40
+ log('--- build log ---');
41
+ log(text);
42
+ }
43
+ }
44
+ catch (err) {
45
+ log(`warn: failed to fetch build log: ${err instanceof Error ? err.message : String(err)}`);
46
+ }
47
+ }
48
+ return 0;
49
+ }
50
+ catch (err) {
51
+ log(formatApiError(err));
52
+ return 1;
53
+ }
54
+ }
55
+ function formatBuildsTable(builds) {
56
+ const rows = builds.map((b) => [
57
+ b.id,
58
+ b.status.padEnd(9),
59
+ (b.framework ?? '-').padEnd(7),
60
+ formatSeconds(b.containerSeconds),
61
+ b.deploymentId ?? '-',
62
+ b.createdAt,
63
+ ]);
64
+ return rows.map((r) => r.join(' ')).join('\n');
65
+ }
66
+ function formatSeconds(s) {
67
+ if (s === null)
68
+ return ' - ';
69
+ return `${s.toFixed(2)}s`.padStart(7);
70
+ }
71
+ function formatApiError(err) {
72
+ if (err instanceof ApiError)
73
+ return `error: ${err.status} ${err.message}`;
74
+ return `error: ${err instanceof Error ? err.message : String(err)}`;
75
+ }
76
+ //# sourceMappingURL=builds.js.map