@launchsecure/launch-kit 0.0.38 → 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 (101) hide show
  1. package/dist/beacon/beacon.mjs +308 -298
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +6 -6
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/internal/screenshot.d.ts.map +1 -1
  6. package/dist/chart-client/assets/index-CWJFFDPu.css +1 -0
  7. package/dist/chart-client/index.html +2 -2
  8. package/dist/client/assets/index-CTzFcfGV.css +32 -0
  9. package/dist/client/index.html +2 -2
  10. package/dist/council-client/assets/index-ArgRc5mN.css +1 -0
  11. package/dist/council-client/index.html +2 -2
  12. package/dist/deck-client/assets/{_baseUniq-CgW32Gdk.js → _baseUniq-BZP7n41F.js} +1 -1
  13. package/dist/deck-client/assets/{arc-D-Mg9gvM.js → arc-31biU3Az.js} +1 -1
  14. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-CdTsXsgl.js → architectureDiagram-Q4EWVU46-DHg6Ss--.js} +1 -1
  15. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-mwTneYyB.js → blockDiagram-DXYQGD6D-CUdblaWk.js} +1 -1
  16. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-C4R8IbjO.js → c4Diagram-AHTNJAMY-MfAO5lak.js} +1 -1
  17. package/dist/deck-client/assets/channel-BBkRLdnC.js +1 -0
  18. package/dist/deck-client/assets/{chunk-4BX2VUAB-ZWuRIUwb.js → chunk-4BX2VUAB-DQ1MrGgN.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-4TB4RGXK-PNHX10sF.js → chunk-4TB4RGXK-BUJtZ7jO.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-55IACEB6-CD9MUgPr.js → chunk-55IACEB6-BdSnXB6g.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-EDXVE4YY-C_CpORb3.js → chunk-EDXVE4YY-94yZIUI8.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-FMBD7UC4-Bg5RoVC-.js → chunk-FMBD7UC4-PnZ9v6ey.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-OYMX7WX6-DhTwgQwd.js → chunk-OYMX7WX6-DXrWNOsV.js} +1 -1
  24. package/dist/deck-client/assets/{chunk-QZHKN3VN-C5VLMaFa.js → chunk-QZHKN3VN-CsIGIDKX.js} +1 -1
  25. package/dist/deck-client/assets/{chunk-YZCP3GAM-NAGHy4Sr.js → chunk-YZCP3GAM-DVkBO9tn.js} +1 -1
  26. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-DFCaeF-7.js +1 -0
  27. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-DFCaeF-7.js +1 -0
  28. package/dist/deck-client/assets/clone-GCEVRScB.js +1 -0
  29. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CpUczjZk.js → cose-bilkent-S5V4N54A-m126Oh3b.js} +1 -1
  30. package/dist/deck-client/assets/{dagre-KV5264BT-BOvb07MG.js → dagre-KV5264BT-C2aig8U5.js} +1 -1
  31. package/dist/deck-client/assets/{diagram-5BDNPKRD-BPxwTiC-.js → diagram-5BDNPKRD-CKpoRfGn.js} +1 -1
  32. package/dist/deck-client/assets/{diagram-G4DWMVQ6-Dz_gsHgx.js → diagram-G4DWMVQ6-Cjh115Ep.js} +1 -1
  33. package/dist/deck-client/assets/{diagram-MMDJMWI5-B7z-oVTW.js → diagram-MMDJMWI5-DKlBv_2L.js} +1 -1
  34. package/dist/deck-client/assets/{diagram-TYMM5635-CAIAglLQ.js → diagram-TYMM5635-CdBh4cEn.js} +1 -1
  35. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-BiViTWF3.js → erDiagram-SMLLAGMA-56pn_93p.js} +1 -1
  36. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-DYVemp0H.js → flowDiagram-DWJPFMVM-BtV3M5xJ.js} +1 -1
  37. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-Chc1Iyu1.js → ganttDiagram-T4ZO3ILL-DTIsC6Zg.js} +1 -1
  38. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-B7eFgaj5.js → gitGraphDiagram-UUTBAWPF-CJYeyCLe.js} +1 -1
  39. package/dist/deck-client/assets/{graph-CKaIoNwb.js → graph-BDvMu1Ss.js} +1 -1
  40. package/dist/deck-client/assets/index-D4eSxcBn.css +1 -0
  41. package/dist/deck-client/assets/{index-BRawc7RA.js → index-QnGVE9PZ.js} +83 -83
  42. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BxsVq7vO.js → infoDiagram-42DDH7IO-BWyKJnpW.js} +1 -1
  43. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DAM7vPwa.js → ishikawaDiagram-UXIWVN3A-DXYkdO3T.js} +1 -1
  44. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-Xe20Nf7R.js → journeyDiagram-VCZTEJTY-C2zBr-J5.js} +1 -1
  45. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DS8YguzB.js → kanban-definition-6JOO6SKY-CdoYLS4Z.js} +1 -1
  46. package/dist/deck-client/assets/{layout-DKMBpzR-.js → layout-vOnxnCQU.js} +1 -1
  47. package/dist/deck-client/assets/{linear-DTNtBg5h.js → linear-B0J0WCGz.js} +1 -1
  48. package/dist/deck-client/assets/{min-C4DrxCcA.js → min-B0AXlT9L.js} +1 -1
  49. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-B4nEtsw5.js → mindmap-definition-QFDTVHPH-oAybLedr.js} +1 -1
  50. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-BzHdGNu5.js → pieDiagram-DEJITSTG-BjHyHxGk.js} +1 -1
  51. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-CaX0SD4-.js → quadrantDiagram-34T5L4WZ-dtluDZXs.js} +1 -1
  52. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-QeG4p2ni.js → requirementDiagram-MS252O5E-Cq8l7bOl.js} +1 -1
  53. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BoAwgAj-.js → sankeyDiagram-XADWPNL6-C1Vih91z.js} +1 -1
  54. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-Dn4pYYgu.js → sequenceDiagram-FGHM5R23-CYkd7oQK.js} +1 -1
  55. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-Is6KRmQV.js → stateDiagram-FHFEXIEX-CtyG8wBK.js} +1 -1
  56. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BLyKWfcN.js +1 -0
  57. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-v64IZGuY.js → timeline-definition-GMOUNBTQ-DZIxSyd1.js} +1 -1
  58. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-noh9eouF.js → vennDiagram-DHZGUBPP-Ct4JVRDM.js} +1 -1
  59. package/dist/deck-client/assets/{wardley-RL74JXVD-cJ_1is2S.js → wardley-RL74JXVD-V29ycxOW.js} +1 -1
  60. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-DxR4j737.js → wardleyDiagram-NUSXRM2D-D-Ua6Cmi.js} +1 -1
  61. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-B26vodaL.js → xychartDiagram-5P7HB3ND-BPCOuRVl.js} +1 -1
  62. package/dist/deck-client/index.html +2 -2
  63. package/dist/server/beacon-monitor-entry.js +106 -24
  64. package/dist/server/chart-serve.js +544 -247
  65. package/dist/server/cli.js +743 -324
  66. package/dist/server/council-entry.js +16 -3
  67. package/dist/server/council-serve.js +15 -2
  68. package/dist/server/deck-mcp-entry.js +270 -108
  69. package/dist/server/deck-serve.js +101 -23
  70. package/dist/server/fb-wizard.js +0 -0
  71. package/dist/server/graph-mcp-entry.js +866 -357
  72. package/dist/server/init-entry.js +152 -25
  73. package/dist/server/orbit-entry.js +91 -7
  74. package/dist/server/radar-docker-init-entry.js +0 -0
  75. package/dist/server/radar-entrypoint-entry.js +0 -0
  76. package/dist/server/radar-teardown-entry.js +0 -0
  77. package/dist/server/recall-entry.js +94 -12
  78. package/dist/server/rover-entry.js +44 -1
  79. package/package.json +23 -22
  80. package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +8 -7
  81. package/scaffolds/ls-marketplace/plugins/kit/commands/deactivate-statusline.md +2 -2
  82. package/scaffolds/ls-marketplace/plugins/kit/skills/comms/SKILL.md +88 -0
  83. package/scaffolds/ls-marketplace/plugins/kit/skills/project-info/SKILL.md +88 -0
  84. package/scaffolds/ls-marketplace/plugins/kit/skills/slides/SKILL.md +118 -0
  85. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
  86. package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
  87. package/scaffolds/statusline/statusline-base.sh +20 -0
  88. package/scaffolds/statusline/statusline-mcp.sh +68 -19
  89. package/scaffolds/statusline/statusline-wrapper.sh +12 -9
  90. package/dist/chart-client/assets/index-B6rR0CWx.css +0 -1
  91. package/dist/client/assets/index-D6uX1lQe.css +0 -32
  92. package/dist/council-client/assets/index-CleYLarJ.css +0 -1
  93. package/dist/deck-client/assets/channel-CuSee7GO.js +0 -1
  94. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-_kTisqzs.js +0 -1
  95. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-_kTisqzs.js +0 -1
  96. package/dist/deck-client/assets/clone-kb3zkY60.js +0 -1
  97. package/dist/deck-client/assets/index-55P73aS_.css +0 -1
  98. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-Cy45Ttqq.js +0 -1
  99. /package/dist/chart-client/assets/{index-C_xCi3gW.js → index-Dzlj-oCj.js} +0 -0
  100. /package/dist/client/assets/{index-CRecYFUA.js → index-tTg_ezUF.js} +0 -0
  101. /package/dist/council-client/assets/{index-DO-Vn15O.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,18 +1506,19 @@ 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;
1470
1519
  const persisted = readPersistedSession(cwd, entry.name);
1471
1520
  if (!persisted) continue;
1521
+ const sync = readSyncRecord(cwd, persisted.id);
1472
1522
  sessions.push({
1473
1523
  id: persisted.id,
1474
1524
  mode: persisted.mode,
@@ -1476,33 +1526,34 @@ function listPersistedSessions(cwd) {
1476
1526
  version: persisted.version,
1477
1527
  createdAt: persisted.createdAt,
1478
1528
  updatedAt: persisted.updatedAt,
1479
- shared: Boolean(readSyncRecord(cwd, persisted.id))
1529
+ shared: Boolean(sync),
1530
+ stale: Boolean(sync) && persisted.version > (sync?.version ?? 0)
1480
1531
  });
1481
1532
  }
1482
1533
  sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1483
1534
  return sessions;
1484
1535
  }
