@launchsecure/launch-kit 0.0.39 → 0.0.40

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 (84) hide show
  1. package/dist/chart-client/assets/index-CWJFFDPu.css +1 -0
  2. package/dist/chart-client/index.html +2 -2
  3. package/dist/client/assets/index-CTzFcfGV.css +32 -0
  4. package/dist/client/index.html +2 -2
  5. package/dist/council-client/assets/index-ArgRc5mN.css +1 -0
  6. package/dist/council-client/index.html +2 -2
  7. package/dist/deck-client/assets/{_baseUniq-DOrnEQMI.js → _baseUniq-BZP7n41F.js} +1 -1
  8. package/dist/deck-client/assets/{arc-DOWK7V3m.js → arc-31biU3Az.js} +1 -1
  9. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-DPhzvk7q.js → architectureDiagram-Q4EWVU46-DHg6Ss--.js} +1 -1
  10. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-CwAGy9lU.js → blockDiagram-DXYQGD6D-CUdblaWk.js} +1 -1
  11. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-L_g_SS21.js → c4Diagram-AHTNJAMY-MfAO5lak.js} +1 -1
  12. package/dist/deck-client/assets/channel-BBkRLdnC.js +1 -0
  13. package/dist/deck-client/assets/{chunk-4BX2VUAB-RKm0LXpu.js → chunk-4BX2VUAB-DQ1MrGgN.js} +1 -1
  14. package/dist/deck-client/assets/{chunk-4TB4RGXK-Bk0FUbxU.js → chunk-4TB4RGXK-BUJtZ7jO.js} +1 -1
  15. package/dist/deck-client/assets/{chunk-55IACEB6-Cl3hja-M.js → chunk-55IACEB6-BdSnXB6g.js} +1 -1
  16. package/dist/deck-client/assets/{chunk-EDXVE4YY-CNIMQCV2.js → chunk-EDXVE4YY-94yZIUI8.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-FMBD7UC4-DqOvWr1k.js → chunk-FMBD7UC4-PnZ9v6ey.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-OYMX7WX6-1Kd7yK5u.js → chunk-OYMX7WX6-DXrWNOsV.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-QZHKN3VN-6_kraYpP.js → chunk-QZHKN3VN-CsIGIDKX.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-YZCP3GAM-FgAwIWlo.js → chunk-YZCP3GAM-DVkBO9tn.js} +1 -1
  21. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-DFCaeF-7.js +1 -0
  22. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-DFCaeF-7.js +1 -0
  23. package/dist/deck-client/assets/clone-GCEVRScB.js +1 -0
  24. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CigVnnPr.js → cose-bilkent-S5V4N54A-m126Oh3b.js} +1 -1
  25. package/dist/deck-client/assets/{dagre-KV5264BT-DHZXTktX.js → dagre-KV5264BT-C2aig8U5.js} +1 -1
  26. package/dist/deck-client/assets/{diagram-5BDNPKRD-H5k0eauU.js → diagram-5BDNPKRD-CKpoRfGn.js} +1 -1
  27. package/dist/deck-client/assets/{diagram-G4DWMVQ6-Bg3dFhSY.js → diagram-G4DWMVQ6-Cjh115Ep.js} +1 -1
  28. package/dist/deck-client/assets/{diagram-MMDJMWI5-CQLC410N.js → diagram-MMDJMWI5-DKlBv_2L.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-TYMM5635-DFTCHVkP.js → diagram-TYMM5635-CdBh4cEn.js} +1 -1
  30. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-aiv9GZnL.js → erDiagram-SMLLAGMA-56pn_93p.js} +1 -1
  31. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-C6Fhvtsy.js → flowDiagram-DWJPFMVM-BtV3M5xJ.js} +1 -1
  32. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DSaGMPM4.js → ganttDiagram-T4ZO3ILL-DTIsC6Zg.js} +1 -1
  33. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-DMjL2Vix.js → gitGraphDiagram-UUTBAWPF-CJYeyCLe.js} +1 -1
  34. package/dist/deck-client/assets/{graph-B7Vn5lkK.js → graph-BDvMu1Ss.js} +1 -1
  35. package/dist/deck-client/assets/index-D4eSxcBn.css +1 -0
  36. package/dist/deck-client/assets/{index-BD36e-tD.js → index-QnGVE9PZ.js} +72 -72
  37. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-mNi4iygG.js → infoDiagram-42DDH7IO-BWyKJnpW.js} +1 -1
  38. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BwCUmUVt.js → ishikawaDiagram-UXIWVN3A-DXYkdO3T.js} +1 -1
  39. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-C6qoqJmJ.js → journeyDiagram-VCZTEJTY-C2zBr-J5.js} +1 -1
  40. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-Dz1Tt3sA.js → kanban-definition-6JOO6SKY-CdoYLS4Z.js} +1 -1
  41. package/dist/deck-client/assets/{layout-CZTyRhOG.js → layout-vOnxnCQU.js} +1 -1
  42. package/dist/deck-client/assets/{linear--7n7iEvd.js → linear-B0J0WCGz.js} +1 -1
  43. package/dist/deck-client/assets/{min-Bh130DN8.js → min-B0AXlT9L.js} +1 -1
  44. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CfXcK1qH.js → mindmap-definition-QFDTVHPH-oAybLedr.js} +1 -1
  45. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DjVHLAVw.js → pieDiagram-DEJITSTG-BjHyHxGk.js} +1 -1
  46. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-CXwvZ1i1.js → quadrantDiagram-34T5L4WZ-dtluDZXs.js} +1 -1
  47. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-Cl6xm0fR.js → requirementDiagram-MS252O5E-Cq8l7bOl.js} +1 -1
  48. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BOH9sLyh.js → sankeyDiagram-XADWPNL6-C1Vih91z.js} +1 -1
  49. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-BC1MYBn6.js → sequenceDiagram-FGHM5R23-CYkd7oQK.js} +1 -1
  50. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-kNp9bv8K.js → stateDiagram-FHFEXIEX-CtyG8wBK.js} +1 -1
  51. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BLyKWfcN.js +1 -0
  52. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DKnITsD4.js → timeline-definition-GMOUNBTQ-DZIxSyd1.js} +1 -1
  53. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-BdajXRrh.js → vennDiagram-DHZGUBPP-Ct4JVRDM.js} +1 -1
  54. package/dist/deck-client/assets/wardley-RL74JXVD-V29ycxOW.js +162 -0
  55. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-B2hDCDl2.js → wardleyDiagram-NUSXRM2D-D-Ua6Cmi.js} +1 -1
  56. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CvnYFs51.js → xychartDiagram-5P7HB3ND-BPCOuRVl.js} +1 -1
  57. package/dist/deck-client/index.html +2 -2
  58. package/dist/server/beacon-monitor-entry.js +106 -24
  59. package/dist/server/chart-serve.js +544 -247
  60. package/dist/server/cli.js +743 -324
  61. package/dist/server/council-entry.js +16 -3
  62. package/dist/server/council-serve.js +15 -2
  63. package/dist/server/deck-mcp-entry.js +267 -107
  64. package/dist/server/deck-serve.js +98 -22
  65. package/dist/server/graph-mcp-entry.js +866 -357
  66. package/dist/server/orbit-entry.js +91 -7
  67. package/dist/server/recall-entry.js +94 -12
  68. package/dist/server/rover-entry.js +1 -1
  69. package/package.json +1 -1
  70. package/scaffolds/statusline/statusline-mcp.sh +68 -19
  71. package/scaffolds/statusline/statusline-wrapper.sh +12 -9
  72. package/dist/chart-client/assets/index-ysGpLeOW.css +0 -1
  73. package/dist/client/assets/index-CMN3tlGP.css +0 -32
  74. package/dist/council-client/assets/index-ChmNX6bZ.css +0 -1
  75. package/dist/deck-client/assets/channel-DqiACUUq.js +0 -1
  76. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-D23cq2C3.js +0 -1
  77. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-D23cq2C3.js +0 -1
  78. package/dist/deck-client/assets/clone-C7jSigGq.js +0 -1
  79. package/dist/deck-client/assets/index-CGbNOpk9.css +0 -1
  80. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-hRsAFc2t.js +0 -1
  81. package/dist/deck-client/assets/wardley-RL74JXVD-BL802-su.js +0 -162
  82. /package/dist/chart-client/assets/{index-BlsuXuQ1.js → index-Dzlj-oCj.js} +0 -0
  83. /package/dist/client/assets/{index-BA7BHBWT.js → index-tTg_ezUF.js} +0 -0
  84. /package/dist/council-client/assets/{index-jjBWyhry.js → index-B1v46vTE.js} +0 -0
