@m-kopa/launchpad-cli 0.61.1 → 0.63.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/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.61.1";
22
+ var CLI_VERSION = "0.63.0";
23
23
 
24
24
  // src/config.ts
25
25
  import { readFileSync } from "node:fs";
@@ -226,7 +226,12 @@ class ForbiddenError extends Error {
226
226
  }
227
227
 
228
228
  class NotFoundError extends Error {
229
+ serverCode;
229
230
  code = "not_found";
231
+ constructor(message, serverCode) {
232
+ super(message);
233
+ this.serverCode = serverCode;
234
+ }
230
235
  }
231
236
  var EXIT_CLI_UPGRADE_REQUIRED = 42;
232
237
 
@@ -1148,8 +1153,9 @@ async function apiRaw(cfg, opts, fetcher = fetch) {
1148
1153
  throw recordedFault(new ForbiddenError(`bot returned 403 for ${opts.path}: ${detail}`));
1149
1154
  }
1150
1155
  if (res.status === 404) {
1151
- const detail = await errorDetail(res, opts.path);
1152
- throw recordedFault(new NotFoundError(`bot returned 404 for ${opts.path}: ${detail}`));
1156
+ const envelope = await readErrorEnvelope(res, opts.path);
1157
+ const detail = formatErrorEnvelope(envelope);
1158
+ throw recordedFault(new NotFoundError(`bot returned 404 for ${opts.path}: ${detail}`, envelope.code));
1153
1159
  }
