@m-kopa/launchpad-cli 0.44.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
7
7
  pre-1.0 minor bumps may carry breaking changes per ADR 0005.
8
8
 
9
+ ## 0.46.0 — 2026-07-07
10
+
11
+ **Honest apply status (sp-k9kw8d).** `launchpad deploy --apply` no longer
12
+ declares success off the merged file - the verdict is now your app's own
13
+ `terraform apply` run:
14
+
15
+ - **Four visible states while polling.** `waiting-approval` (the bot PR is
16
+ open - access-bearing changes need the `allowed-groups-change: true` label
17
+ from someone other than the PR author), `pending` (merged, apply running),
18
+ `applied`, and `failed`. A failed apply exits non-zero and prints the
19
+ failing run's URL - previously the CLI could print `✓ Applied` while the
20
+ apply was failing (the 2026-07-07 people-analytics-v2 incident).
21
+ - **Per-app verdict, not workflow-level.** For per-app-workspace apps the CLI
22
+ reads the app's own `terraform apply (<slug>)` check run, so a sibling
23
+ app's failure in the same matrix never taints your result.
24
+ - **Old-bot fallback.** Against a bot that predates the new endpoint the CLI
25
+ falls back to the previous coarse poll, with a notice.
26
+ - **Access drift now has a self-serve fix.** The `ACCESS DRIFT` refusal from
27
+ `launchpad deploy` (and the drift warning in `launchpad status`) now point
28
+ at `launchpad deploy --apply` as the reconcile path, replacing the manual
29
+ runbook detour. The apply route dispatches on the app's provisioning world
30
+ server-side, so per-app-workspace apps get their workspace regenerated -
31
+ never the legacy flat pair that poisoned shared-root applies.
32
+
33
+ ## 0.45.0 — 2026-07-05
34
+
35
+ **Self-serve recovery of a build-failed app (PS-1934).** An app stuck in a
36
+ `failed` lifecycle because of a build bug now recovers through the normal
37
+ `launchpad deploy` flow — no break-glass, no separate `launchpad recover`:
38
+
39
+ - **Auto-reconcile `failed → live`.** Once your fixing deploy's Cloudflare
40
+ Pages build is **verified serving**, the platform flips the record back to
41
+ `live` on the next `launchpad status` / `launchpad watch` read (which
42
+ `deploy` runs for you). `status` surfaces a `✓ recovered:` line so the
43
+ repair is visible, not silent. Scoped to **build** failures only — a
44
+ security-verdict `failed` (origin reachable without auth) is never
45
+ auto-cleared by a green build.
46
+ - **Honest failed-app deploy UX.** An empty-delta deploy to a `failed` app now
47
+ says so plainly (`slug_failed`) and points at the fix path, instead of an
48
+ opaque 409.
49
+ - **`recover` stays** as the explicit reconcile-only path; the docs now
50
+ cross-reference the broken-CI case (PS-1932), where `launchpad deploy` (bot
51
+ bypass) is the supported recovery.
52
+
53
+ Platform-side (ships with the bot): the `redeploy` path now re-ensures the
54
+ D1 binding like `deploy` does, so a redeploy-based recovery can't leave the
55
+ database unbound (the stock-dispatch-tracker 500 root cause).
56
+
9
57
  ## 0.44.0 — 2026-07-05
10
58
 
11
59
  **Identity guide crystal-clarity — fixes the "gateway signs me in but the app
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // src/version.ts
22
- var CLI_VERSION = "0.44.0";
22
+ var CLI_VERSION = "0.46.0";
23
23
 
24
24
  // src/config.ts
25
25
  import * as os from "node:os";
@@ -3763,7 +3763,8 @@ async function fetchDeploymentStatus(cfg, slug, fetcher = fetch) {
3763
3763
  slug: typeof raw.slug === "string" ? raw.slug : slug,
3764
3764
  supported: raw.supported,
3765
3765
  liveDeployment: raw.liveDeployment ?? null,
3766
- lastSuccessfulDeployment: raw.lastSuccessfulDeployment ?? null
3766
+ lastSuccessfulDeployment: raw.lastSuccessfulDeployment ?? null,
3767
+ ...raw.reconciledToLive === true ? { reconciledToLive: true } : {}
3767
3768
  };