1485
1536
  function deletePersistedSession(cwd, session) {
1486
- 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");
1487
1538
  if (session === "all") {
1488
- 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 });
1489
1540
  return;
1490
1541
  }
1491
1542
  const dir = sessionDir(cwd, session);
1492
- 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 });
1493
1544
  }
1494
1545
  function syncJsonPath(cwd, session) {
1495
- return import_node_path4.default.join(sessionDir(cwd, session), ".sync.json");
1546
+ return import_node_path5.default.join(sessionDir(cwd, session), ".sync.json");
1496
1547
  }
1497
1548
  function readSyncRecord(cwd, session) {
1498
1549
  try {
1499
- 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"));
1500
1551
  } catch {
1501
1552
  return null;
1502
1553
  }
1503
1554
  }
1504
1555
  function writeSyncRecord(cwd, session, rec) {
1505
- 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));
1506
1557
  }
1507
1558
  function escHtml2(s) {
1508
1559
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -1515,7 +1566,7 @@ function readIframeArtifact(cwd, src) {
1515
1566
  const rel = decodeURIComponent(src.slice("/deck-files/".length));
1516
1567
  if (rel.includes("..")) return null;
1517
1568
  try {
1518
- 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");
1519
1570
  } catch {
1520
1571
  return null;
1521
1572
  }
@@ -1612,23 +1663,23 @@ function broadcastToClients(message) {
1612
1663
  }
1613
1664
  }
1614
1665
  function serveStatic(res, filePath) {
1615
- if (!import_node_fs4.default.existsSync(filePath) || !import_node_fs4.default.statSync(filePath).isFile()) return false;
1616
- 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();
1617
1668
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
1618
1669
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
1619
- import_node_fs4.default.createReadStream(filePath).pipe(res);
1670
+ import_node_fs5.default.createReadStream(filePath).pipe(res);
1620
1671
  return true;
1621
1672
  }
1622
1673
  function resolveClientDir() {
1623
- const sibling = import_node_path4.default.join(__dirname, "..", "deck-client");
1624
- if (import_node_fs4.default.existsSync(import_node_path4.default.join(sibling, "assets"))) return sibling;
1625
- const devDist = import_node_path4.default.join(__dirname, "..", "..", "dist", "deck-client");
1626
- 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;
1627
1678
  return sibling;
1628
1679
  }
1629
1680
  function serveIndex(res, clientDir) {
1630
- const indexPath = import_node_path4.default.join(clientDir, "index.html");
1631
- 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)) {
1632
1683
  res.writeHead(500, { "Content-Type": "text/plain" });
1633
1684
  res.end(`LaunchDeck client bundle not found at ${clientDir}. Run 'npm run build:client'.`);
1634
1685
  return;
@@ -1711,6 +1762,10 @@ async function startDeckServer(opts = {}) {
1711
1762
  jsonResponse(res, 200, { config: cfg });
1712
1763
  return;
1713
1764
  }
1765
+ if (req.method === "GET" && url2.pathname === "/api/deck/courses") {
1766
+ jsonResponse(res, 200, { courses: listCourses(cwd) });
1767
+ return;
1768
+ }
1714
1769
  if (req.method === "POST" && url2.pathname === "/api/deck/config") {
1715
1770
  try {
1716
1771
  const body = JSON.parse(await readBody(req));
@@ -1732,15 +1787,15 @@ async function startDeckServer(opts = {}) {
1732
1787
  const baseUrl = `/deck-files/${sessionEncoded}`;
1733
1788
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
1734
1789
  const report = generateBlastRadiusReport(block.manifest);
1735
- const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1736
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1737
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius.html"), html);
1738
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius-report.md"), report);
1739
- 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));
1740
1795
  const contractData = generateContract(block.manifest);
1741
1796
  const contractMd = contractToMarkdown(contractData);
1742
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "contract.json"), JSON.stringify(contractData, null, 2));
1743
- 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);
1744
1799
  block.type = "iframe";
