@launchsecure/launch-kit 0.0.30 → 0.0.31

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 (102) hide show
  1. package/dist/beacon/beacon.mjs +934 -865
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +9 -9
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/internal/screenshot.d.ts +19 -1
  6. package/dist/beacon/types/internal/screenshot.d.ts.map +1 -1
  7. package/dist/beacon/types/plugins/domEle.d.ts.map +1 -1
  8. package/dist/chart-client/assets/{index-CJ4mgRRF.css → index-CDIhdgWg.css} +1 -1
  9. package/dist/chart-client/index.html +2 -2
  10. package/dist/client/assets/{index-DI5qSR_w.css → index-CfW4n40I.css} +1 -1
  11. package/dist/client/index.html +2 -2
  12. package/dist/council-client/assets/{index-C_-vAM9L.css → index-CZim6x1u.css} +1 -1
  13. package/dist/council-client/index.html +2 -2
  14. package/dist/deck-client/assets/{_baseUniq-DCt2IMRR.js → _baseUniq-DdHaBFYO.js} +1 -1
  15. package/dist/deck-client/assets/{arc-h-ifqmNR.js → arc-D98e_18X.js} +1 -1
  16. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-C9dITSPv.js → architectureDiagram-Q4EWVU46-DNFZzh-4.js} +1 -1
  17. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-BHuJT34t.js → blockDiagram-DXYQGD6D-DeQvGUdX.js} +1 -1
  18. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CpvMGtDG.js → c4Diagram-AHTNJAMY-B6ekZf1n.js} +1 -1
  19. package/dist/deck-client/assets/channel-DmR7Tyyt.js +1 -0
  20. package/dist/deck-client/assets/{chunk-4BX2VUAB-B6md1VIm.js → chunk-4BX2VUAB-9aDWymq2.js} +1 -1
  21. package/dist/deck-client/assets/{chunk-4TB4RGXK-BmEnX8ik.js → chunk-4TB4RGXK-DtKQqaI7.js} +1 -1
  22. package/dist/deck-client/assets/{chunk-55IACEB6-BZPUyZAZ.js → chunk-55IACEB6-COy9hEae.js} +1 -1
  23. package/dist/deck-client/assets/{chunk-EDXVE4YY-BWwNUK-l.js → chunk-EDXVE4YY-D_f861An.js} +1 -1
  24. package/dist/deck-client/assets/{chunk-FMBD7UC4-o7gSppGI.js → chunk-FMBD7UC4-CmuA5UKn.js} +1 -1
  25. package/dist/deck-client/assets/{chunk-OYMX7WX6-C4KoTL5p.js → chunk-OYMX7WX6-vT8z8D-0.js} +1 -1
  26. package/dist/deck-client/assets/{chunk-QZHKN3VN-jkf68sDs.js → chunk-QZHKN3VN-CTlwwg-R.js} +1 -1
  27. package/dist/deck-client/assets/{chunk-YZCP3GAM-Cd4yBE7o.js → chunk-YZCP3GAM-C44yr620.js} +1 -1
  28. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-Bl4ozQWs.js +1 -0
  29. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-Bl4ozQWs.js +1 -0
  30. package/dist/deck-client/assets/clone-BAy58j24.js +1 -0
  31. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DeGFUgAV.js → cose-bilkent-S5V4N54A-DBB2J2nL.js} +1 -1
  32. package/dist/deck-client/assets/{dagre-KV5264BT-ekcYJuUV.js → dagre-KV5264BT-DxDTYbKl.js} +1 -1
  33. package/dist/deck-client/assets/{diagram-5BDNPKRD-YHPk4rV2.js → diagram-5BDNPKRD-DByWrWd1.js} +1 -1
  34. package/dist/deck-client/assets/{diagram-G4DWMVQ6-DM-JCd_B.js → diagram-G4DWMVQ6-B8B6ddMq.js} +1 -1
  35. package/dist/deck-client/assets/{diagram-MMDJMWI5-l5FK1ybk.js → diagram-MMDJMWI5-BMUZ2PWK.js} +1 -1
  36. package/dist/deck-client/assets/{diagram-TYMM5635-CIN4_1-j.js → diagram-TYMM5635-Bk9e8BB-.js} +1 -1
  37. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-MyinSkEl.js → erDiagram-SMLLAGMA-DcOSwSol.js} +1 -1
  38. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-Dk8nn42x.js → flowDiagram-DWJPFMVM-DI-4BR0F.js} +1 -1
  39. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-BU1ihicu.js → ganttDiagram-T4ZO3ILL-BeZuXBoU.js} +1 -1
  40. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-BjsTL13C.js → gitGraphDiagram-UUTBAWPF-Bcki__f-.js} +1 -1
  41. package/dist/deck-client/assets/{graph-DJmh-xi7.js → graph-CifKx6G1.js} +1 -1
  42. package/dist/deck-client/assets/index-6sdqbm2o.js +2 -0
  43. package/dist/deck-client/assets/{index-DsIZ3LqL.css → index-BlTlhxFW.css} +1 -1
  44. package/dist/deck-client/assets/index-CB-qlwRT.js +1195 -0
  45. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-Dxvy_RB4.js → infoDiagram-42DDH7IO-CReN1nFN.js} +1 -1
  46. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DPOaNF1l.js → ishikawaDiagram-UXIWVN3A-CDF_VLN_.js} +1 -1
  47. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DMew3K5c.js → journeyDiagram-VCZTEJTY-DwgGrNVB.js} +1 -1
  48. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-csciJFuk.js → kanban-definition-6JOO6SKY-DB_zohh5.js} +1 -1
  49. package/dist/deck-client/assets/{layout-Dg4yyms2.js → layout-DFfX1O3z.js} +1 -1
  50. package/dist/deck-client/assets/{linear-BA3zU6gq.js → linear-CtKb4EXj.js} +1 -1
  51. package/dist/deck-client/assets/{min-lz-Ird-p.js → min-DCRRwUZv.js} +1 -1
  52. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CCEN8OQV.js → mindmap-definition-QFDTVHPH-D0QBOiFe.js} +1 -1
  53. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DM6n1HY7.js → pieDiagram-DEJITSTG-CD-EV5WB.js} +1 -1
  54. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-_ULoR66n.js → quadrantDiagram-34T5L4WZ-B-JXZ8xI.js} +1 -1
  55. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BuwJs7Tn.js → requirementDiagram-MS252O5E-D2_OK5Dp.js} +1 -1
  56. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BEsuzkW4.js → sankeyDiagram-XADWPNL6-BbBJqVSC.js} +1 -1
  57. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-CP2H0YWf.js → sequenceDiagram-FGHM5R23-Db8A-Rkk.js} +1 -1
  58. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-B5Gw_NNL.js → stateDiagram-FHFEXIEX-DGJnanjS.js} +1 -1
  59. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CR7riiab.js +1 -0
  60. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DsoYydQa.js → timeline-definition-GMOUNBTQ-BRkr6T4w.js} +1 -1
  61. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-Dz8JT_ob.js → vennDiagram-DHZGUBPP-d0rsTqFo.js} +1 -1
  62. package/dist/deck-client/assets/wardley-RL74JXVD-2t7cMqdS.js +162 -0
  63. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-DN1LJMB1.js → wardleyDiagram-NUSXRM2D-DzboAsHh.js} +1 -1
  64. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-nb0oSfrQ.js → xychartDiagram-5P7HB3ND-CgTP9u2V.js} +1 -1
  65. package/dist/deck-client/index.html +2 -2
  66. package/dist/server/cli.js +666 -12
  67. package/dist/server/council-entry.js +0 -0
  68. package/dist/server/deck-mcp-entry.js +141 -21
  69. package/dist/server/deck-serve.js +141 -21
  70. package/dist/server/fb-wizard.js +0 -0
  71. package/dist/server/graph-mcp-entry.js +666 -12
  72. package/dist/server/init-entry.js +15 -9
  73. package/package.json +23 -21
  74. package/scaffolds/ls-marketplace/plugins/kit/skills/analyse/SKILL.md +180 -0
  75. package/scaffolds/ls-marketplace/plugins/kit/skills/{blast-radius.md → blast-radius/SKILL.md} +28 -12
  76. package/scaffolds/ls-marketplace/plugins/kit/skills/{debug.md → debug/SKILL.md} +2 -9
  77. package/scaffolds/ls-marketplace/plugins/kit/skills/{diagram.md → diagram/SKILL.md} +27 -9
  78. package/scaffolds/ls-marketplace/plugins/kit/skills/{prototype.md → prototype/SKILL.md} +21 -1
  79. package/scaffolds/ls-marketplace/plugins/kit/skills/recovery/SKILL.md +95 -0
  80. package/scaffolds/ls-marketplace/plugins/kit/skills/{wireframe.md → wireframe/SKILL.md} +21 -1
  81. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
  82. package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
  83. package/dist/deck-client/assets/channel-2PZVMiXf.js +0 -1
  84. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-Bt8xBAof.js +0 -1
  85. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-Bt8xBAof.js +0 -1
  86. package/dist/deck-client/assets/clone-BHQryoDl.js +0 -1
  87. package/dist/deck-client/assets/index-KsShfCV-.js +0 -476
  88. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-4T4wMDXr.js +0 -1
  89. package/dist/deck-client/assets/wardley-RL74JXVD-DGHQ_Ijv.js +0 -162
  90. package/scaffolds/ls-marketplace/plugins/kit/skills/recall.md +0 -83
  91. /package/dist/chart-client/assets/{index-Ccy-DpI-.js → index-B__ARB8k.js} +0 -0
  92. /package/dist/client/assets/{index-Dp0_okva.js → index-h8kMzVtG.js} +0 -0
  93. /package/dist/council-client/assets/{index-Dt4zWKSj.js → index-CWaDcsFR.js} +0 -0
  94. /package/scaffolds/ls-marketplace/plugins/kit/skills/{beacon-array.md → beacon-array/SKILL.md} +0 -0
  95. /package/scaffolds/ls-marketplace/plugins/kit/skills/{beacon-clear.md → beacon-clear/SKILL.md} +0 -0
  96. /package/scaffolds/ls-marketplace/plugins/kit/skills/{beacon-pulse.md → beacon-pulse/SKILL.md} +0 -0
  97. /package/scaffolds/ls-marketplace/plugins/kit/skills/{beacon-scan.md → beacon-scan/SKILL.md} +0 -0
  98. /package/scaffolds/ls-marketplace/plugins/kit/skills/{brief.md → brief/SKILL.md} +0 -0
  99. /package/scaffolds/ls-marketplace/plugins/kit/skills/{course.md → course/SKILL.md} +0 -0
  100. /package/scaffolds/ls-marketplace/plugins/kit/skills/{deploy-check.md → deploy-check/SKILL.md} +0 -0
  101. /package/scaffolds/ls-marketplace/plugins/kit/skills/{orbit.md → orbit/SKILL.md} +0 -0
  102. /package/scaffolds/ls-marketplace/plugins/kit/skills/{show-mcp-status.md → show-mcp-status/SKILL.md} +0 -0
