@launchsecure/launch-kit 0.0.36 → 0.0.38
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.
- package/dist/chart-client/assets/index-B6rR0CWx.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-D6uX1lQe.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-CleYLarJ.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-BiVx0WO_.js → _baseUniq-CgW32Gdk.js} +1 -1
- package/dist/deck-client/assets/{arc-DGMkiEzS.js → arc-D-Mg9gvM.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Y2WRmHtk.js → architectureDiagram-Q4EWVU46-CdTsXsgl.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-_Lbfu5BQ.js → blockDiagram-DXYQGD6D-mwTneYyB.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CTqpYTBX.js → c4Diagram-AHTNJAMY-C4R8IbjO.js} +1 -1
- package/dist/deck-client/assets/channel-CuSee7GO.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-liEIbPHs.js → chunk-4BX2VUAB-ZWuRIUwb.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCc6lYvL.js → chunk-4TB4RGXK-PNHX10sF.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-D02jJUR2.js → chunk-55IACEB6-CD9MUgPr.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-BFmGMbLD.js → chunk-EDXVE4YY-C_CpORb3.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-6wFLOVcJ.js → chunk-FMBD7UC4-Bg5RoVC-.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-Bnr8RiBf.js → chunk-OYMX7WX6-DhTwgQwd.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Ct82MksJ.js → chunk-QZHKN3VN-C5VLMaFa.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-BXmN1diQ.js → chunk-YZCP3GAM-NAGHy4Sr.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-_kTisqzs.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-_kTisqzs.js +1 -0
- package/dist/deck-client/assets/clone-kb3zkY60.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CmQCT-mH.js → cose-bilkent-S5V4N54A-CpUczjZk.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-DDdSa9EX.js → dagre-KV5264BT-BOvb07MG.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-Bccks2xJ.js → diagram-5BDNPKRD-BPxwTiC-.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-CPPNgxmQ.js → diagram-G4DWMVQ6-Dz_gsHgx.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-KrD300pS.js → diagram-MMDJMWI5-B7z-oVTW.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-DefnLuQf.js → diagram-TYMM5635-CAIAglLQ.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DI9FfnFP.js → erDiagram-SMLLAGMA-BiViTWF3.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-twKyd3Fx.js → flowDiagram-DWJPFMVM-DYVemp0H.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-Wau3jhBr.js → ganttDiagram-T4ZO3ILL-Chc1Iyu1.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-D9GgYXwb.js → gitGraphDiagram-UUTBAWPF-B7eFgaj5.js} +1 -1
- package/dist/deck-client/assets/{graph-BhNLzyXS.js → graph-CKaIoNwb.js} +1 -1
- package/dist/deck-client/assets/index-55P73aS_.css +1 -0
- package/dist/deck-client/assets/{index-BtQBaQ7s.js → index-BRawc7RA.js} +49 -48
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-TylGlSG-.js → infoDiagram-42DDH7IO-BxsVq7vO.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DAT8icpg.js → ishikawaDiagram-UXIWVN3A-DAM7vPwa.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-D3v_XL72.js → journeyDiagram-VCZTEJTY-Xe20Nf7R.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DNUOBiNr.js → kanban-definition-6JOO6SKY-DS8YguzB.js} +1 -1
- package/dist/deck-client/assets/{layout-COfodgwF.js → layout-DKMBpzR-.js} +1 -1
- package/dist/deck-client/assets/{linear-DmTsuIvK.js → linear-DTNtBg5h.js} +1 -1
- package/dist/deck-client/assets/{min-BW1F7i1D.js → min-C4DrxCcA.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CErFzKWl.js → mindmap-definition-QFDTVHPH-B4nEtsw5.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DW5F757o.js → pieDiagram-DEJITSTG-BzHdGNu5.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B1S2-TfI.js → quadrantDiagram-34T5L4WZ-CaX0SD4-.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BY5BAR-5.js → requirementDiagram-MS252O5E-QeG4p2ni.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-CE1Cp9HS.js → sankeyDiagram-XADWPNL6-BoAwgAj-.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-IaHnbKye.js → sequenceDiagram-FGHM5R23-Dn4pYYgu.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CwPJm9hU.js → stateDiagram-FHFEXIEX-Is6KRmQV.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-Cy45Ttqq.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DVFGGSgN.js → timeline-definition-GMOUNBTQ-v64IZGuY.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-C1194MJi.js → vennDiagram-DHZGUBPP-noh9eouF.js} +1 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-cJ_1is2S.js +162 -0
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-hpwdFfGj.js → wardleyDiagram-NUSXRM2D-DxR4j737.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYkotwy8.js → xychartDiagram-5P7HB3ND-B26vodaL.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +25 -2
- package/dist/server/council-entry.js +86 -2
- package/dist/server/council-serve.js +81 -2
- package/dist/server/deck-mcp-entry.js +449 -68
- package/dist/server/deck-serve.js +411 -42
- package/dist/server/fb-wizard.js +0 -0
- package/dist/server/init-entry.js +147 -18
- package/dist/server/radar-docker-init-entry.js +139 -17
- package/dist/server/radar-entrypoint-entry.js +0 -0
- package/dist/server/radar-teardown-entry.js +0 -0
- package/dist/server/rover-entry.js +25 -4
- package/package.json +22 -23
- package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +5 -0
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
- package/dist/chart-client/assets/index-DpKO9p0s.css +0 -1
- package/dist/client/assets/index-Dv6dD2zY.css +0 -32
- package/dist/council-client/assets/index-AqQ9Sei6.css +0 -1
- package/dist/deck-client/assets/channel-DB6LxW_l.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/clone-DiIRH1pI.js +0 -1
- package/dist/deck-client/assets/index-B-YQq5b5.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +0 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +0 -162
- /package/dist/chart-client/assets/{index-DFu2xIrM.js → index-C_xCi3gW.js} +0 -0
- /package/dist/client/assets/{index-Cbw6bVdx.js → index-CRecYFUA.js} +0 -0
- /package/dist/council-client/assets/{index-CAsmGTzg.js → index-DO-Vn15O.js} +0 -0
|
@@ -39,8 +39,8 @@ __export(deck_serve_exports, {
|
|
|
39
39
|
});
|
|
40
40
|
module.exports = __toCommonJS(deck_serve_exports);
|
|
41
41
|
var import_node_http = __toESM(require("node:http"));
|
|
42
|
-
var
|
|
43
|
-
var
|
|
42
|
+
var import_node_fs4 = __toESM(require("node:fs"));
|
|
43
|
+
var import_node_path4 = __toESM(require("node:path"));
|
|
44
44
|
var import_ws = require("ws");
|
|
45
45
|
|
|
46
46
|
// src/server/launch-kit-paths.ts
|
|
@@ -704,9 +704,9 @@ function generateBlastRadiusReport(manifest) {
|
|
|
704
704
|
`;
|
|
705
705
|
for (const n of nodes) {
|
|
706
706
|
const ln = layerMap[n.layer] || n.layer;
|
|
707
|
-
const
|
|
707
|
+
const path3 = (n.path || "-").replace(/\|/g, "/");
|
|
708
708
|
const reason = (n.reason || "-").replace(/\|/g, "/");
|
|
709
|
-
md += `| ${n.name} | ${ln} | ${n.type || "-"} | ${
|
|
709
|
+
md += `| ${n.name} | ${ln} | ${n.type || "-"} | ${path3} | ${reason} |
|
|
710
710
|
`;
|
|
711
711
|
}
|
|
712
712
|
md += `
|
|
@@ -1174,6 +1174,216 @@ function slugify(label) {
|
|
|
1174
1174
|
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "block";
|
|
1175
1175
|
}
|
|
1176
1176
|
|
|
1177
|
+
// src/server/mcp-client.ts
|
|
1178
|
+
var import_node_fs3 = require("node:fs");
|
|
1179
|
+
var import_node_path3 = require("node:path");
|
|
1180
|
+
|
|
1181
|
+
// src/server/cred-shape.ts
|
|
1182
|
+
var fs = __toESM(require("node:fs"));
|
|
1183
|
+
var path = __toESM(require("node:path"));
|
|
1184
|
+
var CONFIG_FILENAME2 = ".launch-secure.cred.config";
|
|
1185
|
+
function inferCourseName(serverUrl) {
|
|
1186
|
+
try {
|
|
1187
|
+
const host = new URL(serverUrl).hostname.toLowerCase();
|
|
1188
|
+
if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".local")) return "local";
|
|
1189
|
+
if (host.includes("staging")) return "staging";
|
|
1190
|
+
if (host.endsWith(".vercel.app")) return "prod";
|
|
1191
|
+
return host.split(".")[0] || "default";
|
|
1192
|
+
} catch {
|
|
1193
|
+
return "default";
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
function toNested(cred) {
|
|
1197
|
+
if (cred.profiles && cred.active && cred.profiles[cred.active]) {
|
|
1198
|
+
return { active: cred.active, profiles: cred.profiles };
|
|
1199
|
+
}
|
|
1200
|
+
if (!cred.pat || !cred.orgSlug || !cred.projectSlug || !cred.serverUrl) {
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
const name = inferCourseName(cred.serverUrl);
|
|
1204
|
+
return {
|
|
1205
|
+
active: name,
|
|
1206
|
+
profiles: {
|
|
1207
|
+
[name]: {
|
|
1208
|
+
pat: cred.pat,
|
|
1209
|
+
orgSlug: cred.orgSlug,
|
|
1210
|
+
projectSlug: cred.projectSlug,
|
|
1211
|
+
serverUrl: cred.serverUrl
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
function readCredFile(repoRoot) {
|
|
1217
|
+
const p = path.join(repoRoot, CONFIG_FILENAME2);
|
|
1218
|
+
if (!fs.existsSync(p)) return null;
|
|
1219
|
+
try {
|
|
1220
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
1221
|
+
} catch (err) {
|
|
1222
|
+
throw new Error(`could not parse ${CONFIG_FILENAME2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/server/mcp-client.ts
|
|
1227
|
+
var _config = null;
|
|
1228
|
+
var _requestId = 0;
|
|
1229
|
+
var _sessionId = null;
|
|
1230
|
+
function mcpConfigFromCourse(projectRoot) {
|
|
1231
|
+
let cred;
|
|
1232
|
+
try {
|
|
1233
|
+
cred = readCredFile(projectRoot);
|
|
1234
|
+
} catch {
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
if (!cred) return null;
|
|
1238
|
+
const nested = toNested(cred);
|
|
1239
|
+
if (!nested) return null;
|
|
1240
|
+
const profile = nested.profiles[nested.active];
|
|
1241
|
+
if (!profile?.pat || !profile.serverUrl || !profile.orgSlug || !profile.projectSlug) {
|
|
1242
|
+
return null;
|
|
1243
|
+
}
|
|
1244
|
+
return {
|
|
1245
|
+
url: new URL("/api/mcp/project", profile.serverUrl).toString(),
|
|
1246
|
+
headers: {
|
|
1247
|
+
Authorization: `Bearer ${profile.pat}`,
|
|
1248
|
+
"X-Org-Slug": profile.orgSlug,
|
|
1249
|
+
"X-Project-Slug": profile.projectSlug
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
function loadMcpConfig(projectRoot) {
|
|
1254
|
+
if (_config) return _config;
|
|
1255
|
+
const fromCourse = mcpConfigFromCourse(projectRoot);
|
|
1256
|
+
if (fromCourse) {
|
|
1257
|
+
_config = fromCourse;
|
|
1258
|
+
return _config;
|
|
1259
|
+
}
|
|
1260
|
+
const mcpPath = (0, import_node_path3.join)(projectRoot, ".mcp.json");
|
|
1261
|
+
if (!(0, import_node_fs3.existsSync)(mcpPath)) {
|
|
1262
|
+
throw new Error(
|
|
1263
|
+
`No active course in ${CONFIG_FILENAME2} and no .mcp.json at ${mcpPath}. Run \`launch-course\` or bootstrap with \`launch-kit init\`.`
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
const raw = JSON.parse((0, import_node_fs3.readFileSync)(mcpPath, "utf-8"));
|
|
1267
|
+
const entry = raw.mcpServers["launch-secure"];
|
|
1268
|
+
if (!entry?.url) {
|
|
1269
|
+
throw new Error(
|
|
1270
|
+
`No active course in ${CONFIG_FILENAME2} and no "launch-secure" entry with url in .mcp.json`
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
_config = {
|
|
1274
|
+
url: entry.url,
|
|
1275
|
+
headers: entry.headers ?? {}
|
|
1276
|
+
};
|
|
1277
|
+
return _config;
|
|
1278
|
+
}
|
|
1279
|
+
function parseSSE(text) {
|
|
1280
|
+
const lines = text.split("\n");
|
|
1281
|
+
for (const line of lines) {
|
|
1282
|
+
if (line.startsWith("data: ")) {
|
|
1283
|
+
const data = line.slice(6);
|
|
1284
|
+
try {
|
|
1285
|
+
return JSON.parse(data);
|
|
1286
|
+
} catch {
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return JSON.parse(text);
|
|
1291
|
+
}
|
|
1292
|
+
async function mcpRequest(method, params) {
|
|
1293
|
+
const config = _config;
|
|
1294
|
+
if (!config) throw new Error("MCP config not loaded \u2014 call loadMcpConfig first");
|
|
1295
|
+
const id = ++_requestId;
|
|
1296
|
+
const headers = {
|
|
1297
|
+
...config.headers,
|
|
1298
|
+
"Content-Type": "application/json",
|
|
1299
|
+
"Accept": "application/json, text/event-stream"
|
|
1300
|
+
};
|
|
1301
|
+
if (_sessionId) {
|
|
1302
|
+
headers["Mcp-Session-Id"] = _sessionId;
|
|
1303
|
+
}
|
|
1304
|
+
const response = await fetch(config.url, {
|
|
1305
|
+
method: "POST",
|
|
1306
|
+
headers,
|
|
1307
|
+
body: JSON.stringify({ jsonrpc: "2.0", id, method, params })
|
|
1308
|
+
});
|
|
1309
|
+
const sid = response.headers.get("mcp-session-id");
|
|
1310
|
+
if (sid) _sessionId = sid;
|
|
1311
|
+
if (!response.ok) {
|
|
1312
|
+
const text = await response.text();
|
|
1313
|
+
throw new Error(`MCP ${method} failed (${response.status}): ${text}`);
|
|
1314
|
+
}
|
|
1315
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
1316
|
+
const body = await response.text();
|
|
1317
|
+
if (contentType.includes("text/event-stream")) {
|
|
1318
|
+
return parseSSE(body);
|
|
1319
|
+
}
|
|
1320
|
+
return JSON.parse(body);
|
|
1321
|
+
}
|
|
1322
|
+
async function ensureInitialized() {
|
|
1323
|
+
if (_sessionId) return;
|
|
1324
|
+
await mcpRequest("initialize", {
|
|
1325
|
+
protocolVersion: "2024-11-05",
|
|
1326
|
+
capabilities: {},
|
|
1327
|
+
clientInfo: { name: "launch-council", version: "0.0.1" }
|
|
1328
|
+
});
|
|
1329
|
+
const config = _config;
|
|
1330
|
+
const headers = {
|
|
1331
|
+
...config.headers,
|
|
1332
|
+
"Content-Type": "application/json",
|
|
1333
|
+
"Accept": "application/json, text/event-stream"
|
|
1334
|
+
};
|
|
1335
|
+
if (_sessionId) headers["Mcp-Session-Id"] = _sessionId;
|
|
1336
|
+
await fetch(config.url, {
|
|
1337
|
+
method: "POST",
|
|
1338
|
+
headers,
|
|
1339
|
+
body: JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" })
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
async function callTool(toolName, args) {
|
|
1343
|
+
await ensureInitialized();
|
|
1344
|
+
const result = await mcpRequest("tools/call", {
|
|
1345
|
+
name: toolName,
|
|
1346
|
+
arguments: args
|
|
1347
|
+
});
|
|
1348
|
+
if (result.error) {
|
|
1349
|
+
throw new Error(`MCP tool error: ${result.error.message}`);
|
|
1350
|
+
}
|
|
1351
|
+
const textContent = result.result?.content?.[0]?.text;
|
|
1352
|
+
if (!textContent) return null;
|
|
1353
|
+
return JSON.parse(textContent);
|
|
1354
|
+
}
|
|
1355
|
+
async function writeDeck(input) {
|
|
1356
|
+
const result = await callTool("communication_write", {
|
|
1357
|
+
title: input.title,
|
|
1358
|
+
body: input.body,
|
|
1359
|
+
resource_type: "deck",
|
|
1360
|
+
fields: {
|
|
1361
|
+
deckHtml: input.html,
|
|
1362
|
+
sessionId: input.sessionId,
|
|
1363
|
+
blockCount: input.blockCount,
|
|
1364
|
+
sharedFrom: "launch-deck"
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
return { id: result.created.id };
|
|
1368
|
+
}
|
|
1369
|
+
async function updateDeck(commentId, input) {
|
|
1370
|
+
await callTool("communication_update", {
|
|
1371
|
+
comment_id: commentId,
|
|
1372
|
+
title: input.title,
|
|
1373
|
+
body: input.body,
|
|
1374
|
+
fields: {
|
|
1375
|
+
deckHtml: input.html,
|
|
1376
|
+
sessionId: input.sessionId,
|
|
1377
|
+
blockCount: input.blockCount,
|
|
1378
|
+
sharedFrom: "launch-deck"
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
return { id: commentId };
|
|
1382
|
+
}
|
|
1383
|
+
async function deleteDeck(commentId) {
|
|
1384
|
+
await callTool("communication_delete", { comment_id: commentId });
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1177
1387
|
// src/server/deck-serve.ts
|
|
1178
1388
|
var DEFAULT_PORT = 52829;
|
|
1179
1389
|
var MAX_PORT_SCAN = 3;
|
|
@@ -1189,14 +1399,14 @@ var MIME_TYPES = {
|
|
|
1189
1399
|
".woff2": "font/woff2"
|
|
1190
1400
|
};
|
|
1191
1401
|
function sessionDir(cwd, session) {
|
|
1192
|
-
return
|
|
1402
|
+
return import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", session);
|
|
1193
1403
|
}
|
|
1194
1404
|
function sessionJsonPath(cwd, session) {
|
|
1195
|
-
return
|
|
1405
|
+
return import_node_path4.default.join(sessionDir(cwd, session), "session.json");
|
|
1196
1406
|
}
|
|
1197
1407
|
function readPersistedSession(cwd, session) {
|
|
1198
1408
|
try {
|
|
1199
|
-
const raw =
|
|
1409
|
+
const raw = import_node_fs4.default.readFileSync(sessionJsonPath(cwd, session), "utf-8");
|
|
1200
1410
|
return JSON.parse(raw);
|
|
1201
1411
|
} catch {
|
|
1202
1412
|
return null;
|
|
@@ -1204,7 +1414,7 @@ function readPersistedSession(cwd, session) {
|
|
|
1204
1414
|
}
|
|
1205
1415
|
function writePersistedSession(cwd, session, payload) {
|
|
1206
1416
|
const dir = sessionDir(cwd, session);
|
|
1207
|
-
|
|
1417
|
+
import_node_fs4.default.mkdirSync(dir, { recursive: true });
|
|
1208
1418
|
const existing = readPersistedSession(cwd, session);
|
|
1209
1419
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1210
1420
|
const next = {
|
|
@@ -1216,13 +1426,13 @@ function writePersistedSession(cwd, session, payload) {
|
|
|
1216
1426
|
createdAt: existing?.createdAt ?? now,
|
|
1217
1427
|
updatedAt: now
|
|
1218
1428
|
};
|
|
1219
|
-
|
|
1429
|
+
import_node_fs4.default.writeFileSync(sessionJsonPath(cwd, session), JSON.stringify(next, null, 2));
|
|
1220
1430
|
return next;
|
|
1221
1431
|
}
|
|
1222
1432
|
function listPersistedSessions(cwd) {
|
|
1223
|
-
const base =
|
|
1224
|
-
if (!
|
|
1225
|
-
const entries =
|
|
1433
|
+
const base = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
|
|
1434
|
+
if (!import_node_fs4.default.existsSync(base)) return [];
|
|
1435
|
+
const entries = import_node_fs4.default.readdirSync(base, { withFileTypes: true });
|
|
1226
1436
|
const sessions = [];
|
|
1227
1437
|
for (const entry of entries) {
|
|
1228
1438
|
if (!entry.isDirectory()) continue;
|
|
@@ -1234,20 +1444,106 @@ function listPersistedSessions(cwd) {
|
|
|
1234
1444
|
blockCount: Array.isArray(persisted.blocks) ? persisted.blocks.length : 0,
|
|
1235
1445
|
version: persisted.version,
|
|
1236
1446
|
createdAt: persisted.createdAt,
|
|
1237
|
-
updatedAt: persisted.updatedAt
|
|
1447
|
+
updatedAt: persisted.updatedAt,
|
|
1448
|
+
shared: Boolean(readSyncRecord(cwd, persisted.id))
|
|
1238
1449
|
});
|
|
1239
1450
|
}
|
|
1240
1451
|
sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
1241
1452
|
return sessions;
|
|
1242
1453
|
}
|
|
1243
1454
|
function deletePersistedSession(cwd, session) {
|
|
1244
|
-
const base =
|
|
1455
|
+
const base = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files");
|
|
1245
1456
|
if (session === "all") {
|
|
1246
|
-
if (
|
|
1457
|
+
if (import_node_fs4.default.existsSync(base)) import_node_fs4.default.rmSync(base, { recursive: true, force: true });
|
|
1247
1458
|
return;
|
|
1248
1459
|
}
|
|
1249
1460
|
const dir = sessionDir(cwd, session);
|
|
1250
|
-
if (
|
|
1461
|
+
if (import_node_fs4.default.existsSync(dir)) import_node_fs4.default.rmSync(dir, { recursive: true, force: true });
|
|
1462
|
+
}
|
|
1463
|
+
function syncJsonPath(cwd, session) {
|
|
1464
|
+
return import_node_path4.default.join(sessionDir(cwd, session), ".sync.json");
|
|
1465
|
+
}
|
|
1466
|
+
function readSyncRecord(cwd, session) {
|
|
1467
|
+
try {
|
|
1468
|
+
return JSON.parse(import_node_fs4.default.readFileSync(syncJsonPath(cwd, session), "utf-8"));
|
|
1469
|
+
} catch {
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function writeSyncRecord(cwd, session, rec) {
|
|
1474
|
+
import_node_fs4.default.writeFileSync(syncJsonPath(cwd, session), JSON.stringify(rec, null, 2));
|
|
1475
|
+
}
|
|
1476
|
+
function escHtml2(s) {
|
|
1477
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1478
|
+
}
|
|
1479
|
+
function escAttr(s) {
|
|
1480
|
+
return s.replace(/&/g, "&").replace(/"/g, """);
|
|
1481
|
+
}
|
|
1482
|
+
function readIframeArtifact(cwd, src) {
|
|
1483
|
+
if (typeof src !== "string" || !src.startsWith("/deck-files/")) return null;
|
|
1484
|
+
const rel = decodeURIComponent(src.slice("/deck-files/".length));
|
|
1485
|
+
if (rel.includes("..")) return null;
|
|
1486
|
+
try {
|
|
1487
|
+
return import_node_fs4.default.readFileSync(import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", rel), "utf-8");
|
|
1488
|
+
} catch {
|
|
1489
|
+
return null;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
function renderShareSection(cwd, block) {
|
|
1493
|
+
const label = block?.label ? `<h2 class="db-title">${escHtml2(String(block.label))}</h2>` : "";
|
|
1494
|
+
switch (block?.type) {
|
|
1495
|
+
case "iframe": {
|
|
1496
|
+
const doc = readIframeArtifact(cwd, block.src) ?? "<p>(missing artifact)</p>";
|
|
1497
|
+
return `<section class="db">${label}<iframe class="db-frame" sandbox="allow-scripts allow-popups" srcdoc="${escAttr(doc)}"></iframe></section>`;
|
|
1498
|
+
}
|
|
1499
|
+
case "rich-html":
|
|
1500
|
+
case "html":
|
|
1501
|
+
return `<section class="db">${label}<div class="db-html">${typeof block.content === "string" ? block.content : ""}</div></section>`;
|
|
1502
|
+
case "markdown":
|
|
1503
|
+
return `<section class="db">${label}<div class="db-md" data-md="${escAttr(typeof block.content === "string" ? block.content : "")}"></div></section>`;
|
|
1504
|
+
case "mermaid":
|
|
1505
|
+
return `<section class="db">${label}<pre class="mermaid">${escHtml2(typeof block.content === "string" ? block.content : "")}</pre></section>`;
|
|
1506
|
+
case "options": {
|
|
1507
|
+
const opts = Array.isArray(block.options) ? block.options : [];
|
|
1508
|
+
const lis = opts.map(
|
|
1509
|
+
(o) => `<li><strong>${escHtml2(String(o?.label ?? o?.id ?? ""))}</strong>${o?.description ? ` \u2014 ${escHtml2(String(o.description))}` : ""}</li>`
|
|
1510
|
+
).join("");
|
|
1511
|
+
return `<section class="db">${label}<ul>${lis}</ul></section>`;
|
|
1512
|
+
}
|
|
1513
|
+
default:
|
|
1514
|
+
return `<section class="db">${label}</section>`;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function buildShareHtml(cwd, session, blocks) {
|
|
1518
|
+
if (blocks.length === 1) {
|
|
1519
|
+
const only = blocks[0];
|
|
1520
|
+
if (only?.type === "iframe") {
|
|
1521
|
+
const doc = readIframeArtifact(cwd, only.src);
|
|
1522
|
+
if (doc) return doc;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
const list = blocks;
|
|
1526
|
+
const sections = list.map((b) => renderShareSection(cwd, b)).join("\n");
|
|
1527
|
+
const hasMd = list.some((b) => b?.type === "markdown");
|
|
1528
|
+
const hasMermaid = list.some((b) => b?.type === "mermaid");
|
|
1529
|
+
return `<!DOCTYPE html>
|
|
1530
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1531
|
+
<title>${escHtml2(session)}</title>
|
|
1532
|
+
<style>
|
|
1533
|
+
body { margin:0; font-family: ui-sans-serif, system-ui, sans-serif; background:#0b0e14; color:#e6e9ef; }
|
|
1534
|
+
.db { padding:20px 24px; border-bottom:1px solid #1e2433; }
|
|
1535
|
+
.db-title { font-size:13px; font-weight:600; margin:0 0 12px; color:#9aa4b2; text-transform:uppercase; letter-spacing:.05em; }
|
|
1536
|
+
.db-frame { width:100%; height:70vh; border:0; border-radius:8px; background:#fff; }
|
|
1537
|
+
.db-html { background:#fff; color:#111; border-radius:8px; padding:16px; overflow:auto; }
|
|
1538
|
+
ul { line-height:1.7; }
|
|
1539
|
+
.mermaid { background:#fff; border-radius:8px; padding:16px; }
|
|
1540
|
+
</style></head>
|
|
1541
|
+
<body>
|
|
1542
|
+
${sections}
|
|
1543
|
+
${hasMd ? `<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
1544
|
+
<script>document.querySelectorAll('.db-md').forEach(function(el){try{el.innerHTML=marked.parse(el.dataset.md||'')}catch(e){el.textContent=el.dataset.md||''}});</script>` : ""}
|
|
1545
|
+
${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>` : ""}
|
|
1546
|
+
</body></html>`;
|
|
1251
1547
|
}
|
|
1252
1548
|
var pendingFeedback = /* @__PURE__ */ new Map();
|
|
1253
1549
|
var lastRenderError = null;
|
|
@@ -1288,23 +1584,23 @@ function broadcastToClients(message) {
|
|
|
1288
1584
|
}
|
|
1289
1585
|
}
|
|
1290
1586
|
function serveStatic(res, filePath) {
|
|
1291
|
-
if (!
|
|
1292
|
-
const ext =
|
|
1587
|
+
if (!import_node_fs4.default.existsSync(filePath) || !import_node_fs4.default.statSync(filePath).isFile()) return false;
|
|
1588
|
+
const ext = import_node_path4.default.extname(filePath).toLowerCase();
|
|
1293
1589
|
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1294
1590
|
res.writeHead(200, { "Content-Type": mime, "Cache-Control": "no-cache" });
|
|
1295
|
-
|
|
1591
|
+
import_node_fs4.default.createReadStream(filePath).pipe(res);
|
|
1296
1592
|
return true;
|
|
1297
1593
|
}
|
|
1298
1594
|
function resolveClientDir() {
|
|
1299
|
-
const sibling =
|
|
1300
|
-
if (
|
|
1301
|
-
const devDist =
|
|
1302
|
-
if (
|
|
1595
|
+
const sibling = import_node_path4.default.join(__dirname, "..", "deck-client");
|
|
1596
|
+
if (import_node_fs4.default.existsSync(import_node_path4.default.join(sibling, "assets"))) return sibling;
|
|
1597
|
+
const devDist = import_node_path4.default.join(__dirname, "..", "..", "dist", "deck-client");
|
|
1598
|
+
if (import_node_fs4.default.existsSync(import_node_path4.default.join(devDist, "assets"))) return devDist;
|
|
1303
1599
|
return sibling;
|
|
1304
1600
|
}
|
|
1305
1601
|
function serveIndex(res, clientDir) {
|
|
1306
|
-
const indexPath =
|
|
1307
|
-
if (!
|
|
1602
|
+
const indexPath = import_node_path4.default.join(clientDir, "index.html");
|
|
1603
|
+
if (!import_node_fs4.default.existsSync(indexPath)) {
|
|
1308
1604
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
1309
1605
|
res.end(`LaunchDeck client bundle not found at ${clientDir}. Run 'npm run build:client'.`);
|
|
1310
1606
|
return;
|
|
@@ -1408,15 +1704,15 @@ async function startDeckServer(opts = {}) {
|
|
|
1408
1704
|
const baseUrl = `/deck-files/${sessionEncoded}`;
|
|
1409
1705
|
const html = generateBlastRadiusHtml(block.manifest, baseUrl);
|
|
1410
1706
|
const report = generateBlastRadiusReport(block.manifest);
|
|
1411
|
-
const dir =
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1707
|
+
const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
|
|
1708
|
+
import_node_fs4.default.mkdirSync(dir, { recursive: true });
|
|
1709
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius.html"), html);
|
|
1710
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius-report.md"), report);
|
|
1711
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "blast-radius-manifest.json"), JSON.stringify(block.manifest, null, 2));
|
|
1416
1712
|
const contractData = generateContract(block.manifest);
|
|
1417
1713
|
const contractMd = contractToMarkdown(contractData);
|
|
1418
|
-
|
|
1419
|
-
|
|
1714
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "contract.json"), JSON.stringify(contractData, null, 2));
|
|
1715
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, "contract.md"), contractMd);
|
|
1420
1716
|
block.type = "iframe";
|
|
1421
1717
|
block.src = `${baseUrl}/blast-radius.html`;
|
|
1422
1718
|
delete block.manifest;
|
|
@@ -1443,9 +1739,9 @@ async function startDeckServer(opts = {}) {
|
|
|
1443
1739
|
const sessionEncoded = encodeURIComponent(body.session);
|
|
1444
1740
|
const baseUrl = `/deck-files/${sessionEncoded}`;
|
|
1445
1741
|
const filename = `${slugify(block.label)}.html`;
|
|
1446
|
-
const dir =
|
|
1447
|
-
|
|
1448
|
-
|
|
1742
|
+
const dir = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", body.session);
|
|
1743
|
+
import_node_fs4.default.mkdirSync(dir, { recursive: true });
|
|
1744
|
+
import_node_fs4.default.writeFileSync(import_node_path4.default.join(dir, filename), html);
|
|
1449
1745
|
block.type = "iframe";
|
|
1450
1746
|
block.src = `${baseUrl}/${filename}`;
|
|
1451
1747
|
delete block.content;
|
|
@@ -1477,6 +1773,79 @@ async function startDeckServer(opts = {}) {
|
|
|
1477
1773
|
jsonResponse(res, 200, { sessions: listPersistedSessions(cwd) });
|
|
1478
1774
|
return;
|
|
1479
1775
|
}
|
|
1776
|
+
const shareMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)\/share$/);
|
|
1777
|
+
if (shareMatch && req.method === "POST") {
|
|
1778
|
+
const id = decodeURIComponent(shareMatch[1]);
|
|
1779
|
+
const persisted = readPersistedSession(cwd, id);
|
|
1780
|
+
if (!persisted) {
|
|
1781
|
+
jsonResponse(res, 404, { ok: false, error: "session not found" });
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
try {
|
|
1785
|
+
loadMcpConfig(cwd);
|
|
1786
|
+
} catch (err) {
|
|
1787
|
+
jsonResponse(res, 400, {
|
|
1788
|
+
ok: false,
|
|
1789
|
+
error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}`
|
|
1790
|
+
});
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
try {
|
|
1794
|
+
const blocks = Array.isArray(persisted.blocks) ? persisted.blocks : [];
|
|
1795
|
+
const html = buildShareHtml(cwd, id, blocks);
|
|
1796
|
+
const types = blocks.map((b) => b?.type).filter(Boolean);
|
|
1797
|
+
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.`;
|
|
1798
|
+
const input = { title: id, body, html, sessionId: id, blockCount: blocks.length };
|
|
1799
|
+
const existing2 = readSyncRecord(cwd, id);
|
|
1800
|
+
let resourceId;
|
|
1801
|
+
let updated = false;
|
|
1802
|
+
if (existing2?.resourceId) {
|
|
1803
|
+
try {
|
|
1804
|
+
({ id: resourceId } = await updateDeck(existing2.resourceId, input));
|
|
1805
|
+
updated = true;
|
|
1806
|
+
} catch {
|
|
1807
|
+
({ id: resourceId } = await writeDeck(input));
|
|
1808
|
+
}
|
|
1809
|
+
} else {
|
|
1810
|
+
({ id: resourceId } = await writeDeck(input));
|
|
1811
|
+
}
|
|
1812
|
+
writeSyncRecord(cwd, id, {
|
|
1813
|
+
resourceId,
|
|
1814
|
+
sharedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1815
|
+
version: persisted.version
|
|
1816
|
+
});
|
|
1817
|
+
jsonResponse(res, 200, { ok: true, resourceId, updated });
|
|
1818
|
+
} catch (err) {
|
|
1819
|
+
jsonResponse(res, 500, { ok: false, error: String(err) });
|
|
1820
|
+
}
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
const remoteDelMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)\/remote$/);
|
|
1824
|
+
if (remoteDelMatch && req.method === "DELETE") {
|
|
1825
|
+
const id = decodeURIComponent(remoteDelMatch[1]);
|
|
1826
|
+
const sync = readSyncRecord(cwd, id);
|
|
1827
|
+
if (!sync?.resourceId) {
|
|
1828
|
+
jsonResponse(res, 200, { ok: true, deleted: false, reason: "not shared" });
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
try {
|
|
1832
|
+
loadMcpConfig(cwd);
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
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)}` });
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
try {
|
|
1838
|
+
await deleteDeck(sync.resourceId);
|
|
1839
|
+
try {
|
|
1840
|
+
import_node_fs4.default.rmSync(syncJsonPath(cwd, id), { force: true });
|
|
1841
|
+
} catch {
|
|
1842
|
+
}
|
|
1843
|
+
jsonResponse(res, 200, { ok: true, deleted: true });
|
|
1844
|
+
} catch (err) {
|
|
1845
|
+
jsonResponse(res, 500, { ok: false, error: String(err) });
|
|
1846
|
+
}
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1480
1849
|
const sessionMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)$/);
|
|
1481
1850
|
if (sessionMatch) {
|
|
1482
1851
|
const id = decodeURIComponent(sessionMatch[1]);
|
|
@@ -1579,7 +1948,7 @@ async function startDeckServer(opts = {}) {
|
|
|
1579
1948
|
res.end("Forbidden");
|
|
1580
1949
|
return;
|
|
1581
1950
|
}
|
|
1582
|
-
const filePath =
|
|
1951
|
+
const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
|
|
1583
1952
|
if (serveStatic(res, filePath)) return;
|
|
1584
1953
|
res.writeHead(404);
|
|
1585
1954
|
res.end("Not found");
|
|
@@ -1592,25 +1961,25 @@ async function startDeckServer(opts = {}) {
|
|
|
1592
1961
|
res.end("Forbidden");
|
|
1593
1962
|
return;
|
|
1594
1963
|
}
|
|
1595
|
-
const filePath =
|
|
1596
|
-
if (!
|
|
1964
|
+
const filePath = import_node_path4.default.join(cwd, LAUNCHSECURE_DIR, "deck-files", relative);
|
|
1965
|
+
if (!import_node_fs4.default.existsSync(filePath) || !import_node_fs4.default.statSync(filePath).isFile()) {
|
|
1597
1966
|
res.writeHead(404);
|
|
1598
1967
|
res.end("Not found");
|
|
1599
1968
|
return;
|
|
1600
1969
|
}
|
|
1601
|
-
const ext =
|
|
1970
|
+
const ext = import_node_path4.default.extname(filePath).toLowerCase();
|
|
1602
1971
|
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
1603
|
-
const filename =
|
|
1972
|
+
const filename = import_node_path4.default.basename(filePath);
|
|
1604
1973
|
res.writeHead(200, {
|
|
1605
1974
|
"Content-Type": mime,
|
|
1606
1975
|
"Content-Disposition": `attachment; filename="${filename}"`,
|
|
1607
1976
|
"Cache-Control": "no-cache"
|
|
1608
1977
|
});
|
|
1609
|
-
|
|
1978
|
+
import_node_fs4.default.createReadStream(filePath).pipe(res);
|
|
1610
1979
|
return;
|
|
1611
1980
|
}
|
|
1612
1981
|
if (url2.pathname !== "/") {
|
|
1613
|
-
const staticPath =
|
|
1982
|
+
const staticPath = import_node_path4.default.join(clientDir, url2.pathname);
|
|
1614
1983
|
if (serveStatic(res, staticPath)) return;
|
|
1615
1984
|
}
|
|
1616
1985
|
serveIndex(res, clientDir);
|
package/dist/server/fb-wizard.js
CHANGED
|
File without changes
|