@launchsecure/launch-kit 0.0.32 → 0.0.34

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 (93) hide show
  1. package/dist/chart-client/assets/{index-B__ARB8k.js → index-DFu2xIrM.js} +2 -2
  2. package/dist/chart-client/assets/index-DpKO9p0s.css +1 -0
  3. package/dist/chart-client/index.html +2 -2
  4. package/dist/client/assets/{index-h8kMzVtG.js → index-Cbw6bVdx.js} +2 -2
  5. package/dist/client/assets/index-Dv6dD2zY.css +32 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/council-client/assets/index-AqQ9Sei6.css +1 -0
  8. package/dist/council-client/assets/{index-CWaDcsFR.js → index-CAsmGTzg.js} +2 -2
  9. package/dist/council-client/index.html +2 -2
  10. package/dist/deck-client/assets/{_baseUniq-C7GsHvgg.js → _baseUniq-BiVx0WO_.js} +1 -1
  11. package/dist/deck-client/assets/{arc-CSrZRINY.js → arc-DGMkiEzS.js} +1 -1
  12. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-zoB-G17J.js → architectureDiagram-Q4EWVU46-Y2WRmHtk.js} +1 -1
  13. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BRjjtYH6.js → blockDiagram-DXYQGD6D-_Lbfu5BQ.js} +1 -1
  14. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-C3D3sd2U.js → c4Diagram-AHTNJAMY-CTqpYTBX.js} +1 -1
  15. package/dist/deck-client/assets/channel-DB6LxW_l.js +1 -0
  16. package/dist/deck-client/assets/{chunk-4BX2VUAB-DhpDMOPO.js → chunk-4BX2VUAB-liEIbPHs.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-4TB4RGXK-BIRgPXRl.js → chunk-4TB4RGXK-CCc6lYvL.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-55IACEB6-BF24dwDZ.js → chunk-55IACEB6-D02jJUR2.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-EDXVE4YY-CW75Y61B.js → chunk-EDXVE4YY-BFmGMbLD.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-FMBD7UC4-B5-oyL79.js → chunk-FMBD7UC4-6wFLOVcJ.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-OYMX7WX6-BB2bHe_Q.js → chunk-OYMX7WX6-Bnr8RiBf.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-QZHKN3VN-D80eZO4B.js → chunk-QZHKN3VN-Ct82MksJ.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-YZCP3GAM-Dz9787p_.js → chunk-YZCP3GAM-BXmN1diQ.js} +1 -1
  24. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +1 -0
  25. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +1 -0
  26. package/dist/deck-client/assets/clone-DiIRH1pI.js +1 -0
  27. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-MQjiZLcL.js → cose-bilkent-S5V4N54A-CmQCT-mH.js} +1 -1
  28. package/dist/deck-client/assets/{dagre-KV5264BT-DG4EcLpJ.js → dagre-KV5264BT-DDdSa9EX.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-5BDNPKRD-1n7hM3Gc.js → diagram-5BDNPKRD-Bccks2xJ.js} +1 -1
  30. package/dist/deck-client/assets/{diagram-G4DWMVQ6-CYMarncV.js → diagram-G4DWMVQ6-CPPNgxmQ.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-MMDJMWI5-DSisoipe.js → diagram-MMDJMWI5-KrD300pS.js} +1 -1
  32. package/dist/deck-client/assets/{diagram-TYMM5635-Btnq49OJ.js → diagram-TYMM5635-DefnLuQf.js} +1 -1
  33. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-Cu2Hb_Tz.js → erDiagram-SMLLAGMA-DI9FfnFP.js} +1 -1
  34. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-CGJzUzsO.js → flowDiagram-DWJPFMVM-twKyd3Fx.js} +1 -1
  35. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-D9sqGUBT.js → ganttDiagram-T4ZO3ILL-Wau3jhBr.js} +1 -1
  36. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-C0QwX2od.js → gitGraphDiagram-UUTBAWPF-D9GgYXwb.js} +1 -1
  37. package/dist/deck-client/assets/{graph-CcBjOQCl.js → graph-BhNLzyXS.js} +1 -1
  38. package/dist/deck-client/assets/index-B-YQq5b5.css +1 -0
  39. package/dist/deck-client/assets/{index-0arwoc0z.js → index-BtQBaQ7s.js} +3 -3
  40. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-DTimhhhS.js → infoDiagram-42DDH7IO-TylGlSG-.js} +1 -1
  41. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DxOxg_B4.js → ishikawaDiagram-UXIWVN3A-DAT8icpg.js} +1 -1
  42. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-Bpq0qa4j.js → journeyDiagram-VCZTEJTY-D3v_XL72.js} +1 -1
  43. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-aTIrpcVO.js → kanban-definition-6JOO6SKY-DNUOBiNr.js} +1 -1
  44. package/dist/deck-client/assets/{layout-DqglLR2E.js → layout-COfodgwF.js} +1 -1
  45. package/dist/deck-client/assets/{linear-D5GxehPc.js → linear-DmTsuIvK.js} +1 -1
  46. package/dist/deck-client/assets/{min-DXLfSREq.js → min-BW1F7i1D.js} +1 -1
  47. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-mO5Vys7I.js → mindmap-definition-QFDTVHPH-CErFzKWl.js} +1 -1
  48. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-Dm0gzdAr.js → pieDiagram-DEJITSTG-DW5F757o.js} +1 -1
  49. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-Daq7j3qD.js → quadrantDiagram-34T5L4WZ-B1S2-TfI.js} +1 -1
  50. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CmwV95um.js → requirementDiagram-MS252O5E-BY5BAR-5.js} +1 -1
  51. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BOYl3Nkf.js → sankeyDiagram-XADWPNL6-CE1Cp9HS.js} +1 -1
  52. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-BuUjhIcW.js → sequenceDiagram-FGHM5R23-IaHnbKye.js} +1 -1
  53. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-LUZ_uwio.js → stateDiagram-FHFEXIEX-CwPJm9hU.js} +1 -1
  54. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +1 -0
  55. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-CDUxCCAW.js → timeline-definition-GMOUNBTQ-DVFGGSgN.js} +1 -1
  56. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-BRb24Tf7.js → vennDiagram-DHZGUBPP-C1194MJi.js} +1 -1
  57. package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +162 -0
  58. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BLGlYrQz.js → wardleyDiagram-NUSXRM2D-hpwdFfGj.js} +1 -1
  59. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-De31MSnk.js → xychartDiagram-5P7HB3ND-DYkotwy8.js} +1 -1
  60. package/dist/deck-client/index.html +2 -2
  61. package/dist/server/chart-serve.js +167 -2
  62. package/dist/server/cli.js +328 -42
  63. package/dist/server/course-entry.js +1 -1
  64. package/dist/server/graph-mcp-entry.js +180 -4
  65. package/dist/server/init-entry.js +1133 -219
  66. package/dist/server/launch-radar-entry.js +45 -0
  67. package/dist/server/parse-worker-entry.js +167 -2
  68. package/dist/server/radar-docker-init-entry.js +644 -0
  69. package/dist/server/radar-entrypoint-entry.js +99 -0
  70. package/dist/server/radar-teardown-entry.js +478 -0
  71. package/dist/server/recall-entry.js +4 -1
  72. package/dist/server/rover-entry.js +20122 -0
  73. package/package.json +7 -5
  74. package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +5 -5
  75. package/scaffolds/ls-marketplace/plugins/kit/commands/standup.md +6 -6
  76. package/scaffolds/ls-marketplace/plugins/kit/skills/analyse/SKILL.md +6 -0
  77. package/scaffolds/ls-marketplace/plugins/kit/skills/brief/SKILL.md +40 -48
  78. package/scaffolds/ls-marketplace/plugins/kit/skills/debug/SKILL.md +45 -20
  79. package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +76 -67
  80. package/scaffolds/ls-marketplace/plugins/kit/skills/handoff/SKILL.md +132 -0
  81. package/scaffolds/ls-marketplace/plugins/kit/skills/ship/SKILL.md +290 -0
  82. package/scaffolds/statusline/statusline-mcp.sh +82 -2
  83. package/scaffolds/statusline/statusline-wrapper.sh +8 -1
  84. package/dist/chart-client/assets/index-CDIhdgWg.css +0 -1
  85. package/dist/client/assets/index-CfW4n40I.css +0 -32
  86. package/dist/council-client/assets/index-CZim6x1u.css +0 -1
  87. package/dist/deck-client/assets/channel-8ReQnQfH.js +0 -1
  88. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-cRxTeGkK.js +0 -1
  89. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-cRxTeGkK.js +0 -1
  90. package/dist/deck-client/assets/clone-LSHZ3K6R.js +0 -1
  91. package/dist/deck-client/assets/index-BlTlhxFW.css +0 -1
  92. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CnnRwE5D.js +0 -1
  93. package/dist/deck-client/assets/wardley-RL74JXVD-B0BYyVBY.js +0 -162
