@launchsecure/launch-kit 0.0.36 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/chart-client/assets/index-DJrjyXbN.css +1 -0
  2. package/dist/chart-client/index.html +2 -2
  3. package/dist/client/assets/index-8eSXr3Ez.css +32 -0
  4. package/dist/client/index.html +2 -2
  5. package/dist/council-client/assets/index-4K0t2WrZ.css +1 -0
  6. package/dist/council-client/index.html +2 -2
  7. package/dist/deck-client/assets/{_baseUniq-BiVx0WO_.js → _baseUniq-Cn5TyL9s.js} +1 -1
  8. package/dist/deck-client/assets/{arc-DGMkiEzS.js → arc-D61amKYu.js} +1 -1
  9. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Y2WRmHtk.js → architectureDiagram-Q4EWVU46-CpKrvC2W.js} +1 -1
  10. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-_Lbfu5BQ.js → blockDiagram-DXYQGD6D-Yj5OjxvG.js} +1 -1
  11. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CTqpYTBX.js → c4Diagram-AHTNJAMY-BIR810Tv.js} +1 -1
  12. package/dist/deck-client/assets/channel-DrJz2x-n.js +1 -0
  13. package/dist/deck-client/assets/{chunk-4BX2VUAB-liEIbPHs.js → chunk-4BX2VUAB-BeSHwGvx.js} +1 -1
  14. package/dist/deck-client/assets/{chunk-4TB4RGXK-CCc6lYvL.js → chunk-4TB4RGXK-CCqzsLpg.js} +1 -1
  15. package/dist/deck-client/assets/{chunk-55IACEB6-D02jJUR2.js → chunk-55IACEB6-CuW_aq4-.js} +1 -1
  16. package/dist/deck-client/assets/{chunk-EDXVE4YY-BFmGMbLD.js → chunk-EDXVE4YY-Dl35ixYh.js} +1 -1
  17. package/dist/deck-client/assets/{chunk-FMBD7UC4-6wFLOVcJ.js → chunk-FMBD7UC4-TwreZQTv.js} +1 -1
  18. package/dist/deck-client/assets/{chunk-OYMX7WX6-Bnr8RiBf.js → chunk-OYMX7WX6-Ahfw8EUo.js} +1 -1
  19. package/dist/deck-client/assets/{chunk-QZHKN3VN-Ct82MksJ.js → chunk-QZHKN3VN-DlE_zlU-.js} +1 -1
  20. package/dist/deck-client/assets/{chunk-YZCP3GAM-BXmN1diQ.js → chunk-YZCP3GAM-Dj6QWzSg.js} +1 -1
  21. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-a3tg9w7z.js +1 -0
  22. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-a3tg9w7z.js +1 -0
  23. package/dist/deck-client/assets/clone-Dd7JBCL5.js +1 -0
  24. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CmQCT-mH.js → cose-bilkent-S5V4N54A-BO1z5aOM.js} +1 -1
  25. package/dist/deck-client/assets/{dagre-KV5264BT-DDdSa9EX.js → dagre-KV5264BT-DVsw17fE.js} +1 -1
  26. package/dist/deck-client/assets/{diagram-5BDNPKRD-Bccks2xJ.js → diagram-5BDNPKRD-6jYs7oZk.js} +1 -1
  27. package/dist/deck-client/assets/{diagram-G4DWMVQ6-CPPNgxmQ.js → diagram-G4DWMVQ6-6DbggeGE.js} +1 -1
  28. package/dist/deck-client/assets/{diagram-MMDJMWI5-KrD300pS.js → diagram-MMDJMWI5-CQtk1cSU.js} +1 -1
  29. package/dist/deck-client/assets/{diagram-TYMM5635-DefnLuQf.js → diagram-TYMM5635-BR-gt75b.js} +1 -1
  30. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DI9FfnFP.js → erDiagram-SMLLAGMA-C9qMtjdY.js} +1 -1
  31. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-twKyd3Fx.js → flowDiagram-DWJPFMVM-CdaPhPYb.js} +1 -1
  32. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-Wau3jhBr.js → ganttDiagram-T4ZO3ILL-BRsZWUy4.js} +1 -1
  33. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-D9GgYXwb.js → gitGraphDiagram-UUTBAWPF-B8Z90jCj.js} +1 -1
  34. package/dist/deck-client/assets/{graph-BhNLzyXS.js → graph-my2Zphm4.js} +1 -1
  35. package/dist/deck-client/assets/index-ByqxPEgU.css +1 -0
  36. package/dist/deck-client/assets/{index-BtQBaQ7s.js → index-DqAoYZwV.js} +43 -42
  37. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-TylGlSG-.js → infoDiagram-42DDH7IO-Csr9loin.js} +1 -1
  38. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DAT8icpg.js → ishikawaDiagram-UXIWVN3A-HWdvUNFi.js} +1 -1
  39. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-D3v_XL72.js → journeyDiagram-VCZTEJTY-CjYHG6EM.js} +1 -1
  40. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DNUOBiNr.js → kanban-definition-6JOO6SKY-CX3JdUu7.js} +1 -1
  41. package/dist/deck-client/assets/{layout-COfodgwF.js → layout-Bcucv5Gi.js} +1 -1
  42. package/dist/deck-client/assets/{linear-DmTsuIvK.js → linear-CUGM5FJZ.js} +1 -1
  43. package/dist/deck-client/assets/{min-BW1F7i1D.js → min-Dw4g5w9z.js} +1 -1
  44. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CErFzKWl.js → mindmap-definition-QFDTVHPH-C8oo61fg.js} +1 -1
  45. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DW5F757o.js → pieDiagram-DEJITSTG-D2WYGkq8.js} +1 -1
  46. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B1S2-TfI.js → quadrantDiagram-34T5L4WZ-Vh00GISt.js} +1 -1
  47. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BY5BAR-5.js → requirementDiagram-MS252O5E-DxI-DFrN.js} +1 -1
  48. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-CE1Cp9HS.js → sankeyDiagram-XADWPNL6-QgwyjasI.js} +1 -1
  49. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-IaHnbKye.js → sequenceDiagram-FGHM5R23-DmOmD5Ni.js} +1 -1
  50. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CwPJm9hU.js → stateDiagram-FHFEXIEX-CRwglGg_.js} +1 -1
  51. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BvZLEWAA.js +1 -0
  52. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DVFGGSgN.js → timeline-definition-GMOUNBTQ-Dj9YGKOh.js} +1 -1
  53. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-C1194MJi.js → vennDiagram-DHZGUBPP-xzIaOzEU.js} +1 -1
  54. package/dist/deck-client/assets/wardley-RL74JXVD-CEAay09T.js +162 -0
  55. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-hpwdFfGj.js → wardleyDiagram-NUSXRM2D-BIYYh-JZ.js} +1 -1
  56. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYkotwy8.js → xychartDiagram-5P7HB3ND-Cy9EoJCh.js} +1 -1
  57. package/dist/deck-client/index.html +2 -2
  58. package/dist/server/cli.js +21 -2
  59. package/dist/server/council-entry.js +86 -2
  60. package/dist/server/council-serve.js +81 -2
  61. package/dist/server/deck-mcp-entry.js +449 -68
  62. package/dist/server/deck-serve.js +411 -42
  63. package/dist/server/fb-wizard.js +0 -0
  64. package/dist/server/init-entry.js +96 -4
  65. package/dist/server/radar-docker-init-entry.js +95 -3
  66. package/dist/server/radar-entrypoint-entry.js +0 -0
  67. package/dist/server/radar-teardown-entry.js +0 -0
  68. package/dist/server/rover-entry.js +25 -4
  69. package/package.json +22 -23
  70. package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +5 -0
  71. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
  72. package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
  73. package/dist/chart-client/assets/index-DpKO9p0s.css +0 -1
  74. package/dist/client/assets/index-Dv6dD2zY.css +0 -32
  75. package/dist/council-client/assets/index-AqQ9Sei6.css +0 -1
  76. package/dist/deck-client/assets/channel-DB6LxW_l.js +0 -1
  77. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +0 -1
  78. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +0 -1
  79. package/dist/deck-client/assets/clone-DiIRH1pI.js +0 -1
  80. package/dist/deck-client/assets/index-B-YQq5b5.css +0 -1
  81. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +0 -1
  82. package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +0 -162
  83. /package/dist/chart-client/assets/{index-DFu2xIrM.js → index-BgUxHxwE.js} +0 -0
  84. /package/dist/client/assets/{index-Cbw6bVdx.js → index-CUivaQnN.js} +0 -0
  85. /package/dist/council-client/assets/{index-CAsmGTzg.js → index-DN8HN_5K.js} +0 -0
