chain-insights 0.3.9 → 0.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +49 -52
  2. package/dist/{active-ByNgjuAg.mjs → active-BQopLul8.mjs} +6 -8
  3. package/dist/active-BQopLul8.mjs.map +1 -0
  4. package/dist/{active-BVr55kvW.cjs → active-XWv72R1X.cjs} +4 -12
  5. package/dist/{app-BxojXjtB.cjs → app-DBrqk_iP.cjs} +12 -28
  6. package/dist/{app-CRd39JJ8.mjs → app-DXwILI_a.mjs} +13 -28
  7. package/dist/app-DXwILI_a.mjs.map +1 -0
  8. package/dist/{artifact-server-CP6LXQ9d.mjs → artifact-server-CcmLBv1j.mjs} +2 -2
  9. package/dist/{artifact-server-CP6LXQ9d.mjs.map → artifact-server-CcmLBv1j.mjs.map} +1 -1
  10. package/dist/{artifact-server-XbN16DwU.cjs → artifact-server-v0WgTPFT.cjs} +1 -1
  11. package/dist/{capabilities-BCvkTkIu.mjs → capabilities-CM72SErE.mjs} +2 -2
  12. package/dist/{capabilities-BCvkTkIu.mjs.map → capabilities-CM72SErE.mjs.map} +1 -1
  13. package/dist/{capabilities-DOa6EFO-.cjs → capabilities-DGeF-oHc.cjs} +1 -1
  14. package/dist/cli.cjs +149 -405
  15. package/dist/cli.mjs +149 -405
  16. package/dist/cli.mjs.map +1 -1
  17. package/dist/{client-Y_zqKqJT.cjs → client-BY-56ojr.cjs} +0 -17
  18. package/dist/{client-BgmHjBHQ.mjs → client-ytTO0mcZ.mjs} +2 -13
  19. package/dist/{client-BgmHjBHQ.mjs.map → client-ytTO0mcZ.mjs.map} +1 -1
  20. package/dist/{config-Drgc2HuF.mjs → config-C6zM8Xir.mjs} +3 -3
  21. package/dist/{config-Drgc2HuF.mjs.map → config-C6zM8Xir.mjs.map} +1 -1
  22. package/dist/{config-BwVx19Og.cjs → config-CkW404Cs.cjs} +2 -2
  23. package/dist/{graph-reports-BDELxmpi.mjs → graph-reports-CEq-Mvx0.mjs} +2 -2
  24. package/dist/{graph-reports-BDELxmpi.mjs.map → graph-reports-CEq-Mvx0.mjs.map} +1 -1
  25. package/dist/{graph-reports-B3mkLP8Z.cjs → graph-reports-CkglRtg4.cjs} +1 -1
  26. package/dist/{html-generator-Bx3UcLTB.cjs → html-generator-BFKafL8y.cjs} +5 -6
  27. package/dist/{html-generator-AowOmzyi.mjs → html-generator-D4fX71hI.mjs} +6 -6
  28. package/dist/html-generator-D4fX71hI.mjs.map +1 -0
  29. package/dist/index.cjs +5 -5
  30. package/dist/index.d.cts +1 -2
  31. package/dist/index.d.cts.map +1 -1
  32. package/dist/index.d.mts +1 -2
  33. package/dist/index.d.mts.map +1 -1
  34. package/dist/index.mjs +5 -5
  35. package/dist/{init-CKQ6F07J.mjs → init-BGDvGreX.mjs} +52 -55
  36. package/dist/init-BGDvGreX.mjs.map +1 -0
  37. package/dist/{init-Dhw8F23z.cjs → init-Cuw9TznI.cjs} +51 -54
  38. package/dist/{mcp-endpoint-DHs1cRFH.mjs → mcp-endpoint-QQ5Lbqc2.mjs} +5 -2
  39. package/dist/mcp-endpoint-QQ5Lbqc2.mjs.map +1 -0
  40. package/dist/{mcp-endpoint-BaV8h_lq.cjs → mcp-endpoint-cQIZSjkK.cjs} +4 -1
  41. package/dist/mcp-proxy.cjs +650 -771
  42. package/dist/mcp-proxy.d.cts.map +1 -1
  43. package/dist/mcp-proxy.d.mts.map +1 -1
  44. package/dist/mcp-proxy.mjs +651 -772
  45. package/dist/mcp-proxy.mjs.map +1 -1
  46. package/dist/{output-root-BRhzhhXZ.mjs → output-root-BK4pdjyz.mjs} +6 -3
  47. package/dist/output-root-BK4pdjyz.mjs.map +1 -0
  48. package/dist/{output-root-YIbl6PwF.cjs → output-root-DI0tzA0X.cjs} +5 -2
  49. package/dist/{public-tools-BY3PTw6x.cjs → public-tools-BREojpU7.cjs} +1244 -426
  50. package/dist/{public-tools-CvlZcysd.mjs → public-tools-brHmHGYm.mjs} +1240 -428
  51. package/dist/public-tools-brHmHGYm.mjs.map +1 -0
  52. package/dist/{schema-BFEWhzg7.mjs → schema-D_qwaQA5.mjs} +2 -2
  53. package/dist/{schema-BFEWhzg7.mjs.map → schema-D_qwaQA5.mjs.map} +1 -1
  54. package/dist/{schema-Vl9yuOFO.cjs → schema-Dr6JXSOF.cjs} +1 -1
  55. package/dist/{server-BXLX2j_A.mjs → server-BK4bfOiv.mjs} +2 -2
  56. package/dist/{server-BXLX2j_A.mjs.map → server-BK4bfOiv.mjs.map} +1 -1
  57. package/dist/{server-BqVdWath.cjs → server-ColyTG1t.cjs} +1 -1
  58. package/dist/templates/graph.html +1 -1
  59. package/dist/{tool-visibility-Buq7YdUZ.cjs → tool-visibility--QPgrRE5.cjs} +5 -1
  60. package/dist/{tool-visibility-BpyZHRBi.mjs → tool-visibility-nr6XqO1F.mjs} +6 -2
  61. package/dist/tool-visibility-nr6XqO1F.mjs.map +1 -0
  62. package/dist/viz-BBvY-wXz.cjs +210 -0
  63. package/dist/viz-D8umSF-t.mjs +199 -0
  64. package/dist/viz-D8umSF-t.mjs.map +1 -0
  65. package/docs/architecture.md +4 -3
  66. package/docs/contributing.md +12 -6
  67. package/docs/graph-tools.md +93 -68
  68. package/docs/investigation-workspaces.md +38 -124
  69. package/docs/mcp-proxy.md +23 -34
  70. package/package.json +2 -2
  71. package/skills/chain-insights-address-risk/SKILL.md +92 -0
  72. package/skills/chain-insights-bittensor-cypher/SKILL.md +2 -22
  73. package/skills/chain-insights-developer-experience/SKILL.md +8 -28
  74. package/skills/chain-insights-exposure-analysis/SKILL.md +83 -0
  75. package/skills/chain-insights-investigation/SKILL.md +59 -211
  76. package/skills/chain-insights-investigation/agents/openai.yaml +1 -1
  77. package/skills/chain-insights-investigation/scripts/run-target-uat.sh +37 -55
  78. package/skills/chain-insights-trace-funds/SKILL.md +14 -14
  79. package/skills/ci-status/SKILL.md +9 -15
  80. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +5 -4
  81. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +272 -18
  82. package/dist/active-ByNgjuAg.mjs.map +0 -1
  83. package/dist/app-CRd39JJ8.mjs.map +0 -1
  84. package/dist/canvas-Cn-maEIh.mjs +0 -203
  85. package/dist/canvas-Cn-maEIh.mjs.map +0 -1
  86. package/dist/canvas-p-oKCMjc.cjs +0 -251
  87. package/dist/cases-Bz_9XKEw.cjs +0 -19
  88. package/dist/cases-TVcAifxu.mjs +0 -16
  89. package/dist/cases-TVcAifxu.mjs.map +0 -1
  90. package/dist/data-extractor-B4nHw1wZ.mjs +0 -336
  91. package/dist/data-extractor-B4nHw1wZ.mjs.map +0 -1
  92. package/dist/data-extractor-DS4rzy3M.cjs +0 -353
  93. package/dist/dossier-BXy57V4-.cjs +0 -88
  94. package/dist/dossier-Bjpcbcxa.mjs +0 -78
  95. package/dist/dossier-Bjpcbcxa.mjs.map +0 -1
  96. package/dist/evidence-CvEesemA.cjs +0 -200
  97. package/dist/evidence-D96PTzOQ.mjs +0 -195
  98. package/dist/evidence-D96PTzOQ.mjs.map +0 -1
  99. package/dist/export-CBhcJuZ6.mjs +0 -394
  100. package/dist/export-CBhcJuZ6.mjs.map +0 -1
  101. package/dist/export-D4v4-6F4.cjs +0 -394
  102. package/dist/frontmatter-D0ccQnUM.mjs +0 -26
  103. package/dist/frontmatter-D0ccQnUM.mjs.map +0 -1
  104. package/dist/frontmatter-Dvqa5HX6.cjs +0 -35
  105. package/dist/html-generator-AowOmzyi.mjs.map +0 -1
  106. package/dist/init-CKQ6F07J.mjs.map +0 -1
  107. package/dist/mcp-endpoint-DHs1cRFH.mjs.map +0 -1
  108. package/dist/output-root-BRhzhhXZ.mjs.map +0 -1
  109. package/dist/parser-BXLAHYnZ.cjs +0 -182
  110. package/dist/parser-CJfMsOl6.mjs +0 -182
  111. package/dist/parser-CJfMsOl6.mjs.map +0 -1
  112. package/dist/public-tools-CvlZcysd.mjs.map +0 -1
  113. package/dist/resolver-2jXNtWQO.mjs +0 -184
  114. package/dist/resolver-2jXNtWQO.mjs.map +0 -1
  115. package/dist/resolver-CZdQwKvh.cjs +0 -186
  116. package/dist/runner-CVnjpqc-.mjs +0 -149
  117. package/dist/runner-CVnjpqc-.mjs.map +0 -1
  118. package/dist/runner-bLy0pTr_.cjs +0 -147
  119. package/dist/selector-BvXM9jbe.mjs +0 -12
  120. package/dist/selector-BvXM9jbe.mjs.map +0 -1
  121. package/dist/selector-Dps_ZFxq.cjs +0 -10
  122. package/dist/session-BT7VpbAd.cjs +0 -127
  123. package/dist/session-DROyhebe.mjs +0 -117
  124. package/dist/session-DROyhebe.mjs.map +0 -1
  125. package/dist/store-C2B_AssI.mjs +0 -231
  126. package/dist/store-C2B_AssI.mjs.map +0 -1
  127. package/dist/store-CQhU8dz8.cjs +0 -242
  128. package/dist/tool-visibility-BpyZHRBi.mjs.map +0 -1
  129. package/dist/vault-B2y78Ypu.cjs +0 -560
  130. package/dist/vault-z35Dohdq.mjs +0 -560
  131. package/dist/vault-z35Dohdq.mjs.map +0 -1
  132. package/dist/viz-D1620cBX.cjs +0 -44
  133. package/dist/viz-DB5XFG1z.mjs +0 -35
  134. package/dist/viz-DB5XFG1z.mjs.map +0 -1
  135. package/docs/knowledge-exports.md +0 -204
  136. package/docs/obsidian-vault.md +0 -130
  137. package/skills/ci-case/SKILL.md +0 -43
