@launchsecure/launch-kit 0.0.36 → 0.0.37
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-DJrjyXbN.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-8eSXr3Ez.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-4K0t2WrZ.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-BiVx0WO_.js → _baseUniq-Cn5TyL9s.js} +1 -1
- package/dist/deck-client/assets/{arc-DGMkiEzS.js → arc-D61amKYu.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Y2WRmHtk.js → architectureDiagram-Q4EWVU46-CpKrvC2W.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-_Lbfu5BQ.js → blockDiagram-DXYQGD6D-Yj5OjxvG.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CTqpYTBX.js → c4Diagram-AHTNJAMY-BIR810Tv.js} +1 -1
- package/dist/deck-client/assets/channel-DrJz2x-n.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-liEIbPHs.js → chunk-4BX2VUAB-BeSHwGvx.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCc6lYvL.js → chunk-4TB4RGXK-CCqzsLpg.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-D02jJUR2.js → chunk-55IACEB6-CuW_aq4-.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-BFmGMbLD.js → chunk-EDXVE4YY-Dl35ixYh.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-6wFLOVcJ.js → chunk-FMBD7UC4-TwreZQTv.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-Bnr8RiBf.js → chunk-OYMX7WX6-Ahfw8EUo.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Ct82MksJ.js → chunk-QZHKN3VN-DlE_zlU-.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-BXmN1diQ.js → chunk-YZCP3GAM-Dj6QWzSg.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-a3tg9w7z.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-a3tg9w7z.js +1 -0
- package/dist/deck-client/assets/clone-Dd7JBCL5.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CmQCT-mH.js → cose-bilkent-S5V4N54A-BO1z5aOM.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-DDdSa9EX.js → dagre-KV5264BT-DVsw17fE.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-Bccks2xJ.js → diagram-5BDNPKRD-6jYs7oZk.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-CPPNgxmQ.js → diagram-G4DWMVQ6-6DbggeGE.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-KrD300pS.js → diagram-MMDJMWI5-CQtk1cSU.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-DefnLuQf.js → diagram-TYMM5635-BR-gt75b.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DI9FfnFP.js → erDiagram-SMLLAGMA-C9qMtjdY.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-twKyd3Fx.js → flowDiagram-DWJPFMVM-CdaPhPYb.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-Wau3jhBr.js → ganttDiagram-T4ZO3ILL-BRsZWUy4.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-D9GgYXwb.js → gitGraphDiagram-UUTBAWPF-B8Z90jCj.js} +1 -1
- package/dist/deck-client/assets/{graph-BhNLzyXS.js → graph-my2Zphm4.js} +1 -1
- package/dist/deck-client/assets/index-ByqxPEgU.css +1 -0
- package/dist/deck-client/assets/{index-BtQBaQ7s.js → index-DqAoYZwV.js} +43 -42
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-TylGlSG-.js → infoDiagram-42DDH7IO-Csr9loin.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DAT8icpg.js → ishikawaDiagram-UXIWVN3A-HWdvUNFi.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-D3v_XL72.js → journeyDiagram-VCZTEJTY-CjYHG6EM.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DNUOBiNr.js → kanban-definition-6JOO6SKY-CX3JdUu7.js} +1 -1
- package/dist/deck-client/assets/{layout-COfodgwF.js → layout-Bcucv5Gi.js} +1 -1
- package/dist/deck-client/assets/{linear-DmTsuIvK.js → linear-CUGM5FJZ.js} +1 -1
- package/dist/deck-client/assets/{min-BW1F7i1D.js → min-Dw4g5w9z.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CErFzKWl.js → mindmap-definition-QFDTVHPH-C8oo61fg.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DW5F757o.js → pieDiagram-DEJITSTG-D2WYGkq8.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B1S2-TfI.js → quadrantDiagram-34T5L4WZ-Vh00GISt.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BY5BAR-5.js → requirementDiagram-MS252O5E-DxI-DFrN.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-CE1Cp9HS.js → sankeyDiagram-XADWPNL6-QgwyjasI.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-IaHnbKye.js → sequenceDiagram-FGHM5R23-DmOmD5Ni.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CwPJm9hU.js → stateDiagram-FHFEXIEX-CRwglGg_.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BvZLEWAA.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DVFGGSgN.js → timeline-definition-GMOUNBTQ-Dj9YGKOh.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-C1194MJi.js → vennDiagram-DHZGUBPP-xzIaOzEU.js} +1 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CEAay09T.js +162 -0
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-hpwdFfGj.js → wardleyDiagram-NUSXRM2D-BIYYh-JZ.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYkotwy8.js → xychartDiagram-5P7HB3ND-Cy9EoJCh.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +21 -2
- package/dist/server/council-entry.js +86 -2
- package/dist/server/council-serve.js +81 -2
- package/dist/server/deck-mcp-entry.js +449 -68
- package/dist/server/deck-serve.js +411 -42
- package/dist/server/fb-wizard.js +0 -0
- package/dist/server/init-entry.js +96 -4
- package/dist/server/radar-docker-init-entry.js +95 -3
- package/dist/server/radar-entrypoint-entry.js +0 -0
- package/dist/server/radar-teardown-entry.js +0 -0
- package/dist/server/rover-entry.js +25 -4
- package/package.json +22 -23
- package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +5 -0
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
- package/dist/chart-client/assets/index-DpKO9p0s.css +0 -1
- package/dist/client/assets/index-Dv6dD2zY.css +0 -32
- package/dist/council-client/assets/index-AqQ9Sei6.css +0 -1
- package/dist/deck-client/assets/channel-DB6LxW_l.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/clone-DiIRH1pI.js +0 -1
- package/dist/deck-client/assets/index-B-YQq5b5.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +0 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +0 -162
- /package/dist/chart-client/assets/{index-DFu2xIrM.js → index-BgUxHxwE.js} +0 -0
- /package/dist/client/assets/{index-Cbw6bVdx.js → index-CUivaQnN.js} +0 -0
- /package/dist/council-client/assets/{index-CAsmGTzg.js → index-DN8HN_5K.js} +0 -0
|
@@ -617,6 +617,17 @@ async function cf(opts) {
|
|
|
617
617
|
function isNotFound(env) {
|
|
618
618
|
return !env.success && (env.errors ?? []).some((e) => e.code === 7003 || e.code === 1001 || e.code === 81044);
|
|
619
619
|
}
|
|
620
|
+
async function findTunnelByName(input) {
|
|
621
|
+
const q = new URLSearchParams({ name: input.tunnelName, is_deleted: "false" }).toString();
|
|
622
|
+
const res = await cf({
|
|
623
|
+
apiToken: input.apiToken,
|
|
624
|
+
method: "GET",
|
|
625
|
+
path: `/accounts/${input.accountId}/cfd_tunnel?${q}`
|
|
626
|
+
});
|
|
627
|
+
if (!res.success || !Array.isArray(res.result)) return null;
|
|
628
|
+
const live = res.result.find((t) => t.name === input.tunnelName && !t.deleted_at);
|
|
629
|
+
return live?.id ?? null;
|
|
630
|
+
}
|
|
620
631
|
function loadState(path6) {
|
|
621
632
|
if (!(0, import_node_fs2.existsSync)(path6)) return null;
|
|
622
633
|
try {
|
|
@@ -648,16 +659,26 @@ async function ensureTunnel(input, knownTunnelId) {
|
|
|
648
659
|
throw new Error(`[cf] tunnel GET failed: ${JSON.stringify(got.errors)}`);
|
|
649
660
|
}
|
|
650
661
|
}
|
|
662
|
+
const existing = await findTunnelByName(input);
|
|
663
|
+
if (existing) {
|
|
664
|
+
console.log(`[cf] adopted existing tunnel "${input.tunnelName}" (${existing}) \u2014 local state was missing`);
|
|
665
|
+
return existing;
|
|
666
|
+
}
|
|
651
667
|
const created = await cf({
|
|
652
668
|
apiToken: input.apiToken,
|
|
653
669
|
method: "POST",
|
|
654
670
|
path: `/accounts/${input.accountId}/cfd_tunnel`,
|
|
655
671
|
body: { name: input.tunnelName, config_src: "cloudflare" }
|
|
656
672
|
});
|
|
657
|
-
if (
|
|
658
|
-
|
|
673
|
+
if (created.success && created.result) return created.result.id;
|
|
674
|
+
if ((created.errors ?? []).some((e) => e.code === 1013)) {
|
|
675
|
+
const adopted = await findTunnelByName(input);
|
|
676
|
+
if (adopted) {
|
|
677
|
+
console.log(`[cf] tunnel "${input.tunnelName}" already existed (1013) \u2014 adopted ${adopted}`);
|
|
678
|
+
return adopted;
|
|
679
|
+
}
|
|
659
680
|
}
|
|
660
|
-
|
|
681
|
+
throw new Error(`[cf] tunnel create failed: ${JSON.stringify(created.errors)}`);
|
|
661
682
|
}
|
|
662
683
|
async function fetchConnectorToken(input, tunnelId) {
|
|
663
684
|
const res = await cf({
|
|
@@ -942,6 +963,60 @@ function run2(cmd, args, stdio = "inherit") {
|
|
|
942
963
|
const r = (0, import_node_child_process2.spawnSync)(cmd, args, { stdio });
|
|
943
964
|
return r.status ?? 1;
|
|
944
965
|
}
|
|
966
|
+
function readCrashState() {
|
|
967
|
+
try {
|
|
968
|
+
const s = JSON.parse((0, import_node_fs4.readFileSync)(CRASH_STATE_FILE, "utf8"));
|
|
969
|
+
return typeof s?.count === "number" && s.count >= 0 ? s : null;
|
|
970
|
+
} catch {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function bumpCrashCount() {
|
|
975
|
+
const prev = readCrashState();
|
|
976
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
977
|
+
const next = {
|
|
978
|
+
count: (prev?.count ?? 0) + 1,
|
|
979
|
+
firstAt: prev?.firstAt ?? now,
|
|
980
|
+
lastAt: now
|
|
981
|
+
};
|
|
982
|
+
try {
|
|
983
|
+
(0, import_node_fs4.mkdirSync)(LAUNCHPOD_DIR, { recursive: true });
|
|
984
|
+
(0, import_node_fs4.writeFileSync)(CRASH_STATE_FILE, JSON.stringify(next, null, 2));
|
|
985
|
+
} catch (err) {
|
|
986
|
+
console.warn(`[entrypoint] could not persist boot-crash counter (continuing unprotected): ${err instanceof Error ? err.message : String(err)}`);
|
|
987
|
+
}
|
|
988
|
+
return next.count;
|
|
989
|
+
}
|
|
990
|
+
function clearCrashCount() {
|
|
991
|
+
try {
|
|
992
|
+
if ((0, import_node_fs4.existsSync)(CRASH_STATE_FILE)) (0, import_node_fs4.writeFileSync)(CRASH_STATE_FILE, JSON.stringify({ count: 0, firstAt: "", lastAt: "" }));
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
async function parkAfterCrashLoop(count) {
|
|
997
|
+
const lines = [
|
|
998
|
+
"==================================================================",
|
|
999
|
+
`[entrypoint] CRASH-LOOP HALT \u2014 ${count} consecutive failed boots (cap ${MAX_BOOT_CRASHES}).`,
|
|
1000
|
+
"[entrypoint] Refusing to restart again. Container is now PARKED (idle, not",
|
|
1001
|
+
"[entrypoint] exiting) so it stops thrashing CF APIs and logs. Fix the root",
|
|
1002
|
+
"[entrypoint] cause, clear the counter, then restart the container:",
|
|
1003
|
+
`[entrypoint] rm ${CRASH_STATE_FILE} && docker restart <container>`,
|
|
1004
|
+
"=================================================================="
|
|
1005
|
+
];
|
|
1006
|
+
for (const l of lines) console.error(l);
|
|
1007
|
+
for (const sig of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
1008
|
+
process.on(sig, () => {
|
|
1009
|
+
console.log(`[entrypoint] received ${sig} while parked \u2014 exiting`);
|
|
1010
|
+
process.exit(0);
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
setInterval(() => {
|
|
1014
|
+
console.error(`[entrypoint] still parked after crash-loop halt \u2014 clear ${CRASH_STATE_FILE} and restart to retry`);
|
|
1015
|
+
}, 15 * 6e4);
|
|
1016
|
+
await new Promise(() => {
|
|
1017
|
+
});
|
|
1018
|
+
throw new Error("unreachable");
|
|
1019
|
+
}
|
|
945
1020
|
async function setupFromCloud() {
|
|
946
1021
|
const pat = requireEnv("LS_PAT");
|
|
947
1022
|
const orgSlug = requireEnv("LS_ORG_SLUG");
|
|
@@ -1226,6 +1301,12 @@ function spawnServiceGroup(services) {
|
|
|
1226
1301
|
}).finally(removeSignals);
|
|
1227
1302
|
}
|
|
1228
1303
|
async function main() {
|
|
1304
|
+
const priorCrashes = readCrashState()?.count ?? 0;
|
|
1305
|
+
if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
|
|
1306
|
+
const bootAttempt = bumpCrashCount();
|
|
1307
|
+
if (bootAttempt > 1) {
|
|
1308
|
+
console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
|
|
1309
|
+
}
|
|
1229
1310
|
for (const k of REQUIRED_ENV) requireEnv(k);
|
|
1230
1311
|
const bundle = await setupFromCloud();
|
|
1231
1312
|
setupClaudeCredentials();
|
|
@@ -1257,15 +1338,22 @@ async function main() {
|
|
|
1257
1338
|
console.warn(`[entrypoint] \u26A0 first service is "${first.name}", not "radar" \u2014 quick tunneling is owned by the radar agent today, so NO external URL will be available.`);
|
|
1258
1339
|
}
|
|
1259
1340
|
}
|
|
1341
|
+
const stableTimer = setTimeout(() => {
|
|
1342
|
+
clearCrashCount();
|
|
1343
|
+
console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
|
|
1344
|
+
}, STABLE_AFTER_MS);
|
|
1345
|
+
stableTimer.unref?.();
|
|
1260
1346
|
try {
|
|
1261
1347
|
await spawnServiceGroup(services);
|
|
1348
|
+
clearTimeout(stableTimer);
|
|
1262
1349
|
process.exit(0);
|
|
1263
1350
|
} catch (err) {
|
|
1351
|
+
clearTimeout(stableTimer);
|
|
1264
1352
|
console.error(`[entrypoint] ${err instanceof Error ? err.message : String(err)}`);
|
|
1265
1353
|
process.exit(1);
|
|
1266
1354
|
}
|
|
1267
1355
|
}
|
|
1268
|
-
var import_node_child_process2, import_node_fs4, import_node_path4, REQUIRED_ENV, GATED_SERVICES;
|
|
1356
|
+
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;
|
|
1269
1357
|
var init_radar_docker_init_entry = __esm({
|
|
1270
1358
|
"src/server/radar-docker-init-entry.ts"() {
|
|
1271
1359
|
"use strict";
|
|
@@ -1282,6 +1370,10 @@ var init_radar_docker_init_entry = __esm({
|
|
|
1282
1370
|
"LS_ORG_SLUG",
|
|
1283
1371
|
"LS_PROJECT_SLUG"
|
|
1284
1372
|
];
|
|
1373
|
+
LAUNCHPOD_DIR = "/workspace/.launchpod";
|
|
1374
|
+
CRASH_STATE_FILE = (0, import_node_path4.join)(LAUNCHPOD_DIR, ".boot-crash.json");
|
|
1375
|
+
MAX_BOOT_CRASHES = 5;
|
|
1376
|
+
STABLE_AFTER_MS = 3e4;
|
|
1285
1377
|
GATED_SERVICES = {
|
|
1286
1378
|
// Claude web terminal — live drivable shell ⇒ RCE surface ⇒ short session.
|
|
1287
1379
|
bot: { strict: true },
|
|
@@ -286,6 +286,17 @@ async function cf(opts) {
|
|
|
286
286
|
function isNotFound(env) {
|
|
287
287
|
return !env.success && (env.errors ?? []).some((e) => e.code === 7003 || e.code === 1001 || e.code === 81044);
|
|
288
288
|
}
|
|
289
|
+
async function findTunnelByName(input) {
|
|
290
|
+
const q = new URLSearchParams({ name: input.tunnelName, is_deleted: "false" }).toString();
|
|
291
|
+
const res = await cf({
|
|
292
|
+
apiToken: input.apiToken,
|
|
293
|
+
method: "GET",
|
|
294
|
+
path: `/accounts/${input.accountId}/cfd_tunnel?${q}`
|
|
295
|
+
});
|
|
296
|
+
if (!res.success || !Array.isArray(res.result)) return null;
|
|
297
|
+
const live = res.result.find((t) => t.name === input.tunnelName && !t.deleted_at);
|
|
298
|
+
return live?.id ?? null;
|
|
299
|
+
}
|
|
289
300
|
function loadState(path) {
|
|
290
301
|
if (!(0, import_node_fs2.existsSync)(path)) return null;
|
|
291
302
|
try {
|
|
@@ -317,16 +328,26 @@ async function ensureTunnel(input, knownTunnelId) {
|
|
|
317
328
|
throw new Error(`[cf] tunnel GET failed: ${JSON.stringify(got.errors)}`);
|
|
318
329
|
}
|
|
319
330
|
}
|
|
331
|
+
const existing = await findTunnelByName(input);
|
|
332
|
+
if (existing) {
|
|
333
|
+
console.log(`[cf] adopted existing tunnel "${input.tunnelName}" (${existing}) \u2014 local state was missing`);
|
|
334
|
+
return existing;
|
|
335
|
+
}
|
|
320
336
|
const created = await cf({
|
|
321
337
|
apiToken: input.apiToken,
|
|
322
338
|
method: "POST",
|
|
323
339
|
path: `/accounts/${input.accountId}/cfd_tunnel`,
|
|
324
340
|
body: { name: input.tunnelName, config_src: "cloudflare" }
|
|
325
341
|
});
|
|
326
|
-
if (
|
|
327
|
-
|
|
342
|
+
if (created.success && created.result) return created.result.id;
|
|
343
|
+
if ((created.errors ?? []).some((e) => e.code === 1013)) {
|
|
344
|
+
const adopted = await findTunnelByName(input);
|
|
345
|
+
if (adopted) {
|
|
346
|
+
console.log(`[cf] tunnel "${input.tunnelName}" already existed (1013) \u2014 adopted ${adopted}`);
|
|
347
|
+
return adopted;
|
|
348
|
+
}
|
|
328
349
|
}
|
|
329
|
-
|
|
350
|
+
throw new Error(`[cf] tunnel create failed: ${JSON.stringify(created.errors)}`);
|
|
330
351
|
}
|
|
331
352
|
async function fetchConnectorToken(input, tunnelId) {
|
|
332
353
|
const res = await cf({
|
|
@@ -595,6 +616,64 @@ function run(cmd, args, stdio = "inherit") {
|
|
|
595
616
|
const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio });
|
|
596
617
|
return r.status ?? 1;
|
|
597
618
|
}
|
|
619
|
+
var LAUNCHPOD_DIR = "/workspace/.launchpod";
|
|
620
|
+
var CRASH_STATE_FILE = (0, import_node_path4.join)(LAUNCHPOD_DIR, ".boot-crash.json");
|
|
621
|
+
var MAX_BOOT_CRASHES = 5;
|
|
622
|
+
var STABLE_AFTER_MS = 3e4;
|
|
623
|
+
function readCrashState() {
|
|
624
|
+
try {
|
|
625
|
+
const s = JSON.parse((0, import_node_fs4.readFileSync)(CRASH_STATE_FILE, "utf8"));
|
|
626
|
+
return typeof s?.count === "number" && s.count >= 0 ? s : null;
|
|
627
|
+
} catch {
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function bumpCrashCount() {
|
|
632
|
+
const prev = readCrashState();
|
|
633
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
634
|
+
const next = {
|
|
635
|
+
count: (prev?.count ?? 0) + 1,
|
|
636
|
+
firstAt: prev?.firstAt ?? now,
|
|
637
|
+
lastAt: now
|
|
638
|
+
};
|
|
639
|
+
try {
|
|
640
|
+
(0, import_node_fs4.mkdirSync)(LAUNCHPOD_DIR, { recursive: true });
|
|
641
|
+
(0, import_node_fs4.writeFileSync)(CRASH_STATE_FILE, JSON.stringify(next, null, 2));
|
|
642
|
+
} catch (err) {
|
|
643
|
+
console.warn(`[entrypoint] could not persist boot-crash counter (continuing unprotected): ${err instanceof Error ? err.message : String(err)}`);
|
|
644
|
+
}
|
|
645
|
+
return next.count;
|
|
646
|
+
}
|
|
647
|
+
function clearCrashCount() {
|
|
648
|
+
try {
|
|
649
|
+
if ((0, import_node_fs4.existsSync)(CRASH_STATE_FILE)) (0, import_node_fs4.writeFileSync)(CRASH_STATE_FILE, JSON.stringify({ count: 0, firstAt: "", lastAt: "" }));
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async function parkAfterCrashLoop(count) {
|
|
654
|
+
const lines = [
|
|
655
|
+
"==================================================================",
|
|
656
|
+
`[entrypoint] CRASH-LOOP HALT \u2014 ${count} consecutive failed boots (cap ${MAX_BOOT_CRASHES}).`,
|
|
657
|
+
"[entrypoint] Refusing to restart again. Container is now PARKED (idle, not",
|
|
658
|
+
"[entrypoint] exiting) so it stops thrashing CF APIs and logs. Fix the root",
|
|
659
|
+
"[entrypoint] cause, clear the counter, then restart the container:",
|
|
660
|
+
`[entrypoint] rm ${CRASH_STATE_FILE} && docker restart <container>`,
|
|
661
|
+
"=================================================================="
|
|
662
|
+
];
|
|
663
|
+
for (const l of lines) console.error(l);
|
|
664
|
+
for (const sig of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
665
|
+
process.on(sig, () => {
|
|
666
|
+
console.log(`[entrypoint] received ${sig} while parked \u2014 exiting`);
|
|
667
|
+
process.exit(0);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
setInterval(() => {
|
|
671
|
+
console.error(`[entrypoint] still parked after crash-loop halt \u2014 clear ${CRASH_STATE_FILE} and restart to retry`);
|
|
672
|
+
}, 15 * 6e4);
|
|
673
|
+
await new Promise(() => {
|
|
674
|
+
});
|
|
675
|
+
throw new Error("unreachable");
|
|
676
|
+
}
|
|
598
677
|
async function setupFromCloud() {
|
|
599
678
|
const pat = requireEnv("LS_PAT");
|
|
600
679
|
const orgSlug = requireEnv("LS_ORG_SLUG");
|
|
@@ -885,6 +964,12 @@ function spawnServiceGroup(services) {
|
|
|
885
964
|
}).finally(removeSignals);
|
|
886
965
|
}
|
|
887
966
|
async function main() {
|
|
967
|
+
const priorCrashes = readCrashState()?.count ?? 0;
|
|
968
|
+
if (priorCrashes >= MAX_BOOT_CRASHES) await parkAfterCrashLoop(priorCrashes);
|
|
969
|
+
const bootAttempt = bumpCrashCount();
|
|
970
|
+
if (bootAttempt > 1) {
|
|
971
|
+
console.warn(`[entrypoint] boot attempt ${bootAttempt}/${MAX_BOOT_CRASHES} \u2014 prior boot(s) crashed before becoming stable`);
|
|
972
|
+
}
|
|
888
973
|
for (const k of REQUIRED_ENV) requireEnv(k);
|
|
889
974
|
const bundle = await setupFromCloud();
|
|
890
975
|
setupClaudeCredentials();
|
|
@@ -916,10 +1001,17 @@ async function main() {
|
|
|
916
1001
|
console.warn(`[entrypoint] \u26A0 first service is "${first.name}", not "radar" \u2014 quick tunneling is owned by the radar agent today, so NO external URL will be available.`);
|
|
917
1002
|
}
|
|
918
1003
|
}
|
|
1004
|
+
const stableTimer = setTimeout(() => {
|
|
1005
|
+
clearCrashCount();
|
|
1006
|
+
console.log(`[entrypoint] services stable for ${Math.round(STABLE_AFTER_MS / 1e3)}s \u2014 boot-crash counter cleared`);
|
|
1007
|
+
}, STABLE_AFTER_MS);
|
|
1008
|
+
stableTimer.unref?.();
|
|
919
1009
|
try {
|
|
920
1010
|
await spawnServiceGroup(services);
|
|
1011
|
+
clearTimeout(stableTimer);
|
|
921
1012
|
process.exit(0);
|
|
922
1013
|
} catch (err) {
|
|
1014
|
+
clearTimeout(stableTimer);
|
|
923
1015
|
console.error(`[entrypoint] ${err instanceof Error ? err.message : String(err)}`);
|
|
924
1016
|
process.exit(1);
|
|
925
1017
|
}
|
|
File without changes
|
|
File without changes
|
|
@@ -61,6 +61,17 @@ async function cf(opts) {
|
|
|
61
61
|
function isNotFound(env) {
|
|
62
62
|
return !env.success && (env.errors ?? []).some((e) => e.code === 7003 || e.code === 1001 || e.code === 81044);
|
|
63
63
|
}
|
|
64
|
+
async function findTunnelByName(input) {
|
|
65
|
+
const q = new URLSearchParams({ name: input.tunnelName, is_deleted: "false" }).toString();
|
|
66
|
+
const res = await cf({
|
|
67
|
+
apiToken: input.apiToken,
|
|
68
|
+
method: "GET",
|
|
69
|
+
path: `/accounts/${input.accountId}/cfd_tunnel?${q}`
|
|
70
|
+
});
|
|
71
|
+
if (!res.success || !Array.isArray(res.result)) return null;
|
|
72
|
+
const live = res.result.find((t) => t.name === input.tunnelName && !t.deleted_at);
|
|
73
|
+
return live?.id ?? null;
|
|
74
|
+
}
|
|
64
75
|
function loadState(path) {
|
|
65
76
|
if (!(0, import_node_fs.existsSync)(path)) return null;
|
|
66
77
|
try {
|
|
@@ -92,16 +103,26 @@ async function ensureTunnel(input, knownTunnelId) {
|
|
|
92
103
|
throw new Error(`[cf] tunnel GET failed: ${JSON.stringify(got.errors)}`);
|
|
93
104
|
}
|
|
94
105
|
}
|
|
106
|
+
const existing = await findTunnelByName(input);
|
|
107
|
+
if (existing) {
|
|
108
|
+
console.log(`[cf] adopted existing tunnel "${input.tunnelName}" (${existing}) \u2014 local state was missing`);
|
|
109
|
+
return existing;
|
|
110
|
+
}
|
|
95
111
|
const created = await cf({
|
|
96
112
|
apiToken: input.apiToken,
|
|
97
113
|
method: "POST",
|
|
98
114
|
path: `/accounts/${input.accountId}/cfd_tunnel`,
|
|
99
115
|
body: { name: input.tunnelName, config_src: "cloudflare" }
|
|
100
116
|
});
|
|
101
|
-
if (
|
|
102
|
-
|
|
117
|
+
if (created.success && created.result) return created.result.id;
|
|
118
|
+
if ((created.errors ?? []).some((e) => e.code === 1013)) {
|
|
119
|
+
const adopted = await findTunnelByName(input);
|
|
120
|
+
if (adopted) {
|
|
121
|
+
console.log(`[cf] tunnel "${input.tunnelName}" already existed (1013) \u2014 adopted ${adopted}`);
|
|
122
|
+
return adopted;
|
|
123
|
+
}
|
|
103
124
|
}
|
|
104
|
-
|
|
125
|
+
throw new Error(`[cf] tunnel create failed: ${JSON.stringify(created.errors)}`);
|
|
105
126
|
}
|
|
106
127
|
async function fetchConnectorToken(input, tunnelId) {
|
|
107
128
|
const res = await cf({
|
|
@@ -587,7 +608,7 @@ var require_package = __commonJS({
|
|
|
587
608
|
"package.json"(exports2, module2) {
|
|
588
609
|
module2.exports = {
|
|
589
610
|
name: "@launchsecure/launch-kit",
|
|
590
|
-
version: "0.0.
|
|
611
|
+
version: "0.0.37",
|
|
591
612
|
description: "LaunchSecure toolkit \u2014 launch-sequencer (pipeline runner + terminal bridge), launch-radar (feedback webhook receiver), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup). launch-pod is the container image these run inside.",
|
|
592
613
|
license: "MIT",
|
|
593
614
|
author: "LaunchSecure - AutomateWithUs",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@launchsecure/launch-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"description": "LaunchSecure toolkit — launch-sequencer (pipeline runner + terminal bridge), launch-radar (feedback webhook receiver), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup). launch-pod is the container image these run inside.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "LaunchSecure - AutomateWithUs",
|
|
@@ -59,24 +59,6 @@
|
|
|
59
59
|
"launch-rover": "./dist/server/rover-entry.js",
|
|
60
60
|
"launch-bot": "./dist/server/launch-bot-entry.js"
|
|
61
61
|
},
|
|
62
|
-
"scripts": {
|
|
63
|
-
"build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
|
|
64
|
-
"build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
|
|
65
|
-
"test:beacon": "vitest run --config vite.beacon.config.ts",
|
|
66
|
-
"test:radar": "vitest run --config vite.radar.config.ts",
|
|
67
|
-
"test:chart": "vitest run --config vite.chart.test.config.ts",
|
|
68
|
-
"build:deck-client": "vite build --config vite.deck.config.ts",
|
|
69
|
-
"build:council-client": "vite build --config vite.council.config.ts",
|
|
70
|
-
"build:client": "vite build",
|
|
71
|
-
"build:chart-client": "vite build --config vite.chart.config.ts",
|
|
72
|
-
"build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts src/server/init-entry.ts src/server/orbit-entry.ts src/server/course-entry.ts src/server/beacon-monitor-entry.ts src/server/parse-worker-entry.ts src/server/radar-teardown-entry.ts src/server/radar-docker-init-entry.ts src/server/launch-radar-entry.ts src/server/rover-entry.ts src/server/launch-bot-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native --external:pgsql-parser --external:libpg-query && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
|
|
73
|
-
"dev:client": "vite",
|
|
74
|
-
"dev:deck-serve": "cd ../.. && tsx watch packages/cli/src/server/deck-mcp-entry.ts serve",
|
|
75
|
-
"dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
|
|
76
|
-
"dev:server": "pnpm build:server && node dist/server/cli.js",
|
|
77
|
-
"dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\"",
|
|
78
|
-
"prepublishOnly": "pnpm build"
|
|
79
|
-
},
|
|
80
62
|
"files": [
|
|
81
63
|
"dist",
|
|
82
64
|
"prompts",
|
|
@@ -97,8 +79,6 @@
|
|
|
97
79
|
"ws": "^8.18.0"
|
|
98
80
|
},
|
|
99
81
|
"devDependencies": {
|
|
100
|
-
"@launchsecure/claude-code-web": "workspace:*",
|
|
101
|
-
"@launchsecure/ui": "workspace:*",
|
|
102
82
|
"@types/node": "^20.0.0",
|
|
103
83
|
"@types/pg": "^8.11.10",
|
|
104
84
|
"@types/react": "^18.3.12",
|
|
@@ -122,6 +102,25 @@
|
|
|
122
102
|
"react-router-dom": "^6.28.0",
|
|
123
103
|
"tailwindcss": "^3.4.19",
|
|
124
104
|
"vite": "^5.4.11",
|
|
125
|
-
"vitest": "^1.6.0"
|
|
105
|
+
"vitest": "^1.6.0",
|
|
106
|
+
"@launchsecure/claude-code-web": "0.0.1",
|
|
107
|
+
"@launchsecure/ui": "0.0.1"
|
|
108
|
+
},
|
|
109
|
+
"scripts": {
|
|
110
|
+
"build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
|
|
111
|
+
"build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
|
|
112
|
+
"test:beacon": "vitest run --config vite.beacon.config.ts",
|
|
113
|
+
"test:radar": "vitest run --config vite.radar.config.ts",
|
|
114
|
+
"test:chart": "vitest run --config vite.chart.test.config.ts",
|
|
115
|
+
"build:deck-client": "vite build --config vite.deck.config.ts",
|
|
116
|
+
"build:council-client": "vite build --config vite.council.config.ts",
|
|
117
|
+
"build:client": "vite build",
|
|
118
|
+
"build:chart-client": "vite build --config vite.chart.config.ts",
|
|
119
|
+
"build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts src/server/init-entry.ts src/server/orbit-entry.ts src/server/course-entry.ts src/server/beacon-monitor-entry.ts src/server/parse-worker-entry.ts src/server/radar-teardown-entry.ts src/server/radar-docker-init-entry.ts src/server/launch-radar-entry.ts src/server/rover-entry.ts src/server/launch-bot-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native --external:pgsql-parser --external:libpg-query && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
|
|
120
|
+
"dev:client": "vite",
|
|
121
|
+
"dev:deck-serve": "cd ../.. && tsx watch packages/cli/src/server/deck-mcp-entry.ts serve",
|
|
122
|
+
"dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
|
|
123
|
+
"dev:server": "pnpm build:server && node dist/server/cli.js",
|
|
124
|
+
"dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\""
|
|
126
125
|
}
|
|
127
|
-
}
|
|
126
|
+
}
|
|
@@ -80,6 +80,11 @@ Run `BUILD_CMD` (the project's real build, so the check matches CI). Skip if `--
|
|
|
80
80
|
|
|
81
81
|
**DB-safety guard (only when the build touches a DB).** If the detected `BUILD_CMD` runs migrations as part of the build (e.g. a Node build script that chains `prisma migrate deploy`, or any build that applies `MIGRATION_TOOL` migrations — detect by scanning the build script / target), then a prod DB connection string must NOT be used. If the relevant connection env (`DATABASE_URL`, `DB_URL`, etc.) looks production-y (matches `neon.tech`, `rds.amazonaws.com`, `supabase.co`, a `vercel.app` proxy host, etc.), refuse the build step: `⚠ build skipped — build applies migrations and DATABASE_URL looks production-y (<host>); re-run with a dev DB or pass --skip=build`. If the build does NOT touch a DB (most stacks), skip this guard.
|
|
82
82
|
|
|
83
|
+
**Build-cache hygiene (avoid stale-cache false positives).** Incremental-cache frameworks reuse a build cache across runs, and a cache left over from a prior build/dev run on a different commit can make a *clean-tree* build fail with a stale-manifest error even though CI (which always builds clean) succeeds. The classic signature is Next.js's `PageNotFoundError: Cannot find module for page: <route>` / `Failed to collect page data for <route>` (often `ENOENT` pointing into `.next/`) — observed on `.well-known/*` and other dynamic routes that build fine in CI. To keep the build check faithful to CI:
|
|
84
|
+
|
|
85
|
+
1. **Clear the framework cache before building** when the project uses a cache-based framework. Remove only the framework's own *regenerable, gitignored* cache dir if it exists — `.next/` (Next.js) · `.nuxt/` + `.output/` (Nuxt) · `.svelte-kit/` (SvelteKit) · `.astro/` (Astro) · `.turbo/` (Turborepo/Turbopack). **Never** remove `dist/`, `build/`, `out/`, `node_modules/`, or anything git-tracked — confirm the dir is gitignored before deleting. Non-cache stacks: skip this, there is nothing to clear.
|
|
86
|
+
2. **Retry-once backstop.** If a build still fails AND the stderr matches a stale-cache signature (`Cannot find module for page`, `Failed to collect page data`, or an `ENOENT` referencing the cache dir), clear the cache (step 1) and re-run `BUILD_CMD` exactly once. Report ✗ only if the clean rebuild also fails; if the rebuild passes, report ✓ and note `(first attempt failed on a stale-cache artifact — passed clean)`. This prevents a regenerable-cache quirk from producing a NO-GO.
|
|
87
|
+
|
|
83
88
|
### 5. schema_drift (only when MIGRATION_TOOL supports it)
|
|
84
89
|
|
|
85
90
|
Schema-vs-migrations consistency, using the tool's own drift command:
|
|
File without changes
|
|
File without changes
|