chain-insights 0.2.16 → 0.2.18

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 (130) hide show
  1. package/README.md +29 -18
  2. package/dist/{active-Dv7Tu-O4.cjs → active-BVr55kvW.cjs} +1 -1
  3. package/dist/{active-BSrxLKwn.mjs → active-ByNgjuAg.mjs} +2 -2
  4. package/dist/{active-BSrxLKwn.mjs.map → active-ByNgjuAg.mjs.map} +1 -1
  5. package/dist/{app-BjjuQM0B.mjs → app-DdWQF_zb.mjs} +4 -4
  6. package/dist/{app-BjjuQM0B.mjs.map → app-DdWQF_zb.mjs.map} +1 -1
  7. package/dist/{app-Dq1TdB6p.cjs → app-DxlQE_P5.cjs} +4 -4
  8. package/dist/{artifact-server-Dxz5YbuQ.mjs → artifact-server-4DiMvwhC.mjs} +2 -2
  9. package/dist/{artifact-server-Dxz5YbuQ.mjs.map → artifact-server-4DiMvwhC.mjs.map} +1 -1
  10. package/dist/{artifact-server-DoxJ7fCx.cjs → artifact-server-B-3ho4bk.cjs} +1 -2
  11. package/dist/{call-args-Lk_wOJxd.mjs → call-args-DPXdX3_D.mjs} +1 -1
  12. package/dist/{call-args-Lk_wOJxd.mjs.map → call-args-DPXdX3_D.mjs.map} +1 -1
  13. package/dist/{capabilities-DliMBim-.mjs → capabilities-BShqspb-.mjs} +2 -2
  14. package/dist/{capabilities-DliMBim-.mjs.map → capabilities-BShqspb-.mjs.map} +1 -1
  15. package/dist/{capabilities-CB97WMA5.cjs → capabilities-Bm0JDbV7.cjs} +1 -1
  16. package/dist/cases-c0iV-XLI.cjs +9 -0
  17. package/dist/cases-qjPtbnUd.mjs +6 -0
  18. package/dist/cli.cjs +64 -64
  19. package/dist/cli.mjs +63 -63
  20. package/dist/cli.mjs.map +1 -1
  21. package/dist/{client-D4Bq0rp9.mjs → client-D4_hd4AP.mjs} +23 -9
  22. package/dist/client-D4_hd4AP.mjs.map +1 -0
  23. package/dist/{client-D4fZgIaO.cjs → client-DPc2eyVN.cjs} +27 -7
  24. package/dist/{config-BwrBYmiC.mjs → config-9KYXaAv-.mjs} +3 -3
  25. package/dist/{config-BwrBYmiC.mjs.map → config-9KYXaAv-.mjs.map} +1 -1
  26. package/dist/{config-Bmdl5hdk.cjs → config-BhYbhLDI.cjs} +2 -2
  27. package/dist/{dossier-BsroDgD3.mjs → dossier-Bl0NkJKC.mjs} +3 -3
  28. package/dist/{dossier-BsroDgD3.mjs.map → dossier-Bl0NkJKC.mjs.map} +1 -1
  29. package/dist/{dossier-DtxREpPm.cjs → dossier-Br62hCG7.cjs} +3 -3
  30. package/dist/{evidence-BGcdKxuV.cjs → evidence-CvEesemA.cjs} +3 -3
  31. package/dist/{evidence-BhvFW-y_.mjs → evidence-D96PTzOQ.mjs} +3 -3
  32. package/dist/{evidence-BhvFW-y_.mjs.map → evidence-D96PTzOQ.mjs.map} +1 -1
  33. package/dist/{format-Ce1RObVl.mjs → format-Bq94jSyw.mjs} +1 -1
  34. package/dist/{format-Ce1RObVl.mjs.map → format-Bq94jSyw.mjs.map} +1 -1
  35. package/dist/{frontmatter-D8wWCeOa.mjs → frontmatter-D0ccQnUM.mjs} +1 -1
  36. package/dist/{frontmatter-D8wWCeOa.mjs.map → frontmatter-D0ccQnUM.mjs.map} +1 -1
  37. package/dist/{graph-normalizer-Cv9yK9Pg.mjs → graph-normalizer-CXP06jKh.mjs} +1 -1
  38. package/dist/{graph-normalizer-Cv9yK9Pg.mjs.map → graph-normalizer-CXP06jKh.mjs.map} +1 -1
  39. package/dist/{graph-reports-DU05YCei.cjs → graph-reports-B3mkLP8Z.cjs} +3 -3
  40. package/dist/{graph-reports-C4TBjCkM.mjs → graph-reports-BDELxmpi.mjs} +3 -3
  41. package/dist/{graph-reports-C4TBjCkM.mjs.map → graph-reports-BDELxmpi.mjs.map} +1 -1
  42. package/dist/{html-generator-V6Bp0uRb.mjs → html-generator-AowOmzyi.mjs} +2 -2
  43. package/dist/{html-generator-V6Bp0uRb.mjs.map → html-generator-AowOmzyi.mjs.map} +1 -1
  44. package/dist/{html-generator-CAv81IWH.cjs → html-generator-Bx3UcLTB.cjs} +1 -1
  45. package/dist/index.cjs +8 -8
  46. package/dist/index.d.cts.map +1 -1
  47. package/dist/index.d.mts.map +1 -1
  48. package/dist/index.mjs +8 -8
  49. package/dist/{init-BjuFt54X.cjs → init-BvpZtFiT.cjs} +4 -5
  50. package/dist/{init-CaOsHTIo.mjs → init-CZbZegIW.mjs} +4 -5
  51. package/dist/init-CZbZegIW.mjs.map +1 -0
  52. package/dist/mcp-proxy.cjs +75 -38
  53. package/dist/mcp-proxy.d.cts.map +1 -1
  54. package/dist/mcp-proxy.d.mts.map +1 -1
  55. package/dist/mcp-proxy.mjs +74 -37
  56. package/dist/mcp-proxy.mjs.map +1 -1
  57. package/dist/{output-root-CmWM7aV2.mjs → output-root-BRhzhhXZ.mjs} +3 -3
  58. package/dist/{output-root-CmWM7aV2.mjs.map → output-root-BRhzhhXZ.mjs.map} +1 -1
  59. package/dist/{output-root-CFYms3ad.cjs → output-root-YIbl6PwF.cjs} +2 -2
  60. package/dist/{parser-BUIWW1OH.cjs → parser-BXLAHYnZ.cjs} +1 -1
  61. package/dist/{parser-DO0_SssG.mjs → parser-CJfMsOl6.mjs} +1 -1
  62. package/dist/{parser-DO0_SssG.mjs.map → parser-CJfMsOl6.mjs.map} +1 -1
  63. package/dist/{public-tools-D4UI-Zb0.mjs → public-tools-D6Q5MTcO.mjs} +7 -7
  64. package/dist/{public-tools-D4UI-Zb0.mjs.map → public-tools-D6Q5MTcO.mjs.map} +1 -1
  65. package/dist/{public-tools-XSpkz2ky.cjs → public-tools-V7ON7goq.cjs} +7 -7
  66. package/dist/{resolver-zYbu4wDV.cjs → resolver-BUU7ZgW-.cjs} +1 -1
  67. package/dist/{resolver-C2ZS7oC8.mjs → resolver-D7VBb0uB.mjs} +1 -1
  68. package/dist/{resolver-C2ZS7oC8.mjs.map → resolver-D7VBb0uB.mjs.map} +1 -1
  69. package/dist/{runner-BhUHbiHG.mjs → runner-BatyCxv7.mjs} +7 -7
  70. package/dist/{runner-BhUHbiHG.mjs.map → runner-BatyCxv7.mjs.map} +1 -1
  71. package/dist/{runner-1Eq55OYb.cjs → runner-CCA7SJ7X.cjs} +6 -7
  72. package/dist/{schema-8d0rVIdZ.mjs → schema-BbQVXp36.mjs} +2 -2
  73. package/dist/{schema-8d0rVIdZ.mjs.map → schema-BbQVXp36.mjs.map} +1 -1
  74. package/dist/{schema-4XpzDFQM.cjs → schema-DN-KLkYN.cjs} +1 -1
  75. package/dist/{schema-cache-CgWRCN2N.cjs → schema-cache-CJk1EL3L.cjs} +1 -1
  76. package/dist/{schema-cache-9CksD7tX.mjs → schema-cache-DwDvPy4e.mjs} +1 -1
  77. package/dist/{schema-cache-9CksD7tX.mjs.map → schema-cache-DwDvPy4e.mjs.map} +1 -1
  78. package/dist/{selector-xjm6NTHI.mjs → selector-CTUiQrzI.mjs} +2 -2
  79. package/dist/{selector-xjm6NTHI.mjs.map → selector-CTUiQrzI.mjs.map} +1 -1
  80. package/dist/{selector-CkFcTXzz.cjs → selector-DBS2jYH4.cjs} +1 -1
  81. package/dist/{server-BkM5xrXb.mjs → server-BDlbmGbL.mjs} +3 -3
  82. package/dist/{server-BkM5xrXb.mjs.map → server-BDlbmGbL.mjs.map} +1 -1
  83. package/dist/{server-DXowbpfi.cjs → server-C3y1gQmZ.cjs} +2 -2
  84. package/dist/{session-CcTgYxsj.mjs → session-Bha3zFrx.mjs} +3 -3
  85. package/dist/{session-CcTgYxsj.mjs.map → session-Bha3zFrx.mjs.map} +1 -1
  86. package/dist/{session-BpNylyuJ.cjs → session-DwyikazY.cjs} +3 -3
  87. package/dist/{setup-DOpKPrlx.cjs → setup-CDha4B9s.cjs} +1 -1
  88. package/dist/{setup-DyrWHuwQ.mjs → setup-yXK8agdn.mjs} +1 -1
  89. package/dist/{setup-DyrWHuwQ.mjs.map → setup-yXK8agdn.mjs.map} +1 -1
  90. package/dist/{store-BoWE-Gtl.mjs → store-BT2SCcQr.mjs} +5 -5
  91. package/dist/{store-BoWE-Gtl.mjs.map → store-BT2SCcQr.mjs.map} +1 -1
  92. package/dist/{store-BiUhQOIf.cjs → store-DogLawSj.cjs} +5 -5
  93. package/dist/{tool-visibility-3Z_KvO9Q.mjs → tool-visibility-BHRFLXuU.mjs} +2 -2
  94. package/dist/{tool-visibility-3Z_KvO9Q.mjs.map → tool-visibility-BHRFLXuU.mjs.map} +1 -1
  95. package/dist/{tool-visibility-CwgY205r.cjs → tool-visibility-iAVQV3t0.cjs} +1 -1
  96. package/dist/{tools-f_vJUZAF.cjs → tools-DY8h0WbE.cjs} +2 -2
  97. package/dist/{tools-Cp2jAAAb.mjs → tools-Py6SXg6J.mjs} +3 -3
  98. package/dist/{tools-Cp2jAAAb.mjs.map → tools-Py6SXg6J.mjs.map} +1 -1
  99. package/dist/{topup-server-DUjyFftI.mjs → topup-server-6MH7q73X.mjs} +5 -5
  100. package/dist/{topup-server-DUjyFftI.mjs.map → topup-server-6MH7q73X.mjs.map} +1 -1
  101. package/dist/{topup-server-BZuQifvh.cjs → topup-server-DjUjhNjv.cjs} +4 -4
  102. package/dist/{version-1gP19Lhi.mjs → version-BA3J8hu4.mjs} +1 -1
  103. package/dist/{version-1gP19Lhi.mjs.map → version-BA3J8hu4.mjs.map} +1 -1
  104. package/dist/{version-BNGtdpmH.cjs → version-CO9Or_YV.cjs} +0 -1
  105. package/dist/{data-extractor-BNGj7ECT.cjs → viz-Da9YWN_I.cjs} +37 -8
  106. package/dist/{data-extractor-DFzsa5CS.mjs → viz-DkJyqlUu.mjs} +35 -6
  107. package/dist/viz-DkJyqlUu.mjs.map +1 -0
  108. package/dist/{wallet-BMelXBYP.mjs → wallet-D8IqFRKY.mjs} +2 -2
  109. package/dist/{wallet-BMelXBYP.mjs.map → wallet-D8IqFRKY.mjs.map} +1 -1
  110. package/dist/{wallet-RnvvSpV2.cjs → wallet-TAlNMvIM.cjs} +1 -1
  111. package/docs/architecture.md +1 -2
  112. package/docs/graph-tools.md +3 -3
  113. package/package.json +24 -4
  114. package/skills/chain-insights-investigation/SKILL.md +9 -9
  115. package/skills/chain-insights-trace-funds/SKILL.md +3 -3
  116. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +1 -1
  117. package/dist/cases-By7INiOa.mjs +0 -6
  118. package/dist/cases-CDcNU91B.cjs +0 -9
  119. package/dist/client-D4Bq0rp9.mjs.map +0 -1
  120. package/dist/data-extractor-DFzsa5CS.mjs.map +0 -1
  121. package/dist/init-CaOsHTIo.mjs.map +0 -1
  122. package/dist/viz-BlCJe6Tk.mjs +0 -35
  123. package/dist/viz-BlCJe6Tk.mjs.map +0 -1
  124. package/dist/viz-ClezVXrJ.cjs +0 -44
  125. /package/dist/{call-args-DQA2QcRA.cjs → call-args-CcUV6gFS.cjs} +0 -0
  126. /package/dist/{chunk-CZWwpsFl.cjs → chunk-DakpK96I.cjs} +0 -0
  127. /package/dist/{format-DOrPvXEr.cjs → format-9NLBykEL.cjs} +0 -0
  128. /package/dist/{frontmatter-DgAuai7E.cjs → frontmatter-Dvqa5HX6.cjs} +0 -0
  129. /package/dist/{graph-normalizer-DeIj6Ses.cjs → graph-normalizer-DbjlbMpz.cjs} +0 -0
  130. /package/dist/{rolldown-runtime-wcPFST8Q.mjs → rolldown-runtime-D7D4PA-g.mjs} +0 -0