1745
1800
  block.src = `${baseUrl}/blast-radius.html`;
1746
1801
  delete block.manifest;
@@ -1767,9 +1822,9 @@ async function startDeckServer(opts = {}) {
1767
1822
  const sessionEncoded = encodeURIComponent(body.session);
1768
1823
  const baseUrl = `/deck-files/${sessionEncoded}`;
1769
1824
  const filename = `${slugify(block.label)}.html`;
1770
- const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1771
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1772
- 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);
1773
1828
  block.type = "iframe";
1774
1829
  block.src = `${baseUrl}/${filename}`;
1775
1830
  delete block.content;
@@ -1809,14 +1864,34 @@ async function startDeckServer(opts = {}) {
1809
1864
  jsonResponse(res, 404, { ok: false, error: "session not found" });
1810
1865
  return;
1811
1866
  }
1867
+ let course;
1812
1868
  try {
1813
- loadMcpConfig(cwd);
1814
- } catch (err) {
1815
- jsonResponse(res, 400, {
1816
- ok: false,
1817
- error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}`
1818
- });
1819
- 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
+ }
1820
1895
  }
1821
1896
  try {
1822
1897
  const blocks = Array.isArray(persisted.blocks) ? persisted.blocks : [];
@@ -1825,24 +1900,27 @@ async function startDeckServer(opts = {}) {
1825
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.`;
1826
1901
  const input = { title: id, body, html, sessionId: id, blockCount: blocks.length };
1827
1902
  const existing2 = readSyncRecord(cwd, id);
1828
- let resourceId;
1829
- let updated = false;
1830
- if (existing2?.resourceId) {
1831
- try {
1832
- ({ id: resourceId } = await updateDeck(existing2.resourceId, input));
1833
- updated = true;
1834
- } catch {
1835
- ({ 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
+ }
1836
1912
  }
1837
- } else {
1838
- ({ id: resourceId } = await writeDeck(input));
1839
- }
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();
1840
1917
  writeSyncRecord(cwd, id, {
1841
1918
  resourceId,
1842
1919
  sharedAt: (/* @__PURE__ */ new Date()).toISOString(),
1843
- version: persisted.version
1920
+ version: persisted.version,
1921
+ course
1844
1922
  });
1845
- jsonResponse(res, 200, { ok: true, resourceId, updated });
1923
+ jsonResponse(res, 200, { ok: true, resourceId, updated, course });
1846
1924
  } catch (err) {
1847
1925
  jsonResponse(res, 500, { ok: false, error: String(err) });
1848
1926
  }
@@ -1865,7 +1943,7 @@ async function startDeckServer(opts = {}) {
1865
1943
  try {
1866
1944
  await deleteDeck(sync.resourceId);
1867
1945
  try {
1868
- import_node_fs4.default.rmSync(syncJsonPath(cwd, id), { force: true });
1946
+ import_node_fs5.default.rmSync(syncJsonPath(cwd, id), { force: true });
1869
1947
  } catch {
1870
1948
  }
1871
1949
  jsonResponse(res, 200, { ok: true, deleted: true });
@@ -1976,7 +2054,7 @@ async function startDeckServer(opts = {}) {
1976
2054
  res.end("Forbidden");
1977
2055
  return;
1978
2056
  }
1979
- 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);
1980
2058
  if (serveStatic(res, filePath)) return;
1981
2059
  res.writeHead(404);
1982
2060
  res.end("Not found");
@@ -1989,25 +2067,25 @@ async function startDeckServer(opts = {}) {
1989
2067
  res.end("Forbidden");
1990
2068
  return;
1991
2069
  }
1992
- const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1993
- 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()) {
1994
2072
  res.writeHead(404);
1995
2073
  res.end("Not found");
1996
2074
  return;
1997
2075
  }
1998
- const ext = import_node_path4.default.extname(filePath).toLowerCase();
2076
+ const ext = import_node_path5.default.extname(filePath).toLowerCase();
1999
2077
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
2000
- const filename = import_node_path4.default.basename(filePath);
2078
+ const filename = import_node_path5.default.basename(filePath);
2001
2079
  res.writeHead(200, {
2002
2080
  "Content-Type": mime,
2003
2081
  "Content-Disposition": `attachment; filename="${filename}"`,
2004
2082
  "Cache-Control": "no-cache"
2005
2083
  });
2006
- import_node_fs4.default.createReadStream(filePath).pipe(res);
2084
+ import_node_fs5.default.createReadStream(filePath).pipe(res);
2007
2085
  return;
2008
2086
  }
2009
2087
  if (url2.pathname !== "/") {
2010
- const staticPath = import_node_path4.default.join(clientDir, url2.pathname);
2088
+ const staticPath = import_node_path5.default.join(clientDir, url2.pathname);
2011
2089
  if (serveStatic(res, staticPath)) return;
2012
2090
  }
2013
2091
  serveIndex(res, clientDir);
@@ -2106,13 +2184,13 @@ function runServeCli(argv) {
2106
2184
  process.exit(1);
2107
2185
  });
