chain-insights 0.3.19 → 0.8.19

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 (131) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +54 -36
  3. package/bin/install.cjs +4 -7
  4. package/dist/active-BQopLul8.mjs.map +1 -1
  5. package/dist/{active-XWv72R1X.cjs → active-Bj6dohpc.cjs} +5 -5
  6. package/dist/{app-DBrqk_iP.cjs → app-CDnQ2JzJ.cjs} +6 -6
  7. package/dist/{app-DXwILI_a.mjs → app-qkaa5DYD.mjs} +2 -2
  8. package/dist/{app-DXwILI_a.mjs.map → app-qkaa5DYD.mjs.map} +1 -1
  9. package/dist/{artifact-server-v0WgTPFT.cjs → artifact-server-BXA0gdZE.cjs} +1 -1
  10. package/dist/{artifact-server-CcmLBv1j.mjs → artifact-server-Dw7AI6B1.mjs} +2 -2
  11. package/dist/{artifact-server-CcmLBv1j.mjs.map → artifact-server-Dw7AI6B1.mjs.map} +1 -1
  12. package/dist/{call-args-DPXdX3_D.mjs → call-args-D3UajHDq.mjs} +3 -5
  13. package/dist/call-args-D3UajHDq.mjs.map +1 -0
  14. package/dist/{call-args-CcUV6gFS.cjs → call-args-DyU9d6tT.cjs} +2 -4
  15. package/dist/capabilities-Bnlnmu6P.mjs +137 -0
  16. package/dist/capabilities-Bnlnmu6P.mjs.map +1 -0
  17. package/dist/capabilities-CKBZbIBf.cjs +136 -0
  18. package/dist/cli.cjs +185 -235
  19. package/dist/cli.mjs +176 -226
  20. package/dist/cli.mjs.map +1 -1
  21. package/dist/{client-ytTO0mcZ.mjs → client-C-A_VCul.mjs} +7 -6
  22. package/dist/client-C-A_VCul.mjs.map +1 -0
  23. package/dist/{client-BY-56ojr.cjs → client-oJdEMUFE.cjs} +8 -7
  24. package/dist/{config-C6zM8Xir.mjs → config-BSMWXI4K.mjs} +3 -3
  25. package/dist/{config-C6zM8Xir.mjs.map → config-BSMWXI4K.mjs.map} +1 -1
  26. package/dist/{config-CkW404Cs.cjs → config-Cj4Jwv08.cjs} +6 -6
  27. package/dist/format-Bq94jSyw.mjs.map +1 -1
  28. package/dist/{graph-normalizer-DbjlbMpz.cjs → graph-normalizer-B2Q2lgVy.cjs} +2 -5
  29. package/dist/{graph-normalizer-CXP06jKh.mjs → graph-normalizer-BA3Rbd6l.mjs} +3 -6
  30. package/dist/graph-normalizer-BA3Rbd6l.mjs.map +1 -0
  31. package/dist/{graph-reports-CEq-Mvx0.mjs → graph-reports-B6yoYbPq.mjs} +2 -2
  32. package/dist/{graph-reports-CEq-Mvx0.mjs.map → graph-reports-B6yoYbPq.mjs.map} +1 -1
  33. package/dist/{graph-reports-CkglRtg4.cjs → graph-reports-DSHSp5HL.cjs} +5 -5
  34. package/dist/{html-generator-BFKafL8y.cjs → html-generator-DF0F6nUI.cjs} +5 -5
  35. package/dist/{html-generator-D4fX71hI.mjs → html-generator-DjWagEB5.mjs} +2 -2
  36. package/dist/html-generator-DjWagEB5.mjs.map +1 -0
  37. package/dist/index.cjs +10 -8
  38. package/dist/index.d.cts +34 -2
  39. package/dist/index.d.cts.map +1 -1
  40. package/dist/index.d.mts +34 -2
  41. package/dist/index.d.mts.map +1 -1
  42. package/dist/index.mjs +8 -8
  43. package/dist/{init-BGDvGreX.mjs → init-B3sU6b3N.mjs} +74 -12
  44. package/dist/init-B3sU6b3N.mjs.map +1 -0
  45. package/dist/{init-Cuw9TznI.cjs → init-BmyNlJ9M.cjs} +75 -13
  46. package/dist/{mcp-endpoint-QQ5Lbqc2.mjs → mcp-endpoint-B6QETHQM.mjs} +2 -2
  47. package/dist/{mcp-endpoint-QQ5Lbqc2.mjs.map → mcp-endpoint-B6QETHQM.mjs.map} +1 -1
  48. package/dist/{mcp-endpoint-cQIZSjkK.cjs → mcp-endpoint-CwsaQ8gL.cjs} +1 -1
  49. package/dist/mcp-proxy.cjs +711 -892
  50. package/dist/mcp-proxy.d.cts.map +1 -1
  51. package/dist/mcp-proxy.d.mts.map +1 -1
  52. package/dist/mcp-proxy.mjs +707 -888
  53. package/dist/mcp-proxy.mjs.map +1 -1
  54. package/dist/{output-root-DI0tzA0X.cjs → output-root-C1PFNCqb.cjs} +4 -4
  55. package/dist/{public-tools-brHmHGYm.mjs → public-tools-BTxuEFAS.mjs} +906 -1487
  56. package/dist/public-tools-BTxuEFAS.mjs.map +1 -0
  57. package/dist/{public-tools-BREojpU7.cjs → public-tools-H9x9ob-q.cjs} +907 -1495
  58. package/dist/remote-trace-args-BkWtRX_h.cjs +29 -0
  59. package/dist/remote-trace-args-umlI_GIv.mjs +31 -0
  60. package/dist/remote-trace-args-umlI_GIv.mjs.map +1 -0
  61. package/dist/resolve-port-CbaEoODz.cjs +18 -0
  62. package/dist/resolve-port-DxPK7Ipx.mjs +20 -0
  63. package/dist/resolve-port-DxPK7Ipx.mjs.map +1 -0
  64. package/dist/{schema-D_qwaQA5.mjs → schema-BbtE--TF.mjs} +2 -2
  65. package/dist/{schema-D_qwaQA5.mjs.map → schema-BbtE--TF.mjs.map} +1 -1
  66. package/dist/{schema-Dr6JXSOF.cjs → schema-Byt6GeSZ.cjs} +6 -6
  67. package/dist/{schema-cache-CJk1EL3L.cjs → schema-cache-CX0r2UQp.cjs} +3 -3
  68. package/dist/{server-ColyTG1t.cjs → server-B5HlORyO.cjs} +3 -3
  69. package/dist/{server-BK4bfOiv.mjs → server-Dy4d0Td1.mjs} +2 -2
  70. package/dist/{server-BK4bfOiv.mjs.map → server-Dy4d0Td1.mjs.map} +1 -1
  71. package/dist/templates/graph.html +11 -10
  72. package/dist/tool-visibility-9-rvB91K.mjs +98 -0
  73. package/dist/tool-visibility-9-rvB91K.mjs.map +1 -0
  74. package/dist/tool-visibility-Cl2p7Ghx.cjs +118 -0
  75. package/dist/{tools-v6kcdojg.mjs → tools-B4rwKdvU.mjs} +53 -13
  76. package/dist/tools-B4rwKdvU.mjs.map +1 -0
  77. package/dist/{tools-BhTI3Lmg.cjs → tools-e87fylVa.cjs} +71 -13
  78. package/dist/{topup-server-DhYlOOBM.cjs → topup-server-B8anfXcq.cjs} +2 -2
  79. package/dist/topup-server-R3dNp-p8.mjs.map +1 -1
  80. package/dist/update-CJUfGCxs.mjs.map +1 -1
  81. package/dist/usage-status-D2uosC7s.mjs +36 -0
  82. package/dist/usage-status-D2uosC7s.mjs.map +1 -0
  83. package/dist/usage-status-H1xgCu99.cjs +44 -0
  84. package/dist/{viz-BBvY-wXz.cjs → viz-C9m5OuQT.cjs} +9 -8
  85. package/dist/{viz-D8umSF-t.mjs → viz-Cvb81rMm.mjs} +5 -4
  86. package/dist/viz-Cvb81rMm.mjs.map +1 -0
  87. package/dist/{wallet-BL0fJC29.mjs → wallet-BrTTnf7w.mjs} +43 -2
  88. package/dist/wallet-BrTTnf7w.mjs.map +1 -0
  89. package/dist/{wallet-gC2jxh7j.cjs → wallet-C2yVjcSY.cjs} +47 -6
  90. package/docs/architecture.md +10 -10
  91. package/docs/contributing.md +2 -9
  92. package/docs/debugging.md +33 -17
  93. package/docs/development.md +27 -0
  94. package/docs/graph-tools.md +33 -119
  95. package/docs/investigation-workspaces.md +1 -1
  96. package/docs/mcp-proxy.md +69 -52
  97. package/package.json +17 -13
  98. package/skills/chain-insights-address-risk/SKILL.md +1 -1
  99. package/skills/chain-insights-bittensor-cypher/SKILL.md +61 -52
  100. package/skills/chain-insights-cypher/SKILL.md +44 -23
  101. package/skills/chain-insights-cypher/agents/openai.yaml +1 -1
  102. package/skills/chain-insights-cypher/references/memgraph-examples.md +40 -34
  103. package/skills/chain-insights-developer-experience/SKILL.md +8 -8
  104. package/skills/chain-insights-investigation/SKILL.md +9 -9
  105. package/skills/chain-insights-investigation/scripts/run-target-uat.sh +12 -12
  106. package/skills/chain-insights-trace-funds/SKILL.md +13 -10
  107. package/skills/test-chain-insights-graph/SKILL.md +81 -0
  108. package/skills/test-chain-insights-graph/agents/openai.yaml +4 -0
  109. package/skills/{test-chain-insights-graphrag-mcp → test-chain-insights-graph}/scripts/run-uat.sh +260 -256
  110. package/dist/call-args-DPXdX3_D.mjs.map +0 -1
  111. package/dist/capabilities-CM72SErE.mjs +0 -81
  112. package/dist/capabilities-CM72SErE.mjs.map +0 -1
  113. package/dist/capabilities-DGeF-oHc.cjs +0 -80
  114. package/dist/client-ytTO0mcZ.mjs.map +0 -1
  115. package/dist/graph-normalizer-CXP06jKh.mjs.map +0 -1
  116. package/dist/html-generator-D4fX71hI.mjs.map +0 -1
  117. package/dist/init-BGDvGreX.mjs.map +0 -1
  118. package/dist/public-tools-brHmHGYm.mjs.map +0 -1
  119. package/dist/setup-CDha4B9s.cjs +0 -81
  120. package/dist/setup-yXK8agdn.mjs +0 -80
  121. package/dist/setup-yXK8agdn.mjs.map +0 -1
  122. package/dist/tool-visibility--QPgrRE5.cjs +0 -42
  123. package/dist/tool-visibility-nr6XqO1F.mjs +0 -34
  124. package/dist/tool-visibility-nr6XqO1F.mjs.map +0 -1
  125. package/dist/tools-v6kcdojg.mjs.map +0 -1
  126. package/dist/viz-D8umSF-t.mjs.map +0 -1
  127. package/dist/wallet-BL0fJC29.mjs.map +0 -1
  128. package/skills/chain-insights-exposure-analysis/SKILL.md +0 -83
  129. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +0 -75
  130. package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +0 -4
  131. /package/dist/{chunk-DakpK96I.cjs → rolldown-runtime-DakpK96I.cjs} +0 -0
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Chain Swarm AML
3
+ Copyright (c) 2026 Chainswarm Technology
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # Chain Insights
2
2
 