package/dist/cli.mjs CHANGED
@@ -27,34 +27,6 @@ if (rawArgs[0] === "mcp" && [
27
27
  console.error(`error: unknown command '${rawArgs[1]}'`);
28
28
  process.exit(1);
29
29
  }
30
- async function resolveCaseSelector(input) {
31
- const { resolveCaseSelector } = await import("./selector-BvXM9jbe.mjs");
32
- return resolveCaseSelector(input);
33
- }
34
- async function scopeCasesToInvocationDir() {
35
- if (process.env["CHAIN_INSIGHTS_CASES_ROOT"]?.trim()) return;
36
- const { activeCasesRoot } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
37
- process.env["CHAIN_INSIGHTS_CASES_ROOT"] = activeCasesRoot();
38
- }
39
- async function showCaseContext(caseSelector) {
40
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
41
- const caseId = await resolveCaseSelector(caseSelector);
42
- const ctx = await CaseStore.loadContext(caseId);
43
- console.log(`\n=== Case: ${ctx.case.id} ===`);
44
- console.log(`Name: ${ctx.case.name}`);
45
- console.log(`Status: ${ctx.case.status}`);
46
- console.log(`Tags: ${ctx.case.tags.join(", ") || "none"}`);
47
- console.log(`Evidence files: ${ctx.evidenceCount}`);
48
- console.log(`Dossiers: ${ctx.dossierSummaries.length}`);
49
- if (ctx.lastSession) {
50
- console.log(`\n--- Last Session (${ctx.lastSession.sessionId}) ---`);
51
- console.log(ctx.lastSession.body.slice(0, 500));
52
- } else console.log("\nNo previous sessions.");
53
- if (ctx.dossierSummaries.length > 0) {
54
- console.log("\n--- Entity Dossiers ---");
55
- for (const d of ctx.dossierSummaries) console.log(` ${d.address} [${d.type}] tags: ${d.riskTags || "none"}`);
56
- }
57
- }
58
30
  function optionalNumber(value) {
59
31
  if (value === void 0) return void 0;
60
32
  const parsed = Number(value);
@@ -68,9 +40,9 @@ function optionalNumberArg(value, name) {
68
40
  throw new Error(`Invalid number for ${name}: ${String(value)}`);
69
41
  }
70
42
  async function withGraphMcpClient(name, fn) {
71
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
43
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
72
44
  const config = await loadConfig();
73
- const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
45
+ const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-ytTO0mcZ.mjs").then((n) => n.r);
74
46
  const paymentFetch = await createConfiguredGraphMcpFetch(config);
75
47
  const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
76
48
  const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
@@ -88,9 +60,51 @@ async function withGraphMcpClient(name, fn) {
88
60
  function printMcpTextContent(result) {
89
61
  for (const item of result.content ?? []) if (item.type === "text") console.log(item.text);
90
62
  }
63
+ function addExposureSubjectOptions(command) {
64
+ return command.requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--account <address>", "Account address to inspect").option("--owner <address>", "Owner address to inspect").option("--counterparty <address>", "Counterparty address to inspect").option("--venue <name>", "Optional venue filter, for example Bittensor or Hyperliquid").option("--instrument <id>", "Optional instrument filter, for example a subnet lifecycle id or BTC-PERP").option("--instrument-type <type>", "Optional instrument type filter, for example subnet, perp, spot, vault, or staking").option("--start-timestamp-ms <milliseconds>", "Optional inclusive lower activity timestamp bound").option("--end-timestamp-ms <milliseconds>", "Optional inclusive upper activity timestamp bound").option("--limit <number>", "Maximum exposure rows, default 100, max 500");
65
+ }
66
+ function addExposureMarketOptions(command, requiredInstrument, includeNetwork = true) {
67
+ let configured = command.option("--venue <name>", "Optional venue filter, for example Bittensor or Hyperliquid").option("--market <id>", "Alias for --instrument when using market language").option("--instrument-type <type>", "Optional instrument type filter, for example subnet, perp, spot, vault, or staking").option("--start-timestamp-ms <milliseconds>", "Optional inclusive lower activity timestamp bound").option("--end-timestamp-ms <milliseconds>", "Optional inclusive upper activity timestamp bound").option("--limit <number>", "Maximum exposure rows, default 100, max 500");
68
+ if (includeNetwork) configured = configured.requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.");
69
+ return requiredInstrument ? configured.requiredOption("--instrument <id>", "Instrument, market, subnet, hotkey, vault, or durable exposure target identifier to inspect") : configured.option("--instrument <id>", "Instrument, market, subnet, hotkey, vault, or durable exposure target identifier to inspect");
70
+ }
71
+ function buildExposureInsightCommand(name, tool, description) {
72
+ const command = new Command(name).description(description);
73
+ const configured = tool === "exposure_crowding" ? addExposureMarketOptions(command, true) : tool === "exposure_exit_pressure" ? addExposureSubjectOptions(command).option("--market <id>", "Alias for --instrument when using market language") : addExposureSubjectOptions(command);
74
+ if (tool === "exposure_correlation") configured.option("--candidate-accounts <addresses>", "Comma-separated candidate accounts to compare against");
75
+ if (tool === "exposure_explain") configured.option("--market <id>", "Alias for --instrument when using market language").option("--position-id <id>", "Optional venue-native position, trade, stake, rotation, or lifecycle identifier");
76
+ return configured.action(async (opts) => {
77
+ try {
78
+ await withGraphMcpClient(`chain-insights-cli-${name}`, async (client) => {
79
+ const { exposureCarry, exposureCorrelation, exposureCrowding, exposureExitPressure, exposureExplain, exposureQuality } = await import("./public-tools-brHmHGYm.mjs");
80
+ const args = {
81
+ network: opts.network,
82
+ account: opts.account,
83
+ owner: opts.owner,
84
+ counterparty: opts.counterparty,
85
+ venue: opts.venue,
86
+ instrument: opts.instrument,
87
+ market: opts.market,
88
+ instrumentType: opts.instrumentType,
89
+ startTimestampMs: optionalNumber(opts.startTimestampMs),
90
+ endTimestampMs: optionalNumber(opts.endTimestampMs),
91
+ limit: optionalNumber(opts.limit),
92
+ candidateAccounts: opts.candidateAccounts,
93
+ positionId: opts.positionId
94
+ };
95
+ const result = tool === "exposure_quality" ? await exposureQuality(client, args) : tool === "exposure_carry" ? await exposureCarry(client, args) : tool === "exposure_crowding" ? await exposureCrowding(client, args) : tool === "exposure_exit_pressure" ? await exposureExitPressure(client, args) : tool === "exposure_correlation" ? await exposureCorrelation(client, args) : await exposureExplain(client, args);
96
+ console.log(result.summaryText);
97
+ console.log(JSON.stringify(result.structuredContent, null, 2));
98
+ });
99
+ } catch (err) {
100
+ console.error(err.message);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ }
91
105
  async function printNetworkCapabilities(opts) {
92
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
93
- const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-BCvkTkIu.mjs");
106
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
107
+ const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-CM72SErE.mjs");
94
108
  const document = await fetchNetworkCapabilities(await loadConfig());
95
109
  if (opts.json) console.log(JSON.stringify(document, null, 2));
96
110
  else console.log(formatNetworkCapabilities(document));
@@ -105,9 +119,9 @@ program.command("networks").alias("network").description("List supported graph n
105
119
  });
106
120
  program.command("serve").description("Start local visualization server").option("-p, --port <number>", "Port to bind (default: 4321)", "4321").action(async (opts) => {
107
121
  try {
108
- const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
122
+ const { requireWorkspaceRoot } = await import("./output-root-BK4pdjyz.mjs").then((n) => n.t);
109
123
  const workspaceRoot = requireWorkspaceRoot();
110
- const { startServer } = await import("./server-BXLX2j_A.mjs").then((n) => n.t);
124
+ const { startServer } = await import("./server-BK4bfOiv.mjs").then((n) => n.t);
111
125
  console.log(`Workspace: ${workspaceRoot}`);
112
126
  startServer(parseInt(opts.port, 10));
113
127
  } catch (err) {
@@ -116,8 +130,8 @@ program.command("serve").description("Start local visualization server").option(
116
130
  }
117
131
  });
118
132
  program.command("status").description("Show toolkit status and configuration").action(async () => {
119
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
120
- const { findActiveWorkspace, activeDataDir } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
133
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
134
+ const { findActiveWorkspace, activeDataDir } = await import("./active-BQopLul8.mjs").then((n) => n.t);
121
135
  const config = await loadConfig();
122
136
  const workspace = findActiveWorkspace();
123
137
  const graphMcpStatus = config.graphMcpMode === "debug" && config.graphMcpAuthToken?.trim() ? "bearer access mode" : `${config.graphMcpMode} mode`;
@@ -152,28 +166,9 @@ program.command("update").description("Check npmjs for a newer Chain Insights re
152
166
  process.exit(1);
153
167
  }
154
168
  });
155
- program.command("obsidian").description("Manage the local Obsidian investigation vault").addCommand(new Command("open").description("Open the current Chain Insights vault in Obsidian").argument("[path]", "Workspace path to open as an Obsidian vault").action(async (workspacePath) => {
156
- try {
157
- const { findActiveWorkspace } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
158
- const workspace = workspacePath ? path.resolve(workspacePath) : findActiveWorkspace()?.root;
159
- if (!workspace) {
160
- console.error("No Chain Insights workspace found. Run: cia init .");
161
- process.exit(1);
162
- }
163
- const open = (await import("open")).default;
164
- await open(workspace, {
165
- app: { name: "obsidian" },
166
- wait: false
167
- });
168
- } catch (err) {
169
- console.error(err.message);
170
- console.error("Open Obsidian manually and choose \"Open folder as vault\" for this workspace.");
171
- process.exit(1);
172
- }
173
- }));
174
169
  program.command("debug").description("Configure Graph MCP debug mode").addCommand(new Command("on").description("Enable Graph MCP debug mode without x402 payments").requiredOption("--token <token>", "Debug bearer token").option("--endpoint <url>", "Graph MCP endpoint").action(async (opts) => {
175
170
  try {
176
- const { saveConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
171
+ const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
177
172
  await saveConfig({
178
173
  graphMcpMode: "debug",
179
174
  graphMcpAuthToken: opts.token,
@@ -188,7 +183,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
188
183
  }
189
184
  })).addCommand(new Command("off").description("Disable Graph MCP debug mode and use paid x402 calls").action(async () => {
190
185
  try {
191
- const { saveConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
186
+ const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
192
187
  await saveConfig({
193
188
  graphMcpMode: "paid",
194
189
  graphMcpAuthToken: ""
@@ -201,7 +196,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
201
196
  }
202
197
  })).addCommand(new Command("status").description("Show Graph MCP payment/debug mode").action(async () => {
203
198
  try {
204
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
199
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
205
200
  const config = await loadConfig();
206
201
  console.log(`Graph MCP mode: ${config.graphMcpMode}`);
207
202
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
@@ -216,7 +211,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
216
211
  try {
217
212
  const normalizedKey = key.trim();
218
213
  if (!normalizedKey) throw new Error("Test access key is required");
219
- const { saveConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
214
+ const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
220
215
  await saveConfig({
221
216
  graphMcpMode: "debug",
222
217
  graphMcpAuthToken: normalizedKey,
@@ -231,7 +226,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
231
226
  }
232
227
  })).addCommand(new Command("clear").description("Remove the Graph MCP test access key and use paid x402 calls").action(async () => {
233
228
  try {
234
- const { saveConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
229
+ const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
235
230
  await saveConfig({
236
231
  graphMcpMode: "paid",
237
232
  graphMcpAuthToken: ""
@@ -244,7 +239,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
244
239
  }
245
240
  })).addCommand(new Command("status").description("Show Graph MCP test access key status").action(async () => {
246
241
  try {
247
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
242
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
248
243
  const config = await loadConfig();
249
244
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
250
245
  console.log(`Access key: ${config.graphMcpAuthToken?.trim() ? "configured" : "not configured"}`);
@@ -256,7 +251,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
256
251
  }));
257
252
  program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
258
253
  try {
259
- const { initWorkspace } = await import("./init-CKQ6F07J.mjs");
254
+ const { initWorkspace } = await import("./init-BGDvGreX.mjs");
260
255
  const result = await initWorkspace({
261
256
  targetDir: dir,
262
257
  force: opts.force
@@ -293,8 +288,8 @@ program.command("setup").description("Configure external MCP clients").addComman
293
288
  }
294
289
  }));
295
290
  program.command("config").description("Read or write configuration values").addCommand(new Command("get").argument("<key>", "Config key to read").action(async (key) => {
296
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
297
- const { CONFIG_KEYS } = await import("./schema-BFEWhzg7.mjs").then((n) => n.r);
291
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
292
+ const { CONFIG_KEYS } = await import("./schema-D_qwaQA5.mjs").then((n) => n.r);
298
293
  if (!CONFIG_KEYS.includes(key)) {
299
294
  console.error(`Unknown config key: ${key}`);
300
295
  process.exit(1);
@@ -314,8 +309,8 @@ program.command("config").description("Read or write configuration values").addC
314
309
  }
315
310
  return;
316
311
  }
317
- const { loadConfig, saveConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
318
- const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-BFEWhzg7.mjs").then((n) => n.r);
312
+ const { loadConfig, saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
313
+ const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-D_qwaQA5.mjs").then((n) => n.r);
319
314
  const current = await loadConfig();
320
315
  if (!CONFIG_KEYS.includes(key)) {
321
316
  console.error(`Unknown config key: ${key}`);
@@ -406,9 +401,9 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
406
401
  try {
407
402
  const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
408
403
  const { formatToolsTable } = await import("./format-Bq94jSyw.mjs");
409
- const { visibleRemoteTools } = await import("./tool-visibility-BpyZHRBi.mjs").then((n) => n.n);
410
- const { loadConfig } = await import("./config-Drgc2HuF.mjs").then((n) => n.t);
411
- const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
404
+ const { visibleRemoteTools } = await import("./tool-visibility-nr6XqO1F.mjs").then((n) => n.n);
405
+ const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
406
+ const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-ytTO0mcZ.mjs").then((n) => n.r);
412
407
  const config = await loadConfig();
413
408
  const graphMcpEndpoint = resolveGraphMcpEndpoint(config);
414
409
  let tools = opts.refresh ? null : await loadSchema(graphMcpEndpoint);
@@ -433,12 +428,12 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
433
428
  console.error(err.message);
434
429
  process.exit(1);
435
430
  }
436
- })).addCommand(new Command("address-risk").description("Screen an address for risk, exchange behavior, and optional compare_address connection risk").requiredOption("--address <address>", "Full blockchain address to screen").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--compare-address <address>", "Optional second address for connection-risk compare mode").option("--remote", "Force remote MCP tool call instead of local Chain Insights recipe").action(async (opts) => {
431
+ })).addCommand(new Command("aml-address-risk").description("Screen an address for risk, exchange behavior, and optional compare_address connection risk").requiredOption("--address <address>", "Full blockchain address to screen").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--compare-address <address>", "Optional second address for connection-risk compare mode").option("--remote", "Force remote MCP tool call instead of local Chain Insights recipe").action(async (opts) => {
437
432
  try {
438
- await withGraphMcpClient("chain-insights-cli-address-risk", async (client) => {
433
+ await withGraphMcpClient("chain-insights-cli-aml-address-risk", async (client) => {
439
434
  if (opts.remote) {
440
435
  printMcpTextContent(await client.callTool({
441
- name: "address_risk",
436
+ name: "aml_address_risk",
442
437
  arguments: {
443
438
  address: opts.address,
444
439
  network: opts.network,
@@ -447,7 +442,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
447
442
  }));
448
443
  return;
449
444
  }
450
- const { addressRisk } = await import("./public-tools-CvlZcysd.mjs");
445
+ const { addressRisk } = await import("./public-tools-brHmHGYm.mjs");
451
446
  const result = await addressRisk(client, {
452
447
  address: opts.address,
453
448
  network: opts.network,
@@ -459,14 +454,14 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
459
454
  console.error(err.message);
460
455
  process.exit(1);
461
456
  }
462
- })).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) => {
457
+ })).addCommand(new Command("aml-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("--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) => {
463
458
  try {
464
- const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
459
+ const { requireWorkspaceRoot } = await import("./output-root-BK4pdjyz.mjs").then((n) => n.t);
465
460
  requireWorkspaceRoot();
466
- await withGraphMcpClient("chain-insights-cli-trace-victim-funds", async (client, config) => {
461
+ await withGraphMcpClient("chain-insights-cli-aml-trace-victim-funds", async (client, config) => {
467
462
  if (opts.remote) {
468
463
  printMcpTextContent(await client.callTool({
469
- name: "trace_victim_funds",
464
+ name: "aml_trace_victim_funds",
470
465
  arguments: {
471
466
  victim_addresses: opts.victimAddresses,
472
467
  network: opts.network,
@@ -475,13 +470,11 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
475
470
  }));
476
471
  return;
477
472
  }
478
- const { traceVictimFunds } = await import("./public-tools-CvlZcysd.mjs");
479
- const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
473
+ const { traceVictimFunds } = await import("./public-tools-brHmHGYm.mjs");
480
474
  const result = await traceVictimFunds(client, config, {
481
475
  victimAddresses: opts.victimAddresses,
482
476
  knownSuspectAddresses: opts.knownSuspectAddresses,
483
477
  network: opts.network,
484
- caseId,
485
478
  incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
486
479
  maxHops: optionalNumber(opts.maxHops),
487
480
  perAddressLimit: optionalNumber(opts.perAddressLimit),
@@ -494,21 +487,19 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
494
487
  console.error(err.message);
495
488
  process.exit(1);
496
489
  }
497
- })).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) => {
490
+ })).addCommand(new Command("aml-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").action(async (opts) => {
498
491
  try {
499
- const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
492
+ const { requireWorkspaceRoot } = await import("./output-root-BK4pdjyz.mjs").then((n) => n.t);
500
493
  requireWorkspaceRoot();
501
- await withGraphMcpClient("chain-insights-cli-trace-suspect-funds", async (client, config) => {
502
- const { traceSuspectFunds } = await import("./public-tools-CvlZcysd.mjs");
503
- const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
494
+ await withGraphMcpClient("chain-insights-cli-aml-trace-suspect-funds", async (client, config) => {
495
+ const { traceSuspectFunds } = await import("./public-tools-brHmHGYm.mjs");
504
496
  const result = await traceSuspectFunds(client, config, {
505
497
  suspectAddresses: opts.suspectAddresses,
506
498
  network: opts.network,
507
499
  maxHops: optionalNumber(opts.maxHops),
508
500
  perAddressLimit: optionalNumber(opts.perAddressLimit),
509
501
  minAmountSum: optionalNumber(opts.minAmountSum),
510
- incidentTimestampMs: optionalNumber(opts.incidentTimestampMs),
511
- caseId
502
+ incidentTimestampMs: optionalNumber(opts.incidentTimestampMs)
512
503
  });
513
504
  console.log(result.summaryText);
514
505
  console.log(JSON.stringify(result.structuredContent, null, 2));
@@ -517,18 +508,16 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
517
508
  console.error(err.message);
518
509
  process.exit(1);
519
510
  }
520
- })).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) => {
511
+ })).addCommand(new Command("aml-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").action(async (opts) => {
521
512
  try {
522
- const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
513
+ const { requireWorkspaceRoot } = await import("./output-root-BK4pdjyz.mjs").then((n) => n.t);
523
514
  requireWorkspaceRoot();
524
- await withGraphMcpClient("chain-insights-cli-trace-deposit-sources", async (client, config) => {
525
- const { traceDepositSources } = await import("./public-tools-CvlZcysd.mjs");
526
- const caseId = opts.case ? await resolveCaseSelector(opts.case) : void 0;
515
+ await withGraphMcpClient("chain-insights-cli-aml-trace-deposit-sources", async (client, config) => {
516
+ const { traceDepositSources } = await import("./public-tools-brHmHGYm.mjs");
527
517
  const result = await traceDepositSources(client, config, {
528
518
  depositAddresses: opts.depositAddresses,
529
519
  network: opts.network,
530
- maxHops: optionalNumber(opts.maxHops),
531
- caseId
520
+ maxHops: optionalNumber(opts.maxHops)
532
521
  });
533
522
  console.log(result.summaryText);
534
523
  console.log(JSON.stringify(result.structuredContent, null, 2));
@@ -537,21 +526,21 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
537
526
  console.error(err.message);
538
527
  process.exit(1);
539
528
  }
540
- })).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) => {
529
+ })).addCommand(new Command("exposure-profile").description("Explain staking or trading exposure around one account, owner, or counterparty").requiredOption("--network <network>", "Network to query. Run `cia mcp networks` for supported networks.").option("--account <address>", "Account address to inspect").option("--owner <address>", "Owner address to inspect").option("--counterparty <address>", "Counterparty address to inspect").option("--venue <name>", "Optional venue filter, for example Bittensor or Hyperliquid").option("--instrument <id>", "Optional instrument filter, for example a subnet lifecycle id or BTC-PERP").option("--instrument-type <type>", "Optional instrument type filter, for example subnet, perp, spot, vault, or staking").option("--start-timestamp-ms <milliseconds>", "Optional inclusive lower activity timestamp bound").option("--end-timestamp-ms <milliseconds>", "Optional inclusive upper activity timestamp bound").option("--limit <number>", "Maximum exposure rows, default 100, max 500").action(async (opts) => {
541
530
  try {
542
- await withGraphMcpClient("chain-insights-cli-stake-insights", async (client) => {
543
- const { stakeInsights } = await import("./public-tools-CvlZcysd.mjs");
544
- const result = await stakeInsights(client, {
531
+ await withGraphMcpClient("chain-insights-cli-exposure-profile", async (client) => {
532
+ const { exposureProfile } = await import("./public-tools-brHmHGYm.mjs");
533
+ const result = await exposureProfile(client, {
545
534
  network: opts.network,
546
- address: opts.address,
547
- coldkey: opts.coldkey,
548
- hotkey: opts.hotkey,
549
- netuid: optionalNumber(opts.netuid),
535
+ account: opts.account,
536
+ owner: opts.owner,
537
+ counterparty: opts.counterparty,
538
+ venue: opts.venue,
539
+ instrument: opts.instrument,
540
+ instrumentType: opts.instrumentType,
550
541
  startTimestampMs: optionalNumber(opts.startTimestampMs),
551
542
  endTimestampMs: optionalNumber(opts.endTimestampMs),
552
- startBlock: optionalNumber(opts.startBlock),
553
- endBlock: optionalNumber(opts.endBlock),
554
- depth: optionalNumber(opts.depth)
543
+ limit: optionalNumber(opts.limit)
555
544
  });
556
545
  console.log(result.summaryText);
557
546
  console.log(JSON.stringify(result.structuredContent, null, 2));
@@ -560,15 +549,15 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
560
549
  console.error(err.message);
561
550
  process.exit(1);
562
551
  }
563
- })).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) => {
552
+ })).addCommand(buildExposureInsightCommand("exposure-quality", "exposure_quality", "Score whether exposure behavior looks disciplined, fragile, lucky, or noisy")).addCommand(buildExposureInsightCommand("exposure-carry", "exposure_carry", "Explain carry earned or paid by staking, trading, funding, fees, emissions, or dividends")).addCommand(buildExposureInsightCommand("exposure-crowding", "exposure_crowding", "Measure crowding and side concentration for a market, subnet, hotkey, vault, or strategy")).addCommand(buildExposureInsightCommand("exposure-exit-pressure", "exposure_exit_pressure", "Explain liquidation, slippage, funding pain, unstake, or other exit pressure")).addCommand(buildExposureInsightCommand("exposure-correlation", "exposure_correlation", "Compare accounts for possible copy, overlap, or strategy-cluster exposure behavior")).addCommand(buildExposureInsightCommand("exposure-explain", "exposure_explain", "Explain a specific exposure lifecycle, trade, position, stake, rotation, or incident")).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) => {
564
553
  try {
565
554
  const { parseMcpCallArgs } = await import("./call-args-DPXdX3_D.mjs");
566
- const { assertPublicMcpToolName } = await import("./tool-visibility-BpyZHRBi.mjs").then((n) => n.n);
555
+ const { assertPublicMcpToolName } = await import("./tool-visibility-nr6XqO1F.mjs").then((n) => n.n);
567
556
  const args = parseMcpCallArgs(rawArgs);
568
557
  assertPublicMcpToolName(tool);
569
558
  await withGraphMcpClient("chain-insights-cli-call", async (client, config) => {
570
- if (tool === "address_risk") {
571
- const { addressRisk } = await import("./public-tools-CvlZcysd.mjs");
559
+ if (tool === "aml_address_risk") {
560
+ const { addressRisk } = await import("./public-tools-brHmHGYm.mjs");
572
561
  const result = await addressRisk(client, {
573
562
  address: String(args["address"] ?? ""),
574
563
  network: String(args["network"] ?? ""),
@@ -577,13 +566,12 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
577
566
  console.log(result.summaryText);
578
567
  return;
579
568
  }
580
- if (tool === "trace_victim_funds") {
581
- const { traceVictimFunds } = await import("./public-tools-CvlZcysd.mjs");
569
+ if (tool === "aml_trace_victim_funds") {
570
+ const { traceVictimFunds } = await import("./public-tools-brHmHGYm.mjs");
582
571
  const result = await traceVictimFunds(client, config, {
583
572
  victimAddresses: args["victim_addresses"] ?? "",
584
573
  knownSuspectAddresses: args["known_suspect_addresses"],
585
574
  network: String(args["network"] ?? ""),
586
- caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
587
575
  incidentTimestampMs: optionalNumberArg(args["incident_timestamp_ms"], "incident_timestamp_ms"),
588
576
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
589
577
  perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
@@ -593,12 +581,11 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
593
581
  console.log(JSON.stringify(result.structuredContent, null, 2));
594
582
  return;
595
583
  }
596
- if (tool === "trace_suspect_funds") {
597
- const { traceSuspectFunds } = await import("./public-tools-CvlZcysd.mjs");
584
+ if (tool === "aml_trace_suspect_funds") {
585
+ const { traceSuspectFunds } = await import("./public-tools-brHmHGYm.mjs");
598
586
  const result = await traceSuspectFunds(client, config, {
599
587
  suspectAddresses: args["suspect_addresses"] ?? "",
600
588
  network: String(args["network"] ?? ""),
601
- caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
602
589
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0,
603
590
  perAddressLimit: typeof args["per_address_limit"] === "number" ? args["per_address_limit"] : void 0,
604
591
  minAmountSum: typeof args["min_amount_sum"] === "number" ? args["min_amount_sum"] : void 0,
@@ -608,36 +595,64 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
608
595
  console.log(JSON.stringify(result.structuredContent, null, 2));
609
596
  return;
610
597
  }
611
- if (tool === "trace_deposit_sources") {
612
- const { traceDepositSources } = await import("./public-tools-CvlZcysd.mjs");
598
+ if (tool === "aml_trace_deposit_sources") {
599
+ const { traceDepositSources } = await import("./public-tools-brHmHGYm.mjs");
613
600
  const result = await traceDepositSources(client, config, {
614
601
  depositAddresses: args["deposit_addresses"] ?? "",
615
602
  network: String(args["network"] ?? ""),
616
- caseId: args["case_id"] === void 0 ? void 0 : String(args["case_id"]),
617
603
  maxHops: typeof args["max_hops"] === "number" ? args["max_hops"] : void 0
618
604
  });
619
605
  console.log(result.summaryText);
620
606
  console.log(JSON.stringify(result.structuredContent, null, 2));
621
607
  return;
622
608
  }
623
- if (tool === "stake_insights") {
624
- const { stakeInsights } = await import("./public-tools-CvlZcysd.mjs");
625
- const result = await stakeInsights(client, {
609
+ if (tool === "exposure_profile") {
610
+ const { exposureProfile } = await import("./public-tools-brHmHGYm.mjs");
611
+ const result = await exposureProfile(client, {
626
612
  network: String(args["network"] ?? ""),
627
- address: args["address"] === void 0 ? void 0 : String(args["address"]),
628
- coldkey: args["coldkey"] === void 0 ? void 0 : String(args["coldkey"]),
629
- hotkey: args["hotkey"] === void 0 ? void 0 : String(args["hotkey"]),
630
- netuid: optionalNumberArg(args["netuid"], "netuid"),
613
+ account: args["account"] === void 0 ? void 0 : String(args["account"]),
614
+ owner: args["owner"] === void 0 ? void 0 : String(args["owner"]),
615
+ counterparty: args["counterparty"] === void 0 ? void 0 : String(args["counterparty"]),
616
+ venue: args["venue"] === void 0 ? void 0 : String(args["venue"]),
617
+ instrument: args["instrument"] === void 0 ? void 0 : String(args["instrument"]),
618
+ instrumentType: args["instrument_type"] === void 0 ? void 0 : String(args["instrument_type"]),
631
619
  startTimestampMs: optionalNumberArg(args["start_timestamp_ms"], "start_timestamp_ms"),
632
620
  endTimestampMs: optionalNumberArg(args["end_timestamp_ms"], "end_timestamp_ms"),
633
- startBlock: optionalNumberArg(args["start_block"], "start_block"),
634
- endBlock: optionalNumberArg(args["end_block"], "end_block"),
635
- depth: optionalNumberArg(args["depth"] ?? args["max_hops"], "depth")
621
+ limit: optionalNumberArg(args["limit"], "limit")
636
622
  });
637
623
  console.log(result.summaryText);
638
624
  console.log(JSON.stringify(result.structuredContent, null, 2));
639
625
  return;
640
626
  }
627
+ if ([
628
+ "exposure_quality",
629
+ "exposure_carry",
630
+ "exposure_crowding",
631
+ "exposure_exit_pressure",
632
+ "exposure_correlation",
633
+ "exposure_explain"
634
+ ].includes(tool)) {
635
+ const { exposureCarry, exposureCorrelation, exposureCrowding, exposureExitPressure, exposureExplain, exposureQuality } = await import("./public-tools-brHmHGYm.mjs");
636
+ const exposureArgs = {
637
+ network: String(args["network"] ?? ""),
638
+ account: args["account"] === void 0 ? void 0 : String(args["account"]),
639
+ owner: args["owner"] === void 0 ? void 0 : String(args["owner"]),
640
+ counterparty: args["counterparty"] === void 0 ? void 0 : String(args["counterparty"]),
641
+ venue: args["venue"] === void 0 ? void 0 : String(args["venue"]),
642
+ instrument: args["instrument"] === void 0 ? void 0 : String(args["instrument"]),
643
+ market: args["market"] === void 0 ? void 0 : String(args["market"]),
644
+ instrumentType: args["instrument_type"] === void 0 ? void 0 : String(args["instrument_type"]),
645
+ startTimestampMs: optionalNumberArg(args["start_timestamp_ms"], "start_timestamp_ms"),
646
+ endTimestampMs: optionalNumberArg(args["end_timestamp_ms"], "end_timestamp_ms"),
647
+ limit: optionalNumberArg(args["limit"], "limit"),
648
+ candidateAccounts: args["candidate_accounts"],
649
+ positionId: args["position_id"] === void 0 ? void 0 : String(args["position_id"])
650
+ };
651
+ const result = tool === "exposure_quality" ? await exposureQuality(client, exposureArgs) : tool === "exposure_carry" ? await exposureCarry(client, exposureArgs) : tool === "exposure_crowding" ? await exposureCrowding(client, exposureArgs) : tool === "exposure_exit_pressure" ? await exposureExitPressure(client, exposureArgs) : tool === "exposure_correlation" ? await exposureCorrelation(client, exposureArgs) : await exposureExplain(client, exposureArgs);
652
+ console.log(result.summaryText);
653
+ console.log(JSON.stringify(result.structuredContent, null, 2));
654
+ return;
655
+ }
641
656
  printMcpTextContent(await client.callTool({
642
657
  name: tool,
643
658
  arguments: args
@@ -648,289 +663,18 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
648
663
  process.exit(1);
649
664
  }
650
665
  }));
651
- const caseCommand = new Command("case").description("Manage investigation cases").hook("preAction", async () => {
652
- await scopeCasesToInvocationDir();
653
- }).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) => {
654
- try {
655
- 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>\"`.");
656
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
657
- const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
658
- const c = await CaseStore.create({
659
- name,
660
- tags,
661
- description: opts.description
662
- });
663
- const { casesRoot } = await import("./store-C2B_AssI.mjs").then((n) => n.n);
664
- console.log(`Case opened: ${c.id}`);
665
- console.log(`Directory: ${path.join(casesRoot(), c.id)}/`);
666
- console.log(`Status: ${c.status}`);
667
- const { findActiveWorkspace } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
668
- if (findActiveWorkspace()) try {
669
- const { refreshCaseVault } = await import("./vault-z35Dohdq.mjs");
670
- const result = await refreshCaseVault({
671
- caseId: c.id,
672
- force: true
673
- });
674
- console.log(`Open first: ${result.nextFile}`);
675
- } catch (refreshErr) {
676
- console.error(`Warning: live vault refresh failed: ${refreshErr.message}`);
677
- console.error(`Run: cia case vault refresh ${c.id} --force`);
678
- }
679
- } catch (err) {
680
- console.error(err.message);
681
- process.exit(1);
682
- }
683
- })).addCommand(new Command("activate").description("Activate a case (set status to active)").argument("<case-id>", "Case ID to activate").action(async (caseSelector) => {
684
- try {
685
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
686
- const caseId = await resolveCaseSelector(caseSelector);
687
- const c = await CaseStore.setStatus(caseId, "active");
688
- console.log(`Case ${c.id} is now: active`);
689
- } catch (err) {
690
- console.error(err.message);
691
- process.exit(1);
692
- }
693
- })).addCommand(new Command("suspend").description("Suspend a case (set status to suspended)").argument("<case-id>", "Case ID to suspend").action(async (caseSelector) => {
694
- try {
695
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
696
- const caseId = await resolveCaseSelector(caseSelector);
697
- const c = await CaseStore.setStatus(caseId, "suspended");
698
- console.log(`Case ${c.id} is now: suspended`);
699
- } catch (err) {
700
- console.error(err.message);
701
- process.exit(1);
702
- }
703
- })).addCommand(new Command("close").description("Close a case permanently").argument("<case-id>", "Case ID to close").action(async (caseSelector) => {
704
- try {
705
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
706
- const caseId = await resolveCaseSelector(caseSelector);
707
- const c = await CaseStore.setStatus(caseId, "closed");
708
- console.log(`Case ${c.id} is now: closed`);
709
- } catch (err) {
710
- console.error(err.message);
711
- process.exit(1);
712
- }
713
- })).addCommand(new Command("list").description("List all investigation cases").option("--status <status>", "Filter by status (open|active|suspended|closed)").action(async (opts) => {
714
- try {
715
- const { CaseStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
716
- const cases = await CaseStore.list();
717
- const filtered = opts.status ? cases.filter((c) => c.status === opts.status) : cases;
718
- if (filtered.length === 0) {
719
- console.log("No cases found.");
720
- return;
721
- }
722
- for (const [index, c] of filtered.entries()) console.log(`${index + 1}. ${c.id} [${c.status}] ${c.name}`);
723
- } catch (err) {
724
- console.error(err.message);
725
- process.exit(1);
726
- }
727
- })).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) => {
728
- try {
729
- const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
730
- const caseId = await resolveCaseSelector(caseSelector);
731
- const result = await EvidenceStore.append(caseId, {
732
- source: opts.source,
733
- content: opts.content,
734
- queryParams: opts.queryParams
735
- });
736
- console.log(`Evidence saved: ${result.filename}`);
737
- console.log(`SHA-256: ${result.sha256}`);
738
- } catch (err) {
739
- console.error(err.message);
740
- process.exit(1);
741
- }
742
- })).addCommand(new Command("verify").description("Verify evidence manifest integrity for a case").argument("<case-id>", "Case ID to verify").action(async (caseSelector) => {
743
- try {
744
- const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
745
- const caseId = await resolveCaseSelector(caseSelector);
746
- const result = await EvidenceStore.verifyManifest(caseId);
747
- if (result.ok) console.log(`Manifest OK — ${result.count} evidence file(s) verified`);
748
- else {
749
- console.error(`Manifest FAILED — tampered files: ${(result.tampered ?? []).join(", ")}`);
750
- process.exit(1);
751
- }
752
- } catch (err) {
753
- console.error(err.message);
754
- process.exit(1);
755
- }
756
- }))).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) => {
757
- try {
758
- const { DossierStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
759
- const caseId = await resolveCaseSelector(caseSelector);
760
- const entityType = [
761
- "eoa",
762
- "contract",
763
- "exchange",
764
- "mixer",
765
- "unknown"
766
- ].includes(opts.type) ? opts.type : "unknown";
767
- await DossierStore.appendFinding(caseId, address, opts.finding, entityType);
768
- console.log(`Dossier updated for ${address}`);
769
- } catch (err) {
770
- console.error(err.message);
771
- process.exit(1);
772
- }
773
- }))).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) => {
774
- try {
775
- const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
776
- const caseId = await resolveCaseSelector(caseSelector);
777
- const title = titleParts.join(" ").trim();
778
- const s = await SessionStore.start(caseId, title ? { title } : {});
779
- console.log(`Session started: ${s.sessionId}`);
780
- } catch (err) {
781
- console.error(err.message);
782
- process.exit(1);
783
- }
784
- })).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) => {
785
- try {
786
- const { SessionStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
787
- const caseId = await resolveCaseSelector(caseSelector);
788
- await SessionStore.end(caseId, {
789
- findings: opts.findings,
790
- nextSteps: opts.nextSteps
791
- });
792
- await SessionStore.archiveOldSessions(caseId);
793
- console.log(`Session ended for case ${caseId}`);
794
- } catch (err) {
795
- console.error(err.message);
796
- process.exit(1);
797
- }
798
- }))).addCommand(new Command("vault").description("Manage live Obsidian case vault notes").addCommand(new Command("refresh").description("Refresh Obsidian vault notes for a case").argument("<case-id-or-selector>", "Case ID or case list number to refresh").option("--force", "Overwrite existing generated case vault files").action(async (caseSelector, opts) => {
799
- try {
800
- const caseId = await resolveCaseSelector(caseSelector);
801
- const { refreshCaseVault } = await import("./vault-z35Dohdq.mjs");
802
- const result = await refreshCaseVault({
803
- caseId,
804
- force: opts.force === true
805
- });
806
- console.log(`Case vault refreshed: ${caseId}`);
807
- console.log(`Files: ${result.filesWritten.length}`);
808
- console.log(`Open first: ${result.nextFile}`);
809
- } catch (err) {
810
- console.error(err.message);
811
- process.exit(1);
812
- }
813
- }))).addCommand(new Command("export").description("Export a case for Obsidian, LLM Wiki, 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) => {
814
- try {
815
- const target = opts.target === "obsidian-llmwiki" ? opts.target : void 0;
816
- const mode = [
817
- "private",
818
- "partner",
819
- "public"
820
- ].includes(opts.mode) ? opts.mode : void 0;
821
- if (!target) throw new Error(`Unsupported export target: ${opts.target}`);
822
- if (!mode) throw new Error(`Unsupported export mode: ${opts.mode}`);
823
- const caseId = await resolveCaseSelector(caseSelector);
824
- const { exportCase } = await import("./export-CBhcJuZ6.mjs");
825
- const result = await exportCase({
826
- caseId,
827
- target,
828
- mode,
829
- outputDir: opts.out
830
- });
831
- console.log(`Case exported: ${result.outputDir}`);
832
- console.log(`Manifest: ${result.manifestPath}`);
833
- console.log(`Files: ${result.fileCount}`);
834
- console.log(`Open first: ${result.nextFile}`);
835
- for (const warning of result.warnings) console.warn(`Warning: ${warning}`);
836
- } catch (err) {
837
- console.error(err.message);
838
- process.exit(1);
839
- }
840
- })).addCommand(new Command("show").description("Show saved case context").argument("<case-id>", "Case ID or case list number to show").action(async (caseSelector) => {
841
- try {
842
- await showCaseContext(caseSelector);
843
- } catch (err) {
844
- console.error(err.message);
845
- process.exit(1);
846
- }
847
- }));
848
- program.addCommand(caseCommand);
849
- program.command("playbook").description("Run and manage investigation playbooks").addCommand(new Command("run").description("Execute a playbook by name").argument("<name>", "Playbook name (e.g. trace-funds, risk-check, entity-profile)").option("--case <id>", "Case ID to attach evidence to (auto-created if omitted)").option("--from <n>", "Resume from step N (1-based)", "1").option("--dry-run", "Show steps without executing").option("-p, --param <kv...>", "Parameters as key=value pairs (repeatable, e.g. -p address=0x1 -p hops=3)").action(async (name, opts) => {
850
- try {
851
- const resolvedParams = {};
852
- for (const kv of opts.param ?? []) {
853
- const eq = kv.indexOf("=");
854
- if (eq === -1) {
855
- console.error(`Invalid param format: "${kv}". Use key=value`);
856
- process.exit(1);
857
- }
858
- const key = kv.slice(0, eq);
859
- if (!key) {
860
- console.error(`Invalid param format: "${kv}". Key must be non-empty`);
861
- process.exit(1);
862
- }
863
- resolvedParams[key] = kv.slice(eq + 1);
864
- }
865
- const { resolvePlaybookContent } = await import("./resolver-2jXNtWQO.mjs");
866
- const markdown = await resolvePlaybookContent(name);
867
- const { PlaybookParser } = await import("./parser-CJfMsOl6.mjs");
868
- const definition = PlaybookParser.parse(markdown, resolvedParams);
869
- for (const spec of definition.params) if (spec.required && !resolvedParams[spec.name] && !spec.default) {
870
- console.error(`Missing required param: ${spec.name}. Pass with: -p ${spec.name}=<value>`);
871
- process.exit(1);
872
- }
873
- const fromN = parseInt(opts.from, 10);
874
- if (isNaN(fromN) || fromN < 1) {
875
- console.error(`Invalid --from value: "${opts.from}". Must be a positive integer.`);
876
- process.exit(1);
877
- }
878
- const { PlaybookRunner } = await import("./runner-CVnjpqc-.mjs");
879
- await PlaybookRunner.run(definition, {
880
- caseId: opts.case,
881
- from: fromN,
882
- dryRun: opts.dryRun,
883
- params: resolvedParams
884
- });
885
- } catch (err) {
886
- console.error(err.message);
887
- process.exit(1);
888
- }
889
- })).addCommand(new Command("list").description("List available playbooks (built-in and user-defined)").action(async () => {
890
- try {
891
- const { listPlaybooks } = await import("./resolver-2jXNtWQO.mjs");
892
- const playbooks = await listPlaybooks();
893
- if (playbooks.length === 0) {
894
- console.log("No playbooks found.");
895
- return;
896
- }
897
- for (const p of playbooks) console.log(` ${p.name.padEnd(20)} [${p.source}]`);
898
- } catch (err) {
899
- console.error(err.message);
900
- process.exit(1);
901
- }
902
- })).addCommand(new Command("show").description("Show steps for a playbook without executing").argument("<name>", "Playbook name").action(async (name) => {
903
- try {
904
- const { resolvePlaybookContent } = await import("./resolver-2jXNtWQO.mjs");
905
- const { PlaybookParser } = await import("./parser-CJfMsOl6.mjs");
906
- const markdown = await resolvePlaybookContent(name);
907
- const definition = PlaybookParser.parse(markdown, {});
908
- console.log(`Playbook: ${definition.name} v${definition.version}`);
909
- console.log(`${definition.description}\n`);
910
- console.log(`Parameters:`);
911
- for (const p of definition.params) {
912
- const req = p.required ? "(required)" : `(optional, default: ${p.default ?? "none"})`;
913
- console.log(` ${p.name}: ${p.type} ${req}`);
914
- }
915
- console.log(`\nSteps:`);
916
- for (const step of definition.steps) console.log(` ${step.index}. ${step.label} → tool: ${step.tool}`);
917
- } catch (err) {
918
- console.error(err.message);
919
- process.exit(1);
920
- }
921
- }));
922
- program.command("viz").description("Generate money flow visualization").argument("[case-id]", "Case ID to visualize").option("--data <file>", "Raw transaction JSON file for ad-hoc visualization").option("-p, --port <number>", "Server port", "4321").action(async (caseId, opts) => {
666
+ program.command("viz").description("Generate a workspace visualization").argument("[source-id]", "Workspace graph report ID to render").option("--data <file>", "Raw transaction JSON file for ad-hoc visualization").option("-p, --port <number>", "Server port", "4321").action(async (sourceId, opts) => {
923
667
  try {
924
- if (!caseId && !opts.data) {
925
- console.error("Provide either a case ID or --data <file.json>");
668
+ if (!sourceId && !opts.data) {
669
+ console.error("Provide either a visualization source ID or --data <file.json>");
926
670
  process.exit(1);
927
671
  }
928
- const { generateVisualization } = await import("./viz-DB5XFG1z.mjs").then((n) => n.n);
672
+ const { generateVisualization } = await import("./viz-D8umSF-t.mjs").then((n) => n.n);
929
673
  const result = await generateVisualization({
930
- caseId,
674
+ sourceId,
931
675
  dataFile: opts.data
932
676
  });
933
- const { startServer } = await import("./server-BXLX2j_A.mjs").then((n) => n.t);
677
+ const { startServer } = await import("./server-BK4bfOiv.mjs").then((n) => n.t);
934
678
  const port = parseInt(opts.port, 10);
935
679
  startServer(port);
936
680
  const url = `http://127.0.0.1:${port}/viz/${result.vizId}`;