chain-insights 0.2.16
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/LICENSE +21 -0
- package/README.md +165 -0
- package/bin/cli.js +10 -0
- package/bin/install.cjs +252 -0
- package/bin/mcp-proxy.cjs +10 -0
- package/dist/active-BSrxLKwn.mjs +50 -0
- package/dist/active-BSrxLKwn.mjs.map +1 -0
- package/dist/active-Dv7Tu-O4.cjs +68 -0
- package/dist/app-BjjuQM0B.mjs +155 -0
- package/dist/app-BjjuQM0B.mjs.map +1 -0
- package/dist/app-Dq1TdB6p.cjs +161 -0
- package/dist/artifact-server-DoxJ7fCx.cjs +47 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs +48 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs.map +1 -0
- package/dist/assets/bg-pattern.png +0 -0
- package/dist/assets/logo.png +0 -0
- package/dist/call-args-DQA2QcRA.cjs +27 -0
- package/dist/call-args-Lk_wOJxd.mjs +29 -0
- package/dist/call-args-Lk_wOJxd.mjs.map +1 -0
- package/dist/capabilities-CB97WMA5.cjs +83 -0
- package/dist/capabilities-DliMBim-.mjs +84 -0
- package/dist/capabilities-DliMBim-.mjs.map +1 -0
- package/dist/cases-By7INiOa.mjs +6 -0
- package/dist/cases-CDcNU91B.cjs +9 -0
- package/dist/chunk-CZWwpsFl.cjs +43 -0
- package/dist/cli.cjs +752 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +753 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-D4Bq0rp9.mjs +111 -0
- package/dist/client-D4Bq0rp9.mjs.map +1 -0
- package/dist/client-D4fZgIaO.cjs +132 -0
- package/dist/config-Bmdl5hdk.cjs +67 -0
- package/dist/config-BwrBYmiC.mjs +44 -0
- package/dist/config-BwrBYmiC.mjs.map +1 -0
- package/dist/data-extractor-BNGj7ECT.cjs +347 -0
- package/dist/data-extractor-DFzsa5CS.mjs +336 -0
- package/dist/data-extractor-DFzsa5CS.mjs.map +1 -0
- package/dist/dossier-BsroDgD3.mjs +76 -0
- package/dist/dossier-BsroDgD3.mjs.map +1 -0
- package/dist/dossier-DtxREpPm.cjs +76 -0
- package/dist/evidence-BGcdKxuV.cjs +200 -0
- package/dist/evidence-BhvFW-y_.mjs +195 -0
- package/dist/evidence-BhvFW-y_.mjs.map +1 -0
- package/dist/format-Ce1RObVl.mjs +22 -0
- package/dist/format-Ce1RObVl.mjs.map +1 -0
- package/dist/format-DOrPvXEr.cjs +20 -0
- package/dist/frontmatter-D8wWCeOa.mjs +26 -0
- package/dist/frontmatter-D8wWCeOa.mjs.map +1 -0
- package/dist/frontmatter-DgAuai7E.cjs +35 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs +130 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs.map +1 -0
- package/dist/graph-normalizer-DeIj6Ses.cjs +133 -0
- package/dist/graph-reports-C4TBjCkM.mjs +63 -0
- package/dist/graph-reports-C4TBjCkM.mjs.map +1 -0
- package/dist/graph-reports-DU05YCei.cjs +64 -0
- package/dist/html-generator-CAv81IWH.cjs +85 -0
- package/dist/html-generator-V6Bp0uRb.mjs +68 -0
- package/dist/html-generator-V6Bp0uRb.mjs.map +1 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +9 -0
- package/dist/init-BjuFt54X.cjs +232 -0
- package/dist/init-CaOsHTIo.mjs +232 -0
- package/dist/init-CaOsHTIo.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +1257 -0
- package/dist/mcp-proxy.d.cts +12 -0
- package/dist/mcp-proxy.d.cts.map +1 -0
- package/dist/mcp-proxy.d.mts +12 -0
- package/dist/mcp-proxy.d.mts.map +1 -0
- package/dist/mcp-proxy.mjs +1255 -0
- package/dist/mcp-proxy.mjs.map +1 -0
- package/dist/output-root-CFYms3ad.cjs +43 -0
- package/dist/output-root-CmWM7aV2.mjs +33 -0
- package/dist/output-root-CmWM7aV2.mjs.map +1 -0
- package/dist/parser-BUIWW1OH.cjs +182 -0
- package/dist/parser-DO0_SssG.mjs +182 -0
- package/dist/parser-DO0_SssG.mjs.map +1 -0
- package/dist/public-tools-D4UI-Zb0.mjs +2554 -0
- package/dist/public-tools-D4UI-Zb0.mjs.map +1 -0
- package/dist/public-tools-XSpkz2ky.cjs +2556 -0
- package/dist/resolver-C2ZS7oC8.mjs +201 -0
- package/dist/resolver-C2ZS7oC8.mjs.map +1 -0
- package/dist/resolver-zYbu4wDV.cjs +203 -0
- package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/dist/runner-1Eq55OYb.cjs +148 -0
- package/dist/runner-BhUHbiHG.mjs +149 -0
- package/dist/runner-BhUHbiHG.mjs.map +1 -0
- package/dist/schema-4XpzDFQM.cjs +55 -0
- package/dist/schema-8d0rVIdZ.mjs +37 -0
- package/dist/schema-8d0rVIdZ.mjs.map +1 -0
- package/dist/schema-cache-9CksD7tX.mjs +34 -0
- package/dist/schema-cache-9CksD7tX.mjs.map +1 -0
- package/dist/schema-cache-CgWRCN2N.cjs +36 -0
- package/dist/selector-CkFcTXzz.cjs +10 -0
- package/dist/selector-xjm6NTHI.mjs +12 -0
- package/dist/selector-xjm6NTHI.mjs.map +1 -0
- package/dist/server-BkM5xrXb.mjs +45 -0
- package/dist/server-BkM5xrXb.mjs.map +1 -0
- package/dist/server-DXowbpfi.cjs +54 -0
- package/dist/session-BpNylyuJ.cjs +115 -0
- package/dist/session-CcTgYxsj.mjs +115 -0
- package/dist/session-CcTgYxsj.mjs.map +1 -0
- package/dist/setup-DOpKPrlx.cjs +81 -0
- package/dist/setup-DyrWHuwQ.mjs +80 -0
- package/dist/setup-DyrWHuwQ.mjs.map +1 -0
- package/dist/store-BiUhQOIf.cjs +230 -0
- package/dist/store-BoWE-Gtl.mjs +225 -0
- package/dist/store-BoWE-Gtl.mjs.map +1 -0
- package/dist/templates/graph.html +1406 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs +28 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs.map +1 -0
- package/dist/tool-visibility-CwgY205r.cjs +36 -0
- package/dist/tools-Cp2jAAAb.mjs +100 -0
- package/dist/tools-Cp2jAAAb.mjs.map +1 -0
- package/dist/tools-f_vJUZAF.cjs +139 -0
- package/dist/topup-server-BZuQifvh.cjs +940 -0
- package/dist/topup-server-DUjyFftI.mjs +919 -0
- package/dist/topup-server-DUjyFftI.mjs.map +1 -0
- package/dist/version-1gP19Lhi.mjs +8 -0
- package/dist/version-1gP19Lhi.mjs.map +1 -0
- package/dist/version-BNGtdpmH.cjs +18 -0
- package/dist/viz-BlCJe6Tk.mjs +35 -0
- package/dist/viz-BlCJe6Tk.mjs.map +1 -0
- package/dist/viz-ClezVXrJ.cjs +44 -0
- package/dist/wallet-BMelXBYP.mjs +104 -0
- package/dist/wallet-BMelXBYP.mjs.map +1 -0
- package/dist/wallet-RnvvSpV2.cjs +146 -0
- package/docs/architecture.md +145 -0
- package/docs/contributing.md +68 -0
- package/docs/debugging.md +68 -0
- package/docs/development.md +44 -0
- package/docs/graph-tools.md +251 -0
- package/docs/images/graph-mcp-iframe.png +0 -0
- package/docs/images/graph-visualization.png +0 -0
- package/docs/images/topup-page.png +0 -0
- package/docs/investigation-workspaces.md +151 -0
- package/docs/mcp-proxy.md +180 -0
- package/package.json +59 -0
- package/skills/chain-insights-developer-experience/SKILL.md +101 -0
- package/skills/chain-insights-investigation/SKILL.md +285 -0
- package/skills/chain-insights-investigation/agents/openai.yaml +4 -0
- package/skills/chain-insights-investigation/scripts/run-target-uat.sh +197 -0
- package/skills/chain-insights-trace-funds/SKILL.md +249 -0
- package/skills/ci-case/SKILL.md +43 -0
- package/skills/ci-status/SKILL.md +45 -0
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +75 -0
- package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +4 -0
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +414 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { n as serializeFrontmatter, t as parseFrontmatter } from "./frontmatter-D8wWCeOa.mjs";
|
|
2
|
+
import { n as workspaceOutputPaths } from "./output-root-CmWM7aV2.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
//#region src/cases/session.ts
|
|
6
|
+
function caseDir(caseId) {
|
|
7
|
+
return path.join(workspaceOutputPaths().casesRoot, caseId);
|
|
8
|
+
}
|
|
9
|
+
const MAX_SESSIONS = 5;
|
|
10
|
+
function sessionNumber(filename) {
|
|
11
|
+
return parseInt(filename.replace("session_", "").replace(".md", ""), 10);
|
|
12
|
+
}
|
|
13
|
+
function sessionFromFrontmatter(frontmatter) {
|
|
14
|
+
return {
|
|
15
|
+
sessionId: frontmatter["sessionId"] ?? "",
|
|
16
|
+
caseId: frontmatter["caseId"] ?? "",
|
|
17
|
+
startTime: frontmatter["startTime"] ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
18
|
+
endTime: frontmatter["endTime"] || void 0,
|
|
19
|
+
status: frontmatter["status"] === "ended" ? "ended" : "active"
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function listSessionFiles(dir) {
|
|
23
|
+
return (await readdir(dir)).filter((f) => f.match(/^session_\d+\.md$/)).sort((a, b) => sessionNumber(b) - sessionNumber(a));
|
|
24
|
+
}
|
|
25
|
+
const SessionStore = {
|
|
26
|
+
async start(caseId, input = {}) {
|
|
27
|
+
const dir = caseDir(caseId);
|
|
28
|
+
let sessionFiles = [];
|
|
29
|
+
try {
|
|
30
|
+
sessionFiles = await listSessionFiles(dir);
|
|
31
|
+
} catch {
|
|
32
|
+
sessionFiles = [];
|
|
33
|
+
}
|
|
34
|
+
for (const filename of sessionFiles) {
|
|
35
|
+
const { frontmatter } = parseFrontmatter(await readFile(path.join(dir, filename), "utf8"));
|
|
36
|
+
if (frontmatter["status"] !== "ended") return sessionFromFrontmatter(frontmatter);
|
|
37
|
+
}
|
|
38
|
+
const seq = sessionFiles.length + 1;
|
|
39
|
+
const seqStr = String(seq).padStart(3, "0");
|
|
40
|
+
const filename = `session_${seqStr}.md`;
|
|
41
|
+
const sessionId = `${caseId}_s${seqStr}`;
|
|
42
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
43
|
+
const title = input.title?.trim();
|
|
44
|
+
const fm = {
|
|
45
|
+
sessionId,
|
|
46
|
+
caseId,
|
|
47
|
+
startTime: now,
|
|
48
|
+
endTime: "",
|
|
49
|
+
status: "active"
|
|
50
|
+
};
|
|
51
|
+
if (title) fm["title"] = title;
|
|
52
|
+
const body = `# Session ${seq}: ${title || now.slice(0, 10)}\n\n## Investigation Log\n\n## Key Findings\n\n## Next Steps\n\n`;
|
|
53
|
+
await writeFile(path.join(dir, filename), serializeFrontmatter(fm, body), { mode: 384 });
|
|
54
|
+
return {
|
|
55
|
+
sessionId,
|
|
56
|
+
caseId,
|
|
57
|
+
startTime: now,
|
|
58
|
+
status: "active"
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
async end(caseId, input) {
|
|
62
|
+
const dir = caseDir(caseId);
|
|
63
|
+
const sessionFiles = await listSessionFiles(dir);
|
|
64
|
+
if (sessionFiles.length === 0) throw new Error(`No active session for case ${caseId}`);
|
|
65
|
+
let activeFile = null;
|
|
66
|
+
let activeFrontmatter = null;
|
|
67
|
+
let activeBody = "";
|
|
68
|
+
for (const filename of sessionFiles) {
|
|
69
|
+
const { frontmatter, body } = parseFrontmatter(await readFile(path.join(dir, filename), "utf8"));
|
|
70
|
+
if (frontmatter["status"] !== "ended") {
|
|
71
|
+
activeFile = filename;
|
|
72
|
+
activeFrontmatter = frontmatter;
|
|
73
|
+
activeBody = body;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!activeFile || !activeFrontmatter) throw new Error(`No active session for case ${caseId}`);
|
|
78
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
79
|
+
activeFrontmatter["endTime"] = now;
|
|
80
|
+
activeFrontmatter["status"] = "ended";
|
|
81
|
+
const updatedBody = activeBody.replace("## Key Findings\n", `## Key Findings\n\n${input.findings}\n`).replace("## Next Steps\n", `## Next Steps\n\n${input.nextSteps}\n`);
|
|
82
|
+
await writeFile(path.join(dir, activeFile), serializeFrontmatter(activeFrontmatter, updatedBody), { mode: 384 });
|
|
83
|
+
},
|
|
84
|
+
async getLatest(caseId) {
|
|
85
|
+
const dir = caseDir(caseId);
|
|
86
|
+
try {
|
|
87
|
+
const sessionFiles = await listSessionFiles(dir);
|
|
88
|
+
if (sessionFiles.length === 0) return null;
|
|
89
|
+
return parseFrontmatter(await readFile(path.join(dir, sessionFiles[0]), "utf8"));
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
async archiveOldSessions(caseId) {
|
|
95
|
+
const dir = caseDir(caseId);
|
|
96
|
+
const sessionFiles = (await listSessionFiles(dir)).reverse();
|
|
97
|
+
if (sessionFiles.length <= MAX_SESSIONS) return;
|
|
98
|
+
const toArchive = sessionFiles.slice(0, sessionFiles.length - MAX_SESSIONS);
|
|
99
|
+
const historyPath = path.join(dir, "history.md");
|
|
100
|
+
const existingHistory = await readFile(historyPath, "utf8").catch(() => "");
|
|
101
|
+
const summaries = [];
|
|
102
|
+
for (const filename of toArchive) {
|
|
103
|
+
const { frontmatter, body } = parseFrontmatter(await readFile(path.join(dir, filename), "utf8"));
|
|
104
|
+
const findingsMatch = body.match(/## Key Findings\n+([\s\S]*?)(?:\n## |$)/);
|
|
105
|
+
const findings = findingsMatch ? findingsMatch[1].trim() : "(no findings recorded)";
|
|
106
|
+
summaries.push(`### ${frontmatter["sessionId"] ?? filename} (${frontmatter["startTime"] ?? ""})\n\n${findings}\n`);
|
|
107
|
+
}
|
|
108
|
+
await writeFile(historyPath, existingHistory + "\n" + summaries.join("\n") + "\n", { mode: 384 });
|
|
109
|
+
for (const filename of toArchive) await rm(path.join(dir, filename));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
//#endregion
|
|
113
|
+
export { SessionStore };
|
|
114
|
+
|
|
115
|
+
//# sourceMappingURL=session-CcTgYxsj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-CcTgYxsj.mjs","names":[],"sources":["../src/cases/session.ts"],"sourcesContent":["import { readFile, writeFile, readdir, rm } from 'node:fs/promises'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { parseFrontmatter, serializeFrontmatter } from './frontmatter.js'\nimport { type Session } from './schema.js'\n\nfunction caseDir(caseId: string): string {\n return path.join(workspaceOutputPaths().casesRoot, caseId)\n}\n\nconst MAX_SESSIONS = 5\n\nfunction sessionNumber(filename: string): number {\n return parseInt(filename.replace('session_', '').replace('.md', ''), 10)\n}\n\nfunction sessionFromFrontmatter(frontmatter: Record<string, string>): Session {\n return {\n sessionId: frontmatter['sessionId'] ?? '',\n caseId: frontmatter['caseId'] ?? '',\n startTime: frontmatter['startTime'] ?? new Date().toISOString(),\n endTime: frontmatter['endTime'] || undefined,\n status: frontmatter['status'] === 'ended' ? 'ended' : 'active',\n }\n}\n\nasync function listSessionFiles(dir: string): Promise<string[]> {\n const files = await readdir(dir)\n return files\n .filter(f => f.match(/^session_\\d+\\.md$/))\n .sort((a, b) => sessionNumber(b) - sessionNumber(a))\n}\n\nexport const SessionStore = {\n async start(caseId: string, input: { title?: string } = {}): Promise<Session> {\n const dir = caseDir(caseId)\n let sessionFiles: string[] = []\n try {\n sessionFiles = await listSessionFiles(dir)\n } catch {\n sessionFiles = []\n }\n\n for (const filename of sessionFiles) {\n const raw = await readFile(path.join(dir, filename), 'utf8')\n const { frontmatter } = parseFrontmatter(raw)\n if (frontmatter['status'] !== 'ended') {\n return sessionFromFrontmatter(frontmatter)\n }\n }\n\n const seq = sessionFiles.length + 1\n const seqStr = String(seq).padStart(3, '0')\n const filename = `session_${seqStr}.md`\n const sessionId = `${caseId}_s${seqStr}`\n const now = new Date().toISOString()\n const title = input.title?.trim()\n\n const fm: Record<string, string> = {\n sessionId,\n caseId,\n startTime: now,\n endTime: '',\n status: 'active',\n }\n if (title) fm['title'] = title\n const heading = title || now.slice(0, 10)\n const body = `# Session ${seq}: ${heading}\\n\\n## Investigation Log\\n\\n## Key Findings\\n\\n## Next Steps\\n\\n`\n await writeFile(path.join(dir, filename), serializeFrontmatter(fm, body), { mode: 0o600 })\n\n return { sessionId, caseId, startTime: now, status: 'active' }\n },\n\n async end(\n caseId: string,\n input: { findings: string; nextSteps: string }\n ): Promise<void> {\n const dir = caseDir(caseId)\n const sessionFiles = await listSessionFiles(dir)\n if (sessionFiles.length === 0) throw new Error(`No active session for case ${caseId}`)\n let activeFile: string | null = null\n let activeFrontmatter: Record<string, string> | null = null\n let activeBody = ''\n for (const filename of sessionFiles) {\n const raw = await readFile(path.join(dir, filename), 'utf8')\n const { frontmatter, body } = parseFrontmatter(raw)\n if (frontmatter['status'] !== 'ended') {\n activeFile = filename\n activeFrontmatter = frontmatter\n activeBody = body\n break\n }\n }\n if (!activeFile || !activeFrontmatter) throw new Error(`No active session for case ${caseId}`)\n const now = new Date().toISOString()\n activeFrontmatter['endTime'] = now\n activeFrontmatter['status'] = 'ended'\n\n // Append findings and next steps to body\n const updatedBody = activeBody\n .replace('## Key Findings\\n', `## Key Findings\\n\\n${input.findings}\\n`)\n .replace('## Next Steps\\n', `## Next Steps\\n\\n${input.nextSteps}\\n`)\n\n await writeFile(path.join(dir, activeFile), serializeFrontmatter(activeFrontmatter, updatedBody), { mode: 0o600 })\n },\n\n async getLatest(caseId: string): Promise<{ frontmatter: Record<string, string>; body: string } | null> {\n const dir = caseDir(caseId)\n try {\n const sessionFiles = await listSessionFiles(dir)\n if (sessionFiles.length === 0) return null\n const raw = await readFile(path.join(dir, sessionFiles[0]!), 'utf8')\n return parseFrontmatter(raw)\n } catch {\n return null\n }\n },\n\n async archiveOldSessions(caseId: string): Promise<void> {\n const dir = caseDir(caseId)\n const sessionFiles = (await listSessionFiles(dir)).reverse()\n\n if (sessionFiles.length <= MAX_SESSIONS) return\n\n // Files to archive = everything beyond the 5 most recent (which are at the end)\n const toArchive = sessionFiles.slice(0, sessionFiles.length - MAX_SESSIONS)\n const historyPath = path.join(dir, 'history.md')\n\n // Pitfall 5 mitigation: ENOENT-safe read of history.md\n const existingHistory = await readFile(historyPath, 'utf8').catch(() => '')\n\n const summaries: string[] = []\n for (const filename of toArchive) {\n const raw = await readFile(path.join(dir, filename), 'utf8')\n const { frontmatter, body } = parseFrontmatter(raw)\n // Extract a one-paragraph summary: sessionId + key findings section\n const findingsMatch = body.match(/## Key Findings\\n+([\\s\\S]*?)(?:\\n## |$)/)\n const findings = findingsMatch ? findingsMatch[1]!.trim() : '(no findings recorded)'\n summaries.push(`### ${frontmatter['sessionId'] ?? filename} (${frontmatter['startTime'] ?? ''})\\n\\n${findings}\\n`)\n }\n\n const newHistory = existingHistory + '\\n' + summaries.join('\\n') + '\\n'\n await writeFile(historyPath, newHistory, { mode: 0o600 })\n\n // Delete archived session files\n for (const filename of toArchive) {\n await rm(path.join(dir, filename))\n }\n },\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const require_chunk = require("./chunk-CZWwpsFl.cjs");
|
|
2
|
+
let node_url = require("node:url");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
node_path = require_chunk.__toESM(node_path, 1);
|
|
5
|
+
let node_fs = require("node:fs");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
let node_os = require("node:os");
|
|
8
|
+
node_os = require_chunk.__toESM(node_os, 1);
|
|
9
|
+
//#region src/claude-desktop/setup.ts
|
|
10
|
+
function defaultClaudeDesktopConfigPath() {
|
|
11
|
+
if (process.platform === "darwin") return node_path.default.join(node_os.default.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
12
|
+
if (process.platform === "win32") {
|
|
13
|
+
const appData = process.env["APPDATA"] ?? node_path.default.join(node_os.default.homedir(), "AppData", "Roaming");
|
|
14
|
+
return node_path.default.join(appData, "Claude", "claude_desktop_config.json");
|
|
15
|
+
}
|
|
16
|
+
return node_path.default.join(node_os.default.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
17
|
+
}
|
|
18
|
+
function defaultProxyCommand() {
|
|
19
|
+
const currentDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
20
|
+
const packageRoot = [node_path.default.resolve(currentDir, ".."), node_path.default.resolve(currentDir, "..", "..")].find((candidate) => (0, node_fs.existsSync)(node_path.default.join(candidate, "bin", "mcp-proxy.cjs")));
|
|
21
|
+
if (!packageRoot) throw new Error(`Could not locate Chain Insights package root from ${currentDir}`);
|
|
22
|
+
return {
|
|
23
|
+
command: process.execPath,
|
|
24
|
+
args: [node_path.default.join(packageRoot, "bin", "mcp-proxy.cjs")]
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async function fileExists(filePath) {
|
|
28
|
+
try {
|
|
29
|
+
await (0, node_fs_promises.access)(filePath, node_fs.constants.F_OK);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function parseClaudeConfig(raw, filePath) {
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Claude Desktop config must be a JSON object: ${filePath}`);
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
function backupPathFor(filePath) {
|
|
41
|
+
return `${filePath}.bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}`;
|
|
42
|
+
}
|
|
43
|
+
async function setupClaudeDesktop(options = {}) {
|
|
44
|
+
const configPath = options.configPath ?? defaultClaudeDesktopConfigPath();
|
|
45
|
+
const { command, args } = defaultProxyCommand();
|
|
46
|
+
const exists = await fileExists(configPath);
|
|
47
|
+
const current = exists ? parseClaudeConfig(await (0, node_fs_promises.readFile)(configPath, "utf8"), configPath) : {};
|
|
48
|
+
const next = {
|
|
49
|
+
...current,
|
|
50
|
+
mcpServers: {
|
|
51
|
+
...current.mcpServers ?? {},
|
|
52
|
+
"chain-insights": {
|
|
53
|
+
command,
|
|
54
|
+
args
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const currentText = exists ? JSON.stringify(current, null, 2) + "\n" : "";
|
|
59
|
+
const nextText = JSON.stringify(next, null, 2) + "\n";
|
|
60
|
+
const changed = currentText !== nextText;
|
|
61
|
+
const dryRun = options.dryRun ?? false;
|
|
62
|
+
let backupPath;
|
|
63
|
+
if (!dryRun && changed) {
|
|
64
|
+
await (0, node_fs_promises.mkdir)(node_path.default.dirname(configPath), { recursive: true });
|
|
65
|
+
if (exists) {
|
|
66
|
+
backupPath = backupPathFor(configPath);
|
|
67
|
+
await (0, node_fs_promises.copyFile)(configPath, backupPath);
|
|
68
|
+
}
|
|
69
|
+
await (0, node_fs_promises.writeFile)(configPath, nextText, { mode: 384 });
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
configPath,
|
|
73
|
+
command,
|
|
74
|
+
args,
|
|
75
|
+
backupPath,
|
|
76
|
+
changed,
|
|
77
|
+
dryRun
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
exports.setupClaudeDesktop = setupClaudeDesktop;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { constants, existsSync } from "node:fs";
|
|
4
|
+
import { access, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
//#region src/claude-desktop/setup.ts
|
|
7
|
+
function defaultClaudeDesktopConfigPath() {
|
|
8
|
+
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
9
|
+
if (process.platform === "win32") {
|
|
10
|
+
const appData = process.env["APPDATA"] ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
11
|
+
return path.join(appData, "Claude", "claude_desktop_config.json");
|
|
12
|
+
}
|
|
13
|
+
return path.join(os.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
14
|
+
}
|
|
15
|
+
function defaultProxyCommand() {
|
|
16
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const packageRoot = [path.resolve(currentDir, ".."), path.resolve(currentDir, "..", "..")].find((candidate) => existsSync(path.join(candidate, "bin", "mcp-proxy.cjs")));
|
|
18
|
+
if (!packageRoot) throw new Error(`Could not locate Chain Insights package root from ${currentDir}`);
|
|
19
|
+
return {
|
|
20
|
+
command: process.execPath,
|
|
21
|
+
args: [path.join(packageRoot, "bin", "mcp-proxy.cjs")]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function fileExists(filePath) {
|
|
25
|
+
try {
|
|
26
|
+
await access(filePath, constants.F_OK);
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseClaudeConfig(raw, filePath) {
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Claude Desktop config must be a JSON object: ${filePath}`);
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
function backupPathFor(filePath) {
|
|
38
|
+
return `${filePath}.bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}`;
|
|
39
|
+
}
|
|
40
|
+
async function setupClaudeDesktop(options = {}) {
|
|
41
|
+
const configPath = options.configPath ?? defaultClaudeDesktopConfigPath();
|
|
42
|
+
const { command, args } = defaultProxyCommand();
|
|
43
|
+
const exists = await fileExists(configPath);
|
|
44
|
+
const current = exists ? parseClaudeConfig(await readFile(configPath, "utf8"), configPath) : {};
|
|
45
|
+
const next = {
|
|
46
|
+
...current,
|
|
47
|
+
mcpServers: {
|
|
48
|
+
...current.mcpServers ?? {},
|
|
49
|
+
"chain-insights": {
|
|
50
|
+
command,
|
|
51
|
+
args
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const currentText = exists ? JSON.stringify(current, null, 2) + "\n" : "";
|
|
56
|
+
const nextText = JSON.stringify(next, null, 2) + "\n";
|
|
57
|
+
const changed = currentText !== nextText;
|
|
58
|
+
const dryRun = options.dryRun ?? false;
|
|
59
|
+
let backupPath;
|
|
60
|
+
if (!dryRun && changed) {
|
|
61
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
62
|
+
if (exists) {
|
|
63
|
+
backupPath = backupPathFor(configPath);
|
|
64
|
+
await copyFile(configPath, backupPath);
|
|
65
|
+
}
|
|
66
|
+
await writeFile(configPath, nextText, { mode: 384 });
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
configPath,
|
|
70
|
+
command,
|
|
71
|
+
args,
|
|
72
|
+
backupPath,
|
|
73
|
+
changed,
|
|
74
|
+
dryRun
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { setupClaudeDesktop };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=setup-DyrWHuwQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-DyrWHuwQ.mjs","names":[],"sources":["../src/claude-desktop/setup.ts"],"sourcesContent":["import { access, copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { constants, existsSync } from 'node:fs'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { fileURLToPath } from 'node:url'\n\nexport type ClaudeDesktopSetupOptions = {\n configPath?: string\n dryRun?: boolean\n}\n\nexport type ClaudeDesktopSetupResult = {\n configPath: string\n command: string\n args: string[]\n backupPath?: string\n changed: boolean\n dryRun: boolean\n}\n\ntype ClaudeDesktopConfig = {\n mcpServers?: Record<string, unknown>\n [key: string]: unknown\n}\n\nexport function defaultClaudeDesktopConfigPath(): string {\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\n }\n\n if (process.platform === 'win32') {\n const appData = process.env['APPDATA'] ?? path.join(os.homedir(), 'AppData', 'Roaming')\n return path.join(appData, 'Claude', 'claude_desktop_config.json')\n }\n\n return path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json')\n}\n\nexport function defaultProxyCommand(): { command: string; args: string[] } {\n const currentDir = path.dirname(fileURLToPath(import.meta.url))\n const packageRoot = [\n path.resolve(currentDir, '..'),\n path.resolve(currentDir, '..', '..'),\n ].find((candidate) => existsSync(path.join(candidate, 'bin', 'mcp-proxy.cjs')))\n\n if (!packageRoot) {\n throw new Error(`Could not locate Chain Insights package root from ${currentDir}`)\n }\n\n return {\n command: process.execPath,\n args: [path.join(packageRoot, 'bin', 'mcp-proxy.cjs')],\n }\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath, constants.F_OK)\n return true\n } catch {\n return false\n }\n}\n\nfunction parseClaudeConfig(raw: string, filePath: string): ClaudeDesktopConfig {\n const parsed = JSON.parse(raw) as unknown\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`Claude Desktop config must be a JSON object: ${filePath}`)\n }\n return parsed as ClaudeDesktopConfig\n}\n\nfunction backupPathFor(filePath: string): string {\n const stamp = new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}Z$/, 'Z')\n return `${filePath}.bak-${stamp}`\n}\n\nexport async function setupClaudeDesktop(\n options: ClaudeDesktopSetupOptions = {},\n): Promise<ClaudeDesktopSetupResult> {\n const configPath = options.configPath ?? defaultClaudeDesktopConfigPath()\n const { command, args } = defaultProxyCommand()\n const exists = await fileExists(configPath)\n const current = exists ? parseClaudeConfig(await readFile(configPath, 'utf8'), configPath) : {}\n const next: ClaudeDesktopConfig = {\n ...current,\n mcpServers: {\n ...(current.mcpServers ?? {}),\n 'chain-insights': {\n command,\n args,\n },\n },\n }\n const currentText = exists ? JSON.stringify(current, null, 2) + '\\n' : ''\n const nextText = JSON.stringify(next, null, 2) + '\\n'\n const changed = currentText !== nextText\n const dryRun = options.dryRun ?? false\n let backupPath: string | undefined\n\n if (!dryRun && changed) {\n await mkdir(path.dirname(configPath), { recursive: true })\n if (exists) {\n backupPath = backupPathFor(configPath)\n await copyFile(configPath, backupPath)\n }\n await writeFile(configPath, nextText, { mode: 0o600 })\n }\n\n return {\n configPath,\n command,\n args,\n backupPath,\n changed,\n dryRun,\n }\n}\n"],"mappings":";;;;;;AAyBA,SAAgB,iCAAyC;AACvD,KAAI,QAAQ,aAAa,SACvB,QAAO,KAAK,KAAK,GAAG,SAAS,EAAE,WAAW,uBAAuB,UAAU,6BAA6B;AAG1G,KAAI,QAAQ,aAAa,SAAS;EAChC,MAAM,UAAU,QAAQ,IAAI,cAAc,KAAK,KAAK,GAAG,SAAS,EAAE,WAAW,UAAU;AACvF,SAAO,KAAK,KAAK,SAAS,UAAU,6BAA6B;;AAGnE,QAAO,KAAK,KAAK,GAAG,SAAS,EAAE,WAAW,UAAU,6BAA6B;;AAGnF,SAAgB,sBAA2D;CACzE,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CAC/D,MAAM,cAAc,CAClB,KAAK,QAAQ,YAAY,KAAK,EAC9B,KAAK,QAAQ,YAAY,MAAM,KAAK,CACrC,CAAC,MAAM,cAAc,WAAW,KAAK,KAAK,WAAW,OAAO,gBAAgB,CAAC,CAAC;AAE/E,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,qDAAqD,aAAa;AAGpF,QAAO;EACL,SAAS,QAAQ;EACjB,MAAM,CAAC,KAAK,KAAK,aAAa,OAAO,gBAAgB,CAAC;EACvD;;AAGH,eAAe,WAAW,UAAoC;AAC5D,KAAI;AACF,QAAM,OAAO,UAAU,UAAU,KAAK;AACtC,SAAO;SACD;AACN,SAAO;;;AAIX,SAAS,kBAAkB,KAAa,UAAuC;CAC7E,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,KAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAChE,OAAM,IAAI,MAAM,gDAAgD,WAAW;AAE7E,QAAO;;AAGT,SAAS,cAAc,UAA0B;AAE/C,QAAO,GAAG,SAAS,wBADL,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,GAAG,CAAC,QAAQ,aAAa,IAClD;;AAGjC,eAAsB,mBACpB,UAAqC,EAAE,EACJ;CACnC,MAAM,aAAa,QAAQ,cAAc,gCAAgC;CACzE,MAAM,EAAE,SAAS,SAAS,qBAAqB;CAC/C,MAAM,SAAS,MAAM,WAAW,WAAW;CAC3C,MAAM,UAAU,SAAS,kBAAkB,MAAM,SAAS,YAAY,OAAO,EAAE,WAAW,GAAG,EAAE;CAC/F,MAAM,OAA4B;EAChC,GAAG;EACH,YAAY;GACV,GAAI,QAAQ,cAAc,EAAE;GAC5B,kBAAkB;IAChB;IACA;IACD;GACF;EACF;CACD,MAAM,cAAc,SAAS,KAAK,UAAU,SAAS,MAAM,EAAE,GAAG,OAAO;CACvE,MAAM,WAAW,KAAK,UAAU,MAAM,MAAM,EAAE,GAAG;CACjD,MAAM,UAAU,gBAAgB;CAChC,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI;AAEJ,KAAI,CAAC,UAAU,SAAS;AACtB,QAAM,MAAM,KAAK,QAAQ,WAAW,EAAE,EAAE,WAAW,MAAM,CAAC;AAC1D,MAAI,QAAQ;AACV,gBAAa,cAAc,WAAW;AACtC,SAAM,SAAS,YAAY,WAAW;;AAExC,QAAM,UAAU,YAAY,UAAU,EAAE,MAAM,KAAO,CAAC;;AAGxD,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACD"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
const require_chunk = require("./chunk-CZWwpsFl.cjs");
|
|
2
|
+
const require_frontmatter = require("./frontmatter-DgAuai7E.cjs");
|
|
3
|
+
const require_output_root = require("./output-root-CFYms3ad.cjs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
node_path = require_chunk.__toESM(node_path, 1);
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
let zod = require("zod");
|
|
8
|
+
zod = require_chunk.__toESM(zod, 1);
|
|
9
|
+
//#region src/cases/schema.ts
|
|
10
|
+
const caseIdRegex = /^\d{8}_\d{3}_[a-z0-9][a-z0-9-]*$/;
|
|
11
|
+
const CaseStatusEnum = zod.enum([
|
|
12
|
+
"open",
|
|
13
|
+
"active",
|
|
14
|
+
"suspended",
|
|
15
|
+
"closed"
|
|
16
|
+
]);
|
|
17
|
+
const CaseSchema = zod.object({
|
|
18
|
+
id: zod.string().regex(caseIdRegex, "Invalid case ID format"),
|
|
19
|
+
name: zod.string().min(1).max(200),
|
|
20
|
+
status: CaseStatusEnum.default("open"),
|
|
21
|
+
created: zod.string().datetime(),
|
|
22
|
+
updated: zod.string().datetime(),
|
|
23
|
+
tags: zod.array(zod.string()).default([]),
|
|
24
|
+
description: zod.string().default(""),
|
|
25
|
+
slug: zod.string().optional()
|
|
26
|
+
});
|
|
27
|
+
zod.object({
|
|
28
|
+
id: zod.string().min(1),
|
|
29
|
+
caseId: zod.string().regex(caseIdRegex),
|
|
30
|
+
source: zod.string().min(1),
|
|
31
|
+
timestamp: zod.string().datetime(),
|
|
32
|
+
queryParams: zod.string().default("")
|
|
33
|
+
});
|
|
34
|
+
zod.object({
|
|
35
|
+
address: zod.string().min(1).max(100),
|
|
36
|
+
type: zod.enum([
|
|
37
|
+
"eoa",
|
|
38
|
+
"contract",
|
|
39
|
+
"exchange",
|
|
40
|
+
"mixer",
|
|
41
|
+
"unknown"
|
|
42
|
+
]).default("unknown"),
|
|
43
|
+
firstSeen: zod.string().datetime(),
|
|
44
|
+
lastSeen: zod.string().datetime(),
|
|
45
|
+
riskTags: zod.string().default("")
|
|
46
|
+
});
|
|
47
|
+
zod.object({
|
|
48
|
+
sessionId: zod.string().min(1),
|
|
49
|
+
caseId: zod.string().regex(caseIdRegex),
|
|
50
|
+
startTime: zod.string().datetime(),
|
|
51
|
+
endTime: zod.string().optional(),
|
|
52
|
+
status: zod.enum(["active", "ended"]).default("active")
|
|
53
|
+
});
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/cases/store.ts
|
|
56
|
+
const casesRoot = () => require_output_root.workspaceOutputPaths().casesRoot;
|
|
57
|
+
function caseDir(id) {
|
|
58
|
+
return node_path.default.join(casesRoot(), id);
|
|
59
|
+
}
|
|
60
|
+
function generateCaseId(name, existingIds) {
|
|
61
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
|
|
62
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 40);
|
|
63
|
+
const todayNums = existingIds.filter((id) => id.startsWith(date + "_")).map((id) => parseInt(id.split("_")[1] ?? "0", 10)).filter((n) => !isNaN(n));
|
|
64
|
+
const next = todayNums.length > 0 ? Math.max(...todayNums) + 1 : 1;
|
|
65
|
+
return `${date}_${String(next).padStart(3, "0")}_${slug}`;
|
|
66
|
+
}
|
|
67
|
+
const CaseStore = {
|
|
68
|
+
async create(input) {
|
|
69
|
+
const root = casesRoot();
|
|
70
|
+
await (0, node_fs_promises.mkdir)(root, { recursive: true });
|
|
71
|
+
const existingIds = await (0, node_fs_promises.readdir)(root).catch(() => []);
|
|
72
|
+
const id = generateCaseId(input.name, existingIds);
|
|
73
|
+
const slug = id.split("_").slice(2).join("_");
|
|
74
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
75
|
+
const tags = input.tags;
|
|
76
|
+
const dir = caseDir(id);
|
|
77
|
+
await (0, node_fs_promises.mkdir)(node_path.default.join(dir, "evidence"), { recursive: true });
|
|
78
|
+
await (0, node_fs_promises.mkdir)(node_path.default.join(dir, "dossiers"), { recursive: true });
|
|
79
|
+
const fm = {
|
|
80
|
+
id,
|
|
81
|
+
name: input.name,
|
|
82
|
+
status: "open",
|
|
83
|
+
created: now,
|
|
84
|
+
updated: now,
|
|
85
|
+
tags: tags.join(","),
|
|
86
|
+
description: input.description,
|
|
87
|
+
slug
|
|
88
|
+
};
|
|
89
|
+
const body = [
|
|
90
|
+
`# ${input.name}`,
|
|
91
|
+
"",
|
|
92
|
+
`Opened: ${now}`,
|
|
93
|
+
`Status: open`,
|
|
94
|
+
"",
|
|
95
|
+
"## Question",
|
|
96
|
+
"",
|
|
97
|
+
input.description || "TBD",
|
|
98
|
+
"",
|
|
99
|
+
"## Current Assessment",
|
|
100
|
+
"",
|
|
101
|
+
"TBD",
|
|
102
|
+
"",
|
|
103
|
+
"## Top Findings",
|
|
104
|
+
"",
|
|
105
|
+
"| Finding | Confidence | Evidence |",
|
|
106
|
+
"|---|---:|---|",
|
|
107
|
+
"",
|
|
108
|
+
"## Next Actions",
|
|
109
|
+
"",
|
|
110
|
+
"- TBD",
|
|
111
|
+
"",
|
|
112
|
+
"## Reports",
|
|
113
|
+
""
|
|
114
|
+
].join("\n");
|
|
115
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(dir, "case.md"), require_frontmatter.serializeFrontmatter(fm, body), { mode: 384 });
|
|
116
|
+
const manifest = JSON.stringify({
|
|
117
|
+
caseId: id,
|
|
118
|
+
entries: []
|
|
119
|
+
}, null, 2) + "\n";
|
|
120
|
+
await (0, node_fs_promises.writeFile)(node_path.default.join(dir, "manifest.json"), manifest, { mode: 384 });
|
|
121
|
+
return CaseSchema.parse({
|
|
122
|
+
id,
|
|
123
|
+
name: input.name,
|
|
124
|
+
status: "open",
|
|
125
|
+
created: now,
|
|
126
|
+
updated: now,
|
|
127
|
+
tags,
|
|
128
|
+
description: input.description,
|
|
129
|
+
slug
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
async setStatus(id, status) {
|
|
133
|
+
const dir = caseDir(id);
|
|
134
|
+
const filePath = node_path.default.join(dir, "case.md");
|
|
135
|
+
const { frontmatter, body } = require_frontmatter.parseFrontmatter(await (0, node_fs_promises.readFile)(filePath, "utf8"));
|
|
136
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
137
|
+
frontmatter["status"] = status;
|
|
138
|
+
frontmatter["updated"] = now;
|
|
139
|
+
await (0, node_fs_promises.writeFile)(filePath, require_frontmatter.serializeFrontmatter(frontmatter, body), { mode: 384 });
|
|
140
|
+
const tags = (frontmatter["tags"] ?? "").split(",").filter(Boolean);
|
|
141
|
+
return CaseSchema.parse({
|
|
142
|
+
id,
|
|
143
|
+
name: frontmatter["name"] ?? "",
|
|
144
|
+
status,
|
|
145
|
+
created: frontmatter["created"] ?? now,
|
|
146
|
+
updated: now,
|
|
147
|
+
tags,
|
|
148
|
+
description: frontmatter["description"] ?? ""
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
async list() {
|
|
152
|
+
const root = casesRoot();
|
|
153
|
+
try {
|
|
154
|
+
const ids = await (0, node_fs_promises.readdir)(root);
|
|
155
|
+
const cases = [];
|
|
156
|
+
for (const id of ids) try {
|
|
157
|
+
const { frontmatter } = require_frontmatter.parseFrontmatter(await (0, node_fs_promises.readFile)(node_path.default.join(caseDir(id), "case.md"), "utf8"));
|
|
158
|
+
cases.push({
|
|
159
|
+
id,
|
|
160
|
+
name: frontmatter["name"] ?? id,
|
|
161
|
+
status: frontmatter["status"] ?? "open",
|
|
162
|
+
created: frontmatter["created"] ?? ""
|
|
163
|
+
});
|
|
164
|
+
} catch (err) {
|
|
165
|
+
const nodeErr = err;
|
|
166
|
+
if (nodeErr.code !== "ENOENT" && nodeErr.code !== "ENOTDIR") throw err;
|
|
167
|
+
}
|
|
168
|
+
return cases.sort((a, b) => b.created.localeCompare(a.created) || b.id.localeCompare(a.id)).map(({ id, name, status }) => ({
|
|
169
|
+
id,
|
|
170
|
+
name,
|
|
171
|
+
status
|
|
172
|
+
}));
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err.code === "ENOENT") return [];
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
async get(id) {
|
|
179
|
+
const dir = caseDir(id);
|
|
180
|
+
const { frontmatter } = require_frontmatter.parseFrontmatter(await (0, node_fs_promises.readFile)(node_path.default.join(dir, "case.md"), "utf8"));
|
|
181
|
+
const tags = (frontmatter["tags"] ?? "").split(",").filter(Boolean);
|
|
182
|
+
return CaseSchema.parse({
|
|
183
|
+
id,
|
|
184
|
+
name: frontmatter["name"] ?? "",
|
|
185
|
+
status: frontmatter["status"] ?? "open",
|
|
186
|
+
created: frontmatter["created"] ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
187
|
+
updated: frontmatter["updated"] ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
188
|
+
tags,
|
|
189
|
+
description: frontmatter["description"] ?? ""
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
async loadContext(id) {
|
|
193
|
+
const dir = caseDir(id);
|
|
194
|
+
const { frontmatter } = require_frontmatter.parseFrontmatter(await (0, node_fs_promises.readFile)(node_path.default.join(dir, "case.md"), "utf8"));
|
|
195
|
+
const tags = (frontmatter["tags"] ?? "").split(",").filter(Boolean);
|
|
196
|
+
const { SessionStore } = await Promise.resolve().then(() => require("./session-BpNylyuJ.cjs"));
|
|
197
|
+
const { DossierStore } = await Promise.resolve().then(() => require("./dossier-DtxREpPm.cjs"));
|
|
198
|
+
const [latestSession, dossierSummaries, manifest] = await Promise.all([
|
|
199
|
+
SessionStore.getLatest(id),
|
|
200
|
+
DossierStore.listSummaries(id),
|
|
201
|
+
(0, node_fs_promises.readFile)(node_path.default.join(dir, "manifest.json"), "utf8").catch(() => "{\"entries\":[]}")
|
|
202
|
+
]);
|
|
203
|
+
const evidenceCount = JSON.parse(manifest).entries.length;
|
|
204
|
+
const lastSession = latestSession ? {
|
|
205
|
+
sessionId: latestSession.frontmatter["sessionId"] ?? "",
|
|
206
|
+
startTime: latestSession.frontmatter["startTime"] ?? "",
|
|
207
|
+
endTime: latestSession.frontmatter["endTime"] || void 0,
|
|
208
|
+
body: latestSession.body
|
|
209
|
+
} : null;
|
|
210
|
+
return {
|
|
211
|
+
case: {
|
|
212
|
+
id,
|
|
213
|
+
name: frontmatter["name"] ?? "",
|
|
214
|
+
status: frontmatter["status"] ?? "open",
|
|
215
|
+
created: frontmatter["created"] ?? "",
|
|
216
|
+
updated: frontmatter["updated"] ?? "",
|
|
217
|
+
tags
|
|
218
|
+
},
|
|
219
|
+
lastSession,
|
|
220
|
+
dossierSummaries,
|
|
221
|
+
evidenceCount
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
//#endregion
|
|
226
|
+
exports.CaseSchema = CaseSchema;
|
|
227
|
+
exports.CaseStatusEnum = CaseStatusEnum;
|
|
228
|
+
exports.CaseStore = CaseStore;
|
|
229
|
+
exports.casesRoot = casesRoot;
|
|
230
|
+
exports.generateCaseId = generateCaseId;
|