3768
3769
  }
3769
3770
 
@@ -5121,7 +5122,8 @@ function surfaceAccessDrift(out, io) {
5121
5122
  for (const u of ad.unresolved) {
5122
5123
  io.out(` unresolved group (${u.side}): ${u.token}`);
5123
5124
  }
5124
- io.out(" `launchpad deploy` will NOT fix this — access changes go via the gated TF route.");
5125
+ io.out(" `launchpad deploy` will NOT fix this — run `launchpad deploy --apply` to");
5126
+ io.out(" reconcile access through the gated TF route (sp-k9kw8d).");
5125
5127
  }
5126
5128
  function surfaceNoLocalManifestNote(out, io) {
5127
5129
  if (out.drift !== null)
@@ -5139,6 +5141,9 @@ function surfaceDeployment(out, io) {
5139
5141
  const dep = out.deployment;
5140
5142
  if (dep === undefined || dep === null || !dep.supported)
5141
5143
  return;
5144
+ if (dep.reconciledToLive === true) {
5145
+ io.out(" ✓ recovered: this app was in a failed state; its verified-live build reconciled the record failed→live (no `launchpad recover` needed).");
5146
+ }
5142
5147
  const live = dep.liveDeployment;
5143
5148
  if (live === null) {
5144
5149
  io.out(" last deployment: none — Cloudflare Pages has no production deployment yet.");
@@ -6254,7 +6259,7 @@ async function runDeployApply(opts, io, deps = {}) {
6254
6259
  timeoutMs
6255
6260
  });
6256
6261
  if (outcome2.kind !== "applied")
6257
- return outcomeToExit(outcome2);
6262
+ return outcomeToExit(outcome2, undefined, io);
6258
6263
  deletePinIfPresent(cfg, slug2, io);
6259
6264
  io.out(`✓ Applied ${slug2}.`);
6260
6265
  return 0;
@@ -6335,7 +6340,7 @@ async function runDeployApply(opts, io, deps = {}) {
6335
6340
  timeoutMs
6336
6341
  });
6337
6342
  if (outcome.kind !== "applied")
6338
- return outcomeToExit(outcome, apply.prUrl);
6343
+ return outcomeToExit(outcome, apply.prUrl, io);
6339
6344
  deletePinIfPresent(cfg, slug, io);
6340
6345
  io.out("");
6341
6346
  io.out(`✓ Applied ${slug}. (See ${apply.prUrl} for the merged PR.)`);
@@ -6343,36 +6348,83 @@ async function runDeployApply(opts, io, deps = {}) {
6343
6348
  }
6344
6349
  async function pollUntilApplied(args) {
6345
6350
  const start = Date.now();
6346
- let lastPrSeen = true;
6347
6351
  let dotsThisLine = 0;
6348
- while (Date.now() - start < args.timeoutMs) {
6349
- let state;
6350
- try {
6351
- state = await apiJson(args.cfg, {
6352
- method: "GET",
6353
- path: `/apps/${args.slug}/manifest/state`
6354
- }, args.fetcher);
6355
- } catch (e) {
6356
- args.io.err(`! state fetch failed (will retry): ${describe13(e)}`);
6357
- await sleep(args.pollIntervalSec * 1000);
6358
- continue;
6359
- }
6360
- const prGone = state.openPr === null || state.openPr.number !== args.prNumber;
6361
- const shaMatches2 = args.manifestSha === null ? state.hasAppFile && state.lastAppliedManifestSha !== null : state.lastAppliedManifestSha === args.manifestSha;
6362
- if (prGone && shaMatches2) {
6363
- if (dotsThisLine > 0)
6364
- args.io.out("");
6365
- return { kind: "applied" };
6352
+ let mode = args.manifestSha === null ? "legacy" : "status";
6353
+ let lastRendered = null;
6354
+ let lastPrSeen = true;
6355
+ const breakDots = () => {
6356
+ if (dotsThisLine > 0) {
6357
+ args.io.out("");
6358
+ dotsThisLine = 0;
6366
6359
  }
6367
- if (prGone && !shaMatches2 && lastPrSeen) {
6368
- if (dotsThisLine > 0)
6369
- args.io.out("");
6370
- return {
6371
- kind: "failed",
6372
- reason: "the bot PR closed without applying this manifest sha (was it rejected or superseded?)"
6373
- };
6360
+ };
6361
+ while (Date.now() - start < args.timeoutMs) {
6362
+ if (mode === "status") {
6363
+ let status;
6364
+ try {
6365
+ status = await apiJson(args.cfg, {
6366
+ method: "GET",
6367
+ path: `/apps/${args.slug}/manifest/apply-status?manifestSha=${args.manifestSha}`
6368
+ }, args.fetcher);
6369
+ } catch (e) {
6370
+ if (e instanceof NotFoundError) {
6371
+ breakDots();
6372
+ args.io.err("! bot has no apply-status endpoint yet — falling back to the coarse merge-poll (upgrade pending bot deploy)");
6373
+ mode = "legacy";
6374
+ continue;
6375
+ }
6376
+ args.io.err(`! apply-status fetch failed (will retry): ${describe13(e)}`);
6377
+ await sleep(args.pollIntervalSec * 1000);
6378
+ continue;
6379
+ }
6380
+ if (status.state === "applied") {
6381
+ breakDots();
6382
+ return { kind: "applied" };
6383
+ }
6384
+ if (status.state === "failed") {
6385
+ breakDots();
6386
+ return {
6387
+ kind: "failed",
6388
+ reason: status.detail ?? "the apply run failed",
6389
+ runUrl: status.runUrl ?? null
6390
+ };
6391
+ }
6392
+ if (status.state !== lastRendered) {
6393
+ breakDots();
6394
+ if (status.state === "waiting-approval") {
6395
+ args.io.out(`⏳ waiting for approval/CI — access-bearing changes need the 'allowed-groups-change: true' label from someone other than the PR author (${status.prUrl})`);
6396
+ } else {
6397
+ args.io.out(`⚙ PR merged — waiting for the app's terraform apply${status.checkName !== undefined ? ` ('${status.checkName}')` : ""} …`);
6398
+ }
6399
+ lastRendered = status.state;
6400
+ }
6401
+ } else {
6402
+ let state;
6403
+ try {
6404
+ state = await apiJson(args.cfg, {
6405
+ method: "GET",
6406
+ path: `/apps/${args.slug}/manifest/state`
6407
+ }, args.fetcher);
6408
+ } catch (e) {
6409
+ args.io.err(`! state fetch failed (will retry): ${describe13(e)}`);
6410
+ await sleep(args.pollIntervalSec * 1000);
6411
+ continue;
6412
+ }
6413
+ const prGone = state.openPr === null || state.openPr.number !== args.prNumber;
6414
+ const shaMatches2 = args.manifestSha === null ? state.hasAppFile && state.lastAppliedManifestSha !== null : state.lastAppliedManifestSha === args.manifestSha;
6415
+ if (prGone && shaMatches2) {
6416
+ breakDots();
6417
+ return { kind: "applied" };
6418
+ }
6419
+ if (prGone && !shaMatches2 && lastPrSeen) {
6420
+ breakDots();
6421
+ return {
6422
+ kind: "failed",
6423
+ reason: "the bot PR closed without applying this manifest sha (was it rejected or superseded?)"
6424
+ };
6425
+ }
6426
+ lastPrSeen = state.openPr !== null;
6374
6427
  }
6375
- lastPrSeen = state.openPr !== null;
6376
6428
  process.stdout.write(".");
6377
6429
  dotsThisLine++;
6378
6430
  if (dotsThisLine >= 60) {
@@ -6385,15 +6437,29 @@ async function pollUntilApplied(args) {
6385
6437
  args.io.out("");
6386
6438
  return { kind: "timed-out" };
6387
6439
  }
6388
- function outcomeToExit(outcome, prUrl) {
6440
+ function outcomeToExit(outcome, prUrl, io) {
6441
+ const err = (line) => {
6442
+ if (io !== undefined) {
6443
+ io.err(line);
6444
+ } else {
6445
+ console.error(line);
6446
+ }
6447
+ };
6389
6448
  switch (outcome.kind) {
6390
6449
  case "applied":
6391
6450
  return 0;
6392
6451
  case "failed":
6452
+ err(`✗ Apply FAILED: ${outcome.reason}`);
6453
+ if (outcome.runUrl !== undefined && outcome.runUrl !== null) {
6454
+ err(` Failing run: ${outcome.runUrl}`);
6455
+ }
6456
+ if (prUrl !== undefined) {
6457
+ err(` PR: ${prUrl}`);
6458
+ }
6393
6459
  return 1;
6394
6460
  case "timed-out":
6395
6461
  if (prUrl !== undefined) {
6396
- console.error(`! timed out waiting for apply — check ${prUrl} for live status.`);
6462
+ err(`! timed out waiting for apply — check ${prUrl} for live status.`);
6397
6463
  }
6398
6464
  return 4;
6399
6465
  case "transport":
@@ -7137,7 +7203,7 @@ function surfaceDeployExtras(body, io, slug, allowStale = false) {
7137
7203
  if (adw.manifestOnly !== undefined && adw.manifestOnly.length > 0) {
7138
7204
  io.err(` declared but NOT enforced: ${adw.manifestOnly.join(", ")}`);
7139
7205
  }
7140
- io.err(` reconcile access via the gated TF route — \`launchpad deploy\` can't (it ships content only).`);
7206
+ io.err(` reconcile with \`launchpad deploy --apply\` (the gated TF route) — \`launchpad deploy\` can't (it ships content only).`);
7141
7207
  }
7142
7208
  }
7143
7209
  function isStaleBlock(body) {
@@ -7319,6 +7385,14 @@ async function runModelADeploy(args) {
7319
7385
  }
7320
7386
  return 1;
7321
7387
  }
7388
+ if (result.status === 409 && errorCode === "slug_failed") {
7389
+ io.err(`launchpad deploy: app "${slug}" is in a FAILED state, and this deploy adds no new content (nothing to re-drive the build).`);
7390
+ if (typeof body.message === "string") {
7391
+ io.err(` ${body.message}`);
7392
+ }
7393
+ io.err(` Deploy a content change to re-drive the build (it reconciles failed→live automatically once verified serving — watch \`launchpad status ${slug}\`), or \`launchpad deploy --resume ${slug}\`.`);
7394
+ return 1;
7395
+ }
7322
7396
  if (result.status === 409 && isStaleBlock(body)) {
7323
7397
  surfaceStaleBlock(body, io);
7324
7398
  return 1;
@@ -7335,8 +7409,10 @@ async function runModelADeploy(args) {
7335
7409
  }
7336
7410
  io.err("");
7337
7411
  io.err(" Your manifest's access groups don't match what the gateway enforces.");
7338
- io.err(" `launchpad deploy` ships content only and cannot change access — reconcile");
7339
- io.err(" via the gated TF route (see the access-change runbook), then redeploy.");
7412
+ io.err(" `launchpad deploy` ships content only and cannot change access.");
7413
+ io.err(" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR");
7414
+ io.err(" (access-widening changes need the allowed-groups-change label from a");
7415
+ io.err(" second person), waits for the apply verdict, then redeploy your content.");
7340
7416
  io.err(" Nothing was committed by this attempt.");
7341
7417
  return 1;
7342
7418
  }
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,11 @@ export interface DeploymentStatusView {
26
26
  readonly createdOn: string;
27
27
  readonly url: string | null;
28
28
  } | null;
29
+ /** PS-1934 AC1 — true when THIS read auto-reconciled a build-failed app
30
+ * back to `live` (its latest build is verified serving). Lets `status` /
31
+ * `watch` / the post-deploy verify surface "record reconciled failed→live"
32
+ * rather than the repair happening silently. */
33
+ readonly reconciledToLive?: boolean;
29
34
  }
30
35
  /** One standing-exception inventory entry (sp-devlp1 T3 / D2) — a
31
36
  * policy/boundary/build-command violation observed in content already
@@ -1 +1 @@
1
- {"version":3,"file":"deployment-status.d.ts","sourceRoot":"","sources":["../../src/deploy/deployment-status.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnD,yEAAyE;IACzE,QAAQ,CAAC,wBAAwB,EAAE;QACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,IAAI,CAAC;CACV;AAED;;;wEAGwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,SAAS,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAsBlD;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAuBtC"}
1
+ {"version":3,"file":"deployment-status.d.ts","sourceRoot":"","sources":["../../src/deploy/deployment-status.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,KAAK,GAAG,SAAS,CAAC;AAC/D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;AAEhE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnD,yEAAyE;IACzE,QAAQ,CAAC,wBAAwB,EAAE;QACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,IAAI,CAAC;IACT;;;qDAGiD;IACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED;;;wEAGwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,SAAS,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAsBlD;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAwBtC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.44.0";
1
+ export declare const CLI_VERSION = "0.46.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@m-kopa/launchpad-cli",
3
- "version": "0.44.0",
4
- "description": "Launchpad CLI clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
3
+ "version": "0.46.0",
4
+ "description": "Launchpad CLI \u2014 clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "launchpad": "./dist/cli.js"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-content-pr
3
3
  description: Push a content change to a Launchpad app via `launchpad deploy` and verify it shipped via `launchpad status`. Covers the post-first-deploy iteration loop (edit → deploy → verify) — subsequent deploys commit directly to the app repo's main and the Pages build runs asynchronously, so verification is its own step. Use when someone says "push a content change", "ship an update", "/launchpad-content-pr", "verify my deploy", or after `/launchpad-deploy` reports `done` and they want to follow up with an edit.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-deploy
3
3
  description: Walk a Launchpad user through deploying an app from their local working directory (Model A — `launchpad init` + `launchpad deploy`). Wraps the CLI verbs end-to-end: detects the app shape, scaffolds `launchpad.yaml`, resolves the allowed Entra group via `launchpad groups`, bundles the CWD via `launchpad deploy`, and watches the rollout via `launchpad status`. Use when someone says "deploy a new app", "ship my app to Launchpad", "/launchpad-deploy", "I have an app locally — get it on Launchpad", or any variant. Resume/abandon for legacy in-flight provisioning is at the bottom.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-deploy-status
3
3
  description: Show the current provisioning stage + failure reason for a Launchpad app via `launchpad status` (Model A drift + deployment_verified) and `launchpad apps` (lifecycle bucket), or watch provisioning live with `launchpad watch`. Renders the M-892 stage trace for in-flight provisioning, and is the canonical home for `launchpad recover` (repair a terminal-failed app record that is actually live). Use when someone says "what's the status of demo-X", "/launchpad-deploy-status", "is my deploy stuck", "watch my deploy go live", "watch provisioning", "my app says failed but it's serving", or after `/launchpad-deploy` reports a non-`done` terminal stage.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-destroy
3
3
  description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, edge-auth wiring (gateway KV/audience entries, or the Access app for `auth: access` apps), custom hostname, platform-repo TF, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-identity
3
3
  description: Teach an app author how to use the signed-in user's identity inside a Launchpad app — read the gateway-forwarded X-Launchpad-User-Assertion in a Pages Function, VERIFY it with @m-kopa/platform-auth (fail-closed), and show who's logged in (sub/email/name). Use when someone says "who is logged in", "show the current user", "get the user's email in my app", "auth in my launchpad app", "read the user identity", "/launchpad-identity", or is wiring up an /api/me for a gateway-fronted app.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-onboard
3
3
  description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-report
3
3
  description: File a bug report or feature request to the Launchpad team's tracker from the CLI. Use when someone reports something broken, hits an error in a launchpad command, or wishes a feature existed — e.g. "this is broken", "report a bug", "can you file that", "I wish launchpad could…", "/launchpad-bug", "/launchpad-feature". Always confirm and show exactly what you'll send before filing; never file silently.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-status
3
3
  description: Show whether a Launchpad app's local launchpad.yaml matches what's deployed, and read the deployed manifest. Wraps `launchpad pull` (fetch deployed YAML) and `launchpad status` (drift report). Use when someone says "is my app in sync", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or after `launchpad deploy` to verify the change landed.
4
- version: 0.44.0
4
+ version: 0.46.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->