3
- [Website](https://chain-insights.ai) | [GitHub](https://github.com/chainswarm/chain-insights) | [npm](https://www.npmjs.com/package/chain-insights)
3
+ [Website](https://chain-insights.ai) | [npm](https://www.npmjs.com/package/chain-insights)
4
4
 
5
5
  Chain Insights is an open-source AML investigation toolkit for AI agents and
6
6
  analysts. Install it from npm to screen blockchain addresses, trace role-specific
7
7
  fund flows, manage workspace evidence, and generate graph reports.
8
8
 
9
- Graph access is configuration-driven. The package defaults to a local GraphRAG
10
- MCP endpoint for development; hosted endpoints are set explicitly with
9
+ Graph access is configuration-driven. The package defaults to a local Chain
10
+ Insights Graph endpoint for development; hosted endpoints are set explicitly with
11
11
  `graphMcpEndpoint` or `CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT`.
12
12
 
13
13
  ## What You Can Do Today
@@ -18,16 +18,11 @@ MCP endpoint for development; hosted endpoints are set explicitly with
18
18
  | `aml_trace_victim_funds` | Trace victim/source funds forward to exchange deposit candidates |
19
19
  | `aml_trace_deposit_sources` | Trace backward from suspected deposit/cashout addresses to upstream sources and convergence |
20
20
  | `aml_trace_suspect_funds` | Trace suspected scammer, mule, operator, or laundering-ring funds forward to cashout topology |
21
- | `exposure_profile` | Explain staking or trading exposure around an account, owner, or counterparty |
22
- | `exposure_quality` | Score whether exposure behavior looks disciplined, fragile, lucky, or noisy |
23
- | `exposure_carry` | Explain carry earned or paid from staking, trading, funding, fees, emissions, or dividends |
24
- | `exposure_crowding` | Measure side concentration for a market, subnet, hotkey, or strategy |
25
- | `exposure_exit_pressure` | Explain liquidation, slippage, unstake, funding pain, or other exit pressure |
26
- | `exposure_correlation` | Compare accounts for possible copy, overlap, or strategy-cluster behavior |
27
- | `exposure_explain` | Explain a specific exposure lifecycle, trade, position, stake, rotation, or incident |
28
- | `graph_query` | Run one read-only GQL/Cypher query against a GraphRAG MCP graph layer |
21
+ | `graph_query` | Run one read-only GQL/Cypher query against a Chain Insights Graph layer |
29
22
  | `graph_query_batch` | Run related read-only graph queries as one MCP call |
30
- | `usage_status` | Check the caller's daily free-tier graph query allowance |
23
+ | `meta_network_capabilities` | Check supported Chain Insights networks and graph tools |
24
+ | `meta_usage_status` | Check the caller's daily free-tier graph query allowance |
25
+ | `wallet_balance` | Show the local payment wallet amount |
31
26
 
32
27
  ## Quick Start
33
28
 
@@ -69,10 +64,10 @@ Chain Insights workspaces are plain local folders. Use any editor or agent
69
64
  tooling you want to inspect workspace files, graph reports, artifacts, and
70
65
  published outputs.
71
66
 
72
- ## Configure GraphRAG MCP Endpoint
67
+ ## Configure Chain Insights Graph Endpoint
73
68
 
74
- `cia` uses `graphMcpEndpoint` for all GraphRAG MCP calls. The npm package does
75
- not hardcode a hosted endpoint. Configure the endpoint explicitly for the
69
+ `cia` uses `graphMcpEndpoint` for all Chain Insights Graph calls. The npm
70
+ package does not hardcode a hosted endpoint. Configure the endpoint explicitly for the
76
71
  environment you intend to use.
77
72
 
78
73
  Local development endpoint (default):
@@ -81,6 +76,17 @@ Local development endpoint (default):
81
76
  cia config set graphMcpEndpoint http://127.0.0.1:8012/mcp
82
77
  ```
83
78
 
79
+ For a deterministic local Bittensor backend, use the RBMK-managed devkit
80
+ runbook and point Chain Insights at it persistently:
81
+
82
+ ```bash
83
+ cia debug on --token <any-string> --endpoint http://127.0.0.1:18012/mcp
84
+ ```
85
+
86
+ The devkit backend is unmetered and never issues a paid challenge, so
87
+ `cia config set graphMcpEndpoint http://127.0.0.1:18012/mcp` alone also works;
88
+ `debug on` just skips payment negotiation outright.
89
+
84
90
  Hosted staging endpoint for approved testers:
85
91
 
86
92
  ```bash
@@ -113,22 +119,22 @@ Configuration precedence for `graphMcpEndpoint`:
113
119
  2. `cia config set graphMcpEndpoint ...` saved value
114
120
  3. Local default `http://127.0.0.1:8012/mcp`
115
121
 
116
- Check the configured endpoint and current GraphRAG MCP capabilities:
122
+ Check the configured endpoint and current Chain Insights Graph capabilities:
117
123
 
118
124
  ```bash
119
125
  cia config get graphMcpEndpoint
120
126
  cia mcp networks
121
- cia mcp call usage_status
127
+ cia mcp call meta_usage_status
122
128
  cia mcp tools --refresh
123
129
  ```
124
130
 
125
131
  If network or tool discovery fails, check the endpoint and access mode first.
126
- The CLI can still initialize workspaces and continue investigation workflow without a reachable
127
- GraphRAG MCP endpoint.
132
+ The CLI can still initialize workspaces and continue investigation workflow
133
+ without a reachable Chain Insights Graph endpoint.
128
134
 
129
- Hosted GraphRAG MCP includes a small public free tier for `graph_query` before
130
- paid access is required. The default public free tier is 10 execution seconds
131
- per IP per UTC day. Use `usage_status` to see the current caller allowance.
135
+ Hosted Chain Insights Graph includes a small public free tier for `graph_query`
136
+ before paid access is required. The default public free tier is 10 execution seconds
137
+ per IP per UTC day. Use `meta_usage_status` to see the current caller allowance.
132
138
  Prepared wallet users receive the daily free tier first, then paid access
133
139
  continues automatically after the allowance is exhausted.
134
140
  If you do not have a prepared wallet yet, use bounded single `graph_query`
@@ -173,15 +179,15 @@ Run a direct live topology query:
173
179
  ```bash
174
180
  cia mcp call graph_query \
175
181
  network=bittensor \
176
- "query=USE live_topology MATCH (n) WHERE n.address IS NOT NULL RETURN n.labels AS labels, n.address AS address LIMIT 10"
182
+ "query=USE live_topology MATCH (i:Identity) RETURN i.identity_id AS identity_id, i.labels AS labels, i.risk_level AS risk_level LIMIT 10"
177
183
  ```
178
184
 
179
- Run a batch across graph layers:
185
+ Run a batch across graph views:
180
186
 
181
187
  ```bash
182
188
  cia mcp call graph_query_batch \
183
189
  network=bittensor \
184
- 'queries=[{"id":"count","query":"USE live_topology MATCH (n) RETURN count(n) AS count LIMIT 1"},{"id":"archive_flows","query":"USE archive_topology MATCH (src:Address)-[f:FLOWS_TO]->(dst:Address) RETURN f.period_granularity AS granularity, src.address AS source, dst.address AS target LIMIT 3"},{"id":"facts_sample","query":"USE facts MATCH (a:Address)-[:HAS_FEATURE]->(f:AddressFeature) RETURN a.address AS address, f.sent_count AS sent_count LIMIT 3"}]'
190
+ 'queries=[{"id":"count","query":"USE live_topology MATCH (i:Identity) RETURN count(i) AS count LIMIT 1"},{"id":"archive_flows","query":"USE archive_topology MATCH (src:Identity)-[f:FLOWS_TO]->(dst:Identity) RETURN f.period_granularity AS granularity, src.identity_id AS source, dst.identity_id AS target, f.amount_usd_sum AS amount_usd_sum LIMIT 3"},{"id":"archive_member_address","query":"USE archive_topology MATCH (i:Identity)-[:HAS_ADDRESS]->(m:Address) RETURN i.identity_id AS identity_id, m.address AS member_address, m.network AS member_network LIMIT 3"},{"id":"facts_sample","query":"USE facts MATCH (i:Identity)-[:HAS_FEATURE]->(f:AddressFeature) RETURN i.identity_id AS identity_id, f.tx_out_count AS tx_out_count LIMIT 3"}]'
185
191
  ```
186
192
 
187
193
  For no-wallet public free-tier usage, prefer the single-query example first.
@@ -203,12 +209,12 @@ cia mcp trace-suspect-funds \
203
209
  Agent or CLI user
204
210
  -> Chain Insights CLI / MCP proxy
205
211
  -> local config, wallet, workspace, artifacts, reports
206
- -> GraphRAG MCP
212
+ -> Chain Insights Graph
207
213
  -> graph intelligence for AML workflows
208
214
  ```
209
215
 
210
216
  Chain Insights stores investigation outputs in initialized local workspaces.
211
- GraphRAG MCP performs graph-language reads against network-specific graph
217
+ Chain Insights Graph performs graph-language reads against network-specific
212
218
  layers.
213
219
 
214
220
  ## Graph Access
@@ -237,14 +243,6 @@ and local workspace state:
237
243
 
238
244
  - `aml_address_risk` starts a single-address screen with risk, behavior,
239
245
  neighborhood context, and exchange exposure.
240
- - `exposure_profile` explains staking exposure and trading exposure
241
- with venues, instruments, position changes, public support events, and
242
- caveats when pricing or unit coverage is incomplete.
243
- - `exposure_quality`, `exposure_carry`, `exposure_crowding`,
244
- `exposure_exit_pressure`, `exposure_correlation`, and `exposure_explain`
245
- add deterministic exposure analytics over the same generic model. They work
246
- for Bittensor staking rows now and are shaped for Hyperliquid trading rows as
247
- soon as the Hyperliquid indexer writes the shared exposure contract.
248
246
  - `aml_trace_victim_funds` traces victim/source funds forward through
249
247
  intermediaries to exchange deposit candidates.
250
248
  - `aml_trace_deposit_sources` traces backward from suspected deposit/cashout
@@ -252,6 +250,11 @@ and local workspace state:
252
250
  - `aml_trace_suspect_funds` traces suspected scammer, mule, operator, or
253
251
  laundering-ring funds forward to cashout topology.
254
252
 
253
+ AML tools accept full blockchain addresses and return blockchain addresses as
254
+ the public result surface. Chain Insights resolves those addresses to
255
+ identity-grain topology internally and includes identity resolution metadata for
256
+ audit/debug use.
257
+
255
258
  The three trace tools share `chain-insights.trace.v1` and return compact,
256
259
  chainable results. Full graph/table/report artifacts remain on disk under the
257
260
  workspace, with pointers in the tool result and workspace evidence.
@@ -268,7 +271,8 @@ notes.
268
271
 
269
272
  | Doc | Use it for |
270
273
  | --- | --- |
271
- | [Graph tools](docs/graph-tools.md) | GraphRAG MCP layers, `graph_query`, `graph_query_batch`, AML tool contracts, graph reports, evidence pointers |
274
+ | [Graph tools](docs/graph-tools.md) | Chain Insights Graph layers, `graph_query`, `graph_query_batch`, AML tool contracts, graph reports, evidence pointers |
275
+ | Bittensor devkit parity workflow (RBMK) | Local Chain Insights Graph backend with deterministic Bittensor fixture data for Chain Insights development |
272
276
  | [Investigation workspaces](docs/investigation-workspaces.md) | `cia init`, workspace layout, artifacts, imports, templates, sessions, reports, and visualization outputs |
273
277
  | [MCP proxy](docs/mcp-proxy.md) | Stdio proxy behavior, endpoint configuration, agent installers, local tools, auth modes, Inspector validation |
274
278
  | [Architecture](docs/architecture.md) | Product layers, data flow, local storage, security model, config keys |
@@ -281,3 +285,17 @@ notes.
281
285
  Chain Insights is not a custodial wallet, hosted case database, or replacement
282
286
  for analyst review. It does not write risk labels automatically. Investigation
283
287
  data stays in the local workspace unless the operator exports or shares it.
288
+
289
+ ## Workers
290
+
291
+ <!-- gsd: workers -->
292
+ | Worker | Entrypoint | Component doc |
293
+ |---|---|---|
294
+ | `claude-desktop` | `src/claude-desktop` | [components/claude-desktop.md](docs/architecture/components/claude-desktop.md) |
295
+ | `config` | `src/config` | [components/config.md](docs/architecture/components/config.md) |
296
+ | `investigation` | `src/investigation` | [components/investigation.md](docs/architecture/components/investigation.md) |
297
+ | `mcp` | `src/mcp` | [components/mcp.md](docs/architecture/components/mcp.md) |
298
+ | `server` | `src/server` | [components/server.md](docs/architecture/components/server.md) |
299
+ | `viz` | `src/viz` | [components/viz.md](docs/architecture/components/viz.md) |
300
+ | `wallet` | `src/wallet` | [components/wallet.md](docs/architecture/components/wallet.md) |
301
+ <!-- /gsd: workers -->
package/bin/install.cjs CHANGED
@@ -57,13 +57,10 @@ function copyCommandsAsClaudeSkills(srcDir, targetDir) {
57
57
 
58
58
  fs.mkdirSync(targetDir, { recursive: true });
59
59
 
60
- // Remove stale ci-* skill dirs before copying (clean reinstall)
61
- const existing = fs.readdirSync(targetDir, { withFileTypes: true });
62
- for (const entry of existing) {
63
- if (entry.isDirectory() && entry.name.startsWith('ci-')) {
64
- fs.rmSync(path.join(targetDir, entry.name), { recursive: true, force: true });
65
- }
66
- }
60
+ // NOTE: do NOT blanket-delete by prefix here. The target is a shared skills
61
+ // directory that may hold the user's own skills (including unrelated `ci-*`
62
+ // ones). Clean reinstall is handled per shipped skill below — each skill dir
63
+ // we ship is removed and recopied, and nothing else is touched.
67
64
 
68
65
  const copyTree = (src, dest) => {
69
66
  const stat = fs.statSync(src);
@@ -1 +1 @@
1
- {"version":3,"file":"active-BQopLul8.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 artifacts_dir?: string\n domain_hints?: unknown\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n artifactsRoot: string\n domainHints: 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 artifactsDir = parsed.artifacts_dir ?? 'artifacts'\n const domainHints = Array.isArray(parsed.domain_hints)\n ? parsed.domain_hints.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)\n : []\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n artifactsRoot: path.resolve(workspaceRoot, artifactsDir),\n domainHints,\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 activeArtifactsRoot(): string {\n return findActiveWorkspace()?.artifactsRoot ?? path.join(os.homedir(), '.chain-insights', 'artifacts')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;AAkBA,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,eAAe,OAAO,iBAAiB;CAC7C,MAAM,cAAc,MAAM,QAAQ,OAAO,YAAY,IACjD,OAAO,aAAa,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,CAAC,IAC3G,CAAC;CACL,OAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,iBAAiB;EACvD,eAAe,KAAK,QAAQ,eAAe,YAAY;EACvD;CACF;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;AAUA,SAAgB,cAAc,iBAAkC;CAC9D,OAAO,oBAAoB,GAAG,QAAQ,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB;AACpG"}
1
+ {"version":3,"file":"active-BQopLul8.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 artifacts_dir?: string\n domain_hints?: unknown\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n artifactsRoot: string\n domainHints: 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 artifactsDir = parsed.artifacts_dir ?? 'artifacts'\n const domainHints = Array.isArray(parsed.domain_hints)\n ? parsed.domain_hints.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)\n : []\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n artifactsRoot: path.resolve(workspaceRoot, artifactsDir),\n domainHints,\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 activeArtifactsRoot(): string {\n return findActiveWorkspace()?.artifactsRoot ?? path.join(os.homedir(), '.chain-insights', 'artifacts')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;AAkBA,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,eAAe,OAAO,iBAAiB;CAC7C,MAAM,cAAc,MAAM,QAAQ,OAAO,YAAY,IACjD,OAAO,aAAa,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC,IAC3G,CAAC;CACL,OAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,iBAAiB;EACvD,eAAe,KAAK,QAAQ,eAAe,YAAY;EACvD;CACF;AACF;AAEA,SAAgB,oBAAoB,WAAW,QAAQ,IAAI,GAA2B;CACpF,MAAM,eAAe,QAAQ,IAAI,2BAA2B,EAAE,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;AAUA,SAAgB,cAAc,iBAAkC;CAC9D,OAAO,oBAAoB,CAAC,EAAE,QAAQ,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB;AACpG"}
@@ -1,12 +1,12 @@
1
- const require_chunk = require("./chunk-DakpK96I.cjs");
1
+ const require_rolldown_runtime = require("./rolldown-runtime-DakpK96I.cjs");
2
2
  let node_path = require("node:path");
3
- node_path = require_chunk.__toESM(node_path, 1);
3
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
4
4
  let node_fs = require("node:fs");
5
- node_fs = require_chunk.__toESM(node_fs, 1);
5
+ node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
6
6
  let node_os = require("node:os");
7
- node_os = require_chunk.__toESM(node_os, 1);
7
+ node_os = require_rolldown_runtime.__toESM(node_os, 1);
8
8
  //#region src/workspace/active.ts
9
- var active_exports = /* @__PURE__ */ require_chunk.__exportAll({
9
+ var active_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
10
10
  activeDataDir: () => activeDataDir,
11
11
  findActiveWorkspace: () => findActiveWorkspace
12
12
  });
@@ -1,7 +1,7 @@
1
- const require_chunk = require("./chunk-DakpK96I.cjs");
2
- const require_output_root = require("./output-root-DI0tzA0X.cjs");
1
+ const require_rolldown_runtime = require("./rolldown-runtime-DakpK96I.cjs");
2
+ const require_output_root = require("./output-root-C1PFNCqb.cjs");
3
3
  let node_path = require("node:path");
4
- node_path = require_chunk.__toESM(node_path, 1);
4
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
5
5
  let node_fs_promises = require("node:fs/promises");
6
6
  let hono = require("hono");
7
7
  //#region src/server/app.ts
@@ -84,7 +84,7 @@ function createApp() {
84
84
  ts: Date.now()
85
85
  }));
86
86
  app.get("/status", async (c) => {
87
- const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
87
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-Cj4Jwv08.cjs")).then((n) => n.config_exports);
88
88
  const config = await loadConfig();
89
89
  return c.json({
90
90
  dataDir: config.dataDir,
@@ -102,7 +102,7 @@ function createApp() {
102
102
  app.get("/graph-reports/:filename", async (c) => {
103
103
  const filename = c.req.param("filename");
104
104
  if (!isSafeGraphReportFilename(filename)) return c.json({ error: "Invalid graph report filename" }, 400);
105
- const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-DI0tzA0X.cjs")).then((n) => n.output_root_exports);
105
+ const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-C1PFNCqb.cjs")).then((n) => n.output_root_exports);
106
106
  const paths = workspaceOutputPaths();
107
107
  const graphPath = node_path.default.resolve(paths.reportGraphsRoot, filename);
108
108
  if (!withinRoot(paths.reportGraphsRoot, graphPath)) return c.json({ error: "Invalid graph report filename" }, 400);
@@ -121,7 +121,7 @@ function createApp() {
121
121
  return c.json({ error: "Invalid graph report filename" }, 400);
122
122
  });
123
123
  app.get("/workspace/tree", async (c) => {
124
- const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-DI0tzA0X.cjs")).then((n) => n.output_root_exports);
124
+ const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-C1PFNCqb.cjs")).then((n) => n.output_root_exports);
125
125
  const paths = workspaceOutputPaths();
126
126
  const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS);
127
127
  return c.json({
@@ -82,7 +82,7 @@ function createApp() {
82
82
  ts: Date.now()
83
83
  }));
84
84
  app.get("/status", async (c) => {
85
- const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
85
+ const { loadConfig } = await import("./config-BSMWXI4K.mjs").then((n) => n.t);
86
86
  const config = await loadConfig();
87
87
  return c.json({
88
88
  dataDir: config.dataDir,
@@ -137,4 +137,4 @@ function createApp() {
137
137
  //#endregion
138
138
  export { createApp as t };
139
139
 
140
- //# sourceMappingURL=app-DXwILI_a.mjs.map
140
+ //# sourceMappingURL=app-qkaa5DYD.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-DXwILI_a.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 { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = ['artifacts', 'entities', 'reports', 'sessions', '.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 filename = `${vizId}.html`\n const paths = workspaceOutputPaths()\n const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n try {\n return await readFile(vizPath, 'utf-8')\n } catch {\n return null\n }\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-qkaa5DYD.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 { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = ['artifacts', 'entities', 'reports', 'sessions', '.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 filename = `${vizId}.html`\n const paths = workspaceOutputPaths()\n const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n try {\n return await readFile(vizPath, 'utf-8')\n } catch {\n return null\n }\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":";;;;;AAKA,MAAM,uBAAuB;CAAC;CAAa;CAAY;CAAW;CAAY;AAAwB;AACtG,MAAM,2BAA2B;AAQjC,SAAS,WAAW,MAAc,QAAyB;CACzD,MAAM,WAAW,KAAK,SAAS,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,MAAM,CAAC;CACvE,OAAO,aAAa,MAAO,CAAC,SAAS,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,QAAQ;AACpF;AAEA,eAAe,mBAAmB,MAAc,QAAkC;CAChF,IAAI;EACF,MAAM,CAAC,UAAU,cAAc,MAAM,QAAQ,IAAI,CAAC,SAAS,IAAI,GAAG,SAAS,MAAM,CAAC,CAAC;EACnF,OAAO,WAAW,UAAU,UAAU;CACxC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,oBAAoB,MAAc,QAAwB;CACjE,OAAO,KAAK,SAAS,MAAM,MAAM,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;AAC7D;AAEA,eAAe,qBACb,eACA,QAAQ,sBACR,WAAW,0BACoB;CAC/B,MAAM,UAAgC,CAAC;CACvC,MAAM,OAAO,KAAK,QAAQ,aAAa;CAEvC,eAAe,MAAM,QAAgB,OAA8B;EACjE,MAAM,WAAW,KAAK,QAAQ,MAAM;EACpC,IAAI,CAAC,WAAW,MAAM,QAAQ,GAAG;EAEjC,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,MAAM,QAAQ;EAC7B,QAAQ;GACN;EACF;EAEA,MAAM,OAAO,KAAK,eAAe,IAAI,YAAY,KAAK,YAAY,IAAI,cAAc,KAAK,OAAO,IAAI,SAAS;EAC7G,IAAI,CAAC,MAAM;EAEX,MAAM,QAA4B;GAChC,MAAM,oBAAoB,MAAM,QAAQ;GACxC;EACF;EACA,IAAI,SAAS,QAAQ,MAAM,OAAO,KAAK;EACvC,QAAQ,KAAK,KAAK;EAElB,IAAI,SAAS,eAAe,SAAS,UAAU;EAC/C,IAAI,CAAC,MAAM,mBAAmB,MAAM,QAAQ,GAAG;EAE/C,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,QAAQ,QAAQ;EACnC,QAAQ;GACN;EACF;EAEA,KAAK,MAAM,SAAS,SAAS,KAAK,GAChC,MAAM,MAAM,KAAK,KAAK,UAAU,KAAK,GAAG,QAAQ,CAAC;CAErD;CAEA,KAAK,MAAM,YAAY,OAAO;EAC5B,MAAM,SAAS,KAAK,QAAQ,MAAM,QAAQ;EAC1C,IAAI,WAAW,MAAM,MAAM,GAAG,MAAM,MAAM,QAAQ,CAAC;CACrD;CAEA,OAAO;AACT;AAEA,eAAe,YAAY,OAAuC;CAChE,MAAM,WAAW,GAAG,MAAM;CAC1B,MAAM,QAAQ,qBAAqB;CACnC,MAAM,UAAU,KAAK,KAAK,MAAM,eAAe,OAAO,QAAQ;CAC9D,IAAI;EACF,OAAO,MAAM,SAAS,SAAS,OAAO;CACxC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,0BAA0B,UAA2B;CAC5D,OACE,SAAS,SAAS,aAAa,KAC/B,oBAAoB,KAAK,QAAQ,KACjC,CAAC,SAAS,SAAS,IAAI,KACvB,CAAC,SAAS,SAAS,GAAG,KACtB,CAAC,SAAS,SAAS,IAAI;AAE3B;AAEA,SAAgB,YAAkB;CAChC,MAAM,MAAM,IAAI,KAAK;CAErB,IAAI,IAAI,YAAY,MAAM,EAAE,KAAK;EAAE,IAAI;EAAM,IAAI,KAAK,IAAI;CAAE,CAAC,CAAC;CAE9D,IAAI,IAAI,WAAW,OAAO,MAAM;EAC9B,MAAM,EAAE,eAAe,MAAM,OAAO,wBAAqB,CAAA,MAAA,MAAA,EAAA,CAAA;EACzD,MAAM,SAAS,MAAM,WAAW;EAChC,OAAO,EAAE,KAAK;GACZ,SAAS,OAAO;GAChB,cAAc,OAAO;GACrB,QAAQ;EACV,CAAC;CACH,CAAC;CAED,IAAI,IAAI,YAAY,OAAO,MAAM;EAC/B,MAAM,KAAK,EAAE,IAAI,MAAM,IAAI;EAC3B,IAAI,CAAC,mBAAmB,KAAK,EAAE,GAC7B,OAAO,EAAE,KAAK,EAAE,OAAO,2BAA2B,GAAG,GAAG;EAE1D,MAAM,OAAO,MAAM,YAAY,EAAE;EACjC,IAAI,CAAC,MACH,OAAO,EAAE,KAAK,EAAE,OAAO,0BAA0B,GAAG,GAAG;EAEzD,OAAO,EAAE,KAAK,IAAI;CACpB,CAAC;CAED,IAAI,IAAI,4BAA4B,OAAO,MAAM;EAC/C,MAAM,WAAW,EAAE,IAAI,MAAM,UAAU;EACvC,IAAI,CAAC,0BAA0B,QAAQ,GACrC,OAAO,EAAE,KAAK,EAAE,OAAO,gCAAgC,GAAG,GAAG;EAG/D,MAAM,EAAE,yBAAyB,MAAM,OAAO,6BAA8B,CAAA,MAAA,MAAA,EAAA,CAAA;EAC5E,MAAM,QAAQ,qBAAqB;EACnC,MAAM,YAAY,KAAK,QAAQ,MAAM,kBAAkB,QAAQ;EAC/D,IAAI,CAAC,WAAW,MAAM,kBAAkB,SAAS,GAC/C,OAAO,EAAE,KAAK,EAAE,OAAO,gCAAgC,GAAG,GAAG;EAE/D,IAAI,CAAC,MAAM,mBAAmB,MAAM,kBAAkB,SAAS,GAC7D,OAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,GAAG,GAAG;EAGxD,IAAI;GACF,MAAM,QAAQ,MAAM,SAAS,WAAW,OAAO;GAC/C,OAAO,EAAE,KAAK,OAAO,KAAK;IACxB,gBAAgB;IAChB,+BAA+B;GACjC,CAAC;EACH,QAAQ;GACN,OAAO,EAAE,KAAK,EAAE,OAAO,yBAAyB,GAAG,GAAG;EACxD;CACF,CAAC;CAED,IAAI,IAAI,qBAAqB,MAAM;EACjC,OAAO,EAAE,KAAK,EAAE,OAAO,gCAAgC,GAAG,GAAG;CAC/D,CAAC;CAED,IAAI,IAAI,mBAAmB,OAAO,MAAM;EACtC,MAAM,EAAE,yBAAyB,MAAM,OAAO,6BAA8B,CAAA,MAAA,MAAA,EAAA,CAAA;EAC5E,MAAM,QAAQ,qBAAqB;EACnC,MAAM,UAAU,MAAM,qBAAqB,MAAM,MAAM,oBAAoB;EAC3E,OAAO,EAAE,KAAK;GACZ,QAAQ;GACR,MAAM,MAAM;GACZ;EACF,CAAC;CACH,CAAC;CAED,IAAI,SAAS,KAAK,MAAM;EACtB,QAAQ,MAAM,GAAG;EACjB,OAAO,EAAE,KAAK,EAAE,OAAO,wBAAwB,GAAG,GAAG;CACvD,CAAC;CAED,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- const require_app = require("./app-DBrqk_iP.cjs");
1
+ const require_app = require("./app-CDnQ2JzJ.cjs");
2
2
  let _hono_node_server = require("@hono/node-server");
3
3
  let node_timers_promises = require("node:timers/promises");
4
4
  //#region src/mcp/artifact-server.ts
@@ -1,4 +1,4 @@
1
- import { t as createApp } from "./app-DXwILI_a.mjs";
1
+ import { t as createApp } from "./app-qkaa5DYD.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-CcmLBv1j.mjs.map
48
+ //# sourceMappingURL=artifact-server-Dw7AI6B1.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-server-CcmLBv1j.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
+ {"version":3,"file":"artifact-server-Dw7AI6B1.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,EAAA,CACe;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,CAAC,CAAC;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,10 +1,8 @@
1
1
  //#region src/mcp/call-args.ts
2
- const NUMERIC_ARG_KEYS = new Set([
2
+ const NUMERIC_ARG_KEYS = /* @__PURE__ */ new Set([
3
3
  "per_query_timeout_seconds",
4
4
  "incident_timestamp_ms",
5
- "max_hops",
6
- "per_address_limit",
7
- "min_amount_sum"
5
+ "max_hops"
8
6
  ]);
9
7
  function parseMcpArgValue(key, value) {
10
8
  const trimmed = value.trim();
@@ -26,4 +24,4 @@ function parseMcpCallArgs(rawArgs) {
26
24
  //#endregion
27
25
  export { parseMcpCallArgs };
28
26
 
29
- //# sourceMappingURL=call-args-DPXdX3_D.mjs.map
27
+ //# sourceMappingURL=call-args-D3UajHDq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-args-D3UajHDq.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])\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,mCAAmB,IAAI,IAAI;CAC/B;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,10 +1,8 @@
1
1
  //#region src/mcp/call-args.ts
2
- const NUMERIC_ARG_KEYS = new Set([
2
+ const NUMERIC_ARG_KEYS = /* @__PURE__ */ new Set([
3
3
  "per_query_timeout_seconds",
4
4
  "incident_timestamp_ms",
5
- "max_hops",
6
- "per_address_limit",
7
- "min_amount_sum"
5
+ "max_hops"
8
6
  ]);
9
7
  function parseMcpArgValue(key, value) {
10
8
  const trimmed = value.trim();
@@ -0,0 +1,137 @@
1
+ import { a as resolveGraphMcpEndpoint, n as applyMcpAuthHeaders } from "./client-C-A_VCul.mjs";
2
+ //#region src/mcp/capabilities.ts
3
+ const BITTENSOR_SEMANTIC_NETWORKS = /* @__PURE__ */ new Set([
4
+ "bittensor",
5
+ "bittensor_evm",
6
+ "bittensor_semantic"
7
+ ]);
8
+ const PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS = {
9
+ aml_address_risk: "available",
10
+ aml_trace_victim_funds: "available",
11
+ aml_trace_deposit_sources: "available",
12
+ aml_trace_suspect_funds: "available",
13
+ graph_query: "available",
14
+ graph_query_batch: "available",
15
+ meta_network_capabilities: "available",
16
+ meta_usage_status: "available",
17
+ wallet_balance: "available"
18
+ };
19
+ const AVAILABLE_TOOLS_PER_LINE = 3;
20
+ function publicNetworkCapabilities(document) {
21
+ const source = document.networks.find((network) => BITTENSOR_SEMANTIC_NETWORKS.has(network.network));
22
+ return {
23
+ schema: "chain-insights.network-capabilities.v1",
24
+ networks: source ? [{
25
+ network: "bittensor",
26
+ display_name: "Bittensor",
27
+ status: source.status || "live",
28
+ default: source.default !== false,
29
+ layers: {
30
+ facts: { enabled: source.layers.facts?.enabled === true },
31
+ risk: { enabled: source.layers.risk?.enabled === true },
32
+ topology: {
33
+ enabled: source.layers.topology?.enabled === true,
34
+ ...source.layers.topology?.live ? { live: source.layers.topology.live } : {},
35
+ ...source.layers.topology?.archive ? { archive: source.layers.topology.archive } : {}
36
+ }
37
+ },
38
+ ...source.coverage ? { coverage: source.coverage } : {},
39
+ ...source.freshness ? { freshness: source.freshness } : {},
40
+ tools: PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS
41
+ }] : []
42
+ };
43
+ }
44
+ function metadataNetworksUrl(endpoint) {
45
+ const url = new URL(endpoint);
46
+ url.pathname = "/metadata/networks";
47
+ url.search = "";
48
+ url.hash = "";
49
+ return url;
50
+ }
51
+ async function fetchNetworkCapabilities(config) {
52
+ const request = metadataNetworksUrl(resolveGraphMcpEndpoint(config));
53
+ const headers = new Headers();
54
+ const token = config.graphMcpAuthToken?.trim() || config.mcpAuthToken?.trim();
55
+ if (token) applyMcpAuthHeaders(headers, token);
56
+ let response;
57
+ try {
58
+ response = await fetch(request, { headers });
59
+ } catch (err) {
60
+ throw new Error(`network capabilities unavailable at ${request}: ${err.message}`);
61
+ }
62
+ if (!response.ok) throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`);
63
+ const parsed = await response.json();
64
+ if (parsed.schema !== "chain-insights.network-capabilities.v1" || !Array.isArray(parsed.networks)) throw new Error("network capabilities response has unsupported schema");
65
+ return publicNetworkCapabilities(parsed);
66
+ }
67
+ function layerValue(network, layer) {
68
+ if (!network.layers[layer]?.enabled) return "no";
69
+ return "yes";
70
+ }
71
+ function availableTools(network) {
72
+ const effectiveTools = BITTENSOR_SEMANTIC_NETWORKS.has(network.network) && network.layers.topology?.enabled === true ? {
73
+ ...network.tools ?? {},
74
+ ...PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS
75
+ } : network.tools ?? {};
76
+ return Object.entries(effectiveTools).filter(([, status]) => status === "available").map(([name]) => name).sort();
77
+ }
78
+ function availableToolLines(network) {
79
+ const tools = availableTools(network);
80
+ if (tools.length === 0) return ["none"];
81
+ const lines = [];
82
+ for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(", "));
83
+ return lines;
84
+ }
85
+ function shortDate(value) {
86
+ if (!value) return "";
87
+ return value.slice(0, 10);
88
+ }
89
+ function datasetLabel(network) {
90
+ const coverage = network.coverage;
91
+ if (!coverage) return "unknown";
92
+ const blockRange = coverage.from_block !== void 0 && coverage.to_block !== void 0 ? `${coverage.from_block}..${coverage.to_block}` : "blocks unknown";
93
+ const dateRange = coverage.from_timestamp && coverage.to_timestamp ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}` : "";
94
+ if (blockRange === "blocks unknown" && dateRange === "") return "unknown";
95
+ if (blockRange === "blocks unknown") return dateRange;
96
+ if (dateRange === "") return blockRange;
97
+ return `${blockRange} / ${dateRange}`;
98
+ }
99
+ function formatNetworkCapabilities(document) {
100
+ if (document.networks.length === 0) return "No supported networks advertised.";
101
+ const headers = [
102
+ "Network",
103
+ "Topology",
104
+ "Facts",
105
+ "Risk",
106
+ "Dataset",
107
+ "Available tools"
108
+ ];
109
+ const widths = [
110
+ 14,
111
+ 10,
112
+ 8,
113
+ 8,
114
+ 38,
115
+ 64
116
+ ];
117
+ const row = (values) => values.map((value, index) => value.padEnd(widths[index])).join(" ");
118
+ const networkRows = document.networks.flatMap((network) => {
119
+ return availableToolLines(network).map((toolLine, index) => row([
120
+ index === 0 ? network.display_name || network.network : "",
121
+ index === 0 ? layerValue(network, "topology") : "",
122
+ index === 0 ? layerValue(network, "facts") : "",
123
+ index === 0 ? layerValue(network, "risk") : "",
124
+ index === 0 ? datasetLabel(network) : "",
125
+ toolLine
126
+ ]));
127
+ });
128
+ return [
129
+ row(headers),
130
+ widths.map((width) => "-".repeat(width)).join(" "),
131
+ ...networkRows
132
+ ].join("\n");
133
+ }
134
+ //#endregion
135
+ export { fetchNetworkCapabilities, formatNetworkCapabilities };
136
+
137
+ //# sourceMappingURL=capabilities-Bnlnmu6P.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities-Bnlnmu6P.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\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}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\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\nconst BITTENSOR_SEMANTIC_NETWORKS = new Set(['bittensor', 'bittensor_evm', 'bittensor_semantic'])\nconst PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS = {\n aml_address_risk: 'available',\n aml_trace_victim_funds: 'available',\n aml_trace_deposit_sources: 'available',\n aml_trace_suspect_funds: 'available',\n graph_query: 'available',\n graph_query_batch: 'available',\n meta_network_capabilities: 'available',\n meta_usage_status: 'available',\n wallet_balance: 'available',\n} as const\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction publicNetworkCapabilities(document: NetworkCapabilitiesDocument): NetworkCapabilitiesDocument {\n const source = document.networks.find((network) => BITTENSOR_SEMANTIC_NETWORKS.has(network.network))\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: source\n ? [{\n network: 'bittensor',\n display_name: 'Bittensor',\n status: source.status || 'live',\n default: source.default !== false,\n layers: {\n facts: { enabled: source.layers.facts?.enabled === true },\n risk: { enabled: source.layers.risk?.enabled === true },\n topology: {\n enabled: source.layers.topology?.enabled === true,\n ...(source.layers.topology?.live ? { live: source.layers.topology.live } : {}),\n ...(source.layers.topology?.archive ? { archive: source.layers.topology.archive } : {}),\n },\n },\n ...(source.coverage ? { coverage: source.coverage } : {}),\n ...(source.freshness ? { freshness: source.freshness } : {}),\n tools: PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS,\n }]\n : [],\n }\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 applyMcpAuthHeaders(headers, 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 publicNetworkCapabilities(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 availableTools(network: NetworkCapability): string[] {\n const effectiveTools = BITTENSOR_SEMANTIC_NETWORKS.has(network.network) && network.layers.topology?.enabled === true\n ? {\n ...(network.tools ?? {}),\n ...PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS,\n }\n : network.tools ?? {}\n const tools = Object.entries(effectiveTools)\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\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 : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\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 const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => row([\n index === 0 ? network.display_name || network.network : '',\n index === 0 ? layerValue(network, 'topology') : '',\n index === 0 ? layerValue(network, 'facts') : '',\n index === 0 ? layerValue(network, 'risk') : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ]))\n })\n return [\n row(headers),\n widths.map((width) => '-'.repeat(width)).join(' '),\n ...networkRows,\n ].join('\\n')\n}\n"],"mappings":"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"}