chain-insights 0.2.32 → 0.3.3

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 (65) hide show
  1. package/README.md +40 -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 +125 -117
  52. package/docs/investigation-workspaces.md +14 -0
  53. package/docs/mcp-proxy.md +15 -2
  54. package/package.json +1 -1
  55. package/skills/chain-insights-cypher/SKILL.md +6 -0
  56. package/skills/chain-insights-developer-experience/SKILL.md +26 -6
  57. package/skills/chain-insights-investigation/SKILL.md +64 -48
  58. package/skills/chain-insights-trace-funds/SKILL.md +80 -197
  59. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +1 -1
  60. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +4 -4
  61. package/dist/cases-qjPtbnUd.mjs +0 -6
  62. package/dist/public-tools-DoRNhMn9.mjs.map +0 -1
  63. package/dist/resolver-D7VBb0uB.mjs.map +0 -1
  64. package/dist/tool-visibility-BHRFLXuU.mjs.map +0 -1
  65. package/dist/viz-DkJyqlUu.mjs.map +0 -1
package/dist/cli.mjs CHANGED
@@ -19,12 +19,16 @@ if (installerFlags.length > 0 && !rawArgs.some((a) => !a.startsWith("-"))) {
19
19
  }
20
20
  process.exit(0);
21
21
  }