@@ -712,9 +712,9 @@ function generateBlastRadiusReport(manifest) {
712
712
  `;
713
713
  for (const n of nodes) {
714
714
  const ln = layerMap[n.layer] || n.layer;
715
- const path2 = (n.path || "-").replace(/\|/g, "/");
715
+ const path3 = (n.path || "-").replace(/\|/g, "/");
716
716
  const reason = (n.reason || "-").replace(/\|/g, "/");
717
- md += `| ${n.name} | ${ln} | ${n.type || "-"} | ${path2} | ${reason} |
717
+ md += `| ${n.name} | ${ln} | ${n.type || "-"} | ${path3} | ${reason} |
718
718
  `;
719
719
  }
720
720
  md += `
@@ -1198,6 +1198,227 @@ var init_rich_html_render = __esm({
1198
1198
  }
1199
1199
  });
1200
1200
 
1201
+ // src/server/cred-shape.ts
1202
+ function inferCourseName(serverUrl) {
1203
+ try {
1204
+ const host = new URL(serverUrl).hostname.toLowerCase();
1205
+ if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) return "local";
1206
+ if (host.includes("staging")) return "staging";
1207
+ if (host.endsWith(".vercel.app")) return "prod";
1208
+ return host.split(".")[0] || "default";
1209
+ } catch {
1210
+ return "default";
1211
+ }
1212
+ }
1213
+ function toNested(cred) {
1214
+ if (cred.profiles && cred.active && cred.profiles[cred.active]) {
1215
+ return { active: cred.active, profiles: cred.profiles };
1216
+ }
1217
+ if (!cred.pat || !cred.orgSlug || !cred.projectSlug || !cred.serverUrl) {
1218
+ return null;
1219
+ }
1220
+ const name = inferCourseName(cred.serverUrl);
1221
+ return {
1222
+ active: name,
1223
+ profiles: {
1224
+ [name]: {
1225
+ pat: cred.pat,
1226
+ orgSlug: cred.orgSlug,
1227
+ projectSlug: cred.projectSlug,
1228
+ serverUrl: cred.serverUrl
1229
+ }
1230
+ }
1231
+ };
1232
+ }
1233
+ function readCredFile(repoRoot) {
1234
+ const p = path.join(repoRoot, CONFIG_FILENAME2);
1235
+ if (!fs.existsSync(p)) return null;
1236
+ try {
1237
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
1238
+ } catch (err) {
1239
+ throw new Error(`could not parse ${CONFIG_FILENAME2}: ${err instanceof Error ? err.message : String(err)}`);
1240
+ }
1241
+ }
1242
+ var fs, path, CONFIG_FILENAME2;
1243
+ var init_cred_shape = __esm({
1244
+ "src/server/cred-shape.ts"() {
1245
+ "use strict";
1246
+ fs = __toESM(require("node:fs"));
1247
+ path = __toESM(require("node:path"));
1248
+ CONFIG_FILENAME2 = ".launch-secure.cred.config";
1249
+ }
1250
+ });
1251
+
1252
+ // src/server/mcp-client.ts
1253
+ function mcpConfigFromCourse(projectRoot) {
1254
+ let cred;
1255
+ try {
1256
+ cred = readCredFile(projectRoot);
1257
+ } catch {
1258
+ return null;
1259
+ }
1260
+ if (!cred) return null;
1261
+ const nested = toNested(cred);
1262
+ if (!nested) return null;
1263
+ const profile = nested.profiles[nested.active];
1264
+ if (!profile?.pat || !profile.serverUrl || !profile.orgSlug || !profile.projectSlug) {
1265
+ return null;
1266
+ }
1267
+ return {
1268
+ url: new URL("/api/mcp/project", profile.serverUrl).toString(),
1269
+ headers: {
1270
+ Authorization: `Bearer ${profile.pat}`,
1271
+ "X-Org-Slug": profile.orgSlug,
1272
+ "X-Project-Slug": profile.projectSlug
1273
+ }
1274
+ };
1275
+ }
1276
+ function loadMcpConfig(projectRoot) {
1277
+ if (_config) return _config;
1278
+ const fromCourse = mcpConfigFromCourse(projectRoot);
1279
+ if (fromCourse) {
1280
+ _config = fromCourse;
1281
+ return _config;
1282
+ }
1283
+ const mcpPath = (0, import_node_path3.join)(projectRoot, ".mcp.json");
1284
+ if (!(0, import_node_fs3.existsSync)(mcpPath)) {
1285
+ throw new Error(
1286
+ `No active course in ${CONFIG_FILENAME2} and no .mcp.json at ${mcpPath}. Run \`launch-course\` or bootstrap with \`launch-kit init\`.`
1287
+ );
1288
+ }
1289
+ const raw = JSON.parse((0, import_node_fs3.readFileSync)(mcpPath, "utf-8"));
1290
+ const entry = raw.mcpServers["launch-secure"];
1291
+ if (!entry?.url) {
1292
+ throw new Error(
1293
+ `No active course in ${CONFIG_FILENAME2} and no "launch-secure" entry with url in .mcp.json`
1294
+ );
1295
+ }
1296
+ _config = {
1297
+ url: entry.url,
1298
+ headers: entry.headers ?? {}
1299
+ };
1300
+ return _config;
1301
+ }
1302
+ function parseSSE(text2) {
1303
+ const lines = text2.split("\n");
1304
+ for (const line of lines) {
1305
+ if (line.startsWith("data: ")) {
1306
+ const data = line.slice(6);
1307
+ try {
1308
+ return JSON.parse(data);
1309
+ } catch {
1310
+ }
1311
+ }
1312
+ }
1313
+ return JSON.parse(text2);
1314
+ }
1315
+ async function mcpRequest(method, params) {
1316
+ const config = _config;
1317
+ if (!config) throw new Error("MCP config not loaded \u2014 call loadMcpConfig first");
1318
+ const id = ++_requestId;
1319
+ const headers = {
1320
+ ...config.headers,
1321
+ "Content-Type": "application/json",
1322
+ "Accept": "application/json, text/event-stream"
1323
+ };
1324
+ if (_sessionId) {
1325
+ headers["Mcp-Session-Id"] = _sessionId;
1326
+ }
1327
+ const response = await fetch(config.url, {
1328
+ method: "POST",
1329
+ headers,
1330
+ body: JSON.stringify({ jsonrpc: "2.0", id, method, params })
1331
+ });
1332
+ const sid = response.headers.get("mcp-session-id");
1333
+ if (sid) _sessionId = sid;
1334
+ if (!response.ok) {
1335
+ const text2 = await response.text();
1336
+ throw new Error(`MCP ${method} failed (${response.status}): ${text2}`);
1337
+ }
1338
+ const contentType = response.headers.get("content-type") ?? "";
1339
+ const body = await response.text();
1340
+ if (contentType.includes("text/event-stream")) {
1341
+ return parseSSE(body);
1342
+ }
1343
+ return JSON.parse(body);
1344
+ }
1345
+ async function ensureInitialized() {
1346
+ if (_sessionId) return;
1347
+ await mcpRequest("initialize", {
1348
+ protocolVersion: "2024-11-05",
1349
+ capabilities: {},
1350
+ clientInfo: { name: "launch-council", version: "0.0.1" }
1351
+ });
1352
+ const config = _config;
1353
+ const headers = {
1354
+ ...config.headers,
1355
+ "Content-Type": "application/json",
1356
+ "Accept": "application/json, text/event-stream"
1357
+ };
1358
+ if (_sessionId) headers["Mcp-Session-Id"] = _sessionId;
1359
+ await fetch(config.url, {
1360
+ method: "POST",
1361
+ headers,
1362
+ body: JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })
1363
+ });
1364
+ }
1365
+ async function callTool(toolName, args) {
1366
+ await ensureInitialized();
1367
+ const result = await mcpRequest("tools/call", {
1368
+ name: toolName,
1369
+ arguments: args
1370
+ });
1371
+ if (result.error) {
1372
+ throw new Error(`MCP tool error: ${result.error.message}`);
1373
+ }
1374
+ const textContent = result.result?.content?.[0]?.text;
1375
+ if (!textContent) return null;
1376
+ return JSON.parse(textContent);
1377
+ }
1378
+ async function writeDeck(input) {
1379
+ const result = await callTool("communication_write", {
1380
+ title: input.title,
1381
+ body: input.body,
1382
+ resource_type: "deck",
1383
+ fields: {
1384
+ deckHtml: input.html,
1385
+ sessionId: input.sessionId,
1386
+ blockCount: input.blockCount,
1387
+ sharedFrom: "launch-deck"
1388
+ }
1389
+ });
1390
+ return { id: result.created.id };
1391
+ }
1392
+ async function updateDeck(commentId, input) {
1393
+ await callTool("communication_update", {
1394
+ comment_id: commentId,
1395
+ title: input.title,
1396
+ body: input.body,
1397
+ fields: {
1398
+ deckHtml: input.html,
1399
+ sessionId: input.sessionId,
1400
+ blockCount: input.blockCount,
1401
+ sharedFrom: "launch-deck"
1402
+ }
1403
+ });
1404
+ return { id: commentId };
1405
+ }
1406
+ async function deleteDeck(commentId) {
1407
+ await callTool("communication_delete", { comment_id: commentId });
1408
+ }
1409
+ var import_node_fs3, import_node_path3, _config, _requestId, _sessionId;
1410
+ var init_mcp_client = __esm({
1411
+ "src/server/mcp-client.ts"() {
1412
+ "use strict";
1413
+ import_node_fs3 = require("node:fs");
1414
+ import_node_path3 = require("node:path");
1415
+ init_cred_shape();
1416
+ _config = null;
1417
+ _requestId = 0;
1418
+ _sessionId = null;
1419
+ }
1420
+ });
1421
+
1201
1422
  // src/server/deck-serve.ts
