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
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -Eeuo pipefail
|
|
3
|
+
|
|
4
|
+
CHAIN_INSIGHTS_DIR="${CHAIN_INSIGHTS_DIR:-/home/aphex5/work/chain-insights}"
|
|
5
|
+
GRAPHRAG_ML_DIR="${GRAPHRAG_ML_DIR:-/home/aphex5/work/rbmk/repos/ml}"
|
|
6
|
+
GRAPHRAG_DIR="${GRAPHRAG_DIR:-${GRAPHRAG_ML_DIR}/graphrag}"
|
|
7
|
+
RBMK_DIR="${RBMK_DIR:-$(cd "${GRAPHRAG_ML_DIR}/../.." && pwd)}"
|
|
8
|
+
MCP_ENDPOINT="${GRAPHRAG_MCP_ENDPOINT:-http://localhost:8012/mcp}"
|
|
9
|
+
DEBUG_TOKEN="${GRAPHRAG_DEBUG_TOKEN:-chain-insights-dev-debug}"
|
|
10
|
+
SERVER_PORT="${CHAIN_INSIGHTS_SERVER_PORT:-4321}"
|
|
11
|
+
NETWORK="${NETWORK:-bittensor}"
|
|
12
|
+
UAT_ADDRESS="${UAT_ADDRESS:-5Ccmf1dJKzGtXX7h17eN72MVMRsFwvYjPVmkXPUaapczECf6}"
|
|
13
|
+
REPORT_DIR="${REPORT_DIR:-${CHAIN_INSIGHTS_DIR}/.tmp/uat}"
|
|
14
|
+
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
15
|
+
RUN_DIR="${REPORT_DIR}/${RUN_ID}"
|
|
16
|
+
WORKSPACE_ROOT="${WORKSPACE_ROOT:-${RUN_DIR}/workspace}"
|
|
17
|
+
CHAIN_INSIGHTS_CLI="${CHAIN_INSIGHTS_DIR}/bin/cli.js"
|
|
18
|
+
CHAIN_INSIGHTS_PROXY="${CHAIN_INSIGHTS_DIR}/bin/mcp-proxy.cjs"
|
|
19
|
+
GLOBAL_REPORTS="${HOME}/.chain-insights/reports"
|
|
20
|
+
GLOBAL_CASES="${HOME}/.chain-insights/cases"
|
|
21
|
+
GLOBAL_SNAPSHOT_BEFORE="${RUN_DIR}/global-output-before.txt"
|
|
22
|
+
GLOBAL_SNAPSHOT_AFTER="${RUN_DIR}/global-output-after.txt"
|
|
23
|
+
SERVER_PID=""
|
|
24
|
+
CONFIG_SNAPSHOT_READY=0
|
|
25
|
+
|
|
26
|
+
mkdir -p "${RUN_DIR}"
|
|
27
|
+
|
|
28
|
+
log() {
|
|
29
|
+
printf '[uat] %s\n' "$*"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
require_cmd() {
|
|
33
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
34
|
+
printf '[uat] missing required command: %s\n' "$1" >&2
|
|
35
|
+
exit 127
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
snapshot_global_outputs() {
|
|
40
|
+
local output_file="$1"
|
|
41
|
+
: >"${output_file}"
|
|
42
|
+
for dir in "${GLOBAL_REPORTS}" "${GLOBAL_CASES}"; do
|
|
43
|
+
{
|
|
44
|
+
printf '[%s]\n' "${dir}"
|
|
45
|
+
if [[ -d "${dir}" ]]; then
|
|
46
|
+
(
|
|
47
|
+
cd "${dir}"
|
|
48
|
+
find . -mindepth 1 -type d -print | LC_ALL=C sort | sed 's/^/dir /'
|
|
49
|
+
find . -mindepth 1 -type f -print0 \
|
|
50
|
+
| LC_ALL=C sort -z \
|
|
51
|
+
| xargs -0 -r sha256sum \
|
|
52
|
+
| sed 's/^/file /'
|
|
53
|
+
)
|
|
54
|
+
else
|
|
55
|
+
printf '<missing>\n'
|
|
56
|
+
fi
|
|
57
|
+
} >>"${output_file}"
|
|
58
|
+
done
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
assert_no_global_outputs_changed() {
|
|
62
|
+
snapshot_global_outputs "${GLOBAL_SNAPSHOT_AFTER}"
|
|
63
|
+
if ! cmp -s "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}"; then
|
|
64
|
+
log "global investigation output roots changed; reports/cases must stay workspace-local"
|
|
65
|
+
diff -u "${GLOBAL_SNAPSHOT_BEFORE}" "${GLOBAL_SNAPSHOT_AFTER}" >&2 || true
|
|
66
|
+
return 1
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
cleanup() {
|
|
71
|
+
if [[ -n "${SERVER_PID}" ]]; then
|
|
72
|
+
kill "${SERVER_PID}" >/dev/null 2>&1 || true
|
|
73
|
+
wait "${SERVER_PID}" >/dev/null 2>&1 || true
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
restore_config() {
|
|
78
|
+
if [[ "${CONFIG_SNAPSHOT_READY}" != "1" ]]; then
|
|
79
|
+
return
|
|
80
|
+
fi
|
|
81
|
+
if [[ -n "${OLD_GRAPH_MCP_MODE:-}" ]]; then
|
|
82
|
+
node "${CHAIN_INSIGHTS_CLI}" config set graphMcpMode "${OLD_GRAPH_MCP_MODE}" >/dev/null || true
|
|
83
|
+
fi
|
|
84
|
+
if [[ -n "${OLD_GRAPH_MCP_ENDPOINT:-}" ]]; then
|
|
85
|
+
node "${CHAIN_INSIGHTS_CLI}" config set graphMcpEndpoint "${OLD_GRAPH_MCP_ENDPOINT}" >/dev/null || true
|
|
86
|
+
fi
|
|
87
|
+
node "${CHAIN_INSIGHTS_CLI}" config set graphMcpAuthToken "${OLD_GRAPH_MCP_AUTH_TOKEN:-}" >/dev/null || true
|
|
88
|
+
if [[ -n "${OLD_SERVER_PORT:-}" ]]; then
|
|
89
|
+
node "${CHAIN_INSIGHTS_CLI}" config set serverPort "${OLD_SERVER_PORT}" >/dev/null || true
|
|
90
|
+
fi
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
finish() {
|
|
94
|
+
local status="$?"
|
|
95
|
+
set +e
|
|
96
|
+
cleanup
|
|
97
|
+
if [[ -f "${GLOBAL_SNAPSHOT_BEFORE}" ]]; then
|
|
98
|
+
assert_no_global_outputs_changed || status=1
|
|
99
|
+
fi
|
|
100
|
+
restore_config
|
|
101
|
+
exit "${status}"
|
|
102
|
+
}
|
|
103
|
+
trap finish EXIT
|
|
104
|
+
|
|
105
|
+
require_cmd node
|
|
106
|
+
require_cmd npm
|
|
107
|
+
require_cmd npx
|
|
108
|
+
require_cmd docker
|
|
109
|
+
require_cmd curl
|
|
110
|
+
require_cmd sha256sum
|
|
111
|
+
|
|
112
|
+
if [[ ! -d "${CHAIN_INSIGHTS_DIR}" ]]; then
|
|
113
|
+
log "missing Chain Insights repo: ${CHAIN_INSIGHTS_DIR}"
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ ! -d "${GRAPHRAG_ML_DIR}" ]]; then
|
|
118
|
+
log "missing GraphRAG compose root: ${GRAPHRAG_ML_DIR}"
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
if [[ ! -d "${RBMK_DIR}" ]]; then
|
|
123
|
+
log "missing RBMK dev stack root: ${RBMK_DIR}"
|
|
124
|
+
exit 1
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
if [[ ! -d "${GRAPHRAG_DIR}" ]]; then
|
|
128
|
+
log "missing GraphRAG repo: ${GRAPHRAG_DIR}"
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
log "report directory: ${RUN_DIR}"
|
|
133
|
+
snapshot_global_outputs "${GLOBAL_SNAPSHOT_BEFORE}"
|
|
134
|
+
OLD_GRAPH_MCP_MODE="$(node "${CHAIN_INSIGHTS_CLI}" config get graphMcpMode || true)"
|
|
135
|
+
OLD_GRAPH_MCP_ENDPOINT="$(node "${CHAIN_INSIGHTS_CLI}" config get graphMcpEndpoint || true)"
|
|
136
|
+
OLD_GRAPH_MCP_AUTH_TOKEN="$(node "${CHAIN_INSIGHTS_CLI}" config get graphMcpAuthToken || true)"
|
|
137
|
+
OLD_SERVER_PORT="$(node "${CHAIN_INSIGHTS_CLI}" config get serverPort || true)"
|
|
138
|
+
CONFIG_SNAPSHOT_READY=1
|
|
139
|
+
log "starting RBMK dev stack"
|
|
140
|
+
(cd "${RBMK_DIR}" && bash dev.sh --bg)
|
|
141
|
+
|
|
142
|
+
cd "${CHAIN_INSIGHTS_DIR}"
|
|
143
|
+
|
|
144
|
+
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
|
|
145
|
+
log "building Chain Insights dist"
|
|
146
|
+
npm run build
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
log "initializing Chain Insights UAT workspace: ${WORKSPACE_ROOT}"
|
|
150
|
+
node "${CHAIN_INSIGHTS_CLI}" init "${WORKSPACE_ROOT}" --force >/dev/null
|
|
151
|
+
export CHAIN_INSIGHTS_WORKSPACE="${WORKSPACE_ROOT}"
|
|
152
|
+
|
|
153
|
+
log "configuring Chain Insights MCP endpoint and debug bearer token"
|
|
154
|
+
(
|
|
155
|
+
cd "${WORKSPACE_ROOT}"
|
|
156
|
+
node "${CHAIN_INSIGHTS_CLI}" debug on --token "${DEBUG_TOKEN}" --endpoint "${MCP_ENDPOINT}" >/dev/null
|
|
157
|
+
node "${CHAIN_INSIGHTS_CLI}" config set serverPort "${SERVER_PORT}" >/dev/null
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if curl -sf "http://127.0.0.1:${SERVER_PORT}/health" >/dev/null 2>&1; then
|
|
161
|
+
log "reusing healthy Chain Insights server on port ${SERVER_PORT}"
|
|
162
|
+
else
|
|
163
|
+
log "starting Chain Insights server on port ${SERVER_PORT}"
|
|
164
|
+
(
|
|
165
|
+
cd "${WORKSPACE_ROOT}"
|
|
166
|
+
CHAIN_INSIGHTS_WORKSPACE="${WORKSPACE_ROOT}" node "${CHAIN_INSIGHTS_CLI}" serve -p "${SERVER_PORT}"
|
|
167
|
+
) >"${RUN_DIR}/chain-insights-server.log" 2>&1 &
|
|
168
|
+
SERVER_PID="$!"
|
|
169
|
+
for _ in $(seq 1 30); do
|
|
170
|
+
if curl -sf "http://127.0.0.1:${SERVER_PORT}/health" >/dev/null 2>&1; then
|
|
171
|
+
break
|
|
172
|
+
fi
|
|
173
|
+
sleep 0.5
|
|
174
|
+
done
|
|
175
|
+
curl -sf "http://127.0.0.1:${SERVER_PORT}/health" >"${RUN_DIR}/server-health.json"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
log "refreshing Chain Insights remote tool schema cache"
|
|
179
|
+
(
|
|
180
|
+
cd "${WORKSPACE_ROOT}"
|
|
181
|
+
node "${CHAIN_INSIGHTS_CLI}" mcp tools --refresh
|
|
182
|
+
) >"${RUN_DIR}/chain-insights-tools.txt"
|
|
183
|
+
|
|
184
|
+
DIRECT_TOOLS_JSON="${RUN_DIR}/direct-tools-list.json"
|
|
185
|
+
log "checking direct GraphRAG tools/list"
|
|
186
|
+
npx @modelcontextprotocol/inspector \
|
|
187
|
+
--cli "${MCP_ENDPOINT}" \
|
|
188
|
+
--transport http \
|
|
189
|
+
--header "Authorization: Bearer ${DEBUG_TOKEN}" \
|
|
190
|
+
--header "X-MCP-Debug-Token: ${DEBUG_TOKEN}" \
|
|
191
|
+
--method tools/list >"${DIRECT_TOOLS_JSON}"
|
|
192
|
+
|
|
193
|
+
node - "${DIRECT_TOOLS_JSON}" "${RUN_DIR}/direct-high-level-tools.txt" <<'NODE'
|
|
194
|
+
const fs = require('node:fs')
|
|
195
|
+
const file = process.argv[2]
|
|
196
|
+
const highLevelFile = process.argv[3]
|
|
197
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
198
|
+
const tools = data.tools || []
|
|
199
|
+
const names = new Set(tools.map((tool) => tool.name))
|
|
200
|
+
const required = ['network_capabilities', 'graph_query', 'graph_query_batch']
|
|
201
|
+
const missing = required.filter((name) => !names.has(name))
|
|
202
|
+
if (missing.length) throw new Error(`direct tools/list missing tools: ${missing.join(', ')}`)
|
|
203
|
+
if (JSON.stringify(tools).includes('app_data')) throw new Error('direct tools/list still contains app_data')
|
|
204
|
+
const hasHighLevel = ['address_risk', 'track_funds'].every((name) => names.has(name))
|
|
205
|
+
fs.writeFileSync(highLevelFile, hasHighLevel ? 'yes\n' : 'no\n')
|
|
206
|
+
console.log(`[uat] direct tools/list ok: ${tools.length} tools (${hasHighLevel ? 'high-level' : 'primitive-only'})`)
|
|
207
|
+
NODE
|
|
208
|
+
|
|
209
|
+
DIRECT_JSON="${RUN_DIR}/direct-address-risk.json"
|
|
210
|
+
DIRECT_ADDRESS_RISK_SUMMARY="- direct address_risk skipped: direct endpoint is primitive-only"
|
|
211
|
+
if [[ "$(cat "${RUN_DIR}/direct-high-level-tools.txt")" == "yes" ]]; then
|
|
212
|
+
log "calling direct GraphRAG address_risk"
|
|
213
|
+
npx @modelcontextprotocol/inspector \
|
|
214
|
+
--cli "${MCP_ENDPOINT}" \
|
|
215
|
+
--transport http \
|
|
216
|
+
--header "Authorization: Bearer ${DEBUG_TOKEN}" \
|
|
217
|
+
--header "X-MCP-Debug-Token: ${DEBUG_TOKEN}" \
|
|
218
|
+
--method tools/call \
|
|
219
|
+
--tool-name address_risk \
|
|
220
|
+
--tool-arg "network=${NETWORK}" \
|
|
221
|
+
--tool-arg "address=${UAT_ADDRESS}" \
|
|
222
|
+
--tool-arg include_attachments=true >"${DIRECT_JSON}"
|
|
223
|
+
|
|
224
|
+
node - "${DIRECT_JSON}" <<'NODE'
|
|
225
|
+
const fs = require('node:fs')
|
|
226
|
+
const file = process.argv[2]
|
|
227
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
228
|
+
const errors = []
|
|
229
|
+
const content = data.content || []
|
|
230
|
+
const sc = data.structuredContent || {}
|
|
231
|
+
const graphData = data._meta?.chainInsights?.graph?.data
|
|
232
|
+
const graphArrayKeys = ['app_data', 'nodes', 'edges', 'flows', 'edge_anchors', 'transfers']
|
|
233
|
+
if (data.isError) errors.push('direct address_risk returned isError=true')
|
|
234
|
+
if (content[0]?.type !== 'text') errors.push('direct content[0] is not text')
|
|
235
|
+
if (sc.schema !== 'chain-insights.result.v1') errors.push(`direct structuredContent schema mismatch: ${sc.schema}`)
|
|
236
|
+
for (const key of graphArrayKeys) {
|
|
237
|
+
if (Object.prototype.hasOwnProperty.call(sc, key)) errors.push(`direct structuredContent leaks ${key}`)
|
|
238
|
+
}
|
|
239
|
+
if (!graphData) errors.push('direct _meta.chainInsights.graph.data missing')
|
|
240
|
+
if (graphData?.schema !== 'chain-insights.graph.v1') errors.push(`direct graph schema mismatch: ${graphData?.schema}`)
|
|
241
|
+
for (const key of ['nodes', 'edges', 'flows', 'edge_anchors']) {
|
|
242
|
+
if (!Array.isArray(graphData?.[key])) errors.push(`direct graph ${key} is not an array`)
|
|
243
|
+
}
|
|
244
|
+
if (Object.prototype.hasOwnProperty.call(graphData || {}, 'transfers')) errors.push('direct graph includes transfers')
|
|
245
|
+
if (errors.length) throw new Error(errors.join('; '))
|
|
246
|
+
console.log(`[uat] direct address_risk ok: nodes=${graphData.nodes.length} edges=${graphData.edges.length} flows=${graphData.flows.length} edge_anchors=${graphData.edge_anchors.length}`)
|
|
247
|
+
NODE
|
|
248
|
+
DIRECT_ADDRESS_RISK_SUMMARY="- ${DIRECT_JSON}"
|
|
249
|
+
else
|
|
250
|
+
log "direct GraphRAG high-level tools absent; primitive-only endpoint, skipping direct address_risk check"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
PROXY_TOOLS_JSON="${RUN_DIR}/proxy-tools-list.json"
|
|
254
|
+
log "checking Chain Insights proxy tools/list"
|
|
255
|
+
npx @modelcontextprotocol/inspector \
|
|
256
|
+
--cli node "${CHAIN_INSIGHTS_PROXY}" \
|
|
257
|
+
--transport stdio \
|
|
258
|
+
--method tools/list >"${PROXY_TOOLS_JSON}"
|
|
259
|
+
|
|
260
|
+
node - "${PROXY_TOOLS_JSON}" <<'NODE'
|
|
261
|
+
const fs = require('node:fs')
|
|
262
|
+
const file = process.argv[2]
|
|
263
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
264
|
+
const tools = data.tools || []
|
|
265
|
+
const names = new Set(tools.map((tool) => tool.name))
|
|
266
|
+
const required = ['balance', 'help', 'address_risk', 'track_funds', 'scam_topology', 'network_capabilities', 'graph_query', 'graph_query_batch']
|
|
267
|
+
const missing = required.filter((name) => !names.has(name))
|
|
268
|
+
if (missing.length) throw new Error(`proxy tools/list missing tools: ${missing.join(', ')}`)
|
|
269
|
+
for (const hidden of ['topup', 'trace_funds', 'money_flows_between_exchanges', 'address_connection_risk']) {
|
|
270
|
+
if (names.has(hidden)) throw new Error(`proxy tools/list exposed hidden tool: ${hidden}`)
|
|
271
|
+
}
|
|
272
|
+
if (JSON.stringify(tools).includes('app_data')) throw new Error('proxy tools/list still contains app_data')
|
|
273
|
+
const graphTools = tools.filter((tool) => tool._meta?.ui?.resourceUri === 'ui://chain-insights/graph').map((tool) => tool.name)
|
|
274
|
+
for (const name of ['address_risk', 'track_funds', 'scam_topology']) {
|
|
275
|
+
if (!graphTools.includes(name)) throw new Error(`proxy graph app metadata missing for ${name}`)
|
|
276
|
+
}
|
|
277
|
+
console.log(`[uat] proxy tools/list ok: ${tools.length} tools`)
|
|
278
|
+
NODE
|
|
279
|
+
|
|
280
|
+
PROXY_JSON="${RUN_DIR}/proxy-address-risk.json"
|
|
281
|
+
log "calling Chain Insights proxy address_risk"
|
|
282
|
+
node --input-type=module - "${CHAIN_INSIGHTS_PROXY}" "${NETWORK}" "${UAT_ADDRESS}" "${PROXY_JSON}" <<'NODE'
|
|
283
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
284
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
285
|
+
import fs from 'node:fs'
|
|
286
|
+
|
|
287
|
+
const proxy = process.argv[2]
|
|
288
|
+
const network = process.argv[3]
|
|
289
|
+
const address = process.argv[4]
|
|
290
|
+
const outputFile = process.argv[5]
|
|
291
|
+
const requestTimeoutMs = 5 * 60 * 1000
|
|
292
|
+
|
|
293
|
+
const client = new Client({ name: 'chain-insights-uat', version: '0.0.0' })
|
|
294
|
+
const transport = new StdioClientTransport({
|
|
295
|
+
command: process.execPath,
|
|
296
|
+
args: [proxy],
|
|
297
|
+
env: process.env,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
await client.connect(transport)
|
|
302
|
+
const result = await client.callTool(
|
|
303
|
+
{
|
|
304
|
+
name: 'address_risk',
|
|
305
|
+
arguments: {
|
|
306
|
+
network,
|
|
307
|
+
address,
|
|
308
|
+
include_attachments: true,
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
undefined,
|
|
312
|
+
{
|
|
313
|
+
timeout: requestTimeoutMs,
|
|
314
|
+
maxTotalTimeout: requestTimeoutMs,
|
|
315
|
+
},
|
|
316
|
+
)
|
|
317
|
+
fs.writeFileSync(outputFile, `${JSON.stringify(result, null, 2)}\n`)
|
|
318
|
+
} finally {
|
|
319
|
+
await client.close()
|
|
320
|
+
}
|
|
321
|
+
NODE
|
|
322
|
+
|
|
323
|
+
GRAPH_REPORT_URL="$(
|
|
324
|
+
node - "${PROXY_JSON}" <<'NODE'
|
|
325
|
+
const fs = require('node:fs')
|
|
326
|
+
const file = process.argv[2]
|
|
327
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
328
|
+
const errors = []
|
|
329
|
+
const content = data.content || []
|
|
330
|
+
const sc = data.structuredContent || {}
|
|
331
|
+
const graph = data._meta?.chainInsights?.graph
|
|
332
|
+
if (data.isError) errors.push('proxy address_risk returned isError=true')
|
|
333
|
+
if (content[0]?.type !== 'text') errors.push('proxy content[0] is not text')
|
|
334
|
+
if (sc.schema !== 'chain-insights.result.v1') errors.push(`proxy structuredContent schema mismatch: ${sc.schema}`)
|
|
335
|
+
for (const key of ['app_data', 'nodes', 'edges', 'flows', 'edge_anchors', 'transfers']) {
|
|
336
|
+
if (JSON.stringify(sc).includes(`"${key}"`)) errors.push(`proxy structuredContent leaks ${key}`)
|
|
337
|
+
}
|
|
338
|
+
if (!graph) errors.push('proxy _meta.chainInsights.graph missing')
|
|
339
|
+
if (graph?.data) errors.push('proxy _meta.chainInsights.graph.data leaked')
|
|
340
|
+
if (graph?.schema !== 'chain-insights.graph.v1') errors.push(`proxy graph schema mismatch: ${graph?.schema}`)
|
|
341
|
+
if (graph?.id) errors.push('proxy graph id should not be returned')
|
|
342
|
+
if (!/^http:\/\/127\.0\.0\.1:\d+\/graph-reports\/[A-Za-z0-9._-]+\.graph\.json$/.test(graph?.url || '')) {
|
|
343
|
+
errors.push(`proxy graph url is not a local graph report URL: ${graph?.url}`)
|
|
344
|
+
}
|
|
345
|
+
if (errors.length) throw new Error(errors.join('; '))
|
|
346
|
+
console.error(`[uat] proxy address_risk ok: graph_report=${graph.url}`)
|
|
347
|
+
process.stdout.write(graph.url)
|
|
348
|
+
NODE
|
|
349
|
+
)"
|
|
350
|
+
printf '%s\n' "${GRAPH_REPORT_URL}" >"${RUN_DIR}/graph-report-url.txt"
|
|
351
|
+
|
|
352
|
+
GRAPH_REPORT_JSON="${RUN_DIR}/graph-report.json"
|
|
353
|
+
log "fetching local graph report"
|
|
354
|
+
curl -sf "${GRAPH_REPORT_URL}" >"${GRAPH_REPORT_JSON}"
|
|
355
|
+
|
|
356
|
+
node - "${GRAPH_REPORT_JSON}" <<'NODE'
|
|
357
|
+
const fs = require('node:fs')
|
|
358
|
+
const file = process.argv[2]
|
|
359
|
+
const data = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
360
|
+
const errors = []
|
|
361
|
+
if (data.schema !== 'chain-insights.graph.v1') errors.push(`graph report schema mismatch: ${data.schema}`)
|
|
362
|
+
for (const key of ['nodes', 'edges', 'flows', 'edge_anchors']) {
|
|
363
|
+
if (!Array.isArray(data[key])) errors.push(`graph report ${key} is not an array`)
|
|
364
|
+
}
|
|
365
|
+
if (Object.prototype.hasOwnProperty.call(data, 'transfers')) errors.push('graph report includes transfers')
|
|
366
|
+
if (errors.length) throw new Error(errors.join('; '))
|
|
367
|
+
console.log(`[uat] graph report ok: nodes=${data.nodes.length} edges=${data.edges.length} flows=${data.flows.length} edge_anchors=${data.edge_anchors.length}`)
|
|
368
|
+
NODE
|
|
369
|
+
|
|
370
|
+
GRAPH_QUERY_TEXT="${RUN_DIR}/graph-query-address.txt"
|
|
371
|
+
log "calling Chain Insights CLI graph_query against real MCP"
|
|
372
|
+
(
|
|
373
|
+
cd "${WORKSPACE_ROOT}"
|
|
374
|
+
node "${CHAIN_INSIGHTS_CLI}" mcp call graph_query \
|
|
375
|
+
"network=${NETWORK}" \
|
|
376
|
+
"query=USE live_topology MATCH (n) WHERE n.address = '${UAT_ADDRESS}' RETURN n.labels AS labels, n.address AS address LIMIT 1"
|
|
377
|
+
) >"${GRAPH_QUERY_TEXT}"
|
|
378
|
+
|
|
379
|
+
node - "${GRAPH_QUERY_TEXT}" "${UAT_ADDRESS}" <<'NODE'
|
|
380
|
+
const fs = require('node:fs')
|
|
381
|
+
const file = process.argv[2]
|
|
382
|
+
const address = process.argv[3]
|
|
383
|
+
const text = fs.readFileSync(file, 'utf8').trim()
|
|
384
|
+
const data = JSON.parse(text)
|
|
385
|
+
const first = data.facts?.query?.results?.[0] || data.results?.[0]
|
|
386
|
+
if (!first || first.address !== address) {
|
|
387
|
+
throw new Error(`graph_query did not return expected address ${address}`)
|
|
388
|
+
}
|
|
389
|
+
console.log(`[uat] graph_query ok: ${first.address}`)
|
|
390
|
+
NODE
|
|
391
|
+
|
|
392
|
+
SUMMARY="${RUN_DIR}/summary.txt"
|
|
393
|
+
cat >"${SUMMARY}" <<EOF
|
|
394
|
+
Chain Insights vs GraphRAG MCP UAT PASS
|
|
395
|
+
|
|
396
|
+
Endpoint: ${MCP_ENDPOINT}
|
|
397
|
+
Network: ${NETWORK}
|
|
398
|
+
Address: ${UAT_ADDRESS}
|
|
399
|
+
Graph report URL: ${GRAPH_REPORT_URL}
|
|
400
|
+
|
|
401
|
+
Raw outputs:
|
|
402
|
+
- ${DIRECT_TOOLS_JSON}
|
|
403
|
+
${DIRECT_ADDRESS_RISK_SUMMARY}
|
|
404
|
+
- ${PROXY_TOOLS_JSON}
|
|
405
|
+
- ${PROXY_JSON}
|
|
406
|
+
- ${GRAPH_REPORT_JSON}
|
|
407
|
+
- ${GRAPH_QUERY_TEXT}
|
|
408
|
+
|
|
409
|
+
Workspace:
|
|
410
|
+
- ${WORKSPACE_ROOT}
|
|
411
|
+
EOF
|
|
412
|
+
|
|
413
|
+
log "PASS"
|
|
414
|
+
log "summary: ${SUMMARY}"
|