@m-kopa/launchpad-cli 0.53.0 → 0.54.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,25 @@ pre-1.0 minor bumps may carry breaking changes per ADR 0005.
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 0.54.1 - 2026-07-29
12
+
13
+ **Deleted application files now stay deleted after deployment.**
14
+
15
+ - The legacy clone deploy path excludes tracked files that no longer exist in the working tree, allowing the server to derive their deletion from the complete upload.
16
+ - The platform applies updates and proved app-owned deletions in one Git tree while preserving governance, workflow, ownership, Wrangler, Launchpad metadata, and out-of-boundary files.
17
+ - The server uses exact managed-tree enumeration before absence can authorize deletion and returns a retryable failure when that proof is unavailable.
18
+
19
+ ## 0.54.0 - 2026-07-19
20
+
21
+ **Server-visible CLI compatibility, with dormant minimum-version enforcement.**
22
+
23
+ - Every request to the Launchpad bot now sends the real CLI release in `x-launchpad-cli-version` and the `User-Agent`, replacing the placeholder `launchpad-cli/0.x` value.
24
+ This lets the platform measure authenticated CLI adoption from server observations and reconcile it with PostHog telemetry.
25
+ - Mutating commands make one authenticated compatibility preflight before local build or upload work.
26
+ A future server-configured minimum can return HTTP `426 Upgrade Required`; the CLI then prints its version, the minimum version, and `run launchpad update`, and exits with the dedicated code `42`.
27
+ - Upload and deployment requests also handle `426` directly so a floor change between preflight and mutation cannot proceed silently.
28
+ - No minimum version is configured by this release, so the compatibility machinery is dormant and no client is blocked.
29
+
11
30
  ## 0.53.0 - 2026-07-18
12
31
 
