chain-insights 0.2.16
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/LICENSE +21 -0
- package/README.md +165 -0
- package/bin/cli.js +10 -0
- package/bin/install.cjs +252 -0
- package/bin/mcp-proxy.cjs +10 -0
- package/dist/active-BSrxLKwn.mjs +50 -0
- package/dist/active-BSrxLKwn.mjs.map +1 -0
- package/dist/active-Dv7Tu-O4.cjs +68 -0
- package/dist/app-BjjuQM0B.mjs +155 -0
- package/dist/app-BjjuQM0B.mjs.map +1 -0
- package/dist/app-Dq1TdB6p.cjs +161 -0
- package/dist/artifact-server-DoxJ7fCx.cjs +47 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs +48 -0
- package/dist/artifact-server-Dxz5YbuQ.mjs.map +1 -0
- package/dist/assets/bg-pattern.png +0 -0
- package/dist/assets/logo.png +0 -0
- package/dist/call-args-DQA2QcRA.cjs +27 -0
- package/dist/call-args-Lk_wOJxd.mjs +29 -0
- package/dist/call-args-Lk_wOJxd.mjs.map +1 -0
- package/dist/capabilities-CB97WMA5.cjs +83 -0
- package/dist/capabilities-DliMBim-.mjs +84 -0
- package/dist/capabilities-DliMBim-.mjs.map +1 -0
- package/dist/cases-By7INiOa.mjs +6 -0
- package/dist/cases-CDcNU91B.cjs +9 -0
- package/dist/chunk-CZWwpsFl.cjs +43 -0
- package/dist/cli.cjs +752 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +753 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-D4Bq0rp9.mjs +111 -0
- package/dist/client-D4Bq0rp9.mjs.map +1 -0
- package/dist/client-D4fZgIaO.cjs +132 -0
- package/dist/config-Bmdl5hdk.cjs +67 -0
- package/dist/config-BwrBYmiC.mjs +44 -0
- package/dist/config-BwrBYmiC.mjs.map +1 -0
- package/dist/data-extractor-BNGj7ECT.cjs +347 -0
- package/dist/data-extractor-DFzsa5CS.mjs +336 -0
- package/dist/data-extractor-DFzsa5CS.mjs.map +1 -0
- package/dist/dossier-BsroDgD3.mjs +76 -0
- package/dist/dossier-BsroDgD3.mjs.map +1 -0
- package/dist/dossier-DtxREpPm.cjs +76 -0
- package/dist/evidence-BGcdKxuV.cjs +200 -0
- package/dist/evidence-BhvFW-y_.mjs +195 -0
- package/dist/evidence-BhvFW-y_.mjs.map +1 -0
- package/dist/format-Ce1RObVl.mjs +22 -0
- package/dist/format-Ce1RObVl.mjs.map +1 -0
- package/dist/format-DOrPvXEr.cjs +20 -0
- package/dist/frontmatter-D8wWCeOa.mjs +26 -0
- package/dist/frontmatter-D8wWCeOa.mjs.map +1 -0
- package/dist/frontmatter-DgAuai7E.cjs +35 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs +130 -0
- package/dist/graph-normalizer-Cv9yK9Pg.mjs.map +1 -0
- package/dist/graph-normalizer-DeIj6Ses.cjs +133 -0
- package/dist/graph-reports-C4TBjCkM.mjs +63 -0
- package/dist/graph-reports-C4TBjCkM.mjs.map +1 -0
- package/dist/graph-reports-DU05YCei.cjs +64 -0
- package/dist/html-generator-CAv81IWH.cjs +85 -0
- package/dist/html-generator-V6Bp0uRb.mjs +68 -0
- package/dist/html-generator-V6Bp0uRb.mjs.map +1 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +187 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +9 -0
- package/dist/init-BjuFt54X.cjs +232 -0
- package/dist/init-CaOsHTIo.mjs +232 -0
- package/dist/init-CaOsHTIo.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +1257 -0
- package/dist/mcp-proxy.d.cts +12 -0
- package/dist/mcp-proxy.d.cts.map +1 -0
- package/dist/mcp-proxy.d.mts +12 -0
- package/dist/mcp-proxy.d.mts.map +1 -0
- package/dist/mcp-proxy.mjs +1255 -0
- package/dist/mcp-proxy.mjs.map +1 -0
- package/dist/output-root-CFYms3ad.cjs +43 -0
- package/dist/output-root-CmWM7aV2.mjs +33 -0
- package/dist/output-root-CmWM7aV2.mjs.map +1 -0
- package/dist/parser-BUIWW1OH.cjs +182 -0
- package/dist/parser-DO0_SssG.mjs +182 -0
- package/dist/parser-DO0_SssG.mjs.map +1 -0
- package/dist/public-tools-D4UI-Zb0.mjs +2554 -0
- package/dist/public-tools-D4UI-Zb0.mjs.map +1 -0
- package/dist/public-tools-XSpkz2ky.cjs +2556 -0
- package/dist/resolver-C2ZS7oC8.mjs +201 -0
- package/dist/resolver-C2ZS7oC8.mjs.map +1 -0
- package/dist/resolver-zYbu4wDV.cjs +203 -0
- package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
- package/dist/runner-1Eq55OYb.cjs +148 -0
- package/dist/runner-BhUHbiHG.mjs +149 -0
- package/dist/runner-BhUHbiHG.mjs.map +1 -0
- package/dist/schema-4XpzDFQM.cjs +55 -0
- package/dist/schema-8d0rVIdZ.mjs +37 -0
- package/dist/schema-8d0rVIdZ.mjs.map +1 -0
- package/dist/schema-cache-9CksD7tX.mjs +34 -0
- package/dist/schema-cache-9CksD7tX.mjs.map +1 -0
- package/dist/schema-cache-CgWRCN2N.cjs +36 -0
- package/dist/selector-CkFcTXzz.cjs +10 -0
- package/dist/selector-xjm6NTHI.mjs +12 -0
- package/dist/selector-xjm6NTHI.mjs.map +1 -0
- package/dist/server-BkM5xrXb.mjs +45 -0
- package/dist/server-BkM5xrXb.mjs.map +1 -0
- package/dist/server-DXowbpfi.cjs +54 -0
- package/dist/session-BpNylyuJ.cjs +115 -0
- package/dist/session-CcTgYxsj.mjs +115 -0
- package/dist/session-CcTgYxsj.mjs.map +1 -0
- package/dist/setup-DOpKPrlx.cjs +81 -0
- package/dist/setup-DyrWHuwQ.mjs +80 -0
- package/dist/setup-DyrWHuwQ.mjs.map +1 -0
- package/dist/store-BiUhQOIf.cjs +230 -0
- package/dist/store-BoWE-Gtl.mjs +225 -0
- package/dist/store-BoWE-Gtl.mjs.map +1 -0
- package/dist/templates/graph.html +1406 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs +28 -0
- package/dist/tool-visibility-3Z_KvO9Q.mjs.map +1 -0
- package/dist/tool-visibility-CwgY205r.cjs +36 -0
- package/dist/tools-Cp2jAAAb.mjs +100 -0
- package/dist/tools-Cp2jAAAb.mjs.map +1 -0
- package/dist/tools-f_vJUZAF.cjs +139 -0
- package/dist/topup-server-BZuQifvh.cjs +940 -0
- package/dist/topup-server-DUjyFftI.mjs +919 -0
- package/dist/topup-server-DUjyFftI.mjs.map +1 -0
- package/dist/version-1gP19Lhi.mjs +8 -0
- package/dist/version-1gP19Lhi.mjs.map +1 -0
- package/dist/version-BNGtdpmH.cjs +18 -0
- package/dist/viz-BlCJe6Tk.mjs +35 -0
- package/dist/viz-BlCJe6Tk.mjs.map +1 -0
- package/dist/viz-ClezVXrJ.cjs +44 -0
- package/dist/wallet-BMelXBYP.mjs +104 -0
- package/dist/wallet-BMelXBYP.mjs.map +1 -0
- package/dist/wallet-RnvvSpV2.cjs +146 -0
- package/docs/architecture.md +145 -0
- package/docs/contributing.md +68 -0
- package/docs/debugging.md +68 -0
- package/docs/development.md +44 -0
- package/docs/graph-tools.md +251 -0
- package/docs/images/graph-mcp-iframe.png +0 -0
- package/docs/images/graph-visualization.png +0 -0
- package/docs/images/topup-page.png +0 -0
- package/docs/investigation-workspaces.md +151 -0
- package/docs/mcp-proxy.md +180 -0
- package/package.json +59 -0
- package/skills/chain-insights-developer-experience/SKILL.md +101 -0
- package/skills/chain-insights-investigation/SKILL.md +285 -0
- package/skills/chain-insights-investigation/agents/openai.yaml +4 -0
- package/skills/chain-insights-investigation/scripts/run-target-uat.sh +197 -0
- package/skills/chain-insights-trace-funds/SKILL.md +249 -0
- package/skills/ci-case/SKILL.md +43 -0
- package/skills/ci-status/SKILL.md +45 -0
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +75 -0
- package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +4 -0
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +414 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chain Swarm AML
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Chain Insights
|
|
2
|
+
|
|
3
|
+
Chain Insights is an AML investigation framework on top of GraphRAG MCP. It
|
|
4
|
+
turns graph access into analyst-ready workflows: address screening, fund-flow
|
|
5
|
+
tracing, scam topology discovery, case files, evidence, dossiers, reports, and
|
|
6
|
+
graph visualizations.
|
|
7
|
+
|
|
8
|
+
GraphRAG MCP exposes generic graph tools. Chain Insights adds AML tools and
|
|
9
|
+
investigation workflow around them.
|
|
10
|
+
|
|
11
|
+
## What You Can Do Today
|
|
12
|
+
|
|
13
|
+
| Tool | Use it for |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `address_risk` | Screen one address for risk, behavior, neighborhood context, and exchange exposure |
|
|
16
|
+
| `track_funds` | Trace victim/source funds through intermediaries to exchange deposit candidates |
|
|
17
|
+
| `scam_topology` | Expand a known victim incident into reviewable scam infrastructure and label candidates |
|
|
18
|
+
| `graph_query` | Run one read-only GQL/Cypher query against a GraphRAG MCP graph layer |
|
|
19
|
+
| `graph_query_batch` | Run related read-only graph queries as one MCP call |
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
From an installed package:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cia --version
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
From a local checkout:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install
|
|
33
|
+
npm run build
|
|
34
|
+
npm install -g .
|
|
35
|
+
cia --version
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Create an investigation workspace:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mkdir -p ./chain-insights-investigations
|
|
42
|
+
cd ./chain-insights-investigations
|
|
43
|
+
cia init .
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Check the configured endpoint and current GraphRAG MCP capabilities:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cia config get graphMcpEndpoint
|
|
50
|
+
cia mcp networks
|
|
51
|
+
cia mcp tools --refresh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If network or tool discovery fails, fix endpoint/auth first; the CLI can still
|
|
55
|
+
initialize workspaces and manage cases without a reachable GraphRAG MCP
|
|
56
|
+
endpoint.
|
|
57
|
+
|
|
58
|
+
Open a case and run a small investigation:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cia case open "First Chain Insights investigation" \
|
|
62
|
+
--tags aml,bittensor \
|
|
63
|
+
--description "Screen and trace a known source address"
|
|
64
|
+
|
|
65
|
+
cia mcp track-funds \
|
|
66
|
+
--network bittensor \
|
|
67
|
+
--trusted-addresses 5GTjfJaLpBNrgybhY24NqhDnKW9r94z72RSYLxeodxJfSkj5 \
|
|
68
|
+
--case 1
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then inspect:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cia case show 1
|
|
75
|
+
find reports cases -maxdepth 3 -type f | sort
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Demo
|
|
79
|
+
|
|
80
|
+
Run a direct live topology query:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cia mcp call graph_query \
|
|
84
|
+
network=bittensor \
|
|
85
|
+
"query=USE live_topology MATCH (n) WHERE n.address IS NOT NULL RETURN n.labels AS labels, n.address AS address LIMIT 10"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run a batch across graph layers:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cia mcp call graph_query_batch \
|
|
92
|
+
network=bittensor \
|
|
93
|
+
'queries=[{"id":"count","query":"USE live_topology MATCH (n) RETURN count(n) AS count LIMIT 1"},{"id":"archive_flows","query":"USE archive_topology MATCH (src:Address)-[f:FLOWS_TO]->(dst:Address) RETURN f.period_granularity AS granularity, src.address AS source, dst.address AS target LIMIT 3"},{"id":"facts_sample","query":"USE facts MATCH (a:Address)-[:HAS_FEATURE]->(f:AddressFeature) RETURN a.address AS address, f.sent_count AS sent_count LIMIT 3"}]'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run victim incident topology:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cia mcp scam-topology \
|
|
100
|
+
--network bittensor \
|
|
101
|
+
--victim-address 5... \
|
|
102
|
+
--incident-timestamp-ms 1715532228001 \
|
|
103
|
+
--max-hops 16 \
|
|
104
|
+
--case 1
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## How It Fits Together
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
Agent or CLI user
|
|
111
|
+
-> Chain Insights CLI / MCP proxy
|
|
112
|
+
-> local config, wallet, workspace, cases, evidence, reports
|
|
113
|
+
-> GraphRAG MCP
|
|
114
|
+
-> live_topology, archive_topology, facts
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Chain Insights stores investigation outputs in initialized local workspaces.
|
|
118
|
+
GraphRAG MCP performs graph-language reads against network-specific graph
|
|
119
|
+
layers.
|
|
120
|
+
|
|
121
|
+
## Topology And Facts
|
|
122
|
+
|
|
123
|
+
Graph queries must choose a layer explicitly:
|
|
124
|
+
|
|
125
|
+
| Layer | Backing data |
|
|
126
|
+
| --- | --- |
|
|
127
|
+
| `live_topology` | Memgraph RAM topology for current graph traversal |
|
|
128
|
+
| `archive_topology` | StarRocks historical topology for warehouse-scale flow reads |
|
|
129
|
+
| `facts` | StarRocks facts for feature and enrichment reads |
|
|
130
|
+
|
|
131
|
+
Use `graph_query_batch` when related reads should share one call and one
|
|
132
|
+
result envelope.
|
|
133
|
+
|
|
134
|
+
## AML Tools
|
|
135
|
+
|
|
136
|
+
The high-level AML tools are Chain Insights workflows built around graph access
|
|
137
|
+
and local case state:
|
|
138
|
+
|
|
139
|
+
- `address_risk` starts a single-address screen with risk, behavior,
|
|
140
|
+
neighborhood context, and exchange exposure.
|
|
141
|
+
- `track_funds` traces trusted victim/source funds through intermediaries to
|
|
142
|
+
exchange deposit candidates.
|
|
143
|
+
- `scam_topology` expands from a known victim incident into reviewable
|
|
144
|
+
topology, safety decisions, and ML-ready label candidates.
|
|
145
|
+
|
|
146
|
+
When a case is provided, tools can save compact evidence pointers and graph
|
|
147
|
+
reports under the workspace instead of embedding large payloads in case notes.
|
|
148
|
+
|
|
149
|
+
## Docs Map
|
|
150
|
+
|
|
151
|
+
| Doc | Use it for |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| [Graph tools](docs/graph-tools.md) | GraphRAG MCP layers, `graph_query`, `graph_query_batch`, AML tool contracts, graph reports, evidence pointers |
|
|
154
|
+
| [Investigation workspaces](docs/investigation-workspaces.md) | `cia init`, case layout, evidence, dossiers, imports, templates, sessions, reports |
|
|
155
|
+
| [MCP proxy](docs/mcp-proxy.md) | Stdio proxy behavior, agent installers, local tools, auth modes, Inspector validation |
|
|
156
|
+
| [Architecture](docs/architecture.md) | Product layers, data flow, local storage, security model, config keys |
|
|
157
|
+
| [Development](docs/development.md) | Build, test, and local install commands |
|
|
158
|
+
| [Contributing](docs/contributing.md) | Development workflow, pull requests, release expectations |
|
|
159
|
+
| [Debugging](docs/debugging.md) | Local troubleshooting, diagnostics, debug workflows |
|
|
160
|
+
|
|
161
|
+
## What It Is Not
|
|
162
|
+
|
|
163
|
+
Chain Insights is not a custodial wallet, hosted case database, or replacement
|
|
164
|
+
for analyst review. It does not write risk labels automatically. Investigation
|
|
165
|
+
data stays in the local workspace unless the operator exports or shares it.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// CJS shim — bridges the npm bin entry (CJS, no build step) to the
|
|
5
|
+
// ESM dist built by tsdown. Dynamic import() is the correct bridge pattern
|
|
6
|
+
// (see references/get-shit-done/bin/gsd-sdk.js for the GSD precedent).
|
|
7
|
+
import('../dist/cli.mjs').catch((err) => {
|
|
8
|
+
console.error('Failed to load chain-insights:', err.message);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
package/bin/install.cjs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// Chain Insights installer — CJS, stdlib-only.
|
|
5
|
+
// Runs before node_modules exists; zero npm imports allowed.
|
|
6
|
+
// Extension is .cjs (not .js) because package.json has "type": "module" —
|
|
7
|
+
// a .js file would be treated as ESM and require() calls would crash.
|
|
8
|
+
// Adapted from GSD reference: references/get-shit-done/bin/install.js
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
|
|
14
|
+
// ANSI colors — no chalk
|
|
15
|
+
const cyan = '\x1b[36m';
|
|
16
|
+
const green = '\x1b[32m';
|
|
17
|
+
const bold = '\x1b[1m';
|
|
18
|
+
const dim = '\x1b[2m';
|
|
19
|
+
const reset = '\x1b[0m';
|
|
20
|
+
|
|
21
|
+
// Parse args
|
|
22
|
+
const args = process.argv.slice(2);
|
|
23
|
+
const hasClaude = args.includes('--claude');
|
|
24
|
+
const hasCodex = args.includes('--codex');
|
|
25
|
+
const hasHermes = args.includes('--hermes');
|
|
26
|
+
const hasLocal = args.includes('--local');
|
|
27
|
+
|
|
28
|
+
if (!hasClaude && !hasCodex && !hasHermes && !hasLocal) {
|
|
29
|
+
console.log(`\n${bold}chain-insights installer${reset}`);
|
|
30
|
+
console.log(`\nUsage: node bin/install.cjs --claude | --codex | --hermes`);
|
|
31
|
+
console.log(` ${cyan}--claude${reset} Install Claude Code skills globally to ~/.claude/skills/`);
|
|
32
|
+
console.log(` ${cyan}--codex${reset} Install Codex skills globally to ~/.codex/skills/ and register MCP`);
|
|
33
|
+
console.log(` ${cyan}--hermes${reset} Install Hermes skills globally to ~/.hermes/skills/chain-insights/ and register MCP`);
|
|
34
|
+
console.log(` ${cyan}--local${reset} Install skills locally to ./.claude/commands/chain-insights/`);
|
|
35
|
+
console.log('');
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const homeDir = os.homedir();
|
|
40
|
+
const dataDir = path.join(homeDir, '.chain-insights');
|
|
41
|
+
const configPath = path.join(dataDir, 'config.json');
|
|
42
|
+
const srcSkillsDir = path.join(__dirname, '..', 'skills');
|
|
43
|
+
|
|
44
|
+
// Determine skills targets
|
|
45
|
+
const skillsTargets = [];
|
|
46
|
+
if (hasClaude) skillsTargets.push({ name: 'Claude Code', dir: path.join(homeDir, '.claude', 'skills') });
|
|
47
|
+
if (hasCodex) skillsTargets.push({ name: 'Codex', dir: path.join(homeDir, '.codex', 'skills') });
|
|
48
|
+
if (hasHermes) skillsTargets.push({ name: 'Hermes', dir: path.join(homeDir, '.hermes', 'skills', 'chain-insights') });
|
|
49
|
+
if (hasLocal) skillsTargets.push({ name: 'Local Claude commands', dir: path.join(process.cwd(), '.claude', 'commands', 'chain-insights') });
|
|
50
|
+
|
|
51
|
+
// ─── 1. Copy agent skills ─────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
function copyCommandsAsClaudeSkills(srcDir, targetDir) {
|
|
54
|
+
if (!fs.existsSync(srcDir)) {
|
|
55
|
+
console.error(`Skills source not found: ${srcDir}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
60
|
+
|
|
61
|
+
// Remove stale ci-* skill dirs before copying (clean reinstall)
|
|
62
|
+
const existing = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
63
|
+
for (const entry of existing) {
|
|
64
|
+
if (entry.isDirectory() && entry.name.startsWith('ci-')) {
|
|
65
|
+
fs.rmSync(path.join(targetDir, entry.name), { recursive: true, force: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const copyTree = (src, dest) => {
|
|
70
|
+
const stat = fs.statSync(src);
|
|
71
|
+
if (stat.isDirectory()) {
|
|
72
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
73
|
+
for (const child of fs.readdirSync(src)) {
|
|
74
|
+
copyTree(path.join(src, child), path.join(dest, child));
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (stat.isFile()) {
|
|
79
|
+
fs.copyFileSync(src, dest);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Recurse into skills/ directory; each subdirectory becomes a skill dir.
|
|
84
|
+
const skillDirs = fs.readdirSync(srcDir, { withFileTypes: true })
|
|
85
|
+
.filter(e => e.isDirectory());
|
|
86
|
+
|
|
87
|
+
for (const skillDir of skillDirs) {
|
|
88
|
+
const skillSrc = path.join(srcDir, skillDir.name);
|
|
89
|
+
const skillDest = path.join(targetDir, skillDir.name);
|
|
90
|
+
fs.rmSync(skillDest, { recursive: true, force: true });
|
|
91
|
+
copyTree(skillSrc, skillDest);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const target of skillsTargets) {
|
|
96
|
+
copyCommandsAsClaudeSkills(srcSkillsDir, target.dir);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─── 2. Create ~/.chain-insights/ config directory ────────────────────────
|
|
100
|
+
|
|
101
|
+
if (!fs.existsSync(dataDir)) {
|
|
102
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── 3. Write default config.json if absent ───────────────────────────────
|
|
106
|
+
|
|
107
|
+
if (!fs.existsSync(configPath)) {
|
|
108
|
+
const defaultConfig = {
|
|
109
|
+
mcpEndpoint: 'http://localhost:4000',
|
|
110
|
+
mcpAuthToken: '',
|
|
111
|
+
walletAddress: '',
|
|
112
|
+
graphMcpMode: 'paid',
|
|
113
|
+
serverPort: 4321,
|
|
114
|
+
dataDir: dataDir,
|
|
115
|
+
version: '1',
|
|
116
|
+
};
|
|
117
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8');
|
|
118
|
+
// Owner-readable only — config may contain MCP auth token (ASVS L1 V4.3.2 / T-02-01)
|
|
119
|
+
fs.chmodSync(configPath, 0o600);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ─── 4. Register MCP proxy in supported clients ───────────────────────────
|
|
123
|
+
|
|
124
|
+
const proxyBinPath = path.resolve(__dirname, 'mcp-proxy.cjs');
|
|
125
|
+
const { execFileSync } = require('child_process');
|
|
126
|
+
|
|
127
|
+
if (hasClaude) {
|
|
128
|
+
try {
|
|
129
|
+
execFileSync(
|
|
130
|
+
'claude',
|
|
131
|
+
['mcp', 'add', 'chain-insights-proxy', '--scope', 'user', '--', 'node', proxyBinPath],
|
|
132
|
+
{ stdio: 'pipe' }
|
|
133
|
+
);
|
|
134
|
+
console.log(` ${cyan}Claude MCP:${reset} registered (chain-insights-proxy) at ${proxyBinPath}`);
|
|
135
|
+
} catch {
|
|
136
|
+
console.log(` ${dim}Claude MCP:${reset} run manually: claude mcp add chain-insights-proxy --scope user -- node ${proxyBinPath}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function tomlQuoted(value) {
|
|
141
|
+
return JSON.stringify(value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function installCodexMcp(configFile, proxyPath) {
|
|
145
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
146
|
+
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : '';
|
|
147
|
+
const block = [
|
|
148
|
+
'[mcp_servers.chain-insights]',
|
|
149
|
+
'command = "node"',
|
|
150
|
+
`args = [${tomlQuoted(proxyPath)}]`,
|
|
151
|
+
'',
|
|
152
|
+
].join('\n');
|
|
153
|
+
|
|
154
|
+
const heading = '[mcp_servers.chain-insights]';
|
|
155
|
+
const start = content.indexOf(heading);
|
|
156
|
+
if (start >= 0) {
|
|
157
|
+
let end = content.length;
|
|
158
|
+
const rest = content.slice(start + heading.length);
|
|
159
|
+
const nextSection = rest.search(/\n\[/);
|
|
160
|
+
if (nextSection >= 0) end = start + heading.length + nextSection + 1;
|
|
161
|
+
content = `${content.slice(0, start)}${block}${content.slice(end)}`;
|
|
162
|
+
} else {
|
|
163
|
+
const separator = content.endsWith('\n') || content.length === 0 ? '' : '\n';
|
|
164
|
+
content = `${content}${separator}\n${block}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fs.writeFileSync(configFile, content, 'utf8');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (hasCodex) {
|
|
171
|
+
const codexConfig = path.join(homeDir, '.codex', 'config.toml');
|
|
172
|
+
installCodexMcp(codexConfig, proxyBinPath);
|
|
173
|
+
console.log(` ${cyan}Codex MCP:${reset} registered in ${codexConfig}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function yamlQuoted(value) {
|
|
177
|
+
return JSON.stringify(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function findTopLevelSectionEnd(lines, start) {
|
|
181
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
182
|
+
const line = lines[i];
|
|
183
|
+
if (line.trim() === '') continue;
|
|
184
|
+
if (!line.startsWith(' ') && !line.startsWith('\t')) return i;
|
|
185
|
+
}
|
|
186
|
+
return lines.length;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function findHermesServerEnd(lines, start, sectionEnd) {
|
|
190
|
+
for (let i = start + 1; i < sectionEnd; i++) {
|
|
191
|
+
const line = lines[i];
|
|
192
|
+
if (/^ [^ ].*:/.test(line)) return i;
|
|
193
|
+
}
|
|
194
|
+
return sectionEnd;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function installHermesMcp(configFile, proxyPath) {
|
|
198
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
|
199
|
+
|
|
200
|
+
const serverBlock = [
|
|
201
|
+
' chain-insights:',
|
|
202
|
+
' command: "node"',
|
|
203
|
+
' args:',
|
|
204
|
+
` - ${yamlQuoted(proxyPath)}`,
|
|
205
|
+
' enabled: true',
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : '';
|
|
209
|
+
if (/^mcp_servers:\s*\{\s*\}\s*$/m.test(content)) {
|
|
210
|
+
content = content.replace(/^mcp_servers:\s*\{\s*\}\s*$/m, ['mcp_servers:', ...serverBlock].join('\n'));
|
|
211
|
+
fs.writeFileSync(configFile, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const lines = content.length ? content.split(/\r?\n/) : [];
|
|
216
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
|
|
217
|
+
|
|
218
|
+
const sectionStart = lines.findIndex(line => line === 'mcp_servers:');
|
|
219
|
+
if (sectionStart < 0) {
|
|
220
|
+
if (lines.length > 0) lines.push('');
|
|
221
|
+
lines.push('mcp_servers:', ...serverBlock);
|
|
222
|
+
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8');
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const sectionEnd = findTopLevelSectionEnd(lines, sectionStart);
|
|
227
|
+
const serverStart = lines.findIndex((line, index) => index > sectionStart && index < sectionEnd && line === ' chain-insights:');
|
|
228
|
+
if (serverStart >= 0) {
|
|
229
|
+
const serverEnd = findHermesServerEnd(lines, serverStart, sectionEnd);
|
|
230
|
+
lines.splice(serverStart, serverEnd - serverStart, ...serverBlock);
|
|
231
|
+
} else {
|
|
232
|
+
lines.splice(sectionStart + 1, 0, ...serverBlock);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (hasHermes) {
|
|
239
|
+
const hermesConfig = path.join(homeDir, '.hermes', 'config.yaml');
|
|
240
|
+
installHermesMcp(hermesConfig, proxyBinPath);
|
|
241
|
+
console.log(` ${cyan}Hermes MCP:${reset} registered in ${hermesConfig}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ─── 5. Print installation summary ────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
console.log(`\n${bold}${green}Chain Insights installed${reset}`);
|
|
247
|
+
for (const target of skillsTargets) {
|
|
248
|
+
console.log(` ${cyan}${target.name} skills:${reset} ${target.dir}`);
|
|
249
|
+
}
|
|
250
|
+
console.log(` ${cyan}Config:${reset} ${configPath}`);
|
|
251
|
+
console.log(` ${cyan}Data dir:${reset} ${dataDir}`);
|
|
252
|
+
console.log(`\n${dim}Run ${reset}${cyan}chain-insights status${reset}${dim} to verify the installation.${reset}\n`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// CJS shim for stdio MCP proxy — spawned by Claude Code via ~/.claude.json mcpServers config.
|
|
5
|
+
// IMPORTANT: Do not write to stdout here — stdout is owned by StdioServerTransport.
|
|
6
|
+
// Use process.stderr.write() exclusively for pre-load errors.
|
|
7
|
+
import('../dist/mcp-proxy.mjs').then(({ createProxy }) => createProxy()).catch((err) => {
|
|
8
|
+
process.stderr.write(`Failed to load chain-insights MCP proxy: ${err.message}\n`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
//#region src/workspace/active.ts
|
|
6
|
+
var active_exports = /* @__PURE__ */ __exportAll({
|
|
7
|
+
activeCasesRoot: () => activeCasesRoot,
|
|
8
|
+
activeDataDir: () => activeDataDir,
|
|
9
|
+
findActiveWorkspace: () => findActiveWorkspace
|
|
10
|
+
});
|
|
11
|
+
function workspaceFromRoot(rootCandidate) {
|
|
12
|
+
const root = path.resolve(rootCandidate);
|
|
13
|
+
const metadataDir = path.join(root, ".chain-insights");
|
|
14
|
+
const markerPath = path.join(metadataDir, "workspace.json");
|
|
15
|
+
if (!fs.existsSync(markerPath)) return null;
|
|
16
|
+
const parsed = JSON.parse(fs.readFileSync(markerPath, "utf8"));
|
|
17
|
+
if (parsed.schema !== "chain-insights.workspace.v1") return null;
|
|
18
|
+
const workspaceRoot = path.resolve(parsed.workspace_root ?? root);
|
|
19
|
+
const casesDir = parsed.cases_dir ?? "cases";
|
|
20
|
+
return {
|
|
21
|
+
root: workspaceRoot,
|
|
22
|
+
metadataDir: path.join(workspaceRoot, ".chain-insights"),
|
|
23
|
+
casesRoot: path.resolve(workspaceRoot, casesDir)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function findActiveWorkspace(startDir = process.cwd()) {
|
|
27
|
+
const envWorkspace = process.env["CHAIN_INSIGHTS_WORKSPACE"]?.trim();
|
|
28
|
+
if (envWorkspace) {
|
|
29
|
+
const active = workspaceFromRoot(envWorkspace);
|
|
30
|
+
if (active) return active;
|
|
31
|
+
}
|
|
32
|
+
let current = path.resolve(startDir);
|
|
33
|
+
while (true) {
|
|
34
|
+
const active = workspaceFromRoot(current);
|
|
35
|
+
if (active) return active;
|
|
36
|
+
const parent = path.dirname(current);
|
|
37
|
+
if (parent === current) return null;
|
|
38
|
+
current = parent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function activeCasesRoot() {
|
|
42
|
+
return findActiveWorkspace()?.casesRoot ?? path.join(os.homedir(), ".chain-insights", "cases");
|
|
43
|
+
}
|
|
44
|
+
function activeDataDir(fallbackDataDir) {
|
|
45
|
+
return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), ".chain-insights");
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { active_exports as n, findActiveWorkspace as r, activeCasesRoot as t };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=active-BSrxLKwn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"active-BSrxLKwn.mjs","names":[],"sources":["../src/workspace/active.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\n\ninterface WorkspaceConfig {\n schema?: string\n workspace_root?: string\n cases_dir?: string\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n casesRoot: string\n}\n\nfunction workspaceFromRoot(rootCandidate: string): ActiveWorkspace | null {\n const root = path.resolve(rootCandidate)\n const metadataDir = path.join(root, '.chain-insights')\n const markerPath = path.join(metadataDir, 'workspace.json')\n if (!fs.existsSync(markerPath)) return null\n\n const parsed = JSON.parse(fs.readFileSync(markerPath, 'utf8')) as WorkspaceConfig\n if (parsed.schema !== 'chain-insights.workspace.v1') return null\n\n const workspaceRoot = path.resolve(parsed.workspace_root ?? root)\n const casesDir = parsed.cases_dir ?? 'cases'\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n casesRoot: path.resolve(workspaceRoot, casesDir),\n }\n}\n\nexport function findActiveWorkspace(startDir = process.cwd()): ActiveWorkspace | null {\n const envWorkspace = process.env['CHAIN_INSIGHTS_WORKSPACE']?.trim()\n if (envWorkspace) {\n const active = workspaceFromRoot(envWorkspace)\n if (active) return active\n }\n\n let current = path.resolve(startDir)\n while (true) {\n const active = workspaceFromRoot(current)\n if (active) return active\n\n const parent = path.dirname(current)\n if (parent === current) return null\n current = parent\n }\n}\n\nexport function activeMetadataDir(): string {\n return findActiveWorkspace()?.metadataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n\nexport function activeCasesRoot(): string {\n return findActiveWorkspace()?.casesRoot ?? path.join(os.homedir(), '.chain-insights', 'cases')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAS,kBAAkB,eAA+C;CACxE,MAAM,OAAO,KAAK,QAAQ,cAAc;CACxC,MAAM,cAAc,KAAK,KAAK,MAAM,kBAAkB;CACtD,MAAM,aAAa,KAAK,KAAK,aAAa,iBAAiB;AAC3D,KAAI,CAAC,GAAG,WAAW,WAAW,CAAE,QAAO;CAEvC,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;AAC9D,KAAI,OAAO,WAAW,8BAA+B,QAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,OAAO,kBAAkB,KAAK;CACjE,MAAM,WAAW,OAAO,aAAa;AACrC,QAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,kBAAkB;EACxD,WAAW,KAAK,QAAQ,eAAe,SAAS;EACjD;;AAGH,SAAgB,oBAAoB,WAAW,QAAQ,KAAK,EAA0B;CACpF,MAAM,eAAe,QAAQ,IAAI,6BAA6B,MAAM;AACpE,KAAI,cAAc;EAChB,MAAM,SAAS,kBAAkB,aAAa;AAC9C,MAAI,OAAQ,QAAO;;CAGrB,IAAI,UAAU,KAAK,QAAQ,SAAS;AACpC,QAAO,MAAM;EACX,MAAM,SAAS,kBAAkB,QAAQ;AACzC,MAAI,OAAQ,QAAO;EAEnB,MAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,MAAI,WAAW,QAAS,QAAO;AAC/B,YAAU;;;AAQd,SAAgB,kBAA0B;AACxC,QAAO,qBAAqB,EAAE,aAAa,KAAK,KAAK,GAAG,SAAS,EAAE,mBAAmB,QAAQ;;AAGhG,SAAgB,cAAc,iBAAkC;AAC9D,QAAO,qBAAqB,EAAE,QAAQ,mBAAmB,KAAK,KAAK,GAAG,SAAS,EAAE,kBAAkB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const require_chunk = require("./chunk-CZWwpsFl.cjs");
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
node_path = require_chunk.__toESM(node_path, 1);
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
6
|
+
let node_os = require("node:os");
|
|
7
|
+
node_os = require_chunk.__toESM(node_os, 1);
|
|
8
|
+
//#region src/workspace/active.ts
|
|
9
|
+
var active_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
10
|
+
activeCasesRoot: () => activeCasesRoot,
|
|
11
|
+
activeDataDir: () => activeDataDir,
|
|
12
|
+
findActiveWorkspace: () => findActiveWorkspace
|
|
13
|
+
});
|
|
14
|
+
function workspaceFromRoot(rootCandidate) {
|
|
15
|
+
const root = node_path.default.resolve(rootCandidate);
|
|
16
|
+
const metadataDir = node_path.default.join(root, ".chain-insights");
|
|
17
|
+
const markerPath = node_path.default.join(metadataDir, "workspace.json");
|
|
18
|
+
if (!node_fs.default.existsSync(markerPath)) return null;
|
|
19
|
+
const parsed = JSON.parse(node_fs.default.readFileSync(markerPath, "utf8"));
|
|
20
|
+
if (parsed.schema !== "chain-insights.workspace.v1") return null;
|
|
21
|
+
const workspaceRoot = node_path.default.resolve(parsed.workspace_root ?? root);
|
|
22
|
+
const casesDir = parsed.cases_dir ?? "cases";
|
|
23
|
+
return {
|
|
24
|
+
root: workspaceRoot,
|
|
25
|
+
metadataDir: node_path.default.join(workspaceRoot, ".chain-insights"),
|
|
26
|
+
casesRoot: node_path.default.resolve(workspaceRoot, casesDir)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function findActiveWorkspace(startDir = process.cwd()) {
|
|
30
|
+
const envWorkspace = process.env["CHAIN_INSIGHTS_WORKSPACE"]?.trim();
|
|
31
|
+
if (envWorkspace) {
|
|
32
|
+
const active = workspaceFromRoot(envWorkspace);
|
|
33
|
+
if (active) return active;
|
|
34
|
+
}
|
|
35
|
+
let current = node_path.default.resolve(startDir);
|
|
36
|
+
while (true) {
|
|
37
|
+
const active = workspaceFromRoot(current);
|
|
38
|
+
if (active) return active;
|
|
39
|
+
const parent = node_path.default.dirname(current);
|
|
40
|
+
if (parent === current) return null;
|
|
41
|
+
current = parent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function activeCasesRoot() {
|
|
45
|
+
return findActiveWorkspace()?.casesRoot ?? node_path.default.join(node_os.default.homedir(), ".chain-insights", "cases");
|
|
46
|
+
}
|
|
47
|
+
function activeDataDir(fallbackDataDir) {
|
|
48
|
+
return findActiveWorkspace()?.root ?? fallbackDataDir ?? node_path.default.join(node_os.default.homedir(), ".chain-insights");
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
Object.defineProperty(exports, "activeCasesRoot", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
get: function() {
|
|
54
|
+
return activeCasesRoot;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(exports, "active_exports", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
get: function() {
|
|
60
|
+
return active_exports;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
Object.defineProperty(exports, "findActiveWorkspace", {
|
|
64
|
+
enumerable: true,
|
|
65
|
+
get: function() {
|
|
66
|
+
return findActiveWorkspace;
|
|
67
|
+
}
|
|
68
|
+
});
|