chain-insights 0.2.31 → 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.
- package/README.md +40 -14
- package/dist/cases-Cp9DUbEV.mjs +6 -0
- package/dist/{cases-c0iV-XLI.cjs → cases-sTY5aXav.cjs} +3 -3
- package/dist/cli.cjs +122 -66
- package/dist/cli.mjs +122 -66
- package/dist/cli.mjs.map +1 -1
- package/dist/{viz-Da9YWN_I.cjs → data-extractor-Cavd7wHk.cjs} +11 -34
- package/dist/{viz-DkJyqlUu.mjs → data-extractor-DZUJu1Bz.mjs} +3 -32
- package/dist/data-extractor-DZUJu1Bz.mjs.map +1 -0
- package/dist/{dossier-Br62hCG7.cjs → dossier-BXy57V4-.cjs} +13 -1
- package/dist/{dossier-Bl0NkJKC.mjs → dossier-Bjpcbcxa.mjs} +4 -2
- package/dist/{dossier-Bl0NkJKC.mjs.map → dossier-Bjpcbcxa.mjs.map} +1 -1
- package/dist/export-BqTCO9lP.mjs +591 -0
- package/dist/export-BqTCO9lP.mjs.map +1 -0
- package/dist/export-DsXgtCwO.cjs +592 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{init-DBC9Ml33.mjs → init-DLBL_nVG.mjs} +27 -1
- package/dist/{init-DBC9Ml33.mjs.map → init-DLBL_nVG.mjs.map} +1 -1
- package/dist/{init-CFaUWgjK.cjs → init-zqbd7i-_.cjs} +26 -0
- package/dist/mcp-proxy.cjs +215 -77
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +215 -77
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-BwguvIsf.cjs → public-tools-BvMb3H2P.cjs} +701 -1479
- package/dist/{public-tools-DoRNhMn9.mjs → public-tools-wJoAFDFa.mjs} +700 -1479
- package/dist/public-tools-wJoAFDFa.mjs.map +1 -0
- package/dist/{resolver-D7VBb0uB.mjs → resolver-2jXNtWQO.mjs} +12 -29
- package/dist/resolver-2jXNtWQO.mjs.map +1 -0
- package/dist/{resolver-BUU7ZgW-.cjs → resolver-CZdQwKvh.cjs} +11 -28
- package/dist/{runner-BCDeBYsR.cjs → runner-BhZ4lnF1.cjs} +2 -2
- package/dist/{runner-CTFK0Qcg.mjs → runner-DIJSbkjc.mjs} +3 -3
- package/dist/{runner-CTFK0Qcg.mjs.map → runner-DIJSbkjc.mjs.map} +1 -1
- package/dist/{selector-CTUiQrzI.mjs → selector-CF2o5gxN.mjs} +2 -2
- package/dist/{selector-CTUiQrzI.mjs.map → selector-CF2o5gxN.mjs.map} +1 -1
- package/dist/{selector-DBS2jYH4.cjs → selector-DfAMZEC9.cjs} +1 -1
- package/dist/{session-DwyikazY.cjs → session-BT7VpbAd.cjs} +13 -1
- package/dist/{session-Bha3zFrx.mjs → session-DROyhebe.mjs} +4 -2
- package/dist/{session-Bha3zFrx.mjs.map → session-DROyhebe.mjs.map} +1 -1
- package/dist/{store-BT2SCcQr.mjs → store-CTtqQtaE.mjs} +10 -4
- package/dist/{store-BT2SCcQr.mjs.map → store-CTtqQtaE.mjs.map} +1 -1
- package/dist/{store-DogLawSj.cjs → store-CqPfs47P.cjs} +37 -7
- package/dist/{tool-visibility-BHRFLXuU.mjs → tool-visibility-BpyZHRBi.mjs} +4 -2
- package/dist/tool-visibility-BpyZHRBi.mjs.map +1 -0
- package/dist/{tool-visibility-iAVQV3t0.cjs → tool-visibility-Buq7YdUZ.cjs} +3 -1
- package/dist/viz-5y24S5X1.mjs +35 -0
- package/dist/viz-5y24S5X1.mjs.map +1 -0
- package/dist/viz-Dqp3C5kb.cjs +44 -0
- package/docs/contributing.md +3 -2
- package/docs/graph-tools.md +129 -118
- package/docs/investigation-workspaces.md +14 -0
- package/docs/mcp-proxy.md +20 -2
- package/package.json +1 -1
- package/skills/chain-insights-bittensor-cypher/SKILL.md +24 -0
- package/skills/chain-insights-cypher/SKILL.md +16 -1
- package/skills/chain-insights-cypher/references/memgraph-examples.md +193 -0
- package/skills/chain-insights-developer-experience/SKILL.md +26 -6
- package/skills/chain-insights-investigation/SKILL.md +64 -48
- package/skills/chain-insights-trace-funds/SKILL.md +80 -197
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +1 -1
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +4 -4
- package/dist/cases-qjPtbnUd.mjs +0 -6
- package/dist/public-tools-DoRNhMn9.mjs.map +0 -1
- package/dist/resolver-D7VBb0uB.mjs.map +0 -1
- package/dist/tool-visibility-BHRFLXuU.mjs.map +0 -1
- package/dist/viz-DkJyqlUu.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolver-D7VBb0uB.mjs","names":[],"sources":["../src/playbooks/builtins.ts","../src/playbooks/resolver.ts"],"sourcesContent":["// Built-in playbook definitions tied to the current GraphRAG public MCP tools.\n// Source of truth inspected in rbmk/repos/ml/graphrag/src/mcp_server/tools.\n\nexport const KNOWN_GRAPHRAG_PUBLIC_TOOLS = [\n 'address_risk',\n 'scam_topology',\n 'stake_insights',\n 'track_funds',\n 'graph_query',\n 'graph_query_batch',\n] as const\n\nexport const TRACE_FUNDS_PLAYBOOK = `---\nname: trace-funds\ndescription: Trace stolen funds from a victim address to exchange deposits using Chain Insights GraphRAG\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Screen Victim Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Trace Funds To Exchanges\n\n\\`\\`\\`tool\ntrack_funds\n\\`\\`\\`\n\n\\`\\`\\`params\ntrusted_addresses: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const RISK_CHECK_PLAYBOOK = `---\nname: risk-check\ndescription: Screen an address for Chain Insights risk, behavior, counterparties, exchange connections, and AML patterns\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Address Risk\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const ENTITY_PROFILE_PLAYBOOK = `---\nname: entity-profile\ndescription: Build an entity profile from Chain Insights address identity, metrics, risk, counterparties, and labels\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Entity Profile\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const SCAM_TOPOLOGY_PLAYBOOK = `---\nname: scam-topology\ndescription: Build laundering topology and scam labels from a victim incident\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: incident_timestamp_ms\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n - name: activity_policy\n type: string\n required: false\n default: node_relative_only\n---\n\n## Step 1: Screen Known Scam Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Build Scam Topology\n\nThe victim-only traversal is outward from victim/source funds. The\nprimary traversal is a node-relative novelty wave: each new node expands only\nonce, repeated targets remain as non-expanding convergence context, and\ndownstream edges must be active at or after the current node's wave-arrival\ntimestamp. Set activity_policy to global_incident_only to filter every wave\nagainst incident_timestamp_ms instead. Exchange terminal safety stops exchange\nendpoints, and label candidates are reviewable, not automatic writes.\nContract summary: victim-only traversal is outward from victim/source funds;\nnode-relative novelty wave by default; global_incident_only is available;\nexchange terminal safety; scam_labels are ML-ready flags.\n\n\\`\\`\\`tool\nscam_topology\n\\`\\`\\`\n\n\\`\\`\\`params\nvictim_address: {{address}}\nincident_timestamp_ms: {{incident_timestamp_ms}}\nnetwork: {{network}}\nactivity_policy: {{activity_policy}}\n\\`\\`\\`\n`\n\nexport const BUILTIN_PLAYBOOKS: Record<string, string> = {\n 'trace-funds': TRACE_FUNDS_PLAYBOOK,\n 'scam-topology': SCAM_TOPOLOGY_PLAYBOOK,\n 'risk-check': RISK_CHECK_PLAYBOOK,\n 'entity-profile': ENTITY_PROFILE_PLAYBOOK,\n}\n","import path from 'node:path'\nimport { access, readFile, readdir } from 'node:fs/promises'\nimport os from 'node:os'\nimport { BUILTIN_PLAYBOOKS } from './builtins.js'\n\nfunction userDir(): string {\n return path.join(os.homedir(), '.chain-insights', 'playbooks')\n}\n\n/**\n * Resolve a playbook name to its absolute file path.\n * Checks user directory (~/.chain-insights/playbooks/) first, then built-in directory.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n *\n * @deprecated Prefer resolvePlaybookContent() which returns markdown directly.\n */\nexport async function resolvePlaybook(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n const userPath = path.join(userDir(), `${safeName}.md`)\n\n try {\n await access(userPath)\n return userPath\n } catch {\n // Fall through to built-in check\n }\n\n // Check built-in playbooks map (T-05-01: no filesystem path exposure for built-ins)\n if (safeName in BUILTIN_PLAYBOOKS) {\n // Return a sentinel path for legacy callers — content comes from BUILTIN_PLAYBOOKS\n return `builtin:${safeName}`\n }\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * Resolve a playbook name to its markdown content.\n * Checks user directory (~/.chain-insights/playbooks/<name>.md) first,\n * then falls back to the built-in BUILTIN_PLAYBOOKS map.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n */\nexport async function resolvePlaybookContent(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n // 1. Check user directory first (user playbooks override built-ins)\n const userPath = path.join(userDir(), `${safeName}.md`)\n try {\n return await readFile(userPath, 'utf8')\n } catch {\n // Not in user dir — fall through\n }\n\n // 2. Fall back to built-in map\n const builtin = BUILTIN_PLAYBOOKS[safeName]\n if (builtin !== undefined) return builtin\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * List all available playbooks — user dir first (overrides), then built-ins.\n * Returns array of { name, source } objects.\n */\nexport async function listPlaybooks(): Promise<Array<{ name: string; source: 'builtin' | 'user' }>> {\n const result: Array<{ name: string; source: 'builtin' | 'user' }> = []\n const seen = new Set<string>()\n\n // User playbooks first\n try {\n const userFiles = await readdir(userDir())\n for (const file of userFiles) {\n if (!file.endsWith('.md')) continue\n const name = file.slice(0, -3) // remove .md\n seen.add(name)\n result.push({ name, source: 'user' })\n }\n } catch {\n // User dir doesn't exist — that's fine\n }\n\n // Built-in playbooks from the embedded map (not filesystem)\n for (const name of Object.keys(BUILTIN_PLAYBOOKS)) {\n if (seen.has(name)) continue // user override takes precedence\n result.push({ name, source: 'builtin' })\n }\n\n return result\n}\n"],"mappings":";;;AA8JA,MAAa,oBAA4C;CACvD,eAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,iBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,cAAkB;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;AACpB;;;AC9JA,SAAS,UAAkB;CACzB,OAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,WAAW;AAC/D;;;;;;;AAwCA,eAAsB,uBAAuB,MAA+B;CAE1E,MAAM,WAAW,KAAK,QAAQ,iBAAiB,EAAE;CACjD,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,0BAA0B,MAAM;CAG/D,MAAM,WAAW,KAAK,KAAK,QAAQ,GAAG,GAAG,SAAS,IAAI;CACtD,IAAI;EACF,OAAO,MAAM,SAAS,UAAU,MAAM;CACxC,QAAQ,CAER;CAGA,MAAM,UAAU,kBAAkB;CAClC,IAAI,YAAY,KAAA,GAAW,OAAO;CAElC,MAAM,IAAI,MACR,wBAAwB,SAAS,oEACnC;AACF;;;;;AAMA,eAAsB,gBAA8E;CAClG,MAAM,SAA8D,CAAC;CACrE,MAAM,uBAAO,IAAI,IAAY;CAG7B,IAAI;EACF,MAAM,YAAY,MAAM,QAAQ,QAAQ,CAAC;EACzC,KAAK,MAAM,QAAQ,WAAW;GAC5B,IAAI,CAAC,KAAK,SAAS,KAAK,GAAG;GAC3B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;GAC7B,KAAK,IAAI,IAAI;GACb,OAAO,KAAK;IAAE;IAAM,QAAQ;GAAO,CAAC;EACtC;CACF,QAAQ,CAER;CAGA,KAAK,MAAM,QAAQ,OAAO,KAAK,iBAAiB,GAAG;EACjD,IAAI,KAAK,IAAI,IAAI,GAAG;EACpB,OAAO,KAAK;GAAE;GAAM,QAAQ;EAAU,CAAC;CACzC;CAEA,OAAO;AACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-visibility-BHRFLXuU.mjs","names":[],"sources":["../src/mcp/tool-visibility.ts"],"sourcesContent":["export const HIDDEN_REMOTE_TOOL_NAMES = new Set([\n 'topup',\n 'trace_funds',\n 'money_flows_between_exchanges',\n 'address_connection_risk',\n])\n\nexport function isHiddenRemoteToolName(name: string): boolean {\n return HIDDEN_REMOTE_TOOL_NAMES.has(name)\n}\n\nexport function visibleRemoteTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((tool) => !isHiddenRemoteToolName(tool.name))\n}\n\nexport function assertPublicMcpToolName(name: string): void {\n if (!isHiddenRemoteToolName(name)) return\n const replacement = name === 'trace_funds'\n ? ' Use track_funds instead.'\n : ''\n throw new Error(`MCP tool '${name}' is not exposed by Chain Insights.${replacement}`)\n}\n"],"mappings":";;;;;;;;AAAA,MAAa,2BAA2B,IAAI,IAAI;CAC9C;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,uBAAuB,MAAuB;CAC5D,OAAO,yBAAyB,IAAI,IAAI;AAC1C;AAEA,SAAgB,mBAA+C,OAAiB;CAC9E,OAAO,MAAM,QAAQ,SAAS,CAAC,uBAAuB,KAAK,IAAI,CAAC;AAClE;AAEA,SAAgB,wBAAwB,MAAoB;CAC1D,IAAI,CAAC,uBAAuB,IAAI,GAAG;CAInC,MAAM,IAAI,MAAM,aAAa,KAAK,qCAHd,SAAS,gBACzB,8BACA,IACgF;AACtF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"viz-DkJyqlUu.mjs","names":[],"sources":["../src/viz/graph-model.ts","../src/viz/data-extractor.ts","../src/viz/index.ts"],"sourcesContent":["import * as z from 'zod'\n\nexport const EntityType = z.enum(['eoa', 'contract', 'exchange', 'mixer', 'unknown'])\nexport type EntityType = z.infer<typeof EntityType>\n\nexport const RiskLevel = z.enum(['low', 'medium', 'high', 'critical', 'unknown'])\nexport type RiskLevel = z.infer<typeof RiskLevel>\n\nexport const GraphNode = z.object({\n id: z.string().min(1),\n label: z.string().optional(),\n entityType: EntityType.default('unknown'),\n riskLevel: RiskLevel.default('unknown'),\n totalIn: z.number().default(0),\n totalOut: z.number().default(0),\n txCount: z.number().int().default(0),\n firstSeen: z.string().optional(),\n lastSeen: z.string().optional(),\n})\nexport type GraphNode = z.infer<typeof GraphNode>\n\nexport const GraphEdge = z.object({\n source: z.string().min(1),\n target: z.string().min(1),\n value: z.number(),\n txHash: z.string().optional(),\n blockNumber: z.number().int().optional(),\n timestamp: z.string().optional(),\n})\nexport type GraphEdge = z.infer<typeof GraphEdge>\n\nexport const GraphData = z.object({\n nodes: z.array(GraphNode),\n edges: z.array(GraphEdge),\n metadata: z.object({\n caseId: z.string().optional(),\n title: z.string().default('Money Flow'),\n generatedAt: z.string(),\n truncated: z.boolean().default(false),\n totalNodes: z.number().int().optional(),\n hiddenNodes: z.number().int().optional(),\n }),\n})\nexport type GraphData = z.infer<typeof GraphData>\n\nconst MAX_NODES = 100\n\nexport function truncateGraph(data: GraphData): GraphData {\n if (data.nodes.length <= MAX_NODES) return data\n const sorted = [...data.nodes].sort((a, b) => (b.totalIn + b.totalOut) - (a.totalIn + a.totalOut))\n const kept = sorted.slice(0, MAX_NODES)\n const keptIds = new Set(kept.map(n => n.id))\n const filteredEdges = data.edges.filter(e => keptIds.has(e.source) && keptIds.has(e.target))\n return {\n nodes: kept,\n edges: filteredEdges,\n metadata: {\n ...data.metadata,\n truncated: true,\n totalNodes: data.nodes.length,\n hiddenNodes: data.nodes.length - MAX_NODES,\n },\n }\n}\n","import { readFile, readdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport { GraphData, GraphNode, GraphEdge } from './graph-model.js'\nimport { parseFrontmatter } from '../cases/frontmatter.js'\nimport { activeCasesRoot } from '../workspace/active.js'\n\nfunction caseDir(caseId: string): string {\n if (/[/\\\\]|^\\.\\.?$/.test(caseId)) throw new Error(`Invalid case ID: ${caseId}`)\n return path.join(activeCasesRoot(), caseId)\n}\n\n/**\n * Extracts all items from ```json code blocks in a markdown string.\n * If the parsed value is an array, spreads all items.\n * If the parsed value has 'nodes' and 'edges', wraps it as a single item.\n * Returns empty array if no JSON blocks found or parsing fails.\n */\nexport function parseEvidenceJson(markdown: string): unknown[] {\n const results: unknown[] = []\n const re = /```json\\s*\\n([\\s\\S]*?)```/g\n let match: RegExpExecArray | null\n while ((match = re.exec(markdown)) !== null) {\n const raw = match[1]\n if (!raw) continue\n try {\n const parsed: unknown = JSON.parse(raw)\n if (Array.isArray(parsed)) {\n results.push(...parsed)\n } else if (\n parsed !== null &&\n typeof parsed === 'object' &&\n 'nodes' in (parsed as object) &&\n 'edges' in (parsed as object)\n ) {\n // Full GraphData object embedded in evidence\n results.push(parsed)\n }\n // Ignore other object types\n } catch {\n // Malformed JSON in evidence — skip gracefully (T-04-06: no crash)\n }\n }\n if (results.length > 0) return results\n\n const rawJson = extractEmbeddedJson(markdown)\n if (rawJson) {\n try {\n const parsed: unknown = JSON.parse(rawJson)\n if (Array.isArray(parsed)) return parsed\n if (parsed !== null && typeof parsed === 'object') return [parsed]\n } catch {\n // Ignore non-JSON evidence bodies.\n }\n }\n return results\n}\n\nfunction extractEmbeddedJson(text: string): string | null {\n const trimmed = text.trim()\n const start = [...trimmed]\n .map((char, index) => (char === '{' || char === '[' ? index : -1))\n .find(index => index >= 0)\n if (start === undefined) return null\n return trimmed.slice(start)\n}\n\ntype SimpleTx = { from: string; to: string; value: number; txHash?: string; blockNumber?: number; timestamp?: string }\n\ntype CompactEvidence = {\n schema?: string\n outgoing_flows?: Array<{\n src?: string\n dst?: string\n amount_sum?: number\n tx_count?: number\n first_tx_id?: string\n }>\n}\n\nfunction isSimpleTx(item: unknown): item is SimpleTx {\n return (\n item !== null &&\n typeof item === 'object' &&\n 'from' in (item as object) &&\n 'to' in (item as object) &&\n 'value' in (item as object)\n )\n}\n\nfunction isGraphDataLike(input: unknown): input is { nodes: unknown[]; edges: unknown[] } {\n return (\n input !== null &&\n typeof input === 'object' &&\n Array.isArray((input as Record<string, unknown>)['nodes']) &&\n Array.isArray((input as Record<string, unknown>)['edges'])\n )\n}\n\nfunction compactEvidenceToSimpleTxs(item: unknown): SimpleTx[] {\n const compact = item as CompactEvidence\n if (\n !compact ||\n typeof compact !== 'object' ||\n compact.schema !== 'chain-insights.compact_evidence.v1' ||\n !Array.isArray(compact.outgoing_flows)\n ) {\n return []\n }\n\n return compact.outgoing_flows\n .filter(flow => typeof flow.src === 'string' && typeof flow.dst === 'string' && typeof flow.amount_sum === 'number')\n .map(flow => ({\n from: flow.src!,\n to: flow.dst!,\n value: flow.amount_sum!,\n txHash: flow.first_tx_id,\n }))\n}\n\n/**\n * Converts simple [{from, to, value}] transaction arrays into graph nodes.\n * Computes totalIn, totalOut, txCount per node from edges.\n */\nfunction buildGraphFromSimpleTxs(items: SimpleTx[]): { nodes: GraphNode[]; edges: GraphEdge[] } {\n // Build edges first\n const edges: GraphEdge[] = items.map(tx => GraphEdge.parse({\n source: tx.from,\n target: tx.to,\n value: tx.value,\n txHash: tx.txHash,\n blockNumber: tx.blockNumber,\n timestamp: tx.timestamp,\n }))\n\n // Collect unique addresses\n const addresses = new Set<string>()\n for (const tx of items) {\n addresses.add(tx.from)\n addresses.add(tx.to)\n }\n\n // Compute totals per address\n const totals: Record<string, { totalIn: number; totalOut: number; txCount: number }> = {}\n for (const addr of addresses) {\n totals[addr] = { totalIn: 0, totalOut: 0, txCount: 0 }\n }\n for (const tx of items) {\n const out = totals[tx.from]\n const inp = totals[tx.to]\n if (out) {\n out.totalOut += tx.value\n out.txCount += 1\n }\n if (inp) {\n inp.totalIn += tx.value\n inp.txCount += 1\n }\n }\n\n const nodes: GraphNode[] = [...addresses].map(addr =>\n GraphNode.parse({\n id: addr,\n entityType: 'unknown',\n riskLevel: 'unknown',\n totalIn: totals[addr]?.totalIn ?? 0,\n totalOut: totals[addr]?.totalOut ?? 0,\n txCount: totals[addr]?.txCount ?? 0,\n })\n )\n\n return { nodes, edges }\n}\n\n/**\n * Handles two input formats:\n * 1. Full GraphData object (has nodes + edges) — parse with Zod\n * 2. Array of {from, to, value, ...} transaction objects — auto-derive nodes\n *\n * Throws \"Invalid transaction data\" for any other input.\n */\nexport function extractGraphFromJson(input: unknown): GraphData {\n if (isGraphDataLike(input)) {\n // Full GraphData: parse and validate through Zod\n return GraphData.parse(input)\n }\n\n if (Array.isArray(input)) {\n const simpleTxs: SimpleTx[] = []\n for (const item of input) {\n if (isSimpleTx(item)) {\n simpleTxs.push(item)\n } else {\n simpleTxs.push(...compactEvidenceToSimpleTxs(item))\n }\n }\n const { nodes, edges } = buildGraphFromSimpleTxs(simpleTxs)\n return GraphData.parse({\n nodes,\n edges,\n metadata: {\n title: 'Money Flow',\n generatedAt: new Date().toISOString(),\n },\n })\n }\n\n throw new Error(\n 'Invalid transaction data. The input file must contain a JSON array of transaction objects with `from`, `to`, and `value` fields.'\n )\n}\n\n/**\n * Merges two sets of nodes, deduplicating by id.\n * For duplicate nodes, sums totalIn, totalOut, txCount; keeps earliest firstSeen, latest lastSeen.\n */\nfunction mergeNodes(existing: GraphNode[], incoming: GraphNode[]): GraphNode[] {\n const map = new Map<string, GraphNode>()\n for (const node of existing) {\n map.set(node.id, { ...node })\n }\n for (const node of incoming) {\n const prev = map.get(node.id)\n if (prev) {\n map.set(node.id, {\n ...prev,\n totalIn: prev.totalIn + node.totalIn,\n totalOut: prev.totalOut + node.totalOut,\n txCount: prev.txCount + node.txCount,\n firstSeen: pickEarlier(prev.firstSeen, node.firstSeen),\n lastSeen: pickLater(prev.lastSeen, node.lastSeen),\n })\n } else {\n map.set(node.id, { ...node })\n }\n }\n return [...map.values()]\n}\n\nfunction pickEarlier(a: string | undefined, b: string | undefined): string | undefined {\n if (!a) return b\n if (!b) return a\n return a < b ? a : b\n}\n\nfunction pickLater(a: string | undefined, b: string | undefined): string | undefined {\n if (!a) return b\n if (!b) return a\n return a > b ? a : b\n}\n\n/**\n * Aggregates edges: for duplicate (source, target) pairs, sums value; keeps last txHash/timestamp.\n */\nfunction aggregateEdges(edges: GraphEdge[]): GraphEdge[] {\n const map = new Map<string, GraphEdge>()\n for (const edge of edges) {\n const key = `${edge.source}::${edge.target}`\n const prev = map.get(key)\n if (prev) {\n map.set(key, { ...edge, value: prev.value + edge.value })\n } else {\n map.set(key, { ...edge })\n }\n }\n return [...map.values()]\n}\n\n/**\n * Reads evidence files from a case directory, extracts JSON transaction data\n * from markdown code blocks, enriches nodes with entity types from dossiers,\n * and returns a merged GraphData.\n */\nexport async function extractGraphFromCase(caseId: string): Promise<GraphData> {\n const evidenceDir = path.join(caseDir(caseId), 'evidence')\n\n let files: string[] = []\n try {\n const all = await readdir(evidenceDir)\n files = all.filter(f => f.endsWith('.md'))\n } catch {\n // Evidence directory missing — return empty graph (graceful, not an error)\n return GraphData.parse({\n nodes: [],\n edges: [],\n metadata: {\n caseId,\n title: `${caseId} - Money Flow`,\n generatedAt: new Date().toISOString(),\n },\n })\n }\n\n let allNodes: GraphNode[] = []\n let allEdges: GraphEdge[] = []\n\n for (const file of files) {\n const raw = await readFile(path.join(evidenceDir, file), 'utf-8')\n const { body } = parseFrontmatter(raw)\n const items = parseEvidenceJson(body)\n if (items.length === 0) continue\n\n // Check if items contain a full GraphData object\n const graphDataItems = items.filter(item => isGraphDataLike(item))\n const simpleTxItems = items.flatMap(item => {\n if (isSimpleTx(item)) return [item]\n return compactEvidenceToSimpleTxs(item)\n }) as SimpleTx[]\n\n if (graphDataItems.length > 0) {\n // Merge full GraphData objects\n for (const gd of graphDataItems) {\n try {\n const parsed = GraphData.parse(gd)\n allNodes = mergeNodes(allNodes, parsed.nodes)\n allEdges = [...allEdges, ...parsed.edges]\n } catch {\n // Invalid GraphData in evidence — skip (T-04-06)\n }\n }\n // Also process any simple tx items alongside\n if (simpleTxItems.length > 0) {\n const { nodes, edges } = buildGraphFromSimpleTxs(simpleTxItems)\n allNodes = mergeNodes(allNodes, nodes)\n allEdges = [...allEdges, ...edges]\n }\n } else if (simpleTxItems.length > 0) {\n const { nodes, edges } = buildGraphFromSimpleTxs(simpleTxItems)\n allNodes = mergeNodes(allNodes, nodes)\n allEdges = [...allEdges, ...edges]\n }\n }\n\n // Aggregate edges with same source-target pair\n allEdges = aggregateEdges(allEdges)\n\n // Enrich nodes with entity types from dossiers\n try {\n const { DossierStore } = await import('../cases/index.js')\n const dossiers = await DossierStore.listSummaries(caseId)\n const dossierMap = new Map<string, string>()\n for (const d of dossiers) {\n dossierMap.set(d.address, d.type)\n }\n allNodes = allNodes.map(node => {\n const dossierType = dossierMap.get(node.id)\n if (dossierType && dossierType !== 'unknown') {\n // Validate against EntityType enum — default to 'unknown' if invalid (T-04-07)\n const validTypes = ['eoa', 'contract', 'exchange', 'mixer', 'unknown'] as const\n type ValidType = (typeof validTypes)[number]\n const entityType: ValidType = validTypes.includes(dossierType as ValidType)\n ? (dossierType as ValidType)\n : 'unknown'\n return { ...node, entityType }\n }\n return node\n })\n } catch {\n // Dossier enrichment is best-effort — don't fail extraction if dossiers unavailable\n }\n\n return GraphData.parse({\n nodes: allNodes,\n edges: allEdges,\n metadata: {\n caseId,\n title: `${caseId} - Money Flow`,\n generatedAt: new Date().toISOString(),\n },\n })\n}\n\nexport const DataExtractor = {\n extractGraphFromCase,\n extractGraphFromJson,\n parseEvidenceJson,\n}\n","export { GraphData, GraphNode, GraphEdge, EntityType, RiskLevel, truncateGraph } from './graph-model.js'\nexport type { GraphData as GraphDataType, GraphNode as GraphNodeType, GraphEdge as GraphEdgeType } from './graph-model.js'\nexport { generateHtml, writeVizHtml, transformToGraphHtml } from './html-generator.js'\nexport { DataExtractor, extractGraphFromCase, extractGraphFromJson } from './data-extractor.js'\n\nimport { readFile } from 'node:fs/promises'\nimport { truncateGraph } from './graph-model.js'\nimport { generateHtml, writeVizHtml } from './html-generator.js'\n\nexport async function generateVisualization(opts: {\n caseId?: string\n dataFile?: string\n}): Promise<{ vizId: string; htmlPath: string }> {\n let rawData: unknown\n\n if (opts.dataFile) {\n const content = await readFile(opts.dataFile, 'utf-8')\n let parsed: unknown\n try {\n parsed = JSON.parse(content)\n } catch {\n throw new Error('Invalid transaction data. The input file must contain a JSON array of transaction objects with `from`, `to`, and `value` fields.')\n }\n const { extractGraphFromJson } = await import('./data-extractor.js')\n rawData = extractGraphFromJson(parsed)\n } else if (opts.caseId) {\n const { extractGraphFromCase } = await import('./data-extractor.js')\n const extracted = await extractGraphFromCase(opts.caseId)\n if (extracted.nodes.length === 0) {\n throw new Error('No Transaction Data. This case has no evidence with transaction data. Add evidence using `chain-insights evidence add` or provide a JSON file with `chain-insights viz --data <file.json>`.')\n }\n rawData = extracted\n } else {\n throw new Error('Provide either a case ID or --data <file.json>')\n }\n\n const data = truncateGraph(rawData as Parameters<typeof truncateGraph>[0])\n\n const vizId = opts.caseId\n ? `${opts.caseId}_${Date.now()}`\n : `adhoc_${Date.now()}`\n\n const title = data.metadata.caseId\n ? `${data.metadata.caseId} - Money Flow`\n : 'Ad-hoc Visualization'\n\n const html = generateHtml(data, title)\n const htmlPath = await writeVizHtml(vizId, html, opts.caseId)\n\n return { vizId, htmlPath }\n}\n"],"mappings":"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"}
|