2108
2186
  }
2109
- 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;
2110
2188
  var init_deck_serve = __esm({
2111
2189
  "src/server/deck-serve.ts"() {
2112
2190
  "use strict";
2113
2191
  import_node_http = __toESM(require("node:http"));
2114
- import_node_fs4 = __toESM(require("node:fs"));
2115
- import_node_path4 = __toESM(require("node:path"));
2192
+ import_node_fs5 = __toESM(require("node:fs"));
2193
+ import_node_path5 = __toESM(require("node:path"));
2116
2194
  import_ws = require("ws");
2117
2195
  init_launch_kit_paths();
2118
2196
  init_deck_lockfile();
@@ -2206,15 +2284,15 @@ async function handleTool(name, args) {
2206
2284
  const baseUrl = `/deck-files/${sessionEncoded}`;
2207
2285
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
2208
2286
  const report = generateBlastRadiusReport(block.manifest);
2209
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2210
- import_node_fs5.default.mkdirSync(dir, { recursive: true });
2211
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "blast-radius.html"), html);
2212
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "blast-radius-report.md"), report);
2213
- 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));
2214
2292
  const contractData = generateContract(block.manifest);
2215
2293
  const contractMd = contractToMarkdown(contractData);
2216
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.json"), JSON.stringify(contractData, null, 2));
2217
- 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);
2218
2296
  block.type = "iframe";