@@ -23251,6 +23251,7 @@ function emptyParsedFile(absPath) {
23251
23251
  return {
23252
23252
  name: absPath.split("/").pop() ?? absPath,
23253
23253
  exports: [],
23254
+ defines: [],
23254
23255
  imports: [],
23255
23256
  reExports: [],
23256
23257
  jsxElements: /* @__PURE__ */ new Set(),
@@ -23363,6 +23364,34 @@ function parseFileTS(absPath) {
23363
23364
  reExports.push({ name: "*", from: caps["reexport.wildcard.source"], isWildcard: true });
23364
23365
  }
23365
23366
  }
23367
+ const definesSet = /* @__PURE__ */ new Set();
23368
+ function recordCallable(decl) {
23369
+ if (decl.type === "function_declaration" || decl.type === "generator_function_declaration") {
23370
+ const id = childOfType(decl, "identifier");
23371
+ if (id) definesSet.add(id.text);
23372
+ return;
23373
+ }
23374
+ if (decl.type === "class_declaration") {
23375
+ const id = childOfType(decl, "type_identifier") ?? childOfType(decl, "identifier");
23376
+ if (id) definesSet.add(id.text);
23377
+ return;
23378
+ }
23379
+ if (decl.type === "lexical_declaration" || decl.type === "variable_declaration") {
23380
+ for (const d of childrenOfType(decl, "variable_declarator")) {
23381
+ const id = childOfType(d, "identifier");
23382
+ if (!id) continue;
23383
+ const isCallable = !!childOfType(d, "arrow_function") || !!childOfType(d, "function_expression") || !!childOfType(d, "function") || !!childOfType(d, "generator_function");
23384
+ if (isCallable) definesSet.add(id.text);
23385
+ }
23386
+ }
23387
+ }
23388
+ for (const child of root.children) {
23389
+ if (child.type === "export_statement") {
23390
+ for (const gc of child.children) recordCallable(gc);
23391
+ } else {
23392
+ recordCallable(child);
23393
+ }
23394
+ }
23366
23395
  const jsxElements = /* @__PURE__ */ new Set();
23367
23396
  const jsxQuery = getQuery("jsx-elements");
23368
23397
  const jsxCaptures = jsxQuery.captures(root);
@@ -23451,7 +23480,7 @@ function parseFileTS(absPath) {
23451
23480
  }
23452
23481
  }
