@m-kopa/launchpad-cli 0.55.0 → 0.56.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,9 +19,10 @@ 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.55.0";
22
+ var CLI_VERSION = "0.56.0";
23
23
 
24
24
  // src/config.ts
25
+ import { readFileSync } from "node:fs";
25
26
  import * as os from "node:os";
26
27
  import * as path from "node:path";
27
28
  var DEFAULT_BOT_URL = "https://launchpad-portal-bot.mkopa-launchpad.workers.dev";
@@ -39,6 +40,8 @@ function loadConfig(env = process.env) {
39
40
  const stateDir = env.LAUNCHPAD_STATE_DIR ?? path.join(os.homedir(), ".launchpad", "state");
40
41
  const rawPlatformRepo = env.LAUNCHPAD_PLATFORM_REPO;
41
42
  const platformRepoPath = rawPlatformRepo !== undefined && rawPlatformRepo.length > 0 ? path.resolve(rawPlatformRepo) : null;
43
+ const configPath = env.LAUNCHPAD_CONFIG_PATH ?? path.join(os.homedir(), ".launchpad", "config.json");
44
+ const statusPageAutoOpen = readStatusPageAutoOpen(configPath);
42
45
  return {
43
46
  botUrl,
44
47
  authGatewayUrl,
@@ -47,9 +50,18 @@ function loadConfig(env = process.env) {
47
50
  sessionPath,
48
51
  cacheDir,
49
52
  stateDir,
50
- platformRepoPath
53
+ platformRepoPath,
54
+ statusPageAutoOpen
51
55
  };
52
56
  }
57
+ function readStatusPageAutoOpen(configPath) {
58
+ try {
59
+ const value = JSON.parse(readFileSync(configPath, "utf8"));
60
+ return !(typeof value === "object" && value !== null && value.statusPageAutoOpen === false);
61
+ } catch (error) {
62
+ return error.code === "ENOENT";
63
+ }
64
+ }
53
65
 
54
66
  // src/http/error-envelope.ts
55
67
  var MAX_TEXT_CHARS = 16384;
@@ -1874,7 +1886,7 @@ import { existsSync as existsSync7 } from "node:fs";
1874
1886
  import * as path8 from "node:path";
1875
1887
 
1876
1888
  // src/bundle/orchestrate.ts
1877
- import { readFileSync as readFileSync4 } from "node:fs";
1889
+ import { readFileSync as readFileSync5 } from "node:fs";
1878
1890
  import { join as join7 } from "node:path";
1879
1891
 
1880
1892
  // src/deploy/tar-pack.ts
@@ -2186,7 +2198,8 @@ function deployBundleError(envelope, body) {
2186
2198
  ...paths === undefined ? {} : { paths },
2187
2199
  ...typeof body.reason === "string" ? { reason: body.reason } : {},
2188
2200
  ...typeof body.minimum === "string" ? { minimum: body.minimum } : {},
2189
- ...typeof body.yours === "string" ? { yours: body.yours } : {}
2201
+ ...typeof body.yours === "string" ? { yours: body.yours } : {},
2202
+ ...typeof body.statusPageUrl === "string" ? { statusPageUrl: body.statusPageUrl } : {}
2190
2203
  };
2191
2204
  }