13
32
  **Breaking: `launchpad pull` now synchronizes source.**
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/auth/discovery.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,cAAc,CAAC,CA0EzB"}
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/auth/discovery.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,cAAc,CAAC,CA0EzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-flow.d.ts","sourceRoot":"","sources":["../../src/auth/gateway-flow.ts"],"names":[],"mappings":"AA0CA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAC3D,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAE/C;;6DAE6D;AAC7D,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;CAChD;AAED;;sCAEsC;AACtC,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED;;;gCAGgC;AAChC,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAG,sBAAsB,CAAU;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,aAAa,EAAE,MAAM;CAOlC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;wBAEoB;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAkD5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAsBf"}
1
+ {"version":3,"file":"gateway-flow.d.ts","sourceRoot":"","sources":["../../src/auth/gateway-flow.ts"],"names":[],"mappings":"AA4CA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAC3D,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAE/C;;6DAE6D;AAC7D,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;CAChD;AAED;;sCAEsC;AACtC,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAU;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED;;;gCAGgC;AAChC,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAG,sBAAsB,CAAU;IAChD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;gBACnB,aAAa,EAAE,MAAM;CAOlC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;wBAEoB;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAkD5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAS3B;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACtE,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -1 +1 @@
1
- {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/auth/registration.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,oBAAoB,CAAU;CAC/C;AAED,wBAAsB,cAAc,CAClC,oBAAoB,EAAE,MAAM,EAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CA6C7B"}
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/auth/registration.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAG,oBAAoB,CAAU;CAC/C;AAED,wBAAsB,cAAc,CAClC,oBAAoB,EAAE,MAAM,EAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CA6C7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;qEACiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAU;IACvC;;qDAEiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE;IACN,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;6DAGyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,EACD,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE;IACN,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;+CAE2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,EACD,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,aAAa,CAAC,CAQxB"}
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;qEACiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAG,aAAa,CAAU;IACvC;;qDAEiD;IACjD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;CAKjD;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE;IACN,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;6DAGyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,EACD,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE;IACN,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;+CAE2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,EACD,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,aAAa,CAAC,CAQxB"}
@@ -110,6 +110,8 @@ export interface DeployBundleError extends StructuredErrorEnvelope {
110
110
  readonly head_sha?: string;
111
111
  readonly paths?: readonly string[];
112
112
  readonly reason?: string;
113
+ readonly minimum?: string;
114
+ readonly yours?: string;
113
115
  }
114
116
  export type DeployBundleResult = {
115
117
  kind: "ok";
@@ -1 +1 @@
1
- {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/bundle/upload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/bundle/upload.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,eAAO,MAAM,6BAA6B,kCACT,CAAC;AAClC,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,WAAW,GACX,SAAS,GACT,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,kEAAkE;AAClE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;CAC/B;AAgBD;;;oCAGoC;AACpC,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;qEAGqE;AACrE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACnC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC;4CACwC;IACxC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,sBAAsB,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,MAAM,mBAAmB,GAC3B,oBAAoB,GACpB,+BAA+B,GAC/B,2BAA2B,CAAC;AAEhC,iEAAiE;AACjE,MAAM,WAAW,iBAAkB,SAAQ,uBAAuB;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,oBAAoB,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,UAAU,EACvB,cAAc,CAAC,EAAE,cAAc;AAC/B;gEACgE;AAChE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;AACvB;0EAC0E;AAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;AAC7B,+EAA+E;AAC/E,cAAc,GAAE,OAAe,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CA+H7B"}
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.53.0";
22
+ var CLI_VERSION = "0.54.1";
23
23
 
24
24
  // src/config.ts
25
25
  import * as os from "node:os";
@@ -213,6 +213,22 @@ class ForbiddenError extends Error {
213
213
  class NotFoundError extends Error {
214
214
  code = "not_found";
215
215
  }
216
+ var EXIT_CLI_UPGRADE_REQUIRED = 42;
217
+
218
+ class UpgradeRequiredError extends Error {
219
+ code = "cli_upgrade_required";
220
+ minimum;
221
+ yours;
222
+ constructor(minimum, yours) {
223
+ super(upgradeRequiredMessage(minimum, yours));
224
+ this.name = "UpgradeRequiredError";
225
+ this.minimum = minimum;
226
+ this.yours = yours;
227
+ }
228
+ }
229
+ function upgradeRequiredMessage(minimum, yours) {
230
+ return `you're on ${yours}; minimum supported is ${minimum} - run launchpad update`;
231
+ }
216
232
 
217
233
  class ApiError extends Error {
218
234
  faultCode = "api_error";
@@ -363,6 +379,21 @@ function escapeHtml(s) {
363
379
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
364
380
  }
365
381
 
382
+ // src/http/version-headers.ts
383
+ var CLI_VERSION_HEADER = "x-launchpad-cli-version";
384
+ function cliVersionHeaders(headers = {}) {
385
+ const result = { ...headers };
386
+ for (const key of Object.keys(result)) {
387
+ const lower = key.toLowerCase();
388
+ if (lower === CLI_VERSION_HEADER || lower === "user-agent") {
389
+ delete result[key];
390
+ }
391
+ }
392
+ result[CLI_VERSION_HEADER] = CLI_VERSION;
393
+ result["user-agent"] = `launchpad-cli/${CLI_VERSION}`;
394
+ return result;
395
+ }
396
+
366
397
  // src/auth/discovery.ts
367
398
  class DiscoveryError extends Error {
368
399
  code = "discovery_error";
@@ -401,7 +432,10 @@ async function discoverOauthEndpoints(botUrl, fetcher = fetch) {
401
432
  async function fetchJson(fetcher, url) {
402
433
  let res;
403
434
  try {
404
- res = await fetcher(url, { method: "GET" });
435
+ res = await fetcher(url, {
436
+ method: "GET",
437
+ headers: cliVersionHeaders()
438
+ });
405
439
  } catch (e) {
406
440
  throw new DiscoveryError(`discovery: network error fetching ${url}: ${describe(e)}`);
407
441
  }
@@ -471,10 +505,10 @@ async function registerClient(registrationEndpoint, redirectUri, fetcher = fetch
471
505
  try {
472
506
  res = await fetcher(registrationEndpoint, {
473
507
  method: "POST",
474
- headers: {
508
+ headers: cliVersionHeaders({
475
509
  "content-type": "application/json",
476
510
  accept: "application/json"
477
- },
511
+ }),
478
512
  body: JSON.stringify(body)
479
513
  });
480
514
  } catch (e) {
@@ -535,10 +569,10 @@ async function postTokenForm(tokenEndpoint, form, fetcher) {
535
569
  try {
536
570
  res = await fetcher(tokenEndpoint, {
537
571
  method: "POST",
538
- headers: {
572
+ headers: cliVersionHeaders({
539
573
  "content-type": "application/x-www-form-urlencoded",
540
574
  accept: "application/json"
541
- },
575
+ }),
542
576
  body: form.toString()
543
577
  });
544
578
  } catch (e) {
@@ -805,7 +839,9 @@ async function revokeGatewaySession(params, fetcher = fetch) {
805
839
  try {
806
840
  res = await fetcher(url, {
807
841
  method: "POST",
808
- headers: { "content-type": "application/x-www-form-urlencoded" },
842
+ headers: cliVersionHeaders({
843
+ "content-type": "application/x-www-form-urlencoded"
844
+ }),
809
845
  body: new URLSearchParams({ refresh_token: params.refreshToken }).toString()
810
846
  });
811
847
  } catch (e) {
@@ -823,7 +859,10 @@ async function probeGateway(gatewayUrl, fetcher) {
823
859
  const url = `${gatewayUrl}${CLI_SESSION_AUTH_PATH}`;
824
860
  let res;
825
861
  try {
826
- res = await fetcher(url, { method: "GET" });
862
+ res = await fetcher(url, {
863
+ method: "GET",
864
+ headers: cliVersionHeaders()
865
+ });
827
866
  } catch (e) {
828
867
  throw new GatewayUnavailableError(`auth gateway unreachable at ${gatewayUrl}: ${describe5(e)}`);
829
868
  }
@@ -840,10 +879,10 @@ async function postSessionTokenForm(gatewayUrl, form, fetcher) {
840
879
  try {
841
880
  res = await fetcher(url, {
842
881
  method: "POST",
843
- headers: {
882
+ headers: cliVersionHeaders({
844
883
  "content-type": "application/x-www-form-urlencoded",
845
884
  accept: "application/json"
846
- },
885
+ }),
847
886
  body: form.toString()
848
887
  });
849
888
  } catch (e) {
@@ -1054,12 +1093,11 @@ async function apiRaw(cfg, opts, fetcher = fetch) {
1054
1093
  }
1055
1094
  throw e;
1056
1095
  }
1057
- const headers = {
1096
+ const headers = cliVersionHeaders({
1058
1097
  accept: "application/json",
1059
1098
  ...opts.headers,
1060
- authorization: `Bearer ${token}`,
1061
- "user-agent": "launchpad-cli/0.x"
1062
- };
1099
+ authorization: `Bearer ${token}`
1100
+ });
1063
1101
  for (const k of Object.keys(headers)) {
1064
1102
  if (k !== "authorization" && k.toLowerCase() === "authorization") {
1065
1103
  delete headers[k];
@@ -1083,6 +1121,9 @@ async function apiRaw(cfg, opts, fetcher = fetch) {
1083
1121
  } catch (e) {
1084
1122
  throw recordedFault(new TransportError(`network error calling ${url}: ${describe7(e)}`));
1085
1123
  }
1124
+ if (res.status === 426) {
1125
+ throw recordedFault(await decodeUpgradeRequired(res, opts.path));
1126
+ }
1086
1127
  if (res.status === 401) {
1087
1128
  const detail = await errorDetail(res, opts.path);
1088
1129
  throw recordedFault(new UnauthenticatedError(`bot returned 401 for ${opts.path}: ${detail} — run \`launchpad login\``));
@@ -1104,6 +1145,18 @@ async function apiRaw(cfg, opts, fetcher = fetch) {
1104
1145
  clearFault();
1105
1146
  return res;
1106
1147
  }
1148
+ async function decodeUpgradeRequired(response, endpoint) {
1149
+ const body = await readErrorText(response);
1150
+ if (body.kind === "text") {
1151
+ try {
1152
+ const parsed = JSON.parse(body.text);
1153
+ if (typeof parsed.minimum === "string" && typeof parsed.yours === "string") {
1154
+ return new UpgradeRequiredError(parsed.minimum, parsed.yours);
1155
+ }
1156
+ } catch {}
1157
+ }
1158
+ return new ApiError(decodeErrorEnvelope({ code: "cli_upgrade_required", message: "CLI upgrade required" }, { status: 426, endpoint }));
1159
+ }
1107
1160
  async function readErrorText(res) {
1108
1161
  const reader = res.body?.getReader();
1109
1162
  if (reader === undefined)
@@ -1814,7 +1867,7 @@ function describe10(e) {
1814
1867
  }
1815
1868
 
1816
1869
  // src/commands/deploy.ts
1817
- import { existsSync as existsSync6 } from "node:fs";
1870
+ import { existsSync as existsSync7 } from "node:fs";
1818
1871
  import * as path8 from "node:path";
1819
1872
 
1820
1873
  // src/bundle/orchestrate.ts
@@ -1995,11 +2048,11 @@ async function uploadBundle(cfg, slug, manifestYaml, bundleTarGz, workerArtifact
1995
2048
  }
1996
2049
  const url = `${cfg.botUrl}/apps/${slug}/deploy/bundle`;
1997
2050
  const { accessToken } = await getValidAccessToken(cfg.sessionPath);
1998
- const headers = {
2051
+ const headers = cliVersionHeaders({
1999
2052
  authorization: `Bearer ${accessToken}`,
2000
2053
  accept: "application/json",
2001
2054
  "x-launchpad-capabilities": `${ACCESS_RECONCILE_EXACT_SHA_V1},${SOURCE_PROVENANCE_V1}`
2002
- };
2055
+ });
2003
2056
  if (baseSha)
2004
2057
  headers["x-launchpad-base-sha"] = baseSha;
2005
2058
  if (overrideStale)
@@ -2128,7 +2181,9 @@ function deployBundleError(envelope, body) {
2128
2181
  ...manifestOnly === undefined ? {} : { manifest_only: manifestOnly },
2129
2182
  ...typeof body.head_sha === "string" ? { head_sha: body.head_sha } : {},
2130
2183
  ...paths === undefined ? {} : { paths },
2131
- ...typeof body.reason === "string" ? { reason: body.reason } : {}
2184
+ ...typeof body.reason === "string" ? { reason: body.reason } : {},
2185
+ ...typeof body.minimum === "string" ? { minimum: body.minimum } : {},
2186
+ ...typeof body.yours === "string" ? { yours: body.yours } : {}
2132
2187
  };
2133
2188
  }
2134
2189
  function readStringArray(value) {
@@ -2602,6 +2657,7 @@ var ManifestSchema = z.object({
2602
2657
  app: AppBoundarySchema.optional(),
2603
2658
  verify: VerifySchema.optional(),
2604
2659
  confine_origin: z.boolean().optional(),
2660
+ origin: z.enum(["pages", "dispatch"]).optional(),
2605
2661
  catalogue: z.object({ listed: z.boolean().default(true) }).strict().optional(),
2606
2662
  sensitive: z.boolean().optional()
2607
2663
  }).strict().superRefine((m, ctx) => {
@@ -2992,7 +3048,9 @@ var PLATFORM_SEEDED_GITHUB_PATHS = new Set([
2992
3048
  ".github/scripts/parse-tsc.mjs",
2993
3049
  ".github/scripts/parse-vitest.mjs",
2994
3050
  ".github/scripts/parse-gitleaks.mjs",
2995
- ".github/scripts/parse-bun-audit.mjs"
3051
+ ".github/scripts/parse-bun-audit.mjs",
3052
+ ".github/scripts/parse-html-validate.mjs",
3053
+ ".github/scripts/parse-hadolint.mjs"
2996
3054
  ]);
2997
3055
  function checkDenyList(path7) {
2998
3056
  const segments = path7.split("/");
@@ -3235,6 +3293,7 @@ var SpecSchema = z2.object({
3235
3293
  app: AppBoundarySchema.optional(),
3236
3294
  verify: VerifySchema.optional(),
3237
3295
  confine_origin: z2.boolean().optional(),
3296
+ origin: z2.enum(["pages", "dispatch"]).optional(),
3238
3297
  sensitive: z2.boolean().optional(),
3239
3298
  catalogue: z2.object({ listed: z2.boolean().default(true) }).strict().optional(),
3240
3299
  env_vars: z2.record(z2.string().regex(ENV_NAME_REGEX, "env_vars keys must be UPPER_SNAKE_CASE"), EnvVarSchema).optional(),
@@ -3280,6 +3339,43 @@ var SpecSchema = z2.object({
3280
3339
  });
3281
3340
  }
3282
3341
  }
3342
+ if (s.origin === "dispatch") {
3343
+ if (isContainer) {
3344
+ ctx.addIssue({
3345
+ code: z2.ZodIssueCode.custom,
3346
+ path: ["origin"],
3347
+ message: "origin: dispatch is not available for container apps — they run on the container runtime (ADR 0015/0016), not in a Workers dispatch namespace."
3348
+ });
3349
+ }
3350
+ if ((s.auth ?? "access") !== "gateway") {
3351
+ ctx.addIssue({
3352
+ code: z2.ZodIssueCode.custom,
3353
+ path: ["origin"],
3354
+ message: "origin: dispatch requires an explicit auth: gateway (auth defaults to 'access' when omitted). A dispatch-namespace Worker has no public hostname for Cloudflare Access to gate, so the gateway must be the sole gate."
3355
+ });
3356
+ }
3357
+ if (s.confine_origin !== undefined) {
3358
+ ctx.addIssue({
3359
+ code: z2.ZodIssueCode.custom,
3360
+ path: ["confine_origin"],
3361
+ message: "confine_origin has no meaning with origin: dispatch — there is no public pages.dev apex to confine. Remove it."
3362
+ });
3363
+ }
3364
+ if (s.env_vars !== undefined) {
3365
+ ctx.addIssue({
3366
+ code: z2.ZodIssueCode.custom,
3367
+ path: ["env_vars"],
3368
+ message: "env_vars are not yet supported with origin: dispatch — a dispatch workspace has no Pages module to apply them, and accepting them here would silently drop them. Worker-level vars land with the v2 deploy path."
3369
+ });
3370
+ }
3371
+ if (s.build !== undefined) {
3372
+ ctx.addIssue({
3373
+ code: z2.ZodIssueCode.custom,
3374
+ path: ["build"],
3375
+ message: "build is not yet supported with origin: dispatch — a dispatch workspace has no Pages module to carry it, and the v2 deploy path that will consume it is not shipped yet. Accepting it here would silently drop it."
3376
+ });
3377
+ }
3378
+ }
3283
3379
  if (!isContainer && s.containers !== undefined) {
3284
3380
  ctx.addIssue({
3285
3381
  code: z2.ZodIssueCode.custom,
@@ -6130,6 +6226,8 @@ import { readFileSync as readFileSync10 } from "node:fs";
6130
6226
 
6131
6227
  // src/deploy/git-files.ts
6132
6228
  import { spawn as spawn3 } from "node:child_process";
6229
+ import { existsSync as existsSync5 } from "node:fs";
6230
+ import { resolve as resolve7 } from "node:path";
6133
6231
 
6134
6232
  class GitFilesError extends Error {
6135
6233
  code = "git_files_error";
@@ -6147,6 +6245,7 @@ var PROTECTED_PATHS = new Set([
6147
6245
  ]);
6148
6246
  async function listDeployFiles(opts) {
6149
6247
  const sp = opts.spawner ?? spawn3;
6248
+ const exists = opts.exists ?? existsSync5;
6150
6249
  const stdout = await runGit2(sp, opts.cwd, [
6151
6250
  "ls-files",
6152
6251
  "-c",
@@ -6154,11 +6253,11 @@ async function listDeployFiles(opts) {
6154
6253
  "--exclude-standard",
6155
6254
  "-z"
6156
6255
  ]);
6157
- const paths = stdout.split("\x00").filter((p) => p.length > 0 && !PROTECTED_PATHS.has(p));
6256
+ const paths = stdout.split("\x00").filter((p) => p.length > 0 && exists(resolve7(opts.cwd, p)) && !PROTECTED_PATHS.has(p));
6158
6257
  return [...paths].sort();
6159
6258
  }
6160
6259
  function runGit2(sp, cwd, args) {
6161
- return new Promise((resolve7, reject) => {
6260
+ return new Promise((resolve8, reject) => {
6162
6261
  const spawnOpts = { cwd, stdio: ["ignore", "pipe", "pipe"] };
6163
6262
  const child = sp("git", [...args], spawnOpts);
6164
6263
  let stdout = "";
@@ -6174,7 +6273,7 @@ function runGit2(sp, cwd, args) {
6174
6273
  });
6175
6274
  child.once("close", (code) => {
6176
6275
  if (code === 0) {
6177
- resolve7(stdout);
6276
+ resolve8(stdout);
6178
6277
  return;
6179
6278
  }
6180
6279
  reject(new GitFilesError(`\`git ${args.join(" ")}\` exited ${code}: ${stderr.trim()}`));
@@ -6751,7 +6850,7 @@ function parseDeployFlags(args) {
6751
6850
  }
6752
6851
 
6753
6852
  // src/deploy/apply.ts
6754
- import { existsSync as existsSync5, rmSync } from "node:fs";
6853
+ import { existsSync as existsSync6, rmSync } from "node:fs";
6755
6854
  import { resolve as resolvePath, join as join10 } from "node:path";
6756
6855
 
6757
6856
  // src/manifest/load.ts
@@ -7118,7 +7217,7 @@ function sleep(ms) {
7118
7217
  }
7119
7218
  function deletePinIfPresent(cfg, slug, io) {
7120
7219
  const pinPath = join10(cfg.stateDir, slug, "group.json");
7121
- if (!existsSync5(pinPath))
7220
+ if (!existsSync6(pinPath))
7122
7221
  return;
7123
7222
  try {
7124
7223
  rmSync(pinPath, { force: true });
@@ -7256,12 +7355,12 @@ async function handleAccessDrift(params) {
7256
7355
 
7257
7356
  // src/deploy/dry-run.ts
7258
7357
  import { execFileSync } from "node:child_process";
7259
- import { resolve as resolve7 } from "node:path";
7358
+ import { resolve as resolve8 } from "node:path";
7260
7359
  var MANIFEST_SHA_REGEX2 = /^[0-9a-f]{40}$/;
7261
7360
  async function runDeployDryRun(opts, io, deps = {}) {
7262
7361
  const cfg = deps.config ?? loadConfig();
7263
7362
  const fetcher = deps.fetcher ?? fetch;
7264
- const manifestPath = resolve7(process.cwd(), opts.file ?? "launchpad.yaml");
7363
+ const manifestPath = resolve8(process.cwd(), opts.file ?? "launchpad.yaml");
7265
7364
  const loaded = loadManifest(manifestPath);
7266
7365
  if (loaded.kind !== "ok") {
7267
7366
  return renderManifestError2(loaded, opts.json, io);
@@ -7671,7 +7770,7 @@ async function runDeploy(args, io) {
7671
7770
  }
7672
7771
  const cwd = process.cwd();
7673
7772
  const manifestPath = path8.join(cwd, "launchpad.yaml");
7674
- if (existsSync6(manifestPath)) {
7773
+ if (existsSync7(manifestPath)) {
7675
7774
  const contentMode = flags.mode.kind === "content" ? flags.mode : {
7676
7775
  allowStale: false,
7677
7776
  rebaseOntoHead: false,
@@ -7725,7 +7824,7 @@ async function runDeploy(args, io) {
7725
7824
  }
7726
7825
  let contentManifestYaml = null;
7727
7826
  const contentManifestPath = path8.join(process.cwd(), "launchpad.yaml");
7728
- if (existsSync6(contentManifestPath)) {
7827
+ if (existsSync7(contentManifestPath)) {
7729
7828
  try {
7730
7829
  contentManifestYaml = readFileSync10(contentManifestPath, "utf8");
7731
7830
  } catch {
@@ -7773,6 +7872,10 @@ async function runDeploy(args, io) {
7773
7872
  }
7774
7873
  return 0;
7775
7874
  } catch (e) {
7875
+ if (e instanceof UpgradeRequiredError) {
7876
+ io.err(e.message);
7877
+ return EXIT_CLI_UPGRADE_REQUIRED;
7878
+ }
7776
7879
  if (e instanceof UnauthenticatedError) {
7777
7880
  io.err(e.message);
7778
7881
  return 1;
@@ -7944,11 +8047,11 @@ function surfaceStaleBlock(body, io) {
7944
8047
  }
7945
8048
  var MANIFEST_DIFF_READ_TIMEOUT_MS = 5000;
7946
8049
  function withTimeout(p, ms) {
7947
- return new Promise((resolve8, reject) => {
8050
+ return new Promise((resolve9, reject) => {
7948
8051
  const timer = setTimeout(() => reject(new Error(`manifest read timed out after ${ms}ms`)), ms);
7949
8052
  p.then((v) => {
7950
8053
  clearTimeout(timer);
7951
- resolve8(v);
8054
+ resolve9(v);
7952
8055
  }, (e) => {
7953
8056
  clearTimeout(timer);
7954
8057
  reject(e instanceof Error ? e : new Error(String(e)));
@@ -8086,6 +8189,10 @@ async function runModelADeploy(args) {
8086
8189
  }
8087
8190
  }
8088
8191
  } catch (e) {
8192
+ if (e instanceof UpgradeRequiredError) {
8193
+ io.err(e.message);
8194
+ return EXIT_CLI_UPGRADE_REQUIRED;
8195
+ }
8089
8196
  if (e instanceof UnauthenticatedError) {
8090
8197
  io.err(`launchpad deploy: ${e.message}`);
8091
8198
  io.err(" run `launchpad login` to refresh your session.");
@@ -8113,6 +8220,10 @@ async function runModelADeploy(args) {
8113
8220
  case "upload-error": {
8114
8221
  const body = result.body;
8115
8222
  const errorCode = body.error;
8223
+ if (result.status === 426 && typeof body.minimum === "string" && typeof body.yours === "string") {
8224
+ io.err(upgradeRequiredMessage(body.minimum, body.yours));
8225
+ return EXIT_CLI_UPGRADE_REQUIRED;
8226
+ }
8116
8227
  if (result.status === 409 && errorCode === "slug_in_flight") {
8117
8228
  io.err(`launchpad deploy: slug "${slug}" is already provisioning.`);
8118
8229
  if (typeof body.message === "string") {
@@ -8299,7 +8410,7 @@ function pendingReconcileOutcome(body) {
8299
8410
  }
8300
8411
 
8301
8412
  // src/commands/redeploy.ts
8302
- import { existsSync as existsSync7, readFileSync as readFileSync11 } from "node:fs";
8413
+ import { existsSync as existsSync8, readFileSync as readFileSync11 } from "node:fs";
8303
8414
  import { resolve as resolvePath2 } from "node:path";
8304
8415
  import { parse as parseYaml7 } from "yaml";
8305
8416
  var SLUG_RE6 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
@@ -8377,7 +8488,7 @@ async function runRedeploy(opts, io, deps = {}) {
8377
8488
  }
8378
8489
  function readLocalAppType(cwd, file) {
8379
8490
  const manifestPath = resolvePath2(cwd, file ?? "launchpad.yaml");
8380
- if (!existsSync7(manifestPath))
8491
+ if (!existsSync8(manifestPath))
8381
8492
  return null;
8382
8493
  try {
8383
8494
  const obj = parseYaml7(readFileSync11(manifestPath, "utf8"));
@@ -8665,7 +8776,7 @@ function describe16(e) {
8665
8776
 
8666
8777
  // src/commands/generate.ts
8667
8778
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
8668
- import { dirname as dirname6, resolve as resolve8, relative as relative3 } from "node:path";
8779
+ import { dirname as dirname6, resolve as resolve9, relative as relative3 } from "node:path";
8669
8780
  var generateCommand = {
8670
8781
  name: "generate",
8671
8782
  summary: "emit derived artefacts (wrangler.toml, deploy.yml) from launchpad.yaml",
@@ -8678,14 +8789,14 @@ async function runGenerate(args, io) {
8678
8789
  io.err("Usage: launchpad generate [--file <path>] [--dry-run] [--force] [--json]");
8679
8790
  return 64;
8680
8791
  }
8681
- const manifestPath = resolve8(process.cwd(), flags.file ?? "launchpad.yaml");
8792
+ const manifestPath = resolve9(process.cwd(), flags.file ?? "launchpad.yaml");
8682
8793
  const result = loadManifest(manifestPath);
8683
8794
  if (result.kind !== "ok") {
8684
8795
  return flags.json ? renderManifestErrorJson(result, io) : renderManifestErrorHuman(result, io);
8685
8796
  }
8686
8797
  const appRoot = dirname6(manifestPath);
8687
- const wranglerPath = resolve8(appRoot, "container", "wrangler.toml");
8688
- const workflowPath = resolve8(appRoot, ".github", "workflows", "deploy.yml");
8798
+ const wranglerPath = resolve9(appRoot, "container", "wrangler.toml");
8799
+ const workflowPath = resolve9(appRoot, ".github", "workflows", "deploy.yml");
8689
8800
  const wranglerOut = generateWranglerToml(result.manifest);
8690
8801
  const workflowOut = generateGithubDeployWorkflow(result.manifest);
8691
8802
  if (flags.dryRun) {
@@ -9490,17 +9601,17 @@ function describe19(e) {
9490
9601
 
9491
9602
  // src/commands/init.ts
9492
9603
  import { createInterface } from "node:readline/promises";
9493
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
9494
- import { dirname as dirname7, resolve as resolve9 } from "node:path";
9604
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
9605
+ import { dirname as dirname7, resolve as resolve10 } from "node:path";
9495
9606
  import { stringify as yamlStringify } from "yaml";
9496
9607
 
9497
9608
  // src/detect/index.ts
9498
- import { existsSync as existsSync8, readFileSync as readFileSync13, statSync } from "node:fs";
9609
+ import { existsSync as existsSync9, readFileSync as readFileSync13, statSync } from "node:fs";
9499
9610
  import { join as join12 } from "node:path";
9500
9611
  function detectAppShape(cwd) {
9501
- const hasPackageJson = existsSync8(join12(cwd, "package.json"));
9502
- const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync8(join12(cwd, file)));
9503
- const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync8(join12(cwd, n)));
9612
+ const hasPackageJson = existsSync9(join12(cwd, "package.json"));
9613
+ const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync9(join12(cwd, file)));
9614
+ const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync9(join12(cwd, n)));
9504
9615
  if (!hasPackageJson && !hasAnyLockfile && !hasViteConfig) {
9505
9616
  return {
9506
9617
  kind: "not-applicable",
@@ -9546,7 +9657,7 @@ var LOCKFILES = [
9546
9657
  { file: "yarn.lock", pm: "yarn" }
9547
9658
  ];
9548
9659
  function detectPackageManager(cwd) {
9549
- const present = LOCKFILES.filter(({ file }) => existsSync8(join12(cwd, file)));
9660
+ const present = LOCKFILES.filter(({ file }) => existsSync9(join12(cwd, file)));
9550
9661
  if (present.length === 0) {
9551
9662
  return {
9552
9663
  kind: "ambiguous",
@@ -9569,7 +9680,7 @@ function detectPackageManager(cwd) {
9569
9680
  function detectVitePresence(cwd) {
9570
9681
  for (const name of VITE_CONFIG_NAMES) {
9571
9682
  const p = join12(cwd, name);
9572
- if (existsSync8(p)) {
9683
+ if (existsSync9(p)) {
9573
9684
  return { kind: "ok", value: { path: p } };
9574
9685
  }
9575
9686
  }
@@ -9581,7 +9692,7 @@ function detectVitePresence(cwd) {
9581
9692
  function detectAppType(cwd) {
9582
9693
  const fnDir = join12(cwd, "functions");
9583
9694
  let hasFunctionsDir = false;
9584
- if (existsSync8(fnDir)) {
9695
+ if (existsSync9(fnDir)) {
9585
9696
  try {
9586
9697
  hasFunctionsDir = statSync(fnDir).isDirectory();
9587
9698
  } catch {
@@ -9592,7 +9703,7 @@ function detectAppType(cwd) {
9592
9703
  }
9593
9704
  function detectBuildCommand(cwd, pm) {
9594
9705
  const pkgJsonPath = join12(cwd, "package.json");
9595
- if (!existsSync8(pkgJsonPath)) {
9706
+ if (!existsSync9(pkgJsonPath)) {
9596
9707
  return {
9597
9708
  kind: "ambiguous",
9598
9709
  reason: "no package.json at repo root. Run your package manager's `init` first."
@@ -9664,8 +9775,8 @@ async function runInit(args, io, prompt) {
9664
9775
  return 64;
9665
9776
  }
9666
9777
  const { inputs, options } = parsed;
9667
- const outPath = resolve9(process.cwd(), options.out);
9668
- if (existsSync9(outPath) && !options.force) {
9778
+ const outPath = resolve10(process.cwd(), options.out);
9779
+ if (existsSync10(outPath) && !options.force) {
9669
9780
  io.err(`launchpad init: ${outPath} already exists`);
9670
9781
  io.err("Pass --force to overwrite.");
9671
9782
  return 64;
@@ -9712,7 +9823,7 @@ async function runInit(args, io, prompt) {
9712
9823
  }
9713
9824
  io.out(`✓ wrote ${outPath}`);
9714
9825
  if (options.gitignore) {
9715
- const gitignorePath = resolve9(process.cwd(), ".gitignore");
9826
+ const gitignorePath = resolve10(process.cwd(), ".gitignore");
9716
9827
  try {
9717
9828
  const changed = ensureGitignoreEntries(gitignorePath, [".env", ".env.local"]);
9718
9829
  if (changed.length > 0) {
@@ -9727,9 +9838,9 @@ async function runInit(args, io, prompt) {
9727
9838
  }
9728
9839
  }
9729
9840
  if (fillResult.inputs.type === "static") {
9730
- const serveDir = resolve9(dirname7(outPath), STATIC_SERVE_DIR);
9731
- const indexPath = resolve9(serveDir, "index.html");
9732
- if (!existsSync9(indexPath)) {
9841
+ const serveDir = resolve10(dirname7(outPath), STATIC_SERVE_DIR);
9842
+ const indexPath = resolve10(serveDir, "index.html");
9843
+ if (!existsSync10(indexPath)) {
9733
9844
  try {
9734
9845
  mkdirSync3(serveDir, { recursive: true });
9735
9846
  writeFileSync3(indexPath, starterIndexHtml(fillResult.inputs.name), {
@@ -10121,7 +10232,7 @@ function renderYaml(manifest) {
10121
10232
  }
10122
10233
  function ensureGitignoreEntries(path10, entries) {
10123
10234
  let current = "";
10124
- if (existsSync9(path10)) {
10235
+ if (existsSync10(path10)) {
10125
10236
  current = readFileSync14(path10, "utf8");
10126
10237
  }
10127
10238
  const lines = current.split(/\r?\n/);
@@ -10650,7 +10761,7 @@ function describe23(e) {
10650
10761
  }
10651
10762
 
10652
10763
  // src/commands/plan.ts
10653
- import { resolve as resolve10 } from "node:path";
10764
+ import { resolve as resolve11 } from "node:path";
10654
10765
  var planCommand = {
10655
10766
  name: "plan",
10656
10767
  summary: "summarise what the manifest would deploy (offline)",
@@ -10663,7 +10774,7 @@ async function runPlan(args, io) {
10663
10774
  io.err("Usage: launchpad plan [--file <path>] [--json]");
10664
10775
  return 64;
10665
10776
  }
10666
- const manifestPath = resolve10(process.cwd(), flags.file ?? "launchpad.yaml");
10777
+ const manifestPath = resolve11(process.cwd(), flags.file ?? "launchpad.yaml");
10667
10778
  const result = loadManifest(manifestPath);
10668
10779
  return flags.json ? renderJson(result, io) : renderHuman(result, io);
10669
10780
  }
@@ -11253,12 +11364,12 @@ async function listFiles(root, relative5 = "") {
11253
11364
  return files.sort();
11254
11365
  }
11255
11366
  function gitOutput(cwd, args) {
11256
- return new Promise((resolve12, reject) => {
11367
+ return new Promise((resolve13, reject) => {
11257
11368
  execFile("git", [...args], { cwd, encoding: "utf8" }, (error, stdout) => {
11258
11369
  if (error !== null)
11259
11370
  reject(error);
11260
11371
  else
11261
- resolve12(stdout);
11372
+ resolve13(stdout);
11262
11373
  });
11263
11374
  });
11264
11375
  }
@@ -12193,7 +12304,7 @@ function describe26(e) {
12193
12304
  }
12194
12305
 
12195
12306
  // src/deploy/rollback.ts
12196
- import { existsSync as existsSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
12307
+ import { existsSync as existsSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
12197
12308
  import { resolve as resolvePath3 } from "node:path";
12198
12309
  import { createInterface as createInterface3 } from "node:readline/promises";
12199
12310
 
@@ -12318,7 +12429,7 @@ async function runRollback(opts, io, deps = {}) {
12318
12429
  }, io, applyDeps);
12319
12430
  }
12320
12431
  function readCurrentManifest(path14) {
12321
- if (!existsSync10(path14))
12432
+ if (!existsSync11(path14))
12322
12433
  return null;
12323
12434
  let raw;
12324
12435
  try {
@@ -12478,7 +12589,7 @@ function parseArgs12(args) {
12478
12589
  }
12479
12590
 
12480
12591
  // src/secrets/push.ts
12481
- import { existsSync as existsSync11, readFileSync as readFileSync16 } from "node:fs";
12592
+ import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
12482
12593
  import { resolve as resolvePath4 } from "node:path";
12483
12594
  import { createInterface as createInterface4 } from "node:readline/promises";
12484
12595
  import { stdin as input2, stdout as output2 } from "node:process";
@@ -12535,7 +12646,7 @@ async function runSecretsPush(opts, io, deps = {}) {
12535
12646
  return 0;
12536
12647
  }
12537
12648
  const envPath = resolvePath4(process.cwd(), opts.env ?? ".env");
12538
- if (!existsSync11(envPath)) {
12649
+ if (!existsSync12(envPath)) {
12539
12650
  io.err(`launchpad secrets push: ${envPath}`);
12540
12651
  io.err(" .env file not found. Run `launchpad secrets template` to scaffold one.");
12541
12652
  return 2;
@@ -12867,7 +12978,7 @@ function renderManifestError5(result, io) {
12867
12978
  }
12868
12979
 
12869
12980
  // src/secrets/set.ts
12870
- import { existsSync as existsSync12, readFileSync as readFileSync17 } from "node:fs";
12981
+ import { existsSync as existsSync13, readFileSync as readFileSync17 } from "node:fs";
12871
12982
  import { resolve as resolvePath6 } from "node:path";
12872
12983
  import { parse as parseYaml8 } from "yaml";
12873
12984
  var CELL_LABEL2 = {
@@ -12877,7 +12988,7 @@ var CELL_LABEL2 = {
12877
12988
  };
12878
12989
  function loadSet(fleetFile, setName, io) {
12879
12990
  const path14 = resolvePath6(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
12880
- if (!existsSync12(path14)) {
12991
+ if (!existsSync13(path14)) {
12881
12992
  io.err(`✗ ${path14}`);
12882
12993
  io.err(" fleet-secret-sets.yaml not found. Run from the platform repo root or pass --fleet-file.");
12883
12994
  return 2;
@@ -12969,7 +13080,7 @@ async function runSecretsPushSet(opts, io, deps = {}) {
12969
13080
  if (typeof set === "number")
12970
13081
  return set;
12971
13082
  const envPath = resolvePath6(process.cwd(), opts.env ?? ".env");
12972
- if (!existsSync12(envPath)) {
13083
+ if (!existsSync13(envPath)) {
12973
13084
  io.err(`launchpad secrets push --set: ${envPath} not found.`);
12974
13085
  io.err(` Create a .env carrying the set's secrets: ${set.secrets.join(", ")}`);
12975
13086
  return 2;
@@ -13120,8 +13231,8 @@ function setPushExit(e) {
13120
13231
  }
13121
13232
 
13122
13233
  // src/commands/secrets-template.ts
13123
- import { existsSync as existsSync13, writeFileSync as writeFileSync6 } from "node:fs";
13124
- import { resolve as resolve12 } from "node:path";
13234
+ import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "node:fs";
13235
+ import { resolve as resolve13 } from "node:path";
13125
13236
  async function runSecretsTemplate(args, io) {
13126
13237
  const flags = parseFlags4(args);
13127
13238
  if (flags.kind === "usage-error") {
@@ -13129,7 +13240,7 @@ async function runSecretsTemplate(args, io) {
13129
13240
  io.err("Usage: launchpad secrets template [--file <path>] [--out <path>] " + "[--stdout] [--force] [--include-platform-managed]");
13130
13241
  return 64;
13131
13242
  }
13132
- const manifestPath = resolve12(process.cwd(), flags.file ?? "launchpad.yaml");
13243
+ const manifestPath = resolve13(process.cwd(), flags.file ?? "launchpad.yaml");
13133
13244
  const result = loadManifest(manifestPath);
13134
13245
  const renderResult = renderManifest(result, io);
13135
13246
  if (renderResult.kind !== "ok") {
@@ -13143,8 +13254,8 @@ async function runSecretsTemplate(args, io) {
13143
13254
  }
13144
13255
  return 0;
13145
13256
  }
13146
- const outPath = resolve12(process.cwd(), flags.out);
13147
- if (existsSync13(outPath) && !flags.force) {
13257
+ const outPath = resolve13(process.cwd(), flags.out);
13258
+ if (existsSync14(outPath) && !flags.force) {
13148
13259
  io.err(`launchpad secrets template: ${outPath} already exists`);
13149
13260
  io.err("Pass --force to overwrite, or --stdout to print without writing.");
13150
13261
  return 64;
@@ -13440,8 +13551,8 @@ function printHelp3(io) {
13440
13551
 
13441
13552
  // src/commands/skills.ts
13442
13553
  import { fileURLToPath } from "node:url";
13443
- import { dirname as dirname9, join as join15, resolve as resolve13 } from "node:path";
13444
- import { promises as fs7, existsSync as existsSync14 } from "node:fs";
13554
+ import { dirname as dirname9, join as join15, resolve as resolve14 } from "node:path";
13555
+ import { promises as fs7, existsSync as existsSync15 } from "node:fs";
13445
13556
 
13446
13557
  // src/skills-bundle.ts
13447
13558
  import { homedir as homedir2 } from "node:os";
@@ -13546,11 +13657,11 @@ function resolveInstallEnv() {
13546
13657
  function defaultBundleDir() {
13547
13658
  const here = dirname9(fileURLToPath(import.meta.url));
13548
13659
  const candidates = [
13549
- resolve13(here, "..", "skills"),
13550
- resolve13(here, "..", "..", "skills")
13660
+ resolve14(here, "..", "skills"),
13661
+ resolve14(here, "..", "..", "skills")
13551
13662
  ];
13552
13663
  for (const c of candidates) {
13553
- if (existsSync14(join15(c, "launchpad-onboard", "SKILL.md"))) {
13664
+ if (existsSync15(join15(c, "launchpad-onboard", "SKILL.md"))) {
13554
13665
  return c;
13555
13666
  }
13556
13667
  }
@@ -13659,7 +13770,7 @@ function describe29(e) {
13659
13770
  import { execFile as execFile2, spawn as spawn5 } from "node:child_process";
13660
13771
  import { promisify } from "node:util";
13661
13772
  import { fileURLToPath as fileURLToPath2 } from "node:url";
13662
- import { dirname as dirname10, resolve as resolve14, relative as relative5, isAbsolute as isAbsolute3, join as join16 } from "node:path";
13773
+ import { dirname as dirname10, resolve as resolve15, relative as relative5, isAbsolute as isAbsolute3, join as join16 } from "node:path";
13663
13774
  import { homedir as homedir3, tmpdir } from "node:os";
13664
13775
  import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
13665
13776
 
@@ -13704,9 +13815,9 @@ async function startLoopback(state, timeoutMs) {
13704
13815
  resolveCode(code);
13705
13816
  }
13706
13817
  });
13707
- const bound = await new Promise((resolve14) => {
13708
- server.once("error", () => resolve14(false));
13709
- server.listen(0, "127.0.0.1", () => resolve14(true));
13818
+ const bound = await new Promise((resolve15) => {
13819
+ server.once("error", () => resolve15(false));
13820
+ server.listen(0, "127.0.0.1", () => resolve15(true));
13710
13821
  });
13711
13822
  if (!bound)
13712
13823
  return null;
@@ -13999,7 +14110,7 @@ function errStderr(e) {
13999
14110
  return "";
14000
14111
  }
14001
14112
  async function detectPackageManager2() {
14002
- const pkgRoot = resolve14(dirname10(fileURLToPath2(import.meta.url)), "..", "..");
14113
+ const pkgRoot = resolve15(dirname10(fileURLToPath2(import.meta.url)), "..", "..");
14003
14114
  const candidates = [];
14004
14115
  const npmRoot = await pmRoot("npm");
14005
14116
  if (npmRoot !== null)
@@ -14007,7 +14118,7 @@ async function detectPackageManager2() {
14007
14118
  const pnpmRoot = await pmRoot("pnpm");
14008
14119
  if (pnpmRoot !== null)
14009
14120
  candidates.push(["pnpm", pnpmRoot]);
14010
- candidates.push(["bun", resolve14(homedir3(), ".bun/install/global/node_modules")]);
14121
+ candidates.push(["bun", resolve15(homedir3(), ".bun/install/global/node_modules")]);
14011
14122
  const matches = candidates.filter(([, root]) => pathContains(root, pkgRoot));
14012
14123
  return matches.length === 1 ? matches[0][0] : null;
14013
14124
  }
@@ -14144,8 +14255,8 @@ function printHelp5(io) {
14144
14255
  }
14145
14256
 
14146
14257
  // src/commands/validate.ts
14147
- import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
14148
- import { dirname as dirname11, resolve as resolve15 } from "node:path";
14258
+ import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
14259
+ import { dirname as dirname11, resolve as resolve16 } from "node:path";
14149
14260
  var validateCommand = {
14150
14261
  name: "validate",
14151
14262
  summary: "validate launchpad.yaml against the v1alpha1 schema",
@@ -14159,7 +14270,7 @@ async function runValidate(args, io) {
14159
14270
  return 64;
14160
14271
  }
14161
14272
  const { file, json, strictGroups } = parseResult;
14162
- const path14 = resolve15(process.cwd(), file ?? "launchpad.yaml");
14273
+ const path14 = resolve16(process.cwd(), file ?? "launchpad.yaml");
14163
14274
  const result = loadManifest(path14);
14164
14275
  if (result.kind !== "ok") {
14165
14276
  return json ? renderJsonError(result, io) : renderHumanError(result, io);
@@ -14206,8 +14317,8 @@ function checkStaticServeDir(manifestPath, manifest) {
14206
14317
  ];
14207
14318
  }
14208
14319
  const dir = dirname11(manifestPath);
14209
- const abs = resolve15(dir, serveDir);
14210
- if (!existsSync15(abs)) {
14320
+ const abs = resolve16(dir, serveDir);
14321
+ if (!existsSync16(abs)) {
14211
14322
  return [
14212
14323
  `static app serves '${serveDir}/' but that directory does not exist — nothing would be served. ` + `Create ${serveDir}/index.html (or run \`launchpad init\` to scaffold it).`
14213
14324
  ];
@@ -14232,7 +14343,7 @@ function checkStaticServeDir(manifestPath, manifest) {
14232
14343
  `static app's served directory '${serveDir}/' is empty — the deploy would serve an empty site.`
14233
14344
  ];
14234
14345
  }
14235
- if (!existsSync15(resolve15(abs, "index.html"))) {
14346
+ if (!existsSync16(resolve16(abs, "index.html"))) {
14236
14347
  return [
14237
14348
  `static app's served directory '${serveDir}/' has no index.html — the deploy would serve a directory with no entrypoint.`
14238
14349
  ];
@@ -15103,7 +15214,7 @@ import { spawn as spawn6 } from "node:child_process";
15103
15214
  import { homedir as homedir4 } from "node:os";
15104
15215
  import { join as join17 } from "node:path";
15105
15216
  import {
15106
- existsSync as existsSync16,
15217
+ existsSync as existsSync17,
15107
15218
  mkdirSync as mkdirSync4,
15108
15219
  readFileSync as readFileSync21,
15109
15220
  writeFileSync as writeFileSync8
@@ -15135,7 +15246,7 @@ function writeCache2(state) {
15135
15246
  var SKILLS_HINT_MARKER = join17(homedir4(), ".launchpad", "skills-hint-shown");
15136
15247
  function absentHintShown() {
15137
15248
  try {
15138
- return existsSync16(SKILLS_HINT_MARKER);
15249
+ return existsSync17(SKILLS_HINT_MARKER);
15139
15250
  } catch {
15140
15251
  return false;
15141
15252
  }
@@ -15246,7 +15357,7 @@ var refreshUpdateCacheCommand = {
15246
15357
  import { spawn as spawn7 } from "node:child_process";
15247
15358
  import { homedir as homedir5 } from "node:os";
15248
15359
  import { join as join18 } from "node:path";
15249
- import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
15360
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
15250
15361
  import { randomUUID } from "node:crypto";
15251
15362
  var INTERNAL_EMIT_VERB = "__emit-telemetry";
15252
15363
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -15400,7 +15511,7 @@ function telemetryNoticeLine() {
15400
15511
  }
15401
15512
  function firstRunNoticeShown() {
15402
15513
  try {
15403
- return existsSync17(FIRST_RUN_MARKER);
15514
+ return existsSync18(FIRST_RUN_MARKER);
15404
15515
  } catch {
15405
15516
  return false;
15406
15517
  }
@@ -15642,6 +15753,12 @@ function makeReportCommand(kind, deps = realDeps5()) {
15642
15753
  };
15643
15754
  }
15644
15755
 
15756
+ // src/http/preflight.ts
15757
+ async function preflightMinimumCliVersion(config, fetcher = fetch) {
15758
+ const detachedFetcher = fetcher;
15759
+ await apiRaw(config, { method: "POST", path: "/cli/preflight" }, detachedFetcher);
15760
+ }
15761
+
15645
15762
  // src/dispatcher.ts
15646
15763
  var COMMANDS = [
15647
15764
  loginCommand,
@@ -15681,7 +15798,7 @@ var COMMANDS = [
15681
15798
  refreshUpdateCacheCommand,
15682
15799
  emitTelemetryCommand
15683
15800
  ];
15684
- async function dispatch(argv, io, commands = COMMANDS) {
15801
+ async function dispatch(argv, io, commands = COMMANDS, deps = {}) {
15685
15802
  if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
15686
15803
  printHelp6(io, commands);
15687
15804
  return 0;
@@ -15699,12 +15816,55 @@ async function dispatch(argv, io, commands = COMMANDS) {
15699
15816
  io.err("Run `launchpad --help` for the list of available commands.");
15700
15817
  return 64;
15701
15818
  }
15819
+ const preflightExit = await runCliFloorPreflight(verb, rest, io, deps);
15820
+ if (preflightExit !== null)
15821
+ return preflightExit;
15702
15822
  const code = await cmd.run(rest, io);
15703
15823
  if (!cmd.hidden && verb !== "bug" && verb !== "feature") {
15704
15824
  writeBreadcrumb(verb, code);
15705
15825
  }
15706
15826
  return code;
15707
15827
  }
15828
+ async function runCliFloorPreflight(verb, args, io, deps) {
15829
+ if (!requiresCliFloorPreflight(verb, args))
15830
+ return null;
15831
+ try {
15832
+ const preflight = deps.preflight ?? preflightMinimumCliVersion;
15833
+ await preflight(loadConfig());
15834
+ return null;
15835
+ } catch (error) {
15836
+ if (error instanceof UpgradeRequiredError) {
15837
+ io.err(error.message);
15838
+ return EXIT_CLI_UPGRADE_REQUIRED;
15839
+ }
15840
+ io.err(`launchpad ${verb}: version preflight failed: ${error instanceof Error ? error.message : String(error)}`);
15841
+ return 1;
15842
+ }
15843
+ }
15844
+ function requiresCliFloorPreflight(verb, args) {
15845
+ if (args.includes("--help") || args.includes("-h"))
15846
+ return false;
15847
+ if ([
15848
+ "create",
15849
+ "deploy",
15850
+ "redeploy",
15851
+ "merge",
15852
+ "destroy",
15853
+ "recover",
15854
+ "rollback",
15855
+ "grant-editor",
15856
+ "revoke-editor",
15857
+ "add-owner",
15858
+ "remove-owner"
15859
+ ].includes(verb)) {
15860
+ return true;
15861
+ }
15862
+ if (verb === "envvars")
15863
+ return args[0] === "set" || args[0] === "rm";
15864
+ if (verb === "secrets")
15865
+ return args[0] === "push";
15866
+ return false;
15867
+ }
15708
15868
  function printHelp6(io, commands) {
15709
15869
  io.out("launchpad — manage Launchpad-deployed apps from the command line.");
15710
15870
  io.out("");
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAsEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAc/B,OAAO,EAAa,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAKjE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AAEjD,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAgRD;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAoBpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAwBpE;AAiMD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAyEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAc/B,OAAO,EAAa,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAKjE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AAEjD,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAoRD;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAoBpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAwBpE;AAiMD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
@@ -3,23 +3,19 @@ export declare class GitFilesError extends Error {
3
3
  readonly code: "git_files_error";
4
4
  }
5
5
  /**
6
- * Paths the bot owns and rejects unconditionally on `deploy-pr`
7
- * (see `PROTECTED_PATHS` in `portal-bot/src/deploy-pr.ts`). Mirrored
8
- * here so the CLI excludes them up front rather than shipping a
9
- * tarball the bot will reject.
6
+ * Legacy convenience filter for known bot-owned files.
10
7
  *
11
- * The bot uses `base_tree` mode on the commit, so files we omit
12
- * from the tarball keep their base-branch contents — there's no
13
- * destructive consequence to filtering these out.
14
- *
15
- * Keep in lock-step with the bot's list. A future endpoint that
16
- * exposes this list would let the CLI fetch it dynamically; for
17
- * v1 the set is small and rarely changes.
8
+ * This list is not a security boundary and does not grant deletion
9
+ * authority. The server owns the canonical, family-based path policy
10
+ * and preserves platform files even when an older or newer client
11
+ * sends a different set.
18
12
  */
19
13
  export declare const PROTECTED_PATHS: ReadonlySet<string>;
20
14
  export interface ListFilesOptions {
21
15
  readonly cwd: string;
22
16
  readonly spawner?: typeof spawn;
17
+ /** Test seam for the worktree existence check. Receives an absolute path. */
18
+ readonly exists?: (path: string) => boolean;
23
19
  }
24
20
  /**
25
21
  * Return the deploy-eligible file paths under `cwd`, relative to
@@ -1 +1 @@
1
- {"version":3,"file":"git-files.d.ts","sourceRoot":"","sources":["../../src/deploy/git-files.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAE9D,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAU9C,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAqBxF"}
1
+ {"version":3,"file":"git-files.d.ts","sourceRoot":"","sources":["../../src/deploy/git-files.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAI9D,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAU9C,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC7C;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CA4BxF"}
@@ -1,3 +1,4 @@
1
+ import { type CliConfig } from "./config.js";
1
2
  export interface CliIo {
2
3
  /** Where regular CLI output goes (matches stdout). */
3
4
  readonly out: (line: string) => void;
@@ -15,6 +16,9 @@ export interface Command {
15
16
  */
16
17
  readonly hidden?: boolean;
17
18
  }
19
+ export interface DispatchDeps {
20
+ readonly preflight?: (config: CliConfig) => Promise<void>;
21
+ }
18
22
  /**
19
23
  * Verb table. Each slice imports + appends its `Command` here.
20
24
  * Keeping the registry as a const array (rather than a Map)
@@ -25,6 +29,6 @@ export declare const COMMANDS: readonly Command[];
25
29
  * Top-level dispatcher. Returns an exit code; never calls
26
30
  * `process.exit` itself (the bin wrapper does).
27
31
  */
28
- export declare function dispatch(argv: readonly string[], io: CliIo, commands?: readonly Command[]): Promise<ExitCode>;
32
+ export declare function dispatch(argv: readonly string[], io: CliIo, commands?: readonly Command[], deps?: DispatchDeps): Promise<ExitCode>;
29
33
  export declare function printHelp(io: CliIo, commands: readonly Command[]): void;
30
34
  //# sourceMappingURL=dispatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AA0DA,MAAM,WAAW,KAAK;IACpB,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,OAAO,EAyCtC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,KAAK,EACT,QAAQ,GAAE,SAAS,OAAO,EAAa,GACtC,OAAO,CAAC,QAAQ,CAAC,CA0BnB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAyBvE"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAyDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAOzD,MAAM,WAAW,KAAK;IACpB,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxE;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,SAAS,OAAO,EAyCtC,CAAC;AAEF;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,KAAK,EACT,QAAQ,GAAE,SAAS,OAAO,EAAa,EACvC,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,QAAQ,CAAC,CA4BnB;AAsDD,wBAAgB,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAyBvE"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/http/api-client.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAe9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;;;;OAOG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA0FnB"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/http/api-client.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAgB9C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD;;;;;;;OAOG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC1B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA6FnB"}
@@ -15,6 +15,17 @@ export declare class ForbiddenError extends Error {
15
15
  export declare class NotFoundError extends Error {
16
16
  readonly code: "not_found";
17
17
  }
18
+ /** Stable process exit used only for a server-enforced CLI upgrade. */
19
+ export declare const EXIT_CLI_UPGRADE_REQUIRED = 42;
20
+ /** HTTP 426 with the server's current minimum and the presented CLI version. */
21
+ export declare class UpgradeRequiredError extends Error {
22
+ readonly code: "cli_upgrade_required";
23
+ readonly minimum: string;
24
+ readonly yours: string;
25
+ constructor(minimum: string, yours: string);
26
+ }
27
+ /** Render the one-line recovery message shared by preflight and upload. */
28
+ export declare function upgradeRequiredMessage(minimum: string, yours: string): string;
18
29
  import { type StructuredErrorEnvelope } from "./error-envelope.js";
19
30
  /** Any other non-2xx. Carries the complete bounded structured envelope. */
20
31
  export declare class ApiError extends Error {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/http/errors.ts"],"names":[],"mappings":"AAIA;;2DAE2D;AAC3D,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;uCAEuC;AACvC,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;CACtC;AAED;yEACyE;AACzE,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;CACtC;AAED,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AAE7B,2EAA2E;AAC3E,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,SAAS,EAAG,WAAW,CAAU;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;gBAE/B,QAAQ,EAAE,uBAAuB;CAgB9C;AAED;2BAC2B;AAC3B,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/http/errors.ts"],"names":[],"mappings":"AAIA;;2DAE2D;AAC3D,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C;AAED;;uCAEuC;AACvC,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;CACtC;AAED;yEACyE;AACzE,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;CACtC;AAED,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,gFAAgF;AAChF,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAG,sBAAsB,CAAU;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAM3C;AAED,2EAA2E;AAC3E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AAE7B,2EAA2E;AAC3E,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,SAAS,EAAG,WAAW,CAAU;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;gBAE/B,QAAQ,EAAE,uBAAuB;CAgB9C;AAED;2BAC2B;AAC3B,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;CAC5C"}
@@ -0,0 +1,7 @@
1
+ import type { CliConfig } from "../config.js";
2
+ /**
3
+ * Run the no-side-effect bot endpoint governed by the same floor as mutations.
4
+ * The injected fetcher is passed as a bare callable through `apiRaw`.
5
+ */
6
+ export declare function preflightMinimumCliVersion(config: CliConfig, fetcher?: typeof fetch): Promise<void>;
7
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/http/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAG9C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,OAAO,KAAa,GAC5B,OAAO,CAAC,IAAI,CAAC,CAOf"}
@@ -0,0 +1,10 @@
1
+ /** Stable header used by Launchpad services to observe the real CLI version. */
2
+ export declare const CLI_VERSION_HEADER = "x-launchpad-cli-version";
3
+ /**
4
+ * Add the real CLI version to a Launchpad-controlled HTTP request.
5
+ *
6
+ * Caller-provided casing variants are removed so neither the version header
7
+ * nor the user-agent can be spoofed accidentally by a command-specific map.
8
+ */
9
+ export declare function cliVersionHeaders(headers?: Readonly<Record<string, string>>): Record<string, string>;
10
+ //# sourceMappingURL=version-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-headers.d.ts","sourceRoot":"","sources":["../../src/http/version-headers.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWxB"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.53.0";
1
+ export declare const CLI_VERSION = "0.54.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m-kopa/launchpad-cli",
3
- "version": "0.53.0",
3
+ "version": "0.54.1",
4
4
  "description": "Launchpad CLI — 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": {
@@ -26,6 +26,9 @@
26
26
  "directory": "packages/launchpad-cli"
27
27
  },
28
28
  "homepage": "https://github.com/M-KOPA/launchpad-platform/tree/main/packages/launchpad-cli#readme",
29
+ "bugs": {
30
+ "url": "https://github.com/M-KOPA/launchpad-platform/issues"
31
+ },
29
32
  "license": "UNLICENSED",
30
33
  "dependencies": {
31
34
  "esbuild": "0.28.1",
@@ -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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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.53.0
4
+ version: 0.54.1
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: Safely refresh a Launchpad clone, show manifest drift, or read the deployed manifest. Wraps bare `launchpad pull` for source sync, `launchpad pull --manifest` for deployed YAML, and `launchpad status` for drift. Use for "sync my clone", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or post-deploy verification.
4
- version: 0.53.0
4
+ version: 0.54.1
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -378,4 +378,4 @@ CI.
378
378
 
379
379
  ## Version
380
380
 
381
- This skill ships in launchpad-cli v0.53.0+.
381
+ This skill ships in launchpad-cli v0.54.0+.