chain-insights 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +19 -9
  2. package/dist/canvas-Cn-maEIh.mjs +203 -0
  3. package/dist/canvas-Cn-maEIh.mjs.map +1 -0
  4. package/dist/canvas-p-oKCMjc.cjs +251 -0
  5. package/dist/cases-Bz_9XKEw.cjs +19 -0
  6. package/dist/cases-TVcAifxu.mjs +16 -0
  7. package/dist/cases-TVcAifxu.mjs.map +1 -0
  8. package/dist/cli.cjs +74 -28
  9. package/dist/cli.mjs +74 -28
  10. package/dist/cli.mjs.map +1 -1
  11. package/dist/{data-extractor-DZUJu1Bz.mjs → data-extractor-B4nHw1wZ.mjs} +2 -2
  12. package/dist/{data-extractor-DZUJu1Bz.mjs.map → data-extractor-B4nHw1wZ.mjs.map} +1 -1
  13. package/dist/{data-extractor-Cavd7wHk.cjs → data-extractor-DS4rzy3M.cjs} +1 -1
  14. package/dist/{export-BqTCO9lP.mjs → export-CBhcJuZ6.mjs} +8 -205
  15. package/dist/export-CBhcJuZ6.mjs.map +1 -0
  16. package/dist/{export-DsXgtCwO.cjs → export-D4v4-6F4.cjs} +16 -214
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.mjs +1 -1
  19. package/dist/{init-DLBL_nVG.mjs → init-CKQ6F07J.mjs} +22 -5
  20. package/dist/init-CKQ6F07J.mjs.map +1 -0
  21. package/dist/{init-zqbd7i-_.cjs → init-Dhw8F23z.cjs} +21 -4
  22. package/dist/mcp-proxy.cjs +20 -20
  23. package/dist/mcp-proxy.mjs +20 -20
  24. package/dist/mcp-proxy.mjs.map +1 -1
  25. package/dist/{public-tools-wJoAFDFa.mjs → public-tools-CyUZEz9B.mjs} +3 -3
  26. package/dist/{public-tools-wJoAFDFa.mjs.map → public-tools-CyUZEz9B.mjs.map} +1 -1
  27. package/dist/{public-tools-BvMb3H2P.cjs → public-tools-xfVNz9NE.cjs} +2 -2
  28. package/dist/{runner-BhZ4lnF1.cjs → runner-CVo41fjz.cjs} +2 -2
  29. package/dist/{runner-DIJSbkjc.mjs → runner-DWuSy1Se.mjs} +3 -3
  30. package/dist/{runner-DIJSbkjc.mjs.map → runner-DWuSy1Se.mjs.map} +1 -1
  31. package/dist/{selector-CF2o5gxN.mjs → selector-BvXM9jbe.mjs} +2 -2
  32. package/dist/{selector-CF2o5gxN.mjs.map → selector-BvXM9jbe.mjs.map} +1 -1
  33. package/dist/{selector-DfAMZEC9.cjs → selector-Dps_ZFxq.cjs} +1 -1
  34. package/dist/{store-CTtqQtaE.mjs → store-C2B_AssI.mjs} +2 -2
  35. package/dist/{store-CTtqQtaE.mjs.map → store-C2B_AssI.mjs.map} +1 -1
  36. package/dist/{store-CqPfs47P.cjs → store-CQhU8dz8.cjs} +0 -18
  37. package/dist/vault-B2y78Ypu.cjs +560 -0
  38. package/dist/vault-z35Dohdq.mjs +560 -0
  39. package/dist/vault-z35Dohdq.mjs.map +1 -0
  40. package/dist/{viz-Dqp3C5kb.cjs → viz-D1620cBX.cjs} +3 -3
  41. package/dist/{viz-5y24S5X1.mjs → viz-DB5XFG1z.mjs} +4 -4
  42. package/dist/{viz-5y24S5X1.mjs.map → viz-DB5XFG1z.mjs.map} +1 -1
  43. package/docs/graph-tools.md +15 -7
  44. package/docs/investigation-workspaces.md +36 -10
  45. package/docs/knowledge-exports.md +6 -2
  46. package/docs/mcp-proxy.md +14 -3
  47. package/docs/obsidian-vault.md +130 -0
  48. package/package.json +1 -1
  49. package/skills/chain-insights-developer-experience/SKILL.md +2 -2
  50. package/skills/chain-insights-investigation/SKILL.md +1 -1
  51. package/dist/cases-Cp9DUbEV.mjs +0 -6
  52. package/dist/cases-sTY5aXav.cjs +0 -9
  53. package/dist/export-BqTCO9lP.mjs.map +0 -1
  54. package/dist/init-DLBL_nVG.mjs.map +0 -1