@@ -1,5 +1,5 @@
1
- const require_chunk = require("./chunk-CZWwpsFl.cjs");
2
- const require_schema = require("./schema-4XpzDFQM.cjs");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_schema = require("./schema-DN-KLkYN.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_chunk.__toESM(node_path, 1);
5
5
  let node_fs_promises = require("node:fs/promises");
@@ -1,5 +1,5 @@
1
- import { n as serializeFrontmatter, t as parseFrontmatter } from "./frontmatter-D8wWCeOa.mjs";
2
- import { n as workspaceOutputPaths } from "./output-root-CmWM7aV2.mjs";
1
+ import { n as serializeFrontmatter, t as parseFrontmatter } from "./frontmatter-D0ccQnUM.mjs";
2
+ import { n as workspaceOutputPaths } from "./output-root-BRhzhhXZ.mjs";
3
3
  import path from "node:path";
4
4
  import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
5
5
  import "node:crypto";
@@ -73,4 +73,4 @@ const DossierStore = {
73
73
  //#endregion
74
74
  export { DossierStore };
75
75
 
76
- //# sourceMappingURL=dossier-BsroDgD3.mjs.map
76
+ //# sourceMappingURL=dossier-Bl0NkJKC.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dossier-BsroDgD3.mjs","names":["nodeErr"],"sources":["../src/cases/dossier.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { parseFrontmatter, serializeFrontmatter } from './frontmatter.js'\n\nfunction caseDir(caseId: string): string {\n return path.join(workspaceOutputPaths().casesRoot, caseId)\n}\n\nfunction sanitizeAddress(address: string): string {\n // Security T-03-06: prevent path traversal by stripping all non-alphanumeric chars\n return address.replace(/[^a-zA-Z0-9]/g, '').slice(0, 66)\n}\n\nfunction contentHash(text: string): string {\n return createHash('sha256').update(text).digest('hex')\n}\n\nexport const DossierStore = {\n async appendFinding(\n caseId: string,\n address: string,\n finding: string,\n entityType: 'eoa' | 'contract' | 'exchange' | 'mixer' | 'unknown' = 'unknown'\n ): Promise<void> {\n const safeAddr = sanitizeAddress(address)\n const dossierDir = path.join(caseDir(caseId), 'dossiers')\n await mkdir(dossierDir, { recursive: true })\n const filePath = path.join(dossierDir, `${safeAddr}.md`)\n const now = new Date().toISOString()\n\n let raw: string\n let isNew = false\n try {\n raw = await readFile(filePath, 'utf8')\n } catch (err: unknown) {\n const nodeErr = err as NodeJS.ErrnoException\n if (nodeErr.code !== 'ENOENT') throw err\n // New dossier — create template\n const fm: Record<string, string> = {\n address,\n type: entityType,\n firstSeen: now,\n lastSeen: now,\n riskTags: '',\n }\n const summary = entityType === 'exchange'\n ? 'Exchange-labeled entity observed in this case.'\n : entityType === 'contract'\n ? 'Contract entity observed in this case.'\n : entityType === 'mixer'\n ? 'Mixer-labeled entity observed in this case.'\n : 'Address/entity observed in this case.'\n const body = `# Entity: ${address}\\n\\n## Summary\\n\\n${summary}\\n\\n## Findings\\n\\n`\n raw = serializeFrontmatter(fm, body)\n isNew = true\n }\n\n // Content-hash deduplication — skip if finding already present (text presence check)\n if (!isNew && raw.includes(finding)) {\n return\n }\n\n // Update frontmatter lastSeen\n const { frontmatter, body } = parseFrontmatter(raw)\n frontmatter['lastSeen'] = now\n if (!isNew) {\n frontmatter['type'] = entityType\n }\n\n // Append finding to ## Findings section\n const findingEntry = `- [${now}] ${finding}\\n`\n const updatedBody = body.replace('## Findings\\n', `## Findings\\n\\n${findingEntry}`)\n\n await writeFile(filePath, serializeFrontmatter(frontmatter, updatedBody), { mode: 0o600 })\n },\n\n async get(caseId: string, address: string): Promise<{ frontmatter: Record<string, string>; body: string } | null> {\n const safeAddr = sanitizeAddress(address)\n const filePath = path.join(caseDir(caseId), 'dossiers', `${safeAddr}.md`)\n try {\n const raw = await readFile(filePath, 'utf8')\n return parseFrontmatter(raw)\n } catch (err: unknown) {\n const nodeErr = err as NodeJS.ErrnoException\n if (nodeErr.code === 'ENOENT') return null\n throw err\n }\n },\n\n async listSummaries(caseId: string): Promise<Array<{ address: string; type: string; riskTags: string; firstSeen: string; lastSeen: string }>> {\n const dossierDir = path.join(caseDir(caseId), 'dossiers')\n try {\n const files = await readdir(dossierDir)\n const summaries = []\n for (const file of files.filter(f => f.endsWith('.md'))) {\n const raw = await readFile(path.join(dossierDir, file), 'utf8')\n const { frontmatter } = parseFrontmatter(raw)\n summaries.push({\n address: frontmatter['address'] ?? file.replace('.md', ''),\n type: frontmatter['type'] ?? 'unknown',\n riskTags: frontmatter['riskTags'] ?? '',\n firstSeen: frontmatter['firstSeen'] ?? '',\n lastSeen: frontmatter['lastSeen'] ?? '',\n })\n }\n return summaries\n } catch {\n return []\n }\n },\n}\n"],"mappings":";;;;;;AAMA,SAAS,QAAQ,QAAwB;AACvC,QAAO,KAAK,KAAK,sBAAsB,CAAC,WAAW,OAAO;;AAG5D,SAAS,gBAAgB,SAAyB;AAEhD,QAAO,QAAQ,QAAQ,iBAAiB,GAAG,CAAC,MAAM,GAAG,GAAG;;AAO1D,MAAa,eAAe;CAC1B,MAAM,cACJ,QACA,SACA,SACA,aAAoE,WACrD;EACf,MAAM,WAAW,gBAAgB,QAAQ;EACzC,MAAM,aAAa,KAAK,KAAK,QAAQ,OAAO,EAAE,WAAW;AACzD,QAAM,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;EAC5C,MAAM,WAAW,KAAK,KAAK,YAAY,GAAG,SAAS,KAAK;EACxD,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;EAEpC,IAAI;EACJ,IAAI,QAAQ;AACZ,MAAI;AACF,SAAM,MAAM,SAAS,UAAU,OAAO;WAC/B,KAAc;AAErB,OAAIA,IAAQ,SAAS,SAAU,OAAM;AAiBrC,SAAM,qBAAqB;IAdzB;IACA,MAAM;IACN,WAAW;IACX,UAAU;IACV,UAAU;IAUiB,EAAE,aADL,QAAQ,oBAPlB,eAAe,aAC3B,mDACA,eAAe,aACb,2CACA,eAAe,UACb,gDACA,wCACsD,qBAC1B;AACpC,WAAQ;;AAIV,MAAI,CAAC,SAAS,IAAI,SAAS,QAAQ,CACjC;EAIF,MAAM,EAAE,aAAa,SAAS,iBAAiB,IAAI;AACnD,cAAY,cAAc;AAC1B,MAAI,CAAC,MACH,aAAY,UAAU;EAIxB,MAAM,eAAe,MAAM,IAAI,IAAI,QAAQ;AAG3C,QAAM,UAAU,UAAU,qBAAqB,aAF3B,KAAK,QAAQ,iBAAiB,kBAAkB,eAEG,CAAC,EAAE,EAAE,MAAM,KAAO,CAAC;;CAG5F,MAAM,IAAI,QAAgB,SAAwF;EAChH,MAAM,WAAW,gBAAgB,QAAQ;EACzC,MAAM,WAAW,KAAK,KAAK,QAAQ,OAAO,EAAE,YAAY,GAAG,SAAS,KAAK;AACzE,MAAI;AAEF,UAAO,iBAAiB,MADN,SAAS,UAAU,OAAO,CAChB;WACrB,KAAc;AAErB,OAAIA,IAAQ,SAAS,SAAU,QAAO;AACtC,SAAM;;;CAIV,MAAM,cAAc,QAA0H;EAC5I,MAAM,aAAa,KAAK,KAAK,QAAQ,OAAO,EAAE,WAAW;AACzD,MAAI;GACF,MAAM,QAAQ,MAAM,QAAQ,WAAW;GACvC,MAAM,YAAY,EAAE;AACpB,QAAK,MAAM,QAAQ,MAAM,QAAO,MAAK,EAAE,SAAS,MAAM,CAAC,EAAE;IAEvD,MAAM,EAAE,gBAAgB,iBAAiB,MADvB,SAAS,KAAK,KAAK,YAAY,KAAK,EAAE,OAAO,CAClB;AAC7C,cAAU,KAAK;KACb,SAAS,YAAY,cAAc,KAAK,QAAQ,OAAO,GAAG;KAC1D,MAAM,YAAY,WAAW;KAC7B,UAAU,YAAY,eAAe;KACrC,WAAW,YAAY,gBAAgB;KACvC,UAAU,YAAY,eAAe;KACtC,CAAC;;AAEJ,UAAO;UACD;AACN,UAAO,EAAE;;;CAGd"}
1
+ {"version":3,"file":"dossier-Bl0NkJKC.mjs","names":["nodeErr"],"sources":["../src/cases/dossier.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { parseFrontmatter, serializeFrontmatter } from './frontmatter.js'\n\nfunction caseDir(caseId: string): string {\n return path.join(workspaceOutputPaths().casesRoot, caseId)\n}\n\nfunction sanitizeAddress(address: string): string {\n // Security T-03-06: prevent path traversal by stripping all non-alphanumeric chars\n return address.replace(/[^a-zA-Z0-9]/g, '').slice(0, 66)\n}\n\nfunction contentHash(text: string): string {\n return createHash('sha256').update(text).digest('hex')\n}\n\nexport const DossierStore = {\n async appendFinding(\n caseId: string,\n address: string,\n finding: string,\n entityType: 'eoa' | 'contract' | 'exchange' | 'mixer' | 'unknown' = 'unknown'\n ): Promise<void> {\n const safeAddr = sanitizeAddress(address)\n const dossierDir = path.join(caseDir(caseId), 'dossiers')\n await mkdir(dossierDir, { recursive: true })\n const filePath = path.join(dossierDir, `${safeAddr}.md`)\n const now = new Date().toISOString()\n\n let raw: string\n let isNew = false\n try {\n raw = await readFile(filePath, 'utf8')\n } catch (err: unknown) {\n const nodeErr = err as NodeJS.ErrnoException\n if (nodeErr.code !== 'ENOENT') throw err\n // New dossier — create template\n const fm: Record<string, string> = {\n address,\n type: entityType,\n firstSeen: now,\n lastSeen: now,\n riskTags: '',\n }\n const summary = entityType === 'exchange'\n ? 'Exchange-labeled entity observed in this case.'\n : entityType === 'contract'\n ? 'Contract entity observed in this case.'\n : entityType === 'mixer'\n ? 'Mixer-labeled entity observed in this case.'\n : 'Address/entity observed in this case.'\n const body = `# Entity: ${address}\\n\\n## Summary\\n\\n${summary}\\n\\n## Findings\\n\\n`\n raw = serializeFrontmatter(fm, body)\n isNew = true\n }\n\n // Content-hash deduplication — skip if finding already present (text presence check)\n if (!isNew && raw.includes(finding)) {\n return\n }\n\n // Update frontmatter lastSeen\n const { frontmatter, body } = parseFrontmatter(raw)\n frontmatter['lastSeen'] = now\n if (!isNew) {\n frontmatter['type'] = entityType\n }\n\n // Append finding to ## Findings section\n const findingEntry = `- [${now}] ${finding}\\n`\n const updatedBody = body.replace('## Findings\\n', `## Findings\\n\\n${findingEntry}`)\n\n await writeFile(filePath, serializeFrontmatter(frontmatter, updatedBody), { mode: 0o600 })\n },\n\n async get(caseId: string, address: string): Promise<{ frontmatter: Record<string, string>; body: string } | null> {\n const safeAddr = sanitizeAddress(address)\n const filePath = path.join(caseDir(caseId), 'dossiers', `${safeAddr}.md`)\n try {\n const raw = await readFile(filePath, 'utf8')\n return parseFrontmatter(raw)\n } catch (err: unknown) {\n const nodeErr = err as NodeJS.ErrnoException\n if (nodeErr.code === 'ENOENT') return null\n throw err\n }\n },\n\n async listSummaries(caseId: string): Promise<Array<{ address: string; type: string; riskTags: string; firstSeen: string; lastSeen: string }>> {\n const dossierDir = path.join(caseDir(caseId), 'dossiers')\n try {\n const files = await readdir(dossierDir)\n const summaries = []\n for (const file of files.filter(f => f.endsWith('.md'))) {\n const raw = await readFile(path.join(dossierDir, file), 'utf8')\n const { frontmatter } = parseFrontmatter(raw)\n summaries.push({\n address: frontmatter['address'] ?? file.replace('.md', ''),\n type: frontmatter['type'] ?? 'unknown',\n riskTags: frontmatter['riskTags'] ?? '',\n firstSeen: frontmatter['firstSeen'] ?? '',\n lastSeen: frontmatter['lastSeen'] ?? '',\n })\n }\n return summaries\n } catch {\n return []\n }\n },\n}\n"],"mappings":";;;;;;AAMA,SAAS,QAAQ,QAAwB;CACvC,OAAO,KAAK,KAAK,qBAAqB,EAAE,WAAW,MAAM;AAC3D;AAEA,SAAS,gBAAgB,SAAyB;CAEhD,OAAO,QAAQ,QAAQ,iBAAiB,EAAE,EAAE,MAAM,GAAG,EAAE;AACzD;AAMA,MAAa,eAAe;CAC1B,MAAM,cACJ,QACA,SACA,SACA,aAAoE,WACrD;EACf,MAAM,WAAW,gBAAgB,OAAO;EACxC,MAAM,aAAa,KAAK,KAAK,QAAQ,MAAM,GAAG,UAAU;EACxD,MAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;EAC3C,MAAM,WAAW,KAAK,KAAK,YAAY,GAAG,SAAS,IAAI;EACvD,MAAM,uBAAM,IAAI,KAAK,GAAE,YAAY;EAEnC,IAAI;EACJ,IAAI,QAAQ;EACZ,IAAI;GACF,MAAM,MAAM,SAAS,UAAU,MAAM;EACvC,SAAS,KAAc;GAErB,IAAIA,IAAQ,SAAS,UAAU,MAAM;GAiBrC,MAAM,qBAAqB;IAdzB;IACA,MAAM;IACN,WAAW;IACX,UAAU;IACV,UAAU;GAUgB,GAAG,aADL,QAAQ,oBAPlB,eAAe,aAC3B,mDACA,eAAe,aACb,2CACA,eAAe,UACb,gDACA,wCACsD,oBAC3B;GACnC,QAAQ;EACV;EAGA,IAAI,CAAC,SAAS,IAAI,SAAS,OAAO,GAChC;EAIF,MAAM,EAAE,aAAa,SAAS,iBAAiB,GAAG;EAClD,YAAY,cAAc;EAC1B,IAAI,CAAC,OACH,YAAY,UAAU;EAIxB,MAAM,eAAe,MAAM,IAAI,IAAI,QAAQ;EAG3C,MAAM,UAAU,UAAU,qBAAqB,aAF3B,KAAK,QAAQ,iBAAiB,kBAAkB,cAEE,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;CAC3F;CAEA,MAAM,IAAI,QAAgB,SAAwF;EAChH,MAAM,WAAW,gBAAgB,OAAO;EACxC,MAAM,WAAW,KAAK,KAAK,QAAQ,MAAM,GAAG,YAAY,GAAG,SAAS,IAAI;EACxE,IAAI;GAEF,OAAO,iBAAiB,MADN,SAAS,UAAU,MAAM,CAChB;EAC7B,SAAS,KAAc;GAErB,IAAIA,IAAQ,SAAS,UAAU,OAAO;GACtC,MAAM;EACR;CACF;CAEA,MAAM,cAAc,QAA0H;EAC5I,MAAM,aAAa,KAAK,KAAK,QAAQ,MAAM,GAAG,UAAU;EACxD,IAAI;GACF,MAAM,QAAQ,MAAM,QAAQ,UAAU;GACtC,MAAM,YAAY,CAAC;GACnB,KAAK,MAAM,QAAQ,MAAM,QAAO,MAAK,EAAE,SAAS,KAAK,CAAC,GAAG;IAEvD,MAAM,EAAE,gBAAgB,iBAAiB,MADvB,SAAS,KAAK,KAAK,YAAY,IAAI,GAAG,MAAM,CAClB;IAC5C,UAAU,KAAK;KACb,SAAS,YAAY,cAAc,KAAK,QAAQ,OAAO,EAAE;KACzD,MAAM,YAAY,WAAW;KAC7B,UAAU,YAAY,eAAe;KACrC,WAAW,YAAY,gBAAgB;KACvC,UAAU,YAAY,eAAe;IACvC,CAAC;GACH;GACA,OAAO;EACT,QAAQ;GACN,OAAO,CAAC;EACV;CACF;AACF"}
@@ -1,6 +1,6 @@
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");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_frontmatter = require("./frontmatter-Dvqa5HX6.cjs");
3
+ const require_output_root = require("./output-root-YIbl6PwF.cjs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path, 1);
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -1,6 +1,6 @@
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");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_frontmatter = require("./frontmatter-Dvqa5HX6.cjs");
3
+ const require_output_root = require("./output-root-YIbl6PwF.cjs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path, 1);
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -1,5 +1,5 @@
1
- import { n as serializeFrontmatter } from "./frontmatter-D8wWCeOa.mjs";
2
- import { n as workspaceOutputPaths } from "./output-root-CmWM7aV2.mjs";
1
+ import { n as serializeFrontmatter } from "./frontmatter-D0ccQnUM.mjs";
2
+ import { n as workspaceOutputPaths } from "./output-root-BRhzhhXZ.mjs";
3
3
  import path from "node:path";
4
4
  import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
5
5
  import { createHash } from "node:crypto";
@@ -192,4 +192,4 @@ const EvidenceStore = {
192
192
  //#endregion
193
193
  export { EvidenceStore as t };
194
194
 
195
- //# sourceMappingURL=evidence-BhvFW-y_.mjs.map
195
+ //# sourceMappingURL=evidence-D96PTzOQ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"evidence-BhvFW-y_.mjs","names":["e"],"sources":["../src/cases/evidence.ts"],"sourcesContent":["import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { serializeFrontmatter } from './frontmatter.js'\n\nconst MAX_INLINE_JSON_BYTES = 8 * 1024\n\nfunction caseDir(caseId: string): string {\n return path.join(workspaceOutputPaths().casesRoot, caseId)\n}\n\nfunction sanitizeSource(source: string): string {\n return source.replace(/[^a-z0-9_-]/gi, '').slice(0, 40)\n}\n\nfunction formatTimestamp(): string {\n // Returns timestamp like 20260511T142300 (no colons, no dots)\n return new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}/, '').slice(0, 15)\n}\n\nfunction parseJsonContent(content: string): unknown | null {\n const trimmed = content.trim()\n if (trimmed.startsWith('```')) return null\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return null\n try {\n return JSON.parse(trimmed)\n } catch {\n return null\n }\n}\n\nfunction compactJsonValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(compactJsonValue)\n if (!value || typeof value !== 'object') return value\n\n const compact: Record<string, unknown> = {}\n for (const [key, entry] of Object.entries(value)) {\n if (entry === null || entry === undefined) continue\n compact[key] = compactJsonValue(entry)\n }\n return compact\n}\n\nfunction summarizeJsonValue(value: unknown): Record<string, unknown> {\n if (Array.isArray(value)) {\n return {\n kind: 'array',\n count: value.length,\n sample: value.slice(0, 3).map(compactJsonValue),\n }\n }\n\n if (!value || typeof value !== 'object') {\n return { kind: typeof value, value }\n }\n\n const record = compactJsonValue(value) as Record<string, unknown>\n const summary: Record<string, unknown> = {\n kind: 'object',\n keys: Object.keys(record).slice(0, 50),\n }\n for (const key of ['schema', 'source', 'tool', 'network', 'seed_address', 'address']) {\n if (typeof record[key] === 'string') summary[key] = record[key]\n }\n for (const key of ['files', 'outputs', 'facts']) {\n const entry = record[key]\n if (entry && typeof entry === 'object' && !Array.isArray(entry)) summary[key] = compactJsonValue(entry)\n }\n const counts = Object.fromEntries(\n Object.entries(record)\n .filter(([, entry]) => Array.isArray(entry))\n .map(([key, entry]) => [key, (entry as unknown[]).length]),\n )\n if (Object.keys(counts).length > 0) summary['array_counts'] = counts\n return summary\n}\n\nasync function formatEvidenceContent(\n evidenceId: string,\n source: string,\n timestamp: string,\n content: string,\n): Promise<string> {\n const parsedJson = parseJsonContent(content)\n if (parsedJson === null) return content\n\n const compactJson = compactJsonValue(parsedJson)\n const prettyJson = JSON.stringify(compactJson, null, 2)\n if (Buffer.byteLength(prettyJson, 'utf8') <= MAX_INLINE_JSON_BYTES) {\n return `\\`\\`\\`json\\n${prettyJson}\\n\\`\\`\\``\n }\n\n const paths = workspaceOutputPaths()\n await mkdir(paths.reportTablesRoot, { recursive: true, mode: 0o700 })\n const safeSource = sanitizeSource(source) || 'evidence'\n const tableFilename = `${evidenceId}_${safeSource}_${timestamp}_${Math.random().toString(36).slice(2, 8)}.json`\n const tablePath = path.join(paths.reportTablesRoot, tableFilename)\n await writeFile(tablePath, prettyJson + '\\n', { mode: 0o600, flag: 'wx' })\n const relativeTablePath = path.relative(paths.root, tablePath)\n const summary = {\n schema: 'chain-insights.evidence_summary.v1',\n omitted_inline_json: true,\n stored_json: relativeTablePath,\n summary: summarizeJsonValue(compactJson),\n }\n return [\n 'Large JSON evidence was stored as an analyst table extract instead of inline Markdown.',\n '',\n `Stored JSON: \\`${relativeTablePath}\\``,\n '',\n '```json',\n JSON.stringify(summary, null, 2),\n '```',\n ].join('\\n')\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex')\n}\n\nasync function appendToManifest(\n manifestPath: string,\n entry: { file: string; sha256: string }\n): Promise<void> {\n const existing = JSON.parse(\n await readFile(manifestPath, 'utf8').catch(() => '{\"entries\":[]}')\n ) as { caseId?: string; entries: Array<{ file: string; sha256: string }> }\n existing.entries.push(entry)\n await writeFile(manifestPath, JSON.stringify(existing, null, 2) + '\\n', { mode: 0o600 })\n}\n\nexport const EvidenceStore = {\n async append(\n caseId: string,\n input: { source: string; content: string; queryParams: string }\n ): Promise<{ filename: string; sha256: string }> {\n const dir = caseDir(caseId)\n const evidenceDir = path.join(dir, 'evidence')\n await mkdir(evidenceDir, { recursive: true })\n const safeSource = sanitizeSource(input.source)\n const timestamp = formatTimestamp()\n\n // Determine sequence number\n let seq = 1\n try {\n const files = await readdir(evidenceDir)\n const evidenceFiles = files.filter(f => f.endsWith('.md'))\n seq = evidenceFiles.length + 1\n } catch {\n seq = 1\n }\n const seqStr = String(seq).padStart(3, '0')\n let filename = `${seqStr}_${safeSource}_${timestamp}.md`\n\n // Build file content\n const now = new Date().toISOString()\n const fm: Record<string, string> = {\n id: `${caseId}_ev${seqStr}`,\n caseId,\n source: input.source,\n timestamp: now,\n queryParams: input.queryParams,\n }\n const evidenceId = `${caseId}_ev${seqStr}`\n const formattedContent = await formatEvidenceContent(evidenceId, input.source, timestamp, input.content)\n const body = [\n `## Evidence: ${input.source}`,\n '',\n `**Source:** ${input.source}`,\n `**Captured:** ${now}`,\n '',\n formattedContent,\n '',\n ].join('\\n')\n const fileContent = serializeFrontmatter(fm, body)\n\n // Write with exclusive flag to prevent sequence collision (Pitfall 4)\n const filePath = path.join(evidenceDir, filename)\n try {\n await writeFile(filePath, fileContent, { mode: 0o600, flag: 'wx' })\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException\n if (e.code === 'EEXIST') {\n // Retry with timestamp-unique suffix\n filename = `${seqStr}_${safeSource}_${timestamp}_${Math.random().toString(36).slice(2, 6)}.md`\n await writeFile(path.join(evidenceDir, filename), fileContent, { mode: 0o600, flag: 'wx' })\n } else {\n throw err\n }\n }\n\n // Compute SHA-256 of written content and append to manifest\n const sha256 = hashContent(fileContent)\n await appendToManifest(path.join(dir, 'manifest.json'), { file: filename, sha256 })\n\n return { filename, sha256 }\n },\n\n async verifyManifest(caseId: string): Promise<{ ok: boolean; count: number; tampered?: string[] }> {\n const dir = caseDir(caseId)\n const manifestPath = path.join(dir, 'manifest.json')\n const manifest = JSON.parse(\n await readFile(manifestPath, 'utf8').catch(() => '{\"entries\":[]}')\n ) as { entries: Array<{ file: string; sha256: string }> }\n\n const tampered: string[] = []\n for (const entry of manifest.entries) {\n const filePath = path.join(dir, 'evidence', entry.file)\n try {\n const content = await readFile(filePath, 'utf8')\n const actual = hashContent(content)\n if (actual !== entry.sha256) {\n tampered.push(entry.file)\n }\n } catch {\n tampered.push(entry.file) // File missing = tampered\n }\n }\n\n return {\n ok: tampered.length === 0,\n count: manifest.entries.length,\n ...(tampered.length > 0 ? { tampered } : {}),\n }\n },\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"evidence-D96PTzOQ.mjs","names":["e"],"sources":["../src/cases/evidence.ts"],"sourcesContent":["import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises'\nimport { createHash } from 'node:crypto'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { serializeFrontmatter } from './frontmatter.js'\n\nconst MAX_INLINE_JSON_BYTES = 8 * 1024\n\nfunction caseDir(caseId: string): string {\n return path.join(workspaceOutputPaths().casesRoot, caseId)\n}\n\nfunction sanitizeSource(source: string): string {\n return source.replace(/[^a-z0-9_-]/gi, '').slice(0, 40)\n}\n\nfunction formatTimestamp(): string {\n // Returns timestamp like 20260511T142300 (no colons, no dots)\n return new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}/, '').slice(0, 15)\n}\n\nfunction parseJsonContent(content: string): unknown | null {\n const trimmed = content.trim()\n if (trimmed.startsWith('```')) return null\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return null\n try {\n return JSON.parse(trimmed)\n } catch {\n return null\n }\n}\n\nfunction compactJsonValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(compactJsonValue)\n if (!value || typeof value !== 'object') return value\n\n const compact: Record<string, unknown> = {}\n for (const [key, entry] of Object.entries(value)) {\n if (entry === null || entry === undefined) continue\n compact[key] = compactJsonValue(entry)\n }\n return compact\n}\n\nfunction summarizeJsonValue(value: unknown): Record<string, unknown> {\n if (Array.isArray(value)) {\n return {\n kind: 'array',\n count: value.length,\n sample: value.slice(0, 3).map(compactJsonValue),\n }\n }\n\n if (!value || typeof value !== 'object') {\n return { kind: typeof value, value }\n }\n\n const record = compactJsonValue(value) as Record<string, unknown>\n const summary: Record<string, unknown> = {\n kind: 'object',\n keys: Object.keys(record).slice(0, 50),\n }\n for (const key of ['schema', 'source', 'tool', 'network', 'seed_address', 'address']) {\n if (typeof record[key] === 'string') summary[key] = record[key]\n }\n for (const key of ['files', 'outputs', 'facts']) {\n const entry = record[key]\n if (entry && typeof entry === 'object' && !Array.isArray(entry)) summary[key] = compactJsonValue(entry)\n }\n const counts = Object.fromEntries(\n Object.entries(record)\n .filter(([, entry]) => Array.isArray(entry))\n .map(([key, entry]) => [key, (entry as unknown[]).length]),\n )\n if (Object.keys(counts).length > 0) summary['array_counts'] = counts\n return summary\n}\n\nasync function formatEvidenceContent(\n evidenceId: string,\n source: string,\n timestamp: string,\n content: string,\n): Promise<string> {\n const parsedJson = parseJsonContent(content)\n if (parsedJson === null) return content\n\n const compactJson = compactJsonValue(parsedJson)\n const prettyJson = JSON.stringify(compactJson, null, 2)\n if (Buffer.byteLength(prettyJson, 'utf8') <= MAX_INLINE_JSON_BYTES) {\n return `\\`\\`\\`json\\n${prettyJson}\\n\\`\\`\\``\n }\n\n const paths = workspaceOutputPaths()\n await mkdir(paths.reportTablesRoot, { recursive: true, mode: 0o700 })\n const safeSource = sanitizeSource(source) || 'evidence'\n const tableFilename = `${evidenceId}_${safeSource}_${timestamp}_${Math.random().toString(36).slice(2, 8)}.json`\n const tablePath = path.join(paths.reportTablesRoot, tableFilename)\n await writeFile(tablePath, prettyJson + '\\n', { mode: 0o600, flag: 'wx' })\n const relativeTablePath = path.relative(paths.root, tablePath)\n const summary = {\n schema: 'chain-insights.evidence_summary.v1',\n omitted_inline_json: true,\n stored_json: relativeTablePath,\n summary: summarizeJsonValue(compactJson),\n }\n return [\n 'Large JSON evidence was stored as an analyst table extract instead of inline Markdown.',\n '',\n `Stored JSON: \\`${relativeTablePath}\\``,\n '',\n '```json',\n JSON.stringify(summary, null, 2),\n '```',\n ].join('\\n')\n}\n\nexport function hashContent(content: string): string {\n return createHash('sha256').update(content).digest('hex')\n}\n\nasync function appendToManifest(\n manifestPath: string,\n entry: { file: string; sha256: string }\n): Promise<void> {\n const existing = JSON.parse(\n await readFile(manifestPath, 'utf8').catch(() => '{\"entries\":[]}')\n ) as { caseId?: string; entries: Array<{ file: string; sha256: string }> }\n existing.entries.push(entry)\n await writeFile(manifestPath, JSON.stringify(existing, null, 2) + '\\n', { mode: 0o600 })\n}\n\nexport const EvidenceStore = {\n async append(\n caseId: string,\n input: { source: string; content: string; queryParams: string }\n ): Promise<{ filename: string; sha256: string }> {\n const dir = caseDir(caseId)\n const evidenceDir = path.join(dir, 'evidence')\n await mkdir(evidenceDir, { recursive: true })\n const safeSource = sanitizeSource(input.source)\n const timestamp = formatTimestamp()\n\n // Determine sequence number\n let seq = 1\n try {\n const files = await readdir(evidenceDir)\n const evidenceFiles = files.filter(f => f.endsWith('.md'))\n seq = evidenceFiles.length + 1\n } catch {\n seq = 1\n }\n const seqStr = String(seq).padStart(3, '0')\n let filename = `${seqStr}_${safeSource}_${timestamp}.md`\n\n // Build file content\n const now = new Date().toISOString()\n const fm: Record<string, string> = {\n id: `${caseId}_ev${seqStr}`,\n caseId,\n source: input.source,\n timestamp: now,\n queryParams: input.queryParams,\n }\n const evidenceId = `${caseId}_ev${seqStr}`\n const formattedContent = await formatEvidenceContent(evidenceId, input.source, timestamp, input.content)\n const body = [\n `## Evidence: ${input.source}`,\n '',\n `**Source:** ${input.source}`,\n `**Captured:** ${now}`,\n '',\n formattedContent,\n '',\n ].join('\\n')\n const fileContent = serializeFrontmatter(fm, body)\n\n // Write with exclusive flag to prevent sequence collision (Pitfall 4)\n const filePath = path.join(evidenceDir, filename)\n try {\n await writeFile(filePath, fileContent, { mode: 0o600, flag: 'wx' })\n } catch (err: unknown) {\n const e = err as NodeJS.ErrnoException\n if (e.code === 'EEXIST') {\n // Retry with timestamp-unique suffix\n filename = `${seqStr}_${safeSource}_${timestamp}_${Math.random().toString(36).slice(2, 6)}.md`\n await writeFile(path.join(evidenceDir, filename), fileContent, { mode: 0o600, flag: 'wx' })\n } else {\n throw err\n }\n }\n\n // Compute SHA-256 of written content and append to manifest\n const sha256 = hashContent(fileContent)\n await appendToManifest(path.join(dir, 'manifest.json'), { file: filename, sha256 })\n\n return { filename, sha256 }\n },\n\n async verifyManifest(caseId: string): Promise<{ ok: boolean; count: number; tampered?: string[] }> {\n const dir = caseDir(caseId)\n const manifestPath = path.join(dir, 'manifest.json')\n const manifest = JSON.parse(\n await readFile(manifestPath, 'utf8').catch(() => '{\"entries\":[]}')\n ) as { entries: Array<{ file: string; sha256: string }> }\n\n const tampered: string[] = []\n for (const entry of manifest.entries) {\n const filePath = path.join(dir, 'evidence', entry.file)\n try {\n const content = await readFile(filePath, 'utf8')\n const actual = hashContent(content)\n if (actual !== entry.sha256) {\n tampered.push(entry.file)\n }\n } catch {\n tampered.push(entry.file) // File missing = tampered\n }\n }\n\n return {\n ok: tampered.length === 0,\n count: manifest.entries.length,\n ...(tampered.length > 0 ? { tampered } : {}),\n }\n },\n}\n"],"mappings":"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"}
@@ -19,4 +19,4 @@ function formatToolsTable(tools) {
19
19
  //#endregion
20
20
  export { formatToolsTable };
21
21
 
22
- //# sourceMappingURL=format-Ce1RObVl.mjs.map
22
+ //# sourceMappingURL=format-Bq94jSyw.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-Ce1RObVl.mjs","names":[],"sources":["../src/mcp/format.ts"],"sourcesContent":["import type { McpTool } from './schema-cache.js'\n\nconst NAME_WIDTH = 30\nconst DESC_MAX = 60\n\n/**\n * Formats an array of MCP tools as a plain text table string.\n * Returns \"No tools available.\" for an empty array.\n * Caller controls output — use console.log(formatToolsTable(tools)).\n */\nexport function formatToolsTable(tools: McpTool[]): string {\n if (tools.length === 0) return 'No tools available.'\n const header = `${'Tool'.padEnd(NAME_WIDTH)} Description`\n const divider = '-'.repeat(NAME_WIDTH) + ' ' + '-'.repeat(DESC_MAX)\n const rows = tools.map((t) => {\n const name = t.name.padEnd(NAME_WIDTH)\n const desc = (t.description ?? '').slice(0, DESC_MAX)\n return `${name} ${desc}`\n })\n return [header, divider, ...rows].join('\\n')\n}\n"],"mappings":";AAEA,MAAM,aAAa;AACnB,MAAM,WAAW;;;;;;AAOjB,SAAgB,iBAAiB,OAA0B;AACzD,KAAI,MAAM,WAAW,EAAG,QAAO;AAQ/B,QAAO;EAAC,GAPU,OAAO,OAAO,WAAW,CAAC;EAC5B,IAAI,OAAO,WAAW,GAAG,OAAO,IAAI,OAAO,SAAS;EAM3C,GALZ,MAAM,KAAK,MAAM;AAG5B,UAAO,GAFM,EAAE,KAAK,OAAO,WAEb,CAAC,KADD,EAAE,eAAe,IAAI,MAAM,GAAG,SACrB;IAEO;EAAC,CAAC,KAAK,KAAK"}
1
+ {"version":3,"file":"format-Bq94jSyw.mjs","names":[],"sources":["../src/mcp/format.ts"],"sourcesContent":["import type { McpTool } from './schema-cache.js'\n\nconst NAME_WIDTH = 30\nconst DESC_MAX = 60\n\n/**\n * Formats an array of MCP tools as a plain text table string.\n * Returns \"No tools available.\" for an empty array.\n * Caller controls output — use console.log(formatToolsTable(tools)).\n */\nexport function formatToolsTable(tools: McpTool[]): string {\n if (tools.length === 0) return 'No tools available.'\n const header = `${'Tool'.padEnd(NAME_WIDTH)} Description`\n const divider = '-'.repeat(NAME_WIDTH) + ' ' + '-'.repeat(DESC_MAX)\n const rows = tools.map((t) => {\n const name = t.name.padEnd(NAME_WIDTH)\n const desc = (t.description ?? '').slice(0, DESC_MAX)\n return `${name} ${desc}`\n })\n return [header, divider, ...rows].join('\\n')\n}\n"],"mappings":";AAEA,MAAM,aAAa;AACnB,MAAM,WAAW;;;;;;AAOjB,SAAgB,iBAAiB,OAA0B;CACzD,IAAI,MAAM,WAAW,GAAG,OAAO;CAQ/B,OAAO;EAAC,GAPU,OAAO,OAAO,UAAU,EAAE;EAC5B,IAAI,OAAO,UAAU,IAAI,OAAO,IAAI,OAAO,QAAQ;EAM1C,GALZ,MAAM,KAAK,MAAM;GAG5B,OAAO,GAFM,EAAE,KAAK,OAAO,UAEd,EAAE,KADD,EAAE,eAAe,IAAI,MAAM,GAAG,QACtB;EACxB,CAC+B;CAAC,EAAE,KAAK,IAAI;AAC7C"}
@@ -23,4 +23,4 @@ function serializeFrontmatter(fm, body) {
23
23
  //#endregion
24
24
  export { serializeFrontmatter as n, parseFrontmatter as t };
25
25
 
26
- //# sourceMappingURL=frontmatter-D8wWCeOa.mjs.map
26
+ //# sourceMappingURL=frontmatter-D0ccQnUM.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter-D8wWCeOa.mjs","names":[],"sources":["../src/cases/frontmatter.ts"],"sourcesContent":["// Hand-rolled YAML frontmatter parser — GSD pattern (no gray-matter dependency).\n// Only supports flat key: value pairs. Arrays must be stored as comma-separated strings.\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/\n\nexport function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {\n const m = content.match(FRONTMATTER_RE)\n if (!m) return { frontmatter: {}, body: content }\n const fm: Record<string, string> = {}\n for (const line of m[1].split('\\n')) {\n const colon = line.indexOf(':')\n if (colon < 0) continue\n fm[line.slice(0, colon).trim()] = line.slice(colon + 1).trim()\n }\n return { frontmatter: fm, body: m[2] }\n}\n\nexport function serializeFrontmatter(fm: Record<string, string>, body: string): string {\n const lines = Object.entries(fm).map(([k, v]) => `${k}: ${v}`).join('\\n')\n return `---\\n${lines}\\n---\\n${body}`\n}\n"],"mappings":";AAEA,MAAM,iBAAiB;AAEvB,SAAgB,iBAAiB,SAAwE;CACvG,MAAM,IAAI,QAAQ,MAAM,eAAe;AACvC,KAAI,CAAC,EAAG,QAAO;EAAE,aAAa,EAAE;EAAE,MAAM;EAAS;CACjD,MAAM,KAA6B,EAAE;AACrC,MAAK,MAAM,QAAQ,EAAE,GAAG,MAAM,KAAK,EAAE;EACnC,MAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,MAAI,QAAQ,EAAG;AACf,KAAG,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,MAAM,QAAQ,EAAE,CAAC,MAAM;;AAEhE,QAAO;EAAE,aAAa;EAAI,MAAM,EAAE;EAAI;;AAGxC,SAAgB,qBAAqB,IAA4B,MAAsB;AAErF,QAAO,QADO,OAAO,QAAQ,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,KAChD,CAAC,SAAS"}
1
+ {"version":3,"file":"frontmatter-D0ccQnUM.mjs","names":[],"sources":["../src/cases/frontmatter.ts"],"sourcesContent":["// Hand-rolled YAML frontmatter parser — GSD pattern (no gray-matter dependency).\n// Only supports flat key: value pairs. Arrays must be stored as comma-separated strings.\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/\n\nexport function parseFrontmatter(content: string): { frontmatter: Record<string, string>; body: string } {\n const m = content.match(FRONTMATTER_RE)\n if (!m) return { frontmatter: {}, body: content }\n const fm: Record<string, string> = {}\n for (const line of m[1].split('\\n')) {\n const colon = line.indexOf(':')\n if (colon < 0) continue\n fm[line.slice(0, colon).trim()] = line.slice(colon + 1).trim()\n }\n return { frontmatter: fm, body: m[2] }\n}\n\nexport function serializeFrontmatter(fm: Record<string, string>, body: string): string {\n const lines = Object.entries(fm).map(([k, v]) => `${k}: ${v}`).join('\\n')\n return `---\\n${lines}\\n---\\n${body}`\n}\n"],"mappings":";AAEA,MAAM,iBAAiB;AAEvB,SAAgB,iBAAiB,SAAwE;CACvG,MAAM,IAAI,QAAQ,MAAM,cAAc;CACtC,IAAI,CAAC,GAAG,OAAO;EAAE,aAAa,CAAC;EAAG,MAAM;CAAQ;CAChD,MAAM,KAA6B,CAAC;CACpC,KAAK,MAAM,QAAQ,EAAE,GAAG,MAAM,IAAI,GAAG;EACnC,MAAM,QAAQ,KAAK,QAAQ,GAAG;EAC9B,IAAI,QAAQ,GAAG;EACf,GAAG,KAAK,MAAM,GAAG,KAAK,EAAE,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAK;CAC/D;CACA,OAAO;EAAE,aAAa;EAAI,MAAM,EAAE;CAAG;AACvC;AAEA,SAAgB,qBAAqB,IAA4B,MAAsB;CAErF,OAAO,QADO,OAAO,QAAQ,EAAE,EAAE,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,IAAI,GAAG,EAAE,KAAK,IACjD,EAAE,SAAS;AAChC"}
@@ -127,4 +127,4 @@ function normalizeGraphPayload(payload) {
127
127
  //#endregion
128
128
  export { normalizeGraphPayload as t };
129
129
 
130
- //# sourceMappingURL=graph-normalizer-Cv9yK9Pg.mjs.map
130
+ //# sourceMappingURL=graph-normalizer-CXP06jKh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-normalizer-Cv9yK9Pg.mjs","names":[],"sources":["../src/viz/graph-normalizer.ts"],"sourcesContent":["const GRAPH_TYPE_LABELS = new Set(['Address', 'Exchange', 'Miner', 'Validator', 'Hotkey', 'Subnet', 'IPAddress'])\nconst SOURCE_ADDRESS_TYPES = new Set(['substrate', 'evm'])\nconst FLOW_EDGE_TYPES = new Set(['flows_to', 'transfer', 'transfer_to', 'transfers_to'])\n\ntype GraphRecord = Record<string, unknown>\n\nexport type NormalizedGraphPayload = {\n schema: 'chain-insights.graph.v1'\n nodes: GraphRecord[]\n edges: GraphRecord[]\n flows: unknown[]\n edge_anchors: unknown[]\n [key: string]: unknown\n}\n\nfunction isRecord(value: unknown): value is GraphRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.map(String).filter(Boolean) : []\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)]\n}\n\nfunction lowerSnake(value: string): string {\n return value\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[^A-Za-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .toLowerCase()\n}\n\nfunction isGraphTypeLabel(label: string): boolean {\n return GRAPH_TYPE_LABELS.has(label)\n}\n\nfunction displayLabels(node: GraphRecord): string[] {\n return unique(stringArray(node['labels'])).filter((label) => !isGraphTypeLabel(label))\n}\n\nfunction systemLabels(node: GraphRecord): string[] {\n return unique([\n ...stringArray(node['system_labels']),\n ...stringArray(node['graph_labels']),\n ...stringArray(node['raw_labels']),\n ...stringArray(node['labels']).filter(isGraphTypeLabel),\n ])\n}\n\nfunction normalizeRoles(node: GraphRecord): string[] {\n const roles = stringArray(node['roles'])\n const role = typeof node['role'] === 'string' ? node['role'] : ''\n if (role === 'source_exchange') roles.push('exchange')\n else if (role) roles.push(role)\n\n const display = stringArray(node['labels']).map(lowerSnake)\n if (systemLabels(node).some((label) => lowerSnake(label) === 'exchange') || display.includes('exchange')) {\n roles.push('exchange')\n }\n\n return unique(roles.map(lowerSnake).filter(Boolean))\n}\n\nfunction normalizeNodeType(node: GraphRecord): string {\n if (typeof node['node_type'] === 'string' && node['node_type'].trim()) {\n return lowerSnake(node['node_type'])\n }\n if (typeof node['address'] === 'string' || typeof node['id'] === 'string') return 'address'\n const labels = systemLabels(node)\n if (labels.length > 0) return lowerSnake(labels[0]!)\n return 'unknown'\n}\n\nfunction normalizeNode(node: unknown): GraphRecord {\n if (!isRecord(node)) return {}\n\n const normalized: GraphRecord = {}\n for (const [key, value] of Object.entries(node)) {\n if (\n [\n 'address_type',\n 'address_subtypes',\n 'entity_kind',\n 'graph_labels',\n 'labels',\n 'node_type',\n 'pattern_flags',\n 'raw_labels',\n 'role',\n 'roles',\n 'system_labels',\n 'type',\n ].includes(key)\n ) {\n continue\n }\n normalized[key] = value\n }\n\n const id = typeof node['id'] === 'string' ? node['id'] : typeof node['address'] === 'string' ? node['address'] : undefined\n if (id) normalized['id'] = id\n\n const address = typeof node['address'] === 'string' ? node['address'] : id\n if (address) normalized['address'] = address\n\n normalized['node_type'] = normalizeNodeType(node)\n normalized['labels'] = displayLabels(node)\n\n if (typeof node['address_type'] === 'string' && SOURCE_ADDRESS_TYPES.has(node['address_type'])) {\n normalized['address_type'] = node['address_type']\n }\n\n const addressSubtypes = unique(stringArray(node['address_subtypes']))\n if (addressSubtypes.length > 0) normalized['address_subtypes'] = addressSubtypes\n\n const roles = normalizeRoles(node)\n if (roles.length > 0) normalized['roles'] = roles\n\n if (!Array.isArray(node['flags']) && Array.isArray(node['pattern_flags']) && node['pattern_flags'].length > 0) {\n normalized['flags'] = node['pattern_flags'].map(String)\n }\n\n return normalized\n}\n\nfunction normalizeEdgeType(edge: GraphRecord): string {\n const rawType =\n typeof edge['edge_type'] === 'string' && edge['edge_type'].trim()\n ? edge['edge_type']\n : typeof edge['type'] === 'string' && edge['type'].trim()\n ? edge['type']\n : typeof edge['relationship_type'] === 'string' && edge['relationship_type'].trim()\n ? edge['relationship_type']\n : 'related_to'\n const edgeType = lowerSnake(rawType)\n return FLOW_EDGE_TYPES.has(edgeType) ? 'flows_to' : edgeType\n}\n\nfunction normalizeEdge(edge: unknown): GraphRecord {\n if (!isRecord(edge)) return {}\n\n const normalized: GraphRecord = {}\n for (const [key, value] of Object.entries(edge)) {\n if (['edge_type', 'from_address', 'relationship_type', 'to_address', 'type'].includes(key)) continue\n normalized[key] = value\n }\n\n if (typeof normalized['source'] !== 'string' && typeof edge['from_address'] === 'string') {\n normalized['source'] = edge['from_address']\n }\n if (typeof normalized['target'] !== 'string' && typeof edge['to_address'] === 'string') {\n normalized['target'] = edge['to_address']\n }\n normalized['edge_type'] = normalizeEdgeType(edge)\n\n return normalized\n}\n\nexport function normalizeGraphPayload(payload: unknown): NormalizedGraphPayload {\n if (!isRecord(payload) || payload['schema'] !== 'chain-insights.graph.v1') {\n throw new Error('Unsupported graph payload schema')\n }\n\n return {\n ...payload,\n schema: 'chain-insights.graph.v1',\n nodes: Array.isArray(payload['nodes']) ? payload['nodes'].map(normalizeNode) : [],\n edges: Array.isArray(payload['edges']) ? payload['edges'].map(normalizeEdge) : [],\n flows: Array.isArray(payload['flows']) ? payload['flows'] : [],\n edge_anchors: Array.isArray(payload['edge_anchors']) ? payload['edge_anchors'] : [],\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"graph-normalizer-CXP06jKh.mjs","names":[],"sources":["../src/viz/graph-normalizer.ts"],"sourcesContent":["const GRAPH_TYPE_LABELS = new Set(['Address', 'Exchange', 'Miner', 'Validator', 'Hotkey', 'Subnet', 'IPAddress'])\nconst SOURCE_ADDRESS_TYPES = new Set(['substrate', 'evm'])\nconst FLOW_EDGE_TYPES = new Set(['flows_to', 'transfer', 'transfer_to', 'transfers_to'])\n\ntype GraphRecord = Record<string, unknown>\n\nexport type NormalizedGraphPayload = {\n schema: 'chain-insights.graph.v1'\n nodes: GraphRecord[]\n edges: GraphRecord[]\n flows: unknown[]\n edge_anchors: unknown[]\n [key: string]: unknown\n}\n\nfunction isRecord(value: unknown): value is GraphRecord {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction stringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.map(String).filter(Boolean) : []\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)]\n}\n\nfunction lowerSnake(value: string): string {\n return value\n .trim()\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[^A-Za-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .toLowerCase()\n}\n\nfunction isGraphTypeLabel(label: string): boolean {\n return GRAPH_TYPE_LABELS.has(label)\n}\n\nfunction displayLabels(node: GraphRecord): string[] {\n return unique(stringArray(node['labels'])).filter((label) => !isGraphTypeLabel(label))\n}\n\nfunction systemLabels(node: GraphRecord): string[] {\n return unique([\n ...stringArray(node['system_labels']),\n ...stringArray(node['graph_labels']),\n ...stringArray(node['raw_labels']),\n ...stringArray(node['labels']).filter(isGraphTypeLabel),\n ])\n}\n\nfunction normalizeRoles(node: GraphRecord): string[] {\n const roles = stringArray(node['roles'])\n const role = typeof node['role'] === 'string' ? node['role'] : ''\n if (role === 'source_exchange') roles.push('exchange')\n else if (role) roles.push(role)\n\n const display = stringArray(node['labels']).map(lowerSnake)\n if (systemLabels(node).some((label) => lowerSnake(label) === 'exchange') || display.includes('exchange')) {\n roles.push('exchange')\n }\n\n return unique(roles.map(lowerSnake).filter(Boolean))\n}\n\nfunction normalizeNodeType(node: GraphRecord): string {\n if (typeof node['node_type'] === 'string' && node['node_type'].trim()) {\n return lowerSnake(node['node_type'])\n }\n if (typeof node['address'] === 'string' || typeof node['id'] === 'string') return 'address'\n const labels = systemLabels(node)\n if (labels.length > 0) return lowerSnake(labels[0]!)\n return 'unknown'\n}\n\nfunction normalizeNode(node: unknown): GraphRecord {\n if (!isRecord(node)) return {}\n\n const normalized: GraphRecord = {}\n for (const [key, value] of Object.entries(node)) {\n if (\n [\n 'address_type',\n 'address_subtypes',\n 'entity_kind',\n 'graph_labels',\n 'labels',\n 'node_type',\n 'pattern_flags',\n 'raw_labels',\n 'role',\n 'roles',\n 'system_labels',\n 'type',\n ].includes(key)\n ) {\n continue\n }\n normalized[key] = value\n }\n\n const id = typeof node['id'] === 'string' ? node['id'] : typeof node['address'] === 'string' ? node['address'] : undefined\n if (id) normalized['id'] = id\n\n const address = typeof node['address'] === 'string' ? node['address'] : id\n if (address) normalized['address'] = address\n\n normalized['node_type'] = normalizeNodeType(node)\n normalized['labels'] = displayLabels(node)\n\n if (typeof node['address_type'] === 'string' && SOURCE_ADDRESS_TYPES.has(node['address_type'])) {\n normalized['address_type'] = node['address_type']\n }\n\n const addressSubtypes = unique(stringArray(node['address_subtypes']))\n if (addressSubtypes.length > 0) normalized['address_subtypes'] = addressSubtypes\n\n const roles = normalizeRoles(node)\n if (roles.length > 0) normalized['roles'] = roles\n\n if (!Array.isArray(node['flags']) && Array.isArray(node['pattern_flags']) && node['pattern_flags'].length > 0) {\n normalized['flags'] = node['pattern_flags'].map(String)\n }\n\n return normalized\n}\n\nfunction normalizeEdgeType(edge: GraphRecord): string {\n const rawType =\n typeof edge['edge_type'] === 'string' && edge['edge_type'].trim()\n ? edge['edge_type']\n : typeof edge['type'] === 'string' && edge['type'].trim()\n ? edge['type']\n : typeof edge['relationship_type'] === 'string' && edge['relationship_type'].trim()\n ? edge['relationship_type']\n : 'related_to'\n const edgeType = lowerSnake(rawType)\n return FLOW_EDGE_TYPES.has(edgeType) ? 'flows_to' : edgeType\n}\n\nfunction normalizeEdge(edge: unknown): GraphRecord {\n if (!isRecord(edge)) return {}\n\n const normalized: GraphRecord = {}\n for (const [key, value] of Object.entries(edge)) {\n if (['edge_type', 'from_address', 'relationship_type', 'to_address', 'type'].includes(key)) continue\n normalized[key] = value\n }\n\n if (typeof normalized['source'] !== 'string' && typeof edge['from_address'] === 'string') {\n normalized['source'] = edge['from_address']\n }\n if (typeof normalized['target'] !== 'string' && typeof edge['to_address'] === 'string') {\n normalized['target'] = edge['to_address']\n }\n normalized['edge_type'] = normalizeEdgeType(edge)\n\n return normalized\n}\n\nexport function normalizeGraphPayload(payload: unknown): NormalizedGraphPayload {\n if (!isRecord(payload) || payload['schema'] !== 'chain-insights.graph.v1') {\n throw new Error('Unsupported graph payload schema')\n }\n\n return {\n ...payload,\n schema: 'chain-insights.graph.v1',\n nodes: Array.isArray(payload['nodes']) ? payload['nodes'].map(normalizeNode) : [],\n edges: Array.isArray(payload['edges']) ? payload['edges'].map(normalizeEdge) : [],\n flows: Array.isArray(payload['flows']) ? payload['flows'] : [],\n edge_anchors: Array.isArray(payload['edge_anchors']) ? payload['edge_anchors'] : [],\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- const require_chunk = require("./chunk-CZWwpsFl.cjs");
2
- const require_output_root = require("./output-root-CFYms3ad.cjs");
3
- const require_graph_normalizer = require("./graph-normalizer-DeIj6Ses.cjs");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
+ const require_output_root = require("./output-root-YIbl6PwF.cjs");
3
+ const require_graph_normalizer = require("./graph-normalizer-DbjlbMpz.cjs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_chunk.__toESM(node_path, 1);
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -1,5 +1,5 @@
1
- import { n as workspaceOutputPaths } from "./output-root-CmWM7aV2.mjs";
2
- import { t as normalizeGraphPayload } from "./graph-normalizer-Cv9yK9Pg.mjs";
1
+ import { n as workspaceOutputPaths } from "./output-root-BRhzhhXZ.mjs";
2
+ import { t as normalizeGraphPayload } from "./graph-normalizer-CXP06jKh.mjs";
3
3
  import path from "node:path";
4
4
  import { chmod, mkdir, writeFile } from "node:fs/promises";
5
5
  import * as z from "zod";
@@ -60,4 +60,4 @@ async function writeGraphReport(graphData, options) {
60
60
  //#endregion
61
61
  export { writeGraphReport };
62
62
 
63
- //# sourceMappingURL=graph-reports-C4TBjCkM.mjs.map
63
+ //# sourceMappingURL=graph-reports-BDELxmpi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-reports-C4TBjCkM.mjs","names":[],"sources":["../src/mcp/graph-reports.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { chmod, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport * as z from 'zod'\nimport { normalizeGraphPayload } from '../viz/graph-normalizer.js'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst GraphReportInputSchema = z.object({\n schema: z.literal('chain-insights.graph.v1'),\n nodes: z.array(z.unknown()),\n edges: z.array(z.unknown()),\n flows: z.array(z.unknown()).optional(),\n edge_anchors: z.array(z.unknown()).optional(),\n}).passthrough()\n\nexport type GraphReportRef = {\n schema: 'chain-insights.graph.v1'\n filename: string\n url: string\n path: string\n}\n\nexport type WriteGraphReportOptions = {\n serverPort: number\n slug: string\n}\n\nfunction graphPayloadSchema(graphData: unknown): string {\n return typeof graphData === 'object' && graphData !== null && 'schema' in graphData\n ? String(graphData.schema)\n : 'unknown'\n}\n\nfunction sanitizeSlug(slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^[._-]+|[._-]+$/g, '')\n return sanitized || 'graph'\n}\n\nfunction timestampSegment(date = new Date()): string {\n return date.toISOString().replace(/[-:.]/g, '')\n}\n\nfunction uniqueFilename(slug: string): string {\n const suffix = randomUUID().replace(/-/g, '').slice(0, 12)\n return `${timestampSegment()}-${sanitizeSlug(slug)}-${suffix}.graph.json`\n}\n\nasync function ensurePrivateDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true, mode: 0o700 })\n await chmod(dir, 0o700)\n}\n\nexport async function writeGraphReport(\n graphData: unknown,\n options: WriteGraphReportOptions,\n): Promise<GraphReportRef> {\n const parsed = GraphReportInputSchema.safeParse(graphData)\n if (!parsed.success) {\n const schema = graphPayloadSchema(graphData)\n if (schema !== 'chain-insights.graph.v1') {\n throw new Error(`Unsupported graph payload schema: ${schema}`)\n }\n\n throw new Error('Invalid graph payload: nodes and edges must be arrays; flows and edge_anchors must be arrays when present')\n }\n\n const normalized = normalizeGraphPayload({\n ...parsed.data,\n flows: parsed.data.flows ?? [],\n edge_anchors: parsed.data.edge_anchors ?? [],\n })\n const paths = workspaceOutputPaths()\n const filename = uniqueFilename(options.slug)\n const filePath = path.join(paths.reportGraphsRoot, filename)\n\n await ensurePrivateDirectory(paths.reportsRoot)\n await ensurePrivateDirectory(paths.reportGraphsRoot)\n await writeFile(filePath, JSON.stringify(normalized, null, 2) + '\\n', { mode: 0o600 })\n\n return {\n schema: normalized.schema,\n filename,\n path: filePath,\n url: `http://127.0.0.1:${options.serverPort}/graph-reports/${filename}`,\n }\n}\n"],"mappings":";;;;;;;AAOA,MAAM,yBAAyB,EAAE,OAAO;CACtC,QAAQ,EAAE,QAAQ,0BAA0B;CAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC;CAC3B,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC;CAC3B,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,UAAU;CACtC,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,UAAU;CAC9C,CAAC,CAAC,aAAa;AAchB,SAAS,mBAAmB,WAA4B;AACtD,QAAO,OAAO,cAAc,YAAY,cAAc,QAAQ,YAAY,YACtE,OAAO,UAAU,OAAO,GACxB;;AAGN,SAAS,aAAa,MAAsB;AAM1C,QALkB,KACf,aAAa,CACb,QAAQ,kBAAkB,IAAI,CAC9B,QAAQ,OAAO,IAAI,CACnB,QAAQ,oBAAoB,GACf,IAAI;;AAGtB,SAAS,iBAAiB,uBAAO,IAAI,MAAM,EAAU;AACnD,QAAO,KAAK,aAAa,CAAC,QAAQ,UAAU,GAAG;;AAGjD,SAAS,eAAe,MAAsB;CAC5C,MAAM,SAAS,YAAY,CAAC,QAAQ,MAAM,GAAG,CAAC,MAAM,GAAG,GAAG;AAC1D,QAAO,GAAG,kBAAkB,CAAC,GAAG,aAAa,KAAK,CAAC,GAAG,OAAO;;AAG/D,eAAe,uBAAuB,KAA4B;AAChE,OAAM,MAAM,KAAK;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;AAClD,OAAM,MAAM,KAAK,IAAM;;AAGzB,eAAsB,iBACpB,WACA,SACyB;CACzB,MAAM,SAAS,uBAAuB,UAAU,UAAU;AAC1D,KAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,mBAAmB,UAAU;AAC5C,MAAI,WAAW,0BACb,OAAM,IAAI,MAAM,qCAAqC,SAAS;AAGhE,QAAM,IAAI,MAAM,4GAA4G;;CAG9H,MAAM,aAAa,sBAAsB;EACvC,GAAG,OAAO;EACV,OAAO,OAAO,KAAK,SAAS,EAAE;EAC9B,cAAc,OAAO,KAAK,gBAAgB,EAAE;EAC7C,CAAC;CACF,MAAM,QAAQ,sBAAsB;CACpC,MAAM,WAAW,eAAe,QAAQ,KAAK;CAC7C,MAAM,WAAW,KAAK,KAAK,MAAM,kBAAkB,SAAS;AAE5D,OAAM,uBAAuB,MAAM,YAAY;AAC/C,OAAM,uBAAuB,MAAM,iBAAiB;AACpD,OAAM,UAAU,UAAU,KAAK,UAAU,YAAY,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,KAAO,CAAC;AAEtF,QAAO;EACL,QAAQ,WAAW;EACnB;EACA,MAAM;EACN,KAAK,oBAAoB,QAAQ,WAAW,iBAAiB;EAC9D"}
1
+ {"version":3,"file":"graph-reports-BDELxmpi.mjs","names":[],"sources":["../src/mcp/graph-reports.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { chmod, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport * as z from 'zod'\nimport { normalizeGraphPayload } from '../viz/graph-normalizer.js'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst GraphReportInputSchema = z.object({\n schema: z.literal('chain-insights.graph.v1'),\n nodes: z.array(z.unknown()),\n edges: z.array(z.unknown()),\n flows: z.array(z.unknown()).optional(),\n edge_anchors: z.array(z.unknown()).optional(),\n}).passthrough()\n\nexport type GraphReportRef = {\n schema: 'chain-insights.graph.v1'\n filename: string\n url: string\n path: string\n}\n\nexport type WriteGraphReportOptions = {\n serverPort: number\n slug: string\n}\n\nfunction graphPayloadSchema(graphData: unknown): string {\n return typeof graphData === 'object' && graphData !== null && 'schema' in graphData\n ? String(graphData.schema)\n : 'unknown'\n}\n\nfunction sanitizeSlug(slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^[._-]+|[._-]+$/g, '')\n return sanitized || 'graph'\n}\n\nfunction timestampSegment(date = new Date()): string {\n return date.toISOString().replace(/[-:.]/g, '')\n}\n\nfunction uniqueFilename(slug: string): string {\n const suffix = randomUUID().replace(/-/g, '').slice(0, 12)\n return `${timestampSegment()}-${sanitizeSlug(slug)}-${suffix}.graph.json`\n}\n\nasync function ensurePrivateDirectory(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true, mode: 0o700 })\n await chmod(dir, 0o700)\n}\n\nexport async function writeGraphReport(\n graphData: unknown,\n options: WriteGraphReportOptions,\n): Promise<GraphReportRef> {\n const parsed = GraphReportInputSchema.safeParse(graphData)\n if (!parsed.success) {\n const schema = graphPayloadSchema(graphData)\n if (schema !== 'chain-insights.graph.v1') {\n throw new Error(`Unsupported graph payload schema: ${schema}`)\n }\n\n throw new Error('Invalid graph payload: nodes and edges must be arrays; flows and edge_anchors must be arrays when present')\n }\n\n const normalized = normalizeGraphPayload({\n ...parsed.data,\n flows: parsed.data.flows ?? [],\n edge_anchors: parsed.data.edge_anchors ?? [],\n })\n const paths = workspaceOutputPaths()\n const filename = uniqueFilename(options.slug)\n const filePath = path.join(paths.reportGraphsRoot, filename)\n\n await ensurePrivateDirectory(paths.reportsRoot)\n await ensurePrivateDirectory(paths.reportGraphsRoot)\n await writeFile(filePath, JSON.stringify(normalized, null, 2) + '\\n', { mode: 0o600 })\n\n return {\n schema: normalized.schema,\n filename,\n path: filePath,\n url: `http://127.0.0.1:${options.serverPort}/graph-reports/${filename}`,\n }\n}\n"],"mappings":";;;;;;;AAOA,MAAM,yBAAyB,EAAE,OAAO;CACtC,QAAQ,EAAE,QAAQ,yBAAyB;CAC3C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1B,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;CAC1B,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS;CACrC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,SAAS;AAC9C,CAAC,EAAE,YAAY;AAcf,SAAS,mBAAmB,WAA4B;CACtD,OAAO,OAAO,cAAc,YAAY,cAAc,QAAQ,YAAY,YACtE,OAAO,UAAU,MAAM,IACvB;AACN;AAEA,SAAS,aAAa,MAAsB;CAM1C,OALkB,KACf,YAAY,EACZ,QAAQ,kBAAkB,GAAG,EAC7B,QAAQ,OAAO,GAAG,EAClB,QAAQ,oBAAoB,EAChB,KAAK;AACtB;AAEA,SAAS,iBAAiB,uBAAO,IAAI,KAAK,GAAW;CACnD,OAAO,KAAK,YAAY,EAAE,QAAQ,UAAU,EAAE;AAChD;AAEA,SAAS,eAAe,MAAsB;CAC5C,MAAM,SAAS,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE;CACzD,OAAO,GAAG,iBAAiB,EAAE,GAAG,aAAa,IAAI,EAAE,GAAG,OAAO;AAC/D;AAEA,eAAe,uBAAuB,KAA4B;CAChE,MAAM,MAAM,KAAK;EAAE,WAAW;EAAM,MAAM;CAAM,CAAC;CACjD,MAAM,MAAM,KAAK,GAAK;AACxB;AAEA,eAAsB,iBACpB,WACA,SACyB;CACzB,MAAM,SAAS,uBAAuB,UAAU,SAAS;CACzD,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,mBAAmB,SAAS;EAC3C,IAAI,WAAW,2BACb,MAAM,IAAI,MAAM,qCAAqC,QAAQ;EAG/D,MAAM,IAAI,MAAM,2GAA2G;CAC7H;CAEA,MAAM,aAAa,sBAAsB;EACvC,GAAG,OAAO;EACV,OAAO,OAAO,KAAK,SAAS,CAAC;EAC7B,cAAc,OAAO,KAAK,gBAAgB,CAAC;CAC7C,CAAC;CACD,MAAM,QAAQ,qBAAqB;CACnC,MAAM,WAAW,eAAe,QAAQ,IAAI;CAC5C,MAAM,WAAW,KAAK,KAAK,MAAM,kBAAkB,QAAQ;CAE3D,MAAM,uBAAuB,MAAM,WAAW;CAC9C,MAAM,uBAAuB,MAAM,gBAAgB;CACnD,MAAM,UAAU,UAAU,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;CAErF,OAAO;EACL,QAAQ,WAAW;EACnB;EACA,MAAM;EACN,KAAK,oBAAoB,QAAQ,WAAW,iBAAiB;CAC/D;AACF"}
@@ -1,4 +1,4 @@
1
- import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import path from "node:path";
4
4
  import { readFileSync } from "node:fs";
@@ -65,4 +65,4 @@ async function writeVizHtml(vizId, html, caseId) {
65
65
  //#endregion
66
66
  export { html_generator_exports as n, writeVizHtml as r, generateHtml as t };
67
67
 
68
- //# sourceMappingURL=html-generator-V6Bp0uRb.mjs.map
68
+ //# sourceMappingURL=html-generator-AowOmzyi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-generator-V6Bp0uRb.mjs","names":[],"sources":["../src/viz/html-generator.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport path from 'node:path'\nimport os from 'node:os'\nimport type { GraphData } from './graph-model.js'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst templatePath = path.resolve(__dirname, 'templates', 'graph.html')\nconst template = readFileSync(templatePath, 'utf-8')\n\ninterface GraphHtmlNode {\n address: string\n address_type: string\n labels: string[]\n flow_in_usd: number\n flow_out_usd: number\n role: string | null\n risk_level: string | null\n pattern_flags: string[]\n}\n\ninterface GraphHtmlEdge {\n source: string\n target: string\n usd_amount: number\n tx_count: number\n edge_type: string\n}\n\ninterface GraphHtmlData {\n nodes: GraphHtmlNode[]\n edges: GraphHtmlEdge[]\n metadata?: { seed_address?: string; title?: string }\n}\n\nconst ENTITY_TO_ROLE: Record<string, string | null> = {\n eoa: 'search',\n contract: 'intermediary',\n exchange: 'exchange',\n mixer: 'intermediary',\n unknown: null,\n}\n\nexport function transformToGraphHtml(data: GraphData): GraphHtmlData {\n const nodes: GraphHtmlNode[] = data.nodes.map((n) => ({\n address: n.id,\n address_type: n.entityType === 'exchange' ? 'exchange' : 'wallet',\n labels: n.label ? [n.label] : [],\n flow_in_usd: n.totalIn,\n flow_out_usd: n.totalOut,\n role: ENTITY_TO_ROLE[n.entityType] ?? null,\n risk_level: n.riskLevel === 'unknown' ? null : n.riskLevel,\n pattern_flags: [],\n }))\n\n const edges: GraphHtmlEdge[] = data.edges.map((e) => ({\n source: e.source,\n target: e.target,\n usd_amount: e.value,\n tx_count: 1,\n edge_type: 'flows_to',\n }))\n\n return {\n nodes,\n edges,\n metadata: {\n title: data.metadata.title,\n },\n }\n}\n\nexport function generateHtml(data: GraphData, _title: string): string {\n const graphHtmlData = transformToGraphHtml(data)\n return generateInlineGraphHtml(graphHtmlData)\n}\n\nexport function generateInlineGraphHtml(data: unknown): string {\n const dataJson = JSON.stringify(data).replaceAll('</script>', '<\\\\/script>')\n const inlineScript = `<script>var INLINE_DATA = ${dataJson};</script>`\n\n return template.replace('</body>', `${inlineScript}\\n</body>`)\n}\n\nfunction sanitizePathSegment(segment: string): string {\n if (/[/\\\\]|^\\.\\.?$/.test(segment)) throw new Error(`Invalid path segment: ${segment}`)\n return segment\n}\n\nexport async function writeVizHtml(vizId: string, html: string, caseId?: string): Promise<string> {\n let vizDir: string\n if (caseId) {\n vizDir = path.join(os.homedir(), '.chain-insights', 'cases', sanitizePathSegment(caseId), 'viz')\n } else {\n vizDir = path.join(os.homedir(), '.chain-insights', 'viz')\n }\n await mkdir(vizDir, { recursive: true })\n const filePath = path.join(vizDir, `${vizId}.html`)\n await writeFile(filePath, html, { mode: 0o600 })\n return filePath\n}\n"],"mappings":";;;;;;;;;;;;;AAOA,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE9D,MAAM,WAAW,aADI,KAAK,QAAQ,WAAW,aAAa,aAChB,EAAE,QAAQ;AA2BpD,MAAM,iBAAgD;CACpD,KAAK;CACL,UAAU;CACV,UAAU;CACV,OAAO;CACP,SAAS;CACV;AAED,SAAgB,qBAAqB,MAAgC;AAoBnE,QAAO;EACL,OApB6B,KAAK,MAAM,KAAK,OAAO;GACpD,SAAS,EAAE;GACX,cAAc,EAAE,eAAe,aAAa,aAAa;GACzD,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,EAAE;GAChC,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,MAAM,eAAe,EAAE,eAAe;GACtC,YAAY,EAAE,cAAc,YAAY,OAAO,EAAE;GACjD,eAAe,EAAE;GAClB,EAWM;EACL,OAV6B,KAAK,MAAM,KAAK,OAAO;GACpD,QAAQ,EAAE;GACV,QAAQ,EAAE;GACV,YAAY,EAAE;GACd,UAAU;GACV,WAAW;GACZ,EAIM;EACL,UAAU,EACR,OAAO,KAAK,SAAS,OACtB;EACF;;AAGH,SAAgB,aAAa,MAAiB,QAAwB;AAEpE,QAAO,wBADe,qBAAqB,KACC,CAAC;;AAG/C,SAAgB,wBAAwB,MAAuB;CAE7D,MAAM,eAAe,6BADJ,KAAK,UAAU,KAAK,CAAC,WAAW,cAAa,cACJ,CAAC;AAE3D,QAAO,SAAS,QAAQ,WAAW,GAAG,aAAa,WAAW;;AAGhE,SAAS,oBAAoB,SAAyB;AACpD,KAAI,gBAAgB,KAAK,QAAQ,CAAE,OAAM,IAAI,MAAM,yBAAyB,UAAU;AACtF,QAAO;;AAGT,eAAsB,aAAa,OAAe,MAAc,QAAkC;CAChG,IAAI;AACJ,KAAI,OACF,UAAS,KAAK,KAAK,GAAG,SAAS,EAAE,mBAAmB,SAAS,oBAAoB,OAAO,EAAE,MAAM;KAEhG,UAAS,KAAK,KAAK,GAAG,SAAS,EAAE,mBAAmB,MAAM;AAE5D,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;CACxC,MAAM,WAAW,KAAK,KAAK,QAAQ,GAAG,MAAM,OAAO;AACnD,OAAM,UAAU,UAAU,MAAM,EAAE,MAAM,KAAO,CAAC;AAChD,QAAO"}
1
+ {"version":3,"file":"html-generator-AowOmzyi.mjs","names":[],"sources":["../src/viz/html-generator.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport path from 'node:path'\nimport os from 'node:os'\nimport type { GraphData } from './graph-model.js'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst templatePath = path.resolve(__dirname, 'templates', 'graph.html')\nconst template = readFileSync(templatePath, 'utf-8')\n\ninterface GraphHtmlNode {\n address: string\n address_type: string\n labels: string[]\n flow_in_usd: number\n flow_out_usd: number\n role: string | null\n risk_level: string | null\n pattern_flags: string[]\n}\n\ninterface GraphHtmlEdge {\n source: string\n target: string\n usd_amount: number\n tx_count: number\n edge_type: string\n}\n\ninterface GraphHtmlData {\n nodes: GraphHtmlNode[]\n edges: GraphHtmlEdge[]\n metadata?: { seed_address?: string; title?: string }\n}\n\nconst ENTITY_TO_ROLE: Record<string, string | null> = {\n eoa: 'search',\n contract: 'intermediary',\n exchange: 'exchange',\n mixer: 'intermediary',\n unknown: null,\n}\n\nexport function transformToGraphHtml(data: GraphData): GraphHtmlData {\n const nodes: GraphHtmlNode[] = data.nodes.map((n) => ({\n address: n.id,\n address_type: n.entityType === 'exchange' ? 'exchange' : 'wallet',\n labels: n.label ? [n.label] : [],\n flow_in_usd: n.totalIn,\n flow_out_usd: n.totalOut,\n role: ENTITY_TO_ROLE[n.entityType] ?? null,\n risk_level: n.riskLevel === 'unknown' ? null : n.riskLevel,\n pattern_flags: [],\n }))\n\n const edges: GraphHtmlEdge[] = data.edges.map((e) => ({\n source: e.source,\n target: e.target,\n usd_amount: e.value,\n tx_count: 1,\n edge_type: 'flows_to',\n }))\n\n return {\n nodes,\n edges,\n metadata: {\n title: data.metadata.title,\n },\n }\n}\n\nexport function generateHtml(data: GraphData, _title: string): string {\n const graphHtmlData = transformToGraphHtml(data)\n return generateInlineGraphHtml(graphHtmlData)\n}\n\nexport function generateInlineGraphHtml(data: unknown): string {\n const dataJson = JSON.stringify(data).replaceAll('</script>', '<\\\\/script>')\n const inlineScript = `<script>var INLINE_DATA = ${dataJson};</script>`\n\n return template.replace('</body>', `${inlineScript}\\n</body>`)\n}\n\nfunction sanitizePathSegment(segment: string): string {\n if (/[/\\\\]|^\\.\\.?$/.test(segment)) throw new Error(`Invalid path segment: ${segment}`)\n return segment\n}\n\nexport async function writeVizHtml(vizId: string, html: string, caseId?: string): Promise<string> {\n let vizDir: string\n if (caseId) {\n vizDir = path.join(os.homedir(), '.chain-insights', 'cases', sanitizePathSegment(caseId), 'viz')\n } else {\n vizDir = path.join(os.homedir(), '.chain-insights', 'viz')\n }\n await mkdir(vizDir, { recursive: true })\n const filePath = path.join(vizDir, `${vizId}.html`)\n await writeFile(filePath, html, { mode: 0o600 })\n return filePath\n}\n"],"mappings":";;;;;;;;;;;;;AAOA,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AAE7D,MAAM,WAAW,aADI,KAAK,QAAQ,WAAW,aAAa,YACjB,GAAG,OAAO;AA2BnD,MAAM,iBAAgD;CACpD,KAAK;CACL,UAAU;CACV,UAAU;CACV,OAAO;CACP,SAAS;AACX;AAEA,SAAgB,qBAAqB,MAAgC;CAoBnE,OAAO;EACL,OApB6B,KAAK,MAAM,KAAK,OAAO;GACpD,SAAS,EAAE;GACX,cAAc,EAAE,eAAe,aAAa,aAAa;GACzD,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC;GAC/B,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,MAAM,eAAe,EAAE,eAAe;GACtC,YAAY,EAAE,cAAc,YAAY,OAAO,EAAE;GACjD,eAAe,CAAC;EAClB,EAWM;EACJ,OAV6B,KAAK,MAAM,KAAK,OAAO;GACpD,QAAQ,EAAE;GACV,QAAQ,EAAE;GACV,YAAY,EAAE;GACd,UAAU;GACV,WAAW;EACb,EAIM;EACJ,UAAU,EACR,OAAO,KAAK,SAAS,MACvB;CACF;AACF;AAEA,SAAgB,aAAa,MAAiB,QAAwB;CAEpE,OAAO,wBADe,qBAAqB,IACA,CAAC;AAC9C;AAEA,SAAgB,wBAAwB,MAAuB;CAE7D,MAAM,eAAe,6BADJ,KAAK,UAAU,IAAI,EAAE,WAAW,cAAa,aACL,EAAE;CAE3D,OAAO,SAAS,QAAQ,WAAW,GAAG,aAAa,UAAU;AAC/D;AAEA,SAAS,oBAAoB,SAAyB;CACpD,IAAI,gBAAgB,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,yBAAyB,SAAS;CACrF,OAAO;AACT;AAEA,eAAsB,aAAa,OAAe,MAAc,QAAkC;CAChG,IAAI;CACJ,IAAI,QACF,SAAS,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,SAAS,oBAAoB,MAAM,GAAG,KAAK;MAE/F,SAAS,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,KAAK;CAE3D,MAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CACvC,MAAM,WAAW,KAAK,KAAK,QAAQ,GAAG,MAAM,MAAM;CAClD,MAAM,UAAU,UAAU,MAAM,EAAE,MAAM,IAAM,CAAC;CAC/C,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- const require_chunk = require("./chunk-CZWwpsFl.cjs");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
2
  let node_url = require("node:url");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_chunk.__toESM(node_path, 1);
package/dist/index.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_config = require("./config-Bmdl5hdk.cjs");
3
- const require_app = require("./app-Dq1TdB6p.cjs");
4
- const require_server = require("./server-DXowbpfi.cjs");
5
- const require_wallet = require("./wallet-RnvvSpV2.cjs");
6
- const require_tools = require("./tools-f_vJUZAF.cjs");
7
- const require_topup_server = require("./topup-server-BZuQifvh.cjs");
8
- const require_client = require("./client-D4fZgIaO.cjs");
9
- const require_viz = require("./viz-ClezVXrJ.cjs");
2
+ const require_config = require("./config-BhYbhLDI.cjs");
3
+ const require_app = require("./app-DxlQE_P5.cjs");
4
+ const require_server = require("./server-C3y1gQmZ.cjs");
5
+ const require_wallet = require("./wallet-TAlNMvIM.cjs");
6
+ const require_tools = require("./tools-DY8h0WbE.cjs");
7
+ const require_topup_server = require("./topup-server-DjUjhNjv.cjs");
8
+ const require_client = require("./client-DPc2eyVN.cjs");
9
+ const require_viz = require("./viz-Da9YWN_I.cjs");
10
10
  exports.buildTopupInfo = require_tools.buildTopupInfo;
11
11
  exports.createApp = require_app.createApp;
12
12
  exports.createMcpFetchClient = require_client.createMcpFetchClient;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;cAIa,YAAA,EAAY,CAAA,CAAA,SAAA;;;;;;;;;;;;;;KAYb,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,YAAA;;;iBCJ1B,UAAA,CAAA,GAAc,OAAA,CAAQ,kBAAA;AAAA,iBAYtB,UAAA,CAAW,OAAA,EAAS,OAAA,CAAQ,kBAAA,IAAsB,OAAA;AAAA,iBASlD,gBAAA,CAAA,GAAoB,OAAA;;;iBCkG1B,SAAA,CAAA,GAAa,IAAA;;;iBChIb,WAAA,CAAY,IAAA;;;iBC0BZ,yBAAA,CAA0B,KAAA,WAAgB,GAAA;AAAA,iBAO1C,2BAAA,CAA4B,UAAA,WAAqB,OAAA;;AJhCjE;;;;;;iBI2CsB,UAAA,CAAW,UAAA,WAAqB,OAAA;AAAA,iBA2BhC,mBAAA,CAAoB,UAAA,WAAqB,OAAA,CAAQ,OAAA;;;;;;;iBAajD,UAAA,CAAA,GAAc,OAAA;;;;;iBA2Cd,kBAAA,CAAA,GAAsB,OAAA;;;cC7H/B,aAAA;AAAA,cACA,YAAA;AAAA,UAmBI,oBAAA;EACf,OAAA,EAAS,OAAA;EACT,UAAA,EAAY,GAAA;AAAA;AAAA,UAGG,SAAA;EACf,cAAA;EACA,OAAA;EACA,QAAA,SAAiB,aAAA;EACjB,KAAA;EACA,cAAA,SAAuB,YAAA;EACvB,SAAA;AAAA;AAAA,iBAGoB,gBAAA,CAAA,GAAoB,OAAA,CAAQ,oBAAA;AAAA,iBAM5B,cAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAA;AAAA,iBA2BmB,aAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAA;AAAA,iBAsBa,mBAAA,CAAoB,OAAA,UAAiB,WAAA,UAAqB,UAAA;AAAA,iBAUpD,oBAAA,CAAqB,OAAA,GAAU,oBAAA,GAAuB,OAAA;AAAA,iBAS5D,cAAA,CAAe,OAAA,UAAiB,QAAA,YAAoB,SAAA;;;iBC3BpD,oBAAA,CAAqB,aAAA,UAAuB,QAAA;;;iBCjC5C,WAAA,CAAA;AAAA,iBAIM,gBAAA,CAAiB,OAAA,EAAS,oBAAA,YAAgC,OAAA;;;;;;AP3DhF;;;;;;;iBQsEgB,oBAAA,CAAqB,UAAA,iBAA2B,SAAA,mBAAkB,KAAA;;;cClErE,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;KAWV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;KAQV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;;;iBClCjB,qBAAA,CAAsB,IAAA;EAC1C,MAAA;EACA,QAAA;AAAA,IACE,OAAA;EAAU,KAAA;EAAe,QAAA;AAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;cAIa,YAAA,EAAY,CAAA,CAAA,SAAA;;;;;;;;;;;;;;KAYb,kBAAA,GAAqB,CAAA,CAAE,KAAK,QAAQ,YAAA;;;iBCJ1B,UAAA,CAAA,GAAc,OAAO,CAAC,kBAAA;AAAA,iBAYtB,UAAA,CAAW,OAAA,EAAS,OAAA,CAAQ,kBAAA,IAAsB,OAAA;AAAA,iBASlD,gBAAA,CAAA,GAAoB,OAAO;;;iBCkGjC,SAAA,CAAA,GAAa,IAAI;;;iBChIjB,WAAA,CAAY,IAAW;;;iBC0BvB,yBAAA,CAA0B,KAAA,WAAgB,GAAG;AAAA,iBAO7C,2BAAA,CAA4B,UAAA,WAAqB,OAAO;;AJhCxE;;;;;;iBI2CsB,UAAA,CAAW,UAAA,WAAqB,OAAO;AAAA,iBA2BvC,mBAAA,CAAoB,UAAA,WAAqB,OAAO,CAAC,OAAA;;;;;;;iBAajD,UAAA,CAAA,GAAc,OAAO;;;;;iBA2CrB,kBAAA,CAAA,GAAsB,OAAO;;;cC7HtC,aAAA;AAAA,cACA,YAAA;AAAA,UAmBI,oBAAA;EACf,OAAA,EAAS,OAAA;EACT,UAAA,EAAY,GAAG;AAAA;AAAA,UAGA,SAAA;EACf,cAAA;EACA,OAAA;EACA,QAAA,SAAiB,aAAA;EACjB,KAAA;EACA,cAAA,SAAuB,YAAY;EACnC,SAAA;AAAA;AAAA,iBAGoB,gBAAA,CAAA,GAAoB,OAAO,CAAC,oBAAA;AAAA,iBAM5B,cAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBA2BY,aAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBAsBM,mBAAA,CAAoB,OAAA,UAAiB,WAAA,UAAqB,UAAA;AAAA,iBAUpD,oBAAA,CAAqB,OAAA,GAAU,oBAAA,GAAuB,OAAO;AAAA,iBASnE,cAAA,CAAe,OAAA,UAAiB,QAAA,YAAoB,SAAS;;;iBC3B7D,oBAAA,CAAqB,aAAA,UAAuB,QAAgB;;;iBCjC5D,WAAA,CAAA;AAAA,iBAIM,gBAAA,CAAiB,OAAA,EAAS,oBAAA,YAAgC,OAAO;;;;;;;;;;;;;iBCyBvE,oBAAA,CAAqB,UAAA,iBAA2B,SAAA,mBAAkB,KAAA;;;cChFrE,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;KAWV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;KAQV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;;;iBClCjB,qBAAA,CAAsB,IAAA;EAC1C,MAAA;EACA,QAAA;AAAA,IACE,OAAO;EAAG,KAAA;EAAe,QAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;cAIa,YAAA,EAAY,CAAA,CAAA,SAAA;;;;;;;;;;;;;;KAYb,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,YAAA;;;iBCJ1B,UAAA,CAAA,GAAc,OAAA,CAAQ,kBAAA;AAAA,iBAYtB,UAAA,CAAW,OAAA,EAAS,OAAA,CAAQ,kBAAA,IAAsB,OAAA;AAAA,iBASlD,gBAAA,CAAA,GAAoB,OAAA;;;iBCkG1B,SAAA,CAAA,GAAa,IAAA;;;iBChIb,WAAA,CAAY,IAAA;;;iBC0BZ,yBAAA,CAA0B,KAAA,WAAgB,GAAA;AAAA,iBAO1C,2BAAA,CAA4B,UAAA,WAAqB,OAAA;;AJhCjE;;;;;;iBI2CsB,UAAA,CAAW,UAAA,WAAqB,OAAA;AAAA,iBA2BhC,mBAAA,CAAoB,UAAA,WAAqB,OAAA,CAAQ,OAAA;;;;;;;iBAajD,UAAA,CAAA,GAAc,OAAA;;;;;iBA2Cd,kBAAA,CAAA,GAAsB,OAAA;;;cC7H/B,aAAA;AAAA,cACA,YAAA;AAAA,UAmBI,oBAAA;EACf,OAAA,EAAS,OAAA;EACT,UAAA,EAAY,GAAA;AAAA;AAAA,UAGG,SAAA;EACf,cAAA;EACA,OAAA;EACA,QAAA,SAAiB,aAAA;EACjB,KAAA;EACA,cAAA,SAAuB,YAAA;EACvB,SAAA;AAAA;AAAA,iBAGoB,gBAAA,CAAA,GAAoB,OAAA,CAAQ,oBAAA;AAAA,iBAM5B,cAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAA;AAAA,iBA2BmB,aAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAA;AAAA,iBAsBa,mBAAA,CAAoB,OAAA,UAAiB,WAAA,UAAqB,UAAA;AAAA,iBAUpD,oBAAA,CAAqB,OAAA,GAAU,oBAAA,GAAuB,OAAA;AAAA,iBAS5D,cAAA,CAAe,OAAA,UAAiB,QAAA,YAAoB,SAAA;;;iBC3BpD,oBAAA,CAAqB,aAAA,UAAuB,QAAA;;;iBCjC5C,WAAA,CAAA;AAAA,iBAIM,gBAAA,CAAiB,OAAA,EAAS,oBAAA,YAAgC,OAAA;;;;;;AP3DhF;;;;;;;iBQsEgB,oBAAA,CAAqB,UAAA,iBAA2B,SAAA,mBAAkB,KAAA;;;cClErE,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;KAWV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;KAQV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYV,SAAA,GAAY,CAAA,CAAE,KAAA,QAAa,SAAA;;;iBClCjB,qBAAA,CAAsB,IAAA;EAC1C,MAAA;EACA,QAAA;AAAA,IACE,OAAA;EAAU,KAAA;EAAe,QAAA;AAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/config/schema.ts","../src/config/index.ts","../src/server/app.ts","../src/server/index.ts","../src/wallet/index.ts","../src/wallet/tools.ts","../src/wallet/mcp-proxy/topup-server.ts","../src/wallet/topup-server.ts","../src/mcp/client.ts","../src/viz/graph-model.ts","../src/viz/index.ts"],"mappings":";;;;;cAIa,YAAA,EAAY,CAAA,CAAA,SAAA;;;;;;;;;;;;;;KAYb,kBAAA,GAAqB,CAAA,CAAE,KAAK,QAAQ,YAAA;;;iBCJ1B,UAAA,CAAA,GAAc,OAAO,CAAC,kBAAA;AAAA,iBAYtB,UAAA,CAAW,OAAA,EAAS,OAAA,CAAQ,kBAAA,IAAsB,OAAA;AAAA,iBASlD,gBAAA,CAAA,GAAoB,OAAO;;;iBCkGjC,SAAA,CAAA,GAAa,IAAI;;;iBChIjB,WAAA,CAAY,IAAW;;;iBC0BvB,yBAAA,CAA0B,KAAA,WAAgB,GAAG;AAAA,iBAO7C,2BAAA,CAA4B,UAAA,WAAqB,OAAO;;AJhCxE;;;;;;iBI2CsB,UAAA,CAAW,UAAA,WAAqB,OAAO;AAAA,iBA2BvC,mBAAA,CAAoB,UAAA,WAAqB,OAAO,CAAC,OAAA;;;;;;;iBAajD,UAAA,CAAA,GAAc,OAAO;;;;;iBA2CrB,kBAAA,CAAA,GAAsB,OAAO;;;cC7HtC,aAAA;AAAA,cACA,YAAA;AAAA,UAmBI,oBAAA;EACf,OAAA,EAAS,OAAA;EACT,UAAA,EAAY,GAAG;AAAA;AAAA,UAGA,SAAA;EACf,cAAA;EACA,OAAA;EACA,QAAA,SAAiB,aAAA;EACjB,KAAA;EACA,cAAA,SAAuB,YAAY;EACnC,SAAA;AAAA;AAAA,iBAGoB,gBAAA,CAAA,GAAoB,OAAO,CAAC,oBAAA;AAAA,iBAM5B,cAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBA2BY,aAAA,CACpB,OAAA,EAAS,OAAA,WACT,MAAA,wBACC,OAAO;AAAA,iBAsBM,mBAAA,CAAoB,OAAA,UAAiB,WAAA,UAAqB,UAAA;AAAA,iBAUpD,oBAAA,CAAqB,OAAA,GAAU,oBAAA,GAAuB,OAAO;AAAA,iBASnE,cAAA,CAAe,OAAA,UAAiB,QAAA,YAAoB,SAAS;;;iBC3B7D,oBAAA,CAAqB,aAAA,UAAuB,QAAgB;;;iBCjC5D,WAAA,CAAA;AAAA,iBAIM,gBAAA,CAAiB,OAAA,EAAS,oBAAA,YAAgC,OAAO;;;;;;;;;;;;;iBCyBvE,oBAAA,CAAqB,UAAA,iBAA2B,SAAA,mBAAkB,KAAA;;;cChFrE,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;KAWV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;KAQV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;AAAA,cAE1B,SAAA,EAAS,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAYV,SAAA,GAAY,CAAA,CAAE,KAAK,QAAQ,SAAA;;;iBClCjB,qBAAA,CAAsB,IAAA;EAC1C,MAAA;EACA,QAAA;AAAA,IACE,OAAO;EAAG,KAAA;EAAe,QAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-BwrBYmiC.mjs";
2
- import { t as createApp } from "./app-BjjuQM0B.mjs";
3
- import { n as startServer } from "./server-BkM5xrXb.mjs";
4
- import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-BMelXBYP.mjs";
5
- import { a as getWalletAccount, i as getBalanceUsdc, n as formatWalletBalance, o as getWalletBalanceText, r as getBalanceEth, t as buildTopupInfo } from "./tools-Cp2jAAAb.mjs";
6
- import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-DUjyFftI.mjs";
7
- import { r as createMcpFetchClient } from "./client-D4Bq0rp9.mjs";
8
- import { t as generateVisualization } from "./viz-BlCJe6Tk.mjs";
1
+ import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-9KYXaAv-.mjs";
2
+ import { t as createApp } from "./app-DdWQF_zb.mjs";
3
+ import { n as startServer } from "./server-BDlbmGbL.mjs";
4
+ import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-D8IqFRKY.mjs";
5
+ import { a as getWalletAccount, i as getBalanceUsdc, n as formatWalletBalance, o as getWalletBalanceText, r as getBalanceEth, t as buildTopupInfo } from "./tools-Py6SXg6J.mjs";
6
+ import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-6MH7q73X.mjs";
7
+ import { i as createMcpFetchClient } from "./client-D4_hd4AP.mjs";
8
+ import { t as generateVisualization } from "./viz-DkJyqlUu.mjs";
9
9
  export { buildTopupInfo, createApp, createMcpFetchClient, decryptKey, encryptKey, formatWalletBalance, generateArtifactHtml, generateVisualization, getBalanceEth, getBalanceUsdc, getTopupUrl, getWalletAccount, getWalletBalanceText, isWalletConfigured, loadConfig, normalizeWalletPrivateKey, resetConfigCache, saveConfig, setWalletPrivateKey, startServer, startTopupServer, walletAddressFromPrivateKey };
@@ -1,4 +1,4 @@
1
- const require_chunk = require("./chunk-CZWwpsFl.cjs");
1
+ const require_chunk = require("./chunk-DakpK96I.cjs");
2
2
  let node_path = require("node:path");
3
3
  node_path = require_chunk.__toESM(node_path, 1);
4
4
  let node_fs_promises = require("node:fs/promises");
@@ -146,10 +146,9 @@ property names for the active network.
146
146
  Rules:
147
147
 
148
148
  - Prefer \`graph_query\` and \`graph_query_batch\` for graph-language reads.
149
- - Use \`USE live_topology\` for Memgraph RAM topology, \`USE archive_topology\`
150
- for StarRocks historical topology, and \`USE facts\` for StarRocks fact
151
- labels such as \`AddressLabel\`, \`AddressFeature\`,
152
- \`RiskScore\`, and \`Asset\`. Address facts can be reached through
149
+ - Use \`USE live_topology\` for recent topology, \`USE archive_topology\`
150
+ for historical topology, and \`USE facts\` for labels, features,
151
+ risk scores, assets, and enrichment. Address facts can be reached through
153
152
  relationships such as \`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\`.
154
153
  Archived money-flow topology is exposed as
155
154
  \`(:Address)-[:FLOWS_TO]->(:Address)\` with \`period_granularity\`,
@@ -144,10 +144,9 @@ property names for the active network.
144
144
  Rules:
145
145
 
146
146
  - Prefer \`graph_query\` and \`graph_query_batch\` for graph-language reads.
147
- - Use \`USE live_topology\` for Memgraph RAM topology, \`USE archive_topology\`
148
- for StarRocks historical topology, and \`USE facts\` for StarRocks fact
149
- labels such as \`AddressLabel\`, \`AddressFeature\`,
150
- \`RiskScore\`, and \`Asset\`. Address facts can be reached through
147
+ - Use \`USE live_topology\` for recent topology, \`USE archive_topology\`
148
+ for historical topology, and \`USE facts\` for labels, features,
149
+ risk scores, assets, and enrichment. Address facts can be reached through
151
150
  relationships such as \`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\`.
152
151
  Archived money-flow topology is exposed as
153
152
  \`(:Address)-[:FLOWS_TO]->(:Address)\` with \`period_granularity\`,
@@ -229,4 +228,4 @@ async function initWorkspace(options) {
229
228
  //#endregion
230
229
  export { initWorkspace };
231
230
 
232
- //# sourceMappingURL=init-CaOsHTIo.mjs.map
231
+ //# sourceMappingURL=init-CZbZegIW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-CZbZegIW.mjs","names":[],"sources":["../src/workspace/init.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\n\nexport interface InitWorkspaceOptions {\n targetDir: string\n force?: boolean\n}\n\nexport interface InitWorkspaceResult {\n workspaceRoot: string\n filesWritten: string[]\n}\n\nconst WORKSPACE_DIRS = [\n '.chain-insights',\n '.chain-insights/schema',\n '.chain-insights/runtime',\n '.chain-insights/runtime/logs',\n '.chain-insights/runtime-skill',\n 'cases',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'templates',\n]\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return JSON.stringify({\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Investigations',\n workspace_root: workspaceRoot,\n default_network: 'bittensor',\n graph_mcp_endpoint: 'https://staging-mcp.chain-insights.ai/mcp',\n cases_dir: 'cases',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n created_at: todayIso(),\n }, null, 2) + '\\n'\n}\n\nconst README = `# Chain Insights Investigations\n\nThis is a workspace for Chain Insights AML investigations.\n\n## Start\n\n\\`\\`\\`bash\nchain-insights mcp tools --refresh\nchain-insights wallet balance\n\\`\\`\\`\n\n## Layout\n\n\\`\\`\\`text\n.chain-insights/ Workspace metadata\ncases/ Case exports and notes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\ntemplates/ Reusable case/report templates\n.chain-insights/schema/ Runtime graph schema captures\n.chain-insights/runtime/ Workspace-local runtime process state and debug logs\n.chain-insights/runtime-skill/ Workspace-specific agent schema notes\n\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights investigation workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before investigation-producing commands.\n- Do not rerun init in an existing workspace unless replacing scaffolding with \\`--force\\`.\n- Read .chain-insights/runtime-skill/SKILL.md before graph queries.\n- Preserve full blockchain addresses exactly.\n- Do not guess the network for graph queries.\n- Capture or refresh graph schema before the first case query.\n- Save compact evidence with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Evidence files should summarize and point to graph/table outputs; do not paste large raw JSON blobs into evidence Markdown.\n- Investigation output must stay in this initialized workspace.\n- Never write cases, evidence, reports, graph JSON, HTML, schema captures, or logs to ~/.chain-insights.\n- Keep theories lightweight until evidence supports them.\n`\n\nconst CLAUDE = AGENTS\n\nconst CASE_BRIEF = `# Case Brief\n\n## Summary\n\nStatus:\nNetwork:\nCurrent Assessment:\n\n## Known Addresses\n\n## Claims To Validate\n\n## Evidence\n\n## Next Steps\n`\n\nconst IMPORTS_README = `# External Investigation Inputs\n\nPut user-provided or third-party investigation material here before turning it\ninto case evidence.\n\nExamples:\n\n- Exchange support exports\n- CSV extracts\n- Screenshots\n- Raw notes\n- Partner reports\n\nFiles in this directory are inputs, not verified evidence. When an import\nsupports a claim, summarize it into the case evidence manifest and reference\nthe original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, case, prompt, and evidence templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as case state until copied into a case, evidence file, dossier, or\nreport.\n`\n\nconst RUNTIME_SKILL = `---\nname: chain-insights-runtime-schema\ndescription: Workspace-local Chain Insights runtime schema notes. Refresh this after connecting to a graph MCP endpoint.\n---\n\n# Runtime Graph Schema\n\nBefore the first investigation query, capture the live graph schema into:\n\n\\`\\`\\`text\n.chain-insights/schema/<network>.graph-schema.json\n\\`\\`\\`\n\nUse \\`graph_query_batch\\` for schema capture. Prefix current topology reads\nwith \\`USE live_topology\\`, historical topology reads with\n\\`USE archive_topology\\`, and fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE live_topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"archive_flow_sample\",\"query\":\"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- Use \\`USE live_topology\\` for recent topology, \\`USE archive_topology\\`\n for historical topology, and \\`USE facts\\` for labels, features,\n risk scores, assets, and enrichment. Address facts can be reached through\n relationships such as \\`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\\`.\n Archived money-flow topology is exposed as\n \\`(:Address)-[:FLOWS_TO]->(:Address)\\` with \\`period_granularity\\`,\n \\`period_start_date\\`, and \\`period_end_date\\` on the relationship.\n- Preserve source schema field names in evidence and generated data files.\n- Do not rename, reinterpret, or add unit labels to graph fields unless the\n schema or query result explicitly supports that interpretation.\n- Keep evidence compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs in evidence unless\n the purpose of the query is schema discovery or debugging.\n- Keep analysis products separate from evidence: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Evidence Markdown should be a short provenance record with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in evidence.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nbittensor.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first case query in a fresh\nworkspace, then referenced by evidence, reports, and runtime skill notes.\n`\n\nfunction workspaceFiles(workspaceRoot: string): Array<[string, string]> {\n return [\n ['.chain-insights/workspace.json', workspaceJson(workspaceRoot)],\n ['README.md', README],\n ['AGENTS.md', AGENTS],\n ['CLAUDE.md', CLAUDE],\n ['imports/README.md', IMPORTS_README],\n ['templates/README.md', TEMPLATES_README],\n ['templates/case-brief.md', CASE_BRIEF],\n ['.chain-insights/runtime-skill/SKILL.md', RUNTIME_SKILL],\n ['.chain-insights/schema/README.md', SCHEMA_README],\n ['.chain-insights/runtime/.keep', ''],\n ['.chain-insights/runtime/logs/.keep', ''],\n ]\n}\n\nasync function assertNoFileCollisions(workspaceRoot: string): Promise<void> {\n for (const [relativePath] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await access(filePath)\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue\n }\n throw err\n }\n }\n}\n\nexport async function initWorkspace(options: InitWorkspaceOptions): Promise<InitWorkspaceResult> {\n const workspaceRoot = path.resolve(options.targetDir)\n if (!options.force) {\n await assertNoFileCollisions(workspaceRoot)\n }\n\n for (const dir of WORKSPACE_DIRS) {\n await mkdir(path.join(workspaceRoot, dir), { recursive: true })\n }\n\n const filesWritten: string[] = []\n const flag = options.force ? 'w' : 'wx'\n for (const [relativePath, content] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await writeFile(filePath, content, { mode: 0o600, flag })\n filesWritten.push(relativePath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n }\n throw err\n }\n }\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;AAaA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OAAO,KAAK,UAAU;EACpB,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,WAAW;EACX,aAAa;EACb,aAAa;EACb,eAAe;EACf,YAAY,SAAS;CACvB,GAAG,MAAM,CAAC,IAAI;AAChB;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;AA2Bf,MAAM,SAAS;;;;;;;;;;;;;;;;;;AAmBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CtB,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,eAAe,eAAgD;CACtE,OAAO;EACL,CAAC,kCAAkC,cAAc,aAAa,CAAC;EAC/D,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,qBAAqB,cAAc;EACpC,CAAC,uBAAuB,gBAAgB;EACxC,CAAC,2BAA2B,UAAU;EACtC,CAAC,0CAA0C,aAAa;EACxD,CAAC,oCAAoC,aAAa;EAClD,CAAC,iCAAiC,EAAE;EACpC,CAAC,sCAAsC,EAAE;CAC3C;AACF;AAEA,eAAe,uBAAuB,eAAsC;CAC1E,KAAK,MAAM,CAAC,iBAAiB,eAAe,aAAa,GAAG;EAC1D,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,OAAO,QAAQ;GACrB,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;EACtG,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C;GAEF,MAAM;EACR;CACF;AACF;AAEA,eAAsB,cAAc,SAA6D;CAC/F,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,SAAS;CACpD,IAAI,CAAC,QAAQ,OACX,MAAM,uBAAuB,aAAa;CAG5C,KAAK,MAAM,OAAO,gBAChB,MAAM,MAAM,KAAK,KAAK,eAAe,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;CAGhE,MAAM,eAAyB,CAAC;CAChC,MAAM,OAAO,QAAQ,QAAQ,MAAM;CACnC,KAAK,MAAM,CAAC,cAAc,YAAY,eAAe,aAAa,GAAG;EACnE,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,UAAU,UAAU,SAAS;IAAE,MAAM;IAAO;GAAK,CAAC;GACxD,aAAa,KAAK,YAAY;EAChC,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;GAEtG,MAAM;EACR;CACF;CAEA,OAAO;EAAE;EAAe;CAAa;AACvC"}