23453
23482
  const name = defaultName ?? firstValueExport ?? firstTypeExport ?? "";
23454
- return { name, exports: exportsOrdered, imports, reExports, jsxElements, navigations, fetchCalls };
23483
+ return { name, exports: exportsOrdered, defines: [...definesSet], imports, reExports, jsxElements, navigations, fetchCalls };
23455
23484
  }
23456
23485
  function extractDbCallsTS(absPath) {
23457
23486
  const tree = parseSource(absPath);
@@ -24542,17 +24571,17 @@ function shutdownTerminalBridge() {
24542
24571
  }
24543
24572
  }
24544
24573
 
24545
- // src/server/launchpod-terminal-init.ts
24574
+ // src/server/sequencer-terminal-init.ts
24546
24575
  var import_path = __toESM(require("path"));
24547
24576
  init_launch_kit_paths();
24548
- function initLaunchPodTerminalBridge(httpServer, projectDir) {
24577
+ function initSequencerTerminalBridge(httpServer, projectDir) {
24549
24578
  return initTerminalBridge(httpServer, projectDir, import_path.default.join(projectDir, LAUNCHPOD_DIR, "sessions"));
24550
24579
  }
24551
24580
 
24552
24581
  // src/server/mcp-config-writer.ts
24553
24582
  var import_fs = __toESM(require("fs"));
24554
24583
  var import_path2 = __toESM(require("path"));
24555
- var MANAGED_SERVERS = ["launch-pod", "launch-chart"];
24584
+ var MANAGED_SERVERS = ["launch-pod", "launch-sequencer", "launch-chart"];
24556
24585
  function mergeMcpEntry(existing, ours) {
24557
24586
  if (!existing || typeof existing !== "object") return ours;
24558
24587
  const prev = existing;
@@ -24601,7 +24630,7 @@ function removeClaudeEntries(filePath) {
24601
24630
  } else {
24602
24631
  existing.mcpServers = servers;
24603
24632
  import_fs.default.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
24604
- console.log(`MCP config cleaned (LaunchPod entries removed): ${filePath}`);
24633
+ console.log(`MCP config cleaned (launch-sequencer entries removed): ${filePath}`);
24605
24634
  }
24606
24635
  } catch {
24607
24636
  try {
@@ -24626,7 +24655,8 @@ function writeClaudeConfig(projectDir, mcpUrl, token) {
24626
24655
  }
24627
24656
  }
24628
24657
  const servers = existing.mcpServers ?? {};
24629
- servers["launch-pod"] = mergeMcpEntry(servers["launch-pod"], {
24658
+ delete servers["launch-pod"];
24659
+ servers["launch-sequencer"] = mergeMcpEntry(servers["launch-sequencer"], {
24630
24660
  type: "http",
24631
24661
  url: mcpUrl,
24632
24662
  headers: {
@@ -24655,9 +24685,9 @@ function writeCodexConfig(projectDir, mcpUrl, token) {
24655
24685
  }
24656
24686
  } catch {
24657
24687
  }
24658
- const cleaned = existingContent.replace(/\[mcp_servers\.launch-pod\][^\[]*/, "").replace(/\[mcp_servers\.launch-chart\][^\[]*/, "").trim();
24659
- const launchPodBlock = [
24660
- `[mcp_servers.launch-pod]`,
24688
+ const cleaned = existingContent.replace(/\[mcp_servers\.launch-pod\][^\[]*/, "").replace(/\[mcp_servers\.launch-sequencer\][^\[]*/, "").replace(/\[mcp_servers\.launch-chart\][^\[]*/, "").trim();
24689
+ const managedBlock = [
24690
+ `[mcp_servers.launch-sequencer]`,
24661
24691
  `url = "${mcpUrl}"`,
24662
24692
  `http_headers = { "Authorization" = "Bearer ${token}" }`,
24663
24693
  ``,
@@ -24665,7 +24695,7 @@ function writeCodexConfig(projectDir, mcpUrl, token) {
24665
24695
  `command = "launch-chart"`,
24666
24696
  `args = []`
24667
24697
  ].join("\n");
24668
- const merged = cleaned ? cleaned + "\n\n" + launchPodBlock + "\n" : launchPodBlock + "\n";
24698
+ const merged = cleaned ? cleaned + "\n\n" + managedBlock + "\n" : managedBlock + "\n";
24669
24699
  import_fs.default.writeFileSync(filePath, merged, { mode: 384 });
24670
24700
  writtenPaths.push(filePath);
24671
24701
  console.log(`Codex MCP config merged into ${filePath}`);
@@ -28032,7 +28062,7 @@ var RadarState = class {
28032
28062
  }
28033
28063
  };
28034
28064
 
28035
- // src/server/radar/tunnel.ts
28065
+ // src/server/tunnel/index.ts
28036
28066
  var import_node_fs3 = require("node:fs");
28037
28067
  var import_node_events = require("node:events");
28038
28068
  var import_promises = require("node:dns/promises");
@@ -28386,7 +28416,7 @@ var CacheableLookup = class {
28386
28416
  }
28387
28417
  };
28388
28418
 
28389
- // src/server/radar/tunnel.ts
28419
+ // src/server/tunnel/index.ts
28390
28420
  var import_undici = __toESM(require_undici());
28391
28421
  var import_cloudflared = require("cloudflared");
28392
28422
  var dnsResolver = new import_promises.Resolver();
@@ -28404,14 +28434,42 @@ var BACKOFF_MIN_MS = 1e3;
28404
28434
  var BACKOFF_MAX_MS = 3e5;
28405
28435
  var SELFTEST_INTERVAL_MS = 1e3;
28406
28436
  var SELFTEST_TIMEOUT_MS = 9e4;
28437
+ function resolveTunnelMode(opts) {
28438
+ const env = opts.env ?? process.env;
28439
+ const tokenKey = `${opts.varPrefix}_TUNNEL_TOKEN`;
28440
+ const hostKey = `${opts.varPrefix}_TUNNEL_HOSTNAME`;
28441
+ const token = env[tokenKey]?.trim();
28442
+ const hostname = env[hostKey]?.trim()?.replace(/^https?:\/\//, "").replace(/\/$/, "");
28443
+ if (!token && !hostname) return { kind: "quick" };
28444
+ if (!token || !hostname) {
28445
+ const missing = !token ? tokenKey : hostKey;
28446
+ throw new Error(
28447
+ `[tunnel] named-tunnel config is partial \u2014 ${missing} is not set. Set BOTH ${tokenKey} and ${hostKey} for a stable paid tunnel, or unset both to fall back to the free quick tunnel.`
28448
+ );
28449
+ }
28450
+ return { kind: "named", token, hostname };
28451
+ }
28452
+ var NAMED_FATAL_PATTERNS = [
28453
+ /\bunauthorized\b/i,
28454
+ /\b401\b/,
28455
+ /invalid (tunnel )?token/i,
28456
+ /token (is )?expired/i,
28457
+ /tunnel .{0,80}? not found/i,
28458
+ /failed to (provision|find|authenticate) tunnel/i,
28459
+ /failed to read tunnel credentials/i,
28460
+ /jwt.*expired/i
28461
+ ];
28407
28462
  var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28408
- constructor(localPort) {
28463
+ constructor(opts) {
28409
28464
  super();
28410
28465
  this.tunnel = null;
28411
28466
  this.currentUrl = null;
28412
28467
  this.stopping = false;
28468
+ this.fatal = false;
28413
28469
  this.restartAttempt = 0;
28414
- this.localPort = localPort;
28470
+ this.localPort = opts.localPort;
28471
+ this.mode = opts.mode;
28472
+ this.probePath = opts.probePath;
28415
28473
  }
28416
28474
  get publicUrl() {
28417
28475
  return this.currentUrl;
@@ -28419,7 +28477,7 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28419
28477
  async start() {
28420
28478
  if (this.tunnel) return;
28421
28479
  if (!(0, import_node_fs3.existsSync)(import_cloudflared.bin)) {
28422
- console.log("[radar] downloading cloudflared binary (one-time setup)\u2026");
28480
+ console.log("[tunnel] downloading cloudflared binary (one-time setup)\u2026");
28423
28481
  try {
28424
28482
  await (0, import_cloudflared.install)(import_cloudflared.bin);
28425
28483
  } catch (err2) {
@@ -28427,6 +28485,11 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28427
28485
  throw new Error(`Failed to install cloudflared binary: ${msg}`);
28428
28486
  }
28429
28487
  }
28488
+ if (this.mode.kind === "named") {
28489
+ console.log(`[tunnel] mode=named host=${this.mode.hostname} (paid \xB7 stable URL)`);
28490
+ } else {
28491
+ console.log(`[tunnel] mode=quick (free \xB7 *.trycloudflare.com \xB7 ephemeral URL)`);
28492
+ }
28430
28493
  this.spawnOnce();
28431
28494
  }
28432
28495
  async stop() {
@@ -28447,10 +28510,11 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28447
28510
  });
28448
28511
  }
28449
28512
  spawnOnce() {
28450
- const tun = import_cloudflared.Tunnel.quick(`http://127.0.0.1:${this.localPort}`);
28513
+ if (this.fatal) return;
28514
+ const tun = this.mode.kind === "named" ? import_cloudflared.Tunnel.withToken(this.mode.token) : import_cloudflared.Tunnel.quick(`http://127.0.0.1:${this.localPort}`);
28451
28515
  this.tunnel = tun;
28452
28516
  this.currentUrl = null;
28453
- let urlSeen = null;
28517
+ let urlSeen = this.mode.kind === "named" ? `https://${this.mode.hostname}` : null;
28454
28518
  let connectedSeen = false;
28455
28519
  let announced = false;
28456
28520
  const announceIfReady = () => {
@@ -28460,30 +28524,60 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28460
28524
  void this.selfTestAndAnnounce(url);
28461
28525
  };
28462
28526
  tun.on("url", (url) => {
28527
+ if (this.mode.kind === "named") return;
28463
28528
  if (urlSeen === url) return;
28464
28529
  urlSeen = url;
28465
- console.log(`[radar] tunnel subdomain assigned: ${url} (waiting for edge connection)`);
28530
+ console.log(`[tunnel] subdomain assigned: ${url} (waiting for edge connection)`);
28466
28531
  announceIfReady();
28467
28532
  });
28468
28533
  tun.on("connected", (conn) => {
28469
28534
  if (!connectedSeen) {
28470
28535
  connectedSeen = true;
28471
- console.log(`[radar] tunnel connected to edge: ${conn?.location ?? "?"} (${conn?.id ?? "?"})`);
28536
+ console.log(`[tunnel] connected to edge: ${conn?.location ?? "?"} (${conn?.id ?? "?"})`);
28472
28537
  }
28473
28538
  announceIfReady();
28474
28539
  });
28540
+ if (this.mode.kind === "named") {
28541
+ tun.on("stderr", (line) => {
28542
+ if (this.fatal) return;
28543
+ for (const pat of NAMED_FATAL_PATTERNS) {
28544
+ if (pat.test(line)) {
28545
+ this.markFatal(`cloudflared rejected the named-tunnel config \u2014 ${line.trim().slice(0, 200)}`);
28546
+ return;
28547
+ }
28548
+ }
28549
+ });
28550
+ }
28475
28551
  tun.on("error", (err2) => {
28476
28552
  if (this.stopping) return;
28477
- console.error(`[radar] cloudflared error: ${err2.message}`);
28553
+ console.error(`[tunnel] cloudflared error: ${err2.message}`);
28478
28554
  });
28479
28555
  tun.on("exit", (code) => {
28480
28556
  if (this.stopping) return;
28481
28557
  this.tunnel = null;
28482
28558
  this.currentUrl = null;
28559
+ if (this.fatal) return;
28483
28560
  this.emit("crashed", code);
28484
28561
  this.scheduleRestart();
28485
28562
  });
28486
28563
  }
28564
+ /**
28565
+ * Mark the tunnel fatally broken. Suppresses restart-on-crash and notifies
28566
+ * the parent so it can surface status=error to the UI. Named-mode only —
28567
+ * quick mode has no auth surface to fail on.
28568
+ */
28569
+ markFatal(reason) {
28570
+ if (this.fatal) return;
28571
+ this.fatal = true;
28572
+ this.stopping = true;
28573
+ console.error(`[tunnel] FATAL: ${reason}`);
28574
+ console.error(`[tunnel] not falling back to free tunnel \u2014 fix the named-tunnel env vars (or unset both) and restart.`);
28575
+ this.emit("fatal", reason);
28576
+ try {
28577
+ this.tunnel?.stop();
28578
+ } catch {
28579
+ }
28580
+ }
28487
28581
  /**
28488
28582
  * Poll the public URL until any HTTP response comes back (proving DNS +
28489
28583
  * edge + tunnel work end-to-end), then announce ready. Network errors are
@@ -28491,7 +28585,7 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28491
28585
  * we log loudly but keep cloudflared alive so a later retry can succeed.
28492
28586
  */
28493
28587
  async selfTestAndAnnounce(url) {
28494
- const probeUrl = `${url}/api/radar/ingest`;
28588
+ const probeUrl = `${url}${this.probePath}`;
28495
28589
  const started = Date.now();
28496
28590
  let attempts = 0;
28497
28591
  while (Date.now() - started < SELFTEST_TIMEOUT_MS) {
@@ -28503,7 +28597,7 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28503
28597
  signal: AbortSignal.timeout(5e3),
28504
28598
  dispatcher: dnsResilientDispatcher
28505
28599
  });
28506
- console.log(`[radar] self-test ok (${res.status}) after ${attempts} attempt(s)`);
28600
+ console.log(`[tunnel] self-test ok (${res.status}) after ${attempts} attempt(s)`);
28507
28601
  this.currentUrl = url;
28508
28602
  this.restartAttempt = 0;
28509
28603
  this.emit("ready", url);
@@ -28511,26 +28605,42 @@ var CloudflaredTunnel = class extends import_node_events.EventEmitter {
28511
28605
  } catch (err2) {
28512
28606
  if (attempts === 1 || attempts % 10 === 0) {
28513
28607
  const msg = err2 instanceof Error ? err2.message : String(err2);
28514
- console.log(`[radar] self-test attempt ${attempts}: ${msg} \u2014 retrying`);
28608
+ console.log(`[tunnel] self-test attempt ${attempts}: ${msg} \u2014 retrying`);
28515
28609
  }
28516
28610
  await new Promise((r) => setTimeout(r, SELFTEST_INTERVAL_MS));
28517
28611
  }
28518
28612
  }
28519
- console.error(`[radar] self-test gave up after ${Math.round(SELFTEST_TIMEOUT_MS / 1e3)}s \u2014 tunnel announced but unreachable from this host`);
28613
+ console.error(`[tunnel] self-test gave up after ${Math.round(SELFTEST_TIMEOUT_MS / 1e3)}s \u2014 tunnel announced but unreachable from this host`);
28520
28614
  }
28521
28615
  scheduleRestart() {
28522
28616
  if (this.stopping) return;
28523
28617
  this.restartAttempt += 1;
28524
28618
  const delay = Math.min(BACKOFF_MIN_MS * 2 ** (this.restartAttempt - 1), BACKOFF_MAX_MS);
28525
- console.log(`[radar] tunnel down \u2014 restarting in ${Math.round(delay / 1e3)}s (attempt ${this.restartAttempt})`);
28619
+ console.log(`[tunnel] down \u2014 restarting in ${Math.round(delay / 1e3)}s (attempt ${this.restartAttempt})`);
28526
28620
  setTimeout(() => this.spawnOnce(), delay);
28527
28621
  }
28528
28622
  };
28623
+ function createTunnel(opts) {
28624
+ if (opts.provider === null) return null;
28625
+ if (opts.provider === "cloudflare") {
28626
+ if (!opts.varPrefix) {
28627
+ throw new Error(`[tunnel] createTunnel({ provider: "cloudflare" }) requires varPrefix`);
28628
+ }
28629
+ const mode = resolveTunnelMode({ varPrefix: opts.varPrefix, env: opts.env });
28630
+ return new CloudflaredTunnel({
28631
+ localPort: opts.localPort,
28632
+ mode,
28633
+ probePath: opts.probePath
28634
+ });
28635
+ }
28636
+ const exhaustive = opts.provider;
28637
+ throw new Error(`[tunnel] unknown provider: ${String(exhaustive)}`);
28638
+ }
28529
28639
 
28530
28640
  // src/server/radar/agent.ts
28531
28641
  var SLOT_RELEASE_MS = 15 * 60 * 1e3;
28532
28642
  var AUTO_RESUME_STALENESS_MS = 60 * 60 * 1e3;
28533
- var Radar = class {
28643
+ var Radar = class _Radar {
28534
28644
  constructor(opts) {
28535
28645
  this.wss = null;
28536
28646
  this.clients = /* @__PURE__ */ new Set();
@@ -28555,7 +28665,18 @@ var Radar = class {
28555
28665
  orgSlug: opts.orgSlug,
28556
28666
  projectSlug: opts.projectSlug
28557
28667
  });
28558
- this.tunnel = new CloudflaredTunnel(opts.localPort);
28668
+ const tunnel = createTunnel({
28669
+ provider: _Radar.TUNNEL_PROVIDER,
28670
+ localPort: opts.localPort,
28671
+ probePath: RECEIVER_PATH,
28672
+ varPrefix: "RADAR_CF"
28673
+ });
28674
+ if (!tunnel) {
28675
+ throw new Error(
28676
+ `[radar] tunnel provider "${_Radar.TUNNEL_PROVIDER}" produced no tunnel \u2014 radar requires a public URL to register its receiver webhook.`
28677
+ );
28678
+ }
28679
+ this.tunnel = tunnel;
28559
28680
  this.maxConcurrent = parsePositiveInt(process.env.RADAR_MAX_CONCURRENT_ANALYSES);
28560
28681
  const callbacks = {
28561
28682
  onLive: () => this.handleLive(),
@@ -28567,6 +28688,13 @@ var Radar = class {
28567
28688
  };
28568
28689
  this.receiver = createReceiver({ state: this.state, callbacks });
28569
28690
  }
28691
+ static {
28692
+ // Tunnel provider is hardcoded for now — radar today cannot function
28693
+ // without a public URL (it has to register the receiver as a webhook with
28694
+ // LaunchSecure). If a future story wants radar to run behind a pre-existing
28695
+ // public hostname, add a "none" branch with a RADAR_PUBLIC_URL escape hatch.
28696
+ this.TUNNEL_PROVIDER = "cloudflare";
28697
+ }
28570
28698
  /** Spawn tunnel, register (or refresh) the webhook, wait for activation. */
28571
28699
  start() {
28572
28700
  this.status = "tunneling";
@@ -28577,6 +28705,11 @@ var Radar = class {
28577
28705
  this.tunnel.on("crashed", (code) => {
28578
28706
  console.error(`[radar] tunnel exited (code=${code ?? "?"}) \u2014 restarting`);
28579
28707
  });
28708
+ this.tunnel.on("fatal", (reason) => {
28709
+ this.status = "error";
28710
+ console.error(`[radar] named tunnel halted: ${reason}`);
28711
+ this.broadcastStatus();
28712
+ });
28580
28713
  void this.tunnel.start();
28581
28714
  this.autoResumeRecentlyInterrupted();
28582
28715
  }
@@ -29805,6 +29938,11 @@ function generate(rootDir) {
29805
29938
  const parsed = parsedByPath.get(absPath);
29806
29939
  const name = parsed.name || nameFromFilename(absPath);
29807
29940
  const layer = CLASSIFICATION_TO_LAYER[type] ?? "ui";
29941
+ const importedNames = [...new Set(
29942
+ parsed.imports.flatMap(
29943
+ (imp) => imp.isTypeOnly ? [] : imp.names.filter((n) => !imp.typeNames.has(n))
29944
+ )
29945
+ )];
29808
29946
  nodeIdSet.add(id);
29809
29947
  if (layer === "api") {
29810
29948
  const dbCalls = extractDbCallsTS(absPath);
@@ -29843,6 +29981,8 @@ function generate(rootDir) {
29843
29981
  responses: deep.responses,
29844
29982
  params: deep.params,
29845
29983
  ...deep.effects ? { effects: deep.effects } : {},
29984
+ ...parsed.defines.length > 0 ? { defines: parsed.defines } : {},
29985
+ ...importedNames.length > 0 ? { imported_names: importedNames } : {},
29846
29986
  ...deep.notes ? { notes: deep.notes } : {},
29847
29987
  _dbCalls: dbCalls
29848
29988
  // temp: used for cross-ref building below
@@ -29859,6 +29999,8 @@ function generate(rootDir) {
29859
29999
  layer: "ui",
29860
30000
  route,
29861
30001
  exports: parsed.exports,
30002
+ ...parsed.defines.length > 0 ? { defines: parsed.defines } : {},
30003
+ ...importedNames.length > 0 ? { imported_names: importedNames } : {},
29862
30004
  elements: deep.elements,
29863
30005
  stateVars: deep.stateVars,
29864
30006
  conditions: deep.conditions,
@@ -32819,6 +32961,132 @@ var middlewareGatesParser = {
32819
32961
  }
32820
32962
  };
32821
32963
 
32964
+ // src/server/graph/parsers/crosslayer/call-resolver.ts
32965
+ var BARE_IDENT = /^[A-Za-z_$][\w$]*$/;
32966
+ var callResolverParser = {
32967
+ id: "call-resolver",
32968
+ layer: "crosslayer",
32969
+ concern: "call-graph",
32970
+ detect(_rootDir) {
32971
+ return true;
32972
+ },
32973
+ generate(_rootDir, layerOutputs) {
32974
+ const definers = /* @__PURE__ */ new Map();
32975
+ const addDef = (sym, entry) => {
32976
+ if (!BARE_IDENT.test(sym)) return;
32977
+ const list = definers.get(sym);
32978
+ if (!list) {
32979
+ definers.set(sym, [entry]);
32980
+ } else if (!list.some((d) => d.nodeId === entry.nodeId)) {
32981
+ list.push(entry);
32982
+ }
32983
+ };
32984
+ const nodeLayer = /* @__PURE__ */ new Map();
32985
+ for (const [layer, output] of layerOutputs) {
32986
+ if (layer === "db" || layer === "static") continue;
32987
+ for (const node of output.nodes) {
32988
+ nodeLayer.set(node.id, layer);
32989
+ const entry = { nodeId: node.id, layer };
32990
+ for (const s of node.exports ?? []) addDef(s, entry);
32991
+ for (const s of node.defines ?? []) addDef(s, entry);
32992
+ }
32993
+ }
32994
+ const importTargets = /* @__PURE__ */ new Map();
32995
+ for (const [, output] of layerOutputs) {
32996
+ for (const e of output.edges) {
32997
+ if (e.type !== "imports" && e.type !== "imports_type" && e.type !== "renders") continue;
32998
+ let set = importTargets.get(e.source);
32999
+ if (!set) {
33000
+ set = /* @__PURE__ */ new Set();
33001
+ importTargets.set(e.source, set);
33002
+ }
33003
+ set.add(e.target);
33004
+ }
33005
+ }
33006
+ const crossRefs = [];
33007
+ const seen = /* @__PURE__ */ new Set();
33008
+ let resolvedSelf = 0;
33009
+ let resolvedImport = 0;
33010
+ let ambiguous = 0;
33011
+ let dropped = 0;
33012
+ for (const [, output] of layerOutputs) {
33013
+ for (const node of output.nodes) {
33014
+ const calls = node.effects?.calls;
33015
+ if (!calls || calls.length === 0) continue;
33016
+ const selfSyms = /* @__PURE__ */ new Set([
33017
+ ...node.exports ?? [],
33018
+ ...node.defines ?? []
33019
+ ]);
33020
+ const importedSyms = new Set(node.imported_names ?? []);
33021
+ const myImports = importTargets.get(node.id);
33022
+ const localSeen = /* @__PURE__ */ new Set();
33023
+ for (const callee of calls) {
33024
+ if (!BARE_IDENT.test(callee)) continue;
33025
+ if (localSeen.has(callee)) continue;
33026
+ localSeen.add(callee);
33027
+ const key = `${node.id}\u2192${callee}`;
33028
+ if (selfSyms.has(callee)) {
33029
+ if (seen.has(key)) continue;
33030
+ seen.add(key);
33031
+ crossRefs.push({
33032
+ source: node.id,
33033
+ target: callee,
33034
+ type: "calls",
33035
+ layer: nodeLayer.get(node.id) ?? "ui",
33036
+ defined_in: node.id
33037
+ });
33038
+ resolvedSelf++;
33039
+ continue;
33040
+ }
33041
+ if (!importedSyms.has(callee)) {
33042
+ dropped++;
33043
+ continue;
33044
+ }
33045
+ const defs = (definers.get(callee) ?? []).filter((d) => d.nodeId !== node.id);
33046
+ if (defs.length === 0) {
33047
+ dropped++;
33048
+ continue;
33049
+ }
33050
+ const fromImported = myImports ? defs.filter((d) => myImports.has(d.nodeId)) : [];
33051
+ const chosen = fromImported.length > 0 ? fromImported : defs;
33052
+ const owner = chosen[0];
33053
+ const isAmbiguous = chosen.length > 1;
33054
+ if (isAmbiguous) ambiguous++;
33055
+ if (seen.has(key)) continue;
33056
+ seen.add(key);
33057
+ const ref = {
33058
+ source: node.id,
33059
+ target: callee,
33060
+ type: "calls",
33061
+ layer: owner.layer,
33062
+ defined_in: owner.nodeId
33063
+ };
33064
+ if (isAmbiguous) ref.ambiguous = true;
33065
+ crossRefs.push(ref);
33066
+ resolvedImport++;
33067
+ }
33068
+ }
33069
+ }
33070
+ crossRefs.sort(
33071
+ (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target)
33072
+ );
33073
+ return {
33074
+ cross_refs: crossRefs,
33075
+ flagged_edges: [],
33076
+ warnings: [],
33077
+ patterns: {
33078
+ call_resolution: {
33079
+ resolved_self: resolvedSelf,
33080
+ resolved_import: resolvedImport,
33081
+ ambiguous,
33082
+ dropped,
33083
+ symbols_defined: definers.size
33084
+ }
33085
+ }
33086
+ };
33087
+ }
33088
+ };
33089
+
32822
33090
  // src/server/graph/core/parser-registry.ts
32823
33091
  function isMultiLayerParser(p) {
32824
33092
  return "layers" in p && Array.isArray(p.layers);
@@ -32885,7 +33153,8 @@ function registerBuiltins2(registry, disabled) {
32885
33153
  apiAnnotationsParser,
32886
33154
  urlLiteralScannerParser,
32887
33155
  staticRefScannerParser,
32888
- middlewareGatesParser
33156
+ middlewareGatesParser,
33157
+ callResolverParser
32889
33158
  ];
32890
33159
  for (const parser of builtins) {
32891
33160
  if (disabled.has(parser.id)) continue;
@@ -35889,6 +36158,10 @@ function handleWhoUses(args) {
35889
36158
  };
35890
36159
  const via = cr.via;
35891
36160
  if (Array.isArray(via)) entry.via = via;
36161
+ const definedIn = cr.defined_in;
36162
+ if (typeof definedIn === "string") entry.defined_in = definedIn;
36163
+ const ambiguous = cr.ambiguous;
36164
+ if (ambiguous) entry.ambiguous = true;
35892
36165
  hits.push(entry);
35893
36166
  }
35894
36167
  }
@@ -36556,7 +36829,7 @@ var McpSession = class {
36556
36829
  jsonrpc: "2.0",
36557
36830
  id: this.callId++,
36558
36831
  method: "initialize",
36559
- params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "launchpod", version: "0.0.1" } }
36832
+ params: { protocolVersion: "2025-03-26", capabilities: {}, clientInfo: { name: "launch-sequencer", version: "0.0.1" } }
36560
36833
  });
36561
36834
  const initResp = await httpRequest2(this.mcpUrl, {
36562
36835
  method: "POST",
@@ -36710,7 +36983,7 @@ async function validateRepoUrl(serverUrl, token) {
36710
36983
  console.error("Error: Repository URL mismatch");
36711
36984
  console.error(` Project expects: ${remoteRepoUrl}`);
36712
36985
  console.error(` Local origin: ${localOrigin}`);
36713
- console.error("Run launchpod from the correct repository.");
36986
+ console.error("Run launch-sequencer from the correct repository.");
36714
36987
  process.exit(1);
36715
36988
  }
36716
36989
  }
@@ -36999,6 +37272,14 @@ if (parsedArgs.subcommand === "graph:generate" || parsedArgs.subcommand === "gra
36999
37272
  );
37000
37273
  process.exit(result.status ?? 1);
37001
37274
  }
37275
+ if (parsedArgs.subcommand === "radar:teardown") {
37276
+ const result = (0, import_child_process4.spawnSync)(
37277
+ process.execPath,
37278
+ [import_path9.default.join(__dirname, "radar-teardown-entry.js"), ...process.argv.slice(3)],
37279
+ { stdio: "inherit" }
37280
+ );
37281
+ process.exit(result.status ?? 1);
37282
+ }
37002
37283
  if (parsedArgs.subcommand === "mcp:graph") {
37003
37284
  startGraphMcpServer();
37004
37285
  }
@@ -37458,18 +37739,23 @@ if (!__isMcpMode) {
37458
37739
  console.log(`LaunchPod radar \xB7 ${localUrl}`);
37459
37740
  console.log(` cloud: ${cfg.serverUrl} \xB7 source: ${cfg.source}`);
37460
37741
  console.log(` org: ${cfg.orgSlug} \xB7 project: ${cfg.projectSlug}`);
37461
- radar = new Radar({
37462
- projectRoot: PROJECT_DIR,
37463
- localPort: PORT,
37464
- localUrl,
37465
- serverUrl: cfg.serverUrl,
37466
- pat: cfg.pat,
37467
- orgSlug: cfg.orgSlug,
37468
- projectSlug: cfg.projectSlug
37469
- });
37742
+ try {
37743
+ radar = new Radar({
37744
+ projectRoot: PROJECT_DIR,
37745
+ localPort: PORT,
37746
+ localUrl,
37747
+ serverUrl: cfg.serverUrl,
37748
+ pat: cfg.pat,
37749
+ orgSlug: cfg.orgSlug,
37750
+ projectSlug: cfg.projectSlug
37751
+ });
37752
+ } catch (err2) {
37753
+ console.error(`${err2 instanceof Error ? err2.message : String(err2)}`);
37754
+ process.exit(1);
37755
+ }
37470
37756
  radar.attachWebSocketServer(server, "/api/radar/stream");
37471
37757
  radar.start();
37472
- initLaunchPodTerminalBridge(server, PROJECT_DIR);
37758
+ initSequencerTerminalBridge(server, PROJECT_DIR);
37473
37759
  return;
37474
37760
  }
37475
37761
  if (token) {
@@ -37518,11 +37804,11 @@ if (!__isMcpMode) {
37518
37804
  activeCapabilities = await fetchCapabilities(existingCreds.serverUrl, existingCreds.token);
37519
37805
  }
37520
37806
  if (activeCapabilities.terminal) {
37521
- initLaunchPodTerminalBridge(server, PROJECT_DIR);
37807
+ initSequencerTerminalBridge(server, PROJECT_DIR);
37522
37808
  }
37523
37809
  startPipelineSystem(existingCreds, activeCapabilities);
37524
37810
  } else if (activeCapabilities.terminal) {
37525
- initLaunchPodTerminalBridge(server, PROJECT_DIR);
37811
+ initSequencerTerminalBridge(server, PROJECT_DIR);
37526
37812
  }
37527
37813
  }
37528
37814
  main().catch((err2) => {
@@ -214,7 +214,7 @@ Usage:
214
214
  launch-course rm <name>
215
215
 
216
216
  The cred file (.launch-secure.cred.config) holds every course under \`profiles\`,
217
- with \`active\` selecting which one drives MCP / launch-pod auth. Legacy flat
217
+ with \`active\` selecting which one drives MCP / launch-sequencer auth. Legacy flat
218
218
  files are auto-migrated on the first multi-profile op. \`launch-course set\`
219
219
  also rewrites .mcp.json's launch-secure.url so Claude Code routes to the right
220
220
  server on next MCP reconnect.