1154
1160
  if (!res.ok) {
1155
1161
  if (opts.nonThrowingStatuses?.includes(res.status) === true) {
@@ -1882,8 +1888,8 @@ function describe10(e) {
1882
1888
  }
1883
1889
 
1884
1890
  // src/commands/deploy.ts
1885
- import { existsSync as existsSync7 } from "node:fs";
1886
- import * as path8 from "node:path";
1891
+ import { existsSync as existsSync8 } from "node:fs";
1892
+ import * as path9 from "node:path";
1887
1893
 
1888
1894
  // src/bundle/orchestrate.ts
1889
1895
  import { readFileSync as readFileSync5 } from "node:fs";
@@ -2200,8 +2206,14 @@ function deployBundleError(envelope, body) {
2200
2206
  ...typeof body.head_sha === "string" ? { head_sha: body.head_sha } : {},
2201
2207
  ...paths === undefined ? {} : { paths },
2202
2208
  ...typeof body.reason === "string" ? { reason: body.reason } : {},
2209
+ ...readDeployGuidance(body)
2210
+ };
2211
+ }
2212
+ function readDeployGuidance(body) {
2213
+ return {
2203
2214
  ...typeof body.minimum === "string" ? { minimum: body.minimum } : {},
2204
2215
  ...typeof body.yours === "string" ? { yours: body.yours } : {},
2216
+ ...typeof body.requires_admin_approval === "boolean" ? { requires_admin_approval: body.requires_admin_approval } : {},
2205
2217
  ...typeof body.statusPageUrl === "string" ? { statusPageUrl: body.statusPageUrl } : {}
2206
2218
  };
2207
2219
  }
@@ -6999,7 +7011,7 @@ async function verifyFirstDeployServed(args, liveUrl, io, deps) {
6999
7011
 
7000
7012
  // src/commands/deploy.ts
7001
7013
  import { parse as parseYaml6 } from "yaml";
7002
- import { readFileSync as readFileSync11 } from "node:fs";
7014
+ import { readFileSync as readFileSync12 } from "node:fs";
7003
7015
 
7004
7016
  // src/deploy/git-files.ts
7005
7017
  import { spawn as spawn3 } from "node:child_process";
@@ -8607,6 +8619,211 @@ function rejectUnsupportedCliRuntime(args) {
8607
8619
  return 1;
8608
8620
  }
8609
8621
 
8622
+ // src/commands/migrate.ts
8623
+ import { closeSync, existsSync as existsSync7, lstatSync as lstatSync4, mkdirSync as mkdirSync2, mkdtempSync, openSync, readFileSync as readFileSync11, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
8624
+ import path8 from "node:path";
8625
+ import { stringify } from "yaml";
8626
+
8627
+ // src/manifest/migrate-dispatch.ts
8628
+ function dispatchManifest(manifest) {
8629
+ const v2 = manifest.apiVersion === "launchpad.m-kopa.us/v2";
8630
+ const source = v2 ? manifest.spec : manifest;
8631
+ const appType = v2 ? manifest.spec.appType : manifest.deployment.type;
8632
+ if (appType !== "static" && appType !== "react") {
8633
+ throw new Error("Only static and React apps can use this converter. Pages Functions (react+api) need a compiled Worker build before switching to dispatch; containers use their own runtime.");
8634
+ }
8635
+ if (source.secrets !== undefined || source.containers !== undefined) {
8636
+ throw new Error("Secret and container bindings cannot be converted automatically.");
8637
+ }
8638
+ if (!v2)
8639
+ assertConvertibleV1(manifest);
8640
+ const spec = v2 ? { ...manifest.spec } : {
8641
+ appType,
8642
+ display: {
8643
+ name: manifest.metadata.name,
8644
+ team: manifest.metadata.team,
8645
+ ...manifest.metadata.description === undefined ? {} : { description: manifest.metadata.description }
8646
+ },
8647
+ owner: { email: manifest.metadata.owner },
8648
+ access: {
8649
+ audience: manifest.access.audience,
8650
+ private_owner: manifest.access.private_owner,
8651
+ allowed_entra_groups: [...allowedEntraGroups(manifest.access)],
8652
+ identity_types: manifest.access.identity_types
8653
+ },
8654
+ build: manifest.build,
8655
+ app: manifest.app,
8656
+ verify: manifest.verify,
8657
+ sensitive: manifest.sensitive,
8658
+ catalogue: manifest.catalogue,
8659
+ ...manifest.production_env === undefined ? {} : {
8660
+ env_vars: Object.fromEntries(Object.entries(manifest.production_env).map(([name, value]) => [name, { type: "plain_text", value }]))
8661
+ }
8662
+ };
8663
+ const candidate = {
8664
+ apiVersion: "launchpad.m-kopa.us/v2",
8665
+ kind: "App",
8666
+ metadata: v2 ? manifest.metadata : { slug: manifest.metadata.name, schemaVersion: 2 },
8667
+ spec: { ...spec, origin: "dispatch", auth: "gateway", confine_origin: undefined }
8668
+ };
8669
+ const parsed = parseManifestV2(candidate);
8670
+ if (parsed.kind !== "ok") {
8671
+ throw new Error(`Cannot convert manifest: ${parsed.issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`);
8672
+ }
8673
+ return parsed.manifest;
8674
+ }
8675
+ function assertConvertibleV1(manifest) {
8676
+ const unsupported2 = [
8677
+ manifest.hostnames !== undefined && "hostnames",
8678
+ manifest.targets !== undefined && "targets",
8679
+ manifest.access.session_duration !== undefined && "access.session_duration",
8680
+ manifest.deployment.runtime !== undefined && "deployment.runtime"
8681
+ ].filter(Boolean);
8682
+ if (unsupported2.length > 0) {
8683
+ throw new Error(`Cannot preserve ${unsupported2.join(", ")} on dispatch. Review these settings with the platform team before migrating.`);
8684
+ }
8685
+ }
8686
+
8687
+ // src/commands/migrate.ts
8688
+ var USAGE = "usage: launchpad migrate --origin dispatch [--dry-run]";
8689
+ var migrateCommand = {
8690
+ name: "migrate",
8691
+ summary: "convert an unprovisioned static or React app to Workers dispatch",
8692
+ run: runMigrate
8693
+ };
8694
+ async function runMigrate(args, io) {
8695
+ if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) {
8696
+ io.out(USAGE);
8697
+ return 0;
8698
+ }
8699
+ const flags = args.filter((arg) => arg !== "--dry-run");
8700
+ if (flags.length !== 2 || flags[0] !== "--origin" || flags[1] !== "dispatch" || args.length > 3) {
8701
+ io.err(USAGE);
8702
+ return 64;
8703
+ }
8704
+ try {
8705
+ const result = await convertToDispatch(process.cwd(), args.includes("--dry-run"));
8706
+ if (result.kind === "already") {
8707
+ io.out("launchpad.yaml already selects Workers dispatch. Run `launchpad deploy`.");
8708
+ } else if (result.kind === "eligible") {
8709
+ io.out(`${result.slug}: eligible for v2 Workers dispatch with gateway authentication. No files changed.`);
8710
+ } else {
8711
+ io.out(`${result.slug}: launchpad.yaml now uses v2 Workers dispatch with gateway authentication.`);
8712
+ io.out(`Audience, owner and application settings are preserved. Original: ${BACKUP_PATH}`);
8713
+ io.out("Run `launchpad deploy`. No Pages project is needed.");
8714
+ }
8715
+ return 0;
8716
+ } catch (error) {
8717
+ io.err(`launchpad migrate: ${error instanceof Error ? error.message : String(error)}`);
8718
+ return 1;
8719
+ }
8720
+ }
8721
+ var BACKUP_PATH = ".launchpad/manifest-before-dispatch.yaml";
8722
+ async function convertToDispatch(cwd, dryRun) {
8723
+ const file = path8.join(cwd, "launchpad.yaml");
8724
+ if (!lstatSync4(file).isFile())
8725
+ throw new Error("launchpad.yaml must be a regular file, not a symlink.");
8726
+ const original = readFileSync11(file, "utf8");
8727
+ const loaded = parseManifestAnyVersion(original, file);
8728
+ if (loaded.kind !== "ok")
8729
+ throw new Error("launchpad.yaml is invalid. Run `launchpad validate` and correct it first.");
8730
+ const manifest = loaded.manifest;
8731
+ if (manifest.apiVersion === "launchpad.m-kopa.us/v2" && manifest.spec.origin === "dispatch")
8732
+ return { kind: "already" };
8733
+ const converted = dispatchManifest(manifest);
8734
+ assertNoCustomRuntime(cwd, converted);
8735
+ const slug = converted.metadata.slug;
8736
+ await assertUnregistered(slug);
8737
+ if (dryRun)
8738
+ return { kind: "eligible", slug };
8739
+ try {
8740
+ saveMigration(file, original, converted);
8741
+ } catch (error) {
8742
+ throw new MigrationSaveError(error);
8743
+ }
8744
+ return { kind: "converted", slug };
8745
+ }
8746
+ async function assertUnregistered(slug) {
8747
+ try {
8748
+ await apiRaw(loadConfig(), { path: `/apps/${encodeURIComponent(slug)}/manifest/state` });
8749
+ } catch (error) {
8750
+ if (error instanceof NotFoundError && error.serverCode === "not_found")
8751
+ return;
8752
+ if (error instanceof UpgradeRequiredError || error instanceof UnauthenticatedError)
8753
+ throw error;
8754
+ throw new RegistryUnavailableError(error);
8755
+ }
8756
+ throw new RegisteredAppError;
8757
+ }
8758
+
8759
+ class MigrationSaveError extends Error {
8760
+ constructor(cause) {
8761
+ super(`Could not save the converted manifest: ${cause instanceof Error ? cause.message : String(cause)}`);
8762
+ }
8763
+ }
8764
+
8765
+ class RegistryUnavailableError extends Error {
8766
+ constructor(cause) {
8767
+ super(`Could not confirm whether this app is registered: ${cause instanceof Error ? cause.message : String(cause)}`);
8768
+ }
8769
+ }
8770
+
8771
+ class RegisteredAppError extends Error {
8772
+ constructor() {
8773
+ super("This app is already registered. This command only converts apps that have never provisioned; ask the platform team to migrate an existing app.");
8774
+ }
8775
+ }
8776
+ function assertNoCustomRuntime(cwd, manifest) {
8777
+ const appRoot = path8.resolve(cwd, manifest.spec.app?.root ?? ".");
8778
+ const buildRoot = path8.resolve(appRoot, manifest.spec.build?.root_dir ?? ".");
8779
+ const roots = new Set([
8780
+ cwd,
8781
+ appRoot,
8782
+ buildRoot,
8783
+ path8.resolve(cwd, manifest.spec.build?.root_dir ?? "."),
8784
+ path8.resolve(buildRoot, manifest.spec.build?.destination_dir ?? "dist")
8785
+ ]);
8786
+ for (const root of roots) {
8787
+ for (const name of ["functions", "_worker.js", "wrangler.toml", "wrangler.json", "wrangler.jsonc"]) {
8788
+ if (existsSync7(path8.join(root, name))) {
8789
+ throw new Error(`${name} needs a runtime review before conversion. Pages Functions and custom Worker bindings must not be dropped. Ask the platform team to review this app.`);
8790
+ }
8791
+ }
8792
+ }
8793
+ }
8794
+ function saveMigration(file, original, converted) {
8795
+ if (readFileSync11(file, "utf8") !== original)
8796
+ throw new Error("launchpad.yaml changed during the check. Retry migration.");
8797
+ const backupDir = path8.join(path8.dirname(file), ".launchpad");
8798
+ if (existsSync7(backupDir) && !lstatSync4(backupDir).isDirectory()) {
8799
+ throw new Error(".launchpad must be a regular directory, not a symlink or file.");
8800
+ }
8801
+ mkdirSync2(backupDir, { recursive: true });
8802
+ const backup = path8.join(backupDir, "manifest-before-dispatch.yaml");
8803
+ const attemptDir = mkdtempSync(path8.join(backupDir, "migration-"));
8804
+ let backupCreated = false;
8805
+ try {
8806
+ const fd = openSync(backup, "wx", 384);
8807
+ backupCreated = true;
8808
+ try {
8809
+ writeFileSync2(fd, original);
8810
+ } finally {
8811
+ closeSync(fd);
8812
+ }
8813
+ const temporary = path8.join(attemptDir, "launchpad.yaml");
8814
+ writeFileSync2(temporary, stringify(converted), { flag: "wx", mode: 384 });
8815
+ if (readFileSync11(file, "utf8") !== original)
8816
+ throw new Error("launchpad.yaml changed during migration. Retry migration.");
8817
+ renameSync(temporary, file);
8818
+ } catch (error) {
8819
+ if (backupCreated)
8820
+ rmSync2(backup);
8821
+ throw error;
8822
+ } finally {
8823
+ rmSync2(attemptDir, { recursive: true, force: true });
8824
+ }
8825
+ }
8826
+
8610
8827
  // src/commands/deploy.ts
8611
8828
  var deployCommand = {
8612
8829
  name: "deploy",
@@ -8659,8 +8876,8 @@ async function runDeploy(args, io) {
8659
8876
  }, io);
8660
8877
  }
8661
8878
  const cwd = process.cwd();
8662
- const manifestPath = path8.join(cwd, "launchpad.yaml");
8663
- if (existsSync7(manifestPath)) {
8879
+ const manifestPath = path9.join(cwd, "launchpad.yaml");
8880
+ if (existsSync8(manifestPath)) {
8664
8881
  const contentMode = flags.mode.kind === "content" ? flags.mode : {
8665
8882
  allowStale: false,
8666
8883
  rebaseOntoHead: false,
@@ -8694,7 +8911,7 @@ async function runDeploy(args, io) {
8694
8911
  } else {
8695
8912
  const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
8696
8913
  if (inferred === null) {
8697
- io.err(`launchpad deploy: could not infer slug from cwd (${path8.basename(process.cwd())});
8914
+ io.err(`launchpad deploy: could not infer slug from cwd (${path9.basename(process.cwd())});
8698
8915
  ` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
8699
8916
  return 64;
8700
8917
  }
@@ -8714,10 +8931,10 @@ async function runDeploy(args, io) {
8714
8931
  return 1;
8715
8932
  }
8716
8933
  let contentManifestYaml = null;
8717
- const contentManifestPath = path8.join(process.cwd(), "launchpad.yaml");
8718
- if (existsSync7(contentManifestPath)) {
8934
+ const contentManifestPath = path9.join(process.cwd(), "launchpad.yaml");
8935
+ if (existsSync8(contentManifestPath)) {
8719
8936
  try {
8720
- contentManifestYaml = readFileSync11(contentManifestPath, "utf8");
8937
+ contentManifestYaml = readFileSync12(contentManifestPath, "utf8");
8721
8938
  } catch {
8722
8939
  contentManifestYaml = null;
8723
8940
  }
@@ -8927,8 +9144,8 @@ function surfaceStaleBlock(body, io) {
8927
9144
  const head = body.head_sha ?? null;
8928
9145
  if (body.error === "stale_overlap" && body.paths !== undefined) {
8929
9146
  io.err(`launchpad deploy: BLOCKED — this deploy would roll back ${body.paths.length} file(s) a teammate changed on main since you cloned:`);
8930
- for (const path9 of body.paths.slice(0, 20)) {
8931
- io.err(` - ${path9}`);
9147
+ for (const path10 of body.paths.slice(0, 20)) {
9148
+ io.err(` - ${path10}`);
8932
9149
  }
8933
9150
  if (body.paths.length > 20) {
8934
9151
  io.err(` … and ${body.paths.length - 20} more`);
@@ -8961,7 +9178,7 @@ function withTimeout(p, ms) {
8961
9178
  async function surfaceManifestDiff(cfg, slug, manifestPath, io) {
8962
9179
  let localYaml;
8963
9180
  try {
8964
- localYaml = readFileSync11(manifestPath, "utf8");
9181
+ localYaml = readFileSync12(manifestPath, "utf8");
8965
9182
  } catch {
8966
9183
  return;
8967
9184
  }
@@ -9010,8 +9227,21 @@ async function runModelADeploy(args) {
9010
9227
  let slug;
9011
9228
  let appType = "static";
9012
9229
  let declaredOrigin;
9230
+ let pagesNote;
9231
+ try {
9232
+ pagesNote = await defaultToDispatch(cwd, manifestPath, io);
9233
+ } catch (e) {
9234
+ if (e instanceof UpgradeRequiredError) {
9235
+ io.err(e.message);
9236
+ return EXIT_CLI_UPGRADE_REQUIRED;
9237
+ }
9238
+ io.err(`launchpad deploy: ${describe17(e)}. Nothing was deployed.`);
9239
+ if (e instanceof UnauthenticatedError)
9240
+ io.err(" run `launchpad login` to refresh your session.");
9241
+ return 1;
9242
+ }
9013
9243
  try {
9014
- const manifestObj = parseYaml6(readFileSync11(manifestPath, "utf8"));
9244
+ const manifestObj = parseYaml6(readFileSync12(manifestPath, "utf8"));
9015
9245
  const metaSlug = resolveManifestSlug(manifestObj);
9016
9246
  if (metaSlug === null) {
9017
9247
  io.err(`launchpad deploy: launchpad.yaml is missing metadata.slug (v2) / metadata.name (v1). ` + `Run \`launchpad init\` again to regenerate the manifest.`);
@@ -9076,6 +9306,11 @@ async function runModelADeploy(args) {
9076
9306
  return 64;
9077
9307
  }
9078
9308
  io.out(`Bundling ${cwd} for ${slug} (Model A) …`);
9309
+ if (declaredOrigin !== "dispatch" && appType !== "container") {
9310
+ io.out("Deployment target: Cloudflare Pages, selected by launchpad.yaml.");
9311
+ if (pagesNote !== null)
9312
+ io.out(pagesNote);
9313
+ }
9079
9314
  let cfg;
9080
9315
  try {
9081
9316
  cfg = loadConfig();
@@ -9160,7 +9395,7 @@ async function runModelADeploy(args) {
9160
9395
  const errorCode = body.error;
9161
9396
  if (errorCode === "audience_confirmation_required") {
9162
9397
  io.err(String(body.message));
9163
- io.err("Only the human builder may confirm. Retry this deployment after confirmation.");
9398
+ io.err(body.requires_admin_approval === true ? "This sensitive app needs builder confirmation and platform-admin approval. Retry after both are complete." : "The server still requires browser confirmation. Ask the platform team to update it, then retry.");
9164
9399
  return 1;
9165
9400
  }
9166
9401
  if (result.status === 426 && typeof body.minimum === "string" && typeof body.yours === "string") {
@@ -9363,6 +9598,37 @@ async function runModelADeploy(args) {
9363
9598
  function surfaceCommittedHandoff(dispatchOrigin, io, pagesStyle = "unicode") {
9364
9599
  io.out(committedHandoffMessage(dispatchOrigin, pagesStyle));
9365
9600
  }
9601
+ function isIneligible(e) {
9602
+ return !(e instanceof RegistryUnavailableError || e instanceof MigrationSaveError || e instanceof UpgradeRequiredError || e instanceof UnauthenticatedError);
9603
+ }
9604
+ async function defaultToDispatch(cwd, manifestPath, io) {
9605
+ let raw;
9606
+ try {
9607
+ raw = parseYaml6(readFileSync12(manifestPath, "utf8"));
9608
+ } catch {
9609
+ return null;
9610
+ }
9611
+ const isV22 = raw?.apiVersion === "launchpad.m-kopa.us/v2";
9612
+ const appType = isV22 ? raw?.spec?.appType : raw?.deployment?.type;
9613
+ const origin = isV22 ? raw?.spec?.origin : raw?.origin;
9614
+ const auth = isV22 ? raw?.spec?.auth : raw?.auth;
9615
+ if (appType !== "static" && appType !== "react" || origin !== undefined || auth !== "gateway")
9616
+ return null;
9617
+ try {
9618
+ if ((await convertToDispatch(cwd, false)).kind === "converted") {
9619
+ io.out("New apps deploy on Workers dispatch by default, so launchpad.yaml was converted (no Pages project needed).");
9620
+ io.out(` Audience, owner and build settings are preserved. Original: ${BACKUP_PATH}`);
9621
+ io.out(" To stay on Pages instead, set `spec.origin: pages` and deploy again.");
9622
+ }
9623
+ return null;
9624
+ } catch (e) {
9625
+ if (e instanceof RegisteredAppError)
9626
+ return null;
9627
+ if (!(e instanceof Error) || !isIneligible(e))
9628
+ throw e;
9629
+ return `Not converted to Workers dispatch: ${describe17(e)}`;
9630
+ }
9631
+ }
9366
9632
  function declaredOriginOf(isV22, origin) {
9367
9633
  if (!isV22)
9368
9634
  return "pages";
@@ -9397,7 +9663,7 @@ function pendingReconcileOutcome(body) {
9397
9663
  }
9398
9664
 
9399
9665
  // src/commands/redeploy.ts
9400
- import { existsSync as existsSync8, readFileSync as readFileSync12 } from "node:fs";
9666
+ import { existsSync as existsSync9, readFileSync as readFileSync13 } from "node:fs";
9401
9667
  import { resolve as resolvePath2 } from "node:path";
9402
9668
  import { parse as parseYaml7 } from "yaml";
9403
9669
  var SLUG_RE6 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
@@ -9475,10 +9741,10 @@ async function runRedeploy(opts, io, deps = {}) {
9475
9741
  }
9476
9742
  function readLocalAppType(cwd, file) {
9477
9743
  const manifestPath = resolvePath2(cwd, file ?? "launchpad.yaml");
9478
- if (!existsSync8(manifestPath))
9744
+ if (!existsSync9(manifestPath))
9479
9745
  return null;
9480
9746
  try {
9481
- const obj = parseYaml7(readFileSync12(manifestPath, "utf8"));
9747
+ const obj = parseYaml7(readFileSync13(manifestPath, "utf8"));
9482
9748
  return coerceAppType(obj?.deployment?.type);
9483
9749
  } catch {
9484
9750
  return null;
@@ -9572,7 +9838,7 @@ function printHelp(io) {
9572
9838
  }
9573
9839
 
9574
9840
  // src/commands/envvars.ts
9575
- import * as path9 from "node:path";
9841
+ import * as path10 from "node:path";
9576
9842
  var envvarsCommand = {
9577
9843
  name: "envvars",
9578
9844
  summary: "list / set / remove production env vars (slug-scoped)",
@@ -9592,7 +9858,7 @@ async function runEnvvars(args, io) {
9592
9858
  } else {
9593
9859
  const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
9594
9860
  if (inferred === null) {
9595
- io.err(`launchpad envvars: could not infer slug from cwd (${path9.basename(process.cwd())});
9861
+ io.err(`launchpad envvars: could not infer slug from cwd (${path10.basename(process.cwd())});
9596
9862
  ` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
9597
9863
  return 64;
9598
9864
  }
@@ -9762,7 +10028,7 @@ function describe18(e) {
9762
10028
  }
9763
10029
 
9764
10030
  // src/commands/generate.ts
9765
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "node:fs";
10031
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
9766
10032
  import { dirname as dirname7, resolve as resolve10, relative as relative4 } from "node:path";
9767
10033
  var generateCommand = {
9768
10034
  name: "generate",
@@ -9812,7 +10078,7 @@ async function runGenerate(args, io) {
9812
10078
  ];
9813
10079
  return flags.json ? renderApplyJson(reports, appRoot, io) : renderApplyHuman(reports, appRoot, io);
9814
10080
  }
9815
- function applyOne(artefact, path10, out, force) {
10081
+ function applyOne(artefact, path11, out, force) {
9816
10082
  if (out.kind === "not-applicable" || out.kind === "unsupported") {
9817
10083
  return {
9818
10084
  artefact,
@@ -9823,32 +10089,32 @@ function applyOne(artefact, path10, out, force) {
9823
10089
  warnings: []
9824
10090
  };
9825
10091
  }
9826
- const existing = readIfExists(path10);
10092
+ const existing = readIfExists(path11);
9827
10093
  if (existing.kind === "read-error") {
9828
10094
  return {
9829
10095
  artefact,
9830
- action: { kind: "write-error", path: path10, message: existing.message },
10096
+ action: { kind: "write-error", path: path11, message: existing.message },
9831
10097
  warnings: out.warnings
9832
10098
  };
9833
10099
  }
9834
10100
  if (existing.kind === "ok" && existing.content === out.content) {
9835
- return { artefact, action: { kind: "unchanged", path: path10 }, warnings: out.warnings };
10101
+ return { artefact, action: { kind: "unchanged", path: path11 }, warnings: out.warnings };
9836
10102
  }
9837
10103
  if (existing.kind === "ok" && existing.content !== out.content && !force) {
9838
10104
  return {
9839
10105
  artefact,
9840
- action: { kind: "would-overwrite", path: path10 },
10106
+ action: { kind: "would-overwrite", path: path11 },
9841
10107
  warnings: out.warnings
9842
10108
  };
9843
10109
  }
9844
10110
  try {
9845
- mkdirSync2(dirname7(path10), { recursive: true });
9846
- writeFileSync2(path10, out.content, "utf8");
10111
+ mkdirSync3(dirname7(path11), { recursive: true });
10112
+ writeFileSync3(path11, out.content, "utf8");
9847
10113
  return {
9848
10114
  artefact,
9849
10115
  action: {
9850
10116
  kind: "written",
9851
- path: path10,
10117
+ path: path11,
9852
10118
  bytes: Buffer.byteLength(out.content, "utf8")
9853
10119
  },
9854
10120
  warnings: out.warnings
@@ -9858,16 +10124,16 @@ function applyOne(artefact, path10, out, force) {
9858
10124
  artefact,
9859
10125
  action: {
9860
10126
  kind: "write-error",
9861
- path: path10,
10127
+ path: path11,
9862
10128
  message: err.message ?? String(err)
9863
10129
  },
9864
10130
  warnings: out.warnings
9865
10131
  };
9866
10132
  }
9867
10133
  }
9868
- function readIfExists(path10) {
10134
+ function readIfExists(path11) {
9869
10135
  try {
9870
- return { kind: "ok", content: readFileSync13(path10, "utf8") };
10136
+ return { kind: "ok", content: readFileSync14(path11, "utf8") };
9871
10137
  } catch (err) {
9872
10138
  const e = err;
9873
10139
  if (e.code === "ENOENT")
@@ -10606,17 +10872,17 @@ function describe21(e) {
10606
10872
 
10607
10873
  // src/commands/init.ts
10608
10874
  import { createInterface } from "node:readline/promises";
10609
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync15, writeFileSync as writeFileSync3 } from "node:fs";
10875
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync4 } from "node:fs";
10610
10876
  import { dirname as dirname8, resolve as resolve11 } from "node:path";
10611
10877
  import { stringify as yamlStringify } from "yaml";
10612
10878
 
10613
10879
  // src/detect/index.ts
10614
- import { existsSync as existsSync9, readFileSync as readFileSync14, statSync } from "node:fs";
10880
+ import { existsSync as existsSync10, readFileSync as readFileSync15, statSync } from "node:fs";
10615
10881
  import { join as join13 } from "node:path";
10616
10882
  function detectAppShape(cwd) {
10617
- const hasPackageJson = existsSync9(join13(cwd, "package.json"));
10618
- const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync9(join13(cwd, file)));
10619
- const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync9(join13(cwd, n)));
10883
+ const hasPackageJson = existsSync10(join13(cwd, "package.json"));
10884
+ const hasAnyLockfile = LOCKFILES.some(({ file }) => existsSync10(join13(cwd, file)));
10885
+ const hasViteConfig = VITE_CONFIG_NAMES.some((n) => existsSync10(join13(cwd, n)));
10620
10886
  if (!hasPackageJson && !hasAnyLockfile && !hasViteConfig) {
10621
10887
  return {
10622
10888
  kind: "not-applicable",
@@ -10662,7 +10928,7 @@ var LOCKFILES = [
10662
10928
  { file: "yarn.lock", pm: "yarn" }
10663
10929
  ];
10664
10930
  function detectPackageManager(cwd) {
10665
- const present = LOCKFILES.filter(({ file }) => existsSync9(join13(cwd, file)));
10931
+ const present = LOCKFILES.filter(({ file }) => existsSync10(join13(cwd, file)));
10666
10932
  if (present.length === 0) {
10667
10933
  return {
10668
10934
  kind: "ambiguous",
@@ -10685,7 +10951,7 @@ function detectPackageManager(cwd) {
10685
10951
  function detectVitePresence(cwd) {
10686
10952
  for (const name of VITE_CONFIG_NAMES) {
10687
10953
  const p = join13(cwd, name);
10688
- if (existsSync9(p)) {
10954
+ if (existsSync10(p)) {
10689
10955
  return { kind: "ok", value: { path: p } };
10690
10956
  }
10691
10957
  }
@@ -10697,7 +10963,7 @@ function detectVitePresence(cwd) {
10697
10963
  function detectAppType(cwd) {
10698
10964
  const fnDir = join13(cwd, "functions");
10699
10965
  let hasFunctionsDir = false;
10700
- if (existsSync9(fnDir)) {
10966
+ if (existsSync10(fnDir)) {
10701
10967
  try {
10702
10968
  hasFunctionsDir = statSync(fnDir).isDirectory();
10703
10969
  } catch {
@@ -10708,7 +10974,7 @@ function detectAppType(cwd) {
10708
10974
  }
10709
10975
  function detectBuildCommand(cwd, pm) {
10710
10976
  const pkgJsonPath = join13(cwd, "package.json");
10711
- if (!existsSync9(pkgJsonPath)) {
10977
+ if (!existsSync10(pkgJsonPath)) {
10712
10978
  return {
10713
10979
  kind: "ambiguous",
10714
10980
  reason: "no package.json at repo root. Run your package manager's `init` first."
@@ -10716,7 +10982,7 @@ function detectBuildCommand(cwd, pm) {
10716
10982
  }
10717
10983
  let pkgJson;
10718
10984
  try {
10719
- pkgJson = JSON.parse(readFileSync14(pkgJsonPath, "utf8"));
10985
+ pkgJson = JSON.parse(readFileSync15(pkgJsonPath, "utf8"));
10720
10986
  } catch (e) {
10721
10987
  return {
10722
10988
  kind: "ambiguous",
@@ -10751,7 +11017,7 @@ var OUT_DIR_REGEX = /\bbuild\s*:\s*\{[^{}]*?\boutDir\s*:\s*['"]([^'"]+)['"]/s;
10751
11017
  function detectDestinationDir(cwd, vite) {
10752
11018
  let text;
10753
11019
  try {
10754
- text = readFileSync14(vite.path, "utf8");
11020
+ text = readFileSync15(vite.path, "utf8");
10755
11021
  } catch (e) {
10756
11022
  return {
10757
11023
  kind: "ambiguous",
@@ -10781,7 +11047,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
10781
11047
  }
10782
11048
  const { inputs, options } = parsed;
10783
11049
  const outPath = resolve11(process.cwd(), options.out);
10784
- if (existsSync10(outPath) && !options.force) {
11050
+ if (existsSync11(outPath) && !options.force) {
10785
11051
  io.err(`launchpad init: ${outPath} already exists`);
10786
11052
  io.err("Pass --force to overwrite.");
10787
11053
  return 64;
@@ -10837,7 +11103,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
10837
11103
  io.out(`Audience: private. Only you (${privateOwner.email}) and platform administrators can open this app.`);
10838
11104
  } else {
10839
11105
  io.out(`Audience: selected Entra groups (${fillResult.inputs.groups.join(", ")}), plus platform administrators.`);
10840
- io.out("Company-wide grants require your explicit browser confirmation before publishing.");
11106
+ io.out("The deploy skill confirms company-wide grants with you. Sensitive apps also require browser confirmation and platform-admin approval.");
10841
11107
  }
10842
11108
  const manifest = buildManifest({ ...fillResult.inputs, ...privateOwner === undefined ? {} : { privateOwner } }, detected);
10843
11109
  const validation = manifest.apiVersion === "launchpad.m-kopa.us/v2" ? ManifestV2Schema.safeParse(manifest) : ManifestSchema.safeParse(manifest);
@@ -10850,7 +11116,7 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
10850
11116
  }
10851
11117
  const yamlText = renderYaml(manifest);
10852
11118
  try {
10853
- writeFileSync3(outPath, yamlText, { encoding: "utf8" });
11119
+ writeFileSync4(outPath, yamlText, { encoding: "utf8" });
10854
11120
  } catch (err) {
10855
11121
  const e = err;
10856
11122
  io.err(`launchpad init: failed to write ${outPath}`);
@@ -10876,10 +11142,10 @@ async function runInit(args, io, prompt, identity = () => apiJson(loadConfig(),
10876
11142
  if (fillResult.inputs.type === "static") {
10877
11143
  const serveDir = resolve11(dirname8(outPath), STATIC_SERVE_DIR);
10878
11144
  const indexPath = resolve11(serveDir, "index.html");
10879
- if (!existsSync10(indexPath)) {
11145
+ if (!existsSync11(indexPath)) {
10880
11146
  try {
10881
- mkdirSync3(serveDir, { recursive: true });
10882
- writeFileSync3(indexPath, starterIndexHtml(fillResult.inputs.name), {
11147
+ mkdirSync4(serveDir, { recursive: true });
11148
+ writeFileSync4(indexPath, starterIndexHtml(fillResult.inputs.name), {
10883
11149
  encoding: "utf8"
10884
11150
  });
10885
11151
  io.out(`✓ scaffolded ${indexPath}`);
@@ -11298,10 +11564,10 @@ function starterIndexHtml(slug) {
11298
11564
  function renderYaml(manifest) {
11299
11565
  return yamlStringify(manifest, { lineWidth: 0 });
11300
11566
  }
11301
- function ensureGitignoreEntries(path10, entries) {
11567
+ function ensureGitignoreEntries(path11, entries) {
11302
11568
  let current = "";
11303
- if (existsSync10(path10)) {
11304
- current = readFileSync15(path10, "utf8");
11569
+ if (existsSync11(path11)) {
11570
+ current = readFileSync16(path11, "utf8");
11305
11571
  }
11306
11572
  const lines = current.split(/\r?\n/);
11307
11573
  const present = new Set(lines.map((l) => l.trim()));
@@ -11319,7 +11585,7 @@ function ensureGitignoreEntries(path10, entries) {
11319
11585
  }
11320
11586
  }
11321
11587
  if (added.length > 0) {
11322
- writeFileSync3(path10, out, { encoding: "utf8" });
11588
+ writeFileSync4(path11, out, { encoding: "utf8" });
11323
11589
  }
11324
11590
  return added;
11325
11591
  }
@@ -11491,7 +11757,7 @@ function describe23(e) {
11491
11757
  }
11492
11758
 
11493
11759
  // src/commands/logs.ts
11494
- import * as path10 from "node:path";
11760
+ import * as path11 from "node:path";
11495
11761
  var logsCommand = {
11496
11762
  name: "logs",
11497
11763
  summary: "show recent Pages deployment history (slug-scoped)",
@@ -11513,7 +11779,7 @@ async function runLogs(args, io) {
11513
11779
  } else {
11514
11780
  const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
11515
11781
  if (inferred === null) {
11516
- io.err(`launchpad logs: could not infer slug from cwd (${path10.basename(process.cwd())});
11782
+ io.err(`launchpad logs: could not infer slug from cwd (${path11.basename(process.cwd())});
11517
11783
  ` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
11518
11784
  return 64;
11519
11785
  }
@@ -11641,7 +11907,7 @@ function describe24(e) {
11641
11907
  }
11642
11908
 
11643
11909
  // src/commands/merge.ts
11644
- import * as path11 from "node:path";
11910
+ import * as path12 from "node:path";
11645
11911
  var mergeCommand = {
11646
11912
  name: "merge",
11647
11913
  summary: "squash-merge a review-passed PR (slug-scoped)",
@@ -11662,7 +11928,7 @@ async function runMerge(args, io) {
11662
11928
  } else {
11663
11929
  const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
11664
11930
  if (inferred === null) {
11665
- io.err(`launchpad merge: could not infer slug from cwd (${path11.basename(process.cwd())});
11931
+ io.err(`launchpad merge: could not infer slug from cwd (${path12.basename(process.cwd())});
11666
11932
  ` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
11667
11933
  return 64;
11668
11934
  }
@@ -12292,18 +12558,18 @@ function describe26(e) {
12292
12558
  }
12293
12559
 
12294
12560
  // src/commands/pull.ts
12295
- import { writeFileSync as writeFileSync4 } from "node:fs";
12561
+ import { writeFileSync as writeFileSync5 } from "node:fs";
12296
12562
  import { stringify as stringifyYaml } from "yaml";
12297
12563
 
12298
12564
  // src/deploy/manifest-status.ts
12299
12565
  async function fetchManifestStatus(cfg, slug, fetcher = fetch) {
12300
- const path12 = `/apps/${encodeURIComponent(slug)}/manifest/status`;
12301
- return apiJson(cfg, { path: path12 }, fetcher);
12566
+ const path13 = `/apps/${encodeURIComponent(slug)}/manifest/status`;
12567
+ return apiJson(cfg, { path: path13 }, fetcher);
12302
12568
  }
12303
12569
 
12304
12570
  // src/pull/source-sync.ts
12305
12571
  import * as fs6 from "node:fs/promises";
12306
- import * as path12 from "node:path";
12572
+ import * as path13 from "node:path";
12307
12573
  import { constants as fsConstants } from "node:fs";
12308
12574
  import { createHash as createHash3 } from "node:crypto";
12309
12575
  import { execFile } from "node:child_process";
@@ -12342,9 +12608,9 @@ async function runSourceSync(args) {
12342
12608
  args.io.out(`Already current at ${compare.headSha.slice(0, 12)}.`);
12343
12609
  return 0;
12344
12610
  }
12345
- const stageRoot = path12.resolve(args.cwd, MARKER_DIR, `pull-stage-${process.pid}-${crypto.randomUUID()}`);
12346
- const baseDir = path12.join(stageRoot, "base");
12347
- const headDir = path12.join(stageRoot, "head");
12611
+ const stageRoot = path13.resolve(args.cwd, MARKER_DIR, `pull-stage-${process.pid}-${crypto.randomUUID()}`);
12612
+ const baseDir = path13.join(stageRoot, "base");
12613
+ const headDir = path13.join(stageRoot, "head");
12348
12614
  await fs6.mkdir(baseDir, { recursive: true });
12349
12615
  await fs6.mkdir(headDir, { recursive: true });
12350
12616
  try {
@@ -12396,7 +12662,7 @@ async function bootstrapProvenance(args) {
12396
12662
  args.io.err(" Preserve this directory and obtain a separate current clone for comparison.");
12397
12663
  return 1;
12398
12664
  }
12399
- const stageRoot = path12.resolve(args.cwd, MARKER_DIR, `pull-bootstrap-${process.pid}-${crypto.randomUUID()}`);
12665
+ const stageRoot = path13.resolve(args.cwd, MARKER_DIR, `pull-bootstrap-${process.pid}-${crypto.randomUUID()}`);
12400
12666
  await fs6.mkdir(stageRoot, { recursive: true });
12401
12667
  try {
12402
12668
  const headSha = await fetchSnapshotRef(args.cfg, args.slug, "main", stageRoot);
@@ -12422,7 +12688,7 @@ async function treesEqual(cwd, tracked, headDir, headPaths) {
12422
12688
  for (const relative6 of tracked) {
12423
12689
  if (!expected.has(relative6))
12424
12690
  return false;
12425
- if (!equal(await readEntry(path12.resolve(cwd, relative6)), await readEntry(path12.resolve(headDir, relative6)))) {
12691
+ if (!equal(await readEntry(path13.resolve(cwd, relative6)), await readEntry(path13.resolve(headDir, relative6)))) {
12426
12692
  return false;
12427
12693
  }
12428
12694
  }
@@ -12430,7 +12696,7 @@ async function treesEqual(cwd, tracked, headDir, headPaths) {
12430
12696
  }
12431
12697
  async function listFiles(root, relative6 = "") {
12432
12698
  const files = [];
12433
- const entries = await fs6.readdir(path12.resolve(root, relative6), {
12699
+ const entries = await fs6.readdir(path13.resolve(root, relative6), {
12434
12700
  withFileTypes: true
12435
12701
  });
12436
12702
  for (const entry of entries) {
@@ -12506,7 +12772,7 @@ async function cleanAncestorRemovals(args) {
12506
12772
  const checked = await checkParents(args.cwd, relative6, removals);
12507
12773
  if (checked.kind === "blocked")
12508
12774
  continue;
12509
- const base = await readEntry(path12.resolve(args.baseDir, relative6));
12775
+ const base = await readEntry(path13.resolve(args.baseDir, relative6));
12510
12776
  const local = checked.projectedMissing ? { kind: "missing" } : await readEntry(checked.target);
12511
12777
  if (local.kind !== "missing" && cleanLocal(base, local))
12512
12778
  removals.add(relative6);
@@ -12516,9 +12782,9 @@ async function cleanAncestorRemovals(args) {
12516
12782
  async function planPath(args, relative6, removals) {
12517
12783
  assertSafePath(relative6);
12518
12784
  const checked = await checkParents(args.cwd, relative6, removals);
12519
- const base = await readEntry(path12.resolve(args.baseDir, relative6));
12785
+ const base = await readEntry(path13.resolve(args.baseDir, relative6));
12520
12786
  const upstreamDeleted = args.compare.deleted.includes(relative6);
12521
- const head = upstreamDeleted ? { kind: "missing" } : await readEntry(path12.resolve(args.headDir, relative6));
12787
+ const head = upstreamDeleted ? { kind: "missing" } : await readEntry(path13.resolve(args.headDir, relative6));
12522
12788
  const unsupported2 = (args.compare.unsupported ?? []).includes(relative6);
12523
12789
  if (checked.kind === "blocked") {
12524
12790
  return conflictPlan(relative6, checked.target, { kind: "missing" }, head, upstreamDeleted, checked.reason);
@@ -12655,7 +12921,7 @@ async function writeAtomic(cwd, relative6, bytes, mode, allowMetadata = false) {
12655
12921
  if (checked.kind === "blocked")
12656
12922
  throw new Error(checked.reason);
12657
12923
  const file = checked.target;
12658
- await fs6.mkdir(path12.dirname(file), { recursive: true });
12924
+ await fs6.mkdir(path13.dirname(file), { recursive: true });
12659
12925
  const rechecked = await checkParents(cwd, relative6, new Set, allowMetadata);
12660
12926
  if (rechecked.kind === "blocked")
12661
12927
  throw new Error(rechecked.reason);
@@ -12697,14 +12963,14 @@ function isSafeSourcePath(relative6) {
12697
12963
  return isSafeRelativePath(relative6) && !relative6.startsWith(`${MARKER_DIR}/`);
12698
12964
  }
12699
12965
  function isSafeRelativePath(relative6) {
12700
- const normalized = path12.posix.normalize(relative6);
12701
- return relative6.length > 0 && !relative6.includes("\\") && !path12.isAbsolute(relative6) && normalized === relative6 && normalized !== ".." && !normalized.startsWith("../");
12966
+ const normalized = path13.posix.normalize(relative6);
12967
+ return relative6.length > 0 && !relative6.includes("\\") && !path13.isAbsolute(relative6) && normalized === relative6 && normalized !== ".." && !normalized.startsWith("../");
12702
12968
  }
12703
12969
  function resolveInsideClone(cwd, relative6) {
12704
- const root = path12.resolve(cwd);
12705
- const target = path12.resolve(root, relative6);
12706
- const fromRoot = path12.relative(root, target);
12707
- if (fromRoot.length === 0 || path12.isAbsolute(fromRoot) || fromRoot === ".." || fromRoot.startsWith(`..${path12.sep}`)) {
12970
+ const root = path13.resolve(cwd);
12971
+ const target = path13.resolve(root, relative6);
12972
+ const fromRoot = path13.relative(root, target);
12973
+ if (fromRoot.length === 0 || path13.isAbsolute(fromRoot) || fromRoot === ".." || fromRoot.startsWith(`..${path13.sep}`)) {
12708
12974
  throw new Error(`unsafe source path returned by bot: ${relative6}`);
12709
12975
  }
12710
12976
  return target;
@@ -12719,9 +12985,9 @@ async function checkParents(cwd, relative6, projectedRemovals, allowMetadata = f
12719
12985
  }
12720
12986
  const target = resolveInsideClone(cwd, relative6);
12721
12987
  const components = relative6.split("/");
12722
- let current = path12.resolve(cwd);
12988
+ let current = path13.resolve(cwd);
12723
12989
  for (let index = 0;index < components.length - 1; index += 1) {
12724
- current = path12.join(current, components[index]);
12990
+ current = path13.join(current, components[index]);
12725
12991
  const ancestor = components.slice(0, index + 1).join("/");
12726
12992
  const stat = await lstatIfPresent(current);
12727
12993
  if (stat === null)
@@ -12747,12 +13013,12 @@ async function checkParents(cwd, relative6, projectedRemovals, allowMetadata = f
12747
13013
  return { kind: "safe", target, projectedMissing: false };
12748
13014
  }
12749
13015
  async function assertSafeCloneRoot(cwd) {
12750
- const root = await fs6.lstat(path12.resolve(cwd));
13016
+ const root = await fs6.lstat(path13.resolve(cwd));
12751
13017
  if (!root.isDirectory() || root.isSymbolicLink()) {
12752
13018
  throw new Error("launchpad pull requires a real clone directory");
12753
13019
  }
12754
13020
  try {
12755
- const metadata = await fs6.lstat(path12.resolve(cwd, MARKER_DIR));
13021
+ const metadata = await fs6.lstat(path13.resolve(cwd, MARKER_DIR));
12756
13022
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
12757
13023
  throw new Error("launchpad pull refuses a non-directory or symlinked .launchpad path");
12758
13024
  }
@@ -12808,7 +13074,7 @@ async function runPull(args, io) {
12808
13074
  }
12809
13075
  const yaml = stringifyYaml(res.status);
12810
13076
  if (parsed.out !== null) {
12811
- writeFileSync4(parsed.out, yaml);
13077
+ writeFileSync5(parsed.out, yaml);
12812
13078
  io.err(`launchpad pull: wrote status (${res.role} view) to ${parsed.out}`);
12813
13079
  } else {
12814
13080
  io.out(yaml.replace(/\n$/, ""));
@@ -12829,7 +13095,7 @@ async function runPull(args, io) {
12829
13095
  return 1;
12830
13096
  }
12831
13097
  if (parsed.out !== null) {
12832
- writeFileSync4(parsed.out, state.manifestYaml);
13098
+ writeFileSync5(parsed.out, state.manifestYaml);
12833
13099
  io.err(`launchpad pull: wrote ${state.manifestYaml.length} bytes to ${parsed.out}`);
12834
13100
  } else {
12835
13101
  io.out(state.manifestYaml.replace(/\n$/, ""));
@@ -13230,7 +13496,7 @@ var watchCommand = {
13230
13496
  };
13231
13497
 
13232
13498
  // src/commands/review.ts
13233
- import * as path13 from "node:path";
13499
+ import * as path14 from "node:path";
13234
13500
  var reviewCommand = {
13235
13501
  name: "review",
13236
13502
  summary: "show the review state for a PR (slug-scoped)",
@@ -13250,7 +13516,7 @@ async function runReview(args, io) {
13250
13516
  } else {
13251
13517
  const inferred = inferSlug({ cwd: process.cwd(), warn: (l) => io.err(l) });
13252
13518
  if (inferred === null) {
13253
- io.err(`launchpad review: could not infer slug from cwd (${path13.basename(process.cwd())});
13519
+ io.err(`launchpad review: could not infer slug from cwd (${path14.basename(process.cwd())});
13254
13520
  ` + ` pass --slug <slug>, or cd into a directory named launchpad-app-<slug>.`);
13255
13521
  return 64;
13256
13522
  }
@@ -13383,7 +13649,7 @@ function describe28(e) {
13383
13649
  }
13384
13650
 
13385
13651
  // src/deploy/rollback.ts
13386
- import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync5 } from "node:fs";
13652
+ import { existsSync as existsSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync6 } from "node:fs";
13387
13653
  import { resolve as resolvePath3 } from "node:path";
13388
13654
  import { createInterface as createInterface3 } from "node:readline/promises";
13389
13655
 
@@ -13485,7 +13751,7 @@ async function runRollback(opts, io, deps = {}) {
13485
13751
  }
13486
13752
  }
13487
13753
  try {
13488
- writeFileSync5(manifestPath, historicalYaml, "utf8");
13754
+ writeFileSync6(manifestPath, historicalYaml, "utf8");
13489
13755
  } catch (e) {
13490
13756
  io.err(`launchpad rollback: failed to write ${manifestPath}: ${describe29(e)}`);
13491
13757
  return 2;
@@ -13507,16 +13773,16 @@ async function runRollback(opts, io, deps = {}) {
13507
13773
  yes: true
13508
13774
  }, io, applyDeps);
13509
13775
  }
13510
- function readCurrentManifest(path14) {
13511
- if (!existsSync11(path14))
13776
+ function readCurrentManifest(path15) {
13777
+ if (!existsSync12(path15))
13512
13778
  return null;
13513
13779
  let raw;
13514
13780
  try {
13515
- raw = readFileSync16(path14, "utf8");
13781
+ raw = readFileSync17(path15, "utf8");
13516
13782
  } catch {
13517
13783
  return null;
13518
13784
  }
13519
- const parsed = parseManifest2(raw, path14);
13785
+ const parsed = parseManifest2(raw, path15);
13520
13786
  if (parsed.kind !== "ok")
13521
13787
  return null;
13522
13788
  return summarise(parsed.manifest);
@@ -13668,7 +13934,7 @@ function parseArgs12(args) {
13668
13934
  }
13669
13935
 
13670
13936
  // src/secrets/push.ts
13671
- import { existsSync as existsSync12, readFileSync as readFileSync17 } from "node:fs";
13937
+ import { existsSync as existsSync13, readFileSync as readFileSync18 } from "node:fs";
13672
13938
  import { resolve as resolvePath4 } from "node:path";
13673
13939
  import { createInterface as createInterface4 } from "node:readline/promises";
13674
13940
  import { stdin as input2, stdout as output2 } from "node:process";
@@ -13729,14 +13995,14 @@ async function runSecretsPush(opts, io, deps = {}) {
13729
13995
  return 0;
13730
13996
  }
13731
13997
  const envPath = resolvePath4(process.cwd(), opts.env ?? ".env");
13732
- if (!existsSync12(envPath)) {
13998
+ if (!existsSync13(envPath)) {
13733
13999
  io.err(`launchpad secrets push: ${envPath}`);
13734
14000
  io.err(" .env file not found. Run `launchpad secrets template` to scaffold one.");
13735
14001
  return 2;
13736
14002
  }
13737
14003
  let envText;
13738
14004
  try {
13739
- envText = readFileSync17(envPath, "utf8");
14005
+ envText = readFileSync18(envPath, "utf8");
13740
14006
  } catch (e) {
13741
14007
  io.err(`launchpad secrets push: failed to read ${envPath}: ${describe30(e)}`);
13742
14008
  return 2;
@@ -14079,7 +14345,7 @@ function renderManifestError5(result, io) {
14079
14345
  }
14080
14346
 
14081
14347
  // src/secrets/set.ts
14082
- import { existsSync as existsSync13, readFileSync as readFileSync18 } from "node:fs";
14348
+ import { existsSync as existsSync14, readFileSync as readFileSync19 } from "node:fs";
14083
14349
  import { resolve as resolvePath6 } from "node:path";
14084
14350
  import { parse as parseYaml8 } from "yaml";
14085
14351
  var CELL_LABEL2 = {
@@ -14088,23 +14354,23 @@ var CELL_LABEL2 = {
14088
14354
  not_deployed: "NOT_DEPLOYED"
14089
14355
  };
14090
14356
  function loadSet(fleetFile, setName, io) {
14091
- const path14 = resolvePath6(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
14092
- if (!existsSync13(path14)) {
14093
- io.err(`✗ ${path14}`);
14357
+ const path15 = resolvePath6(process.cwd(), fleetFile ?? "fleet-secret-sets.yaml");
14358
+ if (!existsSync14(path15)) {
14359
+ io.err(`✗ ${path15}`);
14094
14360
  io.err(" fleet-secret-sets.yaml not found. Run from the platform repo root or pass --fleet-file.");
14095
14361
  return 2;
14096
14362
  }
14097
14363
  let obj;
14098
14364
  try {
14099
- obj = parseYaml8(readFileSync18(path14, "utf8"));
14365
+ obj = parseYaml8(readFileSync19(path15, "utf8"));
14100
14366
  } catch (e) {
14101
- io.err(`✗ ${path14}`);
14367
+ io.err(`✗ ${path15}`);
14102
14368
  io.err(` YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
14103
14369
  return 1;
14104
14370
  }
14105
14371
  const parsed = parseFleetSecretSets(obj);
14106
14372
  if (!parsed.ok) {
14107
- io.err(`✗ ${path14}`);
14373
+ io.err(`✗ ${path15}`);
14108
14374
  io.err(` ${parsed.issues.length} schema issue(s):`);
14109
14375
  for (const i of parsed.issues)
14110
14376
  io.err(` ${i.path}: ${i.message}`);
@@ -14113,7 +14379,7 @@ function loadSet(fleetFile, setName, io) {
14113
14379
  const set = parsed.manifest.secretSets.find((s) => s.name === setName);
14114
14380
  if (set === undefined) {
14115
14381
  const names = parsed.manifest.secretSets.map((s) => s.name).join(", ");
14116
- io.err(`✗ secret-set "${setName}" not found in ${path14}`);
14382
+ io.err(`✗ secret-set "${setName}" not found in ${path15}`);
14117
14383
  io.err(` declared sets: ${names}`);
14118
14384
  return 1;
14119
14385
  }
@@ -14181,14 +14447,14 @@ async function runSecretsPushSet(opts, io, deps = {}) {
14181
14447
  if (typeof set === "number")
14182
14448
  return set;
14183
14449
  const envPath = resolvePath6(process.cwd(), opts.env ?? ".env");
14184
- if (!existsSync13(envPath)) {
14450
+ if (!existsSync14(envPath)) {
14185
14451
  io.err(`launchpad secrets push --set: ${envPath} not found.`);
14186
14452
  io.err(` Create a .env carrying the set's secrets: ${set.secrets.join(", ")}`);
14187
14453
  return 2;
14188
14454
  }
14189
14455
  let envText;
14190
14456
  try {
14191
- envText = readFileSync18(envPath, "utf8");
14457
+ envText = readFileSync19(envPath, "utf8");
14192
14458
  } catch (e) {
14193
14459
  io.err(`launchpad secrets push --set: failed to read ${envPath}: ${e instanceof Error ? e.message : String(e)}`);
14194
14460
  return 2;
@@ -14332,7 +14598,7 @@ function setPushExit(e) {
14332
14598
  }
14333
14599
 
14334
14600
  // src/commands/secrets-template.ts
14335
- import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "node:fs";
14601
+ import { existsSync as existsSync15, writeFileSync as writeFileSync7 } from "node:fs";
14336
14602
  import { resolve as resolve14 } from "node:path";
14337
14603
  async function runSecretsTemplate(args, io) {
14338
14604
  const flags = parseFlags4(args);
@@ -14356,13 +14622,13 @@ async function runSecretsTemplate(args, io) {
14356
14622
  return 0;
14357
14623
  }
14358
14624
  const outPath = resolve14(process.cwd(), flags.out);
14359
- if (existsSync14(outPath) && !flags.force) {
14625
+ if (existsSync15(outPath) && !flags.force) {
14360
14626
  io.err(`launchpad secrets template: ${outPath} already exists`);
14361
14627
  io.err("Pass --force to overwrite, or --stdout to print without writing.");
14362
14628
  return 64;
14363
14629
  }
14364
14630
  try {
14365
- writeFileSync6(outPath, body, { encoding: "utf8" });
14631
+ writeFileSync7(outPath, body, { encoding: "utf8" });
14366
14632
  } catch (err) {
14367
14633
  const e = err;
14368
14634
  io.err(`launchpad secrets template: failed to write ${outPath}`);
@@ -14653,12 +14919,12 @@ function printHelp3(io) {
14653
14919
  // src/commands/skills.ts
14654
14920
  import { fileURLToPath } from "node:url";
14655
14921
  import { dirname as dirname10, join as join16, resolve as resolve15 } from "node:path";
14656
- import { promises as fs7, existsSync as existsSync15 } from "node:fs";
14922
+ import { promises as fs7, existsSync as existsSync16 } from "node:fs";
14657
14923
 
14658
14924
  // src/skills-bundle.ts
14659
14925
  import { homedir as homedir2 } from "node:os";
14660
14926
  import { join as join15 } from "node:path";
14661
- import { readFileSync as readFileSync19, readdirSync as readdirSync2 } from "node:fs";
14927
+ import { readFileSync as readFileSync20, readdirSync as readdirSync2 } from "node:fs";
14662
14928
  var SKILL_PREFIX = "launchpad-";
14663
14929
  function skillsTargetDir() {
14664
14930
  return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir2(), ".claude", "skills");
@@ -14677,9 +14943,9 @@ function readInstalledSkills() {
14677
14943
  return { present: false, version: null };
14678
14944
  }
14679
14945
  }
14680
- function readSkillVersion(path14) {
14946
+ function readSkillVersion(path15) {
14681
14947
  try {
14682
- const text = readFileSync19(path14, "utf8");
14948
+ const text = readFileSync20(path15, "utf8");
14683
14949
  const fenceEnd = text.indexOf(`
14684
14950
  ---`, 4);
14685
14951
  const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
@@ -14762,7 +15028,7 @@ function defaultBundleDir() {
14762
15028
  resolve15(here, "..", "..", "skills")
14763
15029
  ];
14764
15030
  for (const c of candidates) {
14765
- if (existsSync15(join16(c, "launchpad-onboard", "SKILL.md"))) {
15031
+ if (existsSync16(join16(c, "launchpad-onboard", "SKILL.md"))) {
14766
15032
  return c;
14767
15033
  }
14768
15034
  }
@@ -14842,10 +15108,10 @@ async function doList(io) {
14842
15108
  }
14843
15109
  return 0;
14844
15110
  }
14845
- async function readVersion(path14) {
15111
+ async function readVersion(path15) {
14846
15112
  let text;
14847
15113
  try {
14848
- text = await fs7.readFile(path14, "utf8");
15114
+ text = await fs7.readFile(path15, "utf8");
14849
15115
  } catch {
14850
15116
  return null;
14851
15117
  }
@@ -14855,9 +15121,9 @@ async function readVersion(path14) {
14855
15121
  const m = /^version:\s*(.+?)\s*$/m.exec(front);
14856
15122
  return m === null ? null : m[1] ?? null;
14857
15123
  }
14858
- async function isDir(path14) {
15124
+ async function isDir(path15) {
14859
15125
  try {
14860
- const stat = await fs7.stat(path14);
15126
+ const stat = await fs7.stat(path15);
14861
15127
  return stat.isDirectory();
14862
15128
  } catch {
14863
15129
  return false;
@@ -14873,7 +15139,7 @@ import { promisify } from "node:util";
14873
15139
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14874
15140
  import { dirname as dirname11, resolve as resolve16, relative as relative6, isAbsolute as isAbsolute3, join as join17 } from "node:path";
14875
15141
  import { homedir as homedir3, tmpdir } from "node:os";
14876
- import { readFileSync as readFileSync20, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
15142
+ import { readFileSync as readFileSync21, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync8, rmSync as rmSync3 } from "node:fs";
14877
15143
 
14878
15144
  // src/commands/channel-auth.ts
14879
15145
  import { createServer as createServer2 } from "node:http";
@@ -15123,17 +15389,17 @@ async function openSystemBrowser(url) {
15123
15389
  await execFileAsync(opener, [url]);
15124
15390
  }
15125
15391
  async function runInstallerScript(script) {
15126
- const dir = mkdtempSync(join17(tmpdir(), "launchpad-update-"));
15392
+ const dir = mkdtempSync2(join17(tmpdir(), "launchpad-update-"));
15127
15393
  const file = join17(dir, "install.sh");
15128
15394
  try {
15129
- writeFileSync7(file, script, { mode: 448 });
15395
+ writeFileSync8(file, script, { mode: 448 });
15130
15396
  return await new Promise((resolvePromise) => {
15131
15397
  const child = spawn5("bash", [file], { stdio: "inherit" });
15132
15398
  child.on("error", () => resolvePromise(127));
15133
15399
  child.on("close", (c) => resolvePromise(c ?? 1));
15134
15400
  });
15135
15401
  } finally {
15136
- rmSync2(dir, { recursive: true, force: true });
15402
+ rmSync3(dir, { recursive: true, force: true });
15137
15403
  }
15138
15404
  }
15139
15405
  async function runChannelInstaller() {
@@ -15148,7 +15414,7 @@ function resolveLatestVersion() {
15148
15414
  }
15149
15415
  function detectInstallChannel() {
15150
15416
  try {
15151
- return readFileSync20(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
15417
+ return readFileSync21(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
15152
15418
  } catch {
15153
15419
  return "github";
15154
15420
  }
@@ -15356,7 +15622,7 @@ function printHelp5(io) {
15356
15622
  }
15357
15623
 
15358
15624
  // src/commands/validate.ts
15359
- import { existsSync as existsSync16, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
15625
+ import { existsSync as existsSync17, readFileSync as readFileSync22, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
15360
15626
  import { dirname as dirname12, resolve as resolve17 } from "node:path";
15361
15627
  var validateCommand = {
15362
15628
  name: "validate",
@@ -15371,14 +15637,14 @@ async function runValidate(args, io) {
15371
15637
  return 64;
15372
15638
  }
15373
15639
  const { file, json, strictGroups } = parseResult;
15374
- const path14 = resolve17(process.cwd(), file ?? "launchpad.yaml");
15375
- const result = loadManifestAnyVersion(path14);
15640
+ const path15 = resolve17(process.cwd(), file ?? "launchpad.yaml");
15641
+ const result = loadManifestAnyVersion(path15);
15376
15642
  if (result.kind !== "ok") {
15377
15643
  return json ? renderJsonError(result, io) : renderHumanError(result, io);
15378
15644
  }
15379
15645
  const unsupported2 = rejectUnsupportedCliRuntime({
15380
15646
  command: "validate",
15381
- manifestPath: path14,
15647
+ manifestPath: path15,
15382
15648
  manifest: result.manifest,
15383
15649
  io,
15384
15650
  json,
@@ -15387,8 +15653,8 @@ async function runValidate(args, io) {
15387
15653
  if (unsupported2 !== null)
15388
15654
  return unsupported2;
15389
15655
  const view = manifestView(result.manifest);
15390
- const boundary = checkBoundary(path14, view.appDeclared);
15391
- const serveErrors = checkStaticServeDir(path14, result.manifest);
15656
+ const boundary = checkBoundary(path15, view.appDeclared);
15657
+ const serveErrors = checkStaticServeDir(path15, result.manifest);
15392
15658
  if (serveErrors.length > 0) {
15393
15659
  boundary.errors = [...boundary.errors, ...serveErrors];
15394
15660
  }
@@ -15405,7 +15671,7 @@ function checkBoundary(manifestPath, declared) {
15405
15671
  }
15406
15672
  let manifestYaml;
15407
15673
  try {
15408
- manifestYaml = readFileSync21(manifestPath, "utf8");
15674
+ manifestYaml = readFileSync22(manifestPath, "utf8");
15409
15675
  } catch {
15410
15676
  manifestYaml = null;
15411
15677
  }
@@ -15431,7 +15697,7 @@ function checkStaticServeDir(manifestPath, manifest) {
15431
15697
  }
15432
15698
  const dir = dirname12(manifestPath);
15433
15699
  const abs = resolve17(dir, serveDir);
15434
- if (!existsSync16(abs)) {
15700
+ if (!existsSync17(abs)) {
15435
15701
  return [
15436
15702
  `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).`
15437
15703
  ];
@@ -15456,7 +15722,7 @@ function checkStaticServeDir(manifestPath, manifest) {
15456
15722
  `static app's served directory '${serveDir}/' is empty — the deploy would serve an empty site.`
15457
15723
  ];
15458
15724
  }
15459
- if (!existsSync16(resolve17(abs, "index.html"))) {
15725
+ if (!existsSync17(resolve17(abs, "index.html"))) {
15460
15726
  return [
15461
15727
  `static app's served directory '${serveDir}/' has no index.html — the deploy would serve a directory with no entrypoint.`
15462
15728
  ];
@@ -16433,10 +16699,10 @@ import { spawn as spawn6 } from "node:child_process";
16433
16699
  import { homedir as homedir4 } from "node:os";
16434
16700
  import { join as join18 } from "node:path";
16435
16701
  import {
16436
- existsSync as existsSync17,
16437
- mkdirSync as mkdirSync4,
16438
- readFileSync as readFileSync22,
16439
- writeFileSync as writeFileSync8
16702
+ existsSync as existsSync18,
16703
+ mkdirSync as mkdirSync5,
16704
+ readFileSync as readFileSync23,
16705
+ writeFileSync as writeFileSync9
16440
16706
  } from "node:fs";
16441
16707
  var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
16442
16708
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -16444,7 +16710,7 @@ var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
16444
16710
  var CACHE_FILE = join18(homedir4(), ".launchpad", "update-check.json");
16445
16711
  function readCache2() {
16446
16712
  try {
16447
- const raw = JSON.parse(readFileSync22(CACHE_FILE, "utf8"));
16713
+ const raw = JSON.parse(readFileSync23(CACHE_FILE, "utf8"));
16448
16714
  if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
16449
16715
  const latest = raw.latest;
16450
16716
  return {
@@ -16457,23 +16723,23 @@ function readCache2() {
16457
16723
  }
16458
16724
  function writeCache2(state) {
16459
16725
  try {
16460
- mkdirSync4(join18(homedir4(), ".launchpad"), { recursive: true });
16461
- writeFileSync8(CACHE_FILE, `${JSON.stringify(state)}
16726
+ mkdirSync5(join18(homedir4(), ".launchpad"), { recursive: true });
16727
+ writeFileSync9(CACHE_FILE, `${JSON.stringify(state)}
16462
16728
  `, { mode: 384 });
16463
16729
  } catch {}
16464
16730
  }
16465
16731
  var SKILLS_HINT_MARKER = join18(homedir4(), ".launchpad", "skills-hint-shown");
16466
16732
  function absentHintShown() {
16467
16733
  try {
16468
- return existsSync17(SKILLS_HINT_MARKER);
16734
+ return existsSync18(SKILLS_HINT_MARKER);
16469
16735
  } catch {
16470
16736
  return false;
16471
16737
  }
16472
16738
  }
16473
16739
  function markAbsentHintShown() {
16474
16740
  try {
16475
- mkdirSync4(join18(homedir4(), ".launchpad"), { recursive: true });
16476
- writeFileSync8(SKILLS_HINT_MARKER, `${Date.now()}
16741
+ mkdirSync5(join18(homedir4(), ".launchpad"), { recursive: true });
16742
+ writeFileSync9(SKILLS_HINT_MARKER, `${Date.now()}
16477
16743
  `, { mode: 384 });
16478
16744
  } catch {}
16479
16745
  }
@@ -16576,7 +16842,7 @@ var refreshUpdateCacheCommand = {
16576
16842
  import { spawn as spawn7 } from "node:child_process";
16577
16843
  import { homedir as homedir5 } from "node:os";
16578
16844
  import { join as join19 } from "node:path";
16579
- import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "node:fs";
16845
+ import { existsSync as existsSync19, mkdirSync as mkdirSync6, readFileSync as readFileSync24, writeFileSync as writeFileSync10 } from "node:fs";
16580
16846
  import { randomUUID } from "node:crypto";
16581
16847
  var INTERNAL_EMIT_VERB = "__emit-telemetry";
16582
16848
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -16652,15 +16918,15 @@ async function resolveIdentityReal() {
16652
16918
  }
16653
16919
  function getOrCreateDeviceId() {
16654
16920
  try {
16655
- const raw = JSON.parse(readFileSync23(DEVICE_ID_FILE, "utf8"));
16921
+ const raw = JSON.parse(readFileSync24(DEVICE_ID_FILE, "utf8"));
16656
16922
  const id2 = raw?.deviceId;
16657
16923
  if (typeof id2 === "string" && id2)
16658
16924
  return id2;
16659
16925
  } catch {}
16660
16926
  const id = randomUUID();
16661
16927
  try {
16662
- mkdirSync5(join19(homedir5(), ".launchpad"), { recursive: true });
16663
- writeFileSync9(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
16928
+ mkdirSync6(join19(homedir5(), ".launchpad"), { recursive: true });
16929
+ writeFileSync10(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
16664
16930
  `, {
16665
16931
  mode: 384
16666
16932
  });
@@ -16730,15 +16996,15 @@ function telemetryNoticeLine() {
16730
16996
  }
16731
16997
  function firstRunNoticeShown() {
16732
16998
  try {
16733
- return existsSync18(FIRST_RUN_MARKER);
16999
+ return existsSync19(FIRST_RUN_MARKER);
16734
17000
  } catch {
16735
17001
  return false;
16736
17002
  }
16737
17003
  }
16738
17004
  function markFirstRunNotice() {
16739
17005
  try {
16740
- mkdirSync5(join19(homedir5(), ".launchpad"), { recursive: true });
16741
- writeFileSync9(FIRST_RUN_MARKER, `${Date.now()}
17006
+ mkdirSync6(join19(homedir5(), ".launchpad"), { recursive: true });
17007
+ writeFileSync10(FIRST_RUN_MARKER, `${Date.now()}
16742
17008
  `, { mode: 384 });
16743
17009
  } catch {}
16744
17010
  }
@@ -16803,7 +17069,7 @@ import { createInterface as createInterface7 } from "node:readline/promises";
16803
17069
  import { platform } from "node:os";
16804
17070
 
16805
17071
  // src/report/breadcrumb.ts
16806
- import { writeFileSync as writeFileSync10, readFileSync as readFileSync24, mkdirSync as mkdirSync6 } from "node:fs";
17072
+ import { writeFileSync as writeFileSync11, readFileSync as readFileSync25, mkdirSync as mkdirSync7 } from "node:fs";
16807
17073
  import { homedir as homedir6 } from "node:os";
16808
17074
  import { join as join20, dirname as dirname13 } from "node:path";
16809
17075
  function breadcrumbPath() {
@@ -16812,13 +17078,13 @@ function breadcrumbPath() {
16812
17078
  function writeBreadcrumb(verb, exit) {
16813
17079
  try {
16814
17080
  const p = breadcrumbPath();
16815
- mkdirSync6(dirname13(p), { recursive: true });
16816
- writeFileSync10(p, JSON.stringify({ verb, exit, ts: Date.now() }), "utf8");
17081
+ mkdirSync7(dirname13(p), { recursive: true });
17082
+ writeFileSync11(p, JSON.stringify({ verb, exit, ts: Date.now() }), "utf8");
16817
17083
  } catch {}
16818
17084
  }
16819
17085
  function readBreadcrumb() {
16820
17086
  try {
16821
- const raw = JSON.parse(readFileSync24(breadcrumbPath(), "utf8"));
17087
+ const raw = JSON.parse(readFileSync25(breadcrumbPath(), "utf8"));
16822
17088
  if (typeof raw !== "object" || raw === null)
16823
17089
  return null;
16824
17090
  const r = raw;
@@ -16987,6 +17253,7 @@ var COMMANDS = [
16987
17253
  createCommand,
16988
17254
  cloneCommand,
16989
17255
  deployCommand,
17256
+ migrateCommand,
16990
17257
  redeployCommand,
16991
17258
  reviewCommand,
16992
17259
  mergeCommand,