@launchsecure/launch-kit 0.0.15 → 0.0.17
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-CWRZxjqR.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-DtbN793z.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/deck-client/assets/_baseUniq-BbqvoK-V.js +1 -0
- package/dist/deck-client/assets/arc-CMtYsIZt.js +1 -0
- package/dist/deck-client/assets/architectureDiagram-Q4EWVU46-BEN5hESa.js +36 -0
- package/dist/deck-client/assets/blockDiagram-DXYQGD6D-BV4CZ6k8.js +132 -0
- package/dist/deck-client/assets/c4Diagram-AHTNJAMY-fLcBXqdD.js +10 -0
- package/dist/deck-client/assets/channel-Nf-B3Qor.js +1 -0
- package/dist/deck-client/assets/chunk-4BX2VUAB-BO_19zwB.js +1 -0
- package/dist/deck-client/assets/chunk-4TB4RGXK-iYegd5fu.js +206 -0
- package/dist/deck-client/assets/chunk-55IACEB6-DM3QwYFL.js +1 -0
- package/dist/deck-client/assets/chunk-EDXVE4YY-DGznOul1.js +1 -0
- package/dist/deck-client/assets/chunk-FMBD7UC4-DsANJqYW.js +15 -0
- package/dist/deck-client/assets/chunk-OYMX7WX6-6PGH1F7d.js +231 -0
- package/dist/deck-client/assets/chunk-QZHKN3VN-Dihf0Uq7.js +1 -0
- package/dist/deck-client/assets/chunk-YZCP3GAM-Cali2wW5.js +1 -0
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-3i3-miMR.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-3i3-miMR.js +1 -0
- package/dist/deck-client/assets/clone-DXBuQlG8.js +1 -0
- package/dist/deck-client/assets/cose-bilkent-S5V4N54A-DsRY4vbI.js +1 -0
- package/dist/deck-client/assets/cytoscape.esm-BiciSPf8.js +331 -0
- package/dist/deck-client/assets/dagre-KV5264BT-DJIKE_pI.js +4 -0
- package/dist/deck-client/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/deck-client/assets/diagram-5BDNPKRD-Ckgli1SP.js +10 -0
- package/dist/deck-client/assets/diagram-G4DWMVQ6-CozcDzae.js +24 -0
- package/dist/deck-client/assets/diagram-MMDJMWI5-xVSwW3f_.js +43 -0
- package/dist/deck-client/assets/diagram-TYMM5635-CJeZVY-P.js +24 -0
- package/dist/deck-client/assets/erDiagram-SMLLAGMA-j4wjAERH.js +85 -0
- package/dist/deck-client/assets/flowDiagram-DWJPFMVM-CVLZ1efi.js +162 -0
- package/dist/deck-client/assets/ganttDiagram-T4ZO3ILL-CcIJ7pkP.js +292 -0
- package/dist/deck-client/assets/gitGraphDiagram-UUTBAWPF-BZRhQX-a.js +106 -0
- package/dist/deck-client/assets/graph-D0l25xfo.js +1 -0
- package/dist/deck-client/assets/index-BXcoHWVM.js +476 -0
- package/dist/deck-client/assets/index-Cdh-f3-c.css +1 -0
- package/dist/deck-client/assets/infoDiagram-42DDH7IO-BLwgxnYT.js +2 -0
- package/dist/deck-client/assets/init-Gi6I4Gst.js +1 -0
- package/dist/deck-client/assets/ishikawaDiagram-UXIWVN3A-BfOLoWv3.js +70 -0
- package/dist/deck-client/assets/journeyDiagram-VCZTEJTY-CPuL6C9h.js +139 -0
- package/dist/deck-client/assets/kanban-definition-6JOO6SKY-D3uf7_tx.js +89 -0
- package/dist/deck-client/assets/katex-DkKDou_j.js +257 -0
- package/dist/deck-client/assets/layout-CzToiXdK.js +1 -0
- package/dist/deck-client/assets/linear-BU36t460.js +1 -0
- package/dist/deck-client/assets/min-DX_q-lqP.js +1 -0
- package/dist/deck-client/assets/mindmap-definition-QFDTVHPH-Ccty4O16.js +96 -0
- package/dist/deck-client/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/deck-client/assets/pieDiagram-DEJITSTG-DVjsvH19.js +30 -0
- package/dist/deck-client/assets/quadrantDiagram-34T5L4WZ-DtOXFVW9.js +7 -0
- package/dist/deck-client/assets/requirementDiagram-MS252O5E-BbO_kKg6.js +84 -0
- package/dist/deck-client/assets/sankeyDiagram-XADWPNL6-qbbj-CmC.js +10 -0
- package/dist/deck-client/assets/sequenceDiagram-FGHM5R23-JNKZAgfQ.js +157 -0
- package/dist/deck-client/assets/stateDiagram-FHFEXIEX-dtFalcNx.js +1 -0
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CviYYulW.js +1 -0
- package/dist/deck-client/assets/timeline-definition-GMOUNBTQ-Dpp5nqSJ.js +120 -0
- package/dist/deck-client/assets/vennDiagram-DHZGUBPP-D8qEutX7.js +34 -0
- package/dist/deck-client/assets/wardley-RL74JXVD-BwMqiNcL.js +162 -0
- package/dist/deck-client/assets/wardleyDiagram-NUSXRM2D-Bxl9X3CK.js +20 -0
- package/dist/deck-client/assets/xychartDiagram-5P7HB3ND-DYcvxLhi.js +7 -0
- package/dist/deck-client/index.html +21 -0
- package/dist/server/cli.js +616 -142
- package/dist/server/deck-mcp-entry.js +1789 -0
- package/dist/server/deck-serve.js +1275 -0
- package/dist/server/deck-server/deck-mcp-entry.js +1789 -0
- package/dist/server/deck-server/deck-serve.js +1275 -0
- package/dist/server/graph-mcp-entry.js +464 -38
- package/dist/server/server/chart-serve.js +4643 -0
- package/dist/server/server/cli.js +13360 -0
- package/dist/server/server/fb-wizard.js +136 -0
- package/dist/server/server/graph-mcp-entry.js +6776 -0
- package/package.json +13 -6
- package/dist/chart-client/assets/index-CbZ13AXL.css +0 -1
- package/dist/client/assets/index-BCYw64M7.css +0 -32
- /package/dist/chart-client/assets/{index-BpQPtTuo.js → index-BUhuLBaw.js} +0 -0
- /package/dist/client/assets/{index-3ENenBk-.js → index-CAAipH3V.js} +0 -0
package/dist/server/cli.js
CHANGED
|
@@ -554,7 +554,7 @@ var require_claude_bridge = __commonJS({
|
|
|
554
554
|
"use strict";
|
|
555
555
|
var { spawn: spawn3 } = require("node-pty");
|
|
556
556
|
var path9 = require("path");
|
|
557
|
-
var
|
|
557
|
+
var fs10 = require("fs");
|
|
558
558
|
var ClaudeBridge = class {
|
|
559
559
|
constructor() {
|
|
560
560
|
this.sessions = /* @__PURE__ */ new Map();
|
|
@@ -572,7 +572,7 @@ var require_claude_bridge = __commonJS({
|
|
|
572
572
|
];
|
|
573
573
|
for (const cmd of possibleCommands) {
|
|
574
574
|
try {
|
|
575
|
-
if (
|
|
575
|
+
if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
|
|
576
576
|
console.log(`Found Claude command at: ${cmd}`);
|
|
577
577
|
return cmd;
|
|
578
578
|
}
|
|
@@ -773,7 +773,7 @@ var require_codex_bridge = __commonJS({
|
|
|
773
773
|
"use strict";
|
|
774
774
|
var { spawn: spawn3 } = require("node-pty");
|
|
775
775
|
var path9 = require("path");
|
|
776
|
-
var
|
|
776
|
+
var fs10 = require("fs");
|
|
777
777
|
var CodexBridge = class {
|
|
778
778
|
constructor() {
|
|
779
779
|
this.sessions = /* @__PURE__ */ new Map();
|
|
@@ -790,7 +790,7 @@ var require_codex_bridge = __commonJS({
|
|
|
790
790
|
];
|
|
791
791
|
for (const cmd of possibleCommands) {
|
|
792
792
|
try {
|
|
793
|
-
if (
|
|
793
|
+
if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
|
|
794
794
|
console.log(`Found Codex command at: ${cmd}`);
|
|
795
795
|
return cmd;
|
|
796
796
|
}
|
|
@@ -966,7 +966,7 @@ var require_agent_bridge = __commonJS({
|
|
|
966
966
|
"use strict";
|
|
967
967
|
var { spawn: spawn3 } = require("node-pty");
|
|
968
968
|
var path9 = require("path");
|
|
969
|
-
var
|
|
969
|
+
var fs10 = require("fs");
|
|
970
970
|
var AgentBridge = class {
|
|
971
971
|
constructor() {
|
|
972
972
|
this.sessions = /* @__PURE__ */ new Map();
|
|
@@ -982,7 +982,7 @@ var require_agent_bridge = __commonJS({
|
|
|
982
982
|
];
|
|
983
983
|
for (const cmd of possibleCommands) {
|
|
984
984
|
try {
|
|
985
|
-
if (
|
|
985
|
+
if (fs10.existsSync(cmd) || this.commandExists(cmd)) {
|
|
986
986
|
console.log(`Found Agent command at: ${cmd}`);
|
|
987
987
|
return cmd;
|
|
988
988
|
}
|
|
@@ -1306,7 +1306,7 @@ var require_script_bridge = __commonJS({
|
|
|
1306
1306
|
var require_session_store = __commonJS({
|
|
1307
1307
|
"../claude-code-web/src/utils/session-store.js"(exports2, module2) {
|
|
1308
1308
|
"use strict";
|
|
1309
|
-
var
|
|
1309
|
+
var fs10 = require("fs").promises;
|
|
1310
1310
|
var path9 = require("path");
|
|
1311
1311
|
var os2 = require("os");
|
|
1312
1312
|
var SessionStore = class {
|
|
@@ -1317,14 +1317,14 @@ var require_session_store = __commonJS({
|
|
|
1317
1317
|
}
|
|
1318
1318
|
async initializeStorage() {
|
|
1319
1319
|
try {
|
|
1320
|
-
await
|
|
1320
|
+
await fs10.mkdir(this.storageDir, { recursive: true });
|
|
1321
1321
|
} catch (error) {
|
|
1322
1322
|
console.error("Failed to create storage directory:", error);
|
|
1323
1323
|
}
|
|
1324
1324
|
}
|
|
1325
1325
|
async saveSessions(sessions) {
|
|
1326
1326
|
try {
|
|
1327
|
-
await
|
|
1327
|
+
await fs10.mkdir(this.storageDir, { recursive: true });
|
|
1328
1328
|
const sessionsArray = Array.from(sessions.entries()).map(([id, session]) => ({
|
|
1329
1329
|
id,
|
|
1330
1330
|
name: session.name || "Unnamed Session",
|
|
@@ -1355,9 +1355,9 @@ var require_session_store = __commonJS({
|
|
|
1355
1355
|
sessions: sessionsArray
|
|
1356
1356
|
};
|
|
1357
1357
|
const tempFile = `${this.sessionsFile}.tmp`;
|
|
1358
|
-
await
|
|
1359
|
-
await
|
|
1360
|
-
await
|
|
1358
|
+
await fs10.writeFile(tempFile, JSON.stringify(data, null, 2));
|
|
1359
|
+
await fs10.mkdir(this.storageDir, { recursive: true });
|
|
1360
|
+
await fs10.rename(tempFile, this.sessionsFile);
|
|
1361
1361
|
return true;
|
|
1362
1362
|
} catch (error) {
|
|
1363
1363
|
console.error("Failed to save sessions:", error.message);
|
|
@@ -1366,8 +1366,8 @@ var require_session_store = __commonJS({
|
|
|
1366
1366
|
}
|
|
1367
1367
|
async loadSessions() {
|
|
1368
1368
|
try {
|
|
1369
|
-
await
|
|
1370
|
-
const data = await
|
|
1369
|
+
await fs10.access(this.sessionsFile);
|
|
1370
|
+
const data = await fs10.readFile(this.sessionsFile, "utf8");
|
|
1371
1371
|
if (!data || !data.trim()) {
|
|
1372
1372
|
console.log("Sessions file is empty, starting fresh");
|
|
1373
1373
|
return /* @__PURE__ */ new Map();
|
|
@@ -1378,7 +1378,7 @@ var require_session_store = __commonJS({
|
|
|
1378
1378
|
} catch (parseError) {
|
|
1379
1379
|
console.error("Sessions file is corrupted, starting fresh:", parseError.message);
|
|
1380
1380
|
try {
|
|
1381
|
-
await
|
|
1381
|
+
await fs10.rename(this.sessionsFile, `${this.sessionsFile}.corrupted.${Date.now()}`);
|
|
1382
1382
|
} catch (renameError) {
|
|
1383
1383
|
}
|
|
1384
1384
|
return /* @__PURE__ */ new Map();
|
|
@@ -1422,7 +1422,7 @@ var require_session_store = __commonJS({
|
|
|
1422
1422
|
}
|
|
1423
1423
|
async clearOldSessions() {
|
|
1424
1424
|
try {
|
|
1425
|
-
await
|
|
1425
|
+
await fs10.unlink(this.sessionsFile);
|
|
1426
1426
|
console.log("Cleared old sessions");
|
|
1427
1427
|
return true;
|
|
1428
1428
|
} catch (error) {
|
|
@@ -1434,9 +1434,9 @@ var require_session_store = __commonJS({
|
|
|
1434
1434
|
}
|
|
1435
1435
|
async getSessionMetadata() {
|
|
1436
1436
|
try {
|
|
1437
|
-
await
|
|
1438
|
-
const stats = await
|
|
1439
|
-
const data = await
|
|
1437
|
+
await fs10.access(this.sessionsFile);
|
|
1438
|
+
const stats = await fs10.stat(this.sessionsFile);
|
|
1439
|
+
const data = await fs10.readFile(this.sessionsFile, "utf8");
|
|
1440
1440
|
const parsed = JSON.parse(data);
|
|
1441
1441
|
return {
|
|
1442
1442
|
exists: true,
|
|
@@ -1461,7 +1461,7 @@ var require_session_store = __commonJS({
|
|
|
1461
1461
|
var require_usage_reader = __commonJS({
|
|
1462
1462
|
"../claude-code-web/src/usage-reader.js"(exports2, module2) {
|
|
1463
1463
|
"use strict";
|
|
1464
|
-
var
|
|
1464
|
+
var fs10 = require("fs").promises;
|
|
1465
1465
|
var path9 = require("path");
|
|
1466
1466
|
var readline = require("readline");
|
|
1467
1467
|
var { createReadStream } = require("fs");
|
|
@@ -1669,12 +1669,12 @@ var require_usage_reader = __commonJS({
|
|
|
1669
1669
|
const projectDirName = cwd.replace(/\//g, "-");
|
|
1670
1670
|
let projectPath = path9.join(this.claudeProjectsPath, projectDirName);
|
|
1671
1671
|
try {
|
|
1672
|
-
await
|
|
1672
|
+
await fs10.access(projectPath);
|
|
1673
1673
|
} catch (err2) {
|
|
1674
1674
|
console.log(`Project directory not found: ${projectPath}`);
|
|
1675
1675
|
return null;
|
|
1676
1676
|
}
|
|
1677
|
-
const files = await
|
|
1677
|
+
const files = await fs10.readdir(projectPath);
|
|
1678
1678
|
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
1679
1679
|
if (jsonlFiles.length === 0) {
|
|
1680
1680
|
return null;
|
|
@@ -1683,7 +1683,7 @@ var require_usage_reader = __commonJS({
|
|
|
1683
1683
|
let mostRecentTime = 0;
|
|
1684
1684
|
for (const file of jsonlFiles) {
|
|
1685
1685
|
const filePath = path9.join(projectPath, file);
|
|
1686
|
-
const stat = await
|
|
1686
|
+
const stat = await fs10.stat(filePath);
|
|
1687
1687
|
if (stat.mtime.getTime() > mostRecentTime) {
|
|
1688
1688
|
mostRecentTime = stat.mtime.getTime();
|
|
1689
1689
|
mostRecentFile = filePath;
|
|
@@ -1698,17 +1698,17 @@ var require_usage_reader = __commonJS({
|
|
|
1698
1698
|
async findJsonlFiles(onlyRecent = false) {
|
|
1699
1699
|
const files = [];
|
|
1700
1700
|
try {
|
|
1701
|
-
const projectDirs = await
|
|
1701
|
+
const projectDirs = await fs10.readdir(this.claudeProjectsPath);
|
|
1702
1702
|
for (const projectDir of projectDirs) {
|
|
1703
1703
|
const projectPath = path9.join(this.claudeProjectsPath, projectDir);
|
|
1704
|
-
const stat = await
|
|
1704
|
+
const stat = await fs10.stat(projectPath);
|
|
1705
1705
|
if (stat.isDirectory()) {
|
|
1706
|
-
const projectFiles = await
|
|
1706
|
+
const projectFiles = await fs10.readdir(projectPath);
|
|
1707
1707
|
const jsonlFiles = projectFiles.filter((f) => f.endsWith(".jsonl"));
|
|
1708
1708
|
for (const jsonlFile of jsonlFiles) {
|
|
1709
1709
|
const filePath = path9.join(projectPath, jsonlFile);
|
|
1710
1710
|
if (onlyRecent) {
|
|
1711
|
-
const fileStat = await
|
|
1711
|
+
const fileStat = await fs10.stat(filePath);
|
|
1712
1712
|
const hoursSinceModified = (Date.now() - fileStat.mtime.getTime()) / (1e3 * 60 * 60);
|
|
1713
1713
|
if (hoursSinceModified <= 24) {
|
|
1714
1714
|
files.push(filePath);
|
|
@@ -1871,7 +1871,7 @@ var require_usage_reader = __commonJS({
|
|
|
1871
1871
|
}
|
|
1872
1872
|
const sessionFile = path9.join(this.claudeProjectsPath, path9.basename(process.cwd()).replace(/[^a-zA-Z0-9-]/g, "-"), `${sessionId}.jsonl`);
|
|
1873
1873
|
try {
|
|
1874
|
-
await
|
|
1874
|
+
await fs10.access(sessionFile);
|
|
1875
1875
|
} catch (err2) {
|
|
1876
1876
|
return null;
|
|
1877
1877
|
}
|
|
@@ -2537,7 +2537,7 @@ var require_src = __commonJS({
|
|
|
2537
2537
|
var SessionStore = require_session_store();
|
|
2538
2538
|
var UsageReader = require_usage_reader();
|
|
2539
2539
|
var UsageAnalytics = require_usage_analytics();
|
|
2540
|
-
var
|
|
2540
|
+
var fs10 = require("fs");
|
|
2541
2541
|
function stripAnsi(str) {
|
|
2542
2542
|
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "").replace(/\x1b[()][AB012]/g, "").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "").replace(/\r\n?/g, "\n").trim();
|
|
2543
2543
|
}
|
|
@@ -2795,7 +2795,7 @@ var require_src = __commonJS({
|
|
|
2795
2795
|
return true;
|
|
2796
2796
|
}
|
|
2797
2797
|
try {
|
|
2798
|
-
const items =
|
|
2798
|
+
const items = fs10.readdirSync(validation.path, { withFileTypes: true });
|
|
2799
2799
|
const showHidden = url.searchParams.get("showHidden") === "true";
|
|
2800
2800
|
const folders = items.filter((item) => item.isDirectory()).filter((item) => !item.name.startsWith(".") || showHidden).map((item) => ({
|
|
2801
2801
|
name: item.name,
|
|
@@ -2830,7 +2830,7 @@ var require_src = __commonJS({
|
|
|
2830
2830
|
res.end(JSON.stringify({ error: validation.error }));
|
|
2831
2831
|
return;
|
|
2832
2832
|
}
|
|
2833
|
-
if (!
|
|
2833
|
+
if (!fs10.existsSync(validation.path) || !fs10.statSync(validation.path).isDirectory()) {
|
|
2834
2834
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2835
2835
|
res.end(JSON.stringify({ error: "Not a valid directory" }));
|
|
2836
2836
|
return;
|
|
@@ -2857,7 +2857,7 @@ var require_src = __commonJS({
|
|
|
2857
2857
|
res.end(JSON.stringify({ error: validation.error }));
|
|
2858
2858
|
return;
|
|
2859
2859
|
}
|
|
2860
|
-
if (!
|
|
2860
|
+
if (!fs10.existsSync(validation.path) || !fs10.statSync(validation.path).isDirectory()) {
|
|
2861
2861
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
2862
2862
|
res.end(JSON.stringify({ error: "Invalid directory path" }));
|
|
2863
2863
|
return;
|
|
@@ -2892,12 +2892,12 @@ var require_src = __commonJS({
|
|
|
2892
2892
|
res.end(JSON.stringify({ message: "Cannot create folder outside the allowed area" }));
|
|
2893
2893
|
return;
|
|
2894
2894
|
}
|
|
2895
|
-
if (
|
|
2895
|
+
if (fs10.existsSync(fullValidation.path)) {
|
|
2896
2896
|
res.writeHead(409, { "Content-Type": "application/json" });
|
|
2897
2897
|
res.end(JSON.stringify({ message: "Folder already exists" }));
|
|
2898
2898
|
return;
|
|
2899
2899
|
}
|
|
2900
|
-
|
|
2900
|
+
fs10.mkdirSync(fullValidation.path, { recursive: true });
|
|
2901
2901
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2902
2902
|
res.end(JSON.stringify({ success: true, path: fullValidation.path }));
|
|
2903
2903
|
} catch (e) {
|
|
@@ -2923,7 +2923,7 @@ var require_src = __commonJS({
|
|
|
2923
2923
|
if (!filePath || filePath === "") filePath = "index.html";
|
|
2924
2924
|
const fullPath = path9.join(__dirname, "public", filePath);
|
|
2925
2925
|
try {
|
|
2926
|
-
if (!
|
|
2926
|
+
if (!fs10.existsSync(fullPath)) return false;
|
|
2927
2927
|
const ext = path9.extname(fullPath);
|
|
2928
2928
|
const mimeTypes = {
|
|
2929
2929
|
".html": "text/html",
|
|
@@ -2935,7 +2935,7 @@ var require_src = __commonJS({
|
|
|
2935
2935
|
".ico": "image/x-icon"
|
|
2936
2936
|
};
|
|
2937
2937
|
const mime = mimeTypes[ext] || "application/octet-stream";
|
|
2938
|
-
const content =
|
|
2938
|
+
const content = fs10.readFileSync(fullPath);
|
|
2939
2939
|
res.writeHead(200, { "Content-Type": mime });
|
|
2940
2940
|
res.end(content);
|
|
2941
2941
|
return true;
|
|
@@ -4363,6 +4363,7 @@ function shutdownTerminalBridge() {
|
|
|
4363
4363
|
// src/server/mcp-config-writer.ts
|
|
4364
4364
|
var import_fs = __toESM(require("fs"));
|
|
4365
4365
|
var import_path = __toESM(require("path"));
|
|
4366
|
+
var MANAGED_SERVERS = ["launch-pod", "launch-chart"];
|
|
4366
4367
|
var writtenPaths = [];
|
|
4367
4368
|
function writeMcpConfigs(opts) {
|
|
4368
4369
|
const { projectDir, token, serverUrl } = opts;
|
|
@@ -4373,7 +4374,10 @@ function writeMcpConfigs(opts) {
|
|
|
4373
4374
|
function removeMcpConfigs() {
|
|
4374
4375
|
for (const filePath of writtenPaths) {
|
|
4375
4376
|
try {
|
|
4376
|
-
if (import_fs.default.existsSync(filePath))
|
|
4377
|
+
if (!import_fs.default.existsSync(filePath)) continue;
|
|
4378
|
+
if (filePath.endsWith(".mcp.json")) {
|
|
4379
|
+
removeClaudeEntries(filePath);
|
|
4380
|
+
} else {
|
|
4377
4381
|
import_fs.default.unlinkSync(filePath);
|
|
4378
4382
|
console.log(`MCP config removed: ${filePath}`);
|
|
4379
4383
|
}
|
|
@@ -4382,46 +4386,88 @@ function removeMcpConfigs() {
|
|
|
4382
4386
|
}
|
|
4383
4387
|
writtenPaths.length = 0;
|
|
4384
4388
|
}
|
|
4389
|
+
function removeClaudeEntries(filePath) {
|
|
4390
|
+
try {
|
|
4391
|
+
const raw = import_fs.default.readFileSync(filePath, "utf-8");
|
|
4392
|
+
const existing = JSON.parse(raw);
|
|
4393
|
+
const servers = existing.mcpServers ?? {};
|
|
4394
|
+
for (const key of MANAGED_SERVERS) {
|
|
4395
|
+
delete servers[key];
|
|
4396
|
+
}
|
|
4397
|
+
if (Object.keys(servers).length === 0 && Object.keys(existing).length <= 1) {
|
|
4398
|
+
import_fs.default.unlinkSync(filePath);
|
|
4399
|
+
console.log(`MCP config removed (empty): ${filePath}`);
|
|
4400
|
+
} else {
|
|
4401
|
+
existing.mcpServers = servers;
|
|
4402
|
+
import_fs.default.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
|
|
4403
|
+
console.log(`MCP config cleaned (LaunchPod entries removed): ${filePath}`);
|
|
4404
|
+
}
|
|
4405
|
+
} catch {
|
|
4406
|
+
try {
|
|
4407
|
+
import_fs.default.unlinkSync(filePath);
|
|
4408
|
+
} catch {
|
|
4409
|
+
}
|
|
4410
|
+
console.log(`MCP config removed: ${filePath}`);
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4385
4413
|
function writeClaudeConfig(projectDir, mcpUrl, token) {
|
|
4386
|
-
const
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
"
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4414
|
+
const filePath = import_path.default.join(projectDir, ".mcp.json");
|
|
4415
|
+
let existing = {};
|
|
4416
|
+
try {
|
|
4417
|
+
if (import_fs.default.existsSync(filePath)) {
|
|
4418
|
+
existing = JSON.parse(import_fs.default.readFileSync(filePath, "utf-8"));
|
|
4419
|
+
}
|
|
4420
|
+
} catch {
|
|
4421
|
+
console.warn(`Warning: existing .mcp.json was malformed, preserving as .mcp.json.bak`);
|
|
4422
|
+
try {
|
|
4423
|
+
import_fs.default.copyFileSync(filePath, filePath + ".bak");
|
|
4424
|
+
} catch {
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
const servers = existing.mcpServers ?? {};
|
|
4428
|
+
servers["launch-pod"] = {
|
|
4429
|
+
type: "http",
|
|
4430
|
+
url: mcpUrl,
|
|
4431
|
+
headers: {
|
|
4432
|
+
Authorization: `Bearer ${token}`
|
|
4399
4433
|
}
|
|
4400
4434
|
};
|
|
4401
|
-
|
|
4402
|
-
|
|
4435
|
+
servers["launch-chart"] = {
|
|
4436
|
+
command: "launch-chart",
|
|
4437
|
+
args: []
|
|
4438
|
+
};
|
|
4439
|
+
existing.mcpServers = servers;
|
|
4440
|
+
import_fs.default.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", { mode: 384 });
|
|
4403
4441
|
writtenPaths.push(filePath);
|
|
4404
|
-
console.log(`Claude Code MCP config
|
|
4442
|
+
console.log(`Claude Code MCP config merged into ${filePath}`);
|
|
4405
4443
|
}
|
|
4406
4444
|
function writeCodexConfig(projectDir, mcpUrl, token) {
|
|
4407
4445
|
const codexDir = import_path.default.join(projectDir, ".codex");
|
|
4408
4446
|
if (!import_fs.default.existsSync(codexDir)) {
|
|
4409
4447
|
import_fs.default.mkdirSync(codexDir, { recursive: true, mode: 448 });
|
|
4410
4448
|
}
|
|
4411
|
-
const
|
|
4449
|
+
const filePath = import_path.default.join(codexDir, "config.toml");
|
|
4450
|
+
let existingContent = "";
|
|
4451
|
+
try {
|
|
4452
|
+
if (import_fs.default.existsSync(filePath)) {
|
|
4453
|
+
existingContent = import_fs.default.readFileSync(filePath, "utf-8");
|
|
4454
|
+
}
|
|
4455
|
+
} catch {
|
|
4456
|
+
}
|
|
4457
|
+
const cleaned = existingContent.replace(/\[mcp_servers\.launch-pod\][^\[]*/, "").replace(/\[mcp_servers\.launch-chart\][^\[]*/, "").trim();
|
|
4458
|
+
const launchPodBlock = [
|
|
4412
4459
|
`[mcp_servers.launch-pod]`,
|
|
4413
4460
|
`url = "${mcpUrl}"`,
|
|
4414
4461
|
`http_headers = { "Authorization" = "Bearer ${token}" }`,
|
|
4415
4462
|
``,
|
|
4416
4463
|
`[mcp_servers.launch-chart]`,
|
|
4417
4464
|
`command = "launch-chart"`,
|
|
4418
|
-
`args = []
|
|
4419
|
-
``
|
|
4465
|
+
`args = []`
|
|
4420
4466
|
].join("\n");
|
|
4421
|
-
const
|
|
4422
|
-
import_fs.default.writeFileSync(filePath,
|
|
4467
|
+
const merged = cleaned ? cleaned + "\n\n" + launchPodBlock + "\n" : launchPodBlock + "\n";
|
|
4468
|
+
import_fs.default.writeFileSync(filePath, merged, { mode: 384 });
|
|
4423
4469
|
writtenPaths.push(filePath);
|
|
4424
|
-
console.log(`Codex MCP config
|
|
4470
|
+
console.log(`Codex MCP config merged into ${filePath}`);
|
|
4425
4471
|
}
|
|
4426
4472
|
|
|
4427
4473
|
// src/server/tracker-poller.ts
|
|
@@ -4528,13 +4574,18 @@ All pipeline data lives in the **database**, not on the local filesystem.
|
|
|
4528
4574
|
---
|
|
4529
4575
|
|
|
4530
4576
|
`;
|
|
4531
|
-
function resolvePrompt(agentId, unitId, inputs) {
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4577
|
+
function resolvePrompt(agentId, unitId, inputs, promptOverride) {
|
|
4578
|
+
let raw;
|
|
4579
|
+
if (promptOverride) {
|
|
4580
|
+
raw = promptOverride;
|
|
4581
|
+
} else {
|
|
4582
|
+
const hyphenated = agentId.replace(/_/g, "-");
|
|
4583
|
+
const filePath = import_path2.default.join(PROMPTS_DIR, `${hyphenated}.md`);
|
|
4584
|
+
if (!import_fs2.default.existsSync(filePath)) {
|
|
4585
|
+
throw new Error(`Prompt file not found for agent "${agentId}": ${filePath}`);
|
|
4586
|
+
}
|
|
4587
|
+
raw = import_fs2.default.readFileSync(filePath, "utf-8");
|
|
4536
4588
|
}
|
|
4537
|
-
const raw = import_fs2.default.readFileSync(filePath, "utf-8");
|
|
4538
4589
|
const unitPreamble = unitId ? `
|
|
4539
4590
|
---
|
|
4540
4591
|
|
|
@@ -4653,7 +4704,7 @@ var NodeTriggerController = class {
|
|
|
4653
4704
|
async startSession(agentId, agent, unitId) {
|
|
4654
4705
|
const key = this.sessionKey(agentId, unitId);
|
|
4655
4706
|
try {
|
|
4656
|
-
const prompt = resolvePrompt(agentId, unitId, agent.in);
|
|
4707
|
+
const prompt = resolvePrompt(agentId, unitId, agent.in, agent.po);
|
|
4657
4708
|
const sessionId = await this.adapter.start({
|
|
4658
4709
|
agentId,
|
|
4659
4710
|
prompt,
|
|
@@ -10099,35 +10150,237 @@ async function handleGraphCommand(subcommand, args) {
|
|
|
10099
10150
|
}
|
|
10100
10151
|
|
|
10101
10152
|
// src/server/graph-mcp.ts
|
|
10102
|
-
var
|
|
10103
|
-
var
|
|
10153
|
+
var import_node_fs19 = require("node:fs");
|
|
10154
|
+
var import_node_path21 = require("node:path");
|
|
10104
10155
|
var import_node_child_process2 = require("node:child_process");
|
|
10105
10156
|
var import_node_os2 = require("node:os");
|
|
10106
10157
|
|
|
10158
|
+
// src/server/blast-radius-builder.ts
|
|
10159
|
+
var import_node_fs15 = __toESM(require("node:fs"));
|
|
10160
|
+
var import_node_path17 = require("node:path");
|
|
10161
|
+
var FALLBACK_DEFAULTS = {
|
|
10162
|
+
rings: [
|
|
10163
|
+
{ id: "modify", name: "Modify", color: "#ff6b00" },
|
|
10164
|
+
{ id: "ripple", name: "Ripple (verify)", color: "#ffff00" },
|
|
10165
|
+
{ id: "create", name: "Create", color: "#00ff00" }
|
|
10166
|
+
],
|
|
10167
|
+
layers: {
|
|
10168
|
+
db: { name: "Database", icon: "database", color: "#cbd5e1" },
|
|
10169
|
+
api: { name: "API", icon: "server", color: "#cbd5e1" },
|
|
10170
|
+
middleware: { name: "Middleware", icon: "shield", color: "#cbd5e1" },
|
|
10171
|
+
ui: { name: "UI", icon: "layout-dashboard", color: "#cbd5e1" },
|
|
10172
|
+
config: { name: "Config / Seed", icon: "settings", color: "#cbd5e1" },
|
|
10173
|
+
shared: { name: "Shared Types", icon: "box", color: "#cbd5e1" }
|
|
10174
|
+
},
|
|
10175
|
+
center: { color: "#ff0000" }
|
|
10176
|
+
};
|
|
10177
|
+
function loadDefaults(rootDir) {
|
|
10178
|
+
const filePath = (0, import_node_path17.join)(rootDir, ".launchsecure", "blast-radius-defaults.json");
|
|
10179
|
+
try {
|
|
10180
|
+
if (import_node_fs15.default.existsSync(filePath)) {
|
|
10181
|
+
const raw = import_node_fs15.default.readFileSync(filePath, "utf-8");
|
|
10182
|
+
return JSON.parse(raw);
|
|
10183
|
+
}
|
|
10184
|
+
} catch {
|
|
10185
|
+
}
|
|
10186
|
+
return FALLBACK_DEFAULTS;
|
|
10187
|
+
}
|
|
10188
|
+
function generateAcceptance(node, inspect) {
|
|
10189
|
+
const criteria = [];
|
|
10190
|
+
const t = node.type?.toLowerCase() ?? "";
|
|
10191
|
+
if (t === "endpoint" || t === "mcp-tool") {
|
|
10192
|
+
const methods = inspect?.methods ?? [];
|
|
10193
|
+
const path9 = inspect?.path ?? node.id;
|
|
10194
|
+
if (methods.length > 0) {
|
|
10195
|
+
criteria.push(`${methods.join("/")} ${path9} still returns correct responses for authorized users`);
|
|
10196
|
+
} else {
|
|
10197
|
+
criteria.push(`${path9} still responds correctly`);
|
|
10198
|
+
}
|
|
10199
|
+
if (inspect?.auth && inspect.auth.includes("withAuth")) {
|
|
10200
|
+
criteria.push("Authentication and authorization still enforced");
|
|
10201
|
+
}
|
|
10202
|
+
if (inspect?.db_models && inspect.db_models.length > 0) {
|
|
10203
|
+
criteria.push(`DB operations on ${inspect.db_models.join(", ")} still work correctly`);
|
|
10204
|
+
}
|
|
10205
|
+
} else if (t === "page" || t === "component" || t === "layout") {
|
|
10206
|
+
criteria.push(`${node.name} renders without errors`);
|
|
10207
|
+
if (inspect?.stateVars && inspect.stateVars.length > 0) {
|
|
10208
|
+
criteria.push("State management still works correctly");
|
|
10209
|
+
}
|
|
10210
|
+
if (inspect?.elements && inspect.elements.length > 5) {
|
|
10211
|
+
criteria.push("All child components render correctly");
|
|
10212
|
+
}
|
|
10213
|
+
} else if (t === "table" || t === "enum") {
|
|
10214
|
+
criteria.push(`${node.name} schema unchanged or migration applies cleanly`);
|
|
10215
|
+
criteria.push("Existing queries against this table still work");
|
|
10216
|
+
} else if (t === "hook") {
|
|
10217
|
+
criteria.push(`${node.name} returns expected shape`);
|
|
10218
|
+
if (inspect?.stateVars && inspect.stateVars.length > 0) {
|
|
10219
|
+
criteria.push(`State variables [${inspect.stateVars.map((s) => s.name).join(", ")}] still returned`);
|
|
10220
|
+
}
|
|
10221
|
+
} else if (t === "context") {
|
|
10222
|
+
criteria.push(`${node.name} provides correct context to consumers`);
|
|
10223
|
+
} else if (t === "lib" || t === "config" || t === "types") {
|
|
10224
|
+
criteria.push(`${node.name} exports still conform to expected interface`);
|
|
10225
|
+
} else if (t === "seed" || t === "seed_role" || t === "seed_permission") {
|
|
10226
|
+
criteria.push("Seed runs without errors");
|
|
10227
|
+
criteria.push("Expected rows created in database");
|
|
10228
|
+
} else {
|
|
10229
|
+
criteria.push("Verify no regression");
|
|
10230
|
+
}
|
|
10231
|
+
return criteria;
|
|
10232
|
+
}
|
|
10233
|
+
function buildManifest(input) {
|
|
10234
|
+
const { mode, title, description, subtitle, blastResults, createNodes, inspectData, defaults } = input;
|
|
10235
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
10236
|
+
const centerNodeIds = /* @__PURE__ */ new Set();
|
|
10237
|
+
for (const result of blastResults) {
|
|
10238
|
+
centerNodeIds.add(result.center.id);
|
|
10239
|
+
for (const node of result.affected) {
|
|
10240
|
+
const existing = nodeMap.get(node.id);
|
|
10241
|
+
if (!existing || node.hop < existing.hop) {
|
|
10242
|
+
nodeMap.set(node.id, node);
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
for (const id of centerNodeIds) {
|
|
10247
|
+
nodeMap.delete(id);
|
|
10248
|
+
}
|
|
10249
|
+
const manifestNodes = [];
|
|
10250
|
+
for (const result of blastResults) {
|
|
10251
|
+
const c = result.center;
|
|
10252
|
+
if (manifestNodes.some((n) => n.id === c.id)) continue;
|
|
10253
|
+
const inspect = inspectData[c.id];
|
|
10254
|
+
manifestNodes.push({
|
|
10255
|
+
id: c.id,
|
|
10256
|
+
name: c.name,
|
|
10257
|
+
layer: c.layer,
|
|
10258
|
+
ring: "modify",
|
|
10259
|
+
type: c.type,
|
|
10260
|
+
reason: `Direct change target`,
|
|
10261
|
+
acceptance: generateAcceptance(
|
|
10262
|
+
{ id: c.id, name: c.name, type: c.type, layer: c.layer, hop: 0 },
|
|
10263
|
+
inspect
|
|
10264
|
+
)
|
|
10265
|
+
});
|
|
10266
|
+
}
|
|
10267
|
+
for (const [, node] of nodeMap) {
|
|
10268
|
+
const ring = node.hop <= 1 ? "modify" : "ripple";
|
|
10269
|
+
const inspect = inspectData[node.id];
|
|
10270
|
+
const reason = node.hop <= 1 ? `Directly depends on changed node` : `Indirect dependency (${node.hop} hops away)`;
|
|
10271
|
+
manifestNodes.push({
|
|
10272
|
+
id: node.id,
|
|
10273
|
+
name: node.name,
|
|
10274
|
+
layer: node.layer,
|
|
10275
|
+
ring,
|
|
10276
|
+
type: node.type,
|
|
10277
|
+
reason,
|
|
10278
|
+
acceptance: generateAcceptance(node, inspect)
|
|
10279
|
+
});
|
|
10280
|
+
}
|
|
10281
|
+
for (const cn of createNodes) {
|
|
10282
|
+
manifestNodes.push({
|
|
10283
|
+
id: cn.id,
|
|
10284
|
+
name: cn.name,
|
|
10285
|
+
layer: cn.layer,
|
|
10286
|
+
ring: "create",
|
|
10287
|
+
type: cn.type ?? "unknown",
|
|
10288
|
+
reason: cn.reason,
|
|
10289
|
+
acceptance: cn.acceptance ?? ["Verify implementation matches spec"]
|
|
10290
|
+
});
|
|
10291
|
+
}
|
|
10292
|
+
const layerIds = /* @__PURE__ */ new Set();
|
|
10293
|
+
for (const n of manifestNodes) {
|
|
10294
|
+
layerIds.add(n.layer);
|
|
10295
|
+
}
|
|
10296
|
+
const layers = [];
|
|
10297
|
+
for (const id of layerIds) {
|
|
10298
|
+
const def = defaults.layers[id];
|
|
10299
|
+
if (def) {
|
|
10300
|
+
layers.push({ id, name: def.name, icon: def.icon, color: def.color });
|
|
10301
|
+
} else {
|
|
10302
|
+
layers.push({ id, name: id, icon: "box", color: "#cbd5e1" });
|
|
10303
|
+
}
|
|
10304
|
+
}
|
|
10305
|
+
const edgeSet = /* @__PURE__ */ new Set();
|
|
10306
|
+
const edges = [];
|
|
10307
|
+
const allNodeIds = new Set(manifestNodes.map((n) => n.id));
|
|
10308
|
+
for (const cId of centerNodeIds) {
|
|
10309
|
+
for (const result of blastResults) {
|
|
10310
|
+
for (const affected of result.affected) {
|
|
10311
|
+
if (affected.hop === 1 && result.center.id === cId && allNodeIds.has(affected.id)) {
|
|
10312
|
+
const key = `${cId}->${affected.id}`;
|
|
10313
|
+
if (!edgeSet.has(key)) {
|
|
10314
|
+
edgeSet.add(key);
|
|
10315
|
+
edges.push({ source: cId, target: affected.id });
|
|
10316
|
+
}
|
|
10317
|
+
}
|
|
10318
|
+
}
|
|
10319
|
+
}
|
|
10320
|
+
}
|
|
10321
|
+
for (const result of blastResults) {
|
|
10322
|
+
if (result.edges) {
|
|
10323
|
+
for (const edge of result.edges) {
|
|
10324
|
+
if (allNodeIds.has(edge.source) && allNodeIds.has(edge.target)) {
|
|
10325
|
+
const key = `${edge.source}->${edge.target}`;
|
|
10326
|
+
if (!edgeSet.has(key)) {
|
|
10327
|
+
edgeSet.add(key);
|
|
10328
|
+
edges.push({ source: edge.source, target: edge.target });
|
|
10329
|
+
}
|
|
10330
|
+
}
|
|
10331
|
+
}
|
|
10332
|
+
}
|
|
10333
|
+
}
|
|
10334
|
+
for (const cn of createNodes) {
|
|
10335
|
+
edges.push({ source: "center", target: cn.id });
|
|
10336
|
+
if (cn.connects_to) {
|
|
10337
|
+
for (const targetId of cn.connects_to) {
|
|
10338
|
+
if (allNodeIds.has(targetId) || createNodes.some((c) => c.id === targetId)) {
|
|
10339
|
+
const key = `${cn.id}->${targetId}`;
|
|
10340
|
+
if (!edgeSet.has(key)) {
|
|
10341
|
+
edgeSet.add(key);
|
|
10342
|
+
edges.push({ source: cn.id, target: targetId });
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10347
|
+
}
|
|
10348
|
+
return {
|
|
10349
|
+
mode,
|
|
10350
|
+
title,
|
|
10351
|
+
subtitle,
|
|
10352
|
+
layers,
|
|
10353
|
+
rings: defaults.rings,
|
|
10354
|
+
center: { name: title, description },
|
|
10355
|
+
nodes: manifestNodes,
|
|
10356
|
+
edges
|
|
10357
|
+
};
|
|
10358
|
+
}
|
|
10359
|
+
|
|
10107
10360
|
// src/server/lockfile.ts
|
|
10108
10361
|
var import_node_child_process = require("node:child_process");
|
|
10109
|
-
var
|
|
10362
|
+
var import_node_fs16 = require("node:fs");
|
|
10110
10363
|
var import_node_os = require("node:os");
|
|
10111
|
-
var
|
|
10364
|
+
var import_node_path18 = require("node:path");
|
|
10112
10365
|
function lockDir(projectRoot) {
|
|
10113
10366
|
if (projectRoot) {
|
|
10114
|
-
return (0,
|
|
10367
|
+
return (0, import_node_path18.join)(projectRoot, ".launchsecure");
|
|
10115
10368
|
}
|
|
10116
|
-
return (0,
|
|
10369
|
+
return (0, import_node_path18.join)((0, import_node_os.homedir)(), ".launchsecure");
|
|
10117
10370
|
}
|
|
10118
10371
|
function lockPath(projectRoot) {
|
|
10119
|
-
return (0,
|
|
10372
|
+
return (0, import_node_path18.join)(lockDir(projectRoot), "launch-chart.lock");
|
|
10120
10373
|
}
|
|
10121
10374
|
var _activeProjectRoot;
|
|
10122
10375
|
function readLock(projectRoot) {
|
|
10123
10376
|
const root = projectRoot ?? _activeProjectRoot;
|
|
10124
10377
|
const p = lockPath(root);
|
|
10125
|
-
if (!(0,
|
|
10378
|
+
if (!(0, import_node_fs16.existsSync)(p)) {
|
|
10126
10379
|
if (root) {
|
|
10127
10380
|
const globalP = lockPath();
|
|
10128
|
-
if ((0,
|
|
10381
|
+
if ((0, import_node_fs16.existsSync)(globalP)) {
|
|
10129
10382
|
try {
|
|
10130
|
-
const data = JSON.parse((0,
|
|
10383
|
+
const data = JSON.parse((0, import_node_fs16.readFileSync)(globalP, "utf-8"));
|
|
10131
10384
|
if (typeof data.pid === "number" && typeof data.port === "number" && data.cwd === root) {
|
|
10132
10385
|
return data;
|
|
10133
10386
|
}
|
|
@@ -10138,7 +10391,7 @@ function readLock(projectRoot) {
|
|
|
10138
10391
|
return null;
|
|
10139
10392
|
}
|
|
10140
10393
|
try {
|
|
10141
|
-
const data = JSON.parse((0,
|
|
10394
|
+
const data = JSON.parse((0, import_node_fs16.readFileSync)(p, "utf-8"));
|
|
10142
10395
|
if (typeof data.pid !== "number" || typeof data.port !== "number") return null;
|
|
10143
10396
|
return data;
|
|
10144
10397
|
} catch {
|
|
@@ -10175,7 +10428,7 @@ function getLiveLock(projectRoot) {
|
|
|
10175
10428
|
const live = listenerPid !== null ? listenerPid === lock.pid : isPidAlive(lock.pid);
|
|
10176
10429
|
if (!live) {
|
|
10177
10430
|
try {
|
|
10178
|
-
(0,
|
|
10431
|
+
(0, import_node_fs16.unlinkSync)(lockPath(root));
|
|
10179
10432
|
} catch {
|
|
10180
10433
|
}
|
|
10181
10434
|
return null;
|
|
@@ -10185,7 +10438,7 @@ function getLiveLock(projectRoot) {
|
|
|
10185
10438
|
function clearLock(projectRoot) {
|
|
10186
10439
|
const root = projectRoot ?? _activeProjectRoot;
|
|
10187
10440
|
try {
|
|
10188
|
-
(0,
|
|
10441
|
+
(0, import_node_fs16.unlinkSync)(lockPath(root));
|
|
10189
10442
|
} catch {
|
|
10190
10443
|
}
|
|
10191
10444
|
}
|
|
@@ -10194,8 +10447,8 @@ function clearLock(projectRoot) {
|
|
|
10194
10447
|
init_config();
|
|
10195
10448
|
|
|
10196
10449
|
// src/server/graph/core/language-detection.ts
|
|
10197
|
-
var
|
|
10198
|
-
var
|
|
10450
|
+
var import_node_fs17 = require("node:fs");
|
|
10451
|
+
var import_node_path19 = require("node:path");
|
|
10199
10452
|
var EXTENSION_TO_LANGUAGE = {
|
|
10200
10453
|
// Web / Frontend
|
|
10201
10454
|
".ts": "typescript",
|
|
@@ -10307,10 +10560,10 @@ var AUXILIARY_LANGUAGES = /* @__PURE__ */ new Set([
|
|
|
10307
10560
|
]);
|
|
10308
10561
|
function walkForExtensions(dir, extCounts, depth = 0) {
|
|
10309
10562
|
if (depth > 10) return;
|
|
10310
|
-
if (!(0,
|
|
10563
|
+
if (!(0, import_node_fs17.existsSync)(dir)) return;
|
|
10311
10564
|
let entries;
|
|
10312
10565
|
try {
|
|
10313
|
-
entries = (0,
|
|
10566
|
+
entries = (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true });
|
|
10314
10567
|
} catch {
|
|
10315
10568
|
return;
|
|
10316
10569
|
}
|
|
@@ -10318,9 +10571,9 @@ function walkForExtensions(dir, extCounts, depth = 0) {
|
|
|
10318
10571
|
if (entry.name.startsWith(".") && entry.isDirectory()) continue;
|
|
10319
10572
|
if (entry.isDirectory()) {
|
|
10320
10573
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
10321
|
-
walkForExtensions((0,
|
|
10574
|
+
walkForExtensions((0, import_node_path19.join)(dir, entry.name), extCounts, depth + 1);
|
|
10322
10575
|
} else {
|
|
10323
|
-
const ext = (0,
|
|
10576
|
+
const ext = (0, import_node_path19.extname)(entry.name).toLowerCase();
|
|
10324
10577
|
if (ext && EXTENSION_TO_LANGUAGE[ext]) {
|
|
10325
10578
|
extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
10326
10579
|
}
|
|
@@ -10361,13 +10614,13 @@ function detectLanguages(rootDir, supportedLanguages) {
|
|
|
10361
10614
|
}
|
|
10362
10615
|
|
|
10363
10616
|
// src/server/graph/core/audit-core.ts
|
|
10364
|
-
var
|
|
10365
|
-
var
|
|
10617
|
+
var import_node_fs18 = require("node:fs");
|
|
10618
|
+
var import_node_path20 = require("node:path");
|
|
10366
10619
|
function readGraphFile(rootDir, layer) {
|
|
10367
|
-
const filePath = (0,
|
|
10368
|
-
if (!(0,
|
|
10620
|
+
const filePath = (0, import_node_path20.join)(rootDir, ".launchsecure", "graphs", `${layer}.json`);
|
|
10621
|
+
if (!(0, import_node_fs18.existsSync)(filePath)) return null;
|
|
10369
10622
|
try {
|
|
10370
|
-
return JSON.parse((0,
|
|
10623
|
+
return JSON.parse((0, import_node_fs18.readFileSync)(filePath, "utf-8"));
|
|
10371
10624
|
} catch {
|
|
10372
10625
|
return null;
|
|
10373
10626
|
}
|
|
@@ -10411,10 +10664,10 @@ function checkUnprotectedRoutes(rootDir) {
|
|
|
10411
10664
|
const api = readGraphFile(rootDir, "api");
|
|
10412
10665
|
const staticGraph = readGraphFile(rootDir, "static");
|
|
10413
10666
|
if (!api) return buildReport("api", "unprotected_routes", findings);
|
|
10414
|
-
const routePermsPath = (0,
|
|
10667
|
+
const routePermsPath = (0, import_node_path20.join)(rootDir, "src", "config", "route-permissions.ts");
|
|
10415
10668
|
let routePermsContent = "";
|
|
10416
|
-
if ((0,
|
|
10417
|
-
routePermsContent = (0,
|
|
10669
|
+
if ((0, import_node_fs18.existsSync)(routePermsPath)) {
|
|
10670
|
+
routePermsContent = (0, import_node_fs18.readFileSync)(routePermsPath, "utf-8");
|
|
10418
10671
|
}
|
|
10419
10672
|
const registeredRoutes = /* @__PURE__ */ new Set();
|
|
10420
10673
|
const routeEntryRe = /path:\s*'([^']+)'/g;
|
|
@@ -10491,10 +10744,10 @@ function checkUnenforcedPermissions(rootDir) {
|
|
|
10491
10744
|
const staticGraph = readGraphFile(rootDir, "static");
|
|
10492
10745
|
if (!staticGraph) return buildReport("static", "unenforced_permissions", findings);
|
|
10493
10746
|
const permissions = staticGraph.nodes.filter((n) => n.type === "seed_permission").map((n) => ({ id: n.id, key: n.value, name: n.name }));
|
|
10494
|
-
const routePermsPath = (0,
|
|
10747
|
+
const routePermsPath = (0, import_node_path20.join)(rootDir, "src", "config", "route-permissions.ts");
|
|
10495
10748
|
let routePermsContent = "";
|
|
10496
|
-
if ((0,
|
|
10497
|
-
routePermsContent = (0,
|
|
10749
|
+
if ((0, import_node_fs18.existsSync)(routePermsPath)) {
|
|
10750
|
+
routePermsContent = (0, import_node_fs18.readFileSync)(routePermsPath, "utf-8");
|
|
10498
10751
|
}
|
|
10499
10752
|
for (const perm of permissions) {
|
|
10500
10753
|
const regex = new RegExp(`permission:\\s*['"]${perm.key}['"]`);
|
|
@@ -10524,9 +10777,9 @@ function checkHardcodedValues(rootDir) {
|
|
|
10524
10777
|
const seen = /* @__PURE__ */ new Set();
|
|
10525
10778
|
for (const node of api.nodes) {
|
|
10526
10779
|
if (node.type !== "endpoint") continue;
|
|
10527
|
-
const filePath = (0,
|
|
10528
|
-
if (!(0,
|
|
10529
|
-
const content = (0,
|
|
10780
|
+
const filePath = (0, import_node_path20.join)(rootDir, "src", node.id);
|
|
10781
|
+
if (!(0, import_node_fs18.existsSync)(filePath)) continue;
|
|
10782
|
+
const content = (0, import_node_fs18.readFileSync)(filePath, "utf-8");
|
|
10530
10783
|
let m;
|
|
10531
10784
|
allCapsRe.lastIndex = 0;
|
|
10532
10785
|
while ((m = allCapsRe.exec(content)) !== null) {
|
|
@@ -10925,6 +11178,81 @@ Example: blast_points(node_id: "server/auth/middleware.ts", hops: 2) \u2192 retu
|
|
|
10925
11178
|
},
|
|
10926
11179
|
required: ["node_id"]
|
|
10927
11180
|
}
|
|
11181
|
+
},
|
|
11182
|
+
{
|
|
11183
|
+
name: "generate_blast_radius",
|
|
11184
|
+
description: `Generate a complete BlastRadiusManifest from graph data \u2014 ready to push to deck.
|
|
11185
|
+
|
|
11186
|
+
Two modes:
|
|
11187
|
+
- **Structural**: single node changed \u2192 auto-discover what's affected via reverse BFS
|
|
11188
|
+
Example: generate_blast_radius({ mode: "structural", node_id: "CommentChannel", title: "CommentChannel refactor" })
|
|
11189
|
+
- **Feature**: new feature \u2192 multiple starting nodes + new nodes to create
|
|
11190
|
+
Example: generate_blast_radius({ mode: "feature", title: "Client Role", description: "...", center_nodes: ["CommentChannel", "ProjectMember"], create_nodes: [{ id: "ChannelMember", name: "ChannelMember table", layer: "db", reason: "..." }] })
|
|
11191
|
+
|
|
11192
|
+
Output is a BlastRadiusManifest JSON that passes directly to the deck tool's blast-radius block.
|
|
11193
|
+
Reads ring/layer/center colors from .launchsecure/blast-radius-defaults.json.
|
|
11194
|
+
Auto-generates acceptance criteria per node using inspect_node AST data.`,
|
|
11195
|
+
inputSchema: {
|
|
11196
|
+
type: "object",
|
|
11197
|
+
properties: {
|
|
11198
|
+
mode: {
|
|
11199
|
+
type: "string",
|
|
11200
|
+
enum: ["structural", "feature"],
|
|
11201
|
+
description: '"structural" = single node changed. "feature" = new feature with multiple nodes.'
|
|
11202
|
+
},
|
|
11203
|
+
title: {
|
|
11204
|
+
type: "string",
|
|
11205
|
+
description: "Title for the blast radius (shown in center node and header)."
|
|
11206
|
+
},
|
|
11207
|
+
description: {
|
|
11208
|
+
type: "string",
|
|
11209
|
+
description: "Description of the change or feature."
|
|
11210
|
+
},
|
|
11211
|
+
subtitle: {
|
|
11212
|
+
type: "string",
|
|
11213
|
+
description: "Optional subtitle shown above title in the viz."
|
|
11214
|
+
},
|
|
11215
|
+
node_id: {
|
|
11216
|
+
type: "string",
|
|
11217
|
+
description: "Structural mode only: the node being changed."
|
|
11218
|
+
},
|
|
11219
|
+
center_nodes: {
|
|
11220
|
+
type: "array",
|
|
11221
|
+
items: { type: "string" },
|
|
11222
|
+
description: "Feature mode: existing graph node IDs that are the starting points for traversal."
|
|
11223
|
+
},
|
|
11224
|
+
create_nodes: {
|
|
11225
|
+
type: "array",
|
|
11226
|
+
items: {
|
|
11227
|
+
type: "object",
|
|
11228
|
+
properties: {
|
|
11229
|
+
id: { type: "string" },
|
|
11230
|
+
name: { type: "string" },
|
|
11231
|
+
layer: { type: "string" },
|
|
11232
|
+
type: { type: "string" },
|
|
11233
|
+
reason: { type: "string" },
|
|
11234
|
+
acceptance: { type: "array", items: { type: "string" } },
|
|
11235
|
+
connects_to: { type: "array", items: { type: "string" }, description: "IDs of existing nodes this new node has FK/relationship edges to." }
|
|
11236
|
+
},
|
|
11237
|
+
required: ["id", "name", "layer", "reason"]
|
|
11238
|
+
},
|
|
11239
|
+
description: "Feature mode: new nodes that need to be created (not in graph yet)."
|
|
11240
|
+
},
|
|
11241
|
+
hops: {
|
|
11242
|
+
type: "number",
|
|
11243
|
+
description: "Max hops for traversal. Default 2. Hop 1 = modify ring, hop 2+ = ripple ring."
|
|
11244
|
+
},
|
|
11245
|
+
push_to_deck: {
|
|
11246
|
+
type: "boolean",
|
|
11247
|
+
description: "If true, pushes the manifest directly to LaunchDeck browser (requires deck server running). Default false."
|
|
11248
|
+
},
|
|
11249
|
+
session: {
|
|
11250
|
+
type: "string",
|
|
11251
|
+
description: "Session name for the deck tab. Required when push_to_deck is true."
|
|
11252
|
+
}
|
|
11253
|
+
},
|
|
11254
|
+
required: ["title"]
|
|
11255
|
+
}
|
|
10928
11256
|
}
|
|
10929
11257
|
];
|
|
10930
11258
|
function matchesSearch(node, query) {
|
|
@@ -11201,6 +11529,144 @@ function handleBlastPoints(args) {
|
|
|
11201
11529
|
}
|
|
11202
11530
|
});
|
|
11203
11531
|
}
|
|
11532
|
+
function handleGenerateBlastRadius(args) {
|
|
11533
|
+
const rootDir = process.cwd();
|
|
11534
|
+
const mode = args.mode ?? "structural";
|
|
11535
|
+
const title = args.title;
|
|
11536
|
+
const description = args.description ?? title;
|
|
11537
|
+
const subtitle = args.subtitle;
|
|
11538
|
+
const hops = args.hops ?? 2;
|
|
11539
|
+
const defaults = loadDefaults(rootDir);
|
|
11540
|
+
let centerNodeIds = [];
|
|
11541
|
+
if (mode === "structural") {
|
|
11542
|
+
const nodeId = args.node_id;
|
|
11543
|
+
if (!nodeId) return err("structural mode requires node_id");
|
|
11544
|
+
centerNodeIds = [nodeId];
|
|
11545
|
+
} else {
|
|
11546
|
+
centerNodeIds = args.center_nodes ?? [];
|
|
11547
|
+
if (centerNodeIds.length === 0) return err("feature mode requires center_nodes[]");
|
|
11548
|
+
}
|
|
11549
|
+
const createNodes = args.create_nodes ?? [];
|
|
11550
|
+
const blastResults = [];
|
|
11551
|
+
for (const nodeId of centerNodeIds) {
|
|
11552
|
+
let targetLayer;
|
|
11553
|
+
const graphs = readAllGraphs(rootDir);
|
|
11554
|
+
for (const [layer, graph2] of Object.entries(graphs)) {
|
|
11555
|
+
if (graph2 && graph2.nodes.some((n) => n.id === nodeId)) {
|
|
11556
|
+
targetLayer = layer;
|
|
11557
|
+
break;
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
if (!targetLayer) continue;
|
|
11561
|
+
const graph = readGraph(rootDir, targetLayer);
|
|
11562
|
+
if (!graph) continue;
|
|
11563
|
+
const center = graph.nodes.find((n) => n.id === nodeId);
|
|
11564
|
+
if (!center) continue;
|
|
11565
|
+
const result2 = reverseNeighborhood(graph, nodeId, hops, "reverse");
|
|
11566
|
+
const affected = [];
|
|
11567
|
+
for (const [id, { node, hop }] of result2.nodes) {
|
|
11568
|
+
if (id === nodeId) continue;
|
|
11569
|
+
const tags = node.tags;
|
|
11570
|
+
affected.push({ id: node.id, name: node.name, type: node.type, layer: targetLayer, hop, module: tags?.module });
|
|
11571
|
+
}
|
|
11572
|
+
const otherLayers = getAvailableLayers(rootDir).filter((l) => l !== targetLayer && l !== "static");
|
|
11573
|
+
for (const otherLayer of otherLayers) {
|
|
11574
|
+
const otherGraph = readGraph(rootDir, otherLayer);
|
|
11575
|
+
if (!otherGraph) continue;
|
|
11576
|
+
for (const edge of otherGraph.edges) {
|
|
11577
|
+
if (edge.target === nodeId || edge.source === nodeId) {
|
|
11578
|
+
const dependentId = edge.target === nodeId ? edge.source : edge.target;
|
|
11579
|
+
if (affected.some((a) => a.id === dependentId)) continue;
|
|
11580
|
+
const depNode = otherGraph.nodes.find((n) => n.id === dependentId);
|
|
11581
|
+
if (depNode) {
|
|
11582
|
+
const tags = depNode.tags;
|
|
11583
|
+
affected.push({ id: depNode.id, name: depNode.name, type: depNode.type, layer: otherLayer, hop: 1, module: tags?.module });
|
|
11584
|
+
}
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
const centerTags = center.tags;
|
|
11589
|
+
const edges = result2.edges.map((e) => ({ source: e.source, target: e.target }));
|
|
11590
|
+
blastResults.push({
|
|
11591
|
+
center: { id: center.id, name: center.name, type: center.type, layer: targetLayer, module: centerTags?.module },
|
|
11592
|
+
affected,
|
|
11593
|
+
edges
|
|
11594
|
+
});
|
|
11595
|
+
}
|
|
11596
|
+
if (blastResults.length === 0) {
|
|
11597
|
+
return err(`None of the center nodes were found in any graph layer: ${centerNodeIds.join(", ")}`);
|
|
11598
|
+
}
|
|
11599
|
+
const inspectData = {};
|
|
11600
|
+
const allAffectedIds = /* @__PURE__ */ new Set();
|
|
11601
|
+
for (const r of blastResults) {
|
|
11602
|
+
allAffectedIds.add(r.center.id);
|
|
11603
|
+
for (const a of r.affected) allAffectedIds.add(a.id);
|
|
11604
|
+
}
|
|
11605
|
+
const allGraphs = readAllGraphs(rootDir);
|
|
11606
|
+
for (const id of allAffectedIds) {
|
|
11607
|
+
for (const [, graph] of Object.entries(allGraphs)) {
|
|
11608
|
+
if (!graph) continue;
|
|
11609
|
+
const node = graph.nodes.find((n) => n.id === id);
|
|
11610
|
+
if (node) {
|
|
11611
|
+
inspectData[id] = {
|
|
11612
|
+
type: node.type,
|
|
11613
|
+
name: node.name,
|
|
11614
|
+
methods: node.methods,
|
|
11615
|
+
path: node.path ?? node.handler,
|
|
11616
|
+
auth: node.auth,
|
|
11617
|
+
db_models: node.db_models
|
|
11618
|
+
};
|
|
11619
|
+
break;
|
|
11620
|
+
}
|
|
11621
|
+
}
|
|
11622
|
+
}
|
|
11623
|
+
const manifest = buildManifest({
|
|
11624
|
+
mode,
|
|
11625
|
+
title,
|
|
11626
|
+
description,
|
|
11627
|
+
subtitle,
|
|
11628
|
+
blastResults,
|
|
11629
|
+
createNodes,
|
|
11630
|
+
inspectData,
|
|
11631
|
+
defaults
|
|
11632
|
+
});
|
|
11633
|
+
const pushToDeck = args.push_to_deck;
|
|
11634
|
+
const session = args.session;
|
|
11635
|
+
let deckResult;
|
|
11636
|
+
if (pushToDeck) {
|
|
11637
|
+
if (!session) return err("push_to_deck requires a session name");
|
|
11638
|
+
const deckLockPath = (0, import_node_path21.join)(rootDir, ".launchsecure", "launch-deck.lock");
|
|
11639
|
+
if (!(0, import_node_fs19.existsSync)(deckLockPath)) {
|
|
11640
|
+
deckResult = { pushed: false, reason: "Deck server not running (no lock file). Push manually via deck tool." };
|
|
11641
|
+
} else {
|
|
11642
|
+
try {
|
|
11643
|
+
const lock = JSON.parse((0, import_node_fs19.readFileSync)(deckLockPath, "utf-8"));
|
|
11644
|
+
const deckUrl = lock.url;
|
|
11645
|
+
const body = JSON.stringify({
|
|
11646
|
+
session,
|
|
11647
|
+
mode: "show",
|
|
11648
|
+
blocks: [{ type: "blast-radius", label: title, manifest }]
|
|
11649
|
+
});
|
|
11650
|
+
(0, import_node_child_process2.execFileSync)("curl", [
|
|
11651
|
+
"-s",
|
|
11652
|
+
"-X",
|
|
11653
|
+
"POST",
|
|
11654
|
+
deckUrl + "/api/deck",
|
|
11655
|
+
"-H",
|
|
11656
|
+
"Content-Type: application/json",
|
|
11657
|
+
"-d",
|
|
11658
|
+
body
|
|
11659
|
+
], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] });
|
|
11660
|
+
deckResult = { pushed: true, session, url: deckUrl };
|
|
11661
|
+
} catch (e) {
|
|
11662
|
+
deckResult = { pushed: false, reason: `Failed to push to deck: ${e}` };
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
}
|
|
11666
|
+
const result = { ...manifest };
|
|
11667
|
+
if (deckResult) result._deck = deckResult;
|
|
11668
|
+
return okJson(result);
|
|
11669
|
+
}
|
|
11204
11670
|
function layerSummary(graph) {
|
|
11205
11671
|
const typeCounts = {};
|
|
11206
11672
|
const moduleCounts = {};
|
|
@@ -11429,12 +11895,12 @@ function handleReadGraph(args) {
|
|
|
11429
11895
|
return okJson(result);
|
|
11430
11896
|
}
|
|
11431
11897
|
function nodeToFilePath(rootDir, layer, nodeId) {
|
|
11432
|
-
if (layer === "ui" || layer === "api") return (0,
|
|
11433
|
-
if (layer === "db") return (0,
|
|
11434
|
-
const withSrc = (0,
|
|
11435
|
-
if ((0,
|
|
11436
|
-
const direct = (0,
|
|
11437
|
-
if ((0,
|
|
11898
|
+
if (layer === "ui" || layer === "api") return (0, import_node_path21.join)(rootDir, "src", nodeId);
|
|
11899
|
+
if (layer === "db") return (0, import_node_path21.join)(rootDir, "prisma", "schema.prisma");
|
|
11900
|
+
const withSrc = (0, import_node_path21.join)(rootDir, "src", nodeId);
|
|
11901
|
+
if ((0, import_node_fs19.existsSync)(withSrc)) return withSrc;
|
|
11902
|
+
const direct = (0, import_node_path21.join)(rootDir, nodeId);
|
|
11903
|
+
if ((0, import_node_fs19.existsSync)(direct)) return direct;
|
|
11438
11904
|
return null;
|
|
11439
11905
|
}
|
|
11440
11906
|
function handleInspectNode(args) {
|
|
@@ -11577,11 +12043,11 @@ function handleGrepNodes(args) {
|
|
|
11577
12043
|
let filesSearched = 0;
|
|
11578
12044
|
let truncated = false;
|
|
11579
12045
|
for (const [filePath, nodeId] of filePaths) {
|
|
11580
|
-
if (!(0,
|
|
12046
|
+
if (!(0, import_node_fs19.existsSync)(filePath)) continue;
|
|
11581
12047
|
filesSearched++;
|
|
11582
12048
|
let content;
|
|
11583
12049
|
try {
|
|
11584
|
-
content = (0,
|
|
12050
|
+
content = (0, import_node_fs19.readFileSync)(filePath, "utf-8");
|
|
11585
12051
|
} catch {
|
|
11586
12052
|
continue;
|
|
11587
12053
|
}
|
|
@@ -11646,11 +12112,11 @@ function handleStartChartServer(args) {
|
|
|
11646
12112
|
});
|
|
11647
12113
|
}
|
|
11648
12114
|
const entryPath = process.argv[1];
|
|
11649
|
-
const logDir = (0,
|
|
11650
|
-
(0,
|
|
11651
|
-
const logPath = (0,
|
|
11652
|
-
const out = (0,
|
|
11653
|
-
const err2 = (0,
|
|
12115
|
+
const logDir = (0, import_node_path21.join)((0, import_node_os2.homedir)(), ".launchsecure");
|
|
12116
|
+
(0, import_node_fs19.mkdirSync)(logDir, { recursive: true });
|
|
12117
|
+
const logPath = (0, import_node_path21.join)(logDir, "launch-chart.log");
|
|
12118
|
+
const out = (0, import_node_fs19.openSync)(logPath, "a");
|
|
12119
|
+
const err2 = (0, import_node_fs19.openSync)(logPath, "a");
|
|
11654
12120
|
const portArgs = args.port ? ["--port", String(args.port)] : [];
|
|
11655
12121
|
const child = (0, import_node_child_process2.spawn)(process.execPath, [entryPath, "serve", ...portArgs], {
|
|
11656
12122
|
detached: true,
|
|
@@ -11770,20 +12236,20 @@ function handleDetectProjectStack() {
|
|
|
11770
12236
|
if (ref.type === "references_api") stats.references_api++;
|
|
11771
12237
|
}
|
|
11772
12238
|
}
|
|
11773
|
-
const srcDir = (0,
|
|
11774
|
-
if ((0,
|
|
12239
|
+
const srcDir = (0, import_node_path21.join)(rootDir, "src");
|
|
12240
|
+
if ((0, import_node_fs19.existsSync)(srcDir)) {
|
|
11775
12241
|
const scanDir = (dir) => {
|
|
11776
|
-
if (!(0,
|
|
11777
|
-
for (const entry of (0,
|
|
12242
|
+
if (!(0, import_node_fs19.existsSync)(dir)) return;
|
|
12243
|
+
for (const entry of (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true })) {
|
|
11778
12244
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
11779
|
-
const full = (0,
|
|
12245
|
+
const full = (0, import_node_path21.join)(dir, entry.name);
|
|
11780
12246
|
if (entry.isDirectory()) {
|
|
11781
12247
|
scanDir(full);
|
|
11782
12248
|
continue;
|
|
11783
12249
|
}
|
|
11784
|
-
if (![".ts", ".tsx"].includes((0,
|
|
12250
|
+
if (![".ts", ".tsx"].includes((0, import_node_path21.extname)(entry.name))) continue;
|
|
11785
12251
|
try {
|
|
11786
|
-
const content = (0,
|
|
12252
|
+
const content = (0, import_node_fs19.readFileSync)(full, "utf-8");
|
|
11787
12253
|
const matches = content.match(/@api\s+(GET|POST|PUT|DELETE|PATCH)\s+\/\S+/g);
|
|
11788
12254
|
if (matches) stats.annotations += matches.length;
|
|
11789
12255
|
} catch {
|
|
@@ -11902,6 +12368,10 @@ async function handleMessage(msg) {
|
|
|
11902
12368
|
respond(id ?? null, handleBlastPoints(args));
|
|
11903
12369
|
return;
|
|
11904
12370
|
}
|
|
12371
|
+
if (toolName === "generate_blast_radius") {
|
|
12372
|
+
respond(id ?? null, handleGenerateBlastRadius(args));
|
|
12373
|
+
return;
|
|
12374
|
+
}
|
|
11905
12375
|
respondError(id ?? null, -32601, `Unknown tool: ${toolName}`);
|
|
11906
12376
|
return;
|
|
11907
12377
|
}
|
|
@@ -11953,7 +12423,8 @@ if (import_fs8.default.existsSync(envPath)) {
|
|
|
11953
12423
|
}
|
|
11954
12424
|
}
|
|
11955
12425
|
var LAUNCHSECURE_URL = process.env.LAUNCHSECURE_URL || "http://localhost:4177";
|
|
11956
|
-
var CONFIG_FILE = "launchpod.
|
|
12426
|
+
var CONFIG_FILE = ".launchpod.json";
|
|
12427
|
+
var CONFIG_FILE_LEGACY = "launchpod.config.json";
|
|
11957
12428
|
var REPO_ROOT = process.cwd();
|
|
11958
12429
|
var PROJECT_DIR = REPO_ROOT;
|
|
11959
12430
|
var CREDENTIALS_DIR = import_path8.default.join(PROJECT_DIR, ".launchpod");
|
|
@@ -11989,8 +12460,10 @@ function parseArgs() {
|
|
|
11989
12460
|
port = parseInt(process.env.PORT, 10);
|
|
11990
12461
|
} else {
|
|
11991
12462
|
const configPath = import_path8.default.join(REPO_ROOT, CONFIG_FILE);
|
|
11992
|
-
|
|
11993
|
-
|
|
12463
|
+
const legacyPath = import_path8.default.join(REPO_ROOT, CONFIG_FILE_LEGACY);
|
|
12464
|
+
const resolvedPath = import_fs8.default.existsSync(configPath) ? configPath : import_fs8.default.existsSync(legacyPath) ? legacyPath : null;
|
|
12465
|
+
if (resolvedPath) {
|
|
12466
|
+
const config = JSON.parse(import_fs8.default.readFileSync(resolvedPath, "utf-8"));
|
|
11994
12467
|
if (config.port) port = config.port;
|
|
11995
12468
|
}
|
|
11996
12469
|
}
|
|
@@ -12219,21 +12692,21 @@ function ensureGitRepo(runId) {
|
|
|
12219
12692
|
console.log("Git repo initialized");
|
|
12220
12693
|
}
|
|
12221
12694
|
const branch = `pipeline/${runId}`;
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
|
|
12226
|
-
|
|
12227
|
-
}
|
|
12228
|
-
} catch {
|
|
12695
|
+
const worktreeDir = import_path8.default.join(REPO_ROOT, ".launchpod", "worktrees", runId);
|
|
12696
|
+
if (import_fs8.default.existsSync(worktreeDir)) {
|
|
12697
|
+
PROJECT_DIR = worktreeDir;
|
|
12698
|
+
console.log(`Resuming in existing worktree ${worktreeDir}`);
|
|
12699
|
+
return;
|
|
12229
12700
|
}
|
|
12701
|
+
import_fs8.default.mkdirSync(import_path8.default.join(REPO_ROOT, ".launchpod", "worktrees"), { recursive: true });
|
|
12230
12702
|
try {
|
|
12231
|
-
(0, import_child_process4.execSync)(`git
|
|
12232
|
-
console.log(`
|
|
12703
|
+
(0, import_child_process4.execSync)(`git worktree add "${worktreeDir}" "${branch}"`, { cwd: REPO_ROOT, stdio: "pipe" });
|
|
12704
|
+
console.log(`Created worktree from existing branch ${branch}`);
|
|
12233
12705
|
} catch {
|
|
12234
|
-
(0, import_child_process4.execSync)(`git
|
|
12235
|
-
console.log(`Created branch ${branch}`);
|
|
12706
|
+
(0, import_child_process4.execSync)(`git worktree add -b "${branch}" "${worktreeDir}"`, { cwd: REPO_ROOT, stdio: "pipe" });
|
|
12707
|
+
console.log(`Created worktree with new branch ${branch}`);
|
|
12236
12708
|
}
|
|
12709
|
+
PROJECT_DIR = worktreeDir;
|
|
12237
12710
|
}
|
|
12238
12711
|
function normalizeGitUrl(url) {
|
|
12239
12712
|
return url.replace(/\.git$/, "").replace(/^git@github\.com:/, "https://github.com/").replace(/\/$/, "").toLowerCase();
|
|
@@ -12336,7 +12809,8 @@ function parseTrackerData(result) {
|
|
|
12336
12809
|
const runStatus = typeof run?.status === "string" ? run.status : void 0;
|
|
12337
12810
|
return { agents, interventions, runStatus };
|
|
12338
12811
|
}
|
|
12339
|
-
async function startPipelineSystem(creds) {
|
|
12812
|
+
async function startPipelineSystem(creds, capabilities) {
|
|
12813
|
+
const activeCapabilities = capabilities ?? { ...DEFAULT_CAPABILITIES };
|
|
12340
12814
|
try {
|
|
12341
12815
|
writeMcpConfigs({
|
|
12342
12816
|
projectDir: PROJECT_DIR,
|
|
@@ -12602,7 +13076,7 @@ if (!__isMcpMode) {
|
|
|
12602
13076
|
});
|
|
12603
13077
|
authenticated = true;
|
|
12604
13078
|
const savedCreds = loadCredentials();
|
|
12605
|
-
if (savedCreds) startPipelineSystem(savedCreds);
|
|
13079
|
+
if (savedCreds) startPipelineSystem(savedCreds, activeCapabilities);
|
|
12606
13080
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
12607
13081
|
res.end(JSON.stringify({ success: true }));
|
|
12608
13082
|
} catch {
|
|
@@ -12820,7 +13294,7 @@ if (!__isMcpMode) {
|
|
|
12820
13294
|
}
|
|
12821
13295
|
serveIndex(res);
|
|
12822
13296
|
});
|
|
12823
|
-
let
|
|
13297
|
+
let activeCapabilities = { ...DEFAULT_CAPABILITIES };
|
|
12824
13298
|
async function main() {
|
|
12825
13299
|
const { token, serverUrl } = parsedArgs;
|
|
12826
13300
|
if (token) {
|
|
@@ -12837,8 +13311,8 @@ if (!__isMcpMode) {
|
|
|
12837
13311
|
process.exit(1);
|
|
12838
13312
|
}
|
|
12839
13313
|
activeRunId = runId;
|
|
12840
|
-
|
|
12841
|
-
console.log(`Capabilities: workspace_setup=${
|
|
13314
|
+
activeCapabilities = await fetchCapabilities(serverUrl, token);
|
|
13315
|
+
console.log(`Capabilities: workspace_setup=${activeCapabilities.workspace_setup}, terminal=${activeCapabilities.terminal}`);
|
|
12842
13316
|
ensureGitRepo(runId);
|
|
12843
13317
|
await validateRepoUrl(serverUrl, token);
|
|
12844
13318
|
const lock = checkLockfile();
|
|
@@ -12866,13 +13340,13 @@ if (!__isMcpMode) {
|
|
|
12866
13340
|
const existingCreds = loadCredentials();
|
|
12867
13341
|
if (existingCreds) {
|
|
12868
13342
|
if (!token) {
|
|
12869
|
-
|
|
13343
|
+
activeCapabilities = await fetchCapabilities(existingCreds.serverUrl, existingCreds.token);
|
|
12870
13344
|
}
|
|
12871
|
-
if (
|
|
13345
|
+
if (activeCapabilities.terminal) {
|
|
12872
13346
|
initTerminalBridge(server, PROJECT_DIR, import_path8.default.join(PROJECT_DIR, ".launchpod", "sessions"));
|
|
12873
13347
|
}
|
|
12874
|
-
startPipelineSystem(existingCreds);
|
|
12875
|
-
} else if (
|
|
13348
|
+
startPipelineSystem(existingCreds, activeCapabilities);
|
|
13349
|
+
} else if (activeCapabilities.terminal) {
|
|
12876
13350
|
initTerminalBridge(server, PROJECT_DIR, import_path8.default.join(PROJECT_DIR, ".launchpod", "sessions"));
|
|
12877
13351
|
}
|
|
12878
13352
|
}
|