@@ -128,24 +128,24 @@ var init_deck_lockfile = __esm({
128
128
 
129
129
  // src/server/deck-config.ts
130
130
  function loadDeckConfig(rootDir) {
131
- const configPath = (0, import_node_path2.join)(rootDir, CONFIG_FILENAME);
132
- if (!(0, import_node_fs2.existsSync)(configPath)) return {};
131
+ const configPath = (0, import_node_path3.join)(rootDir, CONFIG_FILENAME);
132
+ if (!(0, import_node_fs3.existsSync)(configPath)) return {};
133
133
  try {
134
- return JSON.parse((0, import_node_fs2.readFileSync)(configPath, "utf-8"));
134
+ return JSON.parse((0, import_node_fs3.readFileSync)(configPath, "utf-8"));
135
135
  } catch {
136
136
  return {};
137
137
  }
138
138
  }
139
139
  function saveDeckConfig(rootDir, config) {
140
- const configPath = (0, import_node_path2.join)(rootDir, CONFIG_FILENAME);
141
- (0, import_node_fs2.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
140
+ const configPath = (0, import_node_path3.join)(rootDir, CONFIG_FILENAME);
141
+ (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
142
142
  }
143
- var import_node_fs2, import_node_path2, CONFIG_FILENAME;
143
+ var import_node_fs3, import_node_path3, CONFIG_FILENAME;
144
144
  var init_deck_config = __esm({
145
145
  "src/server/deck-config.ts"() {
146
146
  "use strict";
147
- import_node_fs2 = require("node:fs");
148
- import_node_path2 = require("node:path");
147
+ import_node_fs3 = require("node:fs");
148
+ import_node_path3 = require("node:path");
149
149
  CONFIG_FILENAME = ".launchdeck.json";
150
150
  }
151
151
  });
@@ -1250,7 +1250,7 @@ var init_cred_shape = __esm({
1250
1250
  });
1251
1251
 
1252
1252
  // src/server/mcp-client.ts
1253
- function mcpConfigFromCourse(projectRoot) {
1253
+ function mcpConfigForProfile(projectRoot, name) {
1254
1254
  let cred;
1255
1255
  try {
1256
1256
  cred = readCredFile(projectRoot);
@@ -1260,7 +1260,7 @@ function mcpConfigFromCourse(projectRoot) {
1260
1260
  if (!cred) return null;
1261
1261
  const nested = toNested(cred);
1262
1262
  if (!nested) return null;
1263
- const profile = nested.profiles[nested.active];
1263
+ const profile = nested.profiles[name];
1264
1264
  if (!profile?.pat || !profile.serverUrl || !profile.orgSlug || !profile.projectSlug) {
1265
1265
  return null;
1266
1266
  }
@@ -1273,6 +1273,37 @@ function mcpConfigFromCourse(projectRoot) {
1273
1273
  }
1274
1274
  };
1275
1275
  }
1276
+ function mcpConfigFromCourse(projectRoot) {
1277
+ let cred;
1278
+ try {
1279
+ cred = readCredFile(projectRoot);
1280
+ } catch {
1281
+ return null;
1282
+ }
1283
+ if (!cred) return null;
1284
+ const nested = toNested(cred);
1285
+ if (!nested) return null;
1286
+ return mcpConfigForProfile(projectRoot, nested.active);
1287
+ }
1288
+ function listCourses(projectRoot) {
1289
+ let cred;
1290
+ try {
1291
+ cred = readCredFile(projectRoot);
1292
+ } catch {
1293
+ return [];
1294
+ }
1295
+ if (!cred) return [];
1296
+ const nested = toNested(cred);
1297
+ if (!nested) return [];
1298
+ return Object.entries(nested.profiles).map(([name, p]) => ({
1299
+ name,
1300
+ org: p.orgSlug ?? "",
1301
+ project: p.projectSlug ?? "",
1302
+ serverUrl: p.serverUrl ?? "",
1303
+ active: name === nested.active,
1304
+ usable: Boolean(p.pat && p.serverUrl && p.orgSlug && p.projectSlug)
1305
+ }));
1306
+ }
1276
1307
  function loadMcpConfig(projectRoot) {
1277
1308
  if (_config) return _config;
1278
1309
  const fromCourse = mcpConfigFromCourse(projectRoot);
@@ -1280,13 +1311,13 @@ function loadMcpConfig(projectRoot) {
1280
1311
  _config = fromCourse;
1281
1312
  return _config;
1282
1313
  }
1283
- const mcpPath = (0, import_node_path3.join)(projectRoot, ".mcp.json");
1284
- if (!(0, import_node_fs3.existsSync)(mcpPath)) {
1314
+ const mcpPath = (0, import_node_path4.join)(projectRoot, ".mcp.json");
1315
+ if (!(0, import_node_fs4.existsSync)(mcpPath)) {
1285
1316
  throw new Error(
1286
1317
  `No active course in ${CONFIG_FILENAME2} and no .mcp.json at ${mcpPath}. Run \`launch-course\` or bootstrap with \`launch-kit init\`.`
1287
1318
  );
1288
1319
  }
1289
- const raw = JSON.parse((0, import_node_fs3.readFileSync)(mcpPath, "utf-8"));
1320
+ const raw = JSON.parse((0, import_node_fs4.readFileSync)(mcpPath, "utf-8"));
1290
1321
  const entry = raw.mcpServers["launch-secure"];
1291
1322
  if (!entry?.url) {
1292
1323
  throw new Error(
@@ -1299,6 +1330,23 @@ function loadMcpConfig(projectRoot) {
1299
1330
  };
1300
1331
  return _config;
1301
1332
  }
1333
+ function withMcpConfig(override, fn) {
1334
+ const run = async () => {
1335
+ const prevConfig = _config;
1336
+ const prevSession = _sessionId;
1337
+ _config = override;
1338
+ _sessionId = null;
1339
+ try {
1340
+ return await fn();
1341
+ } finally {
1342
+ _config = prevConfig;
1343
+ _sessionId = prevSession;
1344
+ }
1345
+ };
1346
+ const result = _overrideLock.then(run, run);
1347
+ _overrideLock = result.then(() => void 0, () => void 0);
1348
+ return result;
1349
+ }
1302
1350
  function parseSSE(text2) {
1303
1351
  const lines = text2.split("\n");
1304
1352
  for (const line of lines) {
@@ -1406,16 +1454,17 @@ async function updateDeck(commentId, input) {
1406
1454
  async function deleteDeck(commentId) {
1407
1455
  await callTool("communication_delete", { comment_id: commentId });
1408
1456
  }
1409
- var import_node_fs3, import_node_path3, _config, _requestId, _sessionId;
1457
+ var import_node_fs4, import_node_path4, _config, _requestId, _sessionId, _overrideLock;
1410
1458
  var init_mcp_client = __esm({
1411
1459
  "src/server/mcp-client.ts"() {
1412
1460
  "use strict";
1413
- import_node_fs3 = require("node:fs");
1414
- import_node_path3 = require("node:path");
1461
+ import_node_fs4 = require("node:fs");
1462
+ import_node_path4 = require("node:path");
1415
1463
  init_cred_shape();
1416
1464
  _config = null;
1417
1465
  _requestId = 0;
1418
1466
  _sessionId = null;
1467
+ _overrideLock = Promise.resolve();
1419
1468
  }
1420
1469
  });
1421
1470
 
@@ -1430,14 +1479,14 @@ __export(deck_serve_exports, {
1430
1479
  startDeckServer: () => startDeckServer
1431
1480
  });
1432
1481
  function sessionDir(cwd, session) {
1433
- return import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1482
+ return import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1434
1483
  }
1435
1484
  function sessionJsonPath(cwd, session) {
1436
- return import_node_path4.default.join(sessionDir(cwd, session), "session.json");
1485
+ return import_node_path5.default.join(sessionDir(cwd, session), "session.json");
1437
1486
  }
1438
1487
  function readPersistedSession(cwd, session) {
1439
1488
  try {
1440
- const raw = import_node_fs4.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1489
+ const raw = import_node_fs5.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1441
1490
  return JSON.parse(raw);
1442
1491
  } catch {
1443
1492
  return null;
@@ -1445,7 +1494,7 @@ function readPersistedSession(cwd, session) {
1445
1494
  }
1446
1495
  function writePersistedSession(cwd, session, payload) {
1447
1496
  const dir = sessionDir(cwd, session);
1448
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1497
+ import_node_fs5.default.mkdirSync(dir, { recursive: true });
1449
1498
  const existing = readPersistedSession(cwd, session);
1450
1499
  const now = (/* @__PURE__ */ new Date()).toISOString();
1451
1500
  const next = {
@@ -1457,13 +1506,13 @@ function writePersistedSession(cwd, session, payload) {
1457
1506
  createdAt: existing?.createdAt ?? now,
1458
1507
  updatedAt: now
1459
1508
  };
1460
- import_node_fs4.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1509
+ import_node_fs5.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1461
1510
  return next;
1462
1511
  }
1463
1512
  function listPersistedSessions(cwd) {
1464
- const base = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1465
- if (!import_node_fs4.default.existsSync(base)) return [];
1466
- const entries = import_node_fs4.default.readdirSync(base, { withFileTypes: true });
1513
+ const base = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1514
+ if (!import_node_fs5.default.existsSync(base)) return [];
1515
+ const entries = import_node_fs5.default.readdirSync(base, { withFileTypes: true });
1467
1516
  const sessions = [];
1468
1517
  for (const entry of entries) {
1469
1518
  if (!entry.isDirectory()) continue;
@@ -1485,26 +1534,26 @@ function listPersistedSessions(cwd) {
1485
1534
  return sessions;
1486
1535
  }
1487
1536
  function deletePersistedSession(cwd, session) {
1488
- const base = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1537
+ const base = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1489
1538
  if (session === "all") {
1490
- if (import_node_fs4.default.existsSync(base)) import_node_fs4.default.rmSync(base, { recursive: true, force: true });
1539
+ if (import_node_fs5.default.existsSync(base)) import_node_fs5.default.rmSync(base, { recursive: true, force: true });
1491
1540
  return;
1492
1541
  }
1493
1542
  const dir = sessionDir(cwd, session);
1494
- if (import_node_fs4.default.existsSync(dir)) import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
1543
+ if (import_node_fs5.default.existsSync(dir)) import_node_fs5.default.rmSync(dir, { recursive: true, force: true });
1495
1544
  }
1496
1545
  function syncJsonPath(cwd, session) {
1497
- return import_node_path4.default.join(sessionDir(cwd, session), ".sync.json");
1546
+ return import_node_path5.default.join(sessionDir(cwd, session), ".sync.json");
1498
1547
  }
1499
1548
  function readSyncRecord(cwd, session) {
1500
1549
  try {
1501
- return JSON.parse(import_node_fs4.default.readFileSync(syncJsonPath(cwd, session), "utf-8"));
1550
+ return JSON.parse(import_node_fs5.default.readFileSync(syncJsonPath(cwd, session), "utf-8"));
1502
1551
  } catch {
1503
1552
  return null;
1504
1553
  }
1505
1554
  }
1506
1555
  function writeSyncRecord(cwd, session, rec) {
1507
- import_node_fs4.default.writeFileSync(syncJsonPath(cwd, session), JSON.stringify(rec, null, 2));
1556
+ import_node_fs5.default.writeFileSync(syncJsonPath(cwd, session), JSON.stringify(rec, null, 2));
1508
1557
  }
1509
1558
  function escHtml2(s) {
1510
1559
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1517,7 +1566,7 @@ function readIframeArtifact(cwd, src) {
1517
1566
  const rel = decodeURIComponent(src.slice("/deck-files/".length));
1518
1567
  if (rel.includes("..")) return null;
1519
1568
  try {
1520
- return import_node_fs4.default.readFileSync(import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", rel), "utf-8");
1569
+ return import_node_fs5.default.readFileSync(import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", rel), "utf-8");
1521
1570
  } catch {
1522
1571
  return null;
1523
1572
  }
@@ -1614,23 +1663,23 @@ function broadcastToClients(message) {
1614
1663
  }
1615
1664
  }
1616
1665
  function serveStatic(res, filePath) {
1617
- if (!import_node_fs4.default.existsSync(filePath) || !import_node_fs4.default.statSync(filePath).isFile()) return false;
1618
- const ext = import_node_path4.default.extname(filePath).toLowerCase();
1666
+ if (!import_node_fs5.default.existsSync(filePath) || !import_node_fs5.default.statSync(filePath).isFile()) return false;
1667
+ const ext = import_node_path5.default.extname(filePath).toLowerCase();
1619
1668
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
1620
1669
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
1621
- import_node_fs4.default.createReadStream(filePath).pipe(res);
1670
+ import_node_fs5.default.createReadStream(filePath).pipe(res);
1622
1671
  return true;
1623
1672
  }
1624
1673
  function resolveClientDir() {
1625
- const sibling = import_node_path4.default.join(__dirname, "..", "deck-client");
1626
- if (import_node_fs4.default.existsSync(import_node_path4.default.join(sibling, "assets"))) return sibling;
1627
- const devDist = import_node_path4.default.join(__dirname, "..", "..", "dist", "deck-client");
1628
- if (import_node_fs4.default.existsSync(import_node_path4.default.join(devDist, "assets"))) return devDist;
1674
+ const sibling = import_node_path5.default.join(__dirname, "..", "deck-client");
1675
+ if (import_node_fs5.default.existsSync(import_node_path5.default.join(sibling, "assets"))) return sibling;
1676
+ const devDist = import_node_path5.default.join(__dirname, "..", "..", "dist", "deck-client");
1677
+ if (import_node_fs5.default.existsSync(import_node_path5.default.join(devDist, "assets"))) return devDist;
1629
1678
  return sibling;
1630
1679
  }
1631
1680
  function serveIndex(res, clientDir) {
1632
- const indexPath = import_node_path4.default.join(clientDir, "index.html");
1633
- if (!import_node_fs4.default.existsSync(indexPath)) {
1681
+ const indexPath = import_node_path5.default.join(clientDir, "index.html");
1682
+ if (!import_node_fs5.default.existsSync(indexPath)) {
1634
1683
  res.writeHead(500, { "Content-Type": "text/plain" });
1635
1684
  res.end(`LaunchDeck client bundle not found at ${clientDir}. Run 'npm run build:client'.`);
1636
1685
  return;
@@ -1713,6 +1762,10 @@ async function startDeckServer(opts = {}) {
1713
1762
  jsonResponse(res, 200, { config: cfg });
1714
1763
  return;
1715
1764
  }
1765
+ if (req.method === "GET" && url2.pathname === "/api/deck/courses") {
1766
+ jsonResponse(res, 200, { courses: listCourses(cwd) });
1767
+ return;
1768
+ }
1716
1769
  if (req.method === "POST" && url2.pathname === "/api/deck/config") {
1717
1770
  try {
1718
1771
  const body = JSON.parse(await readBody(req));
@@ -1734,15 +1787,15 @@ async function startDeckServer(opts = {}) {
1734
1787
  const baseUrl = `/deck-files/${sessionEncoded}`;
1735
1788
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
1736
1789
  const report = generateBlastRadiusReport(block.manifest);
1737
- const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1738
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1739
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius.html"), html);
1740
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius-report.md"), report);
1741
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
1790
+ const dir = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1791
+ import_node_fs5.default.mkdirSync(dir, { recursive: true });
1792
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, "blast-radius.html"), html);
1793
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, "blast-radius-report.md"), report);
1794
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
1742
1795
  const contractData = generateContract(block.manifest);
1743
1796
  const contractMd = contractToMarkdown(contractData);
1744
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "contract.json"), JSON.stringify(contractData, null, 2));
1745
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "contract.md"), contractMd);
1797
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, "contract.json"), JSON.stringify(contractData, null, 2));
1798
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, "contract.md"), contractMd);
1746
1799
  block.type = "iframe";
1747
1800
  block.src = `${baseUrl}/blast-radius.html`;
1748
1801
  delete block.manifest;
@@ -1769,9 +1822,9 @@ async function startDeckServer(opts = {}) {
1769
1822
  const sessionEncoded = encodeURIComponent(body.session);
1770
1823
  const baseUrl = `/deck-files/${sessionEncoded}`;
1771
1824
  const filename = `${slugify(block.label)}.html`;
1772
- const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1773
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1774
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, filename), html);
1825
+ const dir = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1826
+ import_node_fs5.default.mkdirSync(dir, { recursive: true });
1827
+ import_node_fs5.default.writeFileSync(import_node_path5.default.join(dir, filename), html);
1775
1828
  block.type = "iframe";
1776
1829
  block.src = `${baseUrl}/${filename}`;
1777
1830
  delete block.content;
@@ -1811,14 +1864,34 @@ async function startDeckServer(opts = {}) {
1811
1864
  jsonResponse(res, 404, { ok: false, error: "session not found" });
1812
1865
  return;
1813
1866
  }
1867
+ let course;
1814
1868
  try {
1815
- loadMcpConfig(cwd);
1816
- } catch (err) {
1817
- jsonResponse(res, 400, {
1818
- ok: false,
1819
- error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}`
1820
- });
1821
- return;
1869
+ const raw = await readBody(req);
1870
+ if (raw.trim()) {
1871
+ course = JSON.parse(raw).course?.trim() || void 0;
1872
+ }
1873
+ } catch {
1874
+ }
1875
+ let override = null;
1876
+ if (course) {
1877
+ override = mcpConfigForProfile(cwd, course);
1878
+ if (!override) {
1879
+ jsonResponse(res, 400, {
1880
+ ok: false,
1881
+ error: `Course "${course}" is not a usable profile in .launch-secure.cred.config (missing pat/serverUrl/orgSlug/projectSlug?).`
1882
+ });
1883
+ return;
1884
+ }
1885
+ } else {
1886
+ try {
1887
+ loadMcpConfig(cwd);
1888
+ } catch (err) {
1889
+ jsonResponse(res, 400, {
1890
+ ok: false,
1891
+ error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}`
1892
+ });
1893
+ return;
1894
+ }
1822
1895
  }
1823
1896
  try {
1824
1897
  const blocks = Array.isArray(persisted.blocks) ? persisted.blocks : [];
@@ -1827,24 +1900,27 @@ async function startDeckServer(opts = {}) {
1827
1900
  const body = `Shared deck "${id}" \u2014 ${blocks.length} block${blocks.length === 1 ? "" : "s"}${types.length ? ` (${types.join(", ")})` : ""}. Open the Decks tab in the Communication Center to view.`;
1828
1901
  const input = { title: id, body, html, sessionId: id, blockCount: blocks.length };
1829
1902
  const existing2 = readSyncRecord(cwd, id);
1830
- let resourceId;
1831
- let updated = false;
1832
- if (existing2?.resourceId) {
1833
- try {
1834
- ({ id: resourceId } = await updateDeck(existing2.resourceId, input));
1835
- updated = true;
1836
- } catch {
1837
- ({ id: resourceId } = await writeDeck(input));
1903
+ const doShare = async () => {
1904
+ if (existing2?.resourceId) {
1905
+ try {
1906
+ const { id: rid2 } = await updateDeck(existing2.resourceId, input);
1907
+ return { resourceId: rid2, updated: true };
1908
+ } catch {
1909
+ const { id: rid2 } = await writeDeck(input);
1910
+ return { resourceId: rid2, updated: false };
1911
+ }
1838
1912
  }
1839
- } else {
1840
- ({ id: resourceId } = await writeDeck(input));
1841
- }
1913
+ const { id: rid } = await writeDeck(input);
1914
+ return { resourceId: rid, updated: false };
1915
+ };
1916
+ const { resourceId, updated } = override ? await withMcpConfig(override, doShare) : await doShare();
1842
1917
  writeSyncRecord(cwd, id, {
1843
1918
  resourceId,
1844
1919
  sharedAt: (/* @__PURE__ */ new Date()).toISOString(),
1845
- version: persisted.version
1920
+ version: persisted.version,
1921
+ course
1846
1922
  });
1847
- jsonResponse(res, 200, { ok: true, resourceId, updated });
1923
+ jsonResponse(res, 200, { ok: true, resourceId, updated, course });
1848
1924
  } catch (err) {
1849
1925
  jsonResponse(res, 500, { ok: false, error: String(err) });
1850
1926
  }
@@ -1867,7 +1943,7 @@ async function startDeckServer(opts = {}) {
1867
1943
  try {
1868
1944
  await deleteDeck(sync.resourceId);
1869
1945
  try {
1870
- import_node_fs4.default.rmSync(syncJsonPath(cwd, id), { force: true });
1946
+ import_node_fs5.default.rmSync(syncJsonPath(cwd, id), { force: true });
1871
1947
  } catch {
1872
1948
  }
1873
1949
  jsonResponse(res, 200, { ok: true, deleted: true });
@@ -1978,7 +2054,7 @@ async function startDeckServer(opts = {}) {
1978
2054
  res.end("Forbidden");
1979
2055
  return;
1980
2056
  }
1981
- const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
2057
+ const filePath = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1982
2058
  if (serveStatic(res, filePath)) return;
1983
2059
  res.writeHead(404);
1984
2060
  res.end("Not found");
@@ -1991,25 +2067,25 @@ async function startDeckServer(opts = {}) {
1991
2067
  res.end("Forbidden");
1992
2068
  return;
1993
2069
  }
1994
- const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1995
- if (!import_node_fs4.default.existsSync(filePath) || !import_node_fs4.default.statSync(filePath).isFile()) {
2070
+ const filePath = import_node_path5.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
2071
+ if (!import_node_fs5.default.existsSync(filePath) || !import_node_fs5.default.statSync(filePath).isFile()) {
1996
2072
  res.writeHead(404);
1997
2073
  res.end("Not found");
1998
2074
  return;
1999
2075
  }
2000
- const ext = import_node_path4.default.extname(filePath).toLowerCase();
2076
+ const ext = import_node_path5.default.extname(filePath).toLowerCase();
2001
2077
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
2002
- const filename = import_node_path4.default.basename(filePath);
2078
+ const filename = import_node_path5.default.basename(filePath);
2003
2079
  res.writeHead(200, {
2004
2080
  "Content-Type": mime,
2005
2081
  "Content-Disposition": `attachment; filename="${filename}"`,
2006
2082
  "Cache-Control": "no-cache"
2007
2083
  });
2008
- import_node_fs4.default.createReadStream(filePath).pipe(res);
2084
+ import_node_fs5.default.createReadStream(filePath).pipe(res);
2009
2085
  return;
2010
2086
  }
2011
2087
  if (url2.pathname !== "/") {
2012
- const staticPath = import_node_path4.default.join(clientDir, url2.pathname);
2088
+ const staticPath = import_node_path5.default.join(clientDir, url2.pathname);
2013
2089
  if (serveStatic(res, staticPath)) return;
2014
2090
  }
2015
2091
  serveIndex(res, clientDir);
@@ -2108,13 +2184,13 @@ function runServeCli(argv) {
2108
2184
  process.exit(1);
2109
2185
  });
2110
2186
  }
2111
- var import_node_http, import_node_fs4, import_node_path4, import_ws, DEFAULT_PORT, MAX_PORT_SCAN, MIME_TYPES, pendingFeedback, lastRenderError, wss;
2187
+ var import_node_http, import_node_fs5, import_node_path5, import_ws, DEFAULT_PORT, MAX_PORT_SCAN, MIME_TYPES, pendingFeedback, lastRenderError, wss;
2112
2188
  var init_deck_serve = __esm({
2113
2189
  "src/server/deck-serve.ts"() {
2114
2190
  "use strict";
2115
2191
  import_node_http = __toESM(require("node:http"));
2116
- import_node_fs4 = __toESM(require("node:fs"));
2117
- import_node_path4 = __toESM(require("node:path"));
2192
+ import_node_fs5 = __toESM(require("node:fs"));
2193
+ import_node_path5 = __toESM(require("node:path"));
2118
2194
  import_ws = require("ws");
2119
2195
  init_launch_kit_paths();
2120
2196
  init_deck_lockfile();
@@ -2208,15 +2284,15 @@ async function handleTool(name, args) {
2208
2284
  const baseUrl = `/deck-files/${sessionEncoded}`;
2209
2285
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
2210
2286
  const report = generateBlastRadiusReport(block.manifest);
2211
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2212
- import_node_fs5.default.mkdirSync(dir, { recursive: true });
2213
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "blast-radius.html"), html);
2214
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "blast-radius-report.md"), report);
2215
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
2287
+ const dir = (0, import_node_path6.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2288
+ import_node_fs6.default.mkdirSync(dir, { recursive: true });
2289
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "blast-radius.html"), html);
2290
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "blast-radius-report.md"), report);
2291
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
2216
2292
  const contractData = generateContract(block.manifest);
2217
2293
  const contractMd = contractToMarkdown(contractData);
2218
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.json"), JSON.stringify(contractData, null, 2));
2219
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.md"), contractMd);
2294
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "contract.json"), JSON.stringify(contractData, null, 2));
2295
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "contract.md"), contractMd);
2220
2296
  block.type = "iframe";
2221
2297
  block.src = `${baseUrl}/blast-radius.html`;
2222
2298
  delete block.manifest;
@@ -2243,9 +2319,9 @@ async function handleTool(name, args) {
2243
2319
  const sessionEncoded = encodeURIComponent(session);
2244
2320
  const baseUrl = `/deck-files/${sessionEncoded}`;
2245
2321
  const filename = `${slugify(block.label)}.html`;
2246
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2247
- import_node_fs5.default.mkdirSync(dir, { recursive: true });
2248
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, filename), html);
2322
+ const dir = (0, import_node_path6.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2323
+ import_node_fs6.default.mkdirSync(dir, { recursive: true });
2324
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, filename), html);
2249
2325
  block.type = "iframe";
2250
2326
  block.src = `${baseUrl}/${filename}`;
2251
2327
  delete block.content;
@@ -2339,11 +2415,11 @@ async function handleTool(name, args) {
2339
2415
  }));
2340
2416
  }
2341
2417
  try {
2342
- const logDir = (0, import_node_path5.join)((0, import_node_os2.homedir)(), LAUNCHSECURE_DIR);
2343
- (0, import_node_fs6.mkdirSync)(logDir, { recursive: true });
2344
- const logPath = (0, import_node_path5.join)(logDir, "launch-deck.log");
2345
- const out = (0, import_node_fs6.openSync)(logPath, "a");
2346
- const err = (0, import_node_fs6.openSync)(logPath, "a");
2418
+ const logDir = (0, import_node_path6.join)((0, import_node_os3.homedir)(), LAUNCHSECURE_DIR);
2419
+ (0, import_node_fs7.mkdirSync)(logDir, { recursive: true });
2420
+ const logPath = (0, import_node_path6.join)(logDir, "launch-deck.log");
2421
+ const out = (0, import_node_fs7.openSync)(logPath, "a");
2422
+ const err = (0, import_node_fs7.openSync)(logPath, "a");
2347
2423
  const entryPath = process.argv[1];
2348
2424
  const config = loadDeckConfig(projectRoot);
2349
2425
  const resolvedPort = args.port ?? config.port;
@@ -2399,21 +2475,21 @@ async function handleTool(name, args) {
2399
2475
  }
2400
2476
  case "generate_contract": {
2401
2477
  const session = args.session;
2402
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2403
- const manifestPath = (0, import_node_path5.join)(dir, "blast-radius-manifest.json");
2404
- if (!import_node_fs5.default.existsSync(manifestPath)) {
2478
+ const dir = (0, import_node_path6.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2479
+ const manifestPath = (0, import_node_path6.join)(dir, "blast-radius-manifest.json");
2480
+ if (!import_node_fs6.default.existsSync(manifestPath)) {
2405
2481
  return text(JSON.stringify({
2406
2482
  error: `No blast radius manifest found for session "${session}". Push a blast-radius block first.`,
2407
2483
  hint: "Use the deck tool with a blast-radius block type to create a session."
2408
2484
  }));
2409
2485
  }
2410
2486
  try {
2411
- const raw = import_node_fs5.default.readFileSync(manifestPath, "utf-8");
2487
+ const raw = import_node_fs6.default.readFileSync(manifestPath, "utf-8");
2412
2488
  const manifest = JSON.parse(raw);
2413
2489
  const contract = generateContract(manifest);
2414
2490
  const markdown = contractToMarkdown(contract);
2415
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.json"), JSON.stringify(contract, null, 2));
2416
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.md"), markdown);
2491
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "contract.json"), JSON.stringify(contract, null, 2));
2492
+ import_node_fs6.default.writeFileSync((0, import_node_path6.join)(dir, "contract.md"), markdown);
2417
2493
  return text(JSON.stringify(contract));
2418
2494
  } catch (err) {
2419
2495
  return text(JSON.stringify({ error: `Failed to generate contract: ${err}` }));
@@ -2504,16 +2580,16 @@ function startDeckMcpServer() {
2504
2580
  process.exit(0);
2505
2581
  });
2506
2582
  }
2507
- var import_node_http2, import_node_fs5, import_node_path5, import_node_child_process2, import_node_fs6, import_node_os2, SERVER_INFO, TOOLS;
2583
+ var import_node_http2, import_node_fs6, import_node_path6, import_node_child_process2, import_node_fs7, import_node_os3, SERVER_INFO, TOOLS;
2508
2584
  var init_deck_mcp = __esm({
2509
2585
  "src/server/deck-mcp.ts"() {
2510
2586
  "use strict";
2511
2587
  import_node_http2 = __toESM(require("node:http"));
2512
- import_node_fs5 = __toESM(require("node:fs"));
2513
- import_node_path5 = require("node:path");
2588
+ import_node_fs6 = __toESM(require("node:fs"));
2589
+ import_node_path6 = require("node:path");
2514
2590
  import_node_child_process2 = require("node:child_process");
2515
- import_node_fs6 = require("node:fs");
2516
- import_node_os2 = require("node:os");
2591
+ import_node_fs7 = require("node:fs");
2592
+ import_node_os3 = require("node:os");
2517
2593
  init_launch_kit_paths();
2518
2594
  init_deck_lockfile();
2519
2595
  init_deck_config();
@@ -2657,8 +2733,92 @@ var init_deck_mcp = __esm({
2657
2733
 
2658
2734
  // src/server/deck-mcp-entry.ts
2659
2735
  init_deck_lockfile();
2736
+
2737
+ // src/server/prune-npx-cache.ts
2738
+ var import_node_fs2 = require("node:fs");
2739
+ var import_node_os2 = require("node:os");
2740
+ var import_node_path2 = require("node:path");
2741
+ var import_node_util = require("node:util");
2742
+ var PKG = "@launchsecure/launch-kit";
2743
+ var readFileAsync = (0, import_node_util.promisify)(import_node_fs2.readFile);
2744
+ var readdirAsync = (0, import_node_util.promisify)(import_node_fs2.readdir);
2745
+ var rmAsync = (0, import_node_util.promisify)(import_node_fs2.rm);
2746
+ var realpathAsync = (0, import_node_util.promisify)(import_node_fs2.realpath);
2747
+ function npxCacheRoot() {
2748
+ const cache = process.env.npm_config_cache;
2749
+ if (cache && cache.trim()) return (0, import_node_path2.join)(cache, "_npx");
2750
+ if (process.platform === "win32") {
2751
+ const localAppData = process.env.LOCALAPPDATA;
2752
+ if (localAppData) return (0, import_node_path2.join)(localAppData, "npm-cache", "_npx");
2753
+ }
2754
+ return (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".npm", "_npx");
2755
+ }
2756
+ function ownVersion() {
2757
+ let dir = __dirname;
2758
+ for (let i = 0; i < 8; i++) {
2759
+ try {
2760
+ const pkg = JSON.parse((0, import_node_fs2.readFileSync)((0, import_node_path2.join)(dir, "package.json"), "utf8"));
2761
+ if (pkg && pkg.name === PKG) return typeof pkg.version === "string" ? pkg.version : null;
2762
+ } catch {
2763
+ }
2764
+ const parent = (0, import_node_path2.dirname)(dir);
2765
+ if (parent === dir) break;
2766
+ dir = parent;
2767
+ }
2768
+ return null;
2769
+ }
2770
+ async function pruneStaleNpxCache() {
2771
+ try {
2772
+ const version = ownVersion();
2773
+ if (!version) return;
2774
+ const root = npxCacheRoot();
2775
+ let hashes;
2776
+ try {
2777
+ hashes = await readdirAsync(root);
2778
+ } catch {
2779
+ return;
2780
+ }
2781
+ const selfPath = await realpathAsync(process.argv[1] || __dirname).catch(() => __dirname);
2782
+ let reaped = 0;
2783
+ for (const hash of hashes) {
2784
+ const dir = (0, import_node_path2.join)(root, hash);
2785
+ if (selfPath === dir || selfPath.startsWith(dir + import_node_path2.sep)) continue;
2786
+ let ver;
2787
+ try {
2788
+ const lkPkg = JSON.parse(
2789
+ await readFileAsync((0, import_node_path2.join)(dir, "node_modules", PKG, "package.json"), "utf8")
2790
+ );
2791
+ ver = lkPkg.version;
2792
+ } catch {
2793
+ continue;
2794
+ }
2795
+ if (ver === version) continue;
2796
+ try {
2797
+ const top = JSON.parse(await readFileAsync((0, import_node_path2.join)(dir, "package.json"), "utf8"));
2798
+ const spec = top?.dependencies?.[PKG];
2799
+ if (typeof spec === "string" && spec.startsWith("file:")) continue;
2800
+ } catch {
2801
+ }
2802
+ try {
2803
+ await rmAsync(dir, { recursive: true, force: true });
2804
+ reaped++;
2805
+ } catch {
2806
+ }
2807
+ }
2808
+ if (reaped > 0) {
2809
+ process.stderr.write(
2810
+ `[launch-kit] npx-cache: reaped ${reaped} stale copy(ies), kept v${version}
2811
+ `
2812
+ );
2813
+ }
2814
+ } catch {
2815
+ }
2816
+ }
2817
+
2818
+ // src/server/deck-mcp-entry.ts
2660
2819
  async function main() {
2661
2820
  setProjectRoot(process.cwd());
2821
+ void pruneStaleNpxCache();
2662
2822
  const argv = process.argv.slice(2);
2663
2823
  const subcommand = argv[0];
2664
2824
  if (subcommand === "serve") {