@@ -0,0 +1,560 @@
1
+ const require_data_extractor = require("./data-extractor-DS4rzy3M.cjs");
2
+ const require_frontmatter = require("./frontmatter-Dvqa5HX6.cjs");
3
+ const require_output_root = require("./output-root-YIbl6PwF.cjs");
4
+ const require_dossier = require("./dossier-BXy57V4-.cjs");
5
+ const require_store = require("./store-CQhU8dz8.cjs");
6
+ const require_evidence = require("./evidence-CvEesemA.cjs");
7
+ require("./cases-Bz_9XKEw.cjs");
8
+ const require_canvas = require("./canvas-p-oKCMjc.cjs");
9
+ const require_graph_normalizer = require("./graph-normalizer-DbjlbMpz.cjs");
10
+ let node_path = require("node:path");
11
+ let node_fs_promises = require("node:fs/promises");
12
+ //#region src/vault/schema.ts
13
+ const VAULT_DIRS = [
14
+ ".obsidian",
15
+ "Canvases",
16
+ "Entities",
17
+ "Evidence",
18
+ "published"
19
+ ];
20
+ //#endregion
21
+ //#region src/vault/markdown.ts
22
+ function yamlString(value) {
23
+ return JSON.stringify(value);
24
+ }
25
+ function frontmatter(values) {
26
+ const lines = ["---"];
27
+ for (const [key, value] of Object.entries(values)) if (Array.isArray(value)) {
28
+ lines.push(`${key}:`);
29
+ for (const item of value) lines.push(` - ${yamlString(item)}`);
30
+ } else lines.push(`${key}: ${typeof value === "boolean" ? String(value) : yamlString(value)}`);
31
+ lines.push("---", "");
32
+ return lines.join("\n");
33
+ }
34
+ function renderLiveCaseNote(input) {
35
+ return frontmatter({
36
+ type: "chain-insights-case",
37
+ case_id: input.id,
38
+ status: input.status,
39
+ tags: input.tags,
40
+ contains_sensitive_data: true,
41
+ source_of_truth: `cases/${input.id}/`
42
+ }) + [
43
+ `# ${input.name}`,
44
+ "",
45
+ input.description,
46
+ "",
47
+ "## Live Workspace",
48
+ "",
49
+ `Status: ${input.status}`,
50
+ `Evidence files: ${input.evidenceCount}`,
51
+ `Evidence manifest verified: ${input.evidenceVerified ? "yes" : "no"}`,
52
+ `Entities: ${input.entityCount}`,
53
+ "",
54
+ "## Navigation",
55
+ "",
56
+ "- [[Agent Console]]",
57
+ "- [[Graph.canvas]]",
58
+ "- [[Cases]]",
59
+ `- [[cases/${input.id}/Evidence|Evidence]]`,
60
+ `- [[cases/${input.id}/Entities|Entities]]`,
61
+ "- [[Graphs]]",
62
+ ""
63
+ ].join("\n");
64
+ }
65
+ function renderCaseAgentConsole(input) {
66
+ return frontmatter({
67
+ type: "chain-insights-case-agent-console",
68
+ case_id: input.id,
69
+ contains_sensitive_data: true
70
+ }) + [
71
+ `# Agent Console: ${input.name}`,
72
+ "",
73
+ `Canonical case state: \`cases/${input.id}/\``,
74
+ "",
75
+ "## Case Files",
76
+ "",
77
+ "- [[Case]]",
78
+ "- [[Graph.canvas]]",
79
+ `- [[cases/${input.id}/Evidence|Evidence]]`,
80
+ `- [[cases/${input.id}/Entities|Entities]]`,
81
+ "",
82
+ "## Current Counts",
83
+ "",
84
+ `- Evidence files: ${input.evidenceCount}`,
85
+ `- Entities: ${input.entityCount}`,
86
+ `- Manifest verified: ${input.evidenceVerified ? "yes" : "no"}`,
87
+ ""
88
+ ].join("\n");
89
+ }
90
+ function renderCaseEvidenceIndex(input, evidence) {
91
+ return frontmatter({
92
+ type: "chain-insights-case-evidence-index",
93
+ case_id: input.id,
94
+ contains_sensitive_data: true
95
+ }) + [
96
+ `# Evidence: ${input.name}`,
97
+ "",
98
+ `Canonical evidence directory: \`cases/${input.id}/evidence/\``,
99
+ `Evidence files: ${input.evidenceCount}`,
100
+ `Manifest verified: ${input.evidenceVerified ? "yes" : "no"}`,
101
+ "",
102
+ "## Evidence Notes",
103
+ "",
104
+ ...evidence.length > 0 ? evidence.map((item) => `- [[${item.notePath.replace(/\.md$/, "")}|${item.id}]] (${item.source})`) : ["No evidence files recorded yet."],
105
+ ""
106
+ ].join("\n");
107
+ }
108
+ function renderEvidenceNote(input, caseId) {
109
+ return frontmatter({
110
+ type: "chain-insights-evidence",
111
+ case_id: caseId,
112
+ evidence_id: input.id,
113
+ source: input.source,
114
+ source_file: `cases/${caseId}/evidence/${input.filename}`,
115
+ contains_sensitive_data: true
116
+ }) + [
117
+ `# Evidence: ${input.source}`,
118
+ "",
119
+ `Evidence ID: \`${input.id}\``,
120
+ `Source file: \`cases/${caseId}/evidence/${input.filename}\``,
121
+ `Captured: ${input.timestamp || "unknown"}`,
122
+ `Query params: ${input.queryParams || "none"}`,
123
+ "",
124
+ "## Case",
125
+ "",
126
+ `- [[cases/${caseId}/Case|${caseId}]]`,
127
+ "",
128
+ "## Body",
129
+ "",
130
+ input.body.trim() || "No evidence body recorded.",
131
+ ""
132
+ ].join("\n");
133
+ }
134
+ function renderCaseEntityIndex(input, entities) {
135
+ return frontmatter({
136
+ type: "chain-insights-case-entity-index",
137
+ case_id: input.id,
138
+ contains_sensitive_data: true
139
+ }) + [
140
+ `# Entities: ${input.name}`,
141
+ "",
142
+ `Canonical dossier directory: \`cases/${input.id}/dossiers/\``,
143
+ `Entities: ${entities.length}`,
144
+ "",
145
+ "## Entity Notes",
146
+ "",
147
+ ...entities.length > 0 ? entities.map((item) => `- [[${item.notePath.replace(/\.md$/, "")}|${item.label}]] (${item.entityType})`) : ["No entities recorded yet."],
148
+ ""
149
+ ].join("\n");
150
+ }
151
+ function renderEntityNote(address, caseId, entityType) {
152
+ return frontmatter({
153
+ type: "chain-insights-entity",
154
+ case_id: caseId,
155
+ address,
156
+ entity_type: entityType,
157
+ contains_sensitive_data: true
158
+ }) + [
159
+ `# Entity: ${address}`,
160
+ "",
161
+ `Address: ${address}`,
162
+ `Type: ${entityType}`,
163
+ "",
164
+ "## Cases",
165
+ "",
166
+ `- [[cases/${caseId}/Case|${caseId}]]`,
167
+ ""
168
+ ].join("\n");
169
+ }
170
+ function renderVaultHome() {
171
+ return frontmatter({
172
+ type: "chain-insights-vault-home",
173
+ product: "Chain Insights",
174
+ contains_sensitive_data: true
175
+ }) + [
176
+ "# Chain Insights Vault",
177
+ "",
178
+ "Chain Insights is an AML investigation CLI and MCP proxy layered on GraphRAG MCP.",
179
+ "",
180
+ "## Start Here",
181
+ "",
182
+ "- [[Cases]]",
183
+ "- [[Entities]]",
184
+ "- [[Evidence]]",
185
+ "- [[Graphs]]",
186
+ "- [[Agent Console]]",
187
+ ""
188
+ ].join("\n");
189
+ }
190
+ function renderRootIndex(title, type, links) {
191
+ return frontmatter({
192
+ type,
193
+ product: "Chain Insights",
194
+ contains_sensitive_data: true
195
+ }) + [
196
+ `# ${title}`,
197
+ "",
198
+ ...links.map((link) => `- [[${link}]]`),
199
+ ""
200
+ ].join("\n");
201
+ }
202
+ function renderRootAgentConsole() {
203
+ return frontmatter({
204
+ type: "chain-insights-agent-console",
205
+ product: "Chain Insights",
206
+ contains_sensitive_data: true
207
+ }) + [
208
+ "# Agent Console",
209
+ "",
210
+ "Use Chain Insights case evidence as canonical local state and GraphRAG MCP for fresh graph facts.",
211
+ "",
212
+ "## Reading Order",
213
+ "",
214
+ "1. [[Home]]",
215
+ "2. [[Cases]]",
216
+ "3. [[Entities]]",
217
+ "4. [[Evidence]]",
218
+ "5. [[Graphs]]",
219
+ ""
220
+ ].join("\n");
221
+ }
222
+ function renderObsidianAppConfig() {
223
+ return JSON.stringify({
224
+ useMarkdownLinks: false,
225
+ newLinkFormat: "shortest",
226
+ alwaysUpdateLinks: true
227
+ }, null, 2) + "\n";
228
+ }
229
+ function renderObsidianGraphConfig() {
230
+ return JSON.stringify({
231
+ "collapse-filter": true,
232
+ search: "",
233
+ showTags: true,
234
+ showAttachments: true,
235
+ hideUnresolved: false,
236
+ showOrphans: true
237
+ }, null, 2) + "\n";
238
+ }
239
+ function renderObsidianTemplatesConfig() {
240
+ return JSON.stringify({
241
+ folder: "",
242
+ dateFormat: "YYYY-MM-DD",
243
+ timeFormat: "HH:mm"
244
+ }, null, 2) + "\n";
245
+ }
246
+ function renderVaultGitignore() {
247
+ return [
248
+ "# Chain Insights local runtime state",
249
+ ".chain-insights/runtime/",
250
+ "",
251
+ "# Obsidian local UI state",
252
+ ".obsidian/workspace.json",
253
+ ".obsidian/workspace-mobile.json",
254
+ ".obsidian/workspaces.json",
255
+ "",
256
+ "# Private export bundles are local by default",
257
+ "published/",
258
+ ""
259
+ ].join("\n");
260
+ }
261
+ //#endregion
262
+ //#region src/vault/index.ts
263
+ const VAULT_FILES = [
264
+ {
265
+ path: ".obsidian/app.json",
266
+ content: renderObsidianAppConfig()
267
+ },
268
+ {
269
+ path: ".obsidian/graph.json",
270
+ content: renderObsidianGraphConfig()
271
+ },
272
+ {
273
+ path: ".obsidian/templates.json",
274
+ content: renderObsidianTemplatesConfig()
275
+ },
276
+ {
277
+ path: ".gitignore",
278
+ content: renderVaultGitignore()
279
+ },
280
+ {
281
+ path: "Home.md",
282
+ content: renderVaultHome()
283
+ },
284
+ {
285
+ path: "Cases.md",
286
+ content: renderRootIndex("Cases", "chain-insights-vault-cases-index", ["Home", "Agent Console"])
287
+ },
288
+ {
289
+ path: "Entities.md",
290
+ content: renderRootIndex("Entities", "chain-insights-vault-entities-index", [
291
+ "Home",
292
+ "Cases",
293
+ "Evidence"
294
+ ])
295
+ },
296
+ {
297
+ path: "Evidence.md",
298
+ content: renderRootIndex("Evidence", "chain-insights-vault-evidence-index", [
299
+ "Home",
300
+ "Cases",
301
+ "Entities"
302
+ ])
303
+ },
304
+ {
305
+ path: "Graphs.md",
306
+ content: renderRootIndex("Graphs", "chain-insights-vault-graphs-index", [
307
+ "Home",
308
+ "Cases",
309
+ "Entities",
310
+ "Evidence"
311
+ ])
312
+ },
313
+ {
314
+ path: "Agent Console.md",
315
+ content: renderRootAgentConsole()
316
+ },
317
+ {
318
+ path: "Canvases/README.md",
319
+ content: renderRootIndex("Canvases", "chain-insights-vault-canvases-readme", ["Home", "Graphs"])
320
+ },
321
+ {
322
+ path: "Entities/README.md",
323
+ content: renderRootIndex("Entities Folder", "chain-insights-vault-entities-readme", ["Entities", "Cases"])
324
+ },
325
+ {
326
+ path: "Evidence/README.md",
327
+ content: renderRootIndex("Evidence Folder", "chain-insights-vault-evidence-readme", ["Evidence", "Cases"])
328
+ }
329
+ ];
330
+ async function scaffoldVault(options) {
331
+ const workspaceRoot = (0, node_path.resolve)(options.workspaceRoot);
332
+ const force = options.force === true;
333
+ if (!force) await assertNoVaultFileCollisions(workspaceRoot);
334
+ for (const dir of VAULT_DIRS) await (0, node_fs_promises.mkdir)((0, node_path.join)(workspaceRoot, dir), { recursive: true });
335
+ const filesWritten = [];
336
+ for (const file of VAULT_FILES) {
337
+ await writeVaultFile(workspaceRoot, file, force);
338
+ filesWritten.push(file.path);
339
+ }
340
+ return {
341
+ workspaceRoot,
342
+ filesWritten
343
+ };
344
+ }
345
+ async function refreshCaseVault(options) {
346
+ const workspace = require_output_root.workspaceOutputPaths();
347
+ const force = options.force === true;
348
+ const [caseInfo, evidenceVerification, evidence, dossiers, graph] = await Promise.all([
349
+ require_store.CaseStore.get(options.caseId),
350
+ require_evidence.EvidenceStore.verifyManifest(options.caseId),
351
+ readCaseEvidence(options.caseId),
352
+ require_dossier.DossierStore.listSummaries(options.caseId),
353
+ loadLiveCaseGraph(options.caseId)
354
+ ]);
355
+ const caseSummary = {
356
+ id: caseInfo.id,
357
+ name: caseInfo.name,
358
+ status: caseInfo.status,
359
+ tags: caseInfo.tags,
360
+ description: caseInfo.description,
361
+ evidenceCount: evidenceVerification.count,
362
+ evidenceVerified: evidenceVerification.ok,
363
+ entityCount: dossiers.length
364
+ };
365
+ const canvasGraph = {
366
+ ...graph,
367
+ nodes: mergeDossierNodes(graph.nodes, dossiers)
368
+ };
369
+ const canvas = graphToCaseVaultCanvas(require_canvas.graphToCanvas(canvasGraph), caseInfo.id, canvasGraph.nodes);
370
+ const evidenceSummaries = evidence.map((evidenceDoc) => ({
371
+ ...evidenceDoc,
372
+ notePath: `Evidence/${require_canvas.safeFilename(`${evidenceDoc.filename.replace(/\.md$/, "")}-${caseInfo.id}`)}`
373
+ }));
374
+ const entityFiles = entityFilesForCase(caseInfo.id, canvasGraph.nodes, dossiers);
375
+ const entitySummaries = [...entityFiles.values()].map((entry) => entry.summary).sort((left, right) => left.label.localeCompare(right.label));
376
+ const files = [
377
+ {
378
+ path: `cases/${caseInfo.id}/Case.md`,
379
+ content: renderLiveCaseNote(caseSummary)
380
+ },
381
+ {
382
+ path: `cases/${caseInfo.id}/Agent Console.md`,
383
+ content: renderCaseAgentConsole(caseSummary)
384
+ },
385
+ {
386
+ path: `cases/${caseInfo.id}/Evidence.md`,
387
+ content: renderCaseEvidenceIndex(caseSummary, evidenceSummaries)
388
+ },
389
+ {
390
+ path: `cases/${caseInfo.id}/Entities.md`,
391
+ content: renderCaseEntityIndex(caseSummary, entitySummaries)
392
+ },
393
+ {
394
+ path: `cases/${caseInfo.id}/Graph.canvas`,
395
+ content: JSON.stringify(canvas, null, 2) + "\n"
396
+ },
397
+ ...evidenceSummaries.map((evidenceDoc) => ({
398
+ path: evidenceDoc.notePath,
399
+ content: renderEvidenceNote(evidenceDoc, caseInfo.id)
400
+ })),
401
+ ...[...entityFiles.values()].map((entry) => ({
402
+ path: entry.summary.notePath,
403
+ content: entry.content
404
+ }))
405
+ ];
406
+ if (!force) await assertNoFileCollisions(workspace.root, files);
407
+ const filesWritten = [];
408
+ for (const file of files) {
409
+ await writeVaultFile(workspace.root, file, force);
410
+ filesWritten.push(file.path);
411
+ }
412
+ return {
413
+ caseId: caseInfo.id,
414
+ filesWritten,
415
+ nextFile: `cases/${caseInfo.id}/Case.md`
416
+ };
417
+ }
418
+ async function assertNoVaultFileCollisions(workspaceRoot) {
419
+ await assertNoFileCollisions(workspaceRoot, VAULT_FILES);
420
+ }
421
+ async function assertNoFileCollisions(workspaceRoot, files) {
422
+ for (const file of files) try {
423
+ await (0, node_fs_promises.access)((0, node_path.join)(workspaceRoot, file.path));
424
+ throw new Error(`Refusing to overwrite existing vault file: ${file.path}`);
425
+ } catch (error) {
426
+ if (isNotFoundError(error)) continue;
427
+ throw error;
428
+ }
429
+ }
430
+ async function writeVaultFile(workspaceRoot, file, force) {
431
+ try {
432
+ const filePath = (0, node_path.join)(workspaceRoot, file.path);
433
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
434
+ await (0, node_fs_promises.writeFile)(filePath, file.content, {
435
+ encoding: "utf8",
436
+ flag: force ? "w" : "wx"
437
+ });
438
+ } catch (error) {
439
+ if (!force && isFileExistsError(error)) throw new Error(`Refusing to overwrite existing vault file: ${file.path}`);
440
+ throw error;
441
+ }
442
+ }
443
+ function isFileExistsError(error) {
444
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
445
+ }
446
+ function isNotFoundError(error) {
447
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
448
+ }
449
+ function mergeDossierNodes(graphNodes, dossiers) {
450
+ const nodesById = /* @__PURE__ */ new Map();
451
+ const aliases = /* @__PURE__ */ new Map();
452
+ graphNodes.forEach((node, index) => {
453
+ const id = String(node["id"] ?? node["address"] ?? `node-${index + 1}`);
454
+ nodesById.set(id, node);
455
+ aliases.set(id, id);
456
+ if (typeof node["address"] === "string") aliases.set(node["address"], id);
457
+ });
458
+ for (const dossier of dossiers) {
459
+ const existingId = aliases.get(dossier.address);
460
+ if (existingId) {
461
+ const existing = nodesById.get(existingId);
462
+ if (existing) nodesById.set(existingId, enrichDossierNode(existing, dossier.type));
463
+ } else nodesById.set(dossier.address, {
464
+ id: dossier.address,
465
+ address: dossier.address,
466
+ node_type: dossier.type,
467
+ roles: [dossier.type]
468
+ });
469
+ }
470
+ return [...nodesById.values()];
471
+ }
472
+ async function readCaseEvidence(caseId) {
473
+ const evidenceDir = (0, node_path.join)(require_output_root.workspaceOutputPaths().casesRoot, caseId, "evidence");
474
+ const files = await (0, node_fs_promises.readdir)(evidenceDir).catch(() => []);
475
+ const docs = [];
476
+ for (const filename of files.filter((file) => file.endsWith(".md")).sort()) {
477
+ const { frontmatter, body } = require_frontmatter.parseFrontmatter(await (0, node_fs_promises.readFile)((0, node_path.join)(evidenceDir, filename), "utf8"));
478
+ docs.push({
479
+ id: frontmatter["id"] || filename.replace(/\.md$/, ""),
480
+ filename,
481
+ source: frontmatter["source"] || "unknown",
482
+ timestamp: frontmatter["timestamp"] || "",
483
+ queryParams: frontmatter["queryParams"] || "",
484
+ body
485
+ });
486
+ }
487
+ return docs;
488
+ }
489
+ function entityFilesForCase(caseId, graphNodes, dossiers) {
490
+ const files = /* @__PURE__ */ new Map();
491
+ for (const [index, node] of graphNodes.entries()) {
492
+ const label = entityLabelForGraphNode(node, index);
493
+ const entityType = String(node["entityType"] ?? node["node_type"] ?? node["nodeType"] ?? "unknown");
494
+ const notePath = `Entities/${require_canvas.safeFilename(label)}`;
495
+ files.set(notePath, {
496
+ summary: {
497
+ label,
498
+ notePath,
499
+ entityType
500
+ },
501
+ content: renderEntityNote(label, caseId, entityType)
502
+ });
503
+ }
504
+ for (const dossier of dossiers) {
505
+ const notePath = `Entities/${require_canvas.safeFilename(dossier.address)}`;
506
+ files.set(notePath, {
507
+ summary: {
508
+ label: dossier.address,
509
+ notePath,
510
+ entityType: dossier.type
511
+ },
512
+ content: renderEntityNote(dossier.address, caseId, dossier.type)
513
+ });
514
+ }
515
+ return files;
516
+ }
517
+ function entityLabelForGraphNode(node, index) {
518
+ return String(node["address"] ?? node["id"] ?? `node-${index + 1}`);
519
+ }
520
+ function graphToCaseVaultCanvas(canvas, caseId, graphNodes) {
521
+ return {
522
+ nodes: canvas.nodes.map((node) => {
523
+ if (node.id === "case" && node.type === "file") return {
524
+ ...node,
525
+ file: `cases/${caseId}/Case.md`
526
+ };
527
+ const entityIndex = /^entity-(\d+)$/.exec(node.id)?.[1];
528
+ if (node.type === "file" && entityIndex) {
529
+ const graphNode = graphNodes[Number(entityIndex) - 1];
530
+ if (graphNode) return {
531
+ ...node,
532
+ file: `Entities/${require_canvas.safeFilename(entityLabelForGraphNode(graphNode, Number(entityIndex) - 1))}`
533
+ };
534
+ }
535
+ return node;
536
+ }),
537
+ edges: canvas.edges
538
+ };
539
+ }
540
+ async function loadLiveCaseGraph(caseId) {
541
+ const graph = await require_data_extractor.extractGraphFromCase(caseId);
542
+ return require_graph_normalizer.normalizeGraphPayload({
543
+ schema: "chain-insights.graph.v1",
544
+ nodes: graph.nodes,
545
+ edges: graph.edges,
546
+ flows: [],
547
+ edge_anchors: [],
548
+ metadata: graph.metadata
549
+ });
550
+ }
551
+ function enrichDossierNode(node, dossierType) {
552
+ const enriched = { ...node };
553
+ if (typeof enriched["node_type"] !== "string" || enriched["node_type"] === "unknown") enriched["node_type"] = dossierType;
554
+ if (!Array.isArray(enriched["roles"]) || enriched["roles"].length === 0) enriched["roles"] = [dossierType];
555
+ return enriched;
556
+ }
557
+ //#endregion
558
+ exports.assertNoVaultFileCollisions = assertNoVaultFileCollisions;
559
+ exports.refreshCaseVault = refreshCaseVault;
560
+ exports.scaffoldVault = scaffoldVault;