@launchsecure/launch-kit 0.0.42 → 0.0.44

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.
Files changed (77) hide show
  1. package/dist/chart-client/assets/index-DOKsFe5i.css +1 -0
  2. package/dist/chart-client/index.html +2 -2
  3. package/dist/client/assets/index-BqiDfvZi.js +294 -0
  4. package/dist/client/assets/index-Mewz-s77.css +32 -0
  5. package/dist/client/index.html +2 -2
  6. package/dist/council-client/assets/index-o_3y7Z0J.css +1 -0
  7. package/dist/council-client/index.html +2 -2
  8. package/dist/deck-client/assets/{_baseUniq-BrhDuG3C.js → _baseUniq-C6w7kg8x.js} +1 -1
  9. package/dist/deck-client/assets/{arc-DXtPHMhw.js → arc-Cx9pT3Nn.js} +1 -1
  10. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Cs9IdLQQ.js → architectureDiagram-Q4EWVU46-BITSj3vA.js} +1 -1
  11. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D--wpvoBAt.js → blockDiagram-DXYQGD6D-BehOFuwh.js} +1 -1
  12. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-BNIKE8-Z.js → c4Diagram-AHTNJAMY-BZTYM4na.js} +1 -1
  13. package/dist/deck-client/assets/channel-Cw2WDt9a.js +1 -0
  14. package/dist/deck-client/assets/{chunk-4BX2VUAB-Bz3EWmUo.js → chunk-4BX2VUAB-CCUx5CTd.js} +1 -1
  15. package/dist/deck-client/assets/{chunk-4TB4RGXK-D55BBvVZ.js → chunk-4TB4RGXK-UDZXXga6.js} +1 -1
  16. package/dist/deck-client/assets/{chunk-55IACEB6-BzjEcoTi.js → chunk-55IACEB6-CfcU6PIW.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-EDXVE4YY-CH1vs4Lu.js → chunk-EDXVE4YY-BK6F5Fof.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-FMBD7UC4-CVRnaGM0.js → chunk-FMBD7UC4-C-2idlFB.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-OYMX7WX6-BCTV_aEZ.js → chunk-OYMX7WX6-D6hBkYLP.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-QZHKN3VN-BqM8r4ee.js → chunk-QZHKN3VN-DixNpysA.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-YZCP3GAM-Rhbb691A.js → chunk-YZCP3GAM-Cd3pNBtQ.js} +1 -1
  22. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-JLUXVCUr.js +1 -0
  23. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-JLUXVCUr.js +1 -0
  24. package/dist/deck-client/assets/clone-H0XCnSb6.js +1 -0
  25. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-fWyBR9Nn.js → cose-bilkent-S5V4N54A-OF3JWdEt.js} +1 -1
  26. package/dist/deck-client/assets/{dagre-KV5264BT-Ds0Sqext.js → dagre-KV5264BT-Bqu-qcv4.js} +1 -1
  27. package/dist/deck-client/assets/{diagram-5BDNPKRD-BGz_X007.js → diagram-5BDNPKRD--0eHmUBS.js} +1 -1
  28. package/dist/deck-client/assets/{diagram-G4DWMVQ6-C78jeb-r.js → diagram-G4DWMVQ6-nss6oL20.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-MMDJMWI5-BdoHW8mF.js → diagram-MMDJMWI5-D_gSGnLR.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-TYMM5635-C8i9zc-U.js → diagram-TYMM5635-BIt-P6Pk.js} +1 -1
  31. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DplAZ-yG.js → erDiagram-SMLLAGMA-Bi-E4KQm.js} +1 -1
  32. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-F3N9yTAB.js → flowDiagram-DWJPFMVM-DMJCvLMA.js} +1 -1
  33. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DebdM4S5.js → ganttDiagram-T4ZO3ILL-C3xgEoPD.js} +1 -1
  34. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-htCk0oPz.js → gitGraphDiagram-UUTBAWPF-CD0BEGAW.js} +1 -1
  35. package/dist/deck-client/assets/{graph-Dtxc2PT4.js → graph-Dtsd9Jwe.js} +1 -1
  36. package/dist/deck-client/assets/index-C6YxyZay.css +1 -0
  37. package/dist/deck-client/assets/{index-CwAiam97.js → index-TFX8vtTG.js} +1 -1
  38. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-gTxegviJ.js → infoDiagram-42DDH7IO-7IcQYqe_.js} +1 -1
  39. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DYSEsfUK.js → ishikawaDiagram-UXIWVN3A-DsCEbx3u.js} +1 -1
  40. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DgVc1q4D.js → journeyDiagram-VCZTEJTY-1mP2JwCk.js} +1 -1
  41. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DcOf3i9N.js → kanban-definition-6JOO6SKY-vT0Xrqh9.js} +1 -1
  42. package/dist/deck-client/assets/{layout-CHP9HIw4.js → layout-Cw4rS2pn.js} +1 -1
  43. package/dist/deck-client/assets/{linear-XjVb7x4Q.js → linear-CzOjL-Ih.js} +1 -1
  44. package/dist/deck-client/assets/{mermaid.core-bouKOnsR.js → mermaid.core-DYi3A-qK.js} +4 -4
  45. package/dist/deck-client/assets/{min-Jl4GV9DB.js → min-DstloRoL.js} +1 -1
  46. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-FXXf4cJx.js → mindmap-definition-QFDTVHPH-D-cCX2d2.js} +1 -1
  47. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-Dl4plCTi.js → pieDiagram-DEJITSTG-BqW2NTmy.js} +1 -1
  48. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-BeOEeelg.js → quadrantDiagram-34T5L4WZ-DbJoWA8f.js} +1 -1
  49. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CBh1jchM.js → requirementDiagram-MS252O5E-DQrUiz_d.js} +1 -1
  50. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BK6gG-ub.js → sankeyDiagram-XADWPNL6-kB7PZc3g.js} +1 -1
  51. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-BtQwzsF5.js → sequenceDiagram-FGHM5R23-CpyVu1TN.js} +1 -1
  52. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX--kLQOi8R.js → stateDiagram-FHFEXIEX-CjqQcnty.js} +1 -1
  53. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-tfMSn8xx.js +1 -0
  54. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-CQ_-CLh7.js → timeline-definition-GMOUNBTQ-B2PAO9bk.js} +1 -1
  55. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-DBhKYAFT.js → vennDiagram-DHZGUBPP-C0G3ItCr.js} +1 -1
  56. package/dist/deck-client/assets/{wardley-RL74JXVD-DLKe29q9.js → wardley-RL74JXVD-B0TVaOmp.js} +1 -1
  57. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Brb8ezsV.js → wardleyDiagram-NUSXRM2D-B-qtbNZe.js} +1 -1
  58. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-LogZ0Az9.js → xychartDiagram-5P7HB3ND-41kcBoBE.js} +1 -1
  59. package/dist/deck-client/index.html +2 -2
  60. package/dist/server/cli.js +484 -77
  61. package/dist/server/init-entry.js +54 -23
  62. package/dist/server/launch-bot-entry.js +38 -4
  63. package/dist/server/radar-docker-init-entry.js +53 -22
  64. package/dist/server/rover-entry.js +6056 -5155
  65. package/package.json +1 -1
  66. package/dist/chart-client/assets/index-Dd6IotOZ.css +0 -1
  67. package/dist/client/assets/index-BoIjawzY.js +0 -294
  68. package/dist/client/assets/index-DE0uje6k.css +0 -32
  69. package/dist/council-client/assets/index-CGYusOCK.css +0 -1
  70. package/dist/deck-client/assets/channel-Bb5wIjTD.js +0 -1
  71. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CN-ZYMbT.js +0 -1
  72. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CN-ZYMbT.js +0 -1
  73. package/dist/deck-client/assets/clone-D6e7poKG.js +0 -1
  74. package/dist/deck-client/assets/index-evAPhGvM.css +0 -1
  75. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CHEPjgB0.js +0 -1
  76. /package/dist/chart-client/assets/{index-CrYM1-ac.js → index-DJQYgFcp.js} +0 -0
  77. /package/dist/council-client/assets/{index-DkTFX53U.js → index-Wn06apTg.js} +0 -0