2219
2297
  block.src = `${baseUrl}/blast-radius.html`;
2220
2298
  delete block.manifest;
@@ -2241,9 +2319,9 @@ async function handleTool(name, args) {
2241
2319
  const sessionEncoded = encodeURIComponent(session);
2242
2320
  const baseUrl = `/deck-files/${sessionEncoded}`;
2243
2321
  const filename = `${slugify(block.label)}.html`;
2244
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2245
- import_node_fs5.default.mkdirSync(dir, { recursive: true });
2246
- 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);
2247
2325
  block.type = "iframe";
2248
2326
  block.src = `${baseUrl}/${filename}`;
2249
2327
  delete block.content;
@@ -2337,11 +2415,11 @@ async function handleTool(name, args) {
2337
2415
  }));
2338
2416
  }
2339
2417
  try {
2340
- const logDir = (0, import_node_path5.join)((0, import_node_os2.homedir)(), LAUNCHSECURE_DIR);
2341
- (0, import_node_fs6.mkdirSync)(logDir, { recursive: true });
2342
- const logPath = (0, import_node_path5.join)(logDir, "launch-deck.log");
2343
- const out = (0, import_node_fs6.openSync)(logPath, "a");
2344
- 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");
2345
2423
  const entryPath = process.argv[1];
2346
2424
  const config = loadDeckConfig(projectRoot);
2347
2425
  const resolvedPort = args.port ?? config.port;
@@ -2397,21 +2475,21 @@ async function handleTool(name, args) {
2397
2475
  }
2398
2476
  case "generate_contract": {
2399
2477
  const session = args.session;
2400
- const dir = (0, import_node_path5.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2401
- const manifestPath = (0, import_node_path5.join)(dir, "blast-radius-manifest.json");
2402
- 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)) {
2403
2481
  return text(JSON.stringify({
2404
2482
  error: `No blast radius manifest found for session "${session}". Push a blast-radius block first.`,
2405
2483
  hint: "Use the deck tool with a blast-radius block type to create a session."
2406
2484
  }));
2407
2485
  }
2408
2486
  try {
2409
- const raw = import_node_fs5.default.readFileSync(manifestPath, "utf-8");
2487
+ const raw = import_node_fs6.default.readFileSync(manifestPath, "utf-8");
2410
2488
  const manifest = JSON.parse(raw);
2411
2489
  const contract = generateContract(manifest);
2412
2490
  const markdown = contractToMarkdown(contract);
2413
- import_node_fs5.default.writeFileSync((0, import_node_path5.join)(dir, "contract.json"), JSON.stringify(contract, null, 2));
2414
- 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);
2415
2493
  return text(JSON.stringify(contract));
2416
2494
  } catch (err) {
2417
2495
  return text(JSON.stringify({ error: `Failed to generate contract: ${err}` }));
@@ -2502,16 +2580,16 @@ function startDeckMcpServer() {
2502
2580
  process.exit(0);
2503
2581
  });
