chain-insights 0.2.32 → 0.3.4

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 (66) hide show
  1. package/README.md +43 -14
  2. package/dist/cases-Cp9DUbEV.mjs +6 -0
  3. package/dist/{cases-c0iV-XLI.cjs → cases-sTY5aXav.cjs} +3 -3
  4. package/dist/cli.cjs +122 -66
  5. package/dist/cli.mjs +122 -66
  6. package/dist/cli.mjs.map +1 -1
  7. package/dist/{viz-Da9YWN_I.cjs → data-extractor-Cavd7wHk.cjs} +11 -34
  8. package/dist/{viz-DkJyqlUu.mjs → data-extractor-DZUJu1Bz.mjs} +3 -32
  9. package/dist/data-extractor-DZUJu1Bz.mjs.map +1 -0
  10. package/dist/{dossier-Br62hCG7.cjs → dossier-BXy57V4-.cjs} +13 -1
  11. package/dist/{dossier-Bl0NkJKC.mjs → dossier-Bjpcbcxa.mjs} +4 -2
  12. package/dist/{dossier-Bl0NkJKC.mjs.map → dossier-Bjpcbcxa.mjs.map} +1 -1
  13. package/dist/export-BqTCO9lP.mjs +591 -0
  14. package/dist/export-BqTCO9lP.mjs.map +1 -0
  15. package/dist/export-DsXgtCwO.cjs +592 -0
  16. package/dist/index.cjs +1 -1
  17. package/dist/index.mjs +1 -1
  18. package/dist/{init-DBC9Ml33.mjs → init-DLBL_nVG.mjs} +27 -1
  19. package/dist/{init-DBC9Ml33.mjs.map → init-DLBL_nVG.mjs.map} +1 -1
  20. package/dist/{init-CFaUWgjK.cjs → init-zqbd7i-_.cjs} +26 -0
  21. package/dist/mcp-proxy.cjs +215 -77
  22. package/dist/mcp-proxy.d.cts.map +1 -1
  23. package/dist/mcp-proxy.d.mts.map +1 -1
  24. package/dist/mcp-proxy.mjs +215 -77
  25. package/dist/mcp-proxy.mjs.map +1 -1
  26. package/dist/{public-tools-BwguvIsf.cjs → public-tools-BvMb3H2P.cjs} +701 -1479
  27. package/dist/{public-tools-DoRNhMn9.mjs → public-tools-wJoAFDFa.mjs} +700 -1479
  28. package/dist/public-tools-wJoAFDFa.mjs.map +1 -0
  29. package/dist/{resolver-D7VBb0uB.mjs → resolver-2jXNtWQO.mjs} +12 -29
  30. package/dist/resolver-2jXNtWQO.mjs.map +1 -0
  31. package/dist/{resolver-BUU7ZgW-.cjs → resolver-CZdQwKvh.cjs} +11 -28
  32. package/dist/{runner-BCDeBYsR.cjs → runner-BhZ4lnF1.cjs} +2 -2
  33. package/dist/{runner-CTFK0Qcg.mjs → runner-DIJSbkjc.mjs} +3 -3
  34. package/dist/{runner-CTFK0Qcg.mjs.map → runner-DIJSbkjc.mjs.map} +1 -1
  35. package/dist/{selector-CTUiQrzI.mjs → selector-CF2o5gxN.mjs} +2 -2
  36. package/dist/{selector-CTUiQrzI.mjs.map → selector-CF2o5gxN.mjs.map} +1 -1
  37. package/dist/{selector-DBS2jYH4.cjs → selector-DfAMZEC9.cjs} +1 -1
  38. package/dist/{session-DwyikazY.cjs → session-BT7VpbAd.cjs} +13 -1
  39. package/dist/{session-Bha3zFrx.mjs → session-DROyhebe.mjs} +4 -2
  40. package/dist/{session-Bha3zFrx.mjs.map → session-DROyhebe.mjs.map} +1 -1
  41. package/dist/{store-BT2SCcQr.mjs → store-CTtqQtaE.mjs} +10 -4
  42. package/dist/{store-BT2SCcQr.mjs.map → store-CTtqQtaE.mjs.map} +1 -1
  43. package/dist/{store-DogLawSj.cjs → store-CqPfs47P.cjs} +37 -7
  44. package/dist/{tool-visibility-BHRFLXuU.mjs → tool-visibility-BpyZHRBi.mjs} +4 -2
  45. package/dist/tool-visibility-BpyZHRBi.mjs.map +1 -0
  46. package/dist/{tool-visibility-iAVQV3t0.cjs → tool-visibility-Buq7YdUZ.cjs} +3 -1
  47. package/dist/viz-5y24S5X1.mjs +35 -0
  48. package/dist/viz-5y24S5X1.mjs.map +1 -0
  49. package/dist/viz-Dqp3C5kb.cjs +44 -0
  50. package/docs/contributing.md +3 -2
  51. package/docs/graph-tools.md +126 -117
  52. package/docs/investigation-workspaces.md +17 -0
  53. package/docs/knowledge-exports.md +200 -0
  54. package/docs/mcp-proxy.md +16 -2
  55. package/package.json +1 -1
  56. package/skills/chain-insights-cypher/SKILL.md +6 -0
  57. package/skills/chain-insights-developer-experience/SKILL.md +26 -6
  58. package/skills/chain-insights-investigation/SKILL.md +64 -48
  59. package/skills/chain-insights-trace-funds/SKILL.md +80 -197
  60. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +1 -1
  61. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +4 -4
  62. package/dist/cases-qjPtbnUd.mjs +0 -6
  63. package/dist/public-tools-DoRNhMn9.mjs.map +0 -1
  64. package/dist/resolver-D7VBb0uB.mjs.map +0 -1
  65. package/dist/tool-visibility-BHRFLXuU.mjs.map +0 -1
  66. package/dist/viz-DkJyqlUu.mjs.map +0 -1
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  [Website](https://chain-insights.ai) | [GitHub](https://github.com/chainswarm/chain-insights) | [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
- analysts. Install it from npm to screen blockchain addresses, trace funds,
7
- expand scam topologies, manage case evidence, and generate graph reports.
6
+ analysts. Install it from npm to screen blockchain addresses, trace role-specific
7
+ fund flows, manage case evidence, and generate graph reports.
8
8
 
9
9
  Graph access is configuration-driven. The package defaults to a local GraphRAG
10
10
  MCP endpoint for development; hosted endpoints are set explicitly with
@@ -16,8 +16,9 @@ MCP endpoint for development; hosted endpoints are set explicitly with
16
16
  | --- | --- |
17
17
  | `address_risk` | Screen one address for risk, behavior, neighborhood context, and exchange exposure |
18
18
  | `stake_insights` | Explain Bittensor staking relationships, net stake movement, and counterparties |
19
- | `track_funds` | Trace victim/source funds through intermediaries to exchange deposit candidates |
20
- | `scam_topology` | Expand a known victim incident into reviewable scam infrastructure and label candidates |
19
+ | `trace_victim_funds` | Trace victim/source funds forward to exchange deposit candidates |
20
+ | `trace_deposit_sources` | Trace backward from suspected deposit/cashout addresses to upstream sources and convergence |
21
+ | `trace_suspect_funds` | Trace suspected scammer, mule, operator, or laundering-ring funds forward to cashout topology |
21
22
  | `usage_status` | Check the caller's public free graph query quota for today |
22
23
  | `graph_query` | Run one read-only GQL/Cypher query against a GraphRAG MCP graph layer |
23
24
  | `graph_query_batch` | Run related read-only graph queries as one MCP call |
@@ -123,9 +124,9 @@ cia case open "First Chain Insights investigation" \
123
124
  --tags aml,bittensor \
124
125
  --description "Screen and trace a known source address"
125
126
 
126
- cia mcp track-funds \
127
+ cia mcp trace-victim-funds \
127
128
  --network bittensor \
128
- --trusted-addresses 5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
129
+ --victim-addresses 5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
129
130
  --case 1
130
131
  ```
131
132
 
@@ -136,6 +137,24 @@ cia case show 1
136
137
  find reports cases -maxdepth 3 -type f | sort
137
138
  ```
138
139
 
140
+ ## Export To Obsidian, LLM Wiki, And Agents
141
+
142
+ After a case has evidence, export a local knowledge bundle:
143
+
144
+ ```bash
145
+ cia case evidence verify 1
146
+ cia case export 1 --target obsidian-llmwiki --mode private
147
+ ```
148
+
149
+ The export writes Markdown notes, `manifest.chain-insights.json`,
150
+ `graph.chain-insights.json`, `Graph.canvas`, LLM Wiki entrypoints, and prompts
151
+ for Codex, Claude Code, and ChatGPT under `published/<case-slug>/`.
152
+
153
+ Private exports may include full addresses. Use `--mode public` only for
154
+ shareable demos; public mode aliases addresses and removes secrets by default.
155
+ Install and opening steps live in
156
+ [Knowledge exports](docs/knowledge-exports.md).
157
+
139
158
  ## Demo
140
159
 
141
160
  Run a direct live topology query:
@@ -154,13 +173,12 @@ cia mcp call graph_query_batch \
154
173
  '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"}]'
155
174
  ```
156
175
 
157
- Run victim incident topology:
176
+ Run suspect topology without requiring an incident timestamp:
158
177
 
159
178
  ```bash
160
- cia mcp scam-topology \
179
+ cia mcp trace-suspect-funds \
161
180
  --network bittensor \
162
- --victim-address 5... \
163
- --incident-timestamp-ms 1715532228001 \
181
+ --suspect-addresses 5... \
164
182
  --max-hops 16 \
165
183
  --case 1
166
184
  ```
@@ -208,10 +226,20 @@ and local case state:
208
226
  - `stake_insights` explains Bittensor coldkey-hotkey-netuid staking
209
227
  relationships, aggregate stake movement amounts, top counterparties, first
210
228
  and last activity, and source backend evidence.
211
- - `track_funds` traces trusted victim/source funds through intermediaries to
212
- exchange deposit candidates.
213
- - `scam_topology` expands from a known victim incident into reviewable
214
- topology, safety decisions, and ML-ready label candidates.
229
+ - `trace_victim_funds` traces victim/source funds forward through
230
+ intermediaries to exchange deposit candidates.
231
+ - `trace_deposit_sources` traces backward from suspected deposit/cashout
232
+ addresses to upstream sources and shared-source convergence.
233
+ - `trace_suspect_funds` traces suspected scammer, mule, operator, or
234
+ laundering-ring funds forward to cashout topology.
235
+
236
+ The three trace tools share `chain-insights.trace.v1` and return compact,
237
+ chainable results. Full graph/table/report artifacts remain on disk under the
238
+ workspace, with pointers in the tool result and case evidence.
239
+
240
+ Trace traversal treats exchange hot wallets as terminal endpoints only. Tools do
241
+ not expand through exchange nodes or classify them as deposit, suspect, or
242
+ intermediate candidates.
215
243
 
216
244
  When a case is provided, tools can save compact evidence pointers and graph
217
245
  reports under the workspace instead of embedding large payloads in case notes.
@@ -222,6 +250,7 @@ reports under the workspace instead of embedding large payloads in case notes.
222
250
  | --- | --- |
223
251
  | [Graph tools](docs/graph-tools.md) | GraphRAG MCP layers, `graph_query`, `graph_query_batch`, AML tool contracts, graph reports, evidence pointers |
224
252
  | [Investigation workspaces](docs/investigation-workspaces.md) | `cia init`, case layout, evidence, dossiers, imports, templates, sessions, reports |
253
+ | [Knowledge exports](docs/knowledge-exports.md) | Install Obsidian and LLM Wiki, export verified cases, open vaults, ingest agent-ready Markdown and graph JSON |
225
254
  | [MCP proxy](docs/mcp-proxy.md) | Stdio proxy behavior, endpoint configuration, agent installers, local tools, auth modes, Inspector validation |
226
255
  | [Architecture](docs/architecture.md) | Product layers, data flow, local storage, security model, config keys |
227
256
  | [Development](docs/development.md) | Build, test, and local install commands |
@@ -0,0 +1,6 @@
1
+ import "./frontmatter-D0ccQnUM.mjs";
2
+ import { t as DossierStore } from "./dossier-Bjpcbcxa.mjs";
3
+ import { t as CaseStore } from "./store-CTtqQtaE.mjs";
4
+ import { t as EvidenceStore } from "./evidence-D96PTzOQ.mjs";
5
+ import { t as SessionStore } from "./session-DROyhebe.mjs";
6
+ export { CaseStore, DossierStore, EvidenceStore, SessionStore };
@@ -1,8 +1,8 @@
1
1
  require("./frontmatter-Dvqa5HX6.cjs");
2
- const require_dossier = require("./dossier-Br62hCG7.cjs");
3
- const require_store = require("./store-DogLawSj.cjs");
2
+ const require_dossier = require("./dossier-BXy57V4-.cjs");
3
+ const require_store = require("./store-CqPfs47P.cjs");
4
4
  const require_evidence = require("./evidence-CvEesemA.cjs");
5
- const require_session = require("./session-DwyikazY.cjs");
5
+ const require_session = require("./session-BT7VpbAd.cjs");
6
6
  exports.CaseStore = require_store.CaseStore;
7
7
  exports.DossierStore = require_dossier.DossierStore;
8
8
  exports.EvidenceStore = require_evidence.EvidenceStore;
package/dist/cli.cjs CHANGED
@@ -21,12 +21,16 @@ if (installerFlags.length > 0 && !rawArgs.some((a) => !a.startsWith("-"))) {
21
21
  }
22
22
  process.exit(0);
23
23
  }
24
- if (rawArgs[0] === "mcp" && rawArgs[1] === "trace-funds") {
25
- console.error("error: unknown command 'trace-funds'");
24
+ if (rawArgs[0] === "mcp" && [
25
+ "trace-funds",
26
+ "track-funds",
27
+ "scam-topology"
28
+ ].includes(rawArgs[1] ?? "")) {
29
+ console.error(`error: unknown command '${rawArgs[1]}'`);
26
30
  process.exit(1);
27
31
  }
28
32
  async function resolveCaseSelector(input) {
29
- const { resolveCaseSelector } = await Promise.resolve().then(() => require("./selector-DBS2jYH4.cjs"));
33
+ const { resolveCaseSelector } = await Promise.resolve().then(() => require("./selector-DfAMZEC9.cjs"));
30
34
  return resolveCaseSelector(input);
31
35
  }
32
36
  async function scopeCasesToInvocationDir() {
@@ -35,7 +39,7 @@ async function scopeCasesToInvocationDir() {
35
39
  process.env["CHAIN_INSIGHTS_CASES_ROOT"] = activeCasesRoot();
36
40
  }
37
41
  async function showCaseContext(caseSelector) {
38
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
42
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
39
43
  const caseId = await resolveCaseSelector(caseSelector);
40
44
  const ctx = await CaseStore.loadContext(caseId);
41
45
  console.log(`\n=== Case: ${ctx.case.id} ===`);
@@ -65,11 +69,6 @@ function optionalNumberArg(value, name) {
65
69
  if (typeof value === "string") return optionalNumber(value);
66
70
  throw new Error(`Invalid number for ${name}: ${String(value)}`);
67
71
  }
68
- function optionalScamTopologyActivityPolicy(value) {
69
- if (value === void 0 || value === null || value === "") return void 0;
70
- if (value === "node_relative_only" || value === "global_incident_only") return value;
71
- throw new Error("activity_policy must be one of: node_relative_only, global_incident_only");
72
- }
73
72
  async function withGraphMcpClient(name, fn) {
74
73
  const { loadConfig } = await Promise.resolve().then(() => require("./config-BwVx19Og.cjs")).then((n) => n.config_exports);
75
74
  const config = await loadConfig();
@@ -215,7 +214,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
215
214
  }));
216
215
  program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
217
216
  try {
218
- const { initWorkspace } = await Promise.resolve().then(() => require("./init-CFaUWgjK.cjs"));
217
+ const { initWorkspace } = await Promise.resolve().then(() => require("./init-zqbd7i-_.cjs"));
219
218
  const result = await initWorkspace({
220
219
  targetDir: dir,
221
220
  force: opts.force
@@ -363,7 +362,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
363
362
  try {
364
363
  const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-CJk1EL3L.cjs"));
365
364
  const { formatToolsTable } = await Promise.resolve().then(() => require("./format-9NLBykEL.cjs"));
366
- const { visibleRemoteTools } = await Promise.resolve().then(() => require("./tool-visibility-iAVQV3t0.cjs")).then((n) => n.tool_visibility_exports);
365
+ const { visibleRemoteTools } = await Promise.resolve().then(() => require("./tool-visibility-Buq7YdUZ.cjs")).then((n) => n.tool_visibility_exports);
367
366
  const { loadConfig } = await Promise.resolve().then(() => require("./config-BwVx19Og.cjs")).then((n) => n.config_exports);
368
367
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-Db6IV1tv.cjs")).then((n) => n.client_exports);
369
368
  const config = await loadConfig();
@@ -404,7 +403,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
404
403
  }));
405
404
  return;
406
405
  }
407
- const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
406
+ const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
408
407
  const result = await addressRisk(client, {
409
408
  address: opts.address,
410
409
  network: opts.network,
@@ -416,29 +415,30 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
416
415
  console.error(err.message);
417
416
  process.exit(1);
418
417
  }
419
- })).addCommand(new commander.Command("track-funds").description("Trace trusted/victim addresses and optional known untrusted/scammer addresses").requiredOption("--trusted-addresses <addresses>", "Comma-separated full trusted/victim addresses, max 5").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--untrusted-addresses <addresses>", "Comma-separated full known untrusted/scammer addresses, max 5").option("--case <id>", "Case ID to attach compact evidence pointers").option("--max-hops <number>", "Maximum trace hops, 1-5").option("--per-address-limit <number>", "Maximum exchange paths/results per address, 1-10").option("--min-amount-sum <number>", "Minimum r.amount_sum for traced edges").option("--remote", "Force remote MCP tool call instead of local Chain Insights recipe").action(async (opts) => {
418
+ })).addCommand(new commander.Command("trace-victim-funds").description("Trace victim/source addresses forward to exchange deposit candidates").requiredOption("--victim-addresses <addresses>", "Comma-separated full victim/source addresses, max 5").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--known-suspect-addresses <addresses>", "Optional known suspect addresses for context only, max 5").option("--case <id>", "Case ID to attach compact evidence pointers").option("--incident-timestamp-ms <milliseconds>", "Optional incident timestamp in milliseconds").option("--max-hops <number>", "Maximum trace hops, 1-5").option("--per-address-limit <number>", "Maximum exchange paths/results per address, 1-10").option("--min-amount-sum <number>", "Minimum r.amount_sum for traced edges").option("--remote", "Force remote MCP tool call instead of local Chain Insights recipe").action(async (opts) => {
420
419
  try {
421
420
  const { requireWorkspaceRoot } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
422
421
  requireWorkspaceRoot();
423
- await withGraphMcpClient("chain-insights-cli-track-funds", async (client, config) => {
422
+ await withGraphMcpClient("chain-insights-cli-trace-victim-funds", async (client, config) => {
424
423
  if (opts.remote) {
425
424
  printMcpTextContent(await client.callTool({
426
- name: "track_funds",
425
+ name: "trace_victim_funds",
427
426
  arguments: {
428
- trusted_addresses: opts.trustedAddresses,
427
+ victim_addresses: opts.victimAddresses,
429
428
  network: opts.network,
430
- ...opts.untrustedAddresses ? { untrusted_addresses: opts.untrustedAddresses } : {}
429
+ ...opts.knownSuspectAddresses ? { known_suspect_addresses: opts.knownSuspectAddresses } : {}
431
430
  }
432
431
  }));
433
432
  return;
434
433
  }
435
- const { trackFunds } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
434
+ const { traceVictimFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
436
435
  const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
437
- const result = await trackFunds(client, config, {
438
- trustedAddresses: opts.trustedAddresses,
439
- untrustedAddresses: opts.untrustedAddresses,
436
+ const result = await traceVictimFunds(client, config, {
437
+ victimAddresses: opts.victimAddresses,
438
+ knownSuspectAddresses: opts.knownSuspectAddresses,
440
439
  network: opts.network,
441
440
  caseId,
441
+ incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
442
442
  maxHops: optionalNumber(opts.maxHops),
443
443
  perAddressLimit: optionalNumber(opts.perAddressLimit),
444
444
  minAmountSum: optionalNumber(opts.minAmountSum)
@@ -450,21 +450,40 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
450
450
  console.error(err.message);
451
451
  process.exit(1);
452
452
  }
453
- })).addCommand(new commander.Command("scam-topology").description("Build victim-incident scam topology and ML-ready scam labels").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").requiredOption("--victim-address <address>", "Full victim/source address that anchors the incident").requiredOption("--incident-timestamp-ms <milliseconds>", "Earliest known incident transfer timestamp in milliseconds").option("--max-hops <number>", "Maximum trace hops, default 16, max 64").option("--activity-policy <mode>", "Traversal activity policy: node_relative_only or global_incident_only", "node_relative_only").option("--case <id>", "Case ID to attach compact evidence pointers").action(async (opts) => {
453
+ })).addCommand(new commander.Command("trace-suspect-funds").description("Trace suspected scammer, mule, operator, or laundering-ring addresses forward to cashout topology").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").requiredOption("--suspect-addresses <addresses>", "Comma-separated full suspect-controlled addresses, max 5").option("--incident-timestamp-ms <milliseconds>", "Optional incident timestamp in milliseconds").option("--max-hops <number>", "Maximum trace hops, default 3, max 5").option("--per-address-limit <number>", "Maximum exchange paths/results per address, 1-10").option("--min-amount-sum <number>", "Minimum r.amount_sum for traced edges").option("--case <id>", "Case ID to attach compact evidence pointers").action(async (opts) => {
454
+ try {
455
+ const { requireWorkspaceRoot } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
456
+ requireWorkspaceRoot();
457
+ await withGraphMcpClient("chain-insights-cli-trace-suspect-funds", async (client, config) => {
458
+ const { traceSuspectFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
459
+ const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
460
+ const result = await traceSuspectFunds(client, config, {
461
+ suspectAddresses: opts.suspectAddresses,
462
+ network: opts.network,
463
+ maxHops: optionalNumber(opts.maxHops),
464
+ perAddressLimit: optionalNumber(opts.perAddressLimit),
465
+ minAmountSum: optionalNumber(opts.minAmountSum),
466
+ incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
467
+ caseId
468
+ });
469
+ console.log(result.summaryText);
470
+ console.log(JSON.stringify(result.structuredContent, null, 2));
471
+ });
472
+ } catch (err) {
473
+ console.error(err.message);
474
+ process.exit(1);
475
+ }
476
+ })).addCommand(new commander.Command("trace-deposit-sources").description("Trace backward from suspected deposit/cashout addresses to upstream sources and convergence").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").requiredOption("--deposit-addresses <addresses>", "Comma-separated full suspected deposit/cashout addresses, max 5").option("--max-hops <number>", "Maximum reverse traceback hops, default 2, max 5").option("--case <id>", "Case ID to attach compact evidence pointers").action(async (opts) => {
454
477
  try {
455
478
  const { requireWorkspaceRoot } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
456
479
  requireWorkspaceRoot();
457
- await withGraphMcpClient("chain-insights-cli-scam-topology", async (client, config) => {
458
- const { scamTopology } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
459
- const incidentTimestampMs = optionalNumber(opts.incidentTimestampMs);
460
- if (incidentTimestampMs === void 0) throw new Error("incident-timestamp-ms is required");
480
+ await withGraphMcpClient("chain-insights-cli-trace-deposit-sources", async (client, config) => {
481
+ const { traceDepositSources } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
461
482
  const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
462
- const result = await scamTopology(client, config, {
463
- victimAddress: opts.victimAddress,
483
+ const result = await traceDepositSources(client, config, {
484
+ depositAddresses: opts.depositAddresses,
464
485
  network: opts.network,
465
486
  maxHops: optionalNumber(opts.maxHops),
466
- incidentTimestampMs,
467
- activityPolicyMode: optionalScamTopologyActivityPolicy(opts.activityPolicy),
468
487
  caseId
469
488
  });
470
489
  console.log(result.summaryText);
@@ -477,7 +496,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
477
496
  })).addCommand(new commander.Command("stake-insights").description("Explain Bittensor staking behavior around an address, coldkey, or hotkey").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--address <address>", "Full Bittensor address to inspect as either coldkey or hotkey").option("--coldkey <address>", "Full Bittensor coldkey address to inspect").option("--hotkey <address>", "Full Bittensor hotkey address to inspect").option("--netuid <number>", "Optional subnet netuid filter").option("--start-timestamp-ms <milliseconds>", "Optional inclusive lower activity timestamp bound").option("--end-timestamp-ms <milliseconds>", "Optional inclusive upper activity timestamp bound").option("--start-block <number>", "Optional start block. Current stake graph parity may require timestamp windows instead.").option("--end-block <number>", "Optional end block. Current stake graph parity may require timestamp windows instead.").option("--depth <number>", "Optional expansion depth limit, default 1, max 3").action(async (opts) => {
478
497
  try {
479
498
  await withGraphMcpClient("chain-insights-cli-stake-insights", async (client) => {
480
- const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
499
+ const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
481
500
  const result = await stakeInsights(client, {
482
501
  network: opts.network,
483
502
  address: opts.address,
@@ -500,12 +519,12 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
500
519
  })).addCommand(new commander.Command("call").description("Call an MCP tool directly (debug)").argument("<tool>", "Tool name to call").argument("[args...]", "Key=value arguments (e.g. address=0x1234 chain=ethereum)").action(async (tool, rawArgs) => {
501
520
  try {
502
521
  const { parseMcpCallArgs } = await Promise.resolve().then(() => require("./call-args-CcUV6gFS.cjs"));
503
- const { assertPublicMcpToolName } = await Promise.resolve().then(() => require("./tool-visibility-iAVQV3t0.cjs")).then((n) => n.tool_visibility_exports);
522
+ const { assertPublicMcpToolName } = await Promise.resolve().then(() => require("./tool-visibility-Buq7YdUZ.cjs")).then((n) => n.tool_visibility_exports);
504
523
  const args = parseMcpCallArgs(rawArgs);
505
524
  assertPublicMcpToolName(tool);
506
525
  await withGraphMcpClient("chain-insights-cli-call", async (client, config) => {
507
526
  if (tool === "address_risk") {
508
- const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
527
+ const { addressRisk } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
509
528
  const result = await addressRisk(client, {
510
529
  address: String(args["address"] ?? ""),
511
530
  network: String(args["network"] ?? ""),
@@ -514,13 +533,14 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
514
533
  console.log(result.summaryText);
515
534
  return;
516
535
  }
517
- if (tool === "track_funds") {
518
- const { trackFunds } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
519
- const result = await trackFunds(client, config, {
520
- trustedAddresses: args["trusted_addresses"] ?? "",
521
- untrustedAddresses: args["untrusted_addresses"],
536
+ if (tool === "trace_victim_funds") {
537
+ const { traceVictimFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
538
+ const result = await traceVictimFunds(client, config, {
539
+ victimAddresses: args["victim_addresses"] ?? "",
540
+ knownSuspectAddresses: args["known_suspect_addresses"],
522
541
  network: String(args["network"] ?? ""),
523
542
  caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
543
+ incidentTimestampMs: optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms"),
524
544
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
525
545
  perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
526
546
  minAmountSum: typeof args["min_amount_sum"] === "number" ? args["min_amount_sum"] : void 0
@@ -529,26 +549,35 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
529
549
  console.log(JSON.stringify(result.structuredContent, null, 2));
530
550
  return;
531
551
  }
532
- if (tool === "scam_topology") {
533
- const { scamTopology } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
534
- const victimAddress = String(args["victim_address"] ?? "").trim();
535
- if (!victimAddress) throw new Error("victim_address is required");
536
- const incidentTimestampMs = optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms");
537
- if (incidentTimestampMs === void 0) throw new Error("incident_timestamp_ms is required");
538
- const result = await scamTopology(client, config, {
539
- victimAddress,
552
+ if (tool === "trace_suspect_funds") {
553
+ const { traceSuspectFunds } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
554
+ const result = await traceSuspectFunds(client, config, {
555
+ suspectAddresses: args["suspect_addresses"] ?? "",
540
556
  network: String(args["network"] ?? ""),
541
557
  caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
542
558
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
543
- incidentTimestampMs,
544
- activityPolicyMode: optionalScamTopologyActivityPolicy(args["activity_policy"])
559
+ perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
560
+ minAmountSum: typeof args["min_amount_sum"] === "number" ? args["min_amount_sum"] : void 0,
561
+ incidentTimestampMs: optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms")
562
+ });
563
+ console.log(result.summaryText);
564
+ console.log(JSON.stringify(result.structuredContent, null, 2));
565
+ return;
566
+ }
567
+ if (tool === "trace_deposit_sources") {
568
+ const { traceDepositSources } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
569
+ const result = await traceDepositSources(client, config, {
570
+ depositAddresses: args["deposit_addresses"] ?? "",
571
+ network: String(args["network"] ?? ""),
572
+ caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
573
+ maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0
545
574
  });
546
575
  console.log(result.summaryText);
547
576
  console.log(JSON.stringify(result.structuredContent, null, 2));
548
577
  return;
549
578
  }
550
579
  if (tool === "stake_insights") {
551
- const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BwguvIsf.cjs"));
580
+ const { stakeInsights } = await Promise.resolve().then(() => require("./public-tools-BvMb3H2P.cjs"));
552
581
  const result = await stakeInsights(client, {
553
582
  network: String(args["network"] ?? ""),
554
583
  address: args["address"] === void 0 ? void 0 : String(args["address"]),
@@ -580,14 +609,14 @@ const caseCommand = new commander.Command("case").description("Manage investigat
580
609
  }).addCommand(new commander.Command("open").description("Open a new investigation case").argument("<name>", "Case name (e.g. \"Tornado Mixer Investigation\")").option("--tags <tags>", "Comma-separated tags (e.g. aml,mixer,defi)", "").option("--description <desc>", "Brief description of the investigation", "").action(async (name, opts) => {
581
610
  try {
582
611
  if (/^[1-9]\d*$/.test(name.trim())) throw new Error("Numeric case names look like list selectors. Use a descriptive case name, e.g. `cia case open \"Tracking stolen funds from <address>\"`.");