@@ -519,11 +519,13 @@ function coerceEntry(raw, index) {
519
519
  if (r.args !== void 0 && (!Array.isArray(r.args) || r.args.some((a) => typeof a !== "string"))) {
520
520
  throw new Error(`[launch-kit-services] entry #${index}: args must be a string[]`);
521
521
  }
522
+ const skipSpawn = r.skipSpawn === true || r.bin === "";
522
523
  return {
523
524
  name: r.name,
524
525
  port: r.port,
525
526
  bin: r.bin,
526
- args: r.args ?? []
527
+ args: r.args ?? [],
528
+ skipSpawn
527
529
  };
528
530
  }
529
531
  function validate(services) {
@@ -547,6 +549,9 @@ function validate(services) {
547
549
  throw new Error(`[launch-kit-services] duplicate port ${s.port} (services must each listen on a unique port)`);
548
550
  }
549
551
  seenPorts.add(s.port);
552
+ if (!s.skipSpawn && s.bin === "") {
553
+ throw new Error(`[launch-kit-services] service "${s.name}" has an empty bin but is not ingress-only (skipSpawn) \u2014 nothing to spawn`);
554
+ }
550
555
  }
551
556
  return services;
552
557
  }
@@ -707,7 +712,7 @@ async function fetchConnectorToken(input, tunnelId) {
707
712
  async function setIngressConfig(input, tunnelId) {
708
713
  const ingress = input.services.map((s) => ({
709
714
  hostname: `${serviceLabel(s)}.${input.zone.name}`,
710
- service: `http://localhost:${s.port}`
715
+ service: `http://127.0.0.1:${s.port}`
711
716
  }));
712
717
  ingress.push({ service: "http_status:404" });
713
718
  const res = await cf({
@@ -1076,14 +1081,30 @@ async function setupFromCloud() {
1076
1081
  console.log(`[entrypoint] bundle from cloud: org=${orgSlug} project=${projectSlug} git=${process.env.GIT_USER_NAME} <${process.env.GIT_USER_EMAIL}> github=${bundle.githubTokenStatus.toLowerCase()} ${cfNote}`);
1077
1082
  return bundle;
1078
1083
  }
1084
+ function hasUsableCredentials(credsPath) {
1085
+ if (!(0, import_node_fs4.existsSync)(credsPath)) return false;
1086
+ try {
1087
+ const raw = (0, import_node_fs4.readFileSync)(credsPath, "utf8").trim();
1088
+ if (raw.length === 0) return false;
1089
+ const parsed = JSON.parse(raw);
1090
+ return parsed != null && typeof parsed === "object" && "claudeAiOauth" in parsed;
1091
+ } catch {
1092
+ return false;
1093
+ }
1094
+ }
1079
1095
  function setupClaudeCredentials() {
1080
1096
  const home = process.env.HOME ?? "/home/launchpod";
1081
1097
  const claudeDir = (0, import_node_path4.join)(home, ".claude");
1082
1098
  (0, import_node_fs4.mkdirSync)(claudeDir, { recursive: true });
1083
- const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
1084
1099
  const credsPath = (0, import_node_path4.join)(claudeDir, ".credentials.json");
1085
- (0, import_node_fs4.writeFileSync)(credsPath, decoded);
1086
- (0, import_node_fs4.chmodSync)(credsPath, 384);
1100
+ if (hasUsableCredentials(credsPath)) {
1101
+ console.log("[entrypoint] ~/.claude/.credentials.json already present \u2014 leaving Claude Code's own (refreshed) credentials intact");
1102
+ } else {
1103
+ const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
1104
+ (0, import_node_fs4.writeFileSync)(credsPath, decoded);
1105
+ (0, import_node_fs4.chmodSync)(credsPath, 384);
1106
+ console.log("[entrypoint] seeded ~/.claude/.credentials.json from CLAUDE_CREDENTIALS_B64");
1107
+ }
1087
1108
  const configPath = (0, import_node_path4.join)(home, ".claude.json");
1088
1109
  let cfg = {};
1089
1110
  if ((0, import_node_fs4.existsSync)(configPath)) {
@@ -1107,7 +1128,7 @@ function setupClaudeCredentials() {
1107
1128
  "launch-sequencer"
1108
1129
  ];
1109
1130
  const projects = cfg.projects ?? {};
1110
- const wsKey = "/workspace";
1131
+ const wsKey = POD_WS;
1111
1132
  const wsProject = projects[wsKey] ?? {};
1112
1133
  const existingEnabled = Array.isArray(wsProject.enabledMcpjsonServers) ? wsProject.enabledMcpjsonServers : [];
1113
1134
  const mergedEnabled = Array.from(/* @__PURE__ */ new Set([...existingEnabled, ...PREAPPROVED_MCPS]));
@@ -1126,7 +1147,7 @@ function setupGitAndGh() {
1126
1147
  function detectAndSetPreviewPort() {
1127
1148
  if (process.env.PREVIEW_PORT) return;
1128
1149
  try {
1129
- const pkgPath = "/workspace/package.json";
1150
+ const pkgPath = (0, import_node_path4.join)(POD_WS, "package.json");
1130
1151
  if (!(0, import_node_fs4.existsSync)(pkgPath)) return;
1131
1152
  const pkg = JSON.parse((0, import_node_fs4.readFileSync)(pkgPath, "utf-8"));
1132
1153
  const scripts = pkg.scripts ?? {};
@@ -1145,23 +1166,27 @@ function detectAndSetPreviewPort() {
1145
1166
  }
1146
1167
  }
1147
1168
  function initWorkspaceIfEmpty() {
1148
- process.chdir("/workspace");
1169
+ process.chdir(POD_WS);
1149
1170
  if ((0, import_node_fs4.existsSync)(".git")) {
1150
- console.log("[entrypoint] /workspace already initialized \u2014 skipping init");
1171
+ console.log(`[entrypoint] ${POD_WS} already initialized \u2014 skipping init`);
1151
1172
  return;
1152
1173
  }
1153
- console.log("[entrypoint] /workspace is empty \u2014 running launch-kit init");
1174
+ console.log(`[entrypoint] ${POD_WS} is empty \u2014 running launch-kit init`);
1154
1175
  const status = run2("launch-kit", [
1155
1176
  "init",
1156
1177
  `--token=${requireEnv("LS_PAT")}`,
1157
1178
  `--org=${requireEnv("LS_ORG_SLUG")}`,
1158
1179
  `--project=${requireEnv("LS_PROJECT_SLUG")}`,
1159
1180
  `--url=${process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app"}`,
1160
- `--dir=/workspace`
1181
+ `--dir=${POD_WS}`
1161
1182
  ]);
1162
1183
  if (status !== 0) fail2(`[entrypoint] launch-kit init failed (status ${status})`);
1163
1184
  }
1164
1185
  async function maybeProvisionIngress(bundle, services, projectSlug) {
1186
+ if (LOCAL_ONLY) {
1187
+ console.log("[entrypoint] LAUNCHKIT_LOCAL_ONLY=1 \u2014 skipping CF ingress; services run on localhost only");
1188
+ return null;
1189
+ }
1165
1190
  const token = bundle.cloudflareToken ?? null;
1166
1191
  const accountId = bundle.cloudflareAccountId ?? null;
1167
1192
  const zones = bundle.cloudflareZones ?? [];
@@ -1181,7 +1206,7 @@ async function maybeProvisionIngress(bundle, services, projectSlug) {
1181
1206
  } else {
1182
1207
  fail2(`[entrypoint] cloudflare token covers ${zones.length} zones (${zones.map((z) => z.name).join(", ")}) \u2014 set LAUNCHKIT_CF_BASE_DOMAIN to pick one.`);
1183
1208
  }
1184
- const stateFile = "/workspace/.launchpod/launch-kit-tunnel.json";
1209
+ const stateFile = (0, import_node_path4.join)(POD_WS, ".launchpod", "launch-kit-tunnel.json");
1185
1210
  const slugLabel = projectSlug.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1186
1211
  const DNS_LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
1187
1212
  for (const s of services) {
@@ -1247,7 +1272,7 @@ async function maybeProvisionAccess(bundle, ingress) {
1247
1272
  }
1248
1273
  const serverUrl = process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app";
1249
1274
  const pat = requireEnv("LS_PAT");
1250
- const stateFile = "/workspace/.launchpod/launch-kit-access.json";
1275
+ const stateFile = (0, import_node_path4.join)(POD_WS, ".launchpod", "launch-kit-access.json");
1251
1276
  const gatedHosts = services.filter((s) => !s.bypass).map((s) => s.hostname);
1252
1277
  const bypassed = services.filter((s) => s.bypass).map((s) => `${s.hostname}${s.path ?? ""}`);
1253
1278
  console.log(`[entrypoint] gating ${gatedHosts.join(", ")} behind CF Access (IdP: ${serverUrl})`);
@@ -1346,11 +1371,13 @@ function spawnServiceGroup(services) {
1346
1371
  }).finally(removeSignals);
1347
1372
  }
1348
1373
  async function main() {
1349
- const priorCrashes = readCrashState()?.count ?? 0;
1350
- if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
1351
- const bootAttempt = bumpCrashCount();
1352
- if (bootAttempt > 1) {
1353
- console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
1374
+ if (!NO_PARK) {
1375
+ const priorCrashes = readCrashState()?.count ?? 0;
1376
+ if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
1377
+ const bootAttempt = bumpCrashCount();
1378
+ if (bootAttempt > 1) {
1379
+ console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
1380
+ }
1354
1381
  }
1355
1382
  for (const k of REQUIRED_ENV) requireEnv(k);
1356
1383
  const bundle = await setupFromCloud();
@@ -1384,8 +1411,10 @@ async function main() {
1384
1411
  }
1385
1412
  }
1386
1413
  const stableTimer = setTimeout(() => {
1387
- clearCrashCount();
1388
- console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
1414
+ if (!NO_PARK) {
1415
+ clearCrashCount();
1416
+ console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
1417
+ }
1389
1418
  }, STABLE_AFTER_MS);
1390
1419
  stableTimer.unref?.();
1391
1420
  try {
@@ -1398,7 +1427,7 @@ async function main() {
1398
1427
  process.exit(1);
1399
1428
  }
1400
1429
  }
1401
- var import_node_child_process2, import_node_fs4, import_node_path4, REQUIRED_ENV, LAUNCHPOD_DIR, CRASH_STATE_FILE, MAX_BOOT_CRASHES, STABLE_AFTER_MS, GATED_SERVICES;
1430
+ var import_node_child_process2, import_node_fs4, import_node_path4, REQUIRED_ENV, POD_WS, NO_PARK, LOCAL_ONLY, LAUNCHPOD_DIR, CRASH_STATE_FILE, MAX_BOOT_CRASHES, STABLE_AFTER_MS, GATED_SERVICES;
1402
1431
  var init_radar_docker_init_entry = __esm({
1403
1432
  "src/server/radar-docker-init-entry.ts"() {
1404
1433
  "use strict";
@@ -1411,12 +1440,14 @@ var init_radar_docker_init_entry = __esm({
1411
1440
  init_cf_access();
1412
1441
  init_registration();
1413
1442
  REQUIRED_ENV = [
1414
- "CLAUDE_CREDENTIALS_B64",
1415
1443
  "LS_PAT",
1416
1444
  "LS_ORG_SLUG",
1417
1445
  "LS_PROJECT_SLUG"
1418
1446
  ];
1419
- LAUNCHPOD_DIR = "/workspace/.launchpod";
1447
+ POD_WS = process.env.LAUNCHPOD_WORKSPACE ?? "/workspace";
1448
+ NO_PARK = process.env.LAUNCHPOD_NO_PARK === "1";
1449
+ LOCAL_ONLY = process.env.LAUNCHKIT_LOCAL_ONLY === "1";
1450
+ LAUNCHPOD_DIR = (0, import_node_path4.join)(POD_WS, ".launchpod");
1420
1451
  CRASH_STATE_FILE = (0, import_node_path4.join)(LAUNCHPOD_DIR, ".boot-crash.json");
1421
1452
  MAX_BOOT_CRASHES = 5;
1422
1453
  STABLE_AFTER_MS = 3e4;
@@ -611,6 +611,14 @@ var require_claude_bridge = __commonJS({
611
611
  },
612
612
  onError = () => {
613
613
  },
614
+ // Fired (at most once per session) when Claude Code prints its
615
+ // authentication-failure banner — i.e. the credential it resolved was
616
+ // rejected by the Anthropic API. In a headless pod nobody is watching the
617
+ // pty, so this is the only signal the credential died; callers surface it
618
+ // (radar card + status banner) instead of failing silently. Detail is a
619
+ // short human string.
620
+ onAuthFailure = () => {
621
+ },
614
622
  cols = 80,
615
623
  rows = 24,
616
624
  // When true, spawn `claude --resume <sessionId>` instead of starting a
@@ -671,6 +679,7 @@ var require_claude_bridge = __commonJS({
671
679
  this.sessions.set(sessionId, session);
672
680
  let trustPromptHandled = false;
673
681
  let autoStartSent = !!initialPrompt;
682
+ let authFailureReported = false;
674
683
  let dataBuffer = "";
675
684
  claudeProcess.onData((data) => {
676
685
  if (process.env.DEBUG) {
@@ -685,6 +694,15 @@ var require_claude_bridge = __commonJS({
685
694
  console.log(`Sent Enter to accept trust prompt for session ${sessionId}`);
686
695
  }, 500);
687
696
  }
697
+ if (!authFailureReported && /Please run \/login|Invalid authentication credentials/i.test(dataBuffer)) {
698
+ authFailureReported = true;
699
+ console.error(`Claude session ${sessionId}: authentication failed (credential rejected by Anthropic API)`);
700
+ try {
701
+ onAuthFailure("Claude credential rejected (401) \u2014 pod could not authenticate to the Anthropic API.");
702
+ } catch (e) {
703
+ console.error(`onAuthFailure handler threw for session ${sessionId}:`, e);
704
+ }
705
+ }
688
706
  if (!autoStartSent && appendSystemPrompt && dataBuffer.includes("\u276F")) {
689
707
  autoStartSent = true;
690
708
  console.log(`Auto-starting agent in session ${sessionId}`);
@@ -3246,6 +3264,13 @@ var require_src = __commonJS({
3246
3264
  message: error.message
3247
3265
  });
3248
3266
  },
3267
+ onAuthFailure: (detail) => {
3268
+ this.broadcastToSession(sessionId, {
3269
+ type: "auth_failed",
3270
+ sessionId,
3271
+ message: detail
3272
+ });
3273
+ },
3249
3274
  ...options
3250
3275
  });
3251
3276
  session.active = true;
@@ -3717,13 +3742,22 @@ var require_src = __commonJS({
3717
3742
  return new TerminalHandler(options);
3718
3743
  }
3719
3744
  function createWebSocketHandler2(httpServer, handler2, wsPath = "/terminal/ws") {
3720
- const wss2 = new WebSocket.Server({
3721
- server: httpServer,
3722
- path: wsPath
3723
- });
3745
+ const wss2 = new WebSocket.Server({ noServer: true });
3724
3746
  wss2.on("connection", (ws, req) => {
3725
3747
  handler2.handleWebSocketConnection(ws, req);
3726
3748
  });
3749
+ httpServer.on("upgrade", (req, socket, head) => {
3750
+ let pathname;
3751
+ try {
3752
+ pathname = new URL(req.url || "/", "http://localhost").pathname;
3753
+ } catch {
3754
+ return;
3755
+ }
3756
+ if (pathname !== wsPath) return;
3757
+ wss2.handleUpgrade(req, socket, head, (ws) => {
3758
+ wss2.emit("connection", ws, req);
3759
+ });
3760
+ });
3727
3761
  return wss2;
3728
3762
  }
3729
3763
  function detectClaudeCli2() {
@@ -190,11 +190,13 @@ function coerceEntry(raw, index) {
190
190
  if (r.args !== void 0 && (!Array.isArray(r.args) || r.args.some((a) => typeof a !== "string"))) {
191
191
  throw new Error(`[launch-kit-services] entry #${index}: args must be a string[]`);
192
192
  }
193
+ const skipSpawn = r.skipSpawn === true || r.bin === "";
193
194
  return {
194
195
  name: r.name,
195
196
  port: r.port,
196
197
  bin: r.bin,
197
- args: r.args ?? []
198
+ args: r.args ?? [],
199
+ skipSpawn
198
200
  };
199
201
  }
200
202
  function validate(services) {
@@ -218,6 +220,9 @@ function validate(services) {
218
220
  throw new Error(`[launch-kit-services] duplicate port ${s.port} (services must each listen on a unique port)`);
219
221
  }
220
222
  seenPorts.add(s.port);
223
+ if (!s.skipSpawn && s.bin === "") {
224
+ throw new Error(`[launch-kit-services] service "${s.name}" has an empty bin but is not ingress-only (skipSpawn) \u2014 nothing to spawn`);
225
+ }
221
226
  }
222
227
  return services;
223
228
  }
@@ -363,7 +368,7 @@ async function fetchConnectorToken(input, tunnelId) {
363
368
  async function setIngressConfig(input, tunnelId) {
364
369
  const ingress = input.services.map((s) => ({
365
370
  hostname: `${serviceLabel(s)}.${input.zone.name}`,
366
- service: `http://localhost:${s.port}`
371
+ service: `http://127.0.0.1:${s.port}`
367
372
  }));
368
373
  ingress.push({ service: "http_status:404" });
369
374
  const res = await cf({
@@ -613,11 +618,13 @@ var RECEIVER_PATH = "/api/radar/ingest";
613
618
 
614
619
  // src/server/radar-docker-init-entry.ts
615
620
  var REQUIRED_ENV = [
616
- "CLAUDE_CREDENTIALS_B64",
617
621
  "LS_PAT",
618
622
  "LS_ORG_SLUG",
619
623
  "LS_PROJECT_SLUG"
620
624
  ];
625
+ var POD_WS = process.env.LAUNCHPOD_WORKSPACE ?? "/workspace";
626
+ var NO_PARK = process.env.LAUNCHPOD_NO_PARK === "1";
627
+ var LOCAL_ONLY = process.env.LAUNCHKIT_LOCAL_ONLY === "1";
621
628
  function fail2(message) {
622
629
  console.error(message);
623
630
  process.exit(1);
@@ -631,7 +638,7 @@ function run(cmd, args, stdio = "inherit") {
631
638
  const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio });
632
639
  return r.status ?? 1;
633
640
  }
634
- var LAUNCHPOD_DIR = "/workspace/.launchpod";
641
+ var LAUNCHPOD_DIR = (0, import_node_path4.join)(POD_WS, ".launchpod");
635
642
  var CRASH_STATE_FILE = (0, import_node_path4.join)(LAUNCHPOD_DIR, ".boot-crash.json");
636
643
  var MAX_BOOT_CRASHES = 5;
637
644
  var STABLE_AFTER_MS = 3e4;
@@ -714,14 +721,30 @@ async function setupFromCloud() {
714
721
  console.log(`[entrypoint] bundle from cloud: org=${orgSlug} project=${projectSlug} git=${process.env.GIT_USER_NAME} <${process.env.GIT_USER_EMAIL}> github=${bundle.githubTokenStatus.toLowerCase()} ${cfNote}`);
715
722
  return bundle;
716
723
  }
724
+ function hasUsableCredentials(credsPath) {
725
+ if (!(0, import_node_fs4.existsSync)(credsPath)) return false;
726
+ try {
727
+ const raw = (0, import_node_fs4.readFileSync)(credsPath, "utf8").trim();
728
+ if (raw.length === 0) return false;
729
+ const parsed = JSON.parse(raw);
730
+ return parsed != null && typeof parsed === "object" && "claudeAiOauth" in parsed;
731
+ } catch {
732
+ return false;
733
+ }
734
+ }
717
735
  function setupClaudeCredentials() {
718
736
  const home = process.env.HOME ?? "/home/launchpod";
719
737
  const claudeDir = (0, import_node_path4.join)(home, ".claude");
720
738
  (0, import_node_fs4.mkdirSync)(claudeDir, { recursive: true });
721
- const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
722
739
  const credsPath = (0, import_node_path4.join)(claudeDir, ".credentials.json");
723
- (0, import_node_fs4.writeFileSync)(credsPath, decoded);
724
- (0, import_node_fs4.chmodSync)(credsPath, 384);
740
+ if (hasUsableCredentials(credsPath)) {
741
+ console.log("[entrypoint] ~/.claude/.credentials.json already present \u2014 leaving Claude Code's own (refreshed) credentials intact");
742
+ } else {
743
+ const decoded = Buffer.from(requireEnv("CLAUDE_CREDENTIALS_B64"), "base64").toString("utf8");
744
+ (0, import_node_fs4.writeFileSync)(credsPath, decoded);
745
+ (0, import_node_fs4.chmodSync)(credsPath, 384);
746
+ console.log("[entrypoint] seeded ~/.claude/.credentials.json from CLAUDE_CREDENTIALS_B64");
747
+ }
725
748
  const configPath = (0, import_node_path4.join)(home, ".claude.json");
726
749
  let cfg = {};
727
750
  if ((0, import_node_fs4.existsSync)(configPath)) {
@@ -745,7 +768,7 @@ function setupClaudeCredentials() {
745
768
  "launch-sequencer"
746
769
  ];
747
770
  const projects = cfg.projects ?? {};
748
- const wsKey = "/workspace";
771
+ const wsKey = POD_WS;
749
772
  const wsProject = projects[wsKey] ?? {};
750
773
  const existingEnabled = Array.isArray(wsProject.enabledMcpjsonServers) ? wsProject.enabledMcpjsonServers : [];
751
774
  const mergedEnabled = Array.from(/* @__PURE__ */ new Set([...existingEnabled, ...PREAPPROVED_MCPS]));
@@ -764,7 +787,7 @@ function setupGitAndGh() {
764
787
  function detectAndSetPreviewPort() {
765
788
  if (process.env.PREVIEW_PORT) return;
766
789
  try {
767
- const pkgPath = "/workspace/package.json";
790
+ const pkgPath = (0, import_node_path4.join)(POD_WS, "package.json");
768
791
  if (!(0, import_node_fs4.existsSync)(pkgPath)) return;
769
792
  const pkg = JSON.parse((0, import_node_fs4.readFileSync)(pkgPath, "utf-8"));
770
793
  const scripts = pkg.scripts ?? {};
@@ -783,23 +806,27 @@ function detectAndSetPreviewPort() {
783
806
  }
784
807
  }
785
808
  function initWorkspaceIfEmpty() {
786
- process.chdir("/workspace");
809
+ process.chdir(POD_WS);
787
810
  if ((0, import_node_fs4.existsSync)(".git")) {
788
- console.log("[entrypoint] /workspace already initialized \u2014 skipping init");
811
+ console.log(`[entrypoint] ${POD_WS} already initialized \u2014 skipping init`);
789
812
  return;
790
813
  }
791
- console.log("[entrypoint] /workspace is empty \u2014 running launch-kit init");
814
+ console.log(`[entrypoint] ${POD_WS} is empty \u2014 running launch-kit init`);
792
815
  const status = run("launch-kit", [
793
816
  "init",
794
817
  `--token=${requireEnv("LS_PAT")}`,
795
818
  `--org=${requireEnv("LS_ORG_SLUG")}`,
796
819
  `--project=${requireEnv("LS_PROJECT_SLUG")}`,
797
820
  `--url=${process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app"}`,
798
- `--dir=/workspace`
821
+ `--dir=${POD_WS}`
799
822
  ]);
800
823
  if (status !== 0) fail2(`[entrypoint] launch-kit init failed (status ${status})`);
801
824
  }
802
825
  async function maybeProvisionIngress(bundle, services, projectSlug) {
826
+ if (LOCAL_ONLY) {
827
+ console.log("[entrypoint] LAUNCHKIT_LOCAL_ONLY=1 \u2014 skipping CF ingress; services run on localhost only");
828
+ return null;
829
+ }
803
830
  const token = bundle.cloudflareToken ?? null;
804
831
  const accountId = bundle.cloudflareAccountId ?? null;
805
832
  const zones = bundle.cloudflareZones ?? [];
@@ -819,7 +846,7 @@ async function maybeProvisionIngress(bundle, services, projectSlug) {
819
846
  } else {
820
847
  fail2(`[entrypoint] cloudflare token covers ${zones.length} zones (${zones.map((z) => z.name).join(", ")}) \u2014 set LAUNCHKIT_CF_BASE_DOMAIN to pick one.`);
821
848
  }
822
- const stateFile = "/workspace/.launchpod/launch-kit-tunnel.json";
849
+ const stateFile = (0, import_node_path4.join)(POD_WS, ".launchpod", "launch-kit-tunnel.json");
823
850
  const slugLabel = projectSlug.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
824
851
  const DNS_LABEL_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
825
852
  for (const s of services) {
@@ -895,7 +922,7 @@ async function maybeProvisionAccess(bundle, ingress) {
895
922
  }
896
923
  const serverUrl = process.env.LS_SERVER_URL ?? "https://launchsecure-v2.vercel.app";
897
924
  const pat = requireEnv("LS_PAT");
898
- const stateFile = "/workspace/.launchpod/launch-kit-access.json";
925
+ const stateFile = (0, import_node_path4.join)(POD_WS, ".launchpod", "launch-kit-access.json");
899
926
  const gatedHosts = services.filter((s) => !s.bypass).map((s) => s.hostname);
900
927
  const bypassed = services.filter((s) => s.bypass).map((s) => `${s.hostname}${s.path ?? ""}`);
901
928
  console.log(`[entrypoint] gating ${gatedHosts.join(", ")} behind CF Access (IdP: ${serverUrl})`);
@@ -994,11 +1021,13 @@ function spawnServiceGroup(services) {
994
1021
  }).finally(removeSignals);
995
1022
  }
996
1023
  async function main() {
997
- const priorCrashes = readCrashState()?.count ?? 0;
998
- if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
999
- const bootAttempt = bumpCrashCount();
1000
- if (bootAttempt > 1) {
1001
- console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
1024
+ if (!NO_PARK) {
1025
+ const priorCrashes = readCrashState()?.count ?? 0;
1026
+ if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
1027
+ const bootAttempt = bumpCrashCount();
1028
+ if (bootAttempt > 1) {
1029
+ console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
1030
+ }
1002
1031
  }
1003
1032
  for (const k of REQUIRED_ENV) requireEnv(k);
1004
1033
  const bundle = await setupFromCloud();
@@ -1032,8 +1061,10 @@ async function main() {
1032
1061
  }
1033
1062
  }
1034
1063
  const stableTimer = setTimeout(() => {
1035
- clearCrashCount();
1036
- console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
1064
+ if (!NO_PARK) {
1065
+ clearCrashCount();
1066
+ console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
1067
+ }
1037
1068
  }, STABLE_AFTER_MS);
1038
1069
  stableTimer.unref?.();
1039
1070
  try {