1202
1423
  var deck_serve_exports = {};
1203
1424
  __export(deck_serve_exports, {
@@ -1209,14 +1430,14 @@ __export(deck_serve_exports, {
1209
1430
  startDeckServer: () => startDeckServer
1210
1431
  });
1211
1432
  function sessionDir(cwd, session) {
1212
- return import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1433
+ return import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1213
1434
  }
1214
1435
  function sessionJsonPath(cwd, session) {
1215
- return import_node_path3.default.join(sessionDir(cwd, session), "session.json");
1436
+ return import_node_path4.default.join(sessionDir(cwd, session), "session.json");
1216
1437
  }
1217
1438
  function readPersistedSession(cwd, session) {
1218
1439
  try {
1219
- const raw = import_node_fs3.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1440
+ const raw = import_node_fs4.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1220
1441
  return JSON.parse(raw);
1221
1442
  } catch {
1222
1443
  return null;
@@ -1224,7 +1445,7 @@ function readPersistedSession(cwd, session) {
1224
1445
  }
1225
1446
  function writePersistedSession(cwd, session, payload) {
1226
1447
  const dir = sessionDir(cwd, session);
1227
- import_node_fs3.default.mkdirSync(dir, { recursive: true });
1448
+ import_node_fs4.default.mkdirSync(dir, { recursive: true });
1228
1449
  const existing = readPersistedSession(cwd, session);
1229
1450
  const now = (/* @__PURE__ */ new Date()).toISOString();
1230
1451
  const next = {
@@ -1236,13 +1457,13 @@ function writePersistedSession(cwd, session, payload) {
1236
1457
  createdAt: existing?.createdAt ?? now,
1237
1458
  updatedAt: now
1238
1459
  };
1239
- import_node_fs3.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1460
+ import_node_fs4.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1240
1461
  return next;
1241
1462
  }
1242
1463
  function listPersistedSessions(cwd) {
1243
- const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1244
- if (!import_node_fs3.default.existsSync(base)) return [];
1245
- const entries = import_node_fs3.default.readdirSync(base, { withFileTypes: true });
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 });
1246
1467
  const sessions = [];
1247
1468
  for (const entry of entries) {
1248
1469
  if (!entry.isDirectory()) continue;
@@ -1254,20 +1475,106 @@ function listPersistedSessions(cwd) {
1254
1475
  blockCount: Array.isArray(persisted.blocks) ? persisted.blocks.length : 0,
1255
1476
  version: persisted.version,
1256
1477
  createdAt: persisted.createdAt,
1257
- updatedAt: persisted.updatedAt
1478
+ updatedAt: persisted.updatedAt,
1479
+ shared: Boolean(readSyncRecord(cwd, persisted.id))
1258
1480
  });
1259
1481
  }
1260
1482
  sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1261
1483
  return sessions;
1262
1484
  }
1263
1485
  function deletePersistedSession(cwd, session) {
1264
- const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1486
+ const base = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1265
1487
  if (session === "all") {
1266
- if (import_node_fs3.default.existsSync(base)) import_node_fs3.default.rmSync(base, { recursive: true, force: true });
1488
+ if (import_node_fs4.default.existsSync(base)) import_node_fs4.default.rmSync(base, { recursive: true, force: true });
1267
1489
  return;
1268
1490
  }
1269
1491
  const dir = sessionDir(cwd, session);
1270
- if (import_node_fs3.default.existsSync(dir)) import_node_fs3.default.rmSync(dir, { recursive: true, force: true });
1492
+ if (import_node_fs4.default.existsSync(dir)) import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
1493
+ }
1494
+ function syncJsonPath(cwd, session) {
1495
+ return import_node_path4.default.join(sessionDir(cwd, session), ".sync.json");
1496
+ }
1497
+ function readSyncRecord(cwd, session) {
1498
+ try {
1499
+ return JSON.parse(import_node_fs4.default.readFileSync(syncJsonPath(cwd, session), "utf-8"));
1500
+ } catch {
1501
+ return null;
1502
+ }
1503
+ }
1504
+ function writeSyncRecord(cwd, session, rec) {
1505
+ import_node_fs4.default.writeFileSync(syncJsonPath(cwd, session), JSON.stringify(rec, null, 2));
1506
+ }
1507
+ function escHtml2(s) {
1508
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1509
+ }
1510
+ function escAttr(s) {
1511
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
1512
+ }
1513
+ function readIframeArtifact(cwd, src) {
1514
+ if (typeof src !== "string" || !src.startsWith("/deck-files/")) return null;
1515
+ const rel = decodeURIComponent(src.slice("/deck-files/".length));
1516
+ if (rel.includes("..")) return null;
1517
+ try {
1518
+ return import_node_fs4.default.readFileSync(import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", rel), "utf-8");
1519
+ } catch {
1520
+ return null;
1521
+ }
1522
+ }
1523
+ function renderShareSection(cwd, block) {
1524
+ const label = block?.label ? `<h2 class="db-title">${escHtml2(String(block.label))}</h2>` : "";
1525
+ switch (block?.type) {
1526
+ case "iframe": {
1527
+ const doc = readIframeArtifact(cwd, block.src) ?? "<p>(missing artifact)</p>";
1528
+ return `<section class="db">${label}<iframe class="db-frame" sandbox="allow-scripts allow-popups" srcdoc="${escAttr(doc)}"></iframe></section>`;
1529
+ }
1530
+ case "rich-html":
1531
+ case "html":
1532
+ return `<section class="db">${label}<div class="db-html">${typeof block.content === "string" ? block.content : ""}</div></section>`;
1533
+ case "markdown":
1534
+ return `<section class="db">${label}<div class="db-md" data-md="${escAttr(typeof block.content === "string" ? block.content : "")}"></div></section>`;
1535
+ case "mermaid":
1536
+ return `<section class="db">${label}<pre class="mermaid">${escHtml2(typeof block.content === "string" ? block.content : "")}</pre></section>`;
1537
+ case "options": {
1538
+ const opts = Array.isArray(block.options) ? block.options : [];
1539
+ const lis = opts.map(
1540
+ (o) => `<li><strong>${escHtml2(String(o?.label ?? o?.id ?? ""))}</strong>${o?.description ? ` \u2014 ${escHtml2(String(o.description))}` : ""}</li>`
1541
+ ).join("");
1542
+ return `<section class="db">${label}<ul>${lis}</ul></section>`;
1543
+ }
1544
+ default:
1545
+ return `<section class="db">${label}</section>`;
1546
+ }
1547
+ }
1548
+ function buildShareHtml(cwd, session, blocks) {
1549
+ if (blocks.length === 1) {
1550
+ const only = blocks[0];
1551
+ if (only?.type === "iframe") {
1552
+ const doc = readIframeArtifact(cwd, only.src);
1553
+ if (doc) return doc;
1554
+ }
1555
+ }
1556
+ const list = blocks;
1557
+ const sections = list.map((b) => renderShareSection(cwd, b)).join("\n");
1558
+ const hasMd = list.some((b) => b?.type === "markdown");
1559
+ const hasMermaid = list.some((b) => b?.type === "mermaid");
1560
+ return `<!DOCTYPE html>
1561
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
1562
+ <title>${escHtml2(session)}</title>
1563
+ <style>
1564
+ body { margin:0; font-family: ui-sans-serif, system-ui, sans-serif; background:#0b0e14; color:#e6e9ef; }
1565
+ .db { padding:20px 24px; border-bottom:1px solid #1e2433; }
1566
+ .db-title { font-size:13px; font-weight:600; margin:0 0 12px; color:#9aa4b2; text-transform:uppercase; letter-spacing:.05em; }
1567
+ .db-frame { width:100%; height:70vh; border:0; border-radius:8px; background:#fff; }
1568
+ .db-html { background:#fff; color:#111; border-radius:8px; padding:16px; overflow:auto; }
1569
+ ul { line-height:1.7; }
1570
+ .mermaid { background:#fff; border-radius:8px; padding:16px; }
1571
+ </style></head>
1572
+ <body>
1573
+ ${sections}
1574
+ ${hasMd ? `<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
1575
+ <script>document.querySelectorAll('.db-md').forEach(function(el){try{el.innerHTML=marked.parse(el.dataset.md||'')}catch(e){el.textContent=el.dataset.md||''}});</script>` : ""}
1576
+ ${hasMermaid ? `<script type="module">import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';mermaid.initialize({startOnLoad:true,theme:'dark'});</script>` : ""}
1577
+ </body></html>`;
1271
1578
  }
1272
1579
  function consumeRenderError() {
1273
1580
  const err = lastRenderError;
@@ -1305,23 +1612,23 @@ function broadcastToClients(message) {
1305
1612
  }
1306
1613
  }
1307
1614
  function serveStatic(res, filePath) {
1308
- if (!import_node_fs3.default.existsSync(filePath) || !import_node_fs3.default.statSync(filePath).isFile()) return false;
1309
- const ext = import_node_path3.default.extname(filePath).toLowerCase();
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();
1310
1617
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
1311
1618
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
1312
- import_node_fs3.default.createReadStream(filePath).pipe(res);
1619
+ import_node_fs4.default.createReadStream(filePath).pipe(res);
1313
1620
  return true;
1314
1621
  }
1315
1622
  function resolveClientDir() {
1316
- const sibling = import_node_path3.default.join(__dirname, "..", "deck-client");
1317
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(sibling, "assets"))) return sibling;
1318
- const devDist = import_node_path3.default.join(__dirname, "..", "..", "dist", "deck-client");
1319
- if (import_node_fs3.default.existsSync(import_node_path3.default.join(devDist, "assets"))) return devDist;
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;
1320
1627
  return sibling;
1321
1628
  }
1322
1629
  function serveIndex(res, clientDir) {
1323
- const indexPath = import_node_path3.default.join(clientDir, "index.html");
1324
- if (!import_node_fs3.default.existsSync(indexPath)) {
1630
+ const indexPath = import_node_path4.default.join(clientDir, "index.html");
1631
+ if (!import_node_fs4.default.existsSync(indexPath)) {
1325
1632
  res.writeHead(500, { "Content-Type": "text/plain" });
1326
1633
  res.end(`LaunchDeck client bundle not found at ${clientDir}. Run 'npm run build:client'.`);
1327
1634
  return;
@@ -1425,15 +1732,15 @@ async function startDeckServer(opts = {}) {
1425
1732
  const baseUrl = `/deck-files/${sessionEncoded}`;
1426
1733
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
1427
1734
  const report = generateBlastRadiusReport(block.manifest);
1428
- const dir = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1429
- import_node_fs3.default.mkdirSync(dir, { recursive: true });
1430
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, "blast-radius.html"), html);
1431
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, "blast-radius-report.md"), report);
1432
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
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));
1433
1740
  const contractData = generateContract(block.manifest);
1434
1741
  const contractMd = contractToMarkdown(contractData);
1435
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, "contract.json"), JSON.stringify(contractData, null, 2));
1436
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, "contract.md"), contractMd);
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);
1437
1744
  block.type = "iframe";
1438
1745
  block.src = `${baseUrl}/blast-radius.html`;
1439
1746
  delete block.manifest;
@@ -1460,9 +1767,9 @@ async function startDeckServer(opts = {}) {
1460
1767
  const sessionEncoded = encodeURIComponent(body.session);
1461
1768
  const baseUrl = `/deck-files/${sessionEncoded}`;
1462
1769
  const filename = `${slugify(block.label)}.html`;
1463
- const dir = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
1464
- import_node_fs3.default.mkdirSync(dir, { recursive: true });
1465
- import_node_fs3.default.writeFileSync(import_node_path3.default.join(dir, filename), 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);
1466
1773
  block.type = "iframe";
1467
1774
  block.src = `${baseUrl}/${filename}`;
1468
1775
  delete block.content;
@@ -1494,6 +1801,79 @@ async function startDeckServer(opts = {}) {
1494
1801
  jsonResponse(res, 200, { sessions: listPersistedSessions(cwd) });
1495
1802
  return;
1496
1803
  }
1804
+ const shareMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)\/share$/);
1805
+ if (shareMatch && req.method === "POST") {
1806
+ const id = decodeURIComponent(shareMatch[1]);
1807
+ const persisted = readPersistedSession(cwd, id);
1808
+ if (!persisted) {
1809
+ jsonResponse(res, 404, { ok: false, error: "session not found" });
1810
+ return;
1811
+ }
1812
+ 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;
1820
+ }
1821
+ try {
1822
+ const blocks = Array.isArray(persisted.blocks) ? persisted.blocks : [];
1823
+ const html = buildShareHtml(cwd, id, blocks);
1824
+ const types = blocks.map((b) => b?.type).filter(Boolean);
1825
+ 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
+ const input = { title: id, body, html, sessionId: id, blockCount: blocks.length };
1827
+ 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));
1836
+ }
1837
+ } else {
1838
+ ({ id: resourceId } = await writeDeck(input));
1839
+ }
1840
+ writeSyncRecord(cwd, id, {
1841
+ resourceId,
1842
+ sharedAt: (/* @__PURE__ */ new Date()).toISOString(),
1843
+ version: persisted.version
1844
+ });
1845
+ jsonResponse(res, 200, { ok: true, resourceId, updated });
1846
+ } catch (err) {
1847
+ jsonResponse(res, 500, { ok: false, error: String(err) });
1848
+ }
1849
+ return;
1850
+ }
1851
+ const remoteDelMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)\/remote$/);
1852
+ if (remoteDelMatch && req.method === "DELETE") {
1853
+ const id = decodeURIComponent(remoteDelMatch[1]);
1854
+ const sync = readSyncRecord(cwd, id);
1855
+ if (!sync?.resourceId) {
1856
+ jsonResponse(res, 200, { ok: true, deleted: false, reason: "not shared" });
1857
+ return;
1858
+ }
1859
+ try {
1860
+ loadMcpConfig(cwd);
1861
+ } catch (err) {
1862
+ jsonResponse(res, 400, { ok: false, error: `Cloud delete needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}` });
1863
+ return;
1864
+ }
1865
+ try {
1866
+ await deleteDeck(sync.resourceId);
1867
+ try {
1868
+ import_node_fs4.default.rmSync(syncJsonPath(cwd, id), { force: true });
1869
+ } catch {
1870
+ }
1871
+ jsonResponse(res, 200, { ok: true, deleted: true });
1872
+ } catch (err) {
1873
+ jsonResponse(res, 500, { ok: false, error: String(err) });
1874
+ }
1875
+ return;
1876
+ }
1497
1877
  const sessionMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)$/);
1498
1878
  if (sessionMatch) {
1499
1879
  const id = decodeURIComponent(sessionMatch[1]);
@@ -1596,7 +1976,7 @@ async function startDeckServer(opts = {}) {
1596
1976
  res.end("Forbidden");
1597
1977
  return;
1598
1978
  }
1599
- const filePath = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1979
+ const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1600
1980
  if (serveStatic(res, filePath)) return;
1601
1981
  res.writeHead(404);
1602
1982
  res.end("Not found");
@@ -1609,25 +1989,25 @@ async function startDeckServer(opts = {}) {
1609
1989
  res.end("Forbidden");
1610
1990
  return;
1611
1991
  }
1612
- const filePath = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
1613
- if (!import_node_fs3.default.existsSync(filePath) || !import_node_fs3.default.statSync(filePath).isFile()) {
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()) {
1614
1994
  res.writeHead(404);
1615
1995
  res.end("Not found");
1616
1996
  return;
1617
1997
  }
1618
- const ext = import_node_path3.default.extname(filePath).toLowerCase();
1998
+ const ext = import_node_path4.default.extname(filePath).toLowerCase();
1619
1999
  const mime = MIME_TYPES[ext] ?? "application/octet-stream";
1620
- const filename = import_node_path3.default.basename(filePath);
2000
+ const filename = import_node_path4.default.basename(filePath);
1621
2001
  res.writeHead(200, {
1622
2002
  "Content-Type": mime,
1623
2003
  "Content-Disposition": `attachment; filename="${filename}"`,
1624
2004
  "Cache-Control": "no-cache"
1625
2005
  });
1626
- import_node_fs3.default.createReadStream(filePath).pipe(res);
2006
+ import_node_fs4.default.createReadStream(filePath).pipe(res);
1627
2007
  return;
1628
2008
  }
1629
2009
  if (url2.pathname !== "/") {
1630
- const staticPath = import_node_path3.default.join(clientDir, url2.pathname);
2010
+ const staticPath = import_node_path4.default.join(clientDir, url2.pathname);
1631
2011
  if (serveStatic(res, staticPath)) return;
1632
2012
  }
1633
2013
  serveIndex(res, clientDir);
@@ -1726,13 +2106,13 @@ function runServeCli(argv) {
1726
2106
  process.exit(1);
1727
2107
  });
1728
2108
  }
1729
- var import_node_http, import_node_fs3, import_node_path3, import_ws, DEFAULT_PORT, MAX_PORT_SCAN, MIME_TYPES, pendingFeedback, lastRenderError, wss;
2109
+ var import_node_http, import_node_fs4, import_node_path4, import_ws, DEFAULT_PORT, MAX_PORT_SCAN, MIME_TYPES, pendingFeedback, lastRenderError, wss;
1730
2110
  var init_deck_serve = __esm({
1731
2111
  "src/server/deck-serve.ts"() {
1732
2112
  "use strict";
1733
2113
  import_node_http = __toESM(require("node:http"));
1734
- import_node_fs3 = __toESM(require("node:fs"));
1735
- import_node_path3 = __toESM(require("node:path"));
2114
+ import_node_fs4 = __toESM(require("node:fs"));
2115
+ import_node_path4 = __toESM(require("node:path"));
1736
2116
  import_ws = require("ws");
1737
2117
  init_launch_kit_paths();
1738
2118
  init_deck_lockfile();
@@ -1740,6 +2120,7 @@ var init_deck_serve = __esm({
1740
2120
  init_blast_radius_render();
1741
2121
  init_contract_generator();
1742
2122
  init_rich_html_render();
2123
+ init_mcp_client();
1743
2124
  DEFAULT_PORT = 52829;
1744
2125
  MAX_PORT_SCAN = 3;
1745
2126
  MIME_TYPES = {
@@ -1825,15 +2206,15 @@ async function handleTool(name, args) {
1825
2206
  const baseUrl = `/deck-files/${sessionEncoded}`;
1826
2207
  const html = generateBlastRadiusHtml(block.manifest, baseUrl);
1827
2208
  const report = generateBlastRadiusReport(block.manifest);
1828
- const dir = (0, import_node_path4.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
1829
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1830
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "blast-radius.html"), html);
1831
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "blast-radius-report.md"), report);
1832
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
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));
1833
2214
  const contractData = generateContract(block.manifest);
1834
2215
  const contractMd = contractToMarkdown(contractData);
1835
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "contract.json"), JSON.stringify(contractData, null, 2));
1836
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "contract.md"), contractMd);
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);
1837
2218
  block.type = "iframe";
1838
2219
  block.src = `${baseUrl}/blast-radius.html`;
1839
2220
  delete block.manifest;
@@ -1860,9 +2241,9 @@ async function handleTool(name, args) {
1860
2241
  const sessionEncoded = encodeURIComponent(session);
1861
2242
  const baseUrl = `/deck-files/${sessionEncoded}`;
1862
2243
  const filename = `${slugify(block.label)}.html`;
1863
- const dir = (0, import_node_path4.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
1864
- import_node_fs4.default.mkdirSync(dir, { recursive: true });
1865
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, filename), 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);
1866
2247
  block.type = "iframe";
1867
2248
  block.src = `${baseUrl}/${filename}`;
1868
2249
  delete block.content;
@@ -1956,11 +2337,11 @@ async function handleTool(name, args) {
1956
2337
  }));
1957
2338
  }
1958
2339
  try {
1959
- const logDir = (0, import_node_path4.join)((0, import_node_os2.homedir)(), LAUNCHSECURE_DIR);
1960
- (0, import_node_fs5.mkdirSync)(logDir, { recursive: true });
1961
- const logPath = (0, import_node_path4.join)(logDir, "launch-deck.log");
1962
- const out = (0, import_node_fs5.openSync)(logPath, "a");
1963
- const err = (0, import_node_fs5.openSync)(logPath, "a");
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");
1964
2345
  const entryPath = process.argv[1];
1965
2346
  const config = loadDeckConfig(projectRoot);
1966
2347
  const resolvedPort = args.port ?? config.port;
@@ -2016,21 +2397,21 @@ async function handleTool(name, args) {
2016
2397
  }
2017
2398
  case "generate_contract": {
2018
2399
  const session = args.session;
2019
- const dir = (0, import_node_path4.join)(projectRoot, LAUNCHSECURE_DIR, "deck-files", session);
2020
- const manifestPath = (0, import_node_path4.join)(dir, "blast-radius-manifest.json");
2021
- if (!import_node_fs4.default.existsSync(manifestPath)) {
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)) {
2022
2403
  return text(JSON.stringify({
2023
2404
  error: `No blast radius manifest found for session "${session}". Push a blast-radius block first.`,
2024
2405
  hint: "Use the deck tool with a blast-radius block type to create a session."
2025
2406
  }));
2026
2407
  }
2027
2408
  try {
2028
- const raw = import_node_fs4.default.readFileSync(manifestPath, "utf-8");
2409
+ const raw = import_node_fs5.default.readFileSync(manifestPath, "utf-8");
2029
2410
  const manifest = JSON.parse(raw);
2030
2411
  const contract = generateContract(manifest);
2031
2412
  const markdown = contractToMarkdown(contract);
2032
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "contract.json"), JSON.stringify(contract, null, 2));
2033
- import_node_fs4.default.writeFileSync((0, import_node_path4.join)(dir, "contract.md"), markdown);
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);
2034
2415
  return text(JSON.stringify(contract));
2035
2416
  } catch (err) {
2036
2417
  return text(JSON.stringify({ error: `Failed to generate contract: ${err}` }));
@@ -2121,15 +2502,15 @@ function startDeckMcpServer() {
2121
2502
  process.exit(0);
2122
2503
  });
2123
2504
  }
2124
- var import_node_http2, import_node_fs4, import_node_path4, import_node_child_process2, import_node_fs5, import_node_os2, SERVER_INFO, TOOLS;
2505
+ var import_node_http2, import_node_fs5, import_node_path5, import_node_child_process2, import_node_fs6, import_node_os2, SERVER_INFO, TOOLS;
2125
2506
  var init_deck_mcp = __esm({
2126
2507
  "src/server/deck-mcp.ts"() {
2127
2508
  "use strict";
2128
2509
  import_node_http2 = __toESM(require("node:http"));
2129
- import_node_fs4 = __toESM(require("node:fs"));
2130
- import_node_path4 = require("node:path");
2510
+ import_node_fs5 = __toESM(require("node:fs"));
2511
+ import_node_path5 = require("node:path");
2131
2512
  import_node_child_process2 = require("node:child_process");
2132
- import_node_fs5 = require("node:fs");
2513
+ import_node_fs6 = require("node:fs");
2133
2514
  import_node_os2 = require("node:os");
2134
2515
  init_launch_kit_paths();
2135
2516
  init_deck_lockfile();