583
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
612
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
584
613
  const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
585
614
  const c = await CaseStore.create({
586
615
  name,
587
616
  tags,
588
617
  description: opts.description
589
618
  });
590
- const { casesRoot } = await Promise.resolve().then(() => require("./store-DogLawSj.cjs"));
619
+ const { casesRoot } = await Promise.resolve().then(() => require("./store-CqPfs47P.cjs")).then((n) => n.store_exports);
591
620
  console.log(`Case opened: ${c.id}`);
592
621
  console.log(`Directory: ${node_path.default.join(casesRoot(), c.id)}/`);
593
622
  console.log(`Status: ${c.status}`);
@@ -597,7 +626,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
597
626
  }
598
627
  })).addCommand(new commander.Command("activate").description("Activate a case (set status to active)").argument("<case-id>", "Case ID to activate").action(async (caseSelector) => {
599
628
  try {
600
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
629
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
601
630
  const caseId = await resolveCaseSelector(caseSelector);
602
631
  const c = await CaseStore.setStatus(caseId, "active");
603
632
  console.log(`Case ${c.id} is now: active`);
@@ -607,7 +636,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
607
636
  }
608
637
  })).addCommand(new commander.Command("suspend").description("Suspend a case (set status to suspended)").argument("<case-id>", "Case ID to suspend").action(async (caseSelector) => {
609
638
  try {
610
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
639
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
611
640
  const caseId = await resolveCaseSelector(caseSelector);
612
641
  const c = await CaseStore.setStatus(caseId, "suspended");
613
642
  console.log(`Case ${c.id} is now: suspended`);
@@ -617,7 +646,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
617
646
  }
618
647
  })).addCommand(new commander.Command("close").description("Close a case permanently").argument("<case-id>", "Case ID to close").action(async (caseSelector) => {
619
648
  try {
620
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
649
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
621
650
  const caseId = await resolveCaseSelector(caseSelector);
622
651
  const c = await CaseStore.setStatus(caseId, "closed");
623
652
  console.log(`Case ${c.id} is now: closed`);
@@ -627,7 +656,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
627
656
  }
628
657
  })).addCommand(new commander.Command("list").description("List all investigation cases").option("--status <status>", "Filter by status (open|active|suspended|closed)").action(async (opts) => {
629
658
  try {
630
- const { CaseStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
659
+ const { CaseStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
631
660
  const cases = await CaseStore.list();
632
661
  const filtered = opts.status ? cases.filter((c) => c.status === opts.status) : cases;
633
662
  if (filtered.length === 0) {
@@ -641,7 +670,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
641
670
  }
642
671
  })).addCommand(new commander.Command("evidence").description("Manage case evidence").addCommand(new commander.Command("add").description("Add evidence to a case from an MCP query result").argument("<case-id>", "Case ID to add evidence to").option("--source <tool>", "MCP tool name that produced this evidence", "manual").option("--content <text>", "Evidence content (MCP response or notes)", "").option("--query-params <params>", "Query parameters used (e.g. address=0x1234)", "").action(async (caseSelector, opts) => {
643
672
  try {
644
- const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
673
+ const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
645
674
  const caseId = await resolveCaseSelector(caseSelector);
646
675
  const result = await EvidenceStore.append(caseId, {
647
676
  source: opts.source,
@@ -656,7 +685,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
656
685
  }
657
686
  })).addCommand(new commander.Command("verify").description("Verify evidence manifest integrity for a case").argument("<case-id>", "Case ID to verify").action(async (caseSelector) => {
658
687
  try {
659
- const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
688
+ const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
660
689
  const caseId = await resolveCaseSelector(caseSelector);
661
690
  const result = await EvidenceStore.verifyManifest(caseId);
662
691
  if (result.ok) console.log(`Manifest OK — ${result.count} evidence file(s) verified`);
@@ -670,7 +699,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
670
699
  }
671
700
  }))).addCommand(new commander.Command("dossier").description("Manage entity dossiers for a case").addCommand(new commander.Command("update").description("Append a finding to an entity dossier").argument("<case-id>", "Case ID").argument("<address>", "Entity address or identifier").option("--finding <text>", "Finding to append to the dossier", "").option("--type <type>", "Entity type (eoa|contract|exchange|mixer|unknown)", "unknown").action(async (caseSelector, address, opts) => {
672
701
  try {
673
- const { DossierStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
702
+ const { DossierStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
674
703
  const caseId = await resolveCaseSelector(caseSelector);
675
704
  const entityType = [
676
705
  "eoa",
@@ -687,7 +716,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
687
716
  }
688
717
  }))).addCommand(new commander.Command("session").description("Manage investigation sessions").addCommand(new commander.Command("start").description("Start a new investigation session for a case").argument("<case-id>", "Case ID").argument("[title...]", "Optional session title").action(async (caseSelector, titleParts) => {
689
718
  try {
690
- const { SessionStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
719
+ const { SessionStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
691
720
  const caseId = await resolveCaseSelector(caseSelector);
692
721
  const title = titleParts.join(" ").trim();
693
722
  const s = await SessionStore.start(caseId, title ? { title } : {});
@@ -698,7 +727,7 @@ const caseCommand = new commander.Command("case").description("Manage investigat
698
727
  }
699
728
  })).addCommand(new commander.Command("end").description("End the current session with findings and next steps").argument("<case-id>", "Case ID").option("--findings <text>", "Key findings from this session", "").option("--next-steps <text>", "Next steps for the investigation", "").action(async (caseSelector, opts) => {
700
729
  try {
701
- const { SessionStore } = await Promise.resolve().then(() => require("./cases-c0iV-XLI.cjs"));
730
+ const { SessionStore } = await Promise.resolve().then(() => require("./cases-sTY5aXav.cjs"));
702
731
  const caseId = await resolveCaseSelector(caseSelector);
703
732
  await SessionStore.end(caseId, {
704
733
  findings: opts.findings,
@@ -710,7 +739,34 @@ const caseCommand = new commander.Command("case").description("Manage investigat
710
739
  console.error(err.message);
711
740
  process.exit(1);
712
741
  }
713
- }))).addCommand(new commander.Command("show").description("Show saved case context").argument("<case-id>", "Case ID or case list number to show").action(async (caseSelector) => {
742
+ }))).addCommand(new commander.Command("export").description("Export a case for Obsidian, LLMWiki, and agents").argument("<case-id>", "Case ID or case list number to export").option("--target <target>", "Export target: obsidian-llmwiki", "obsidian-llmwiki").option("--mode <mode>", "Redaction mode: private|partner|public", "private").option("--out <directory>", "Output directory. Defaults to published/<case-slug>").action(async (caseSelector, opts) => {
743
+ try {
744
+ const target = opts.target === "obsidian-llmwiki" ? opts.target : void 0;
745
+ const mode = [
746
+ "private",
747
+ "partner",
748
+ "public"
749
+ ].includes(opts.mode) ? opts.mode : void 0;
750
+ if (!target) throw new Error(`Unsupported export target: ${opts.target}`);
751
+ if (!mode) throw new Error(`Unsupported export mode: ${opts.mode}`);
752
+ const caseId = await resolveCaseSelector(caseSelector);
753
+ const { exportCase } = await Promise.resolve().then(() => require("./export-DsXgtCwO.cjs"));
754
+ const result = await exportCase({
755
+ caseId,
756
+ target,
757
+ mode,
758
+ outputDir: opts.out
759
+ });
760
+ console.log(`Case exported: ${result.outputDir}`);
761
+ console.log(`Manifest: ${result.manifestPath}`);
762
+ console.log(`Files: ${result.fileCount}`);
763
+ console.log(`Open first: ${result.nextFile}`);
764
+ for (const warning of result.warnings) console.warn(`Warning: ${warning}`);
765
+ } catch (err) {
766
+ console.error(err.message);
767
+ process.exit(1);
768
+ }
769
+ })).addCommand(new commander.Command("show").description("Show saved case context").argument("<case-id>", "Case ID or case list number to show").action(async (caseSelector) => {
714
770
  try {
715
771
  await showCaseContext(caseSelector);
716
772
  } catch (err) {
@@ -735,7 +791,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
735
791
  }
736
792
  resolvedParams[key] = kv.slice(eq + 1);
737
793
  }
738
- const { resolvePlaybookContent } = await Promise.resolve().then(() => require("./resolver-BUU7ZgW-.cjs"));
794
+ const { resolvePlaybookContent } = await Promise.resolve().then(() => require("./resolver-CZdQwKvh.cjs"));
739
795
  const markdown = await resolvePlaybookContent(name);
740
796
  const { PlaybookParser } = await Promise.resolve().then(() => require("./parser-BXLAHYnZ.cjs"));
741
797
  const definition = PlaybookParser.parse(markdown, resolvedParams);
@@ -748,7 +804,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
748
804
  console.error(`Invalid --from value: "${opts.from}". Must be a positive integer.`);
749
805
  process.exit(1);
750
806
  }
751
- const { PlaybookRunner } = await Promise.resolve().then(() => require("./runner-BCDeBYsR.cjs"));
807
+ const { PlaybookRunner } = await Promise.resolve().then(() => require("./runner-BhZ4lnF1.cjs"));
752
808
  await PlaybookRunner.run(definition, {
753
809
  caseId: opts.case,
754
810
  from: fromN,
@@ -761,7 +817,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
761
817
  }
762
818
  })).addCommand(new commander.Command("list").description("List available playbooks (built-in and user-defined)").action(async () => {
763
819
  try {
764
- const { listPlaybooks } = await Promise.resolve().then(() => require("./resolver-BUU7ZgW-.cjs"));
820
+ const { listPlaybooks } = await Promise.resolve().then(() => require("./resolver-CZdQwKvh.cjs"));
765
821
  const playbooks = await listPlaybooks();
766
822
  if (playbooks.length === 0) {
767
823
  console.log("No playbooks found.");
@@ -774,7 +830,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
774
830
  }
775
831
  })).addCommand(new commander.Command("show").description("Show steps for a playbook without executing").argument("<name>", "Playbook name").action(async (name) => {
776
832
  try {
777
- const { resolvePlaybookContent } = await Promise.resolve().then(() => require("./resolver-BUU7ZgW-.cjs"));
833
+ const { resolvePlaybookContent } = await Promise.resolve().then(() => require("./resolver-CZdQwKvh.cjs"));
778
834
  const { PlaybookParser } = await Promise.resolve().then(() => require("./parser-BXLAHYnZ.cjs"));
779
835
  const markdown = await resolvePlaybookContent(name);
780
836
  const definition = PlaybookParser.parse(markdown, {});
@@ -798,7 +854,7 @@ program.command("viz").description("Generate money flow visualization").argument
798
854
  console.error("Provide either a case ID or --data <file.json>");
799
855
  process.exit(1);
800
856
  }
801
- const { generateVisualization } = await Promise.resolve().then(() => require("./viz-Da9YWN_I.cjs")).then((n) => n.viz_exports);
857
+ const { generateVisualization } = await Promise.resolve().then(() => require("./viz-Dqp3C5kb.cjs")).then((n) => n.viz_exports);
802
858
  const result = await generateVisualization({
803
859
  caseId,
804
860
  dataFile: opts.data