File without changes
@@ -1183,6 +1183,67 @@ __export(deck_serve_exports, {
1183
1183
  runServeCli: () => runServeCli,
1184
1184
  startDeckServer: () => startDeckServer
1185
1185
  });
1186
+ function sessionDir(cwd, session) {
1187
+ return import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1188
+ }
1189
+ function sessionJsonPath(cwd, session) {
1190
+ return import_node_path3.default.join(sessionDir(cwd, session), "session.json");
1191
+ }
1192
+ function readPersistedSession(cwd, session) {
1193
+ try {
1194
+ const raw = import_node_fs3.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1195
+ return JSON.parse(raw);
1196
+ } catch {
1197
+ return null;
1198
+ }
1199
+ }
1200
+ function writePersistedSession(cwd, session, payload) {
1201
+ const dir = sessionDir(cwd, session);
1202
+ import_node_fs3.default.mkdirSync(dir, { recursive: true });
1203
+ const existing = readPersistedSession(cwd, session);
1204
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1205
+ const next = {
1206
+ id: session,
1207
+ mode: payload.mode,
1208
+ prompt: payload.prompt,
1209
+ blocks: payload.blocks,
1210
+ version: existing ? existing.version + 1 : 1,
1211
+ createdAt: existing?.createdAt ?? now,
1212
+ updatedAt: now
1213
+ };
1214
+ import_node_fs3.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1215
+ return next;
1216
+ }
1217
+ function listPersistedSessions(cwd) {
1218
+ const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1219
+ if (!import_node_fs3.default.existsSync(base)) return [];
1220
+ const entries = import_node_fs3.default.readdirSync(base, { withFileTypes: true });
1221
+ const sessions = [];
1222
+ for (const entry of entries) {
1223
+ if (!entry.isDirectory()) continue;
1224
+ const persisted = readPersistedSession(cwd, entry.name);
1225
+ if (!persisted) continue;
1226
+ sessions.push({
1227
+ id: persisted.id,
1228
+ mode: persisted.mode,
1229
+ blockCount: Array.isArray(persisted.blocks) ? persisted.blocks.length : 0,
1230
+ version: persisted.version,
1231
+ createdAt: persisted.createdAt,
1232
+ updatedAt: persisted.updatedAt
1233
+ });
1234
+ }
1235
+ sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1236
+ return sessions;
1237
+ }
1238
+ function deletePersistedSession(cwd, session) {
1239
+ const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1240
+ if (session === "all") {
1241
+ if (import_node_fs3.default.existsSync(base)) import_node_fs3.default.rmSync(base, { recursive: true, force: true });
1242
+ return;
1243
+ }
1244
+ const dir = sessionDir(cwd, session);
1245
+ if (import_node_fs3.default.existsSync(dir)) import_node_fs3.default.rmSync(dir, { recursive: true, force: true });
1246
+ }
1186
1247
  function consumeRenderError() {
1187
1248
  const err = lastRenderError;
1188
1249
  lastRenderError = null;
@@ -1226,6 +1287,13 @@ function serveStatic(res, filePath) {
1226
1287
  import_node_fs3.default.createReadStream(filePath).pipe(res);
1227
1288
  return true;
1228
1289
  }
1290
+ function resolveClientDir() {
1291
+ const sibling = import_node_path3.default.join(__dirname, "..", "deck-client");
1292
+ if (import_node_fs3.default.existsSync(import_node_path3.default.join(sibling, "assets"))) return sibling;
1293
+ const devDist = import_node_path3.default.join(__dirname, "..", "..", "dist", "deck-client");
1294
+ if (import_node_fs3.default.existsSync(import_node_path3.default.join(devDist, "assets"))) return devDist;
1295
+ return sibling;
1296
+ }
1229
1297
  function serveIndex(res, clientDir) {
1230
1298
  const indexPath = import_node_path3.default.join(clientDir, "index.html");
1231
1299
  if (!import_node_fs3.default.existsSync(indexPath)) {
@@ -1290,7 +1358,7 @@ async function startDeckServer(opts = {}) {
1290
1358
  }
1291
1359
  return { port: existing.port, url: existing.url };
1292
1360
  }
1293
- const clientDir = opts.clientDir ?? import_node_path3.default.join(__dirname, "..", "deck-client");
1361
+ const clientDir = opts.clientDir ?? resolveClientDir();
1294
1362
  const server = import_node_http.default.createServer(async (req, res) => {
1295
1363
  try {
1296
1364
  const url2 = new URL(req.url ?? "/", `http://${req.headers.host}`);
@@ -1372,16 +1440,46 @@ async function startDeckServer(opts = {}) {
1372
1440
  }
1373
1441
  }
1374
1442
  }
1443
+ const persisted = writePersistedSession(cwd, body.session, {
1444
+ mode: body.mode,
1445
+ prompt: body.prompt,
1446
+ blocks: body.blocks
1447
+ });
1375
1448
  broadcastToClients({
1376
1449
  type: "session",
1377
1450
  session: body.session,
1378
1451
  mode: body.mode,
1379
1452
  blocks: body.blocks,
1380
- prompt: body.prompt
1453
+ prompt: body.prompt,
1454
+ version: persisted.version
1381
1455
  });
1382
- jsonResponse(res, 200, { ok: true });
1456
+ jsonResponse(res, 200, { ok: true, version: persisted.version });
1457
+ return;
1458
+ }
1459
+ if (req.method === "GET" && url2.pathname === "/api/sessions") {
1460
+ jsonResponse(res, 200, { sessions: listPersistedSessions(cwd) });
1383
1461
  return;
1384
1462
  }
1463
+ const sessionMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)$/);
1464
+ if (sessionMatch) {
1465
+ const id = decodeURIComponent(sessionMatch[1]);
1466
+ if (req.method === "GET") {
1467
+ const persisted = readPersistedSession(cwd, id);
1468
+ if (!persisted) {
1469
+ jsonResponse(res, 404, { error: "not found" });
1470
+ return;
1471
+ }
1472
+ jsonResponse(res, 200, persisted);
1473
+ return;
1474
+ }
1475
+ if (req.method === "DELETE") {
1476
+ broadcastToClients({ type: "clear_session", session: id });
1477
+ resolveFeedback(id, { comment: "", closed: true });
1478
+ deletePersistedSession(cwd, id);
1479
+ jsonResponse(res, 200, { ok: true });
1480
+ return;
1481
+ }
1482
+ }
1385
1483
  if (req.method === "POST" && url2.pathname === "/api/await-feedback") {
1386
1484
  const { session } = JSON.parse(await readBody(req));
1387
1485
  try {
@@ -1392,6 +1490,45 @@ async function startDeckServer(opts = {}) {
1392
1490
  }
1393
1491
  return;
1394
1492
  }
1493
+ if (req.method === "POST" && url2.pathname === "/api/feedback") {
1494
+ try {
1495
+ const payload = JSON.parse(await readBody(req));
1496
+ const sessionId = payload.context?.sessionId;
1497
+ if (!sessionId) {
1498
+ jsonResponse(res, 400, { ok: false, error: "context.sessionId is required" });
1499
+ return;
1500
+ }
1501
+ const verdictMap = {
1502
+ bug: "blocker",
1503
+ idea: "iterate",
1504
+ ux: "note",
1505
+ a11y: "approve"
1506
+ };
1507
+ const domEle = payload.plugins?.domEle;
1508
+ const domSS = payload.plugins?.domSS;
1509
+ const picks = Array.isArray(domEle?.pins) ? domEle.pins.filter((p) => p && typeof p.selector === "string").map((p) => ({
1510
+ selector: p.selector,
1511
+ ...p.note ? { note: p.note } : {},
1512
+ ...p.boundingRect ? { rect: p.boundingRect } : {}
1513
+ })) : [];
1514
+ const response = {
1515
+ comment: payload.description ?? "",
1516
+ selections: payload.context?.selections,
1517
+ verdict: payload.severity ? verdictMap[payload.severity] : void 0,
1518
+ picks: picks.length > 0 ? picks : void 0,
1519
+ screenshot: typeof domSS?.dataUrl === "string" ? domSS.dataUrl : void 0
1520
+ };
1521
+ const resolved = resolveFeedback(sessionId, response);
1522
+ if (!resolved) {
1523
+ jsonResponse(res, 409, { ok: false, error: `no pending await_feedback for session "${sessionId}"` });
1524
+ return;
1525
+ }
1526
+ jsonResponse(res, 200, { ok: true });
1527
+ } catch (err) {
1528
+ jsonResponse(res, 400, { ok: false, error: String(err) });
1529
+ }
1530
+ return;
1531
+ }
1395
1532
  if (req.method === "POST" && url2.pathname === "/api/clear-session") {
1396
1533
  const { session } = JSON.parse(await readBody(req));
1397
1534
  broadcastToClients({
@@ -1407,17 +1544,7 @@ async function startDeckServer(opts = {}) {
1407
1544
  resolveFeedback(session, { comment: "", closed: true });
1408
1545
  }
1409
1546
  try {
1410
- const deckFilesBase = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1411
- if (session === "all") {
1412
- if (import_node_fs3.default.existsSync(deckFilesBase)) {
1413
- import_node_fs3.default.rmSync(deckFilesBase, { recursive: true, force: true });
1414
- }
1415
- } else {
1416
- const sessionDir = import_node_path3.default.join(deckFilesBase, session);
1417
- if (import_node_fs3.default.existsSync(sessionDir)) {
1418
- import_node_fs3.default.rmSync(sessionDir, { recursive: true, force: true });
1419
- }
1420
- }
1547
+ deletePersistedSession(cwd, session);
1421
1548
  } catch {
1422
1549
  }
1423
1550
  jsonResponse(res, 200, { ok: true });
@@ -1532,13 +1659,6 @@ async function startDeckServer(opts = {}) {
1532
1659
  waiter.reject(new Error("LaunchDeck server shutting down"));
1533
1660
  pendingFeedback.delete(id);
1534
1661
  }
1535
- try {
1536
- const deckFilesBase = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1537
- if (import_node_fs3.default.existsSync(deckFilesBase)) {
1538
- import_node_fs3.default.rmSync(deckFilesBase, { recursive: true, force: true });
1539
- }
1540
- } catch {
1541
- }
1542
1662
  clearLock(cwd);
1543
1663
  server.close();
1544
1664
  };
@@ -1163,6 +1163,67 @@ var MIME_TYPES = {
1163
1163
  ".woff": "font/woff",
1164
1164
  ".woff2": "font/woff2"
1165
1165
  };
1166
+ function sessionDir(cwd, session) {
1167
+ return import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
1168
+ }
1169
+ function sessionJsonPath(cwd, session) {
1170
+ return import_node_path3.default.join(sessionDir(cwd, session), "session.json");
1171
+ }
1172
+ function readPersistedSession(cwd, session) {
1173
+ try {
1174
+ const raw = import_node_fs3.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
1175
+ return JSON.parse(raw);
1176
+ } catch {
1177
+ return null;
1178
+ }
1179
+ }
1180
+ function writePersistedSession(cwd, session, payload) {
1181
+ const dir = sessionDir(cwd, session);
1182
+ import_node_fs3.default.mkdirSync(dir, { recursive: true });
1183
+ const existing = readPersistedSession(cwd, session);
1184
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1185
+ const next = {
1186
+ id: session,
1187
+ mode: payload.mode,
1188
+ prompt: payload.prompt,
1189
+ blocks: payload.blocks,
1190
+ version: existing ? existing.version + 1 : 1,
1191
+ createdAt: existing?.createdAt ?? now,
1192
+ updatedAt: now
1193
+ };
1194
+ import_node_fs3.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
1195
+ return next;
1196
+ }
1197
+ function listPersistedSessions(cwd) {
1198
+ const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1199
+ if (!import_node_fs3.default.existsSync(base)) return [];
1200
+ const entries = import_node_fs3.default.readdirSync(base, { withFileTypes: true });
1201
+ const sessions = [];
1202
+ for (const entry of entries) {
1203
+ if (!entry.isDirectory()) continue;
1204
+ const persisted = readPersistedSession(cwd, entry.name);
1205
+ if (!persisted) continue;
1206
+ sessions.push({
1207
+ id: persisted.id,
1208
+ mode: persisted.mode,
1209
+ blockCount: Array.isArray(persisted.blocks) ? persisted.blocks.length : 0,
1210
+ version: persisted.version,
1211
+ createdAt: persisted.createdAt,
1212
+ updatedAt: persisted.updatedAt
1213
+ });
1214
+ }
1215
+ sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
1216
+ return sessions;
1217
+ }
1218
+ function deletePersistedSession(cwd, session) {
1219
+ const base = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1220
+ if (session === "all") {
1221
+ if (import_node_fs3.default.existsSync(base)) import_node_fs3.default.rmSync(base, { recursive: true, force: true });
1222
+ return;
1223
+ }
1224
+ const dir = sessionDir(cwd, session);
1225
+ if (import_node_fs3.default.existsSync(dir)) import_node_fs3.default.rmSync(dir, { recursive: true, force: true });
1226
+ }
1166
1227
  var pendingFeedback = /* @__PURE__ */ new Map();
1167
1228
  var lastRenderError = null;
1168
1229
  function consumeRenderError() {
@@ -1209,6 +1270,13 @@ function serveStatic(res, filePath) {
1209
1270
  import_node_fs3.default.createReadStream(filePath).pipe(res);
1210
1271
  return true;
1211
1272
  }
1273
+ function resolveClientDir() {
1274
+ const sibling = import_node_path3.default.join(__dirname, "..", "deck-client");
1275
+ if (import_node_fs3.default.existsSync(import_node_path3.default.join(sibling, "assets"))) return sibling;
1276
+ const devDist = import_node_path3.default.join(__dirname, "..", "..", "dist", "deck-client");
1277
+ if (import_node_fs3.default.existsSync(import_node_path3.default.join(devDist, "assets"))) return devDist;
1278
+ return sibling;
1279
+ }
1212
1280
  function serveIndex(res, clientDir) {
1213
1281
  const indexPath = import_node_path3.default.join(clientDir, "index.html");
1214
1282
  if (!import_node_fs3.default.existsSync(indexPath)) {
@@ -1273,7 +1341,7 @@ async function startDeckServer(opts = {}) {
1273
1341
  }
1274
1342
  return { port: existing.port, url: existing.url };
1275
1343
  }
1276
- const clientDir = opts.clientDir ?? import_node_path3.default.join(__dirname, "..", "deck-client");
1344
+ const clientDir = opts.clientDir ?? resolveClientDir();
1277
1345
  const server = import_node_http.default.createServer(async (req, res) => {
1278
1346
  try {
1279
1347
  const url2 = new URL(req.url ?? "/", `http://${req.headers.host}`);
@@ -1355,16 +1423,46 @@ async function startDeckServer(opts = {}) {
1355
1423
  }
1356
1424
  }
1357
1425
  }
1426
+ const persisted = writePersistedSession(cwd, body.session, {
1427
+ mode: body.mode,
1428
+ prompt: body.prompt,
1429
+ blocks: body.blocks
1430
+ });
1358
1431
  broadcastToClients({
1359
1432
  type: "session",
1360
1433
  session: body.session,
1361
1434
  mode: body.mode,
1362
1435
  blocks: body.blocks,
1363
- prompt: body.prompt
1436
+ prompt: body.prompt,
1437
+ version: persisted.version
1364
1438
  });
1365
- jsonResponse(res, 200, { ok: true });
1439
+ jsonResponse(res, 200, { ok: true, version: persisted.version });
1440
+ return;
1441
+ }
1442
+ if (req.method === "GET" && url2.pathname === "/api/sessions") {
1443
+ jsonResponse(res, 200, { sessions: listPersistedSessions(cwd) });
1366
1444
  return;
1367
1445
  }
1446
+ const sessionMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)$/);
1447
+ if (sessionMatch) {
1448
+ const id = decodeURIComponent(sessionMatch[1]);
1449
+ if (req.method === "GET") {
1450
+ const persisted = readPersistedSession(cwd, id);
1451
+ if (!persisted) {
1452
+ jsonResponse(res, 404, { error: "not found" });
1453
+ return;
1454
+ }
1455
+ jsonResponse(res, 200, persisted);
1456
+ return;
1457
+ }
1458
+ if (req.method === "DELETE") {
1459
+ broadcastToClients({ type: "clear_session", session: id });
1460
+ resolveFeedback(id, { comment: "", closed: true });
1461
+ deletePersistedSession(cwd, id);
1462
+ jsonResponse(res, 200, { ok: true });
1463
+ return;
1464
+ }
1465
+ }
1368
1466
  if (req.method === "POST" && url2.pathname === "/api/await-feedback") {
1369
1467
  const { session } = JSON.parse(await readBody(req));
1370
1468
  try {
@@ -1375,6 +1473,45 @@ async function startDeckServer(opts = {}) {
1375
1473
  }
1376
1474
  return;
1377
1475
  }
1476
+ if (req.method === "POST" && url2.pathname === "/api/feedback") {
1477
+ try {
1478
+ const payload = JSON.parse(await readBody(req));
1479
+ const sessionId = payload.context?.sessionId;
1480
+ if (!sessionId) {
1481
+ jsonResponse(res, 400, { ok: false, error: "context.sessionId is required" });
1482
+ return;
1483
+ }
1484
+ const verdictMap = {
1485
+ bug: "blocker",
1486
+ idea: "iterate",
1487
+ ux: "note",
1488
+ a11y: "approve"
1489
+ };
1490
+ const domEle = payload.plugins?.domEle;
1491
+ const domSS = payload.plugins?.domSS;
1492
+ const picks = Array.isArray(domEle?.pins) ? domEle.pins.filter((p) => p && typeof p.selector === "string").map((p) => ({
1493
+ selector: p.selector,
1494
+ ...p.note ? { note: p.note } : {},
1495
+ ...p.boundingRect ? { rect: p.boundingRect } : {}
1496
+ })) : [];
1497
+ const response = {
1498
+ comment: payload.description ?? "",
1499
+ selections: payload.context?.selections,
1500
+ verdict: payload.severity ? verdictMap[payload.severity] : void 0,
1501
+ picks: picks.length > 0 ? picks : void 0,
1502
+ screenshot: typeof domSS?.dataUrl === "string" ? domSS.dataUrl : void 0
1503
+ };
1504
+ const resolved = resolveFeedback(sessionId, response);
1505
+ if (!resolved) {
1506
+ jsonResponse(res, 409, { ok: false, error: `no pending await_feedback for session "${sessionId}"` });
1507
+ return;
1508
+ }
1509
+ jsonResponse(res, 200, { ok: true });
1510
+ } catch (err) {
1511
+ jsonResponse(res, 400, { ok: false, error: String(err) });
1512
+ }
1513
+ return;
1514
+ }
1378
1515
  if (req.method === "POST" && url2.pathname === "/api/clear-session") {
1379
1516
  const { session } = JSON.parse(await readBody(req));
1380
1517
  broadcastToClients({
@@ -1390,17 +1527,7 @@ async function startDeckServer(opts = {}) {
1390
1527
  resolveFeedback(session, { comment: "", closed: true });
1391
1528
  }
1392
1529
  try {
1393
- const deckFilesBase = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1394
- if (session === "all") {
1395
- if (import_node_fs3.default.existsSync(deckFilesBase)) {
1396
- import_node_fs3.default.rmSync(deckFilesBase, { recursive: true, force: true });
1397
- }
1398
- } else {
1399
- const sessionDir = import_node_path3.default.join(deckFilesBase, session);
1400
- if (import_node_fs3.default.existsSync(sessionDir)) {
1401
- import_node_fs3.default.rmSync(sessionDir, { recursive: true, force: true });
1402
- }
1403
- }
1530
+ deletePersistedSession(cwd, session);
1404
1531
  } catch {
1405
1532
  }
1406
1533
  jsonResponse(res, 200, { ok: true });
@@ -1515,13 +1642,6 @@ async function startDeckServer(opts = {}) {
1515
1642
  waiter.reject(new Error("LaunchDeck server shutting down"));
1516
1643
  pendingFeedback.delete(id);
1517
1644
  }
1518
- try {
1519
- const deckFilesBase = import_node_path3.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
1520
- if (import_node_fs3.default.existsSync(deckFilesBase)) {
1521
- import_node_fs3.default.rmSync(deckFilesBase, { recursive: true, force: true });
1522
- }
1523
- } catch {
1524
- }
1525
1645
  clearLock(cwd);
1526
1646
  server.close();
1527
1647
  };
File without changes