chain-insights 0.3.3 → 0.3.5
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 +20 -7
- package/dist/canvas-Cn-maEIh.mjs +203 -0
- package/dist/canvas-Cn-maEIh.mjs.map +1 -0
- package/dist/canvas-p-oKCMjc.cjs +251 -0
- package/dist/cases-Bz_9XKEw.cjs +19 -0
- package/dist/cases-TVcAifxu.mjs +16 -0
- package/dist/cases-TVcAifxu.mjs.map +1 -0
- package/dist/cli.cjs +74 -28
- package/dist/cli.mjs +74 -28
- package/dist/cli.mjs.map +1 -1
- package/dist/{data-extractor-DZUJu1Bz.mjs → data-extractor-B4nHw1wZ.mjs} +2 -2
- package/dist/{data-extractor-DZUJu1Bz.mjs.map → data-extractor-B4nHw1wZ.mjs.map} +1 -1
- package/dist/{data-extractor-Cavd7wHk.cjs → data-extractor-DS4rzy3M.cjs} +1 -1
- package/dist/{export-BqTCO9lP.mjs → export-CBhcJuZ6.mjs} +8 -205
- package/dist/export-CBhcJuZ6.mjs.map +1 -0
- package/dist/{export-DsXgtCwO.cjs → export-D4v4-6F4.cjs} +16 -214
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{init-DLBL_nVG.mjs → init-CKQ6F07J.mjs} +22 -5
- package/dist/init-CKQ6F07J.mjs.map +1 -0
- package/dist/{init-zqbd7i-_.cjs → init-Dhw8F23z.cjs} +21 -4
- package/dist/mcp-proxy.cjs +20 -20
- package/dist/mcp-proxy.mjs +20 -20
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-wJoAFDFa.mjs → public-tools-CyUZEz9B.mjs} +3 -3
- package/dist/{public-tools-wJoAFDFa.mjs.map → public-tools-CyUZEz9B.mjs.map} +1 -1
- package/dist/{public-tools-BvMb3H2P.cjs → public-tools-xfVNz9NE.cjs} +2 -2
- package/dist/{runner-BhZ4lnF1.cjs → runner-CVo41fjz.cjs} +2 -2
- package/dist/{runner-DIJSbkjc.mjs → runner-DWuSy1Se.mjs} +3 -3
- package/dist/{runner-DIJSbkjc.mjs.map → runner-DWuSy1Se.mjs.map} +1 -1
- package/dist/{selector-CF2o5gxN.mjs → selector-BvXM9jbe.mjs} +2 -2
- package/dist/{selector-CF2o5gxN.mjs.map → selector-BvXM9jbe.mjs.map} +1 -1
- package/dist/{selector-DfAMZEC9.cjs → selector-Dps_ZFxq.cjs} +1 -1
- package/dist/{store-CTtqQtaE.mjs → store-C2B_AssI.mjs} +2 -2
- package/dist/{store-CTtqQtaE.mjs.map → store-C2B_AssI.mjs.map} +1 -1
- package/dist/{store-CqPfs47P.cjs → store-CQhU8dz8.cjs} +0 -18
- package/dist/vault-B2y78Ypu.cjs +560 -0
- package/dist/vault-z35Dohdq.mjs +560 -0
- package/dist/vault-z35Dohdq.mjs.map +1 -0
- package/dist/{viz-Dqp3C5kb.cjs → viz-D1620cBX.cjs} +3 -3
- package/dist/{viz-5y24S5X1.mjs → viz-DB5XFG1z.mjs} +4 -4
- package/dist/{viz-5y24S5X1.mjs.map → viz-DB5XFG1z.mjs.map} +1 -1
- package/docs/graph-tools.md +15 -6
- package/docs/investigation-workspaces.md +38 -9
- package/docs/knowledge-exports.md +204 -0
- package/docs/mcp-proxy.md +15 -3
- package/docs/obsidian-vault.md +130 -0
- package/package.json +1 -1
- package/skills/chain-insights-developer-experience/SKILL.md +2 -2
- package/skills/chain-insights-investigation/SKILL.md +1 -1
- package/dist/cases-Cp9DUbEV.mjs +0 -6
- package/dist/cases-sTY5aXav.cjs +0 -9
- package/dist/export-BqTCO9lP.mjs.map +0 -1
- package/dist/init-DLBL_nVG.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viz-
|
|
1
|
+
{"version":3,"file":"viz-DB5XFG1z.mjs","names":[],"sources":["../src/viz/index.ts"],"sourcesContent":["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":";;;;;;AASA,eAAsB,sBAAsB,MAGK;CAC/C,IAAI;CAEJ,IAAI,KAAK,UAAU;EACjB,MAAM,UAAU,MAAM,SAAS,KAAK,UAAU,OAAO;EACrD,IAAI;EACJ,IAAI;GACF,SAAS,KAAK,MAAM,OAAO;EAC7B,QAAQ;GACN,MAAM,IAAI,MAAM,kIAAkI;EACpJ;EACA,MAAM,EAAE,yBAAyB,MAAM,OAAO,iCAAA,MAAA,MAAA,EAAA,CAAA;EAC9C,UAAU,qBAAqB,MAAM;CACvC,OAAO,IAAI,KAAK,QAAQ;EACtB,MAAM,EAAE,yBAAyB,MAAM,OAAO,iCAAA,MAAA,MAAA,EAAA,CAAA;EAC9C,MAAM,YAAY,MAAM,qBAAqB,KAAK,MAAM;EACxD,IAAI,UAAU,MAAM,WAAW,GAC7B,MAAM,IAAI,MAAM,6LAA6L;EAE/M,UAAU;CACZ,OACE,MAAM,IAAI,MAAM,gDAAgD;CAGlE,MAAM,OAAO,cAAc,OAA8C;CAEzE,MAAM,QAAQ,KAAK,SACf,GAAG,KAAK,OAAO,GAAG,KAAK,IAAI,MAC3B,SAAS,KAAK,IAAI;CAStB,OAAO;EAAE;EAAO,UAAA,MAFO,aAAa,OADvB,aAAa,MAJZ,KAAK,SAAS,SACxB,GAAG,KAAK,SAAS,OAAO,iBACxB,sBAG0C,GAAG,KAAK,MAAM;CAEnC;AAC3B"}
|
package/docs/graph-tools.md
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
This document covers the graph-facing tools and the result contracts that
|
|
4
4
|
agents should rely on during investigations.
|
|
5
5
|
|
|
6
|
+
Chain Insights workspaces are Obsidian-compatible vaults. Graph tools write
|
|
7
|
+
case evidence and report pointers into the workspace; use live vault refresh for
|
|
8
|
+
local review and exports only for sharing, partner handoff, LLM Wiki ingestion,
|
|
9
|
+
or archive.
|
|
10
|
+
|
|
6
11
|
## Graph MCP Surface
|
|
7
12
|
|
|
8
13
|
The GraphRAG MCP public graph surface is intentionally small:
|
|
@@ -319,12 +324,16 @@ HTML, CSV or table files, and Markdown reports.
|
|
|
319
324
|
Evidence Markdown should be a provenance record with key facts and pointers.
|
|
320
325
|
Large JSON belongs under workspace report directories, not inline in evidence.
|
|
321
326
|
|
|
322
|
-
After a case has useful evidence, run
|
|
323
|
-
`cia case
|
|
324
|
-
|
|
325
|
-
`
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
After a case has useful evidence, run
|
|
328
|
+
`cia case vault refresh <case-id> --force` for live Obsidian-compatible notes in
|
|
329
|
+
the workspace. When you need to share, hand off to a partner, ingest into LLM
|
|
330
|
+
Wiki, or archive a checkpoint, run `cia case evidence verify <case-id>` and
|
|
331
|
+
`cia case export <case-id> --target obsidian-llmwiki --mode private`. The export
|
|
332
|
+
uses `manifest.chain-insights.json`, `graph.chain-insights.json`,
|
|
333
|
+
`Graph.canvas`, Markdown evidence/entity notes, and agent prompt files as views
|
|
334
|
+
over the canonical case evidence and report artifacts. Vault workflow guidance
|
|
335
|
+
lives in [Obsidian vault workflow](obsidian-vault.md); export and ingestion
|
|
336
|
+
steps live in [Knowledge exports](knowledge-exports.md).
|
|
328
337
|
|
|
329
338
|
## Runtime Schema Capture
|
|
330
339
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Investigation Workspaces
|
|
2
2
|
|
|
3
|
-
An investigation workspace is a normal directory
|
|
4
|
-
case state, evidence, reports, graph
|
|
5
|
-
schema notes. Agents and humans
|
|
6
|
-
editor.
|
|
3
|
+
An investigation workspace is a normal directory and Obsidian-compatible vault
|
|
4
|
+
that contains Chain Insights case state, live notes, evidence, reports, graph
|
|
5
|
+
payloads, imports, templates, and runtime schema notes. Agents and humans
|
|
6
|
+
should be able to inspect it with a normal editor.
|
|
7
7
|
|
|
8
8
|
## Initialize
|
|
9
9
|
|
|
@@ -24,12 +24,16 @@ workspace files before it creates directories or writes any files.
|
|
|
24
24
|
.chain-insights/schema/ Runtime graph schema captures
|
|
25
25
|
.chain-insights/runtime/ Workspace-local runtime state and debug logs
|
|
26
26
|
.chain-insights/runtime-skill/ Workspace-specific agent schema notes
|
|
27
|
+
.obsidian/ Obsidian vault settings
|
|
28
|
+
Canvases/ Obsidian Canvas graph review files
|
|
29
|
+
Entities/ Live entity notes and indexes
|
|
30
|
+
Evidence/ Live evidence notes and indexes
|
|
27
31
|
cases/ Case exports and notes
|
|
28
32
|
imports/ External investigation inputs
|
|
29
33
|
reports/ Final or interim analyst reports
|
|
30
34
|
reports/graphs/ Canonical graph JSON for visualization
|
|
31
35
|
reports/tables/ Compact tabular extracts
|
|
32
|
-
published/ Obsidian,
|
|
36
|
+
published/ Shareable Obsidian, LLM Wiki, and agent export bundles
|
|
33
37
|
templates/ Reusable case and report templates
|
|
34
38
|
```
|
|
35
39
|
|
|
@@ -78,6 +82,20 @@ cia case close <case-id>
|
|
|
78
82
|
Use `cia case show <case-id>` to show saved case context, evidence count,
|
|
79
83
|
dossier summaries, and recent session notes.
|
|
80
84
|
|
|
85
|
+
Refresh live Obsidian vault notes after case evidence, dossiers, or sessions
|
|
86
|
+
change:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
cia case vault refresh <case-id>
|
|
90
|
+
cia case vault refresh <case-id> --force
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Open the workspace in Obsidian:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
cia obsidian open .
|
|
97
|
+
```
|
|
98
|
+
|
|
81
99
|
## Evidence
|
|
82
100
|
|
|
83
101
|
Append evidence:
|
|
@@ -105,16 +123,27 @@ original query parameters, compact facts, and workspace-local file pointers.
|
|
|
105
123
|
|
|
106
124
|
## Knowledge Exports
|
|
107
125
|
|
|
108
|
-
|
|
126
|
+
For normal local review, use the live vault notes:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
cia case vault refresh <case-id> --force
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Export a verified case bundle only when sharing, handing off to a partner,
|
|
133
|
+
ingesting into LLM Wiki, or archiving a checkpoint:
|
|
109
134
|
|
|
110
135
|
```bash
|
|
111
136
|
cia case evidence verify <case-id>
|
|
112
137
|
cia case export <case-id> --target obsidian-llmwiki --mode private
|
|
113
138
|
```
|
|
114
139
|
|
|
115
|
-
Open `published/<case-slug>/` as
|
|
116
|
-
Codex, Claude Code, or ChatGPT as the case context. The case manifest
|
|
117
|
-
the source of truth.
|
|
140
|
+
Open `published/<case-slug>/` as a portable Obsidian vault, or give it to LLM
|
|
141
|
+
Wiki, Codex, Claude Code, or ChatGPT as the case context. The case manifest
|
|
142
|
+
remains the source of truth for the export.
|
|
143
|
+
|
|
144
|
+
For install commands, Obsidian opening steps, LLM Wiki ingestion, and agent
|
|
145
|
+
prompts, see [Obsidian vault workflow](obsidian-vault.md) and
|
|
146
|
+
[Knowledge exports](knowledge-exports.md).
|
|
118
147
|
|
|
119
148
|
## Dossiers
|
|
120
149
|
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Knowledge Exports
|
|
2
|
+
|
|
3
|
+
Chain Insights workspaces are Obsidian-compatible vaults. Normal local
|
|
4
|
+
investigation work happens in the workspace; exports are portable, redacted,
|
|
5
|
+
and shareable bundles for partner handoff, LLM Wiki ingestion, and archive.
|
|
6
|
+
|
|
7
|
+
Chain Insights can export a verified case as a local knowledge bundle for LLM
|
|
8
|
+
Wiki, Codex, Claude Code, ChatGPT, Obsidian, and any agent that can read a
|
|
9
|
+
folder of Markdown and JSON files.
|
|
10
|
+
|
|
11
|
+
The export is local-first. Chain Insights writes files under your initialized
|
|
12
|
+
workspace and does not upload the case anywhere by itself.
|
|
13
|
+
|
|
14
|
+
## Install Viewers And Agent Tools
|
|
15
|
+
|
|
16
|
+
Install Chain Insights:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g chain-insights
|
|
20
|
+
cia --version
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Install Obsidian when you want a human vault UI:
|
|
24
|
+
|
|
25
|
+
- Windows and macOS: download the installer from
|
|
26
|
+
<https://obsidian.md/download> and open it.
|
|
27
|
+
- Linux AppImage: download the AppImage from <https://obsidian.md/download>,
|
|
28
|
+
then run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
chmod u+x Obsidian-<version>.AppImage
|
|
32
|
+
./Obsidian-<version>.AppImage --no-sandbox
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- Linux Flatpak:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
flatpak install flathub md.obsidian.Obsidian
|
|
39
|
+
flatpak run md.obsidian.Obsidian
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Install LLM Wiki when you want an agent-native research wiki on top of the
|
|
43
|
+
exported case:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Claude Code
|
|
47
|
+
claude plugin install wiki@llm-wiki
|
|
48
|
+
|
|
49
|
+
# OpenAI Codex
|
|
50
|
+
codex plugin marketplace add nvk/llm-wiki
|
|
51
|
+
# Then open /plugins, enable "LLM Wiki", and use @wiki.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For any other LLM agent, install the portable LLM Wiki instructions in the
|
|
55
|
+
project where the agent runs:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
curl -sL https://raw.githubusercontent.com/nvk/llm-wiki/master/AGENTS.md \
|
|
59
|
+
> AGENTS.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
You do not need Obsidian or LLM Wiki to read an export. The bundle is plain
|
|
63
|
+
Markdown plus JSON, so editors and agents can consume it directly.
|
|
64
|
+
|
|
65
|
+
## Create A Case Export
|
|
66
|
+
|
|
67
|
+
Start from a Chain Insights workspace:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
mkdir -p ~/work/chain-insights-investigations
|
|
71
|
+
cd ~/work/chain-insights-investigations
|
|
72
|
+
cia init .
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Open a case and run investigation tools with `--case` so evidence, reports, and
|
|
76
|
+
graph artifacts stay attached to the case:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cia case open "Exchange deposit clustering" \
|
|
80
|
+
--tags aml,bittensor \
|
|
81
|
+
--description "Trace source funds into exchange entities"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
After evidence exists, verify and export:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cia case evidence verify <case-id>
|
|
88
|
+
cia case export <case-id> --target obsidian-llmwiki --mode private
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The default output path is:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
published/<case-slug>/
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Inspect the bundle:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
find published/<case-slug> -maxdepth 3 -type f | sort
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Output Files
|
|
104
|
+
|
|
105
|
+
The export writes:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
README.md
|
|
109
|
+
Case.md
|
|
110
|
+
Agent Console.md
|
|
111
|
+
LLMWIKI.md
|
|
112
|
+
llms.txt
|
|
113
|
+
manifest.chain-insights.json
|
|
114
|
+
graph.chain-insights.json
|
|
115
|
+
Graph.canvas
|
|
116
|
+
Entities/
|
|
117
|
+
Evidence/
|
|
118
|
+
Prompts/Codex.md
|
|
119
|
+
Prompts/Claude-Code.md
|
|
120
|
+
Prompts/ChatGPT.md
|
|
121
|
+
Sources/evidence-manifest.md
|
|
122
|
+
Sources/reports-index.md
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use `manifest.chain-insights.json` as the machine-readable source of truth for
|
|
126
|
+
case ID, export mode, verification status, file hashes, and redaction warnings.
|
|
127
|
+
Use `graph.chain-insights.json` for node/edge import, and `Graph.canvas` for an
|
|
128
|
+
Obsidian Canvas view of the same graph.
|
|
129
|
+
|
|
130
|
+
## Open In Obsidian
|
|
131
|
+
|
|
132
|
+
1. Open Obsidian.
|
|
133
|
+
2. Open the vault switcher.
|
|
134
|
+
3. Select **Open folder as vault**.
|
|
135
|
+
4. Choose `published/<case-slug>/`.
|
|
136
|
+
5. Start with `Case.md`, `Agent Console.md`, and `Graph.canvas`.
|
|
137
|
+
|
|
138
|
+
Obsidian treats the export directory as a normal vault. It may create a local
|
|
139
|
+
`.obsidian/` settings folder inside the export; that file is viewer state, not
|
|
140
|
+
Chain Insights evidence.
|
|
141
|
+
|
|
142
|
+
## Load Into LLM Wiki
|
|
143
|
+
|
|
144
|
+
LLM Wiki stores topic wikis under `~/wiki/` by default and can ingest local
|
|
145
|
+
files. A simple Chain Insights flow is:
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
/wiki init chain-insights-cases
|
|
149
|
+
/wiki:ingest /absolute/path/to/published/<case-slug>/LLMWIKI.md --wiki chain-insights-cases
|
|
150
|
+
/wiki:ingest /absolute/path/to/published/<case-slug>/Case.md --wiki chain-insights-cases
|
|
151
|
+
/wiki:ingest /absolute/path/to/published/<case-slug>/Agent\ Console.md --wiki chain-insights-cases
|
|
152
|
+
/wiki:ingest /absolute/path/to/published/<case-slug>/manifest.chain-insights.json --wiki chain-insights-cases
|
|
153
|
+
/wiki:ingest /absolute/path/to/published/<case-slug>/graph.chain-insights.json --wiki chain-insights-cases
|
|
154
|
+
/wiki:compile --wiki chain-insights-cases
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For larger cases, drop selected exported files into the topic wiki inbox and run
|
|
158
|
+
LLM Wiki ingestion from there. Keep the original `published/<case-slug>/`
|
|
159
|
+
directory unchanged so the Chain Insights manifest hashes remain auditable.
|
|
160
|
+
|
|
161
|
+
## Use With Codex, Claude Code, Or ChatGPT
|
|
162
|
+
|
|
163
|
+
For coding-agent sessions, open or attach `published/<case-slug>/` and start
|
|
164
|
+
with this instruction:
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
Read Case.md, Agent Console.md, LLMWIKI.md, manifest.chain-insights.json, and
|
|
168
|
+
graph.chain-insights.json first. Treat manifest.chain-insights.json and
|
|
169
|
+
Sources/evidence-manifest.md as canonical. Preserve full blockchain addresses
|
|
170
|
+
exactly unless this is a public redacted export. Use Chain Insights MCP tools
|
|
171
|
+
for fresh graph facts when available.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
For ChatGPT, upload the smallest useful subset first:
|
|
175
|
+
|
|
176
|
+
- `Case.md`
|
|
177
|
+
- `Agent Console.md`
|
|
178
|
+
- `manifest.chain-insights.json`
|
|
179
|
+
- `graph.chain-insights.json`
|
|
180
|
+
- selected files from `Evidence/` and `Entities/`
|
|
181
|
+
|
|
182
|
+
## Export Modes
|
|
183
|
+
|
|
184
|
+
Use the least-shareable mode that fits the audience:
|
|
185
|
+
|
|
186
|
+
| Mode | Use it for |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `private` | Local analyst work. May include full addresses and sensitive notes. |
|
|
189
|
+
| `partner` | Controlled partner handoff. Review the bundle before sharing. |
|
|
190
|
+
| `public` | Demos and public writeups. Addresses are aliased and redaction metadata is written to the manifest. |
|
|
191
|
+
|
|
192
|
+
Do not share `private` exports outside the investigation team.
|
|
193
|
+
|
|
194
|
+
## MCP Tool Flow
|
|
195
|
+
|
|
196
|
+
Agents connected through the Chain Insights MCP proxy can create the same
|
|
197
|
+
bundle without shelling out:
|
|
198
|
+
|
|
199
|
+
1. Call `case_verify_evidence` for the case.
|
|
200
|
+
2. Call `case_export` with `case_id`, `target=obsidian-llmwiki`, and
|
|
201
|
+
`mode=private`.
|
|
202
|
+
3. Open the returned `outputDir`, then read `Agent Console.md` first.
|
|
203
|
+
|
|
204
|
+
The MCP tool and CLI command write the same file format.
|
package/docs/mcp-proxy.md
CHANGED
|
@@ -4,6 +4,11 @@ The Chain Insights stdio proxy lets AI agents consume Chain Insights tools as
|
|
|
4
4
|
an MCP server. It connects to the configured GraphRAG MCP endpoint and adds
|
|
5
5
|
local wallet, case, evidence, and graph-report behavior.
|
|
6
6
|
|
|
7
|
+
Chain Insights workspaces are Obsidian-compatible vaults. Use live vault
|
|
8
|
+
refresh and local notes for normal review; use `case_export` only when an agent
|
|
9
|
+
needs a handoff bundle for sharing, partner review, LLM Wiki ingestion, or
|
|
10
|
+
archive.
|
|
11
|
+
|
|
7
12
|
## Basic Configuration
|
|
8
13
|
|
|
9
14
|
Use this MCP server configuration:
|
|
@@ -88,14 +93,21 @@ The proxy:
|
|
|
88
93
|
| `case_resume` | Load case context, evidence count, dossiers, and latest session |
|
|
89
94
|
| `case_add_evidence` | Append a report or note to the evidence manifest |
|
|
90
95
|
| `case_verify_evidence` | Verify saved evidence integrity |
|
|
91
|
-
| `case_export` | Export a case for Obsidian,
|
|
96
|
+
| `case_export` | Export a case for Obsidian, LLM Wiki, Codex, Claude Code, and ChatGPT |
|
|
92
97
|
| `case_update_dossier` | Add a durable finding to an address/entity dossier |
|
|
93
98
|
| `case_start_session` | Start an investigation session |
|
|
94
99
|
| `case_end_session` | End a session with findings and next steps |
|
|
95
100
|
|
|
101
|
+
For normal local review, refresh live vault notes from the CLI with
|
|
102
|
+
`cia case vault refresh <case-id> --force` and inspect the same workspace in
|
|
103
|
+
Obsidian, VS Code, Codex, or Claude Code.
|
|
104
|
+
|
|
96
105
|
`case_export` writes the same local bundle as `cia case export`. Use it after
|
|
97
|
-
`case_verify_evidence` when an agent needs Obsidian,
|
|
98
|
-
Code, or ChatGPT-ready files.
|
|
106
|
+
`case_verify_evidence` when an agent needs Obsidian, LLM Wiki, Codex, Claude
|
|
107
|
+
Code, or ChatGPT-ready files for handoff. See
|
|
108
|
+
[Obsidian vault workflow](obsidian-vault.md) and
|
|
109
|
+
[Knowledge exports](knowledge-exports.md) for vault opening, sharing, and LLM
|
|
110
|
+
Wiki ingestion steps.
|
|
99
111
|
|
|
100
112
|
Remote graph tools are discovered from the configured GraphRAG MCP endpoint. The
|
|
101
113
|
expected primitive graph tools are `usage_status`, `graph_query`, and
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Obsidian Vault Workflow
|
|
2
|
+
|
|
3
|
+
Chain Insights workspaces are Obsidian-compatible investigation vaults.
|
|
4
|
+
Obsidian is the first-class human review UI, while Chain Insights case files,
|
|
5
|
+
evidence manifests, reports, graph JSON, and exports remain plain local files.
|
|
6
|
+
Obsidian plugin is not required.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
Install Chain Insights:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g chain-insights
|
|
14
|
+
cia --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Install Obsidian when you want the vault UI:
|
|
18
|
+
|
|
19
|
+
- Windows and macOS: download the installer from
|
|
20
|
+
<https://obsidian.md/download> and open it.
|
|
21
|
+
- Linux AppImage: download the AppImage from <https://obsidian.md/download>,
|
|
22
|
+
then run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
chmod u+x Obsidian-<version>.AppImage
|
|
26
|
+
./Obsidian-<version>.AppImage --no-sandbox
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- Linux Flatpak:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
flatpak install flathub md.obsidian.Obsidian
|
|
33
|
+
flatpak run md.obsidian.Obsidian
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Create An Investigation Vault
|
|
37
|
+
|
|
38
|
+
Create a normal local folder and initialize it:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mkdir -p ~/work/chain-insights-investigations
|
|
42
|
+
cd ~/work/chain-insights-investigations
|
|
43
|
+
cia init .
|
|
44
|
+
cia obsidian open .
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If the app association is not available, open Obsidian manually, choose
|
|
48
|
+
**Open folder as vault**, and select the initialized workspace folder.
|
|
49
|
+
|
|
50
|
+
The workspace includes Chain Insights runtime metadata, case state, generated
|
|
51
|
+
vault notes, Obsidian settings, evidence, entities, canvases, reports, and
|
|
52
|
+
published handoff bundles. The files remain readable in any editor.
|
|
53
|
+
|
|
54
|
+
## Work A Case
|
|
55
|
+
|
|
56
|
+
Open a case:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cia case open "Exchange deposit clustering" \
|
|
60
|
+
--tags aml,bittensor \
|
|
61
|
+
--description "Trace source funds into exchange entities"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Run investigation tools with `--case` so evidence and report pointers attach to
|
|
65
|
+
the case:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
cia mcp trace-victim-funds \
|
|
69
|
+
--network bittensor \
|
|
70
|
+
--victim-addresses 5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
|
|
71
|
+
--case 1
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Refresh live vault notes whenever case evidence, dossiers, or sessions change:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cia case vault refresh 1 --force
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use Obsidian for review of `Home.md`, case notes, entity notes, evidence notes,
|
|
81
|
+
and canvases. Use the CLI or MCP tools for live GraphRAG MCP reads and evidence
|
|
82
|
+
capture.
|
|
83
|
+
|
|
84
|
+
## VS Code And Agent Operator Setup
|
|
85
|
+
|
|
86
|
+
Open the same folder in VS Code, Codex, Claude Code, or another local agent.
|
|
87
|
+
The agent and Obsidian should operate over the same workspace path:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
code .
|
|
91
|
+
codex
|
|
92
|
+
claude
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Agents should read the workspace notes, case files, evidence manifests, graph
|
|
96
|
+
JSON, and report pointers before running new tools. When a result changes the
|
|
97
|
+
case, refresh the vault notes again:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cia case vault refresh 1 --force
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## LLM Wiki Overlay
|
|
104
|
+
|
|
105
|
+
LLM Wiki is an optional overlay for agent-native retrieval and compiled case
|
|
106
|
+
knowledge. Use the live workspace for normal local work, then export a selected
|
|
107
|
+
case when you need an ingestion bundle:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cia case evidence verify 1
|
|
111
|
+
cia case export 1 --target obsidian-llmwiki --mode private
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The exported `published/<case-slug>/LLMWIKI.md`,
|
|
115
|
+
`manifest.chain-insights.json`, `graph.chain-insights.json`, and Markdown notes
|
|
116
|
+
can be ingested into an LLM Wiki topic while the original workspace remains the
|
|
117
|
+
auditable source.
|
|
118
|
+
|
|
119
|
+
## Sharing
|
|
120
|
+
|
|
121
|
+
Normal local investigation work stays in the initialized vault. Export only
|
|
122
|
+
when you need sharing, partner handoff, LLM Wiki ingestion, or archive.
|
|
123
|
+
|
|
124
|
+
Use `private` only inside the investigation team. Use `partner` for controlled
|
|
125
|
+
handoff after review. Use `public` for demos and public writeups where address
|
|
126
|
+
aliasing and redaction metadata are required.
|
|
127
|
+
|
|
128
|
+
Do not share `.chain-insights/` runtime state, local credentials, wallet
|
|
129
|
+
material, debug tokens, or unreviewed private notes. Review generated bundles
|
|
130
|
+
before sending them outside the team.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chain-insights",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "AML investigation CLI and MCP proxy for blockchain risk, fund-flow tracing, case reports, and GraphRAG MCP access",
|
|
5
5
|
"homepage": "https://chain-insights.ai",
|
|
6
6
|
"repository": {
|
|
@@ -96,14 +96,14 @@ Every new AML tool needs:
|
|
|
96
96
|
`cia case export` and MCP `case_export` are developer-experience surfaces, not
|
|
97
97
|
new graph primitives. Keep CLI and MCP adapters thin over the shared export
|
|
98
98
|
service, and keep `manifest.chain-insights.json`, case evidence, graph reports,
|
|
99
|
-
and source report artifacts canonical. Obsidian,
|
|
99
|
+
and source report artifacts canonical. Obsidian, LLM Wiki, Codex, Claude Code,
|
|
100
100
|
and ChatGPT files are generated views.
|
|
101
101
|
|
|
102
102
|
Case export changes need:
|
|
103
103
|
|
|
104
104
|
- CLI help and MCP tool descriptions.
|
|
105
105
|
- Private, partner, and public redaction behavior.
|
|
106
|
-
- Obsidian/
|
|
106
|
+
- Obsidian/LLM Wiki/agent artifact tests.
|
|
107
107
|
- Clean-workspace dogfood that opens a case, verifies evidence, exports it,
|
|
108
108
|
and inspects `manifest.chain-insights.json`, `Graph.canvas`, and
|
|
109
109
|
`graph.chain-insights.json`.
|
|
@@ -278,7 +278,7 @@ cia case export <case> --target obsidian-llmwiki --mode private
|
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
Treat `manifest.chain-insights.json`, case evidence, and report artifacts as
|
|
281
|
-
canonical. Treat Obsidian,
|
|
281
|
+
canonical. Treat Obsidian, LLM Wiki, Codex, Claude Code, and ChatGPT notes as
|
|
282
282
|
views over that evidence.
|
|
283
283
|
|
|
284
284
|
## When Asked "What Next?"
|
package/dist/cases-Cp9DUbEV.mjs
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import "./frontmatter-D0ccQnUM.mjs";
|
|
2
|
-
import { t as DossierStore } from "./dossier-Bjpcbcxa.mjs";
|
|
3
|
-
import { t as CaseStore } from "./store-CTtqQtaE.mjs";
|
|
4
|
-
import { t as EvidenceStore } from "./evidence-D96PTzOQ.mjs";
|
|
5
|
-
import { t as SessionStore } from "./session-DROyhebe.mjs";
|
|
6
|
-
export { CaseStore, DossierStore, EvidenceStore, SessionStore };
|
package/dist/cases-sTY5aXav.cjs
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
require("./frontmatter-Dvqa5HX6.cjs");
|
|
2
|
-
const require_dossier = require("./dossier-BXy57V4-.cjs");
|
|
3
|
-
const require_store = require("./store-CqPfs47P.cjs");
|
|
4
|
-
const require_evidence = require("./evidence-CvEesemA.cjs");
|
|
5
|
-
const require_session = require("./session-BT7VpbAd.cjs");
|
|
6
|
-
exports.CaseStore = require_store.CaseStore;
|
|
7
|
-
exports.DossierStore = require_dossier.DossierStore;
|
|
8
|
-
exports.EvidenceStore = require_evidence.EvidenceStore;
|
|
9
|
-
exports.SessionStore = require_session.SessionStore;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"export-BqTCO9lP.mjs","names":[],"sources":["../src/export/paths.ts","../src/export/schema.ts","../src/export/canvas.ts","../src/export/graph.ts","../src/export/markdown.ts","../src/export/redaction.ts","../src/export/index.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { lstat, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\n\nexport function safeSlug(value: string): string {\n const slug = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 80)\n return slug || 'case-export'\n}\n\nexport function safeFilename(value: string): string {\n const parsed = path.parse(value)\n const name = safeSlug(parsed.name)\n const ext = parsed.ext.toLowerCase().replace(/[^.a-z0-9]/g, '')\n return `${name}${ext || '.md'}`\n}\n\nexport function assertInsideDirectory(root: string, candidate: string): void {\n const resolvedRoot = path.resolve(root)\n const resolvedCandidate = path.resolve(candidate)\n const relative = path.relative(resolvedRoot, resolvedCandidate)\n if (relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))) return\n throw new Error(`Refusing to write outside export directory: ${candidate}`)\n}\n\nexport async function assertNoSymlink(filePath: string): Promise<void> {\n try {\n const stat = await lstat(filePath)\n if (stat.isSymbolicLink()) throw new Error(`Refusing to write through symlink: ${filePath}`)\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return\n throw err\n }\n}\n\nexport async function writePrivateFile(\n root: string,\n relativePath: string,\n content: string,\n): Promise<{ path: string; sha256: string; bytes: number }> {\n const filePath = path.join(root, relativePath)\n assertInsideDirectory(root, filePath)\n await mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 })\n await assertNoSymlink(filePath)\n await writeFile(filePath, content, { mode: 0o600 })\n const bytes = Buffer.byteLength(content, 'utf8')\n const sha256 = createHash('sha256').update(content).digest('hex')\n return { path: relativePath, sha256, bytes }\n}\n","import * as z from 'zod'\n\nexport const CaseExportTargetSchema = z.enum(['obsidian-llmwiki'])\nexport type CaseExportTarget = z.infer<typeof CaseExportTargetSchema>\n\nexport const CaseExportModeSchema = z.enum(['private', 'partner', 'public'])\nexport type CaseExportMode = z.infer<typeof CaseExportModeSchema>\n\nconst caseIdRegex = /^\\d{8}_\\d{3}_[a-z0-9][a-z0-9-]*$/\n\nexport const CaseExportOptionsSchema = z.object({\n caseId: z.string().regex(caseIdRegex),\n target: CaseExportTargetSchema.default('obsidian-llmwiki'),\n mode: CaseExportModeSchema.default('private'),\n outputDir: z.string().optional(),\n})\nexport type CaseExportOptions = z.infer<typeof CaseExportOptionsSchema>\n\nexport const ExportedFileSchema = z.object({\n path: z.string().min(1),\n sha256: z.string().regex(/^[a-f0-9]{64}$/),\n bytes: z.number().int().nonnegative(),\n})\nexport type ExportedFile = z.infer<typeof ExportedFileSchema>\n\nexport const CaseExportManifestSchema = z.object({\n schema: z.literal('chain-insights.case_export.v1'),\n case_id: z.string().regex(caseIdRegex),\n case_name: z.string().min(1),\n exported_at: z.string().datetime(),\n mode: CaseExportModeSchema,\n target: CaseExportTargetSchema,\n source_workspace: z.string().min(1),\n verification: z.object({\n evidence_manifest_verified: z.boolean(),\n verified_at: z.string().datetime(),\n evidence_count: z.number().int().nonnegative(),\n }),\n files: z.array(ExportedFileSchema),\n redactions: z.array(z.string()),\n warnings: z.array(z.string()),\n})\nexport type CaseExportManifest = z.infer<typeof CaseExportManifestSchema>\n\nexport const JsonCanvasNodeSchema = z.object({\n id: z.string().min(1),\n type: z.enum(['text', 'file', 'link', 'group']),\n x: z.number(),\n y: z.number(),\n width: z.number().positive(),\n height: z.number().positive(),\n text: z.string().optional(),\n file: z.string().optional(),\n url: z.string().optional(),\n label: z.string().optional(),\n color: z.string().optional(),\n})\n\nexport const JsonCanvasEdgeSchema = z.object({\n id: z.string().min(1),\n fromNode: z.string().min(1),\n toNode: z.string().min(1),\n fromSide: z.enum(['top', 'right', 'bottom', 'left']).optional(),\n toSide: z.enum(['top', 'right', 'bottom', 'left']).optional(),\n toEnd: z.enum(['none', 'arrow']).optional(),\n label: z.string().optional(),\n color: z.string().optional(),\n})\n\nexport const JsonCanvasSchema = z.object({\n nodes: z.array(JsonCanvasNodeSchema),\n edges: z.array(JsonCanvasEdgeSchema),\n})\nexport type JsonCanvas = z.infer<typeof JsonCanvasSchema>\n\nexport type CaseExportResult = {\n manifestPath: string\n outputDir: string\n fileCount: number\n warnings: string[]\n nextFile: string\n}\n","import { safeFilename } from './paths.js'\nimport { JsonCanvasSchema, type JsonCanvas } from './schema.js'\n\nfunction roleColor(roles: string[]): string {\n if (roles.includes('victim')) return '1'\n if (roles.includes('suspect') || roles.includes('scam_candidate')) return '2'\n if (roles.includes('deposit')) return '3'\n if (roles.includes('exchange')) return '5'\n if (roles.includes('service')) return '6'\n return '#808080'\n}\n\nfunction nodeRoles(node: Record<string, unknown>): string[] {\n return Array.isArray(node['roles']) ? node['roles'].map(String) : []\n}\n\nfunction nodeLabel(node: Record<string, unknown>): string {\n return String(node['address'] ?? node['id'] ?? 'unknown')\n}\n\nexport function graphNodeId(node: Record<string, unknown>, index: number): string {\n return String(node['id'] ?? node['address'] ?? `node-${index + 1}`)\n}\n\nexport function entityNotePath(entityId: string): string {\n return `Entities/${safeFilename(entityId)}`\n}\n\nexport function graphToCanvas(graph: { nodes: Record<string, unknown>[]; edges: Record<string, unknown>[] }): JsonCanvas {\n const nodes = [\n {\n id: 'case',\n type: 'file' as const,\n file: 'Case.md',\n x: 0,\n y: 0,\n width: 360,\n height: 120,\n color: '4',\n },\n ]\n\n const nodeIdMap = new Map<string, string>()\n graph.nodes.forEach((node, index) => {\n const rawId = graphNodeId(node, index)\n const canvasId = `entity-${index + 1}`\n nodeIdMap.set(rawId, canvasId)\n nodes.push({\n id: canvasId,\n type: 'file' as const,\n file: entityNotePath(rawId),\n x: 420 + (index % 4) * 340,\n y: Math.floor(index / 4) * 220,\n width: 300,\n height: 120,\n color: roleColor(nodeRoles(node)),\n })\n })\n\n const edges = graph.edges.flatMap((edge, index) => {\n const from = nodeIdMap.get(String(edge['source'] ?? ''))\n const to = nodeIdMap.get(String(edge['target'] ?? ''))\n if (!from || !to) return []\n return [{\n id: `edge-${index + 1}`,\n fromNode: from,\n toNode: to,\n fromSide: 'right' as const,\n toSide: 'left' as const,\n toEnd: 'arrow' as const,\n label: String(edge['edge_type'] ?? 'related_to'),\n }]\n })\n\n for (const [index, node] of graph.nodes.entries()) {\n edges.push({\n id: `case-link-${index + 1}`,\n fromNode: 'case',\n toNode: `entity-${index + 1}`,\n fromSide: 'right' as const,\n toSide: 'left' as const,\n toEnd: 'arrow' as const,\n label: nodeLabel(node),\n })\n }\n\n return JsonCanvasSchema.parse({ nodes, edges })\n}\n","import { readFile, readdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport { normalizeGraphPayload, type NormalizedGraphPayload } from '../viz/graph-normalizer.js'\nimport { extractGraphFromCase } from '../viz/data-extractor.js'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction nodeId(node: Record<string, unknown>): string {\n return String(node['id'] ?? node['address'] ?? '')\n}\n\nfunction edgeKey(edge: Record<string, unknown>): string {\n return `${String(edge['source'] ?? '')}->${String(edge['target'] ?? '')}:${String(edge['edge_type'] ?? 'related_to')}`\n}\n\nfunction mergeGraphs(graphs: NormalizedGraphPayload[]): NormalizedGraphPayload {\n const nodes = new Map<string, Record<string, unknown>>()\n const edges = new Map<string, Record<string, unknown>>()\n for (const graph of graphs) {\n for (const rawNode of graph.nodes) {\n const id = nodeId(rawNode)\n if (id) nodes.set(id, { ...(nodes.get(id) ?? {}), ...rawNode, id })\n }\n for (const rawEdge of graph.edges) {\n if (typeof rawEdge['source'] !== 'string' || typeof rawEdge['target'] !== 'string') continue\n edges.set(edgeKey(rawEdge), { ...(edges.get(edgeKey(rawEdge)) ?? {}), ...rawEdge })\n }\n }\n return {\n schema: 'chain-insights.graph.v1',\n nodes: [...nodes.values()],\n edges: [...edges.values()],\n flows: graphs.flatMap(graph => graph.flows),\n edge_anchors: graphs.flatMap(graph => graph.edge_anchors),\n metadata: {\n source: 'case-export',\n graph_count: graphs.length,\n generated_at: new Date().toISOString(),\n },\n }\n}\n\nexport async function loadCaseExportGraph(caseId: string): Promise<NormalizedGraphPayload> {\n const paths = workspaceOutputPaths()\n const files = await readdir(paths.reportGraphsRoot).catch(() => [])\n const graphs: NormalizedGraphPayload[] = []\n for (const file of files.filter(name => name.endsWith('.graph.json')).sort()) {\n const parsed = JSON.parse(await readFile(path.join(paths.reportGraphsRoot, file), 'utf8')) as unknown\n if (isRecord(parsed) && parsed['schema'] === 'chain-insights.graph.v1') {\n graphs.push(normalizeGraphPayload(parsed))\n }\n }\n if (graphs.length > 0) return mergeGraphs(graphs)\n\n const fallback = await extractGraphFromCase(caseId)\n return normalizeGraphPayload({\n schema: 'chain-insights.graph.v1',\n nodes: fallback.nodes,\n edges: fallback.edges,\n flows: [],\n edge_anchors: [],\n metadata: fallback.metadata,\n })\n}\n","import type { CaseExportMode } from './schema.js'\n\nexport type MarkdownCase = {\n id: string\n name: string\n status: string\n tags: string[]\n description?: string\n}\n\nexport function frontmatter(values: Record<string, unknown>): string {\n const lines = ['---']\n for (const [key, value] of Object.entries(values)) {\n if (Array.isArray(value)) {\n lines.push(`${key}:`)\n for (const item of value) lines.push(` - ${JSON.stringify(String(item))}`)\n } else {\n lines.push(`${key}: ${JSON.stringify(value)}`)\n }\n }\n lines.push('---', '')\n return lines.join('\\n')\n}\n\nexport function renderReadme(caseName: string): string {\n return [\n `# ${caseName} Export`,\n '',\n 'Open this directory as an Obsidian vault or give it to an LLMWiki-style knowledge workflow.',\n '',\n 'Start with:',\n '',\n '- `Case.md`',\n '- `Agent Console.md`',\n '- `LLMWIKI.md`',\n '- `graph.chain-insights.json` when present',\n '',\n ].join('\\n')\n}\n\nexport function renderCaseMarkdown(input: {\n caseInfo: MarkdownCase\n mode: CaseExportMode\n evidenceVerified: boolean\n evidenceCount: number\n entityCount: number\n}): string {\n return frontmatter({\n type: 'chain-insights-case',\n case_id: input.caseInfo.id,\n status: input.caseInfo.status,\n tags: input.caseInfo.tags,\n contains_sensitive_data: input.mode !== 'public',\n }) + [\n `# ${input.caseInfo.name}`,\n '',\n `Case ID: \\`${input.caseInfo.id}\\``,\n `Status: ${input.caseInfo.status}`,\n `Evidence manifest: ${input.evidenceVerified ? 'verified' : 'failed'}`,\n `Evidence files: ${input.evidenceCount}`,\n `Entities: ${input.entityCount}`,\n '',\n '## Summary',\n '',\n input.caseInfo.description || 'No description recorded.',\n '',\n '## Start Here',\n '',\n '- [[Agent Console]]',\n '- [[LLMWIKI]]',\n '- [[Sources/evidence-manifest]]',\n '',\n ].join('\\n')\n}\n\nexport function renderAgentConsole(caseName: string): string {\n return [\n '# Agent Console',\n '',\n `Case: [[Case|${caseName}]]`,\n '',\n '## Reading Order',\n '',\n '1. [[Case]]',\n '2. [[LLMWIKI]]',\n '3. `graph.chain-insights.json`',\n '4. [[Sources/evidence-manifest]]',\n '5. Entity and evidence notes linked from the case.',\n '',\n '## Agent Prompts',\n '',\n '- [[Prompts/Codex]]',\n '- [[Prompts/Claude-Code]]',\n '- [[Prompts/ChatGPT]]',\n '',\n '## Rules',\n '',\n '- Treat Chain Insights case evidence and manifests as canonical.',\n '- Use Chain Insights tools for fresh graph facts.',\n '- Preserve full blockchain addresses exactly unless this is a public redacted export.',\n '',\n ].join('\\n')\n}\n\nexport function renderLlmWiki(): string {\n return [\n '# LLMWiki Entry',\n '',\n 'This directory is a Chain Insights case export.',\n '',\n 'Canonical machine files:',\n '',\n '- `manifest.chain-insights.json`',\n '- `graph.chain-insights.json`',\n '- `Graph.canvas`',\n '',\n 'Human and agent notes:',\n '',\n '- `Case.md`',\n '- `Agent Console.md`',\n '- `Entities/`',\n '- `Evidence/`',\n '- `Prompts/`',\n '',\n ].join('\\n')\n}\n\nexport function renderLlmsTxt(): string {\n return [\n '# Chain Insights Case Export',\n '',\n 'Read these files first:',\n '- Case.md',\n '- Agent Console.md',\n '- graph.chain-insights.json',\n '- Entities/',\n '- Evidence/',\n '',\n 'Source of truth:',\n '- manifest.chain-insights.json',\n '- Sources/evidence-manifest.md',\n '',\n ].join('\\n')\n}\n\nexport function renderPrompt(agentName: string): string {\n return [\n `# ${agentName} Case Prompt`,\n '',\n 'You are reading a Chain Insights case export.',\n '',\n 'Treat `manifest.chain-insights.json`, `Sources/evidence-manifest.md`, and original case evidence as canonical.',\n 'Use generated prose for orientation, not as a replacement for evidence.',\n 'Use Chain Insights MCP tools for fresh graph facts when available.',\n '',\n ].join('\\n')\n}\n","import type { CaseExportMode } from './schema.js'\n\nconst EVM_ADDRESS_RE = /\\b0x[a-fA-F0-9]{40}\\b/g\nconst SUBSTRATE_ADDRESS_RE = /\\b5[1-9A-HJ-NP-Za-km-z]{20,64}\\b/g\nconst SECRET_PATTERNS = [\n /\\bci_test_[A-Za-z0-9_-]+\\b/g,\n /\\b(?:privateKey|walletPrivateKey|secret|token|authorization)\\s*[:=]\\s*[\"']?[^\"'\\s]+/gi,\n /\\b0x[a-fA-F0-9]{64}\\b/g,\n]\n\nexport type Redactor = {\n text(input: string): string\n value<T>(input: T): T\n aliasFor(address: string): string\n redactions(): string[]\n}\n\nexport function createRedactor(mode: CaseExportMode): Redactor {\n const aliases = new Map<string, string>()\n const redactions = new Set<string>()\n\n function aliasFor(address: string): string {\n const existing = aliases.get(address)\n if (existing) return existing\n const alias = `addr_${String(aliases.size + 1).padStart(3, '0')}`\n aliases.set(address, alias)\n redactions.add(`aliased:${alias}`)\n return alias\n }\n\n function redactSecrets(input: string): string {\n let output = input\n for (const pattern of SECRET_PATTERNS) {\n output = output.replace(pattern, () => {\n redactions.add('secret')\n return '[redacted-secret]'\n })\n }\n return output\n }\n\n function text(input: string): string {\n let output = redactSecrets(input)\n if (mode === 'public') {\n output = output.replace(SUBSTRATE_ADDRESS_RE, match => aliasFor(match))\n output = output.replace(EVM_ADDRESS_RE, match => aliasFor(match))\n }\n return output\n }\n\n function value<T>(input: T): T {\n if (typeof input === 'string') return text(input) as T\n if (Array.isArray(input)) return input.map(item => value(item)) as T\n if (input && typeof input === 'object') {\n return Object.fromEntries(\n Object.entries(input).map(([key, entry]) => [key, value(entry)]),\n ) as T\n }\n return input\n }\n\n return {\n text,\n value,\n aliasFor,\n redactions: () => [...redactions].sort(),\n }\n}\n","import { mkdir, readFile, readdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport { CaseStore, DossierStore, EvidenceStore, parseFrontmatter } from '../cases/index.js'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\nimport { entityNotePath, graphNodeId, graphToCanvas } from './canvas.js'\nimport { loadCaseExportGraph } from './graph.js'\nimport { renderAgentConsole, renderCaseMarkdown, renderLlmsTxt, renderLlmWiki, renderPrompt, renderReadme } from './markdown.js'\nimport { safeFilename, safeSlug, writePrivateFile } from './paths.js'\nimport { createRedactor } from './redaction.js'\nimport { CaseExportManifestSchema, CaseExportOptionsSchema, type CaseExportOptions, type CaseExportResult, type ExportedFile } from './schema.js'\n\ntype EvidenceDoc = {\n id: string\n filename: string\n source: string\n timestamp: string\n body: string\n}\n\nasync function readEvidence(caseId: string): Promise<EvidenceDoc[]> {\n const paths = workspaceOutputPaths()\n const dir = path.join(paths.casesRoot, caseId, 'evidence')\n const files = await readdir(dir).catch(() => [])\n const docs: EvidenceDoc[] = []\n for (const filename of files.filter(file => file.endsWith('.md')).sort()) {\n const raw = await readFile(path.join(dir, filename), 'utf8')\n const { frontmatter, body } = parseFrontmatter(raw)\n docs.push({\n id: frontmatter['id'] || filename.replace(/\\.md$/, ''),\n filename,\n source: frontmatter['source'] || 'unknown',\n timestamp: frontmatter['timestamp'] || '',\n body,\n })\n }\n return docs\n}\n\nasync function writeFiles(root: string, entries: Array<[string, string]>): Promise<ExportedFile[]> {\n const written: ExportedFile[] = []\n for (const [relativePath, content] of entries) {\n written.push(await writePrivateFile(root, relativePath, content))\n }\n return written\n}\n\nexport async function exportCase(rawOptions: CaseExportOptions): Promise<CaseExportResult> {\n const options = CaseExportOptionsSchema.parse(rawOptions)\n const workspace = workspaceOutputPaths()\n const caseInfo = await CaseStore.get(options.caseId)\n const redactor = createRedactor(options.mode)\n const evidenceVerification = await EvidenceStore.verifyManifest(options.caseId)\n const evidenceDocs = await readEvidence(options.caseId)\n const dossiers = await DossierStore.listSummaries(options.caseId)\n const graph = redactor.value(await loadCaseExportGraph(options.caseId))\n const canvas = graphToCanvas(graph)\n const outputRoot = path.resolve(options.outputDir ?? path.join(workspace.root, 'published', safeSlug(caseInfo.name)))\n await mkdir(outputRoot, { recursive: true, mode: 0o700 })\n\n const entries: Array<[string, string]> = [\n ['README.md', renderReadme(redactor.text(caseInfo.name))],\n ['Case.md', renderCaseMarkdown({\n caseInfo: {\n id: caseInfo.id,\n name: redactor.text(caseInfo.name),\n status: caseInfo.status,\n tags: caseInfo.tags,\n description: redactor.text(caseInfo.description),\n },\n mode: options.mode,\n evidenceVerified: evidenceVerification.ok,\n evidenceCount: evidenceVerification.count,\n entityCount: dossiers.length,\n })],\n ['LLMWIKI.md', renderLlmWiki()],\n ['llms.txt', renderLlmsTxt()],\n ['Agent Console.md', renderAgentConsole(redactor.text(caseInfo.name))],\n ['Prompts/Codex.md', renderPrompt('Codex')],\n ['Prompts/Claude-Code.md', renderPrompt('Claude Code')],\n ['Prompts/ChatGPT.md', renderPrompt('ChatGPT')],\n ['Sources/evidence-manifest.md', `# Evidence Manifest\\n\\nVerified: ${evidenceVerification.ok ? 'yes' : 'no'}\\nEvidence files: ${evidenceVerification.count}\\n`],\n ['Sources/reports-index.md', '# Reports Index\\n\\nGraph and report artifacts are exported when present.\\n'],\n ['graph.chain-insights.json', JSON.stringify(graph, null, 2) + '\\n'],\n ['Graph.canvas', JSON.stringify(canvas, null, 2) + '\\n'],\n ]\n\n for (const evidence of evidenceDocs) {\n entries.push([\n path.join('Evidence', safeFilename(evidence.id)),\n redactor.text([\n `# Evidence: ${evidence.source}`,\n '',\n `Source file: \\`${evidence.filename}\\``,\n `Captured: ${evidence.timestamp || 'unknown'}`,\n '',\n evidence.body,\n '',\n ].join('\\n')),\n ])\n }\n\n const entityPaths = new Set<string>()\n for (const dossier of dossiers) {\n const entityId = options.mode === 'public' ? redactor.aliasFor(dossier.address) : dossier.address\n const entityPath = path.join('Entities', safeFilename(entityId))\n entityPaths.add(entityPath)\n entries.push([\n entityPath,\n redactor.text([\n `# Entity: ${entityId}`,\n '',\n `Type: ${dossier.type}`,\n `First seen: ${dossier.firstSeen || 'unknown'}`,\n `Last seen: ${dossier.lastSeen || 'unknown'}`,\n `Risk tags: ${dossier.riskTags || 'none'}`,\n '',\n ].join('\\n')),\n ])\n }\n\n for (const [index, node] of graph.nodes.entries()) {\n const entityId = graphNodeId(node, index)\n const entityPath = entityNotePath(entityId)\n if (entityPaths.has(entityPath)) continue\n entityPaths.add(entityPath)\n entries.push([\n entityPath,\n [\n `# Entity: ${entityId}`,\n '',\n `Address: ${String(node['address'] ?? entityId)}`,\n `Roles: ${Array.isArray(node['roles']) ? node['roles'].map(String).join(', ') || 'none' : 'none'}`,\n `Node type: ${String(node['node_type'] ?? 'unknown')}`,\n '',\n '## Graph Links',\n '',\n '- [[Graph.canvas]]',\n '',\n ].join('\\n'),\n ])\n }\n\n const files = await writeFiles(outputRoot, entries)\n const exportedAt = new Date().toISOString()\n const manifest = CaseExportManifestSchema.parse({\n schema: 'chain-insights.case_export.v1',\n case_id: caseInfo.id,\n case_name: redactor.text(caseInfo.name),\n exported_at: exportedAt,\n mode: options.mode,\n target: options.target,\n source_workspace: workspace.root,\n verification: {\n evidence_manifest_verified: evidenceVerification.ok,\n verified_at: exportedAt,\n evidence_count: evidenceVerification.count,\n },\n files,\n redactions: redactor.redactions(),\n warnings: evidenceVerification.ok ? [] : [`Evidence manifest failed: ${(evidenceVerification.tampered ?? []).join(', ')}`],\n })\n const manifestFile = await writePrivateFile(outputRoot, 'manifest.chain-insights.json', JSON.stringify(manifest, null, 2) + '\\n')\n\n return {\n manifestPath: path.join(outputRoot, manifestFile.path),\n outputDir: outputRoot,\n fileCount: files.length + 1,\n warnings: manifest.warnings,\n nextFile: 'Agent Console.md',\n }\n}\n"],"mappings":"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"}
|