@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.
- package/dist/chart-client/assets/index-DOKsFe5i.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-BqiDfvZi.js +294 -0
- package/dist/client/assets/index-Mewz-s77.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-o_3y7Z0J.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-BrhDuG3C.js → _baseUniq-C6w7kg8x.js} +1 -1
- package/dist/deck-client/assets/{arc-DXtPHMhw.js → arc-Cx9pT3Nn.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Cs9IdLQQ.js → architectureDiagram-Q4EWVU46-BITSj3vA.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D--wpvoBAt.js → blockDiagram-DXYQGD6D-BehOFuwh.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-BNIKE8-Z.js → c4Diagram-AHTNJAMY-BZTYM4na.js} +1 -1
- package/dist/deck-client/assets/channel-Cw2WDt9a.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-Bz3EWmUo.js → chunk-4BX2VUAB-CCUx5CTd.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-D55BBvVZ.js → chunk-4TB4RGXK-UDZXXga6.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-BzjEcoTi.js → chunk-55IACEB6-CfcU6PIW.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-CH1vs4Lu.js → chunk-EDXVE4YY-BK6F5Fof.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-CVRnaGM0.js → chunk-FMBD7UC4-C-2idlFB.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-BCTV_aEZ.js → chunk-OYMX7WX6-D6hBkYLP.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-BqM8r4ee.js → chunk-QZHKN3VN-DixNpysA.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-Rhbb691A.js → chunk-YZCP3GAM-Cd3pNBtQ.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-JLUXVCUr.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-JLUXVCUr.js +1 -0
- package/dist/deck-client/assets/clone-H0XCnSb6.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-fWyBR9Nn.js → cose-bilkent-S5V4N54A-OF3JWdEt.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-Ds0Sqext.js → dagre-KV5264BT-Bqu-qcv4.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-BGz_X007.js → diagram-5BDNPKRD--0eHmUBS.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-C78jeb-r.js → diagram-G4DWMVQ6-nss6oL20.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-BdoHW8mF.js → diagram-MMDJMWI5-D_gSGnLR.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-C8i9zc-U.js → diagram-TYMM5635-BIt-P6Pk.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DplAZ-yG.js → erDiagram-SMLLAGMA-Bi-E4KQm.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-F3N9yTAB.js → flowDiagram-DWJPFMVM-DMJCvLMA.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DebdM4S5.js → ganttDiagram-T4ZO3ILL-C3xgEoPD.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-htCk0oPz.js → gitGraphDiagram-UUTBAWPF-CD0BEGAW.js} +1 -1
- package/dist/deck-client/assets/{graph-Dtxc2PT4.js → graph-Dtsd9Jwe.js} +1 -1
- package/dist/deck-client/assets/index-C6YxyZay.css +1 -0
- package/dist/deck-client/assets/{index-CwAiam97.js → index-TFX8vtTG.js} +1 -1
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-gTxegviJ.js → infoDiagram-42DDH7IO-7IcQYqe_.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DYSEsfUK.js → ishikawaDiagram-UXIWVN3A-DsCEbx3u.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DgVc1q4D.js → journeyDiagram-VCZTEJTY-1mP2JwCk.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DcOf3i9N.js → kanban-definition-6JOO6SKY-vT0Xrqh9.js} +1 -1
- package/dist/deck-client/assets/{layout-CHP9HIw4.js → layout-Cw4rS2pn.js} +1 -1
- package/dist/deck-client/assets/{linear-XjVb7x4Q.js → linear-CzOjL-Ih.js} +1 -1
- package/dist/deck-client/assets/{mermaid.core-bouKOnsR.js → mermaid.core-DYi3A-qK.js} +4 -4
- package/dist/deck-client/assets/{min-Jl4GV9DB.js → min-DstloRoL.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-FXXf4cJx.js → mindmap-definition-QFDTVHPH-D-cCX2d2.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-Dl4plCTi.js → pieDiagram-DEJITSTG-BqW2NTmy.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-BeOEeelg.js → quadrantDiagram-34T5L4WZ-DbJoWA8f.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CBh1jchM.js → requirementDiagram-MS252O5E-DQrUiz_d.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BK6gG-ub.js → sankeyDiagram-XADWPNL6-kB7PZc3g.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-BtQwzsF5.js → sequenceDiagram-FGHM5R23-CpyVu1TN.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX--kLQOi8R.js → stateDiagram-FHFEXIEX-CjqQcnty.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-tfMSn8xx.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-CQ_-CLh7.js → timeline-definition-GMOUNBTQ-B2PAO9bk.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-DBhKYAFT.js → vennDiagram-DHZGUBPP-C0G3ItCr.js} +1 -1
- package/dist/deck-client/assets/{wardley-RL74JXVD-DLKe29q9.js → wardley-RL74JXVD-B0TVaOmp.js} +1 -1
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Brb8ezsV.js → wardleyDiagram-NUSXRM2D-B-qtbNZe.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-LogZ0Az9.js → xychartDiagram-5P7HB3ND-41kcBoBE.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +484 -77
- package/dist/server/init-entry.js +54 -23
- package/dist/server/launch-bot-entry.js +38 -4
- package/dist/server/radar-docker-init-entry.js +53 -22
- package/dist/server/rover-entry.js +6056 -5155
- package/package.json +1 -1
- package/dist/chart-client/assets/index-Dd6IotOZ.css +0 -1
- package/dist/client/assets/index-BoIjawzY.js +0 -294
- package/dist/client/assets/index-DE0uje6k.css +0 -32
- package/dist/council-client/assets/index-CGYusOCK.css +0 -1
- package/dist/deck-client/assets/channel-Bb5wIjTD.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CN-ZYMbT.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CN-ZYMbT.js +0 -1
- package/dist/deck-client/assets/clone-D6e7poKG.js +0 -1
- package/dist/deck-client/assets/index-evAPhGvM.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CHEPjgB0.js +0 -1
- /package/dist/chart-client/assets/{index-CrYM1-ac.js → index-DJQYgFcp.js} +0 -0
- /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://
|
|
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
|
-
|
|
1086
|
-
|
|
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 =
|
|
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 = "
|
|
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(
|
|
1169
|
+
process.chdir(POD_WS);
|
|
1149
1170
|
if ((0, import_node_fs4.existsSync)(".git")) {
|
|
1150
|
-
console.log(
|
|
1171
|
+
console.log(`[entrypoint] ${POD_WS} already initialized \u2014 skipping init`);
|
|
1151
1172
|
return;
|
|
1152
1173
|
}
|
|
1153
|
-
console.log(
|
|
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
|
|
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 = "
|
|
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 = "
|
|
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
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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
|
-
|
|
1388
|
-
|
|
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
|
-
|
|
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://
|
|
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 = "
|
|
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
|
-
|
|
724
|
-
|
|
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 =
|
|
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 = "
|
|
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(
|
|
809
|
+
process.chdir(POD_WS);
|
|
787
810
|
if ((0, import_node_fs4.existsSync)(".git")) {
|
|
788
|
-
console.log(
|
|
811
|
+
console.log(`[entrypoint] ${POD_WS} already initialized \u2014 skipping init`);
|
|
789
812
|
return;
|
|
790
813
|
}
|
|
791
|
-
console.log(
|
|
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
|
|
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 = "
|
|
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 = "
|
|
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
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
-
|
|
1036
|
-
|
|
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 {
|