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
package/README.md CHANGED
@@ -1,12 +1,15 @@
1
1
  # Chain Insights
2
2
 
3
- Chain Insights is an AML investigation framework on top of GraphRAG MCP. It
4
- turns graph access into analyst-ready workflows: address screening, fund-flow
5
- tracing, scam topology discovery, case files, evidence, dossiers, reports, and
6
- graph visualizations.
3
+ [Website](https://chain-insights.ai) | [GitHub](https://github.com/chainswarm/chain-insights) | [npm](https://www.npmjs.com/package/chain-insights)
7
4
 
8
- GraphRAG MCP exposes generic graph tools. Chain Insights adds AML tools and
9
- investigation workflow around them.
5
+ Chain Insights is an open-source AML investigation toolkit for AI agents and
6
+ analysts. Install it from npm to screen blockchain addresses, trace funds,
7
+ expand scam topologies, manage case evidence, and generate graph reports from
8
+ Chain Insights graph intelligence.
9
+
10
+ The hosted GraphRAG MCP access path is paid through x402. The CLI and MCP proxy
11
+ handle local wallet status, paid graph calls, approved test access, case files,
12
+ evidence pointers, dossiers, and reports.
10
13
 
11
14
  ## What You Can Do Today
12
15
 
@@ -20,7 +23,13 @@ investigation workflow around them.
20
23
 
21
24
  ## Quick Start
22
25
 
23
- From an installed package:
26
+ Install from npm:
27
+
28
+ ```bash
29
+ npm install -g chain-insights
30
+ ```
31
+
32
+ Check the CLI:
24
33
 
25
34
  ```bash
26
35
  cia --version
@@ -47,13 +56,15 @@ Check the configured endpoint and current GraphRAG MCP capabilities:
47
56
 
48
57
  ```bash
49
58
  cia config get graphMcpEndpoint
59
+ cia wallet balance
50
60
  cia mcp networks
51
61
  cia mcp tools --refresh
52
62
  ```
53
63
 
54
- If network or tool discovery fails, fix endpoint/auth first; the CLI can still
55
- initialize workspaces and manage cases without a reachable GraphRAG MCP
56
- endpoint.
64
+ GraphRAG MCP calls use x402 paid mode by default unless you configure approved
65
+ test access or local debug access. If network or tool discovery fails, fix
66
+ endpoint/auth/payment first; the CLI can still initialize workspaces and manage
67
+ cases without a reachable GraphRAG MCP endpoint.
57
68
 
58
69
  Open a case and run a small investigation:
59
70
 
@@ -111,25 +122,25 @@ Agent or CLI user
111
122
  -> Chain Insights CLI / MCP proxy
112
123
  -> local config, wallet, workspace, cases, evidence, reports
113
124
  -> GraphRAG MCP
114
- -> live_topology, archive_topology, facts
125
+ -> graph intelligence for AML workflows
115
126
  ```
116
127
 
117
128
  Chain Insights stores investigation outputs in initialized local workspaces.
118
129
  GraphRAG MCP performs graph-language reads against network-specific graph
119
130
  layers.
120
131
 
121
- ## Topology And Facts
132
+ ## Graph Access
122
133
 
123
- Graph queries must choose a layer explicitly:
134
+ Graph queries must choose the right read layer explicitly:
124
135
 
125
- | Layer | Backing data |
136
+ | Layer | Use it for |
126
137
  | --- | --- |
127
- | `live_topology` | Memgraph RAM topology for current graph traversal |
128
- | `archive_topology` | StarRocks historical topology for warehouse-scale flow reads |
129
- | `facts` | StarRocks facts for feature and enrichment reads |
138
+ | `live_topology` | Recent topology and fast traversal |
139
+ | `archive_topology` | Historical fund-flow context |
140
+ | `facts` | Labels, features, risk scores, assets, and enrichment |
130
141
 
131
142
  Use `graph_query_batch` when related reads should share one call and one
132
- result envelope.
143
+ result envelope. Paid hosted calls are settled through x402.
133
144
 
134
145
  ## AML Tools
135
146
 
@@ -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 = require("node:fs");
@@ -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 path from "node:path";
3
3
  import fs from "node:fs";
4
4
  import os from "node:os";
@@ -47,4 +47,4 @@ function activeDataDir(fallbackDataDir) {
47
47
  //#endregion
48
48
  export { active_exports as n, findActiveWorkspace as r, activeCasesRoot as t };
49
49
 
50
- //# sourceMappingURL=active-BSrxLKwn.mjs.map
50
+ //# sourceMappingURL=active-ByNgjuAg.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"active-BSrxLKwn.mjs","names":[],"sources":["../src/workspace/active.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\ninterface WorkspaceConfig {\n schema?: string\n workspace_root?: string\n cases_dir?: string\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n casesRoot: string\n}\n\nfunction workspaceFromRoot(rootCandidate: string): ActiveWorkspace | null {\n const root = path.resolve(rootCandidate)\n const metadataDir = path.join(root, '.chain-insights')\n const markerPath = path.join(metadataDir, 'workspace.json')\n if (!fs.existsSync(markerPath)) return null\n\n const parsed = JSON.parse(fs.readFileSync(markerPath, 'utf8')) as WorkspaceConfig\n if (parsed.schema !== 'chain-insights.workspace.v1') return null\n\n const workspaceRoot = path.resolve(parsed.workspace_root ?? root)\n const casesDir = parsed.cases_dir ?? 'cases'\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n casesRoot: path.resolve(workspaceRoot, casesDir),\n }\n}\n\nexport function findActiveWorkspace(startDir = process.cwd()): ActiveWorkspace | null {\n const envWorkspace = process.env['CHAIN_INSIGHTS_WORKSPACE']?.trim()\n if (envWorkspace) {\n const active = workspaceFromRoot(envWorkspace)\n if (active) return active\n }\n\n let current = path.resolve(startDir)\n while (true) {\n const active = workspaceFromRoot(current)\n if (active) return active\n\n const parent = path.dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nexport function activeMetadataDir(): string {\n return findActiveWorkspace()?.metadataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n\nexport function activeCasesRoot(): string {\n return findActiveWorkspace()?.casesRoot ?? path.join(os.homedir(), '.chain-insights', 'cases')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAS,kBAAkB,eAA+C;CACxE,MAAM,OAAO,KAAK,QAAQ,cAAc;CACxC,MAAM,cAAc,KAAK,KAAK,MAAM,kBAAkB;CACtD,MAAM,aAAa,KAAK,KAAK,aAAa,iBAAiB;AAC3D,KAAI,CAAC,GAAG,WAAW,WAAW,CAAE,QAAO;CAEvC,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;AAC9D,KAAI,OAAO,WAAW,8BAA+B,QAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,OAAO,kBAAkB,KAAK;CACjE,MAAM,WAAW,OAAO,aAAa;AACrC,QAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,kBAAkB;EACxD,WAAW,KAAK,QAAQ,eAAe,SAAS;EACjD;;AAGH,SAAgB,oBAAoB,WAAW,QAAQ,KAAK,EAA0B;CACpF,MAAM,eAAe,QAAQ,IAAI,6BAA6B,MAAM;AACpE,KAAI,cAAc;EAChB,MAAM,SAAS,kBAAkB,aAAa;AAC9C,MAAI,OAAQ,QAAO;;CAGrB,IAAI,UAAU,KAAK,QAAQ,SAAS;AACpC,QAAO,MAAM;EACX,MAAM,SAAS,kBAAkB,QAAQ;AACzC,MAAI,OAAQ,QAAO;EAEnB,MAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,MAAI,WAAW,QAAS,QAAO;AAC/B,YAAU;;;AAQd,SAAgB,kBAA0B;AACxC,QAAO,qBAAqB,EAAE,aAAa,KAAK,KAAK,GAAG,SAAS,EAAE,mBAAmB,QAAQ;;AAGhG,SAAgB,cAAc,iBAAkC;AAC9D,QAAO,qBAAqB,EAAE,QAAQ,mBAAmB,KAAK,KAAK,GAAG,SAAS,EAAE,kBAAkB"}
1
+ {"version":3,"file":"active-ByNgjuAg.mjs","names":[],"sources":["../src/workspace/active.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\ninterface WorkspaceConfig {\n schema?: string\n workspace_root?: string\n cases_dir?: string\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n casesRoot: string\n}\n\nfunction workspaceFromRoot(rootCandidate: string): ActiveWorkspace | null {\n const root = path.resolve(rootCandidate)\n const metadataDir = path.join(root, '.chain-insights')\n const markerPath = path.join(metadataDir, 'workspace.json')\n if (!fs.existsSync(markerPath)) return null\n\n const parsed = JSON.parse(fs.readFileSync(markerPath, 'utf8')) as WorkspaceConfig\n if (parsed.schema !== 'chain-insights.workspace.v1') return null\n\n const workspaceRoot = path.resolve(parsed.workspace_root ?? root)\n const casesDir = parsed.cases_dir ?? 'cases'\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n casesRoot: path.resolve(workspaceRoot, casesDir),\n }\n}\n\nexport function findActiveWorkspace(startDir = process.cwd()): ActiveWorkspace | null {\n const envWorkspace = process.env['CHAIN_INSIGHTS_WORKSPACE']?.trim()\n if (envWorkspace) {\n const active = workspaceFromRoot(envWorkspace)\n if (active) return active\n }\n\n let current = path.resolve(startDir)\n while (true) {\n const active = workspaceFromRoot(current)\n if (active) return active\n\n const parent = path.dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nexport function activeMetadataDir(): string {\n return findActiveWorkspace()?.metadataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n\nexport function activeCasesRoot(): string {\n return findActiveWorkspace()?.casesRoot ?? path.join(os.homedir(), '.chain-insights', 'cases')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAS,kBAAkB,eAA+C;CACxE,MAAM,OAAO,KAAK,QAAQ,aAAa;CACvC,MAAM,cAAc,KAAK,KAAK,MAAM,iBAAiB;CACrD,MAAM,aAAa,KAAK,KAAK,aAAa,gBAAgB;CAC1D,IAAI,CAAC,GAAG,WAAW,UAAU,GAAG,OAAO;CAEvC,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,MAAM,CAAC;CAC7D,IAAI,OAAO,WAAW,+BAA+B,OAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,OAAO,kBAAkB,IAAI;CAChE,MAAM,WAAW,OAAO,aAAa;CACrC,OAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,iBAAiB;EACvD,WAAW,KAAK,QAAQ,eAAe,QAAQ;CACjD;AACF;AAEA,SAAgB,oBAAoB,WAAW,QAAQ,IAAI,GAA2B;CACpF,MAAM,eAAe,QAAQ,IAAI,6BAA6B,KAAK;CACnE,IAAI,cAAc;EAChB,MAAM,SAAS,kBAAkB,YAAY;EAC7C,IAAI,QAAQ,OAAO;CACrB;CAEA,IAAI,UAAU,KAAK,QAAQ,QAAQ;CACnC,OAAO,MAAM;EACX,MAAM,SAAS,kBAAkB,OAAO;EACxC,IAAI,QAAQ,OAAO;EAEnB,MAAM,SAAS,KAAK,QAAQ,OAAO;EACnC,IAAI,WAAW,SAAS,OAAO;EAC/B,UAAU;CACZ;AACF;AAMA,SAAgB,kBAA0B;CACxC,OAAO,oBAAoB,GAAG,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,OAAO;AAC/F;AAEA,SAAgB,cAAc,iBAAkC;CAC9D,OAAO,oBAAoB,GAAG,QAAQ,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB;AACpG"}
@@ -97,7 +97,7 @@ function createApp() {
97
97
  ts: Date.now()
98
98
  }));
99
99
  app.get("/status", async (c) => {
100
- const { loadConfig } = await import("./config-BwrBYmiC.mjs").then((n) => n.t);
100
+ const { loadConfig } = await import("./config-9KYXaAv-.mjs").then((n) => n.t);
101
101
  const config = await loadConfig();
102
102
  return c.json({
103
103
  dataDir: config.dataDir,
@@ -115,7 +115,7 @@ function createApp() {
115
115
  app.get("/graph-reports/:filename", async (c) => {
116
116
  const filename = c.req.param("filename");
117
117
  if (!isSafeGraphReportFilename(filename)) return c.json({ error: "Invalid graph report filename" }, 400);
118
- const { workspaceOutputPaths } = await import("./output-root-CmWM7aV2.mjs").then((n) => n.t);
118
+ const { workspaceOutputPaths } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
119
119
  const paths = workspaceOutputPaths();
120
120
  const graphPath = path.resolve(paths.reportGraphsRoot, filename);
121
121
  if (!withinRoot(paths.reportGraphsRoot, graphPath)) return c.json({ error: "Invalid graph report filename" }, 400);
@@ -134,7 +134,7 @@ function createApp() {
134
134
  return c.json({ error: "Invalid graph report filename" }, 400);
135
135
  });
136
136
  app.get("/workspace/tree", async (c) => {
137
- const { workspaceOutputPaths } = await import("./output-root-CmWM7aV2.mjs").then((n) => n.t);
137
+ const { workspaceOutputPaths } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
138
138
  const paths = workspaceOutputPaths();
139
139
  const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS);
140
140
  return c.json({
@@ -152,4 +152,4 @@ function createApp() {
152
152
  //#endregion
153
153
  export { createApp as t };
154
154
 
155
- //# sourceMappingURL=app-BjjuQM0B.mjs.map
155
+ //# sourceMappingURL=app-DdWQF_zb.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-BjjuQM0B.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\n\nconst WORKSPACE_TREE_ROOTS = ['cases', 'reports', '.chain-insights/schema']\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n path: string\n type: 'file' | 'directory' | 'symlink'\n size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n const relative = path.relative(path.resolve(root), path.resolve(target))\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n try {\n const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n return withinRoot(realRoot, realTarget)\n } catch {\n return false\n }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n workspaceRoot: string,\n roots = WORKSPACE_TREE_ROOTS,\n maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n const entries: WorkspaceTreeEntry[] = []\n const root = path.resolve(workspaceRoot)\n\n async function visit(target: string, depth: number): Promise<void> {\n const resolved = path.resolve(target)\n if (!withinRoot(root, resolved)) return\n\n let info: Awaited<ReturnType<typeof lstat>>\n try {\n info = await lstat(resolved)\n } catch {\n return\n }\n\n const type = info.isSymbolicLink() ? 'symlink' : info.isDirectory() ? 'directory' : info.isFile() ? 'file' : null\n if (!type) return\n\n const entry: WorkspaceTreeEntry = {\n path: toWorkspaceRelative(root, resolved),\n type,\n }\n if (type === 'file') entry.size = info.size\n entries.push(entry)\n\n if (type !== 'directory' || depth >= maxDepth) return\n if (!await realPathWithinRoot(root, resolved)) return\n\n let children: string[]\n try {\n children = await readdir(resolved)\n } catch {\n return\n }\n\n for (const child of children.sort()) {\n await visit(path.join(resolved, child), depth + 1)\n }\n }\n\n for (const rootName of roots) {\n const target = path.resolve(root, rootName)\n if (withinRoot(root, target)) await visit(target, 0)\n }\n\n return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n const home = os.homedir()\n const filename = `${vizId}.html`\n\n // 1. Check central standalone directory first (fast, single path)\n const centralPath = path.join(home, '.chain-insights', 'viz', filename)\n try {\n return await readFile(centralPath, 'utf-8')\n } catch { /* not found here, continue */ }\n\n // 2. Check per-case directory using vizId prefix (case-based vizs use <caseId>_<timestamp>)\n // The vizId for case-based vizs is formatted as <case-id>_<timestamp>,\n // so extract the case-id prefix to check its directory first.\n const underscoreIdx = vizId.lastIndexOf('_')\n if (underscoreIdx > 0) {\n const possibleCaseId = vizId.substring(0, underscoreIdx)\n const casePath = path.join(home, '.chain-insights', 'cases', possibleCaseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not found here, continue */ }\n }\n\n // 3. Fallback: scan all case directories (CONTEXT.md: ~/.chain-insights/cases/<case-id>/viz/)\n const casesDir = path.join(home, '.chain-insights', 'cases')\n try {\n const cases = await readdir(casesDir)\n for (const caseId of cases) {\n const casePath = path.join(casesDir, caseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not in this case dir */ }\n }\n } catch { /* cases dir doesn't exist */ }\n\n return null\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n return (\n filename.endsWith('.graph.json') &&\n /^[A-Za-z0-9._-]+$/.test(filename) &&\n !filename.includes('..') &&\n !filename.includes('/') &&\n !filename.includes('\\\\')\n )\n}\n\nexport function createApp(): Hono {\n const app = new Hono()\n\n app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n app.get('/status', async (c) => {\n const { loadConfig } = await import('../config/index.js')\n const config = await loadConfig()\n return c.json({\n dataDir: config.dataDir,\n graphMcpMode: config.graphMcpMode,\n server: 'running',\n })\n })\n\n app.get('/viz/:id', async (c) => {\n const id = c.req.param('id')\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n return c.json({ error: 'Invalid visualization ID' }, 400)\n }\n const html = await findVizHtml(id)\n if (!html) {\n return c.json({ error: 'Visualization not found' }, 404)\n }\n return c.html(html)\n })\n\n app.get('/graph-reports/:filename', async (c) => {\n const filename = c.req.param('filename')\n if (!isSafeGraphReportFilename(filename)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n if (!await realPathWithinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n\n try {\n const graph = await readFile(graphPath, 'utf-8')\n return c.body(graph, 200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n } catch {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n })\n\n app.get('/graph-reports/*', (c) => {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n })\n\n app.get('/workspace/tree', async (c) => {\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n return c.json({\n schema: 'chain-insights.workspace-tree.v1',\n root: paths.root,\n entries,\n })\n })\n\n app.onError((err, c) => {\n console.error(err)\n return c.json({ error: 'Internal server error' }, 500)\n })\n\n return app\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app-DdWQF_zb.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\n\nconst WORKSPACE_TREE_ROOTS = ['cases', 'reports', '.chain-insights/schema']\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n path: string\n type: 'file' | 'directory' | 'symlink'\n size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n const relative = path.relative(path.resolve(root), path.resolve(target))\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n try {\n const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n return withinRoot(realRoot, realTarget)\n } catch {\n return false\n }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n workspaceRoot: string,\n roots = WORKSPACE_TREE_ROOTS,\n maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n const entries: WorkspaceTreeEntry[] = []\n const root = path.resolve(workspaceRoot)\n\n async function visit(target: string, depth: number): Promise<void> {\n const resolved = path.resolve(target)\n if (!withinRoot(root, resolved)) return\n\n let info: Awaited<ReturnType<typeof lstat>>\n try {\n info = await lstat(resolved)\n } catch {\n return\n }\n\n const type = info.isSymbolicLink() ? 'symlink' : info.isDirectory() ? 'directory' : info.isFile() ? 'file' : null\n if (!type) return\n\n const entry: WorkspaceTreeEntry = {\n path: toWorkspaceRelative(root, resolved),\n type,\n }\n if (type === 'file') entry.size = info.size\n entries.push(entry)\n\n if (type !== 'directory' || depth >= maxDepth) return\n if (!await realPathWithinRoot(root, resolved)) return\n\n let children: string[]\n try {\n children = await readdir(resolved)\n } catch {\n return\n }\n\n for (const child of children.sort()) {\n await visit(path.join(resolved, child), depth + 1)\n }\n }\n\n for (const rootName of roots) {\n const target = path.resolve(root, rootName)\n if (withinRoot(root, target)) await visit(target, 0)\n }\n\n return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n const home = os.homedir()\n const filename = `${vizId}.html`\n\n // 1. Check central standalone directory first (fast, single path)\n const centralPath = path.join(home, '.chain-insights', 'viz', filename)\n try {\n return await readFile(centralPath, 'utf-8')\n } catch { /* not found here, continue */ }\n\n // 2. Check per-case directory using vizId prefix (case-based vizs use <caseId>_<timestamp>)\n // The vizId for case-based vizs is formatted as <case-id>_<timestamp>,\n // so extract the case-id prefix to check its directory first.\n const underscoreIdx = vizId.lastIndexOf('_')\n if (underscoreIdx > 0) {\n const possibleCaseId = vizId.substring(0, underscoreIdx)\n const casePath = path.join(home, '.chain-insights', 'cases', possibleCaseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not found here, continue */ }\n }\n\n // 3. Fallback: scan all case directories (CONTEXT.md: ~/.chain-insights/cases/<case-id>/viz/)\n const casesDir = path.join(home, '.chain-insights', 'cases')\n try {\n const cases = await readdir(casesDir)\n for (const caseId of cases) {\n const casePath = path.join(casesDir, caseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not in this case dir */ }\n }\n } catch { /* cases dir doesn't exist */ }\n\n return null\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n return (\n filename.endsWith('.graph.json') &&\n /^[A-Za-z0-9._-]+$/.test(filename) &&\n !filename.includes('..') &&\n !filename.includes('/') &&\n !filename.includes('\\\\')\n )\n}\n\nexport function createApp(): Hono {\n const app = new Hono()\n\n app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n app.get('/status', async (c) => {\n const { loadConfig } = await import('../config/index.js')\n const config = await loadConfig()\n return c.json({\n dataDir: config.dataDir,\n graphMcpMode: config.graphMcpMode,\n server: 'running',\n })\n })\n\n app.get('/viz/:id', async (c) => {\n const id = c.req.param('id')\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n return c.json({ error: 'Invalid visualization ID' }, 400)\n }\n const html = await findVizHtml(id)\n if (!html) {\n return c.json({ error: 'Visualization not found' }, 404)\n }\n return c.html(html)\n })\n\n app.get('/graph-reports/:filename', async (c) => {\n const filename = c.req.param('filename')\n if (!isSafeGraphReportFilename(filename)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n if (!await realPathWithinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n\n try {\n const graph = await readFile(graphPath, 'utf-8')\n return c.body(graph, 200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n } catch {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n })\n\n app.get('/graph-reports/*', (c) => {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n })\n\n app.get('/workspace/tree', async (c) => {\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n return c.json({\n schema: 'chain-insights.workspace-tree.v1',\n root: paths.root,\n entries,\n })\n })\n\n app.onError((err, c) => {\n console.error(err)\n return c.json({ error: 'Internal server error' }, 500)\n })\n\n return app\n}\n"],"mappings":"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"}
@@ -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");
@@ -100,7 +100,7 @@ function createApp() {
100
100
  ts: Date.now()
101
101
  }));
102
102
  app.get("/status", async (c) => {
103
- const { loadConfig } = await Promise.resolve().then(() => require("./config-Bmdl5hdk.cjs")).then((n) => n.config_exports);
103
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-BhYbhLDI.cjs")).then((n) => n.config_exports);
104
104
  const config = await loadConfig();
105
105
  return c.json({
106
106
  dataDir: config.dataDir,
@@ -118,7 +118,7 @@ function createApp() {
118
118
  app.get("/graph-reports/:filename", async (c) => {
119
119
  const filename = c.req.param("filename");
120
120
  if (!isSafeGraphReportFilename(filename)) return c.json({ error: "Invalid graph report filename" }, 400);
121
- const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-CFYms3ad.cjs")).then((n) => n.output_root_exports);
121
+ const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
122
122
  const paths = workspaceOutputPaths();
123
123
  const graphPath = node_path.default.resolve(paths.reportGraphsRoot, filename);
124
124
  if (!withinRoot(paths.reportGraphsRoot, graphPath)) return c.json({ error: "Invalid graph report filename" }, 400);
@@ -137,7 +137,7 @@ function createApp() {
137
137
  return c.json({ error: "Invalid graph report filename" }, 400);
138
138
  });
139
139
  app.get("/workspace/tree", async (c) => {
140
- const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-CFYms3ad.cjs")).then((n) => n.output_root_exports);
140
+ const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
141
141
  const paths = workspaceOutputPaths();
142
142
  const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS);
143
143
  return c.json({
@@ -1,4 +1,4 @@
1
- import { t as createApp } from "./app-BjjuQM0B.mjs";
1
+ import { t as createApp } from "./app-DdWQF_zb.mjs";
2
2
  import { serve } from "@hono/node-server";
3
3
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
4
4
  //#region src/mcp/artifact-server.ts
@@ -45,4 +45,4 @@ async function ensureArtifactServer(port) {
45
45
  //#endregion
46
46
  export { ensureArtifactServer };
47
47
 
48
- //# sourceMappingURL=artifact-server-Dxz5YbuQ.mjs.map
48
+ //# sourceMappingURL=artifact-server-4DiMvwhC.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-server-Dxz5YbuQ.mjs","names":["delay"],"sources":["../src/mcp/artifact-server.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { createApp } from '../server/app.js'\n\ntype ArtifactServer = ReturnType<typeof serve>\n\nconst servers = new Map<number, ArtifactServer>()\n\nasync function isHealthy(port: number): Promise<boolean> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 500)\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n })\n return response.ok\n } catch {\n return false\n } finally {\n clearTimeout(timeout)\n }\n}\n\nasync function waitUntilHealthy(port: number): Promise<void> {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (await isHealthy(port)) return\n await delay(50)\n }\n throw new Error(`Graph report server did not become healthy on 127.0.0.1:${port}`)\n}\n\nexport async function ensureArtifactServer(port: number): Promise<void> {\n if (servers.has(port)) return\n if (await isHealthy(port)) return\n\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1',\n port,\n })\n servers.set(port, server)\n server.on('error', (err) => {\n servers.delete(port)\n process.stderr.write(`Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`)\n })\n\n try {\n await waitUntilHealthy(port)\n } catch (err) {\n servers.delete(port)\n server.close()\n throw err\n }\n}\n\nexport function closeArtifactServers(): void {\n for (const [port, server] of servers.entries()) {\n server.close()\n servers.delete(port)\n }\n}\n"],"mappings":";;;;AAMA,MAAM,0BAAU,IAAI,KAA6B;AAEjD,eAAe,UAAU,MAAgC;CACvD,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,UAAU,iBAAiB,WAAW,OAAO,EAAE,IAAI;AACzD,KAAI;AAIF,UAAO,MAHgB,MAAM,oBAAoB,KAAK,UAAU,EAC9D,QAAQ,WAAW,QACpB,CAAC,EACc;SACV;AACN,SAAO;WACC;AACR,eAAa,QAAQ;;;AAIzB,eAAe,iBAAiB,MAA6B;AAC3D,MAAK,IAAI,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;AAChD,MAAI,MAAM,UAAU,KAAK,CAAE;AAC3B,QAAMA,aAAM,GAAG;;AAEjB,OAAM,IAAI,MAAM,2DAA2D,OAAO;;AAGpF,eAAsB,qBAAqB,MAA6B;AACtE,KAAI,QAAQ,IAAI,KAAK,CAAE;AACvB,KAAI,MAAM,UAAU,KAAK,CAAE;CAG3B,MAAM,SAAS,MAAM;EACnB,OAFU,WAEA,CAAC;EACX,UAAU;EACV;EACD,CAAC;AACF,SAAQ,IAAI,MAAM,OAAO;AACzB,QAAO,GAAG,UAAU,QAAQ;AAC1B,UAAQ,OAAO,KAAK;AACpB,UAAQ,OAAO,MAAM,0DAA0D,KAAK,IAAK,IAAc,QAAQ,IAAI;GACnH;AAEF,KAAI;AACF,QAAM,iBAAiB,KAAK;UACrB,KAAK;AACZ,UAAQ,OAAO,KAAK;AACpB,SAAO,OAAO;AACd,QAAM"}
1
+ {"version":3,"file":"artifact-server-4DiMvwhC.mjs","names":["delay"],"sources":["../src/mcp/artifact-server.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { createApp } from '../server/app.js'\n\ntype ArtifactServer = ReturnType<typeof serve>\n\nconst servers = new Map<number, ArtifactServer>()\n\nasync function isHealthy(port: number): Promise<boolean> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 500)\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n })\n return response.ok\n } catch {\n return false\n } finally {\n clearTimeout(timeout)\n }\n}\n\nasync function waitUntilHealthy(port: number): Promise<void> {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (await isHealthy(port)) return\n await delay(50)\n }\n throw new Error(`Graph report server did not become healthy on 127.0.0.1:${port}`)\n}\n\nexport async function ensureArtifactServer(port: number): Promise<void> {\n if (servers.has(port)) return\n if (await isHealthy(port)) return\n\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1',\n port,\n })\n servers.set(port, server)\n server.on('error', (err) => {\n servers.delete(port)\n process.stderr.write(`Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`)\n })\n\n try {\n await waitUntilHealthy(port)\n } catch (err) {\n servers.delete(port)\n server.close()\n throw err\n }\n}\n\nexport function closeArtifactServers(): void {\n for (const [port, server] of servers.entries()) {\n server.close()\n servers.delete(port)\n }\n}\n"],"mappings":";;;;AAMA,MAAM,0BAAU,IAAI,IAA4B;AAEhD,eAAe,UAAU,MAAgC;CACvD,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,GAAG;CACxD,IAAI;EAIF,QAAO,MAHgB,MAAM,oBAAoB,KAAK,UAAU,EAC9D,QAAQ,WAAW,OACrB,CAAC,GACe;CAClB,QAAQ;EACN,OAAO;CACT,UAAU;EACR,aAAa,OAAO;CACtB;AACF;AAEA,eAAe,iBAAiB,MAA6B;CAC3D,KAAK,IAAI,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;EAChD,IAAI,MAAM,UAAU,IAAI,GAAG;EAC3B,MAAMA,aAAM,EAAE;CAChB;CACA,MAAM,IAAI,MAAM,2DAA2D,MAAM;AACnF;AAEA,eAAsB,qBAAqB,MAA6B;CACtE,IAAI,QAAQ,IAAI,IAAI,GAAG;CACvB,IAAI,MAAM,UAAU,IAAI,GAAG;CAG3B,MAAM,SAAS,MAAM;EACnB,OAFU,UAED,EAAE;EACX,UAAU;EACV;CACF,CAAC;CACD,QAAQ,IAAI,MAAM,MAAM;CACxB,OAAO,GAAG,UAAU,QAAQ;EAC1B,QAAQ,OAAO,IAAI;EACnB,QAAQ,OAAO,MAAM,0DAA0D,KAAK,IAAK,IAAc,QAAQ,GAAG;CACpH,CAAC;CAED,IAAI;EACF,MAAM,iBAAiB,IAAI;CAC7B,SAAS,KAAK;EACZ,QAAQ,OAAO,IAAI;EACnB,OAAO,MAAM;EACb,MAAM;CACR;AACF"}
@@ -1,5 +1,4 @@
1
- require("./chunk-CZWwpsFl.cjs");
2
- const require_app = require("./app-Dq1TdB6p.cjs");
1
+ const require_app = require("./app-DxlQE_P5.cjs");
3
2
  let _hono_node_server = require("@hono/node-server");
4
3
  let node_timers_promises = require("node:timers/promises");
5
4
  //#region src/mcp/artifact-server.ts
@@ -26,4 +26,4 @@ function parseMcpCallArgs(rawArgs) {
26
26
  //#endregion
27
27
  export { parseMcpCallArgs };
28
28
 
29
- //# sourceMappingURL=call-args-Lk_wOJxd.mjs.map
29
+ //# sourceMappingURL=call-args-DPXdX3_D.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"call-args-Lk_wOJxd.mjs","names":[],"sources":["../src/mcp/call-args.ts"],"sourcesContent":["const NUMERIC_ARG_KEYS = new Set([\n 'per_query_timeout_seconds',\n 'incident_timestamp_ms',\n 'max_hops',\n 'per_address_limit',\n 'min_amount_sum',\n])\n\nfunction parseMcpArgValue(key: string, value: string): unknown {\n const trimmed = value.trim()\n if (!trimmed) return value\n\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return JSON.parse(trimmed) as unknown\n }\n\n if (NUMERIC_ARG_KEYS.has(key) && /^-?\\d+(?:\\.\\d+)?$/.test(trimmed)) {\n return Number(trimmed)\n }\n\n return value\n}\n\nexport function parseMcpCallArgs(rawArgs: string[]): Record<string, unknown> {\n const args: Record<string, unknown> = {}\n for (const pair of rawArgs) {\n const eqIdx = pair.indexOf('=')\n if (eqIdx === -1) {\n throw new Error(`Invalid arg format: ${pair} (expected key=value)`)\n }\n const key = pair.slice(0, eqIdx)\n args[key] = parseMcpArgValue(key, pair.slice(eqIdx + 1))\n }\n return args\n}\n"],"mappings":";AAAA,MAAM,mBAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,iBAAiB,KAAa,OAAwB;CAC7D,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI,QAAQ,WAAW,IAAI,IAAI,QAAQ,WAAW,IAAI,CACpD,QAAO,KAAK,MAAM,QAAQ;AAG5B,KAAI,iBAAiB,IAAI,IAAI,IAAI,oBAAoB,KAAK,QAAQ,CAChE,QAAO,OAAO,QAAQ;AAGxB,QAAO;;AAGT,SAAgB,iBAAiB,SAA4C;CAC3E,MAAM,OAAgC,EAAE;AACxC,MAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,MAAI,UAAU,GACZ,OAAM,IAAI,MAAM,uBAAuB,KAAK,uBAAuB;EAErE,MAAM,MAAM,KAAK,MAAM,GAAG,MAAM;AAChC,OAAK,OAAO,iBAAiB,KAAK,KAAK,MAAM,QAAQ,EAAE,CAAC;;AAE1D,QAAO"}
1
+ {"version":3,"file":"call-args-DPXdX3_D.mjs","names":[],"sources":["../src/mcp/call-args.ts"],"sourcesContent":["const NUMERIC_ARG_KEYS = new Set([\n 'per_query_timeout_seconds',\n 'incident_timestamp_ms',\n 'max_hops',\n 'per_address_limit',\n 'min_amount_sum',\n])\n\nfunction parseMcpArgValue(key: string, value: string): unknown {\n const trimmed = value.trim()\n if (!trimmed) return value\n\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n return JSON.parse(trimmed) as unknown\n }\n\n if (NUMERIC_ARG_KEYS.has(key) && /^-?\\d+(?:\\.\\d+)?$/.test(trimmed)) {\n return Number(trimmed)\n }\n\n return value\n}\n\nexport function parseMcpCallArgs(rawArgs: string[]): Record<string, unknown> {\n const args: Record<string, unknown> = {}\n for (const pair of rawArgs) {\n const eqIdx = pair.indexOf('=')\n if (eqIdx === -1) {\n throw new Error(`Invalid arg format: ${pair} (expected key=value)`)\n }\n const key = pair.slice(0, eqIdx)\n args[key] = parseMcpArgValue(key, pair.slice(eqIdx + 1))\n }\n return args\n}\n"],"mappings":";AAAA,MAAM,mBAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,iBAAiB,KAAa,OAAwB;CAC7D,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,SAAS,OAAO;CAErB,IAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,WAAW,GAAG,GACnD,OAAO,KAAK,MAAM,OAAO;CAG3B,IAAI,iBAAiB,IAAI,GAAG,KAAK,oBAAoB,KAAK,OAAO,GAC/D,OAAO,OAAO,OAAO;CAGvB,OAAO;AACT;AAEA,SAAgB,iBAAiB,SAA4C;CAC3E,MAAM,OAAgC,CAAC;CACvC,KAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,QAAQ,KAAK,QAAQ,GAAG;EAC9B,IAAI,UAAU,IACZ,MAAM,IAAI,MAAM,uBAAuB,KAAK,sBAAsB;EAEpE,MAAM,MAAM,KAAK,MAAM,GAAG,KAAK;EAC/B,KAAK,OAAO,iBAAiB,KAAK,KAAK,MAAM,QAAQ,CAAC,CAAC;CACzD;CACA,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- import { i as resolveGraphMcpEndpoint } from "./client-D4Bq0rp9.mjs";
1
+ import { a as resolveGraphMcpEndpoint } from "./client-D4_hd4AP.mjs";
2
2
  //#region src/mcp/capabilities.ts
3
3
  function metadataNetworksUrl(endpoint) {
4
4
  const url = new URL(endpoint);
@@ -81,4 +81,4 @@ function formatNetworkCapabilities(document) {
81
81
  //#endregion
82
82
  export { fetchNetworkCapabilities, formatNetworkCapabilities };
83
83
 
84
- //# sourceMappingURL=capabilities-DliMBim-.mjs.map
84
+ //# sourceMappingURL=capabilities-BShqspb-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities-DliMBim-.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkRetention {\n mode: 'full_history' | 'rolling_window' | 'expanding_then_rolling' | 'bounded_range' | 'unknown' | string\n window_days?: number\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n started_at?: string\n rolls_after_at?: string\n current_window_seconds?: number\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n retention?: NetworkRetention | null\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n layers: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n }\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'mcpAuthToken' | 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint' | 'mcpEndpoint'>,\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim() || config.mcpAuthToken?.trim()\n if (token) {\n headers.set('X-MCP-Debug-Token', token)\n headers.set('Authorization', `Bearer ${token}`)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = await response.json() as NetworkCapabilitiesDocument\n if (parsed.schema !== 'chain-insights.network-capabilities.v1' || !Array.isArray(parsed.networks)) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return parsed\n}\n\nfunction layerValue(network: NetworkCapability, layer: string): string {\n const capability = network.layers[layer]\n if (!capability?.enabled) return 'no'\n return 'yes'\n}\n\nfunction availableToolsLabel(network: NetworkCapability): string {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.length > 0 ? tools.join(', ') : 'none'\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange = coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange = coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : 'dates unknown'\n if (blockRange === 'blocks unknown' && dateRange === 'dates unknown') return 'unknown'\n return `${blockRange} / ${dateRange}`\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const headers = ['Network', 'Topology', 'Facts', 'Risk', 'Dataset', 'Available tools']\n const widths = [14, 10, 8, 8, 38, 64]\n const row = (values: string[]) => values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [\n row(headers),\n widths.map((width) => '-'.repeat(width)).join(' '),\n ...document.networks.map((network) => row([\n network.display_name || network.network,\n layerValue(network, 'topology'),\n layerValue(network, 'facts'),\n layerValue(network, 'risk'),\n datasetLabel(network),\n availableToolsLabel(network),\n ])),\n ].join('\\n')\n}\n"],"mappings":";;AAgDA,SAAS,oBAAoB,UAAuB;CAClD,MAAM,MAAM,IAAI,IAAI,SAAS;AAC7B,KAAI,WAAW;AACf,KAAI,SAAS;AACb,KAAI,OAAO;AACX,QAAO;;AAGT,eAAsB,yBACpB,QACsC;CAEtC,MAAM,UAAU,oBADC,wBAAwB,OACG,CAAC;CAC7C,MAAM,UAAU,IAAI,SAAS;CAC7B,MAAM,QAAQ,OAAO,mBAAmB,MAAM,IAAI,OAAO,cAAc,MAAM;AAC7E,KAAI,OAAO;AACT,UAAQ,IAAI,qBAAqB,MAAM;AACvC,UAAQ,IAAI,iBAAiB,UAAU,QAAQ;;CAEjD,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,MAAM,SAAS,EAAE,SAAS,CAAC;UACrC,KAAK;AACZ,QAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAK,IAAc,UAAU;;AAE9F,KAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,uCAAuC,QAAQ,SAAS,SAAS,SAAS;CAE5F,MAAM,SAAS,MAAM,SAAS,MAAM;AACpC,KAAI,OAAO,WAAW,4CAA4C,CAAC,MAAM,QAAQ,OAAO,SAAS,CAC/F,OAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAO;;AAGT,SAAS,WAAW,SAA4B,OAAuB;AAErE,KAAI,CADe,QAAQ,OAAO,QACjB,QAAS,QAAO;AACjC,QAAO;;AAGT,SAAS,oBAAoB,SAAoC;CAC/D,MAAM,QAAQ,OAAO,QAAQ,QAAQ,SAAS,EAAE,CAAC,CAC9C,QAAQ,GAAG,YAAY,WAAW,YAAY,CAC9C,KAAK,CAAC,UAAU,KAAK;AACxB,QAAO,MAAM,SAAS,IAAI,MAAM,KAAK,KAAK,GAAG;;AAG/C,SAAS,UAAU,OAAwB;AACzC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO,MAAM,MAAM,GAAG,GAAG;;AAG3B,SAAS,aAAa,SAAoC;CACxD,MAAM,WAAW,QAAQ;AACzB,KAAI,CAAC,SAAU,QAAO;CACtB,MAAM,aAAa,SAAS,eAAe,KAAA,KAAa,SAAS,aAAa,KAAA,IAC1E,GAAG,SAAS,WAAW,IAAI,SAAS,aACpC;CACJ,MAAM,YAAY,SAAS,kBAAkB,SAAS,eAClD,GAAG,UAAU,SAAS,eAAe,CAAC,IAAI,UAAU,SAAS,aAAa,KAC1E;AACJ,KAAI,eAAe,oBAAoB,cAAc,gBAAiB,QAAO;AAC7E,QAAO,GAAG,WAAW,KAAK;;AAG5B,SAAgB,0BAA0B,UAA+C;AACvF,KAAI,SAAS,SAAS,WAAW,EAAG,QAAO;CAC3C,MAAM,UAAU;EAAC;EAAW;EAAY;EAAS;EAAQ;EAAW;EAAkB;CACtF,MAAM,SAAS;EAAC;EAAI;EAAI;EAAG;EAAG;EAAI;EAAG;CACrC,MAAM,OAAO,WAAqB,OAAO,KAAK,OAAO,UAAU,MAAM,OAAO,OAAO,OAAQ,CAAC,CAAC,KAAK,KAAK;AACvG,QAAO;EACL,IAAI,QAAQ;EACZ,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK;EACnD,GAAG,SAAS,SAAS,KAAK,YAAY,IAAI;GACxC,QAAQ,gBAAgB,QAAQ;GAChC,WAAW,SAAS,WAAW;GAC/B,WAAW,SAAS,QAAQ;GAC5B,WAAW,SAAS,OAAO;GAC3B,aAAa,QAAQ;GACrB,oBAAoB,QAAQ;GAC7B,CAAC,CAAC;EACJ,CAAC,KAAK,KAAK"}
1
+ {"version":3,"file":"capabilities-BShqspb-.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkRetention {\n mode: 'full_history' | 'rolling_window' | 'expanding_then_rolling' | 'bounded_range' | 'unknown' | string\n window_days?: number\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n started_at?: string\n rolls_after_at?: string\n current_window_seconds?: number\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n retention?: NetworkRetention | null\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n layers: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n }\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'mcpAuthToken' | 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint' | 'mcpEndpoint'>,\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim() || config.mcpAuthToken?.trim()\n if (token) {\n headers.set('X-MCP-Debug-Token', token)\n headers.set('Authorization', `Bearer ${token}`)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = await response.json() as NetworkCapabilitiesDocument\n if (parsed.schema !== 'chain-insights.network-capabilities.v1' || !Array.isArray(parsed.networks)) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return parsed\n}\n\nfunction layerValue(network: NetworkCapability, layer: string): string {\n const capability = network.layers[layer]\n if (!capability?.enabled) return 'no'\n return 'yes'\n}\n\nfunction availableToolsLabel(network: NetworkCapability): string {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.length > 0 ? tools.join(', ') : 'none'\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange = coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange = coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : 'dates unknown'\n if (blockRange === 'blocks unknown' && dateRange === 'dates unknown') return 'unknown'\n return `${blockRange} / ${dateRange}`\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const headers = ['Network', 'Topology', 'Facts', 'Risk', 'Dataset', 'Available tools']\n const widths = [14, 10, 8, 8, 38, 64]\n const row = (values: string[]) => values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [\n row(headers),\n widths.map((width) => '-'.repeat(width)).join(' '),\n ...document.networks.map((network) => row([\n network.display_name || network.network,\n layerValue(network, 'topology'),\n layerValue(network, 'facts'),\n layerValue(network, 'risk'),\n datasetLabel(network),\n availableToolsLabel(network),\n ])),\n ].join('\\n')\n}\n"],"mappings":";;AAgDA,SAAS,oBAAoB,UAAuB;CAClD,MAAM,MAAM,IAAI,IAAI,QAAQ;CAC5B,IAAI,WAAW;CACf,IAAI,SAAS;CACb,IAAI,OAAO;CACX,OAAO;AACT;AAEA,eAAsB,yBACpB,QACsC;CAEtC,MAAM,UAAU,oBADC,wBAAwB,MACE,CAAC;CAC5C,MAAM,UAAU,IAAI,QAAQ;CAC5B,MAAM,QAAQ,OAAO,mBAAmB,KAAK,KAAK,OAAO,cAAc,KAAK;CAC5E,IAAI,OAAO;EACT,QAAQ,IAAI,qBAAqB,KAAK;EACtC,QAAQ,IAAI,iBAAiB,UAAU,OAAO;CAChD;CACA,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,MAAM,SAAS,EAAE,QAAQ,CAAC;CAC7C,SAAS,KAAK;EACZ,MAAM,IAAI,MAAM,uCAAuC,QAAQ,IAAK,IAAc,SAAS;CAC7F;CACA,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,uCAAuC,QAAQ,SAAS,SAAS,QAAQ;CAE3F,MAAM,SAAS,MAAM,SAAS,KAAK;CACnC,IAAI,OAAO,WAAW,4CAA4C,CAAC,MAAM,QAAQ,OAAO,QAAQ,GAC9F,MAAM,IAAI,MAAM,sDAAsD;CAExE,OAAO;AACT;AAEA,SAAS,WAAW,SAA4B,OAAuB;CAErE,IAAI,CADe,QAAQ,OAAO,QACjB,SAAS,OAAO;CACjC,OAAO;AACT;AAEA,SAAS,oBAAoB,SAAoC;CAC/D,MAAM,QAAQ,OAAO,QAAQ,QAAQ,SAAS,CAAC,CAAC,EAC7C,QAAQ,GAAG,YAAY,WAAW,WAAW,EAC7C,KAAK,CAAC,UAAU,IAAI;CACvB,OAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAC/C;AAEA,SAAS,UAAU,OAAwB;CACzC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,MAAM,MAAM,GAAG,EAAE;AAC1B;AAEA,SAAS,aAAa,SAAoC;CACxD,MAAM,WAAW,QAAQ;CACzB,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,aAAa,SAAS,eAAe,KAAA,KAAa,SAAS,aAAa,KAAA,IAC1E,GAAG,SAAS,WAAW,IAAI,SAAS,aACpC;CACJ,MAAM,YAAY,SAAS,kBAAkB,SAAS,eAClD,GAAG,UAAU,SAAS,cAAc,EAAE,IAAI,UAAU,SAAS,YAAY,MACzE;CACJ,IAAI,eAAe,oBAAoB,cAAc,iBAAiB,OAAO;CAC7E,OAAO,GAAG,WAAW,KAAK;AAC5B;AAEA,SAAgB,0BAA0B,UAA+C;CACvF,IAAI,SAAS,SAAS,WAAW,GAAG,OAAO;CAC3C,MAAM,UAAU;EAAC;EAAW;EAAY;EAAS;EAAQ;EAAW;CAAiB;CACrF,MAAM,SAAS;EAAC;EAAI;EAAI;EAAG;EAAG;EAAI;CAAE;CACpC,MAAM,OAAO,WAAqB,OAAO,KAAK,OAAO,UAAU,MAAM,OAAO,OAAO,MAAO,CAAC,EAAE,KAAK,IAAI;CACtG,OAAO;EACL,IAAI,OAAO;EACX,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,IAAI;EAClD,GAAG,SAAS,SAAS,KAAK,YAAY,IAAI;GACxC,QAAQ,gBAAgB,QAAQ;GAChC,WAAW,SAAS,UAAU;GAC9B,WAAW,SAAS,OAAO;GAC3B,WAAW,SAAS,MAAM;GAC1B,aAAa,OAAO;GACpB,oBAAoB,OAAO;EAC7B,CAAC,CAAC;CACJ,EAAE,KAAK,IAAI;AACb"}
@@ -1,4 +1,4 @@
1
- const require_client = require("./client-D4fZgIaO.cjs");
1
+ const require_client = require("./client-DPc2eyVN.cjs");
2
2
  //#region src/mcp/capabilities.ts
3
3
  function metadataNetworksUrl(endpoint) {
4
4
  const url = new URL(endpoint);
@@ -0,0 +1,9 @@
1
+ require("./frontmatter-Dvqa5HX6.cjs");
2
+ const require_dossier = require("./dossier-Br62hCG7.cjs");
3
+ const require_store = require("./store-DogLawSj.cjs");
4
+ const require_evidence = require("./evidence-CvEesemA.cjs");
5
+ const require_session = require("./session-DwyikazY.cjs");
6
+ exports.CaseStore = require_store.CaseStore;
7
+ exports.DossierStore = require_dossier.DossierStore;
8
+ exports.EvidenceStore = require_evidence.EvidenceStore;
9
+ exports.SessionStore = require_session.SessionStore;
@@ -0,0 +1,6 @@
1
+ import "./frontmatter-D0ccQnUM.mjs";
2
+ import { DossierStore } from "./dossier-Bl0NkJKC.mjs";
3
+ import { CaseStore } from "./store-BT2SCcQr.mjs";
4
+ import { t as EvidenceStore } from "./evidence-D96PTzOQ.mjs";
5
+ import { SessionStore } from "./session-Bha3zFrx.mjs";
6
+ export { CaseStore, DossierStore, EvidenceStore, SessionStore };