2192
2205
  function readStringArray(value) {
@@ -2210,7 +2223,7 @@ function isRecord2(value) {
2210
2223
  }
2211
2224
 
2212
2225
  // src/bundle/cwd-walker.ts
2213
- import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
2226
+ import { existsSync, lstatSync, readFileSync as readFileSync2, readdirSync } from "node:fs";
2214
2227
  import { join as join4, relative as relative2, sep } from "node:path";
2215
2228
  var DEFAULT_IGNORE = [
2216
2229
  ".git",
@@ -2316,7 +2329,7 @@ function loadIgnore(cwd) {
2316
2329
  }
2317
2330
  let content;
2318
2331
  try {
2319
- content = readFileSync(file, "utf8");
2332
+ content = readFileSync2(file, "utf8");
2320
2333
  } catch {
2321
2334
  return { matches: () => false };
2322
2335
  }
@@ -2390,7 +2403,7 @@ function globToRegExp(glob) {
2390
2403
  }
2391
2404
 
2392
2405
  // src/bundle/cron-bundle.ts
2393
- import { readFileSync as readFileSync2 } from "node:fs";
2406
+ import { readFileSync as readFileSync3 } from "node:fs";
2394
2407
  import { dirname as dirname4, join as join5, resolve as resolve5 } from "node:path";
2395
2408
  import { parse as parseYaml } from "yaml";
2396
2409
  import { build as esbuild } from "esbuild";
@@ -2470,7 +2483,7 @@ function locateWorkerConfig(cwd, walkFiles, script) {
2470
2483
  const abs = resolve5(cwd, rel);
2471
2484
  let text;
2472
2485
  try {
2473
- text = readFileSync2(abs, "utf8");
2486
+ text = readFileSync3(abs, "utf8");
2474
2487
  } catch {
2475
2488
  continue;
2476
2489
  }
@@ -2549,7 +2562,7 @@ async function buildWorkerArtifact(cwd, manifestYaml, walkFiles) {
2549
2562
  }
2550
2563
 
2551
2564
  // src/bundle/boundary.ts
2552
- import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync3 } from "node:fs";
2565
+ import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync4 } from "node:fs";
2553
2566
  import { join as join6 } from "node:path";
2554
2567
  import { parse as parseYaml2 } from "yaml";
2555
2568
 
@@ -4390,7 +4403,7 @@ function applyBoundaryToFiles(args) {
4390
4403
  const ignorePath = join6(cwd, ".launchpadignore");
4391
4404
  if (existsSync2(ignorePath)) {
4392
4405
  try {
4393
- launchpadIgnore = parseLaunchpadIgnore(readFileSync3(ignorePath, "utf8"));
4406
+ launchpadIgnore = parseLaunchpadIgnore(readFileSync4(ignorePath, "utf8"));
4394
4407
  } catch {}
4395
4408
  }
4396
4409
  const result = applyAppBoundary(contract, files, {
@@ -4401,7 +4414,7 @@ function applyBoundaryToFiles(args) {
4401
4414
  const stat = lstatSync2(abs);
4402
4415
  if (!stat.isFile() || stat.size > MAX_CONTENT_PROBE_BYTES)
4403
4416
  return;
4404
- return readFileSync3(abs, "utf8");
4417
+ return readFileSync4(abs, "utf8");
4405
4418
  } catch {
4406
4419
  return;
4407
4420
  }
@@ -4453,7 +4466,7 @@ async function bundleAndDeploy(args) {
4453
4466
  const manifestPath = join7(cwd, "launchpad.yaml");
4454
4467
  let manifestYaml;
4455
4468
  try {
4456
- manifestYaml = readFileSync4(manifestPath, "utf8");
4469
+ manifestYaml = readFileSync5(manifestPath, "utf8");
4457
4470
  } catch (e) {
4458
4471
  return {
4459
4472
  kind: "no-manifest",
@@ -4515,7 +4528,7 @@ async function bundleAndDeploy(args) {
4515
4528
  }
4516
4529
 
4517
4530
  // src/deploy/verify-deploy.ts
4518
- import { readFileSync as readFileSync8, existsSync as existsSync4 } from "node:fs";
4531
+ import { readFileSync as readFileSync9, existsSync as existsSync4 } from "node:fs";
4519
4532
  import { join as join9 } from "node:path";
4520
4533
 
4521
4534
  // src/deploy/redact-diagnostic.ts
@@ -4760,7 +4773,7 @@ async function verifyServed(args, deps = { fetch }) {
4760
4773
  }
4761
4774
 
4762
4775
  // src/commands/status.ts
4763
- import { readFileSync as readFileSync7 } from "node:fs";
4776
+ import { readFileSync as readFileSync8 } from "node:fs";
4764
4777
 
4765
4778
  // src/deploy/manifest-state.ts
4766
4779
  async function fetchManifestState(cfg, slug, opts = {}, fetcher = fetch) {
@@ -4770,6 +4783,7 @@ async function fetchManifestState(cfg, slug, opts = {}, fetcher = fetch) {
4770
4783
  slug: raw.slug,
4771
4784
  hasAppFile: raw.hasAppFile,
4772
4785
  lastAppliedManifestSha: raw.lastAppliedManifestSha,
4786
+ deployedManifestSha: raw.deployedManifestSha ?? raw.lastAppliedManifestSha,
4773
4787
  appRepoHeadSha: raw.appRepoHeadSha ?? null,
4774
4788
  openPr: raw.openPr,
4775
4789
  enforcedGroups: raw.enforcedGroups ?? null,
@@ -4779,7 +4793,7 @@ async function fetchManifestState(cfg, slug, opts = {}, fetcher = fetch) {
4779
4793
 
4780
4794
  // src/commands/infer-slug.ts
4781
4795
  import * as path7 from "node:path";
4782
- import { existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
4796
+ import { existsSync as existsSync3, readFileSync as readFileSync6 } from "node:fs";
4783
4797
  import { parse as parseYaml3 } from "yaml";
4784
4798
  var DIRNAME_RE = /^launchpad-app-([a-z0-9][a-z0-9-]*[a-z0-9])$/;
4785
4799
  function inferSlugFromCwd(cwd) {
@@ -4802,7 +4816,7 @@ function inferSlugFromManifestFile(manifestPath) {
4802
4816
  if (!existsSync3(manifestPath))
4803
4817
  return null;
4804
4818
  try {
4805
- return resolveManifestSlug(parseYaml3(readFileSync5(manifestPath, "utf8")));
4819
+ return resolveManifestSlug(parseYaml3(readFileSync6(manifestPath, "utf8")));
4806
4820
  } catch {
4807
4821
  return null;
4808
4822
  }
@@ -4818,7 +4832,7 @@ function inferSlug(opts) {
4818
4832
  }
4819
4833
 
4820
4834
  // src/groups/client.ts
4821
- import { mkdirSync, readFileSync as readFileSync6, writeFileSync } from "node:fs";
4835
+ import { mkdirSync, readFileSync as readFileSync7, writeFileSync } from "node:fs";
4822
4836
  import { dirname as dirname5, join as join8 } from "node:path";
4823
4837
  var CACHE_TTL_MS = 60 * 60 * 1000;
4824
4838
  var CACHE_FILENAME = "groups.json";
@@ -4862,7 +4876,7 @@ async function fetchGroups(cfg, opts = {}) {
4862
4876
  function readCache(path8) {
4863
4877
  let raw;
4864
4878
  try {
4865
- raw = readFileSync6(path8, "utf8");
4879
+ raw = readFileSync7(path8, "utf8");
4866
4880
  } catch {
4867
4881
  return null;
4868
4882
  }
@@ -5498,9 +5512,10 @@ async function runWatch(slug, io, deps = {}) {
5498
5512
  const cfg = loadConfig();
5499
5513
  const now = deps.now ?? (() => Date.now());
5500
5514
  const stdout = deps.stdout ?? process.stdout;
5515
+ const readLifecycle = deps.fetchLifecycle ?? (() => fetchLifecycleView(cfg, slug));
5501
5516
  let first;
5502
5517
  try {
5503
- first = await fetchLifecycleView(cfg, slug);
5518
+ first = await readLifecycle();
5504
5519
  } catch (e) {
5505
5520
  return mapWatchError(e, slug, io);
5506
5521
  }
@@ -5508,6 +5523,9 @@ async function runWatch(slug, io, deps = {}) {
5508
5523
  io.err(`launchpad watch: no app "${slug}" to watch (unknown slug, or first deploy not started)`);
5509
5524
  return 1;
5510
5525
  }
5526
+ if (first.statusPageUrl !== undefined) {
5527
+ io.out(`Status page: ${first.statusPageUrl}`);
5528
+ }
5511
5529
  if (first.state === "destroyed" || first.state === "destroying" || first.state === "destroy_failed") {
5512
5530
  io.out(`${slug}: ${first.state} — nothing to watch.`);
5513
5531
  return 0;
@@ -5534,7 +5552,7 @@ async function runWatch(slug, io, deps = {}) {
5534
5552
  };
5535
5553
  try {
5536
5554
  const res = await runWatchLoop(slug, {
5537
- fetchLifecycle: () => fetchLifecycleView(cfg, slug),
5555
+ fetchLifecycle: readLifecycle,
5538
5556
  now,
5539
5557
  sleep: makeRealSleep(),
5540
5558
  signal: controller.signal,
@@ -5571,6 +5589,19 @@ var statusCommand = {
5571
5589
  run: runStatus
5572
5590
  };
5573
5591
  var SLUG_RE3 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
5592
+ function statusTruthFields(args) {
5593
+ return {
5594
+ slug: args.slug,
5595
+ deployedSha: args.deployedSha,
5596
+ headSha: args.headSha,
5597
+ hasOpenPr: args.openPrNumber !== null,
5598
+ openPrNumber: args.openPrNumber,
5599
+ driftFields: [],
5600
+ driftDetails: [],
5601
+ ...args.deployment !== undefined ? { deployment: args.deployment } : {},
5602
+ ...args.standingExceptions !== undefined ? { standingExceptions: args.standingExceptions } : {}
5603
+ };
5604
+ }
5574
5605
  async function fetchLifecycle(cfg, slug) {
5575
5606
  try {
5576
5607
  return await apiJson(cfg, { path: `/apps/${slug}/lifecycle` });
@@ -5646,7 +5677,7 @@ async function runStatus(args, io) {
5646
5677
  }
5647
5678
  let localYaml = null;
5648
5679
  try {
5649
- localYaml = readFileSync7(parsed.file, "utf8");
5680
+ localYaml = readFileSync8(parsed.file, "utf8");
5650
5681
  } catch (e) {
5651
5682
  if (!isEnoent(e)) {
5652
5683
  io.err(`launchpad status: cannot read local manifest at ${parsed.file}: ${describe12(e)}`);
@@ -5707,16 +5738,16 @@ async function runStatus(args, io) {
5707
5738
  const platformTracked = state.hasAppFile === true || live?.trigger === "bot";
5708
5739
  const out = {
5709
5740
  state: contentIsLive ? platformTracked ? "live_drift_unknown" : "live_content_untracked" : platformTracked && !deploymentKnown ? "live_drift_unknown" : liveButEmpty ? "live_no_content" : "no_deployed_manifest",
5710
- slug: parsed.slug,
5711
5741
  ...local === null ? { drift: null } : {},
5712
- deployedSha: state.lastAppliedManifestSha,
5713
- headSha: state.appRepoHeadSha,
5714
- hasOpenPr: state.openPr !== null,
5715
- openPrNumber: state.openPr?.number ?? null,
5716
- driftFields: [],
5717
- driftDetails: [],
5718
- ...deploymentKnown ? { deployment } : {},
5719
- ...standingExceptions !== null ? { standingExceptions } : {}
5742
+ ...statusTruthFields({
5743
+ slug: parsed.slug,
5744
+ deployedSha: state.deployedManifestSha,
5745
+ headSha: state.appRepoHeadSha,
5746
+ openPrNumber: state.openPr?.number ?? null,
5747
+ ...deploymentKnown ? { deployment } : {},
5748
+ ...standingExceptions !== null ? { standingExceptions } : {}
5749
+ }),
5750
+ ...lifecycle?.statusPageUrl !== undefined ? { statusPageUrl: lifecycle.statusPageUrl } : {}
5720
5751
  };
5721
5752
  emit4(out, parsed.json, io);
5722
5753
  return 0;
@@ -5724,16 +5755,16 @@ async function runStatus(args, io) {
5724
5755
  if (local === null) {
5725
5756
  const out = {
5726
5757
  state: "live_drift_unknown",
5727
- slug: parsed.slug,
5728
5758
  drift: null,
5729
- deployedSha: state.lastAppliedManifestSha,
5730
- headSha: state.appRepoHeadSha,
5731
- hasOpenPr: state.openPr !== null,
5732
- openPrNumber: state.openPr?.number ?? null,
5733
- driftFields: [],
5734
- driftDetails: [],
5735
- ...deploymentKnown ? { deployment } : {},
5736
- ...standingExceptions !== null ? { standingExceptions } : {}
5759
+ ...statusTruthFields({
5760
+ slug: parsed.slug,
5761
+ deployedSha: state.deployedManifestSha,
5762
+ headSha: state.appRepoHeadSha,
5763
+ openPrNumber: state.openPr?.number ?? null,
5764
+ ...deploymentKnown ? { deployment } : {},
5765
+ ...standingExceptions !== null ? { standingExceptions } : {}
5766
+ }),
5767
+ ...lifecycle?.statusPageUrl !== undefined ? { statusPageUrl: lifecycle.statusPageUrl } : {}
5737
5768
  };
5738
5769
  emit4(out, parsed.json, io);
5739
5770
  if (parsed.strict) {
@@ -5750,12 +5781,12 @@ async function runStatus(args, io) {
5750
5781
  const { parse: parseYaml4 } = await import("yaml");
5751
5782
  deployedObj = parseYaml4(state.manifestYaml);
5752
5783
  } catch (e) {
5753
- io.err(`launchpad status: deployed manifest at ${state.lastAppliedManifestSha} is not valid YAML: ${describe12(e)}`);
5784
+ io.err(`launchpad status: deployed manifest at ${state.deployedManifestSha} is not valid YAML: ${describe12(e)}`);
5754
5785
  return 2;
5755
5786
  }
5756
5787
  const deployedParse = parseManifest(deployedObj);
5757
5788
  if (deployedParse.kind !== "ok") {
5758
- io.err(`launchpad status: deployed manifest at ${state.lastAppliedManifestSha} failed schema validation:`);
5789
+ io.err(`launchpad status: deployed manifest at ${state.deployedManifestSha} failed schema validation:`);
5759
5790
  for (const issue of deployedParse.issues) {
5760
5791
  io.err(` - ${issue.path}: ${issue.message}`);
5761
5792
  }
@@ -5775,7 +5806,7 @@ async function runStatus(args, io) {
5775
5806
  cfg,
5776
5807
  refs: allowedEntraGroups(deployed.access),
5777
5808
  source: "deployed manifest",
5778
- manifestSha: state.lastAppliedManifestSha
5809
+ manifestSha: state.deployedManifestSha
5779
5810
  });
5780
5811
  if (deployedGroups.kind === "error") {
5781
5812
  return renderStatusGroupError(deployedGroups.envelope, parsed.json, io);
@@ -5790,7 +5821,7 @@ async function runStatus(args, io) {
5790
5821
  cfg,
5791
5822
  refs: state.enforcedGroups,
5792
5823
  source: "enforced access",
5793
- manifestSha: state.lastAppliedManifestSha
5824
+ manifestSha: state.deployedManifestSha
5794
5825
  });
5795
5826
  if (enforcedGroups.kind === "error") {
5796
5827
  return renderStatusGroupError(enforcedGroups.envelope, parsed.json, io);
@@ -5799,7 +5830,7 @@ async function runStatus(args, io) {
5799
5830
  }
5800
5831
  const localAssignmentGroups = (local.auth ?? "access") === "access" ? localGroups : null;
5801
5832
  const deployedAssignmentGroups = (deployed.auth ?? "access") === "access" ? deployedGroups : null;
5802
- const assignmentCheck = localAssignmentGroups === null && deployedAssignmentGroups === null ? { kind: "ok", issues: [] } : await findAssignmentIssues(cfg, localAssignmentGroups, deployedAssignmentGroups, state.lastAppliedManifestSha);
5833
+ const assignmentCheck = localAssignmentGroups === null && deployedAssignmentGroups === null ? { kind: "ok", issues: [] } : await findAssignmentIssues(cfg, localAssignmentGroups, deployedAssignmentGroups, state.deployedManifestSha);
5803
5834
  if (assignmentCheck.kind === "error") {
5804
5835
  return renderStatusGroupError(assignmentCheck.envelope, parsed.json, io);
5805
5836
  }
@@ -5807,7 +5838,7 @@ async function runStatus(args, io) {
5807
5838
  const result = {
5808
5839
  state: drift.length === 0 ? "in_sync" : "drift",
5809
5840
  slug: parsed.slug,
5810
- deployedSha: state.lastAppliedManifestSha,
5841
+ deployedSha: state.deployedManifestSha,
5811
5842
  headSha: state.appRepoHeadSha,
5812
5843
  hasOpenPr: state.openPr !== null,
5813
5844
  openPrNumber: state.openPr?.number ?? null,
@@ -5816,7 +5847,8 @@ async function runStatus(args, io) {
5816
5847
  ...deploymentKnown ? { deployment } : {},
5817
5848
  ...standingExceptions !== null ? { standingExceptions } : {},
5818
5849
  ...accessDrift !== undefined ? { accessDrift } : {},
5819
- ...groupIssues.length === 0 ? {} : { groupIssues }
5850
+ ...groupIssues.length === 0 ? {} : { groupIssues },
5851
+ ...lifecycle?.statusPageUrl !== undefined ? { statusPageUrl: lifecycle.statusPageUrl } : {}
5820
5852
  };
5821
5853
  emit4(result, parsed.json, io);
5822
5854
  if (parsed.strict && (result.state === "drift" || accessDrift?.inSync === false || groupIssues.length > 0)) {
@@ -6011,7 +6043,8 @@ function lifecycleOutput(slug, lc) {
6011
6043
  hasOpenPr: false,
6012
6044
  openPrNumber: null,
6013
6045
  driftFields: [],
6014
- driftDetails: []
6046
+ driftDetails: [],
6047
+ ...lc.statusPageUrl !== undefined ? { statusPageUrl: lc.statusPageUrl } : {}
6015
6048
  };
6016
6049
  }
6017
6050
  function emit4(out, asJson, io) {
@@ -6019,6 +6052,9 @@ function emit4(out, asJson, io) {
6019
6052
  io.out(JSON.stringify(out, null, 2));
6020
6053
  return;
6021
6054
  }
6055
+ if (out.statusPageUrl !== undefined) {
6056
+ io.out(`Status page: ${out.statusPageUrl}`);
6057
+ }
6022
6058
  switch (out.state) {
6023
6059
  case "provisioning":
6024
6060
  io.out(`${out.slug}: provisioning (stage: ${out.stage ?? "starting"})` + (out.submissionId ? ` — submission ${out.submissionId}` : ""));
@@ -6343,7 +6379,7 @@ function readLocalEntrypoint(cwd) {
6343
6379
  for (const rel of ["index.html", join9("static", "index.html")]) {
6344
6380
  const p = join9(cwd, rel);
6345
6381
  if (existsSync4(p))
6346
- return new Uint8Array(readFileSync8(p));
6382
+ return new Uint8Array(readFileSync9(p));
6347
6383
  }
6348
6384
  return null;
6349
6385
  }
@@ -6577,7 +6613,7 @@ async function verifyFirstDeployServed(args, liveUrl, io, deps) {
6577
6613
 
6578
6614
  // src/commands/deploy.ts
6579
6615
  import { parse as parseYaml6 } from "yaml";
6580
- import { readFileSync as readFileSync10 } from "node:fs";
6616
+ import { readFileSync as readFileSync11 } from "node:fs";
6581
6617
 
6582
6618
  // src/deploy/git-files.ts
6583
6619
  import { spawn as spawn3 } from "node:child_process";
@@ -6834,6 +6870,7 @@ function parseDeployFlags(args) {
6834
6870
  let rebaseOntoHead = false;
6835
6871
  let overrideStale = null;
6836
6872
  let allowUnguarded = false;
6873
+ let noOpen = false;
6837
6874
  let i = 0;
6838
6875
  while (i < args.length) {
6839
6876
  const a = args[i] ?? "";
@@ -7038,6 +7075,11 @@ function parseDeployFlags(args) {
7038
7075
  i += 1;
7039
7076
  continue;
7040
7077
  }
7078
+ if (a === "--no-open") {
7079
+ noOpen = true;
7080
+ i += 1;
7081
+ continue;
7082
+ }
7041
7083
  if (a.startsWith("--verify-timeout=")) {
7042
7084
  const v = a.slice("--verify-timeout=".length);
7043
7085
  const n = Number(v);
@@ -7058,6 +7100,9 @@ function parseDeployFlags(args) {
7058
7100
  if (rebaseOntoHead && overrideStale !== null) {
7059
7101
  return "--rebase-onto-head and --override-stale are mutually exclusive — re-stamp to head, or override in place, not both";
7060
7102
  }
7103
+ if (noOpen && mode !== "content" && mode !== "apply") {
7104
+ return "--no-open is only valid for a content deploy or --apply";
7105
+ }
7061
7106
  if (mode === "dry-run") {
7062
7107
  if (slug !== null)
7063
7108
  return "--dry-run does not accept --slug (slug is read from launchpad.yaml)";
@@ -7114,7 +7159,8 @@ function parseDeployFlags(args) {
7114
7159
  timeoutMinutes
7115
7160
  },
7116
7161
  message: null,
7117
- timeoutSeconds: null
7162
+ timeoutSeconds: null,
7163
+ ...noOpen ? { noOpen: true } : {}
7118
7164
  };
7119
7165
  }
7120
7166
  if (resumePrNumber !== null) {
@@ -7155,7 +7201,8 @@ function parseDeployFlags(args) {
7155
7201
  allowUnguarded
7156
7202
  },
7157
7203
  message,
7158
- timeoutSeconds
7204
+ timeoutSeconds,
7205
+ ...noOpen ? { noOpen: true } : {}
7159
7206
  };
7160
7207
  }
7161
7208
  if (mode === "resume" || mode === "abandon") {
@@ -7209,12 +7256,12 @@ import { existsSync as existsSync6, rmSync } from "node:fs";
7209
7256
  import { resolve as resolvePath, join as join10 } from "node:path";
7210
7257
 
7211
7258
  // src/manifest/load.ts
7212
- import { readFileSync as readFileSync9 } from "node:fs";
7259
+ import { readFileSync as readFileSync10 } from "node:fs";
7213
7260
  import { parse as parseYaml5, YAMLParseError } from "yaml";
7214
7261
  function loadManifest(path8) {
7215
7262
  let raw;
7216
7263
  try {
7217
- raw = readFileSync9(path8, "utf8");
7264
+ raw = readFileSync10(path8, "utf8");
7218
7265
  } catch (err) {
7219
7266
  const e = err;
7220
7267
  if (e.code === "ENOENT") {
@@ -7249,6 +7296,38 @@ function formatIssues(error) {
7249
7296
  }));
7250
7297
  }
7251
7298
 
7299
+ // src/status-page-output.ts
7300
+ async function surfaceStatusPageUrl(url, io, options) {
7301
+ if (typeof url !== "string" || url.length === 0)
7302
+ return;
7303
+ io.out(`Status page: ${url}`);
7304
+ const env = options.env ?? process.env;
7305
+ const isTty = options.isTty ?? process.stdout.isTTY === true;
7306
+ if (options.noOpen || !options.autoOpenEnabled || !isTty || env.CI !== undefined) {
7307
+ return;
7308
+ }
7309
+ if (!isAllowedStatusPageUrl(url, options.allowedHost)) {
7310
+ io.err("warning: refused to open an untrusted status page URL");
7311
+ return;
7312
+ }
7313
+ try {
7314
+ await (options.open ?? openBrowser)(url);
7315
+ } catch (error) {
7316
+ io.err(`warning: couldn't open the status page automatically: ${describe13(error)}`);
7317
+ }
7318
+ }
7319
+ function isAllowedStatusPageUrl(url, allowedHost) {
7320
+ try {
7321
+ const parsed = new URL(url);
7322
+ return parsed.protocol === "https:" && (allowedHost === undefined || parsed.hostname === allowedHost);
7323
+ } catch {
7324
+ return false;
7325
+ }
7326
+ }
7327
+ function describe13(error) {
7328
+ return error instanceof Error ? error.message : String(error);
7329
+ }
7330
+
7252
7331
  // src/deploy/apply.ts
7253
7332
  var DEFAULT_TIMEOUT_MINUTES = 90;
7254
7333
  var DEFAULT_POLL_INTERVAL_SECONDS = 10;
@@ -7383,6 +7462,11 @@ async function runDeployApply(opts, io, deps = {}) {
7383
7462
  io.err(`launchpad deploy --apply: protocol failure from /apps/${slug}/manifest/apply: ` + `requested ${manifestSha}, received ${String(apply.manifestSha)}`);
7384
7463
  return 2;
7385
7464
  }
7465
+ await surfaceStatusPageUrl(apply.statusPageUrl, io, {
7466
+ noOpen: opts.noOpen === true,
7467
+ autoOpenEnabled: cfg.statusPageAutoOpen,
7468
+ allowedHost: cfg.statusPageHost
7469
+ });
7386
7470
  io.out("");
7387
7471
  io.out(`PR opened: ${apply.prUrl}`);
7388
7472
  io.out(`Branch: ${apply.branch}`);
@@ -7457,7 +7541,7 @@ async function pollUntilApplied(args) {
7457
7541
  breakDots();
7458
7542
  return { kind: "failed", reason: e.message };
7459
7543
  }
7460
- args.io.err(`! apply-status fetch failed (will retry): ${describe13(e)}`);
7544
+ args.io.err(`! apply-status fetch failed (will retry): ${describe14(e)}`);
7461
7545
  await sleep(args.pollIntervalSec * 1000);
7462
7546
  continue;
7463
7547
  }
@@ -7504,7 +7588,7 @@ async function pollUntilApplied(args) {
7504
7588
  path: `/apps/${args.slug}/manifest/state`
7505
7589
  }, args.fetcher);
7506
7590
  } catch (e) {
7507
- args.io.err(`! state fetch failed (will retry): ${describe13(e)}`);
7591
+ args.io.err(`! state fetch failed (will retry): ${describe14(e)}`);
7508
7592
  await sleep(args.pollIntervalSec * 1000);
7509
7593
  continue;
7510
7594
  }
@@ -7578,7 +7662,7 @@ function deletePinIfPresent(cfg, slug, io) {
7578
7662
  rmSync(pinPath, { force: true });
7579
7663
  io.out(` (cleaned up obsolete pin file ${pinPath})`);
7580
7664
  } catch (e) {
7581
- io.err(`! warning: failed to delete obsolete pin file ${pinPath}: ${describe13(e)}`);
7665
+ io.err(`! warning: failed to delete obsolete pin file ${pinPath}: ${describe14(e)}`);
7582
7666
  }
7583
7667
  }
7584
7668
  async function loadSlugForResume(opts, io) {
@@ -7602,7 +7686,7 @@ async function defaultPrompt(question) {
7602
7686
  rl.close();
7603
7687
  }
7604
7688
  }
7605
- function describe13(e) {
7689
+ function describe14(e) {
7606
7690
  return e instanceof Error ? e.message : String(e);
7607
7691
  }
7608
7692
  function mapHttpError(e, slug, io) {
@@ -7626,7 +7710,7 @@ function mapHttpError(e, slug, io) {
7626
7710
  io.err(`launchpad deploy --apply: ${e.message}`);
7627
7711
  return 1;
7628
7712
  }
7629
- io.err(`launchpad deploy --apply failed: ${describe13(e)}`);
7713
+ io.err(`launchpad deploy --apply failed: ${describe14(e)}`);
7630
7714
  return 2;
7631
7715
  }
7632
7716
  function renderManifestError(loaded, io) {
@@ -7669,7 +7753,7 @@ function renderAccessDriftSummary(slug, body) {
7669
7753
  return lines;
7670
7754
  }
7671
7755
  async function handleAccessDrift(params) {
7672
- const { slug, manifestPath, body, io, deps, outcome } = params;
7756
+ const { slug, manifestPath, body, io, deps, noOpen, outcome } = params;
7673
7757
  if (outcome === undefined) {
7674
7758
  for (const line of renderAccessDriftSummary(slug, body))
7675
7759
  io.err(line);
@@ -7685,6 +7769,7 @@ async function handleAccessDrift(params) {
7685
7769
  platformRepo: null,
7686
7770
  rePin: false,
7687
7771
  yes: true,
7772
+ noOpen,
7688
7773
  ...outcome === undefined ? {} : { atSha: outcome.manifest_sha },
7689
7774
  ...outcome?.pr === undefined ? {} : {
7690
7775
  preopenedPr: {
@@ -7725,7 +7810,7 @@ async function runDeployDryRun(opts, io, deps = {}) {
7725
7810
  try {
7726
7811
  manifestSha = (deps.gitHeadSha ?? defaultGitHeadSha)(process.cwd());
7727
7812
  } catch (e) {
7728
- const msg = `failed to resolve git HEAD in ${process.cwd()}: ${describe14(e)}`;
7813
+ const msg = `failed to resolve git HEAD in ${process.cwd()}: ${describe15(e)}`;
7729
7814
  if (opts.json) {
7730
7815
  io.out(JSON.stringify({ ok: false, kind: "git-error", message: msg }));
7731
7816
  } else {
@@ -7774,7 +7859,7 @@ function defaultGitHeadSha(cwd) {
7774
7859
  });
7775
7860
  return out.trim();
7776
7861
  }
7777
- function describe14(e) {
7862
+ function describe15(e) {
7778
7863
  return e instanceof Error ? e.message : String(e);
7779
7864
  }
7780
7865
  function mapHttpError2(e, slug, json, io) {
@@ -8086,7 +8171,7 @@ function validateContainerFilesystem(manifestPath, capability) {
8086
8171
  try {
8087
8172
  stat = lstatSync3(current);
8088
8173
  } catch (error) {
8089
- return failure("container_image_unreadable", relative3(root, current), `cannot inspect container image path: ${describe15(error)}`);
8174
+ return failure("container_image_unreadable", relative3(root, current), `cannot inspect container image path: ${describe16(error)}`);
8090
8175
  }
8091
8176
  if (stat.isSymbolicLink()) {
8092
8177
  return failure("container_image_symlink", relative3(root, current), "container image path must not contain symbolic links");
@@ -8096,7 +8181,7 @@ function validateContainerFilesystem(manifestPath, capability) {
8096
8181
  try {
8097
8182
  finalPath = realpathSync(current);
8098
8183
  } catch (error) {
8099
- return failure("container_image_unreadable", relative3(root, current), `cannot resolve container image path: ${describe15(error)}`);
8184
+ return failure("container_image_unreadable", relative3(root, current), `cannot resolve container image path: ${describe16(error)}`);
8100
8185
  }
8101
8186
  const rootPrefix = resolve9(root) + sep2;
8102
8187
  if (!resolve9(finalPath).startsWith(rootPrefix)) {
@@ -8110,7 +8195,7 @@ function validateContainerFilesystem(manifestPath, capability) {
8110
8195
  function failure(reason, path8, message) {
8111
8196
  return { kind: "error", reason, path: path8, message };
8112
8197
  }
8113
- function describe15(error) {
8198
+ function describe16(error) {
8114
8199
  return error instanceof Error ? error.message : String(error);
8115
8200
  }
8116
8201
 
@@ -8179,7 +8264,8 @@ async function runDeploy(args, io) {
8179
8264
  yes: flags.mode.yes,
8180
8265
  ...flags.mode.atSha !== null ? { atSha: flags.mode.atSha } : {},
8181
8266
  ...flags.mode.resumePrNumber !== null ? { resumePrNumber: flags.mode.resumePrNumber } : {},
8182
- ...flags.mode.timeoutMinutes !== null ? { timeoutMinutes: flags.mode.timeoutMinutes } : {}
8267
+ ...flags.mode.timeoutMinutes !== null ? { timeoutMinutes: flags.mode.timeoutMinutes } : {},
8268
+ noOpen: flags.noOpen === true
8183
8269
  }, io);
8184
8270
  }
8185
8271
  if (flags.mode.kind === "abandon") {
@@ -8215,7 +8301,8 @@ async function runDeploy(args, io) {
8215
8301
  allowStale: contentMode.allowStale,
8216
8302
  rebaseOntoHead: contentMode.rebaseOntoHead,
8217
8303
  overrideStale: contentMode.overrideStale,
8218
- allowUnguarded: contentMode.allowUnguarded
8304
+ allowUnguarded: contentMode.allowUnguarded,
8305
+ noOpen: flags.noOpen === true
8219
8306
  });
8220
8307
  }
8221
8308
  if (flags.mode.kind === "content" && flags.mode.allowUnguarded) {
@@ -8256,7 +8343,7 @@ async function runDeploy(args, io) {
8256
8343
  const contentManifestPath = path8.join(process.cwd(), "launchpad.yaml");
8257
8344
  if (existsSync7(contentManifestPath)) {
8258
8345
  try {
8259
- contentManifestYaml = readFileSync10(contentManifestPath, "utf8");
8346
+ contentManifestYaml = readFileSync11(contentManifestPath, "utf8");
8260
8347
  } catch {
8261
8348
  contentManifestYaml = null;
8262
8349
  }
@@ -8289,6 +8376,11 @@ async function runDeploy(args, io) {
8289
8376
  headers,
8290
8377
  jsonBody: undefined
8291
8378
  }, makeBytesFetcher(packed.bytes, `/apps/${slug}/deploy-pr`));
8379
+ await surfaceStatusPageUrl(response.statusPageUrl, io, {
8380
+ noOpen: flags.noOpen === true,
8381
+ autoOpenEnabled: cfg.statusPageAutoOpen,
8382
+ allowedHost: cfg.statusPageHost
8383
+ });
8292
8384
  io.out("");
8293
8385
  io.out(`PR opened: ${response.reviewUrl}`);
8294
8386
  io.out(`Branch: ${response.branch}`);
@@ -8334,12 +8426,12 @@ async function runDeploy(args, io) {
8334
8426
  io.err(`launchpad deploy: ${e.message}`);
8335
8427
  return 1;
8336
8428
  }
8337
- io.err(`launchpad deploy failed: ${describe16(e)}`);
8429
+ io.err(`launchpad deploy failed: ${describe17(e)}`);
8338
8430
  return 1;
8339
8431
  }
8340
8432
  }
8341
8433
  function deployUsage() {
8342
- return `usage: launchpad deploy [--message <text>] [--slug <slug>] [--allow-unguarded]
8434
+ return `usage: launchpad deploy [--message <text>] [--slug <slug>] [--allow-unguarded] [--no-open]
8343
8435
  ` + ` (slug comes from launchpad.yaml at cwd when present — Model A —
8344
8436
  ` + " else from the current directory's `launchpad-app-<slug>` suffix)\n" + `
8345
8437
  ` + `provisioning modes:
@@ -8377,6 +8469,10 @@ function parseArgs3(args) {
8377
8469
  i += 2;
8378
8470
  continue;
8379
8471
  }
8472
+ if (a === "--no-open") {
8473
+ i += 1;
8474
+ continue;
8475
+ }
8380
8476
  return null;
8381
8477
  }
8382
8478
  return { slug, message };
@@ -8405,7 +8501,7 @@ function formatBytes2(n) {
8405
8501
  return `${(n / 1024).toFixed(1)}KB`;
8406
8502
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
8407
8503
  }
8408
- function describe16(e) {
8504
+ function describe17(e) {
8409
8505
  return e instanceof Error ? e.message : String(e);
8410
8506
  }
8411
8507
  function surfaceDeployExtras(body, io, slug, allowStale = false) {
@@ -8491,7 +8587,7 @@ function withTimeout(p, ms) {
8491
8587
  async function surfaceManifestDiff(cfg, slug, manifestPath, io) {
8492
8588
  let localYaml;
8493
8589
  try {
8494
- localYaml = readFileSync10(manifestPath, "utf8");
8590
+ localYaml = readFileSync11(manifestPath, "utf8");
8495
8591
  } catch {
8496
8592
  return;
8497
8593
  }
@@ -8528,7 +8624,7 @@ async function advanceBaseShaMarker(cwd, sha, io) {
8528
8624
  return;
8529
8625
  await writeBaseShaMarker(cwd, sha);
8530
8626
  } catch (e) {
8531
- io.err(`warning: couldn't update the local base-sha marker (${describe16(e)}); ` + `a future deploy may re-warn about staleness. Harmless — your deploy landed.`);
8627
+ io.err(`warning: couldn't update the local base-sha marker (${describe17(e)}); ` + `a future deploy may re-warn about staleness. Harmless — your deploy landed.`);
8532
8628
  }
8533
8629
  }
8534
8630
  async function runModelADeploy(args) {
@@ -8536,7 +8632,7 @@ async function runModelADeploy(args) {
8536
8632
  let slug;
8537
8633
  let appType = "static";
8538
8634
  try {
8539
- const manifestObj = parseYaml6(readFileSync10(manifestPath, "utf8"));
8635
+ const manifestObj = parseYaml6(readFileSync11(manifestPath, "utf8"));
8540
8636
  const metaSlug = resolveManifestSlug(manifestObj);
8541
8637
  if (metaSlug === null) {
8542
8638
  io.err(`launchpad deploy: launchpad.yaml is missing metadata.slug (v2) / metadata.name (v1). ` + `Run \`launchpad init\` again to regenerate the manifest.`);
@@ -8566,7 +8662,7 @@ async function runModelADeploy(args) {
8566
8662
  }
8567
8663
  }
8568
8664
  } catch (e) {
8569
- io.err(`launchpad deploy: failed to read ${manifestPath}: ${describe16(e)}`);
8665
+ io.err(`launchpad deploy: failed to read ${manifestPath}: ${describe17(e)}`);
8570
8666
  return 1;
8571
8667
  }
8572
8668
  const noVerify = args.argv.includes("--no-verify");
@@ -8596,7 +8692,7 @@ async function runModelADeploy(args) {
8596
8692
  try {
8597
8693
  cfg = loadConfig();
8598
8694
  } catch (e) {
8599
- io.err(`launchpad deploy: ${describe16(e)}`);
8695
+ io.err(`launchpad deploy: ${describe17(e)}`);
8600
8696
  return 1;
8601
8697
  }
8602
8698
  try {
@@ -8646,7 +8742,7 @@ async function runModelADeploy(args) {
8646
8742
  io.err(" run `launchpad login` to refresh your session.");
8647
8743
  return 1;
8648
8744
  }
8649
- io.err(`launchpad deploy: unexpected error: ${describe16(e)}`);
8745
+ io.err(`launchpad deploy: unexpected error: ${describe17(e)}`);
8650
8746
  return 1;
8651
8747
  }
8652
8748
  switch (result.kind) {
@@ -8667,6 +8763,11 @@ async function runModelADeploy(args) {
8667
8763
  return 1;
8668
8764
  case "upload-error": {
8669
8765
  const body = result.body;
8766
+ await surfaceStatusPageUrl(body.statusPageUrl, io, {
8767
+ noOpen: args.noOpen,
8768
+ autoOpenEnabled: cfg.statusPageAutoOpen,
8769
+ allowedHost: cfg.statusPageHost
8770
+ });
8670
8771
  const errorCode = body.error;
8671
8772
  if (result.status === 426 && typeof body.minimum === "string" && typeof body.yours === "string") {
8672
8773
  io.err(upgradeRequiredMessage(body.minimum, body.yours));
@@ -8703,6 +8804,7 @@ async function runModelADeploy(args) {
8703
8804
  manifestPath,
8704
8805
  body,
8705
8806
  io,
8807
+ noOpen: args.noOpen,
8706
8808
  deps: { runApply: runDeployApply }
8707
8809
  });
8708
8810
  }
@@ -8720,6 +8822,7 @@ async function runModelADeploy(args) {
8720
8822
  body,
8721
8823
  outcome,
8722
8824
  io,
8825
+ noOpen: args.noOpen,
8723
8826
  deps: { runApply: runDeployApply }
8724
8827
  });
8725
8828
  if (accessExit !== 0)
@@ -8763,6 +8866,11 @@ async function runModelADeploy(args) {
8763
8866
  io.err(`warning: ${w}`);
8764
8867
  }
8765
8868
  const success = result.result;
8869
+ await surfaceStatusPageUrl(success.statusPageUrl, io, {
8870
+ noOpen: args.noOpen,
8871
+ autoOpenEnabled: cfg.statusPageAutoOpen,
8872
+ allowedHost: cfg.statusPageHost
8873
+ });
8766
8874
  if ("outcome" in success) {
8767
8875
  io.out("Nothing to deploy — your app is already live at this content.");
8768
8876
  if (typeof success.head_sha === "string") {
@@ -8807,6 +8915,7 @@ async function runModelADeploy(args) {
8807
8915
  body: {},
8808
8916
  outcome: success.reconcile,
8809
8917
  io,
8918
+ noOpen: args.noOpen,
8810
8919
  deps: { runApply: runDeployApply }
8811
8920
  });
8812
8921
  if (accessExit !== 0)
@@ -8858,7 +8967,7 @@ function pendingReconcileOutcome(body) {
8858
8967
  }
8859
8968
 
8860
8969
  // src/commands/redeploy.ts
8861
- import { existsSync as existsSync8, readFileSync as readFileSync11 } from "node:fs";
8970
+ import { existsSync as existsSync8, readFileSync as readFileSync12 } from "node:fs";
8862
8971
  import { resolve as resolvePath2 } from "node:path";
8863
8972
  import { parse as parseYaml7 } from "yaml";
8864
8973
  var SLUG_RE6 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
@@ -8939,7 +9048,7 @@ function readLocalAppType(cwd, file) {
8939
9048
  if (!existsSync8(manifestPath))
8940
9049
  return null;
8941
9050
  try {
8942
- const obj = parseYaml7(readFileSync11(manifestPath, "utf8"));
9051
+ const obj = parseYaml7(readFileSync12(manifestPath, "utf8"));
8943
9052
  return coerceAppType(obj?.deployment?.type);
8944
9053
  } catch {
8945
9054
  return null;
@@ -9121,7 +9230,7 @@ async function runEnvvars(args, io) {
9121
9230
  io.err(`launchpad envvars: ${e.message}`);
9122
9231
  return 1;
9123
9232
  }
9124
- io.err(`launchpad envvars failed: ${describe17(e)}`);
9233
+ io.err(`launchpad envvars failed: ${describe18(e)}`);
9125
9234
  return 1;
9126
9235
  }
9127
9236
  }
@@ -9218,12 +9327,12 @@ function renderList(envVars, io) {
9218
9327
  io.out(fmt(row));
9219
9328
  }
9220
9329
  }
9221
- function describe17(e) {
9330
+ function describe18(e) {
9222
9331
  return e instanceof Error ? e.message : String(e);
9223
9332
  }
9224
9333
 
9225
9334
  // src/commands/generate.ts
9226
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
9335
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync13, writeFileSync as writeFileSync2 } from "node:fs";
9227
9336
  import { dirname as dirname7, resolve as resolve10, relative as relative4 } from "node:path";
9228
9337
  var generateCommand = {
9229
9338
  name: "generate",
@@ -9328,7 +9437,7 @@ function applyOne(artefact, path10, out, force) {
9328
9437
  }
9329
9438
  function readIfExists(path10) {
9330
9439
  try {
9331
- return { kind: "ok", content: readFileSync12(path10, "utf8") };
9440
+ return { kind: "ok", content: readFileSync13(path10, "utf8") };
9332
9441
  } catch (err) {
9333
9442
  const e = err;
9334
9443
  if (e.code === "ENOENT")
@@ -9552,13 +9661,13 @@ function decodeJwtPayload(token) {
9552
9661
  try {
9553
9662
  json = Buffer.from(b64UrlToB64(payload), "base64").toString("utf8");
9554
9663
  } catch (e) {
9555
- throw new JwtParseError(`could not base64-decode JWT payload: ${describe18(e)}`);
9664
+ throw new JwtParseError(`could not base64-decode JWT payload: ${describe19(e)}`);
9556
9665
  }
9557
9666
  let parsed;
9558
9667
  try {
9559
9668
  parsed = JSON.parse(json);
9560
9669
  } catch (e) {
9561
- throw new JwtParseError(`JWT payload is not JSON: ${describe18(e)}`);
9670
+ throw new JwtParseError(`JWT payload is not JSON: ${describe19(e)}`);
9562
9671
  }
9563
9672
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
9564
9673
  throw new JwtParseError(`JWT payload is not an object`);
@@ -9569,7 +9678,7 @@ function b64UrlToB64(s) {
9569
9678
  const padded = s.padEnd(s.length + (4 - s.length % 4) % 4, "=");
9570
9679
  return padded.replace(/-/g, "+").replace(/_/g, "/");
9571
9680
  }
9572
- function describe18(e) {
9681
+ function describe19(e) {
9573
9682
  return e instanceof Error ? e.message : String(e);
9574
9683
  }
9575
9684
 
@@ -9596,7 +9705,7 @@ async function runGroupsWhoami(args, io) {
9596
9705
  try {
9597
9706
  payload = decodeJwtPayload(session.accessToken);
9598
9707
  } catch (e) {
9599
- const message = e instanceof JwtParseError ? e.message : describe19(e);
9708
+ const message = e instanceof JwtParseError ? e.message : describe20(e);
9600
9709
  if (json) {
9601
9710
  io.out(JSON.stringify({ ok: false, kind: "jwt-parse-error", message }));
9602
9711
  } else {
@@ -9677,7 +9786,7 @@ function parseFlags2(args) {
9677
9786
  }
9678
9787
  return { kind: "ok", json };
9679
9788
  }
9680
- function describe19(e) {
9789
+ function describe20(e) {
9681
9790
  return e instanceof Error ? e.message : String(e);
9682
9791
  }
9683
9792
 
@@ -9780,7 +9889,7 @@ async function loadGroups(io, refresh, json) {
9780
9889
  }
9781
9890
  return { kind: "error", code: 3 };
9782
9891
  }
9783
- renderFetchError(io, describe20(e), json);
9892
+ renderFetchError(io, describe21(e), json);
9784
9893
  return { kind: "error", code: 2 };
9785
9894
  }
9786
9895
  }
@@ -9817,7 +9926,7 @@ async function lookupTenantWide(key) {
9817
9926
  return { kind: "unauthenticated", message: e.message };
9818
9927
  if (e instanceof ForbiddenError)
9819
9928
  return { kind: "forbidden", message: e.message };
9820
- return { kind: "error", message: describe20(e) };
9929
+ return { kind: "error", message: describe21(e) };
9821
9930
  }
9822
9931
  }
9823
9932
  function renderLookupFailure(io, json, hit) {
@@ -10061,18 +10170,18 @@ function renderTable2(io, groups) {
10061
10170
  for (const r of rows)
10062
10171
  io.out(fmt(r));
10063
10172
  }
10064
- function describe20(e) {
10173
+ function describe21(e) {
10065
10174
  return e instanceof Error ? e.message : String(e);
10066
10175
  }
10067
10176
 
10068
10177
  // src/commands/init.ts
10069
10178
  import { createInterface } from "node:readline/promises";
10070
- import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
10179
+ import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync15, writeFileSync as writeFileSync3 } from "node:fs";
10071
10180
  import { dirname as dirname8, resolve as resolve11 } from "node:path";
10072
10181
  import { stringify as yamlStringify } from "yaml";
10073
10182
 
10074
10183
  // src/detect/index.ts
10075
- import { existsSync as existsSync9, readFileSync as readFileSync13, statSync } from "node:fs";
10184
+ import { existsSync as existsSync9, readFileSync as readFileSync14, statSync } from "node:fs";
10076
10185
  import { join as join13 } from "node:path";
10077
10186
  function detectAppShape(cwd) {
10078
10187
  const hasPackageJson = existsSync9(join13(cwd, "package.json"));
@@ -10177,7 +10286,7 @@ function detectBuildCommand(cwd, pm) {
10177
10286
  }
10178
10287
  let pkgJson;
10179
10288
  try {
10180
- pkgJson = JSON.parse(readFileSync13(pkgJsonPath, "utf8"));
10289
+ pkgJson = JSON.parse(readFileSync14(pkgJsonPath, "utf8"));
10181
10290
  } catch (e) {
10182
10291
  return {
10183
10292
  kind: "ambiguous",
@@ -10212,7 +10321,7 @@ var OUT_DIR_REGEX = /\bbuild\s*:\s*\{[^{}]*?\boutDir\s*:\s*['"]([^'"]+)['"]/s;
10212
10321
  function detectDestinationDir(cwd, vite) {
10213
10322
  let text;
10214
10323
  try {
10215
- text = readFileSync13(vite.path, "utf8");
10324
+ text = readFileSync14(vite.path, "utf8");
10216
10325
  } catch (e) {
10217
10326
  return {
10218
10327
  kind: "ambiguous",
@@ -10699,7 +10808,7 @@ function renderYaml(manifest) {
10699
10808
  function ensureGitignoreEntries(path10, entries) {
10700
10809
  let current = "";
10701
10810
  if (existsSync10(path10)) {
10702
- current = readFileSync14(path10, "utf8");
10811
+ current = readFileSync15(path10, "utf8");
10703
10812
  }
10704
10813
  const lines = current.split(/\r?\n/);
10705
10814
  const present = new Set(lines.map((l) => l.trim()));
@@ -10772,13 +10881,13 @@ async function runLogin(args, io, deps) {
10772
10881
  io.out(`Access token expires in ~${expiresIn}s; refreshes silently.`);
10773
10882
  return 0;
10774
10883
  } catch (e) {
10775
- io.err(`Gateway login failed: ${describe21(e)}`);
10884
+ io.err(`Gateway login failed: ${describe22(e)}`);
10776
10885
  io.err("Falling back to the legacy Cloudflare Access flow (DEPRECATED — this fallback will be removed when the dual-auth window closes).");
10777
10886
  io.err("");
10778
10887
  return await runLegacyLogin(io, cfg.botUrl, cfg.sessionPath, deps);
10779
10888
  }
10780
10889
  } catch (e) {
10781
- io.err(`launchpad login failed: ${describe21(e)}`);
10890
+ io.err(`launchpad login failed: ${describe22(e)}`);
10782
10891
  return 1;
10783
10892
  }
10784
10893
  }
@@ -10820,7 +10929,7 @@ function printLoginHelp(io) {
10820
10929
  io.out(" 0 signed in / session stored");
10821
10930
  io.out(" 1 login failed (network, browser, or gateway error)");
10822
10931
  }
10823
- function describe21(e) {
10932
+ function describe22(e) {
10824
10933
  return e instanceof Error ? e.message : String(e);
10825
10934
  }
10826
10935
 
@@ -10857,14 +10966,14 @@ async function runLogout(args, io, deps) {
10857
10966
  });
10858
10967
  io.out("Server-side session revoked.");
10859
10968
  } catch (e) {
10860
- io.err(`warning: could not revoke the session server-side (${describe22(e)}) — clearing the local session anyway. Any in-flight access token expires within 15 minutes.`);
10969
+ io.err(`warning: could not revoke the session server-side (${describe23(e)}) — clearing the local session anyway. Any in-flight access token expires within 15 minutes.`);
10861
10970
  }
10862
10971
  }
10863
10972
  const had = await clearSession(cfg.sessionPath);
10864
10973
  io.out(had ? `Logged out. Session cleared at ${cfg.sessionPath}` : "Already logged out.");
10865
10974
  return 0;
10866
10975
  } catch (e) {
10867
- io.err(`launchpad logout failed: ${describe22(e)}`);
10976
+ io.err(`launchpad logout failed: ${describe23(e)}`);
10868
10977
  return 1;
10869
10978
  }
10870
10979
  }
@@ -10884,7 +10993,7 @@ function printLogoutHelp(io) {
10884
10993
  io.out(" 0 logged out (or already logged out)");
10885
10994
  io.out(" 1 could not clear the local session (file IO / config error)");
10886
10995
  }
10887
- function describe22(e) {
10996
+ function describe23(e) {
10888
10997
  return e instanceof Error ? e.message : String(e);
10889
10998
  }
10890
10999
 
@@ -10954,7 +11063,7 @@ async function runLogs(args, io) {
10954
11063
  io.err(`launchpad logs: ${e.message}`);
10955
11064
  return 1;
10956
11065
  }
10957
- io.err(`launchpad logs failed: ${describe23(e)}`);
11066
+ io.err(`launchpad logs failed: ${describe24(e)}`);
10958
11067
  return 1;
10959
11068
  }
10960
11069
  }
@@ -11034,7 +11143,7 @@ function formatRelative2(iso) {
11034
11143
  return `${hr}h ago`;
11035
11144
  return `${Math.floor(hr / 24)}d ago`;
11036
11145
  }
11037
- function describe23(e) {
11146
+ function describe24(e) {
11038
11147
  return e instanceof Error ? e.message : String(e);
11039
11148
  }
11040
11149
 
@@ -11083,7 +11192,7 @@ async function runMerge(args, io) {
11083
11192
  try {
11084
11193
  body = await res.json();
11085
11194
  } catch (e) {
11086
- io.err(`launchpad merge: bot returned 2xx with malformed body: ${describe24(e)}`);
11195
+ io.err(`launchpad merge: bot returned 2xx with malformed body: ${describe25(e)}`);
11087
11196
  return 1;
11088
11197
  }
11089
11198
  io.out("");
@@ -11123,7 +11232,7 @@ async function runMerge(args, io) {
11123
11232
  io.err(`launchpad merge: ${e.message}`);
11124
11233
  return 1;
11125
11234
  }
11126
- io.err(`launchpad merge failed: ${describe24(e)}`);
11235
+ io.err(`launchpad merge failed: ${describe25(e)}`);
11127
11236
  return 1;
11128
11237
  }
11129
11238
  }
@@ -11222,7 +11331,7 @@ function renderBotError(status, env, io, prNumber = null) {
11222
11331
  io.err(`launchpad merge: bot returned ${status} ${code}${detail !== undefined ? `: ${detail}` : ""}`);
11223
11332
  }
11224
11333
  }
11225
- function describe24(e) {
11334
+ function describe25(e) {
11226
11335
  return e instanceof Error ? e.message : String(e);
11227
11336
  }
11228
11337
 
@@ -11485,7 +11594,7 @@ async function runDestroy(args, io, prompt, isTty) {
11485
11594
  io.err(`launchpad destroy: ${e.message}`);
11486
11595
  return 1;
11487
11596
  }
11488
- io.err(`launchpad destroy failed: ${describe25(e)}`);
11597
+ io.err(`launchpad destroy failed: ${describe26(e)}`);
11489
11598
  return 1;
11490
11599
  }
11491
11600
  }
@@ -11685,7 +11794,7 @@ function printUsage4(io) {
11685
11794
  ].join(`
11686
11795
  `));
11687
11796
  }
11688
- function describe25(e) {
11797
+ function describe26(e) {
11689
11798
  return e instanceof Error ? e.message : String(e);
11690
11799
  }
11691
11800
 
@@ -12255,7 +12364,7 @@ async function runPull(args, io) {
12255
12364
  io.err(`launchpad pull: ${e.message}`);
12256
12365
  return 1;
12257
12366
  }
12258
- io.err(`launchpad pull failed: ${describe26(e)}`);
12367
+ io.err(`launchpad pull failed: ${describe27(e)}`);
12259
12368
  return 1;
12260
12369
  }
12261
12370
  }
@@ -12363,7 +12472,7 @@ function printUsage5(io) {
12363
12472
  ].join(`
12364
12473
  `));
12365
12474
  }
12366
- function describe26(e) {
12475
+ function describe27(e) {
12367
12476
  return e instanceof Error ? e.message : String(e);
12368
12477
  }
12369
12478
 
@@ -12698,7 +12807,7 @@ async function runReview(args, io) {
12698
12807
  io.err(`launchpad review: ${e.message}`);
12699
12808
  return 1;
12700
12809
  }
12701
- io.err(`launchpad review failed: ${describe27(e)}`);
12810
+ io.err(`launchpad review failed: ${describe28(e)}`);
12702
12811
  return 1;
12703
12812
  }
12704
12813
  }
@@ -12776,12 +12885,12 @@ function severityRank(s) {
12776
12885
  return 3;
12777
12886
  }
12778
12887
  }
12779
- function describe27(e) {
12888
+ function describe28(e) {
12780
12889
  return e instanceof Error ? e.message : String(e);
12781
12890
  }
12782
12891
 
12783
12892
  // src/deploy/rollback.ts
12784
- import { existsSync as existsSync11, readFileSync as readFileSync15, writeFileSync as writeFileSync5 } from "node:fs";
12893
+ import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync5 } from "node:fs";
12785
12894
  import { resolve as resolvePath3 } from "node:path";
12786
12895
  import { createInterface as createInterface3 } from "node:readline/promises";
12787
12896
 
@@ -12836,7 +12945,7 @@ async function runRollback(opts, io, deps = {}) {
12836
12945
  cwd: process.cwd()
12837
12946
  });
12838
12947
  } catch (e) {
12839
- io.err(`launchpad rollback: git rev-parse failed to start: ${describe28(e)}`);
12948
+ io.err(`launchpad rollback: git rev-parse failed to start: ${describe29(e)}`);
12840
12949
  return 2;
12841
12950
  }
12842
12951
  if (revParse.exitCode !== 0) {
@@ -12851,7 +12960,7 @@ async function runRollback(opts, io, deps = {}) {
12851
12960
  try {
12852
12961
  show = await runner.run("git", ["show", `${verifiedSha}:${manifestRelpath}`], { cwd: process.cwd() });
12853
12962
  } catch (e) {
12854
- io.err(`launchpad rollback: git show failed to start: ${describe28(e)}`);
12963
+ io.err(`launchpad rollback: git show failed to start: ${describe29(e)}`);
12855
12964
  return 2;
12856
12965
  }
12857
12966
  if (show.exitCode !== 0) {
@@ -12885,7 +12994,7 @@ async function runRollback(opts, io, deps = {}) {
12885
12994
  try {
12886
12995
  writeFileSync5(manifestPath, historicalYaml, "utf8");
12887
12996
  } catch (e) {
12888
- io.err(`launchpad rollback: failed to write ${manifestPath}: ${describe28(e)}`);
12997
+ io.err(`launchpad rollback: failed to write ${manifestPath}: ${describe29(e)}`);
12889
12998
  return 2;
12890
12999
  }
12891
13000
  io.out(`Restored ${manifestPath} from ${verifiedSha.slice(0, 12)}.`);
@@ -12910,7 +13019,7 @@ function readCurrentManifest(path14) {
12910
13019
  return null;
12911
13020
  let raw;
12912
13021
  try {
12913
- raw = readFileSync15(path14, "utf8");
13022
+ raw = readFileSync16(path14, "utf8");
12914
13023
  } catch {
12915
13024
  return null;
12916
13025
  }
@@ -12967,7 +13076,7 @@ async function defaultPrompt4(question) {
12967
13076
  rl.close();
12968
13077
  }
12969
13078
  }
12970
- function describe28(e) {
13079
+ function describe29(e) {
12971
13080
  return e instanceof Error ? e.message : String(e);
12972
13081
  }
12973
13082
  function renderManifestError3(result, io) {
@@ -13066,7 +13175,7 @@ function parseArgs12(args) {
13066
13175
  }
13067
13176
 
13068
13177
  // src/secrets/push.ts
13069
- import { existsSync as existsSync12, readFileSync as readFileSync16 } from "node:fs";
13178
+ import { existsSync as existsSync12, readFileSync as readFileSync17 } from "node:fs";
13070
13179
  import { resolve as resolvePath4 } from "node:path";
13071
13180
  import { createInterface as createInterface4 } from "node:readline/promises";
13072
13181
  import { stdin as input2, stdout as output2 } from "node:process";
@@ -13130,9 +13239,9 @@ async function runSecretsPush(opts, io, deps = {}) {
13130
13239
  }
13131
13240
  let envText;
13132
13241
  try {
13133
- envText = readFileSync16(envPath, "utf8");
13242
+ envText = readFileSync17(envPath, "utf8");
13134
13243
  } catch (e) {
13135
- io.err(`launchpad secrets push: failed to read ${envPath}: ${describe29(e)}`);
13244
+ io.err(`launchpad secrets push: failed to read ${envPath}: ${describe30(e)}`);
13136
13245
  return 2;
13137
13246
  }
13138
13247
  const parsed = parseEnv(envText);
@@ -13316,10 +13425,10 @@ function mapPushError(e, slug, name, io) {
13316
13425
  io.err(`✗ secrets push ${name}: ${e.message}`);
13317
13426
  return 1;
13318
13427
  }
13319
- io.err(`✗ secrets push ${name} failed: ${describe29(e)}`);
13428
+ io.err(`✗ secrets push ${name} failed: ${describe30(e)}`);
13320
13429
  return 2;
13321
13430
  }
13322
- function describe29(e) {
13431
+ function describe30(e) {
13323
13432
  return e instanceof Error ? e.message : String(e);
13324
13433
  }
13325
13434
  function renderManifestError4(result, io) {
@@ -13455,7 +13564,7 @@ function renderManifestError5(result, io) {
13455
13564
  }
13456
13565
 
13457
13566
  // src/secrets/set.ts
13458
- import { existsSync as existsSync13, readFileSync as readFileSync17 } from "node:fs";
13567
+ import { existsSync as existsSync13, readFileSync as readFileSync18 } from "node:fs";
13459
13568
  import { resolve as resolvePath6 } from "node:path";
13460
13569
  import { parse as parseYaml8 } from "yaml";
13461
13570
  var CELL_LABEL2 = {
@@ -13472,7 +13581,7 @@ function loadSet(fleetFile, setName, io) {
13472
13581
  }
13473
13582
  let obj;
13474
13583
  try {
13475
- obj = parseYaml8(readFileSync17(path14, "utf8"));
13584
+ obj = parseYaml8(readFileSync18(path14, "utf8"));
13476
13585
  } catch (e) {
13477
13586
  io.err(`✗ ${path14}`);
13478
13587
  io.err(` YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
@@ -13564,7 +13673,7 @@ async function runSecretsPushSet(opts, io, deps = {}) {
13564
13673
  }
13565
13674
  let envText;
13566
13675
  try {
13567
- envText = readFileSync17(envPath, "utf8");
13676
+ envText = readFileSync18(envPath, "utf8");
13568
13677
  } catch (e) {
13569
13678
  io.err(`launchpad secrets push --set: failed to read ${envPath}: ${e instanceof Error ? e.message : String(e)}`);
13570
13679
  return 2;
@@ -14034,7 +14143,7 @@ import { promises as fs7, existsSync as existsSync15 } from "node:fs";
14034
14143
  // src/skills-bundle.ts
14035
14144
  import { homedir as homedir2 } from "node:os";
14036
14145
  import { join as join15 } from "node:path";
14037
- import { readFileSync as readFileSync18, readdirSync as readdirSync2 } from "node:fs";
14146
+ import { readFileSync as readFileSync19, readdirSync as readdirSync2 } from "node:fs";
14038
14147
  var SKILL_PREFIX = "launchpad-";
14039
14148
  function skillsTargetDir() {
14040
14149
  return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir2(), ".claude", "skills");
@@ -14055,7 +14164,7 @@ function readInstalledSkills() {
14055
14164
  }
14056
14165
  function readSkillVersion(path14) {
14057
14166
  try {
14058
- const text = readFileSync18(path14, "utf8");
14167
+ const text = readFileSync19(path14, "utf8");
14059
14168
  const fenceEnd = text.indexOf(`
14060
14169
  ---`, 4);
14061
14170
  const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
@@ -14109,7 +14218,7 @@ async function runSkills(args, io) {
14109
14218
  return 64;
14110
14219
  }
14111
14220
  } catch (e) {
14112
- io.err(`launchpad skills ${action}: ${describe30(e)}`);
14221
+ io.err(`launchpad skills ${action}: ${describe31(e)}`);
14113
14222
  return 1;
14114
14223
  }
14115
14224
  }
@@ -14239,7 +14348,7 @@ async function isDir(path14) {
14239
14348
  return false;
14240
14349
  }
14241
14350
  }
14242
- function describe30(e) {
14351
+ function describe31(e) {
14243
14352
  return e instanceof Error ? e.message : String(e);
14244
14353
  }
14245
14354
 
@@ -14249,7 +14358,7 @@ import { promisify } from "node:util";
14249
14358
  import { fileURLToPath as fileURLToPath2 } from "node:url";
14250
14359
  import { dirname as dirname11, resolve as resolve16, relative as relative6, isAbsolute as isAbsolute3, join as join17 } from "node:path";
14251
14360
  import { homedir as homedir3, tmpdir } from "node:os";
14252
- import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
14361
+ import { readFileSync as readFileSync20, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
14253
14362
 
14254
14363
  // src/commands/channel-auth.ts
14255
14364
  import { createServer as createServer2 } from "node:http";
@@ -14524,7 +14633,7 @@ function resolveLatestVersion() {
14524
14633
  }
14525
14634
  function detectInstallChannel() {
14526
14635
  try {
14527
- return readFileSync19(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
14636
+ return readFileSync20(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
14528
14637
  } catch {
14529
14638
  return "github";
14530
14639
  }
@@ -14732,7 +14841,7 @@ function printHelp5(io) {
14732
14841
  }
14733
14842
 
14734
14843
  // src/commands/validate.ts
14735
- import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
14844
+ import { existsSync as existsSync16, readFileSync as readFileSync21, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
14736
14845
  import { dirname as dirname12, resolve as resolve17 } from "node:path";
14737
14846
  var validateCommand = {
14738
14847
  name: "validate",
@@ -14780,7 +14889,7 @@ function checkBoundary(manifestPath, declared) {
14780
14889
  }
14781
14890
  let manifestYaml;
14782
14891
  try {
14783
- manifestYaml = readFileSync20(manifestPath, "utf8");
14892
+ manifestYaml = readFileSync21(manifestPath, "utf8");
14784
14893
  } catch {
14785
14894
  manifestYaml = null;
14786
14895
  }
@@ -14814,7 +14923,7 @@ function checkStaticServeDir(manifestPath, manifest) {
14814
14923
  try {
14815
14924
  isDir2 = statSync2(abs).isDirectory();
14816
14925
  } catch (e) {
14817
- return [`failed to inspect static served directory '${serveDir}/': ${describe31(e)}`];
14926
+ return [`failed to inspect static served directory '${serveDir}/': ${describe32(e)}`];
14818
14927
  }
14819
14928
  if (!isDir2) {
14820
14929
  return [`static app serves '${serveDir}/' but that path is not a directory.`];
@@ -14823,7 +14932,7 @@ function checkStaticServeDir(manifestPath, manifest) {
14823
14932
  try {
14824
14933
  entries = readdirSync3(abs);
14825
14934
  } catch (e) {
14826
- return [`failed to read static served directory '${serveDir}/': ${describe31(e)}`];
14935
+ return [`failed to read static served directory '${serveDir}/': ${describe32(e)}`];
14827
14936
  }
14828
14937
  if (entries.length === 0) {
14829
14938
  return [
@@ -14878,7 +14987,7 @@ async function checkGroup(allowedGroup, cfg) {
14878
14987
  if (e instanceof ForbiddenError) {
14879
14988
  return { kind: "forbidden", message: e.message };
14880
14989
  }
14881
- return { kind: "network", message: describe31(e) };
14990
+ return { kind: "network", message: describe32(e) };
14882
14991
  }
14883
14992
  }
14884
14993
  async function checkGroups(groups) {
@@ -15093,7 +15202,7 @@ function parseArgs13(args) {
15093
15202
  }
15094
15203
  return { kind: "ok", file, json, strictGroups };
15095
15204
  }
15096
- function describe31(e) {
15205
+ function describe32(e) {
15097
15206
  return e instanceof Error ? e.message : String(e);
15098
15207
  }
15099
15208
 
@@ -15146,7 +15255,7 @@ async function runWhoami(args, io) {
15146
15255
  io.err(e.message);
15147
15256
  return 1;
15148
15257
  }
15149
- io.err(`launchpad whoami failed: ${describe32(e)}`);
15258
+ io.err(`launchpad whoami failed: ${describe33(e)}`);
15150
15259
  return 1;
15151
15260
  }
15152
15261
  }
@@ -15176,11 +15285,11 @@ async function runShare(args, io) {
15176
15285
  io.err(e.message);
15177
15286
  return 1;
15178
15287
  }
15179
- io.err(`launchpad whoami --share failed: ${describe32(e)}`);
15288
+ io.err(`launchpad whoami --share failed: ${describe33(e)}`);
15180
15289
  return 1;
15181
15290
  }
15182
15291
  }
15183
- function describe32(e) {
15292
+ function describe33(e) {
15184
15293
  return e instanceof Error ? e.message : String(e);
15185
15294
  }
15186
15295
 
@@ -15346,7 +15455,7 @@ async function runEditor(action, args, io, deps) {
15346
15455
  io.err(`launchpad ${verb}: no app "${slug}" — check the slug with \`launchpad apps\`.`);
15347
15456
  return 1;
15348
15457
  }
15349
- io.err(`launchpad ${verb} failed: ${describe33(e)}`);
15458
+ io.err(`launchpad ${verb} failed: ${describe34(e)}`);
15350
15459
  return 1;
15351
15460
  }
15352
15461
  }
@@ -15380,7 +15489,7 @@ async function sendMutation(action, slug, target, version, cfg, deps) {
15380
15489
  nonThrowingStatuses: [409]
15381
15490
  });
15382
15491
  }
15383
- function describe33(e) {
15492
+ function describe34(e) {
15384
15493
  return e instanceof Error ? e.message : String(e);
15385
15494
  }
15386
15495
  var grantEditorCommand = makeEditorCommand("grant");
@@ -15586,7 +15695,7 @@ async function runOwner(action, args, io, deps) {
15586
15695
  io.err(`launchpad ${verb}: no app "${slug}" — check the slug with \`launchpad apps\`.`);
15587
15696
  return 1;
15588
15697
  }
15589
- io.err(`launchpad ${verb} failed: ${describe34(e)}`);
15698
+ io.err(`launchpad ${verb} failed: ${describe35(e)}`);
15590
15699
  return 1;
15591
15700
  }
15592
15701
  }
@@ -15620,7 +15729,7 @@ async function sendMutation2(action, slug, target, version, cfg, deps) {
15620
15729
  nonThrowingStatuses: [409]
15621
15730
  });
15622
15731
  }
15623
- function describe34(e) {
15732
+ function describe35(e) {
15624
15733
  return e instanceof Error ? e.message : String(e);
15625
15734
  }
15626
15735
  var addOwnerCommand = makeOwnerCommand("add");
@@ -15785,7 +15894,7 @@ import { join as join18 } from "node:path";
15785
15894
  import {
15786
15895
  existsSync as existsSync17,
15787
15896
  mkdirSync as mkdirSync4,
15788
- readFileSync as readFileSync21,
15897
+ readFileSync as readFileSync22,
15789
15898
  writeFileSync as writeFileSync8
15790
15899
  } from "node:fs";
15791
15900
  var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
@@ -15794,7 +15903,7 @@ var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
15794
15903
  var CACHE_FILE = join18(homedir4(), ".launchpad", "update-check.json");
15795
15904
  function readCache2() {
15796
15905
  try {
15797
- const raw = JSON.parse(readFileSync21(CACHE_FILE, "utf8"));
15906
+ const raw = JSON.parse(readFileSync22(CACHE_FILE, "utf8"));
15798
15907
  if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
15799
15908
  const latest = raw.latest;
15800
15909
  return {
@@ -15926,7 +16035,7 @@ var refreshUpdateCacheCommand = {
15926
16035
  import { spawn as spawn7 } from "node:child_process";
15927
16036
  import { homedir as homedir5 } from "node:os";
15928
16037
  import { join as join19 } from "node:path";
15929
- import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
16038
+ import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "node:fs";
15930
16039
  import { randomUUID } from "node:crypto";
15931
16040
  var INTERNAL_EMIT_VERB = "__emit-telemetry";
15932
16041
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -16002,7 +16111,7 @@ async function resolveIdentityReal() {
16002
16111
  }
16003
16112
  function getOrCreateDeviceId() {
16004
16113
  try {
16005
- const raw = JSON.parse(readFileSync22(DEVICE_ID_FILE, "utf8"));
16114
+ const raw = JSON.parse(readFileSync23(DEVICE_ID_FILE, "utf8"));
16006
16115
  const id2 = raw?.deviceId;
16007
16116
  if (typeof id2 === "string" && id2)
16008
16117
  return id2;
@@ -16153,7 +16262,7 @@ import { createInterface as createInterface7 } from "node:readline/promises";
16153
16262
  import { platform } from "node:os";
16154
16263
 
16155
16264
  // src/report/breadcrumb.ts
16156
- import { writeFileSync as writeFileSync10, readFileSync as readFileSync23, mkdirSync as mkdirSync6 } from "node:fs";
16265
+ import { writeFileSync as writeFileSync10, readFileSync as readFileSync24, mkdirSync as mkdirSync6 } from "node:fs";
16157
16266
  import { homedir as homedir6 } from "node:os";
16158
16267
  import { join as join20, dirname as dirname13 } from "node:path";
16159
16268
  function breadcrumbPath() {
@@ -16168,7 +16277,7 @@ function writeBreadcrumb(verb, exit) {
16168
16277
  }
16169
16278
  function readBreadcrumb() {
16170
16279
  try {
16171
- const raw = JSON.parse(readFileSync23(breadcrumbPath(), "utf8"));
16280
+ const raw = JSON.parse(readFileSync24(breadcrumbPath(), "utf8"));
16172
16281
  if (typeof raw !== "object" || raw === null)
16173
16282
  return null;
16174
16283
  const r = raw;