2504
2582
  }
2505
- 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;
2506
2584
  var init_deck_mcp = __esm({
2507
2585
  "src/server/deck-mcp.ts"() {
2508
2586
  "use strict";
2509
2587
  import_node_http2 = __toESM(require("node:http"));
2510
- import_node_fs5 = __toESM(require("node:fs"));
2511
- import_node_path5 = require("node:path");
2588
+ import_node_fs6 = __toESM(require("node:fs"));
2589
+ import_node_path6 = require("node:path");
2512
2590
  import_node_child_process2 = require("node:child_process");
2513
- import_node_fs6 = require("node:fs");
2514
- import_node_os2 = require("node:os");
2591
+ import_node_fs7 = require("node:fs");
2592
+ import_node_os3 = require("node:os");
2515
2593
  init_launch_kit_paths();
2516
2594
  init_deck_lockfile();
2517
2595
  init_deck_config();
@@ -2655,8 +2733,92 @@ var init_deck_mcp = __esm({
2655
2733
 
2656
2734
  // src/server/deck-mcp-entry.ts
2657
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
2658
2819
  async function main() {
2659
2820
  setProjectRoot(process.cwd());
2821
+ void pruneStaleNpxCache();
2660
2822
  const argv = process.argv.slice(2);
2661
2823
  const subcommand = argv[0];
2662
2824
  if (subcommand === "serve") {