22
- if (rawArgs[0] === "mcp" && rawArgs[1] === "trace-funds") {
23
- console.error("error: unknown command 'trace-funds'");
22
+ if (rawArgs[0] === "mcp" && [
23
+ "trace-funds",
24
+ "track-funds",
25
+ "scam-topology"
26
+ ].includes(rawArgs[1] ?? "")) {
27
+ console.error(`error: unknown command '${rawArgs[1]}'`);
24
28
  process.exit(1);
25
29
  }
26
30
  async function resolveCaseSelector(input) {
27
- const { resolveCaseSelector } = await import("./selector-CTUiQrzI.mjs");
31
+ const { resolveCaseSelector } = await import("./selector-CF2o5gxN.mjs");
28
32
  return resolveCaseSelector(input);
29
33
  }
30
34
  async function scopeCasesToInvocationDir() {
@@ -33,7 +37,7 @@ async function scopeCasesToInvocationDir() {
33
37
  process.env["CHAIN_INSIGHTS_CASES_ROOT"] = activeCasesRoot();
34
38
  }
35
39
  async function showCaseContext(caseSelector) {
36
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
40
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
37
41
  const caseId = await resolveCaseSelector(caseSelector);
38
42
  const ctx = await CaseStore.loadContext(caseId);
39
43
  console.log(`\n=== Case: ${ctx.case.id} ===`);
@@ -63,11 +67,6 @@ function optionalNumberArg(value, name) {
63
67
  if (typeof value === "string") return optionalNumber(value);
64
68
  throw new Error(`Invalid number for ${name}: ${String(value)}`);
65
69
  }
66
- function optionalScamTopologyActivityPolicy(value) {
67
- if (value === void 0 || value === null || value === "") return void 0;
68
- if (value === "node_relative_only" || value === "global_incident_only") return value;
69
- throw new Error("activity_policy must be one of: node_relative_only, global_incident_only");
70
- }
71
70
  async function withGraphMcpClient(name, fn) {
72
71
  const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
73
72
  const config = await loadConfig();
@@ -213,7 +212,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
213
212
  }));
214
213
  program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
215
214
  try {
216
- const { initWorkspace } = await import("./init-DBC9Ml33.mjs");
215
+ const { initWorkspace } = await import("./init-DLBL_nVG.mjs");
217
216
  const result = await initWorkspace({
218
217
  targetDir: dir,
219
218
  force: opts.force
@@ -361,7 +360,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
361
360
  try {
362
361
  const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
363
362
  const { formatToolsTable } = await import("./format-Bq94jSyw.mjs");
364
- const { visibleRemoteTools } = await import("./tool-visibility-BHRFLXuU.mjs").then((n) => n.n);
363
+ const { visibleRemoteTools } = await import("./tool-visibility-BpyZHRBi.mjs").then((n) => n.n);
365
364
  const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
366
365
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4JE7fFF.mjs").then((n) => n.n);
367
366
  const config = await loadConfig();
@@ -402,7 +401,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
402
401
  }));
403
402
  return;
404
403
  }
405
- const { addressRisk } = await import("./public-tools-DoRNhMn9.mjs");
404
+ const { addressRisk } = await import("./public-tools-wJoAFDFa.mjs");
406
405
  const result = await addressRisk(client, {
407
406
  address: opts.address,
408
407
  network: opts.network,
@@ -414,29 +413,30 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
414
413
  console.error(err.message);
415
414
  process.exit(1);
416
415
  }
417
- })).addCommand(new 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) => {
416
+ })).addCommand(new 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) => {
418
417
  try {
419
418
  const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
420
419
  requireWorkspaceRoot();
421
- await withGraphMcpClient("chain-insights-cli-track-funds", async (client, config) => {
420
+ await withGraphMcpClient("chain-insights-cli-trace-victim-funds", async (client, config) => {
422
421
  if (opts.remote) {
423
422
  printMcpTextContent(await client.callTool({
424
- name: "track_funds",
423
+ name: "trace_victim_funds",
425
424
  arguments: {
426
- trusted_addresses: opts.trustedAddresses,
425
+ victim_addresses: opts.victimAddresses,
427
426
  network: opts.network,
428
- ...opts.untrustedAddresses ? { untrusted_addresses: opts.untrustedAddresses } : {}
427
+ ...opts.knownSuspectAddresses ? { known_suspect_addresses: opts.knownSuspectAddresses } : {}
429
428
  }
430
429
  }));
431
430
  return;
432
431
  }
433
- const { trackFunds } = await import("./public-tools-DoRNhMn9.mjs");
432
+ const { traceVictimFunds } = await import("./public-tools-wJoAFDFa.mjs");
434
433
  const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
435
- const result = await trackFunds(client, config, {
436
- trustedAddresses: opts.trustedAddresses,
437
- untrustedAddresses: opts.untrustedAddresses,
434
+ const result = await traceVictimFunds(client, config, {
435
+ victimAddresses: opts.victimAddresses,
436
+ knownSuspectAddresses: opts.knownSuspectAddresses,
438
437
  network: opts.network,
439
438
  caseId,
439
+ incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
440
440
  maxHops: optionalNumber(opts.maxHops),
441
441
  perAddressLimit: optionalNumber(opts.perAddressLimit),
442
442
  minAmountSum: optionalNumber(opts.minAmountSum)
@@ -448,21 +448,40 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
448
448
  console.error(err.message);
449
449
  process.exit(1);
450
450
  }
451
- })).addCommand(new 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) => {
451
+ })).addCommand(new 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) => {
452
+ try {
453
+ const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
454
+ requireWorkspaceRoot();
455
+ await withGraphMcpClient("chain-insights-cli-trace-suspect-funds", async (client, config) => {
456
+ const { traceSuspectFunds } = await import("./public-tools-wJoAFDFa.mjs");
457
+ const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
458
+ const result = await traceSuspectFunds(client, config, {
459
+ suspectAddresses: opts.suspectAddresses,
460
+ network: opts.network,
461
+ maxHops: optionalNumber(opts.maxHops),
462
+ perAddressLimit: optionalNumber(opts.perAddressLimit),
463
+ minAmountSum: optionalNumber(opts.minAmountSum),
464
+ incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
465
+ caseId
466
+ });
467
+ console.log(result.summaryText);
468
+ console.log(JSON.stringify(result.structuredContent, null, 2));
469
+ });
470
+ } catch (err) {
471
+ console.error(err.message);
472
+ process.exit(1);
473
+ }
474
+ })).addCommand(new 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) => {
452
475
  try {
453
476
  const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
454
477
  requireWorkspaceRoot();
455
- await withGraphMcpClient("chain-insights-cli-scam-topology", async (client, config) => {
456
- const { scamTopology } = await import("./public-tools-DoRNhMn9.mjs");
457
- const incidentTimestampMs = optionalNumber(opts.incidentTimestampMs);
458
- if (incidentTimestampMs === void 0) throw new Error("incident-timestamp-ms is required");
478
+ await withGraphMcpClient("chain-insights-cli-trace-deposit-sources", async (client, config) => {
479
+ const { traceDepositSources } = await import("./public-tools-wJoAFDFa.mjs");
459
480
  const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
460
- const result = await scamTopology(client, config, {
461
- victimAddress: opts.victimAddress,
481
+ const result = await traceDepositSources(client, config, {
482
+ depositAddresses: opts.depositAddresses,
462
483
  network: opts.network,
463
484
  maxHops: optionalNumber(opts.maxHops),
464
- incidentTimestampMs,
465
- activityPolicyMode: optionalScamTopologyActivityPolicy(opts.activityPolicy),
466
485
  caseId
467
486
  });
468
487
  console.log(result.summaryText);
@@ -475,7 +494,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
475
494
  })).addCommand(new 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) => {
476
495
  try {
477
496
  await withGraphMcpClient("chain-insights-cli-stake-insights", async (client) => {
478
- const { stakeInsights } = await import("./public-tools-DoRNhMn9.mjs");
497
+ const { stakeInsights } = await import("./public-tools-wJoAFDFa.mjs");
479
498
  const result = await stakeInsights(client, {
480
499
  network: opts.network,
481
500
  address: opts.address,
@@ -498,12 +517,12 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
498
517
  })).addCommand(new 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) => {
499
518
  try {
500
519
  const { parseMcpCallArgs } = await import("./call-args-DPXdX3_D.mjs");
501
- const { assertPublicMcpToolName } = await import("./tool-visibility-BHRFLXuU.mjs").then((n) => n.n);
520
+ const { assertPublicMcpToolName } = await import("./tool-visibility-BpyZHRBi.mjs").then((n) => n.n);
502
521
  const args = parseMcpCallArgs(rawArgs);
503
522
  assertPublicMcpToolName(tool);
504
523
  await withGraphMcpClient("chain-insights-cli-call", async (client, config) => {
505
524
  if (tool === "address_risk") {
506
- const { addressRisk } = await import("./public-tools-DoRNhMn9.mjs");
525
+ const { addressRisk } = await import("./public-tools-wJoAFDFa.mjs");
507
526
  const result = await addressRisk(client, {
508
527
  address: String(args["address"] ?? ""),
509
528
  network: String(args["network"] ?? ""),
@@ -512,13 +531,14 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
512
531
  console.log(result.summaryText);
513
532
  return;
514
533
  }
515
- if (tool === "track_funds") {
516
- const { trackFunds } = await import("./public-tools-DoRNhMn9.mjs");
517
- const result = await trackFunds(client, config, {
518
- trustedAddresses: args["trusted_addresses"] ?? "",
519
- untrustedAddresses: args["untrusted_addresses"],
534
+ if (tool === "trace_victim_funds") {
535
+ const { traceVictimFunds } = await import("./public-tools-wJoAFDFa.mjs");
536
+ const result = await traceVictimFunds(client, config, {
537
+ victimAddresses: args["victim_addresses"] ?? "",
538
+ knownSuspectAddresses: args["known_suspect_addresses"],
520
539
  network: String(args["network"] ?? ""),
521
540
  caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
541
+ incidentTimestampMs: optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms"),
522
542
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
523
543
  perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
524
544
  minAmountSum: typeof args["min_amount_sum"] === "number" ? args["min_amount_sum"] : void 0
@@ -527,26 +547,35 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
527
547
  console.log(JSON.stringify(result.structuredContent, null, 2));
528
548
  return;
529
549
  }
530
- if (tool === "scam_topology") {
531
- const { scamTopology } = await import("./public-tools-DoRNhMn9.mjs");
532
- const victimAddress = String(args["victim_address"] ?? "").trim();
533
- if (!victimAddress) throw new Error("victim_address is required");
534
- const incidentTimestampMs = optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms");
535
- if (incidentTimestampMs === void 0) throw new Error("incident_timestamp_ms is required");
536
- const result = await scamTopology(client, config, {
537
- victimAddress,
550
+ if (tool === "trace_suspect_funds") {
551
+ const { traceSuspectFunds } = await import("./public-tools-wJoAFDFa.mjs");
552
+ const result = await traceSuspectFunds(client, config, {
553
+ suspectAddresses: args["suspect_addresses"] ?? "",
538
554
  network: String(args["network"] ?? ""),
539
555
  caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
540
556
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
541
- incidentTimestampMs,
542
- activityPolicyMode: optionalScamTopologyActivityPolicy(args["activity_policy"])
557
+ perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
558
+ minAmountSum: typeof args["min_amount_sum"] === "number" ? args["min_amount_sum"] : void 0,
559
+ incidentTimestampMs: optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms")
560
+ });
561
+ console.log(result.summaryText);
562
+ console.log(JSON.stringify(result.structuredContent, null, 2));
563
+ return;
564
+ }
565
+ if (tool === "trace_deposit_sources") {
566
+ const { traceDepositSources } = await import("./public-tools-wJoAFDFa.mjs");
567
+ const result = await traceDepositSources(client, config, {
568
+ depositAddresses: args["deposit_addresses"] ?? "",
569
+ network: String(args["network"] ?? ""),
570
+ caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
571
+ maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0
543
572
  });
544
573
  console.log(result.summaryText);
545
574
  console.log(JSON.stringify(result.structuredContent, null, 2));
546
575
  return;
547
576
  }
548
577
  if (tool === "stake_insights") {
549
- const { stakeInsights } = await import("./public-tools-DoRNhMn9.mjs");
578
+ const { stakeInsights } = await import("./public-tools-wJoAFDFa.mjs");
550
579
  const result = await stakeInsights(client, {
551
580
  network: String(args["network"] ?? ""),
552
581
  address: args["address"] === void 0 ? void 0 : String(args["address"]),
@@ -578,14 +607,14 @@ const caseCommand = new Command("case").description("Manage investigation cases"
578
607
  }).addCommand(new 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) => {
579
608
  try {
580
609
  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>\"`.");
581
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
610
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
582
611
  const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
583
612
  const c = await CaseStore.create({
584
613
  name,
585
614
  tags,
586
615
  description: opts.description
587
616
  });
588
- const { casesRoot } = await import("./store-BT2SCcQr.mjs");
617
+ const { casesRoot } = await import("./store-CTtqQtaE.mjs").then((n) => n.r);
589
618
  console.log(`Case opened: ${c.id}`);
590
619
  console.log(`Directory: ${path.join(casesRoot(), c.id)}/`);
591
620
  console.log(`Status: ${c.status}`);
@@ -595,7 +624,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
595
624
  }
596
625
  })).addCommand(new Command("activate").description("Activate a case (set status to active)").argument("<case-id>", "Case ID to activate").action(async (caseSelector) => {
597
626
  try {
598
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
627
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
599
628
  const caseId = await resolveCaseSelector(caseSelector);
600
629
  const c = await CaseStore.setStatus(caseId, "active");
601
630
  console.log(`Case ${c.id} is now: active`);
@@ -605,7 +634,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
605
634
  }
606
635
  })).addCommand(new Command("suspend").description("Suspend a case (set status to suspended)").argument("<case-id>", "Case ID to suspend").action(async (caseSelector) => {
607
636
  try {
608
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
637
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
609
638
  const caseId = await resolveCaseSelector(caseSelector);
610
639
  const c = await CaseStore.setStatus(caseId, "suspended");
611
640
  console.log(`Case ${c.id} is now: suspended`);
@@ -615,7 +644,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
615
644
  }
616
645
  })).addCommand(new Command("close").description("Close a case permanently").argument("<case-id>", "Case ID to close").action(async (caseSelector) => {
617
646
  try {
618
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
647
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
619
648
  const caseId = await resolveCaseSelector(caseSelector);
620
649
  const c = await CaseStore.setStatus(caseId, "closed");
621
650
  console.log(`Case ${c.id} is now: closed`);
@@ -625,7 +654,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
625
654
  }
626
655
  })).addCommand(new Command("list").description("List all investigation cases").option("--status <status>", "Filter by status (open|active|suspended|closed)").action(async (opts) => {
627
656
  try {
628
- const { CaseStore } = await import("./cases-qjPtbnUd.mjs");
657
+ const { CaseStore } = await import("./cases-Cp9DUbEV.mjs");
629
658
  const cases = await CaseStore.list();
630
659
  const filtered = opts.status ? cases.filter((c) => c.status === opts.status) : cases;
631
660
  if (filtered.length === 0) {
@@ -639,7 +668,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
639
668
  }
640
669
  })).addCommand(new Command("evidence").description("Manage case evidence").addCommand(new 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) => {
641
670
  try {
642
- const { EvidenceStore } = await import("./cases-qjPtbnUd.mjs");
671
+ const { EvidenceStore } = await import("./cases-Cp9DUbEV.mjs");
643
672
  const caseId = await resolveCaseSelector(caseSelector);
644
673
  const result = await EvidenceStore.append(caseId, {
645
674
  source: opts.source,
@@ -654,7 +683,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
654
683
  }
655
684
  })).addCommand(new Command("verify").description("Verify evidence manifest integrity for a case").argument("<case-id>", "Case ID to verify").action(async (caseSelector) => {
656
685
  try {
657
- const { EvidenceStore } = await import("./cases-qjPtbnUd.mjs");
686
+ const { EvidenceStore } = await import("./cases-Cp9DUbEV.mjs");
658
687
  const caseId = await resolveCaseSelector(caseSelector);
659
688
  const result = await EvidenceStore.verifyManifest(caseId);
660
689
  if (result.ok) console.log(`Manifest OK — ${result.count} evidence file(s) verified`);
@@ -668,7 +697,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
668
697
  }
669
698
  }))).addCommand(new Command("dossier").description("Manage entity dossiers for a case").addCommand(new 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) => {
670
699
  try {
671
- const { DossierStore } = await import("./cases-qjPtbnUd.mjs");
700
+ const { DossierStore } = await import("./cases-Cp9DUbEV.mjs");
672
701
  const caseId = await resolveCaseSelector(caseSelector);
673
702
  const entityType = [
674
703
  "eoa",
@@ -685,7 +714,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
685
714
  }
686
715
  }))).addCommand(new Command("session").description("Manage investigation sessions").addCommand(new 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) => {
687
716
  try {
688
- const { SessionStore } = await import("./cases-qjPtbnUd.mjs");
717
+ const { SessionStore } = await import("./cases-Cp9DUbEV.mjs");
689
718
  const caseId = await resolveCaseSelector(caseSelector);
690
719
  const title = titleParts.join(" ").trim();
691
720
  const s = await SessionStore.start(caseId, title ? { title } : {});
@@ -696,7 +725,7 @@ const caseCommand = new Command("case").description("Manage investigation cases"
696
725
  }
697
726
  })).addCommand(new 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) => {
698
727
  try {
699
- const { SessionStore } = await import("./cases-qjPtbnUd.mjs");
728
+ const { SessionStore } = await import("./cases-Cp9DUbEV.mjs");
700
729
  const caseId = await resolveCaseSelector(caseSelector);
701
730
  await SessionStore.end(caseId, {
702
731
  findings: opts.findings,
@@ -708,7 +737,34 @@ const caseCommand = new Command("case").description("Manage investigation cases"
708
737
  console.error(err.message);
709
738
  process.exit(1);
710
739
  }
711
- }))).addCommand(new Command("show").description("Show saved case context").argument("<case-id>", "Case ID or case list number to show").action(async (caseSelector) => {
740
+ }))).addCommand(new 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) => {
741
+ try {
742
+ const target = opts.target === "obsidian-llmwiki" ? opts.target : void 0;
743
+ const mode = [
744
+ "private",
745
+ "partner",
746
+ "public"
747
+ ].includes(opts.mode) ? opts.mode : void 0;
748
+ if (!target) throw new Error(`Unsupported export target: ${opts.target}`);
749
+ if (!mode) throw new Error(`Unsupported export mode: ${opts.mode}`);
750
+ const caseId = await resolveCaseSelector(caseSelector);
751
+ const { exportCase } = await import("./export-BqTCO9lP.mjs");
752
+ const result = await exportCase({
753
+ caseId,
754
+ target,
755
+ mode,
756
+ outputDir: opts.out
757
+ });
758
+ console.log(`Case exported: ${result.outputDir}`);
759
+ console.log(`Manifest: ${result.manifestPath}`);
760
+ console.log(`Files: ${result.fileCount}`);
761
+ console.log(`Open first: ${result.nextFile}`);
762
+ for (const warning of result.warnings) console.warn(`Warning: ${warning}`);
763
+ } catch (err) {
764
+ console.error(err.message);
765
+ process.exit(1);
766
+ }
767
+ })).addCommand(new Command("show").description("Show saved case context").argument("<case-id>", "Case ID or case list number to show").action(async (caseSelector) => {
712
768
  try {
713
769
  await showCaseContext(caseSelector);
714
770
  } catch (err) {
@@ -733,7 +789,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
733
789
  }
734
790
  resolvedParams[key] = kv.slice(eq + 1);
735
791
  }
736
- const { resolvePlaybookContent } = await import("./resolver-D7VBb0uB.mjs");
792
+ const { resolvePlaybookContent } = await import("./resolver-2jXNtWQO.mjs");
737
793
  const markdown = await resolvePlaybookContent(name);
738
794
  const { PlaybookParser } = await import("./parser-CJfMsOl6.mjs");
739
795
  const definition = PlaybookParser.parse(markdown, resolvedParams);
@@ -746,7 +802,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
746
802
  console.error(`Invalid --from value: "${opts.from}". Must be a positive integer.`);
747
803
  process.exit(1);
748
804
  }
749
- const { PlaybookRunner } = await import("./runner-CTFK0Qcg.mjs");
805
+ const { PlaybookRunner } = await import("./runner-DIJSbkjc.mjs");
750
806
  await PlaybookRunner.run(definition, {
751
807
  caseId: opts.case,
752
808
  from: fromN,
@@ -759,7 +815,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
759
815
  }
760
816
  })).addCommand(new Command("list").description("List available playbooks (built-in and user-defined)").action(async () => {
761
817
  try {
762
- const { listPlaybooks } = await import("./resolver-D7VBb0uB.mjs");
818
+ const { listPlaybooks } = await import("./resolver-2jXNtWQO.mjs");
763
819
  const playbooks = await listPlaybooks();
764
820
  if (playbooks.length === 0) {
765
821
  console.log("No playbooks found.");
@@ -772,7 +828,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
772
828
  }
773
829
  })).addCommand(new Command("show").description("Show steps for a playbook without executing").argument("<name>", "Playbook name").action(async (name) => {
774
830
  try {
775
- const { resolvePlaybookContent } = await import("./resolver-D7VBb0uB.mjs");
831
+ const { resolvePlaybookContent } = await import("./resolver-2jXNtWQO.mjs");
776
832
  const { PlaybookParser } = await import("./parser-CJfMsOl6.mjs");
777
833
  const markdown = await resolvePlaybookContent(name);
778
834
  const definition = PlaybookParser.parse(markdown, {});
@@ -796,7 +852,7 @@ program.command("viz").description("Generate money flow visualization").argument
796
852
  console.error("Provide either a case ID or --data <file.json>");
797
853
  process.exit(1);
798
854
  }
799
- const { generateVisualization } = await import("./viz-DkJyqlUu.mjs").then((n) => n.n);
855
+ const { generateVisualization } = await import("./viz-5y24S5X1.mjs").then((n) => n.n);
800
856
  const result = await generateVisualization({
801
857
  caseId,
802
858
  dataFile: opts.data