chain-insights 0.18.19 → 0.22.2
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 +48 -124
- package/bin/cli.js +4 -4
- package/bin/install.cjs +157 -118
- package/bin/mcp-proxy.cjs +7 -5
- package/dist/{action-log-Dfe88TwV.cjs → action-log-HniMMF7f.cjs} +1 -1
- package/dist/action-log-diTHyMbV.mjs.map +1 -1
- package/dist/active-BQopLul8.mjs.map +1 -1
- package/dist/{active-Bj6dohpc.cjs → active-Bc13hJIm.cjs} +1 -1
- package/dist/{app-CJwx_2Rr.mjs → app-7NRCqx_Q.mjs} +2 -2
- package/dist/app-7NRCqx_Q.mjs.map +1 -0
- package/dist/{app-D4Cm69ob.cjs → app-l9JE6bt3.cjs} +5 -5
- package/dist/{artifact-server-sJBFGcum.cjs → artifact-server-CH-asxF5.cjs} +3 -2
- package/dist/{artifact-server-D1D-_t5l.mjs → artifact-server-DoxJr0z8.mjs} +4 -3
- package/dist/artifact-server-DoxJr0z8.mjs.map +1 -0
- package/dist/{capabilities-rLNPG-O2.mjs → capabilities-BZI6qROg.mjs} +32 -19
- package/dist/capabilities-BZI6qROg.mjs.map +1 -0
- package/dist/{capabilities-BR985-bW.cjs → capabilities-CPcrxodx.cjs} +35 -22
- package/dist/cli.cjs +42 -216
- package/dist/cli.mjs +36 -210
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-DLqndaSv.cjs → client-CqAzD519.cjs} +38 -5
- package/dist/{client-BD9FLUt2.mjs → client-D4aM4bzP.mjs} +38 -5
- package/dist/client-D4aM4bzP.mjs.map +1 -0
- package/dist/{config-DE0dUT3P.cjs → config-BVFf3v8-.cjs} +2 -2
- package/dist/{config-DA6KRx60.mjs → config-CnNMdt4M.mjs} +2 -2
- package/dist/config-CnNMdt4M.mjs.map +1 -0
- package/dist/graph-normalizer-BA3Rbd6l.mjs.map +1 -1
- package/dist/graph-reports-B6yoYbPq.mjs.map +1 -1
- package/dist/{graph-reports-DSHSp5HL.cjs → graph-reports-_gqzxvvs.cjs} +2 -2
- package/dist/{html-generator-Ui29DLSD.cjs → html-generator-B9f_GIi2.cjs} +4 -3
- package/dist/{html-generator-ClysZjfY.mjs → html-generator-D2BDzLED.mjs} +3 -2
- package/dist/{html-generator-ClysZjfY.mjs.map → html-generator-D2BDzLED.mjs.map} +1 -1
- package/dist/index.cjs +9 -9
- package/dist/index.d.cts +3 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +9 -9
- package/dist/{init-Jp_AIMZ7.mjs → init-Djhml4ih.mjs} +10 -10
- package/dist/init-Djhml4ih.mjs.map +1 -0
- package/dist/{init-HVwOeOyo.cjs → init-DvrKxK15.cjs} +10 -10
- package/dist/limits-D3Mn-gig.mjs.map +1 -1
- package/dist/mcp-endpoint-DbWtLcba.mjs.map +1 -1
- package/dist/mcp-proxy.cjs +23 -42
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +17 -36
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{merge-CJRZpuZ4.mjs → merge-BZw-pMPN.mjs} +2 -2
- package/dist/merge-BZw-pMPN.mjs.map +1 -0
- package/dist/{merge-B_5kxbGv.cjs → merge-C52eaAGU.cjs} +1 -1
- package/dist/{output-root-C1PFNCqb.cjs → output-root-BewoQaTx.cjs} +2 -2
- package/dist/{public-tools-CgiKgYgf.cjs → public-tools-BFs_7U_Z.cjs} +8 -7
- package/dist/{public-tools-B9_wXs2o.mjs → public-tools-BjqZGGAC.mjs} +7 -6
- package/dist/public-tools-BjqZGGAC.mjs.map +1 -0
- package/dist/{rolldown-runtime-DakpK96I.cjs → rolldown-runtime-C0BPl7ul.cjs} +1 -1
- package/dist/{schema-DF0aOXII.cjs → schema-BpeV3xDV.cjs} +5 -2
- package/dist/{schema-0BVyF-aL.mjs → schema-DOJA2nNi.mjs} +5 -2
- package/dist/schema-DOJA2nNi.mjs.map +1 -0
- package/dist/{schema-cache-DwDvPy4e.mjs → schema-cache-DJdU4MIP.mjs} +2 -2
- package/dist/{schema-cache-DwDvPy4e.mjs.map → schema-cache-DJdU4MIP.mjs.map} +1 -1
- package/dist/{schema-cache-CX0r2UQp.cjs → schema-cache-n7Gbhvsj.cjs} +2 -2
- package/dist/{server-ta1oyJ2a.cjs → server-CmvzYu7v.cjs} +4 -3
- package/dist/{server-BXj6BRI1.mjs → server-mDSRgk1X.mjs} +4 -3
- package/dist/server-mDSRgk1X.mjs.map +1 -0
- package/dist/tool-visibility-C0ALejXI.mjs.map +1 -1
- package/dist/{tool-visibility-DMIEexib.cjs → tool-visibility-DZBAP62b.cjs} +1 -1
- package/dist/{tools-CWwX4yth.mjs → tools-BKLnlBG1.mjs} +8 -6
- package/dist/tools-BKLnlBG1.mjs.map +1 -0
- package/dist/{tools-BPfUpCUR.cjs → tools-lPY6k-vJ.cjs} +8 -6
- package/dist/{topup-server-VGl8-Fz5.cjs → topup-server-DAqmAuu1.cjs} +23 -21
- package/dist/{topup-server-D2X7My7i.mjs → topup-server-rqI7bqem.mjs} +23 -21
- package/dist/topup-server-rqI7bqem.mjs.map +1 -0
- package/dist/update-CJUfGCxs.mjs.map +1 -1
- package/dist/{usage-status-H1xgCu99.cjs → usage-status-BdwwgYsu.cjs} +2 -2
- package/dist/{usage-status-D2uosC7s.mjs → usage-status-CHiOPA-2.mjs} +2 -2
- package/dist/usage-status-CHiOPA-2.mjs.map +1 -0
- package/dist/version-BA3J8hu4.mjs.map +1 -1
- package/dist/{viz-HSwLcKr6.cjs → viz-BJ_kyRjI.cjs} +7 -5
- package/dist/{viz-Cq1jSWSH.mjs → viz-Uh_EF-qB.mjs} +6 -4
- package/dist/viz-Uh_EF-qB.mjs.map +1 -0
- package/dist/{wallet-C2yVjcSY.cjs → wallet-BEvHrHi3.cjs} +3 -2
- package/dist/{wallet-BrTTnf7w.mjs → wallet-fMbhvKXT.mjs} +3 -2
- package/dist/wallet-fMbhvKXT.mjs.map +1 -0
- package/docs/architecture.md +18 -18
- package/docs/debugging.md +4 -49
- package/docs/development.md +0 -27
- package/docs/graph-query-compatibility.md +69 -58
- package/docs/graph-tools.md +16 -18
- package/docs/investigation-workspaces.md +0 -35
- package/docs/mcp-proxy.md +15 -17
- package/docs/search-limits.md +8 -8
- package/docs/stability.md +4 -5
- package/package.json +1 -5
- package/skills/chain-insights-address-risk/SKILL.md +20 -77
- package/skills/chain-insights-cypher/SKILL.md +92 -213
- package/skills/chain-insights-cypher/agents/openai.yaml +3 -3
- package/skills/chain-insights-schema-bittensor/SKILL.md +99 -0
- package/skills/chain-insights-schema-evm/SKILL.md +104 -0
- package/dist/app-CJwx_2Rr.mjs.map +0 -1
- package/dist/artifact-server-D1D-_t5l.mjs.map +0 -1
- package/dist/capabilities-rLNPG-O2.mjs.map +0 -1
- package/dist/cases-C1XewLdx.mjs +0 -155
- package/dist/cases-C1XewLdx.mjs.map +0 -1
- package/dist/cases-CVtc7Rf8.cjs +0 -160
- package/dist/client-BD9FLUt2.mjs.map +0 -1
- package/dist/config-CwIb-tav.mjs +0 -33
- package/dist/config-CwIb-tav.mjs.map +0 -1
- package/dist/config-Cy-qzx4W.cjs +0 -33
- package/dist/config-DA6KRx60.mjs.map +0 -1
- package/dist/init-9jY5rCcH.cjs +0 -24
- package/dist/init-BiRfMQcA.mjs +0 -26
- package/dist/init-BiRfMQcA.mjs.map +0 -1
- package/dist/init-Jp_AIMZ7.mjs.map +0 -1
- package/dist/lock-Cs-a3Ng1.cjs +0 -38
- package/dist/lock-D_sWGS7W.mjs +0 -40
- package/dist/lock-D_sWGS7W.mjs.map +0 -1
- package/dist/merge-CJRZpuZ4.mjs.map +0 -1
- package/dist/paths-CffVTUCv.cjs +0 -31
- package/dist/paths-_3isihzD.mjs +0 -21
- package/dist/paths-_3isihzD.mjs.map +0 -1
- package/dist/public-tools-B9_wXs2o.mjs.map +0 -1
- package/dist/render-TmSq0f4f.cjs +0 -207
- package/dist/render-mxE0Tdsf.mjs +0 -206
- package/dist/render-mxE0Tdsf.mjs.map +0 -1
- package/dist/report-BFXFrJmj.mjs +0 -36
- package/dist/report-BFXFrJmj.mjs.map +0 -1
- package/dist/report-BwKQqOCR.cjs +0 -36
- package/dist/runner-CSss8Hvy.mjs +0 -33
- package/dist/runner-CSss8Hvy.mjs.map +0 -1
- package/dist/runner-aErdz2zn.cjs +0 -31
- package/dist/schema-0BVyF-aL.mjs.map +0 -1
- package/dist/server-BXj6BRI1.mjs.map +0 -1
- package/dist/tools-CWwX4yth.mjs.map +0 -1
- package/dist/topup-server-D2X7My7i.mjs.map +0 -1
- package/dist/usage-status-D2uosC7s.mjs.map +0 -1
- package/dist/viz-Cq1jSWSH.mjs.map +0 -1
- package/dist/wallet-BrTTnf7w.mjs.map +0 -1
- package/docs/monitoring.md +0 -279
- package/skills/chain-insights-bittensor-cypher/SKILL.md +0 -240
- package/skills/chain-insights-bittensor-cypher/agents/openai.yaml +0 -4
- package/skills/chain-insights-cypher/references/gql-translation-matrix.md +0 -22
- package/skills/chain-insights-cypher/references/memgraph-examples.md +0 -188
- package/skills/chain-insights-developer-experience/SKILL.md +0 -140
- package/skills/chain-insights-investigation/SKILL.md +0 -168
- package/skills/chain-insights-investigation/agents/openai.yaml +0 -4
- package/skills/chain-insights-investigation/scripts/run-target-uat.sh +0 -179
- package/skills/chain-insights-monitoring/SKILL.md +0 -160
- package/skills/chain-insights-monitoring/references/pm2-scheduling.md +0 -100
- package/skills/ci-status/SKILL.md +0 -39
- package/skills/test-chain-insights-graph/SKILL.md +0 -110
- package/skills/test-chain-insights-graph/agents/openai.yaml +0 -4
- package/skills/test-chain-insights-graph/scripts/run-uat.sh +0 -600
package/bin/install.cjs
CHANGED
|
@@ -1,101 +1,131 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict'
|
|
2
|
+
'use strict'
|
|
3
3
|
|
|
4
4
|
// Chain Insights installer — CJS, stdlib-only.
|
|
5
5
|
// Runs before node_modules exists; zero npm imports allowed.
|
|
6
6
|
// Extension is .cjs (not .js) because package.json has "type": "module" —
|
|
7
7
|
// a .js file would be treated as ESM and require() calls would crash.
|
|
8
8
|
|
|
9
|
-
const fs
|
|
10
|
-
const path = require('path')
|
|
11
|
-
const os
|
|
9
|
+
const fs = require('fs')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const os = require('os')
|
|
12
12
|
|
|
13
13
|
// ANSI colors — no chalk
|
|
14
|
-
const cyan
|
|
15
|
-
const green = '\x1b[32m'
|
|
16
|
-
const bold
|
|
17
|
-
const dim
|
|
18
|
-
const reset = '\x1b[0m'
|
|
14
|
+
const cyan = '\x1b[36m'
|
|
15
|
+
const green = '\x1b[32m'
|
|
16
|
+
const bold = '\x1b[1m'
|
|
17
|
+
const dim = '\x1b[2m'
|
|
18
|
+
const reset = '\x1b[0m'
|
|
19
19
|
|
|
20
20
|
// Parse args
|
|
21
|
-
const args
|
|
22
|
-
const hasClaude = args.includes('--claude')
|
|
23
|
-
const hasCodex
|
|
24
|
-
const hasHermes = args.includes('--hermes')
|
|
25
|
-
const hasLocal
|
|
21
|
+
const args = process.argv.slice(2)
|
|
22
|
+
const hasClaude = args.includes('--claude')
|
|
23
|
+
const hasCodex = args.includes('--codex')
|
|
24
|
+
const hasHermes = args.includes('--hermes')
|
|
25
|
+
const hasLocal = args.includes('--local')
|
|
26
26
|
|
|
27
27
|
if (!hasClaude && !hasCodex && !hasHermes && !hasLocal) {
|
|
28
|
-
console.log(`\n${bold}chain-insights installer${reset}`)
|
|
29
|
-
console.log(`\nUsage: node bin/install.cjs --claude | --codex | --hermes`)
|
|
30
|
-
console.log(` ${cyan}--claude${reset} Install Claude Code skills globally to ~/.claude/skills/`)
|
|
31
|
-
console.log(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log(
|
|
35
|
-
|
|
28
|
+
console.log(`\n${bold}chain-insights installer${reset}`)
|
|
29
|
+
console.log(`\nUsage: node bin/install.cjs --claude | --codex | --hermes`)
|
|
30
|
+
console.log(` ${cyan}--claude${reset} Install Claude Code skills globally to ~/.claude/skills/`)
|
|
31
|
+
console.log(
|
|
32
|
+
` ${cyan}--codex${reset} Install Codex skills globally to ~/.codex/skills/ and register MCP`
|
|
33
|
+
)
|
|
34
|
+
console.log(
|
|
35
|
+
` ${cyan}--hermes${reset} Install Hermes skills globally to ~/.hermes/skills/chain-insights/ and register MCP`
|
|
36
|
+
)
|
|
37
|
+
console.log(
|
|
38
|
+
` ${cyan}--local${reset} Install skills locally to ./.claude/commands/chain-insights/`
|
|
39
|
+
)
|
|
40
|
+
console.log('')
|
|
41
|
+
process.exit(0)
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
const homeDir
|
|
39
|
-
const dataDir
|
|
40
|
-
const configPath = path.join(dataDir, 'config.json')
|
|
41
|
-
const srcSkillsDir = path.join(__dirname, '..', 'skills')
|
|
44
|
+
const homeDir = os.homedir()
|
|
45
|
+
const dataDir = path.join(homeDir, '.chain-insights')
|
|
46
|
+
const configPath = path.join(dataDir, 'config.json')
|
|
47
|
+
const srcSkillsDir = path.join(__dirname, '..', 'skills')
|
|
48
|
+
const PUBLIC_SKILL_NAMES = Object.freeze([
|
|
49
|
+
'chain-insights-address-risk',
|
|
50
|
+
'chain-insights-cypher',
|
|
51
|
+
'chain-insights-schema-bittensor',
|
|
52
|
+
'chain-insights-schema-evm',
|
|
53
|
+
])
|
|
54
|
+
const RETIRED_SKILL_NAMES = Object.freeze([
|
|
55
|
+
'chain-insights-bittensor-cypher',
|
|
56
|
+
'chain-insights-developer-experience',
|
|
57
|
+
'chain-insights-investigation',
|
|
58
|
+
'chain-insights-monitoring',
|
|
59
|
+
'ci-status',
|
|
60
|
+
'test-chain-insights-graph',
|
|
61
|
+
])
|
|
42
62
|
|
|
43
63
|
// Determine skills targets
|
|
44
|
-
const skillsTargets = []
|
|
45
|
-
if (hasClaude)
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
if (
|
|
64
|
+
const skillsTargets = []
|
|
65
|
+
if (hasClaude)
|
|
66
|
+
skillsTargets.push({ name: 'Claude Code', dir: path.join(homeDir, '.claude', 'skills') })
|
|
67
|
+
if (hasCodex) skillsTargets.push({ name: 'Codex', dir: path.join(homeDir, '.codex', 'skills') })
|
|
68
|
+
if (hasHermes)
|
|
69
|
+
skillsTargets.push({
|
|
70
|
+
name: 'Hermes',
|
|
71
|
+
dir: path.join(homeDir, '.hermes', 'skills', 'chain-insights'),
|
|
72
|
+
})
|
|
73
|
+
if (hasLocal)
|
|
74
|
+
skillsTargets.push({
|
|
75
|
+
name: 'Local Claude commands',
|
|
76
|
+
dir: path.join(process.cwd(), '.claude', 'commands', 'chain-insights'),
|
|
77
|
+
})
|
|
49
78
|
|
|
50
79
|
// ─── 1. Copy agent skills ─────────────────────────────────────────────────
|
|
51
80
|
|
|
52
81
|
function copyCommandsAsClaudeSkills(srcDir, targetDir) {
|
|
53
82
|
if (!fs.existsSync(srcDir)) {
|
|
54
|
-
|
|
55
|
-
return;
|
|
83
|
+
throw new Error(`Skills source not found: ${srcDir}`)
|
|
56
84
|
}
|
|
57
85
|
|
|
58
|
-
fs.mkdirSync(targetDir, { recursive: true })
|
|
86
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
59
87
|
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// we ship is removed and recopied, and nothing else is touched.
|
|
88
|
+
// The target is shared with user-owned skills. Remove only the exact retired
|
|
89
|
+
// names from older Chain Insights releases, then replace the reviewed set.
|
|
90
|
+
// Never delete by prefix or enumerate arbitrary source directories.
|
|
64
91
|
|
|
65
92
|
const copyTree = (src, dest) => {
|
|
66
|
-
const stat = fs.statSync(src)
|
|
93
|
+
const stat = fs.statSync(src)
|
|
67
94
|
if (stat.isDirectory()) {
|
|
68
|
-
fs.mkdirSync(dest, { recursive: true })
|
|
95
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
69
96
|
for (const child of fs.readdirSync(src)) {
|
|
70
|
-
copyTree(path.join(src, child), path.join(dest, child))
|
|
97
|
+
copyTree(path.join(src, child), path.join(dest, child))
|
|
71
98
|
}
|
|
72
|
-
return
|
|
99
|
+
return
|
|
73
100
|
}
|
|
74
101
|
if (stat.isFile()) {
|
|
75
|
-
fs.copyFileSync(src, dest)
|
|
102
|
+
fs.copyFileSync(src, dest)
|
|
76
103
|
}
|
|
77
|
-
}
|
|
104
|
+
}
|
|
78
105
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
106
|
+
for (const skillName of RETIRED_SKILL_NAMES) {
|
|
107
|
+
fs.rmSync(path.join(targetDir, skillName), { recursive: true, force: true })
|
|
108
|
+
}
|
|
82
109
|
|
|
83
|
-
for (const
|
|
84
|
-
const skillSrc
|
|
85
|
-
const skillDest = path.join(targetDir,
|
|
86
|
-
fs.
|
|
87
|
-
|
|
110
|
+
for (const skillName of PUBLIC_SKILL_NAMES) {
|
|
111
|
+
const skillSrc = path.join(srcDir, skillName)
|
|
112
|
+
const skillDest = path.join(targetDir, skillName)
|
|
113
|
+
if (!fs.existsSync(skillSrc) || !fs.statSync(skillSrc).isDirectory()) {
|
|
114
|
+
throw new Error(`Reviewed skill source not found: ${skillSrc}`)
|
|
115
|
+
}
|
|
116
|
+
fs.rmSync(skillDest, { recursive: true, force: true })
|
|
117
|
+
copyTree(skillSrc, skillDest)
|
|
88
118
|
}
|
|
89
119
|
}
|
|
90
120
|
|
|
91
121
|
for (const target of skillsTargets) {
|
|
92
|
-
copyCommandsAsClaudeSkills(srcSkillsDir, target.dir)
|
|
122
|
+
copyCommandsAsClaudeSkills(srcSkillsDir, target.dir)
|
|
93
123
|
}
|
|
94
124
|
|
|
95
125
|
// ─── 2. Create ~/.chain-insights/ config directory ────────────────────────
|
|
96
126
|
|
|
97
127
|
if (!fs.existsSync(dataDir)) {
|
|
98
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
128
|
+
fs.mkdirSync(dataDir, { recursive: true })
|
|
99
129
|
}
|
|
100
130
|
|
|
101
131
|
// ─── 3. Write default config.json if absent ───────────────────────────────
|
|
@@ -104,20 +134,20 @@ if (!fs.existsSync(configPath)) {
|
|
|
104
134
|
const defaultConfig = {
|
|
105
135
|
graphMcpEndpoint: 'http://127.0.0.1:8012/mcp',
|
|
106
136
|
walletAddress: '',
|
|
107
|
-
graphMcpMode:
|
|
108
|
-
serverPort:
|
|
109
|
-
dataDir:
|
|
110
|
-
version:
|
|
111
|
-
}
|
|
112
|
-
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8')
|
|
137
|
+
graphMcpMode: 'paid',
|
|
138
|
+
serverPort: 4321,
|
|
139
|
+
dataDir: dataDir,
|
|
140
|
+
version: '1',
|
|
141
|
+
}
|
|
142
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8')
|
|
113
143
|
// Owner-readable only — config may contain MCP auth token (ASVS L1 V4.3.2 / T-02-01)
|
|
114
|
-
fs.chmodSync(configPath, 0o600)
|
|
144
|
+
fs.chmodSync(configPath, 0o600)
|
|
115
145
|
}
|
|
116
146
|
|
|
117
147
|
// ─── 4. Register MCP proxy in supported clients ───────────────────────────
|
|
118
148
|
|
|
119
|
-
const proxyBinPath = path.resolve(__dirname, 'mcp-proxy.cjs')
|
|
120
|
-
const { execFileSync } = require('child_process')
|
|
149
|
+
const proxyBinPath = path.resolve(__dirname, 'mcp-proxy.cjs')
|
|
150
|
+
const { execFileSync } = require('child_process')
|
|
121
151
|
|
|
122
152
|
if (hasClaude) {
|
|
123
153
|
try {
|
|
@@ -125,72 +155,74 @@ if (hasClaude) {
|
|
|
125
155
|
'claude',
|
|
126
156
|
['mcp', 'add', 'chain-insights-proxy', '--scope', 'user', '--', 'node', proxyBinPath],
|
|
127
157
|
{ stdio: 'pipe' }
|
|
128
|
-
)
|
|
129
|
-
console.log(` ${cyan}Claude MCP:${reset} registered (chain-insights-proxy) at ${proxyBinPath}`)
|
|
158
|
+
)
|
|
159
|
+
console.log(` ${cyan}Claude MCP:${reset} registered (chain-insights-proxy) at ${proxyBinPath}`)
|
|
130
160
|
} catch {
|
|
131
|
-
console.log(
|
|
161
|
+
console.log(
|
|
162
|
+
` ${dim}Claude MCP:${reset} run manually: claude mcp add chain-insights-proxy --scope user -- node ${proxyBinPath}`
|
|
163
|
+
)
|
|
132
164
|
}
|
|
133
165
|
}
|
|
134
166
|
|
|
135
167
|
function tomlQuoted(value) {
|
|
136
|
-
return JSON.stringify(value)
|
|
168
|
+
return JSON.stringify(value)
|
|
137
169
|
}
|
|
138
170
|
|
|
139
171
|
function installCodexMcp(configFile, proxyPath) {
|
|
140
|
-
fs.mkdirSync(path.dirname(configFile), { recursive: true })
|
|
141
|
-
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : ''
|
|
172
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true })
|
|
173
|
+
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : ''
|
|
142
174
|
const block = [
|
|
143
175
|
'[mcp_servers.chain-insights]',
|
|
144
176
|
'command = "node"',
|
|
145
177
|
`args = [${tomlQuoted(proxyPath)}]`,
|
|
146
178
|
'',
|
|
147
|
-
].join('\n')
|
|
179
|
+
].join('\n')
|
|
148
180
|
|
|
149
|
-
const heading = '[mcp_servers.chain-insights]'
|
|
150
|
-
const start = content.indexOf(heading)
|
|
181
|
+
const heading = '[mcp_servers.chain-insights]'
|
|
182
|
+
const start = content.indexOf(heading)
|
|
151
183
|
if (start >= 0) {
|
|
152
|
-
let end = content.length
|
|
153
|
-
const rest = content.slice(start + heading.length)
|
|
154
|
-
const nextSection = rest.search(/\n\[/)
|
|
155
|
-
if (nextSection >= 0) end = start + heading.length + nextSection + 1
|
|
156
|
-
content = `${content.slice(0, start)}${block}${content.slice(end)}
|
|
184
|
+
let end = content.length
|
|
185
|
+
const rest = content.slice(start + heading.length)
|
|
186
|
+
const nextSection = rest.search(/\n\[/)
|
|
187
|
+
if (nextSection >= 0) end = start + heading.length + nextSection + 1
|
|
188
|
+
content = `${content.slice(0, start)}${block}${content.slice(end)}`
|
|
157
189
|
} else {
|
|
158
|
-
const separator = content.endsWith('\n') || content.length === 0 ? '' : '\n'
|
|
159
|
-
content = `${content}${separator}\n${block}
|
|
190
|
+
const separator = content.endsWith('\n') || content.length === 0 ? '' : '\n'
|
|
191
|
+
content = `${content}${separator}\n${block}`
|
|
160
192
|
}
|
|
161
193
|
|
|
162
|
-
fs.writeFileSync(configFile, content, 'utf8')
|
|
194
|
+
fs.writeFileSync(configFile, content, 'utf8')
|
|
163
195
|
}
|
|
164
196
|
|
|
165
197
|
if (hasCodex) {
|
|
166
|
-
const codexConfig = path.join(homeDir, '.codex', 'config.toml')
|
|
167
|
-
installCodexMcp(codexConfig, proxyBinPath)
|
|
168
|
-
console.log(` ${cyan}Codex MCP:${reset} registered in ${codexConfig}`)
|
|
198
|
+
const codexConfig = path.join(homeDir, '.codex', 'config.toml')
|
|
199
|
+
installCodexMcp(codexConfig, proxyBinPath)
|
|
200
|
+
console.log(` ${cyan}Codex MCP:${reset} registered in ${codexConfig}`)
|
|
169
201
|
}
|
|
170
202
|
|
|
171
203
|
function yamlQuoted(value) {
|
|
172
|
-
return JSON.stringify(value)
|
|
204
|
+
return JSON.stringify(value)
|
|
173
205
|
}
|
|
174
206
|
|
|
175
207
|
function findTopLevelSectionEnd(lines, start) {
|
|
176
208
|
for (let i = start + 1; i < lines.length; i++) {
|
|
177
|
-
const line = lines[i]
|
|
178
|
-
if (line.trim() === '') continue
|
|
179
|
-
if (!line.startsWith(' ') && !line.startsWith('\t')) return i
|
|
209
|
+
const line = lines[i]
|
|
210
|
+
if (line.trim() === '') continue
|
|
211
|
+
if (!line.startsWith(' ') && !line.startsWith('\t')) return i
|
|
180
212
|
}
|
|
181
|
-
return lines.length
|
|
213
|
+
return lines.length
|
|
182
214
|
}
|
|
183
215
|
|
|
184
216
|
function findHermesServerEnd(lines, start, sectionEnd) {
|
|
185
217
|
for (let i = start + 1; i < sectionEnd; i++) {
|
|
186
|
-
const line = lines[i]
|
|
187
|
-
if (/^ [^ ].*:/.test(line)) return i
|
|
218
|
+
const line = lines[i]
|
|
219
|
+
if (/^ [^ ].*:/.test(line)) return i
|
|
188
220
|
}
|
|
189
|
-
return sectionEnd
|
|
221
|
+
return sectionEnd
|
|
190
222
|
}
|
|
191
223
|
|
|
192
224
|
function installHermesMcp(configFile, proxyPath) {
|
|
193
|
-
fs.mkdirSync(path.dirname(configFile), { recursive: true })
|
|
225
|
+
fs.mkdirSync(path.dirname(configFile), { recursive: true })
|
|
194
226
|
|
|
195
227
|
const serverBlock = [
|
|
196
228
|
' chain-insights:',
|
|
@@ -198,50 +230,57 @@ function installHermesMcp(configFile, proxyPath) {
|
|
|
198
230
|
' args:',
|
|
199
231
|
` - ${yamlQuoted(proxyPath)}`,
|
|
200
232
|
' enabled: true',
|
|
201
|
-
]
|
|
233
|
+
]
|
|
202
234
|
|
|
203
|
-
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : ''
|
|
235
|
+
let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : ''
|
|
204
236
|
if (/^mcp_servers:\s*\{\s*\}\s*$/m.test(content)) {
|
|
205
|
-
content = content.replace(
|
|
206
|
-
|
|
207
|
-
|
|
237
|
+
content = content.replace(
|
|
238
|
+
/^mcp_servers:\s*\{\s*\}\s*$/m,
|
|
239
|
+
['mcp_servers:', ...serverBlock].join('\n')
|
|
240
|
+
)
|
|
241
|
+
fs.writeFileSync(configFile, content.endsWith('\n') ? content : `${content}\n`, 'utf8')
|
|
242
|
+
return
|
|
208
243
|
}
|
|
209
244
|
|
|
210
|
-
const lines = content.length ? content.split(/\r?\n/) : []
|
|
211
|
-
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop()
|
|
245
|
+
const lines = content.length ? content.split(/\r?\n/) : []
|
|
246
|
+
if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop()
|
|
212
247
|
|
|
213
|
-
const sectionStart = lines.findIndex(line => line === 'mcp_servers:')
|
|
248
|
+
const sectionStart = lines.findIndex((line) => line === 'mcp_servers:')
|
|
214
249
|
if (sectionStart < 0) {
|
|
215
|
-
if (lines.length > 0) lines.push('')
|
|
216
|
-
lines.push('mcp_servers:', ...serverBlock)
|
|
217
|
-
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8')
|
|
218
|
-
return
|
|
250
|
+
if (lines.length > 0) lines.push('')
|
|
251
|
+
lines.push('mcp_servers:', ...serverBlock)
|
|
252
|
+
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8')
|
|
253
|
+
return
|
|
219
254
|
}
|
|
220
255
|
|
|
221
|
-
const sectionEnd = findTopLevelSectionEnd(lines, sectionStart)
|
|
222
|
-
const serverStart = lines.findIndex(
|
|
256
|
+
const sectionEnd = findTopLevelSectionEnd(lines, sectionStart)
|
|
257
|
+
const serverStart = lines.findIndex(
|
|
258
|
+
(line, index) => index > sectionStart && index < sectionEnd && line === ' chain-insights:'
|
|
259
|
+
)
|
|
223
260
|
if (serverStart >= 0) {
|
|
224
|
-
const serverEnd = findHermesServerEnd(lines, serverStart, sectionEnd)
|
|
225
|
-
lines.splice(serverStart, serverEnd - serverStart, ...serverBlock)
|
|
261
|
+
const serverEnd = findHermesServerEnd(lines, serverStart, sectionEnd)
|
|
262
|
+
lines.splice(serverStart, serverEnd - serverStart, ...serverBlock)
|
|
226
263
|
} else {
|
|
227
|
-
lines.splice(sectionStart + 1, 0, ...serverBlock)
|
|
264
|
+
lines.splice(sectionStart + 1, 0, ...serverBlock)
|
|
228
265
|
}
|
|
229
266
|
|
|
230
|
-
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8')
|
|
267
|
+
fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8')
|
|
231
268
|
}
|
|
232
269
|
|
|
233
270
|
if (hasHermes) {
|
|
234
|
-
const hermesConfig = path.join(homeDir, '.hermes', 'config.yaml')
|
|
235
|
-
installHermesMcp(hermesConfig, proxyBinPath)
|
|
236
|
-
console.log(` ${cyan}Hermes MCP:${reset} registered in ${hermesConfig}`)
|
|
271
|
+
const hermesConfig = path.join(homeDir, '.hermes', 'config.yaml')
|
|
272
|
+
installHermesMcp(hermesConfig, proxyBinPath)
|
|
273
|
+
console.log(` ${cyan}Hermes MCP:${reset} registered in ${hermesConfig}`)
|
|
237
274
|
}
|
|
238
275
|
|
|
239
276
|
// ─── 5. Print installation summary ────────────────────────────────────────
|
|
240
277
|
|
|
241
|
-
console.log(`\n${bold}${green}Chain Insights installed${reset}`)
|
|
278
|
+
console.log(`\n${bold}${green}Chain Insights installed${reset}`)
|
|
242
279
|
for (const target of skillsTargets) {
|
|
243
|
-
console.log(` ${cyan}${target.name} skills:${reset} ${target.dir}`)
|
|
280
|
+
console.log(` ${cyan}${target.name} skills:${reset} ${target.dir}`)
|
|
244
281
|
}
|
|
245
|
-
console.log(` ${cyan}Config:${reset} ${configPath}`)
|
|
246
|
-
console.log(` ${cyan}Data dir:${reset} ${dataDir}`)
|
|
247
|
-
console.log(
|
|
282
|
+
console.log(` ${cyan}Config:${reset} ${configPath}`)
|
|
283
|
+
console.log(` ${cyan}Data dir:${reset} ${dataDir}`)
|
|
284
|
+
console.log(
|
|
285
|
+
`\n${dim}Run ${reset}${cyan}chain-insights status${reset}${dim} to verify the installation.${reset}\n`
|
|
286
|
+
)
|
package/bin/mcp-proxy.cjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
'use strict'
|
|
2
|
+
'use strict'
|
|
3
3
|
|
|
4
4
|
// CJS shim for stdio MCP proxy — spawned by Claude Code via ~/.claude.json mcpServers config.
|
|
5
5
|
// IMPORTANT: Do not write to stdout here — stdout is owned by StdioServerTransport.
|
|
6
6
|
// Use process.stderr.write() exclusively for pre-load errors.
|
|
7
|
-
import('../dist/mcp-proxy.mjs')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
})
|
|
7
|
+
import('../dist/mcp-proxy.mjs')
|
|
8
|
+
.then(({ createProxy }) => createProxy())
|
|
9
|
+
.catch((err) => {
|
|
10
|
+
process.stderr.write(`Failed to load chain-insights MCP proxy: ${err.message}\n`)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
2
|
let node_path = require("node:path");
|
|
3
3
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
4
4
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-log-diTHyMbV.mjs","names":[],"sources":["../src/mcp/action-log.ts"],"sourcesContent":["// src/mcp/action-log.ts\n// Optional append-only record of every MCP tool invocation, for operators\n// running cia unattended who need to audit what ran and why a result looked\n// the way it did. Off unless CIA_ACTION_LOG names a path.\n//\n// `warnings` and `search_limits` are captured deliberately: they are how a\n// reader tells \"found nothing\" from \"hit a cap\", which is invisible in the\n// result alone.\nimport { appendFile, mkdir } from 'node:fs/promises'\nimport path from 'node:path'\n\nexport interface ActionLogEntry {\n timestamp: number\n tool: string\n args: Record<string, unknown>\n outcome: 'ok' | 'error'\n duration_ms: number\n result_counts?: Record<string, number>\n warnings?: string[]\n search_limits?: Record<string, unknown>\n error?: string\n}\n\nexport function actionLogPath(): string | undefined {\n const configured = process.env.CIA_ACTION_LOG?.trim()\n return configured ? configured : undefined\n}\n\n// Some special filesystems (notably /proc) answer mkdir on a child with\n// ENOENT even though the parent visibly exists. Node's fs.mkdir(dir,\n// {recursive: true}) treats that as a transient race and retries with no\n// cap, which spins forever rather than rejecting — confirmed via strace\n// (an unbounded, rapid mkdir/statx loop; the event loop stays live and the\n// process never blocks, it just never stops). Walking the path ourselves,\n// one plain mkdir per component with no retry, makes every case settle in\n// a bounded number of syscalls: EEXIST is expected and skipped, anything\n// else (including that ENOENT-on-existing-parent case) fails once instead\n// of retrying.\nasync function ensureDir(dir: string): Promise<void> {\n const segments = dir.split(path.sep).filter(Boolean)\n let current = path.isAbsolute(dir) ? path.sep : ''\n for (const segment of segments) {\n current = current ? path.join(current, segment) : segment\n try {\n await mkdir(current)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') continue\n throw err\n }\n }\n}\n\n// A residual, undemonstrated risk remains distinct from the mkdir retry loop\n// above: a single mkdir/appendFile syscall genuinely wedged on a stalled\n// filesystem (e.g. a hung network mount) blocks like any normal blocking I/O\n// would. Node's fs promises do not support cancelling an in-flight mkdir, so\n// a bounded wait here only frees the *caller* — the wedged operation itself\n// keeps running in the background until the OS/filesystem gives up on its\n// own. That is an accepted, documented limitation, not a bug this module can\n// fix from userland; it is unrelated to (and much rarer than) the retry-loop\n// case ensureDir() eliminates above.\nconst WRITE_TIMEOUT_MS = 2000\n\nasync function writeEntry(file: string, entry: ActionLogEntry): Promise<void> {\n await ensureDir(path.dirname(file))\n await appendFile(file, JSON.stringify(entry) + '\\n', 'utf8')\n}\n\n// Never throws, and never hangs beyond WRITE_TIMEOUT_MS. A failure (or a\n// stall) recording an action must not fail (or stall) the action:\n// observability that can break the thing it observes is worse than none.\nexport async function appendActionLog(entry: ActionLogEntry): Promise<void> {\n const file = actionLogPath()\n if (!file) return\n try {\n await Promise.race([\n writeEntry(file, entry),\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, WRITE_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n } catch {\n // Intentionally swallowed.\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n// Exported so BOTH client paths log identically: the MCP proxy's remote client\n// and the CLI's own client. They are separate clients, and wiring only one is how\n// an unattended instance ends up with an empty log while the mechanism 'works'.\nexport function actionLogSignalsFromResult(result: unknown): {
|
|
1
|
+
{"version":3,"file":"action-log-diTHyMbV.mjs","names":[],"sources":["../src/mcp/action-log.ts"],"sourcesContent":["// src/mcp/action-log.ts\n// Optional append-only record of every MCP tool invocation, for operators\n// running cia unattended who need to audit what ran and why a result looked\n// the way it did. Off unless CIA_ACTION_LOG names a path.\n//\n// `warnings` and `search_limits` are captured deliberately: they are how a\n// reader tells \"found nothing\" from \"hit a cap\", which is invisible in the\n// result alone.\nimport { appendFile, mkdir } from 'node:fs/promises'\nimport path from 'node:path'\n\nexport interface ActionLogEntry {\n timestamp: number\n tool: string\n args: Record<string, unknown>\n outcome: 'ok' | 'error'\n duration_ms: number\n result_counts?: Record<string, number>\n warnings?: string[]\n search_limits?: Record<string, unknown>\n error?: string\n}\n\nexport function actionLogPath(): string | undefined {\n const configured = process.env.CIA_ACTION_LOG?.trim()\n return configured ? configured : undefined\n}\n\n// Some special filesystems (notably /proc) answer mkdir on a child with\n// ENOENT even though the parent visibly exists. Node's fs.mkdir(dir,\n// {recursive: true}) treats that as a transient race and retries with no\n// cap, which spins forever rather than rejecting — confirmed via strace\n// (an unbounded, rapid mkdir/statx loop; the event loop stays live and the\n// process never blocks, it just never stops). Walking the path ourselves,\n// one plain mkdir per component with no retry, makes every case settle in\n// a bounded number of syscalls: EEXIST is expected and skipped, anything\n// else (including that ENOENT-on-existing-parent case) fails once instead\n// of retrying.\nasync function ensureDir(dir: string): Promise<void> {\n const segments = dir.split(path.sep).filter(Boolean)\n let current = path.isAbsolute(dir) ? path.sep : ''\n for (const segment of segments) {\n current = current ? path.join(current, segment) : segment\n try {\n await mkdir(current)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') continue\n throw err\n }\n }\n}\n\n// A residual, undemonstrated risk remains distinct from the mkdir retry loop\n// above: a single mkdir/appendFile syscall genuinely wedged on a stalled\n// filesystem (e.g. a hung network mount) blocks like any normal blocking I/O\n// would. Node's fs promises do not support cancelling an in-flight mkdir, so\n// a bounded wait here only frees the *caller* — the wedged operation itself\n// keeps running in the background until the OS/filesystem gives up on its\n// own. That is an accepted, documented limitation, not a bug this module can\n// fix from userland; it is unrelated to (and much rarer than) the retry-loop\n// case ensureDir() eliminates above.\nconst WRITE_TIMEOUT_MS = 2000\n\nasync function writeEntry(file: string, entry: ActionLogEntry): Promise<void> {\n await ensureDir(path.dirname(file))\n await appendFile(file, JSON.stringify(entry) + '\\n', 'utf8')\n}\n\n// Never throws, and never hangs beyond WRITE_TIMEOUT_MS. A failure (or a\n// stall) recording an action must not fail (or stall) the action:\n// observability that can break the thing it observes is worse than none.\nexport async function appendActionLog(entry: ActionLogEntry): Promise<void> {\n const file = actionLogPath()\n if (!file) return\n try {\n await Promise.race([\n writeEntry(file, entry),\n new Promise<void>((resolve) => {\n const timer = setTimeout(resolve, WRITE_TIMEOUT_MS)\n timer.unref?.()\n }),\n ])\n } catch {\n // Intentionally swallowed.\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n// Exported so BOTH client paths log identically: the MCP proxy's remote client\n// and the CLI's own client. They are separate clients, and wiring only one is how\n// an unattended instance ends up with an empty log while the mechanism 'works'.\nexport function actionLogSignalsFromResult(result: unknown): {\n warnings?: string[]\n search_limits?: Record<string, unknown>\n} {\n const structuredContent = isRecord(result) ? result['structuredContent'] : undefined\n if (!isRecord(structuredContent)) return {}\n const warnings = Array.isArray(structuredContent['warnings'])\n ? (structuredContent['warnings'] as string[])\n : undefined\n const input = structuredContent['input']\n const search_limits =\n isRecord(input) && isRecord(input['search_limits'])\n ? (input['search_limits'] as Record<string, unknown>)\n : undefined\n return { warnings, search_limits }\n}\n\ntype CallToolFn = (...args: never[]) => Promise<unknown>\n\n// Wrap a client's callTool so every invocation is recorded. Returns the client.\n// Idempotent-unsafe: call once per client.\nexport function installActionLogging<T extends { callTool: CallToolFn }>(client: T): T {\n const original = client.callTool.bind(client) as CallToolFn\n const wrapped = (async (...args: never[]) => {\n const startedAt = Date.now()\n const input = (args[0] ?? {}) as { name?: unknown; arguments?: Record<string, unknown> }\n const tool = String(input.name ?? 'unknown')\n const toolArgs = input.arguments ?? {}\n try {\n const result = await original(...args)\n const { warnings, search_limits } = actionLogSignalsFromResult(result)\n await appendActionLog({\n timestamp: startedAt,\n tool,\n args: toolArgs,\n outcome: 'ok',\n duration_ms: Date.now() - startedAt,\n warnings,\n search_limits,\n })\n return result\n } catch (err) {\n await appendActionLog({\n timestamp: startedAt,\n tool,\n args: toolArgs,\n outcome: 'error',\n duration_ms: Date.now() - startedAt,\n error: (err as Error).message,\n })\n throw err\n }\n }) as CallToolFn\n client.callTool = wrapped as T['callTool']\n return client\n}\n"],"mappings":";;;;;;;;;;AAuBA,SAAgB,gBAAoC;CAClD,MAAM,aAAa,QAAQ,IAAI,gBAAgB,KAAK;CACpD,OAAO,aAAa,aAAa,KAAA;AACnC;AAYA,eAAe,UAAU,KAA4B;CACnD,MAAM,WAAW,IAAI,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,OAAO;CACnD,IAAI,UAAU,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM;CAChD,KAAK,MAAM,WAAW,UAAU;EAC9B,UAAU,UAAU,KAAK,KAAK,SAAS,OAAO,IAAI;EAClD,IAAI;GACF,MAAM,MAAM,OAAO;EACrB,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAAU;GACtD,MAAM;EACR;CACF;AACF;AAWA,MAAM,mBAAmB;AAEzB,eAAe,WAAW,MAAc,OAAsC;CAC5E,MAAM,UAAU,KAAK,QAAQ,IAAI,CAAC;CAClC,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,IAAI,MAAM,MAAM;AAC7D;AAKA,eAAsB,gBAAgB,OAAsC;CAC1E,MAAM,OAAO,cAAc;CAC3B,IAAI,CAAC,MAAM;CACX,IAAI;EACF,MAAM,QAAQ,KAAK,CACjB,WAAW,MAAM,KAAK,GACtB,IAAI,SAAe,YAAY;GAE7B,WADyB,SAAS,gBAC9B,CAAC,CAAC,QAAQ;EAChB,CAAC,CACH,CAAC;CACH,QAAQ,CAER;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAKA,SAAgB,2BAA2B,QAGzC;CACA,MAAM,oBAAoB,SAAS,MAAM,IAAI,OAAO,uBAAuB,KAAA;CAC3E,IAAI,CAAC,SAAS,iBAAiB,GAAG,OAAO,CAAC;CAC1C,MAAM,WAAW,MAAM,QAAQ,kBAAkB,WAAW,IACvD,kBAAkB,cACnB,KAAA;CACJ,MAAM,QAAQ,kBAAkB;CAKhC,OAAO;EAAE;EAAU,eAHjB,SAAS,KAAK,KAAK,SAAS,MAAM,gBAAgB,IAC7C,MAAM,mBACP,KAAA;CAC2B;AACnC;AAMA,SAAgB,qBAAyD,QAAc;CACrF,MAAM,WAAW,OAAO,SAAS,KAAK,MAAM;CAC5C,MAAM,WAAW,OAAO,GAAG,SAAkB;EAC3C,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,QAAS,KAAK,MAAM,CAAC;EAC3B,MAAM,OAAO,OAAO,MAAM,QAAQ,SAAS;EAC3C,MAAM,WAAW,MAAM,aAAa,CAAC;EACrC,IAAI;GACF,MAAM,SAAS,MAAM,SAAS,GAAG,IAAI;GACrC,MAAM,EAAE,UAAU,kBAAkB,2BAA2B,MAAM;GACrE,MAAM,gBAAgB;IACpB,WAAW;IACX;IACA,MAAM;IACN,SAAS;IACT,aAAa,KAAK,IAAI,IAAI;IAC1B;IACA;GACF,CAAC;GACD,OAAO;EACT,SAAS,KAAK;GACZ,MAAM,gBAAgB;IACpB,WAAW;IACX;IACA,MAAM;IACN,SAAS;IACT,aAAa,KAAK,IAAI,IAAI;IAC1B,OAAQ,IAAc;GACxB,CAAC;GACD,MAAM;EACR;CACF;CACA,OAAO,WAAW;CAClB,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"active-BQopLul8.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 artifacts_dir?: string\n domain_hints?: unknown\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n artifactsRoot: string\n domainHints: 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 artifactsDir = parsed.artifacts_dir ?? 'artifacts'\n const domainHints = Array.isArray(parsed.domain_hints)\n ? parsed.domain_hints.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)\n : []\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n artifactsRoot: path.resolve(workspaceRoot, artifactsDir),\n domainHints,\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 activeArtifactsRoot(): string {\n return findActiveWorkspace()?.artifactsRoot ?? path.join(os.homedir(), '.chain-insights', 'artifacts')\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAS,kBAAkB,eAA+C;CACxE,MAAM,OAAO,KAAK,QAAQ,aAAa;CACvC,MAAM,cAAc,KAAK,KAAK,MAAM,iBAAiB;CACrD,MAAM,aAAa,KAAK,KAAK,aAAa,gBAAgB;CAC1D,IAAI,CAAC,GAAG,WAAW,UAAU,GAAG,OAAO;CAEvC,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,MAAM,CAAC;CAC7D,IAAI,OAAO,WAAW,+BAA+B,OAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,OAAO,kBAAkB,IAAI;CAChE,MAAM,eAAe,OAAO,iBAAiB;CAC7C,MAAM,cAAc,MAAM,QAAQ,OAAO,YAAY,IACjD,OAAO,aAAa,
|
|
1
|
+
{"version":3,"file":"active-BQopLul8.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 artifacts_dir?: string\n domain_hints?: unknown\n}\n\nexport interface ActiveWorkspace {\n root: string\n metadataDir: string\n artifactsRoot: string\n domainHints: 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 artifactsDir = parsed.artifacts_dir ?? 'artifacts'\n const domainHints = Array.isArray(parsed.domain_hints)\n ? parsed.domain_hints.filter(\n (value): value is string => typeof value === 'string' && value.trim().length > 0\n )\n : []\n return {\n root: workspaceRoot,\n metadataDir: path.join(workspaceRoot, '.chain-insights'),\n artifactsRoot: path.resolve(workspaceRoot, artifactsDir),\n domainHints,\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 activeArtifactsRoot(): string {\n return (\n findActiveWorkspace()?.artifactsRoot ?? path.join(os.homedir(), '.chain-insights', 'artifacts')\n )\n}\n\nexport function activeDataDir(fallbackDataDir?: string): string {\n return (\n findActiveWorkspace()?.root ?? fallbackDataDir ?? path.join(os.homedir(), '.chain-insights')\n )\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAS,kBAAkB,eAA+C;CACxE,MAAM,OAAO,KAAK,QAAQ,aAAa;CACvC,MAAM,cAAc,KAAK,KAAK,MAAM,iBAAiB;CACrD,MAAM,aAAa,KAAK,KAAK,aAAa,gBAAgB;CAC1D,IAAI,CAAC,GAAG,WAAW,UAAU,GAAG,OAAO;CAEvC,MAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,MAAM,CAAC;CAC7D,IAAI,OAAO,WAAW,+BAA+B,OAAO;CAE5D,MAAM,gBAAgB,KAAK,QAAQ,OAAO,kBAAkB,IAAI;CAChE,MAAM,eAAe,OAAO,iBAAiB;CAC7C,MAAM,cAAc,MAAM,QAAQ,OAAO,YAAY,IACjD,OAAO,aAAa,QACjB,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,CACjF,IACA,CAAC;CACL,OAAO;EACL,MAAM;EACN,aAAa,KAAK,KAAK,eAAe,iBAAiB;EACvD,eAAe,KAAK,QAAQ,eAAe,YAAY;EACvD;CACF;AACF;AAEA,SAAgB,oBAAoB,WAAW,QAAQ,IAAI,GAA2B;CACpF,MAAM,eAAe,QAAQ,IAAI,2BAA2B,EAAE,KAAK;CACnE,IAAI,cAAc;EAChB,MAAM,SAAS,kBAAkB,YAAY;EAC7C,IAAI,QAAQ,OAAO;CACrB;CAEA,IAAI,UAAU,KAAK,QAAQ,QAAQ;CACnC,OAAO,MAAM;EACX,MAAM,SAAS,kBAAkB,OAAO;EACxC,IAAI,QAAQ,OAAO;EAEnB,MAAM,SAAS,KAAK,QAAQ,OAAO;EACnC,IAAI,WAAW,SAAS,OAAO;EAC/B,UAAU;CACZ;AACF;AAYA,SAAgB,cAAc,iBAAkC;CAC9D,OACE,oBAAoB,CAAC,EAAE,QAAQ,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB;AAE/F"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
2
|
let node_path = require("node:path");
|
|
3
3
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
4
4
|
let node_fs = require("node:fs");
|
|
@@ -82,7 +82,7 @@ function createApp() {
|
|
|
82
82
|
ts: Date.now()
|
|
83
83
|
}));
|
|
84
84
|
app.get("/status", async (c) => {
|
|
85
|
-
const { loadConfig } = await import("./config-
|
|
85
|
+
const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
|
|
86
86
|
const config = await loadConfig();
|
|
87
87
|
return c.json({
|
|
88
88
|
dataDir: config.dataDir,
|
|
@@ -137,4 +137,4 @@ function createApp() {
|
|
|
137
137
|
//#endregion
|
|
138
138
|
export { createApp as t };
|
|
139
139
|
|
|
140
|
-
//# sourceMappingURL=app-
|
|
140
|
+
//# sourceMappingURL=app-7NRCqx_Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-7NRCqx_Q.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = [\n 'artifacts',\n 'entities',\n 'reports',\n 'sessions',\n '.chain-insights/schema',\n]\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n path: string\n type: 'file' | 'directory' | 'symlink'\n size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n const relative = path.relative(path.resolve(root), path.resolve(target))\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n try {\n const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n return withinRoot(realRoot, realTarget)\n } catch {\n return false\n }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n workspaceRoot: string,\n roots = WORKSPACE_TREE_ROOTS,\n maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n const entries: WorkspaceTreeEntry[] = []\n const root = path.resolve(workspaceRoot)\n\n async function visit(target: string, depth: number): Promise<void> {\n const resolved = path.resolve(target)\n if (!withinRoot(root, resolved)) return\n\n let info: Awaited<ReturnType<typeof lstat>>\n try {\n info = await lstat(resolved)\n } catch {\n return\n }\n\n const type = info.isSymbolicLink()\n ? 'symlink'\n : info.isDirectory()\n ? 'directory'\n : info.isFile()\n ? 'file'\n : null\n if (!type) return\n\n const entry: WorkspaceTreeEntry = {\n path: toWorkspaceRelative(root, resolved),\n type,\n }\n if (type === 'file') entry.size = info.size\n entries.push(entry)\n\n if (type !== 'directory' || depth >= maxDepth) return\n if (!(await realPathWithinRoot(root, resolved))) return\n\n let children: string[]\n try {\n children = await readdir(resolved)\n } catch {\n return\n }\n\n for (const child of children.sort()) {\n await visit(path.join(resolved, child), depth + 1)\n }\n }\n\n for (const rootName of roots) {\n const target = path.resolve(root, rootName)\n if (withinRoot(root, target)) await visit(target, 0)\n }\n\n return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n const filename = `${vizId}.html`\n const paths = workspaceOutputPaths()\n const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n try {\n return await readFile(vizPath, 'utf-8')\n } catch {\n return null\n }\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n return (\n filename.endsWith('.graph.json') &&\n /^[A-Za-z0-9._-]+$/.test(filename) &&\n !filename.includes('..') &&\n !filename.includes('/') &&\n !filename.includes('\\\\')\n )\n}\n\nexport function createApp(): Hono {\n const app = new Hono()\n\n app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n app.get('/status', async (c) => {\n const { loadConfig } = await import('../config/index.js')\n const config = await loadConfig()\n return c.json({\n dataDir: config.dataDir,\n graphMcpMode: config.graphMcpMode,\n server: 'running',\n })\n })\n\n app.get('/viz/:id', async (c) => {\n const id = c.req.param('id')\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n return c.json({ error: 'Invalid visualization ID' }, 400)\n }\n const html = await findVizHtml(id)\n if (!html) {\n return c.json({ error: 'Visualization not found' }, 404)\n }\n return c.html(html)\n })\n\n app.get('/graph-reports/:filename', async (c) => {\n const filename = c.req.param('filename')\n if (!isSafeGraphReportFilename(filename)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n if (!(await realPathWithinRoot(paths.reportGraphsRoot, graphPath))) {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n\n try {\n const graph = await readFile(graphPath, 'utf-8')\n return c.body(graph, 200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n } catch {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n })\n\n app.get('/graph-reports/*', (c) => {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n })\n\n app.get('/workspace/tree', async (c) => {\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n return c.json({\n schema: 'chain-insights.workspace-tree.v1',\n root: paths.root,\n entries,\n })\n })\n\n app.onError((err, c) => {\n console.error(err)\n return c.json({ error: 'Internal server error' }, 500)\n })\n\n return app\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
2
|
-
const require_output_root = require("./output-root-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
|
+
const require_output_root = require("./output-root-BewoQaTx.cjs");
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -84,7 +84,7 @@ function createApp() {
|
|
|
84
84
|
ts: Date.now()
|
|
85
85
|
}));
|
|
86
86
|
app.get("/status", async (c) => {
|
|
87
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
87
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
|
|
88
88
|
const config = await loadConfig();
|
|
89
89
|
return c.json({
|
|
90
90
|
dataDir: config.dataDir,
|
|
@@ -102,7 +102,7 @@ function createApp() {
|
|
|
102
102
|
app.get("/graph-reports/:filename", async (c) => {
|
|
103
103
|
const filename = c.req.param("filename");
|
|
104
104
|
if (!isSafeGraphReportFilename(filename)) return c.json({ error: "Invalid graph report filename" }, 400);
|
|
105
|
-
const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-
|
|
105
|
+
const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-BewoQaTx.cjs")).then((n) => n.output_root_exports);
|
|
106
106
|
const paths = workspaceOutputPaths();
|
|
107
107
|
const graphPath = node_path.default.resolve(paths.reportGraphsRoot, filename);
|
|
108
108
|
if (!withinRoot(paths.reportGraphsRoot, graphPath)) return c.json({ error: "Invalid graph report filename" }, 400);
|
|
@@ -121,7 +121,7 @@ function createApp() {
|
|
|
121
121
|
return c.json({ error: "Invalid graph report filename" }, 400);
|
|
122
122
|
});
|
|
123
123
|
app.get("/workspace/tree", async (c) => {
|
|
124
|
-
const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-
|
|
124
|
+
const { workspaceOutputPaths } = await Promise.resolve().then(() => require("./output-root-BewoQaTx.cjs")).then((n) => n.output_root_exports);
|
|
125
125
|
const paths = workspaceOutputPaths();
|
|
126
126
|
const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS);
|
|
127
127
|
return c.json({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_app = require("./app-
|
|
1
|
+
const require_app = require("./app-l9JE6bt3.cjs");
|
|
2
2
|
let _hono_node_server = require("@hono/node-server");
|
|
3
3
|
let node_timers_promises = require("node:timers/promises");
|
|
4
4
|
//#region src/mcp/artifact-server.ts
|
|
@@ -24,8 +24,9 @@ async function waitUntilHealthy(port) {
|
|
|
24
24
|
async function ensureArtifactServer(port) {
|
|
25
25
|
if (servers.has(port)) return;
|
|
26
26
|
if (await isHealthy(port)) return;
|
|
27
|
+
const app = require_app.createApp();
|
|
27
28
|
const server = (0, _hono_node_server.serve)({
|
|
28
|
-
fetch:
|
|
29
|
+
fetch: app.fetch,
|
|
29
30
|
hostname: "127.0.0.1",
|
|
30
31
|
port
|
|
31
32
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as createApp } from "./app-
|
|
1
|
+
import { t as createApp } from "./app-7NRCqx_Q.mjs";
|
|
2
2
|
import { serve } from "@hono/node-server";
|
|
3
3
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
4
4
|
//#region src/mcp/artifact-server.ts
|
|
@@ -24,8 +24,9 @@ async function waitUntilHealthy(port) {
|
|
|
24
24
|
async function ensureArtifactServer(port) {
|
|
25
25
|
if (servers.has(port)) return;
|
|
26
26
|
if (await isHealthy(port)) return;
|
|
27
|
+
const app = createApp();
|
|
27
28
|
const server = serve({
|
|
28
|
-
fetch:
|
|
29
|
+
fetch: app.fetch,
|
|
29
30
|
hostname: "127.0.0.1",
|
|
30
31
|
port
|
|
31
32
|
});
|
|
@@ -45,4 +46,4 @@ async function ensureArtifactServer(port) {
|
|
|
45
46
|
//#endregion
|
|
46
47
|
export { ensureArtifactServer };
|
|
47
48
|
|
|
48
|
-
//# sourceMappingURL=artifact-server-
|
|
49
|
+
//# sourceMappingURL=artifact-server-DoxJr0z8.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-server-DoxJr0z8.mjs","names":["delay"],"sources":["../src/mcp/artifact-server.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { createApp } from '../server/app.js'\n\ntype ArtifactServer = ReturnType<typeof serve>\n\nconst servers = new Map<number, ArtifactServer>()\n\nasync function isHealthy(port: number): Promise<boolean> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 500)\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n })\n return response.ok\n } catch {\n return false\n } finally {\n clearTimeout(timeout)\n }\n}\n\nasync function waitUntilHealthy(port: number): Promise<void> {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (await isHealthy(port)) return\n await delay(50)\n }\n throw new Error(`Graph report server did not become healthy on 127.0.0.1:${port}`)\n}\n\nexport async function ensureArtifactServer(port: number): Promise<void> {\n if (servers.has(port)) return\n if (await isHealthy(port)) return\n\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1',\n port,\n })\n servers.set(port, server)\n server.on('error', (err) => {\n servers.delete(port)\n process.stderr.write(\n `Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`\n )\n })\n\n try {\n await waitUntilHealthy(port)\n } catch (err) {\n servers.delete(port)\n server.close()\n throw err\n }\n}\n\nexport function closeArtifactServers(): void {\n for (const [port, server] of servers.entries()) {\n server.close()\n servers.delete(port)\n }\n}\n"],"mappings":";;;;AAMA,MAAM,0BAAU,IAAI,IAA4B;AAEhD,eAAe,UAAU,MAAgC;CACvD,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,GAAG;CACxD,IAAI;EAIF,QAAO,MAHgB,MAAM,oBAAoB,KAAK,UAAU,EAC9D,QAAQ,WAAW,OACrB,CAAC,EAAA,CACe;CAClB,QAAQ;EACN,OAAO;CACT,UAAU;EACR,aAAa,OAAO;CACtB;AACF;AAEA,eAAe,iBAAiB,MAA6B;CAC3D,KAAK,IAAI,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;EAChD,IAAI,MAAM,UAAU,IAAI,GAAG;EAC3B,MAAMA,aAAM,EAAE;CAChB;CACA,MAAM,IAAI,MAAM,2DAA2D,MAAM;AACnF;AAEA,eAAsB,qBAAqB,MAA6B;CACtE,IAAI,QAAQ,IAAI,IAAI,GAAG;CACvB,IAAI,MAAM,UAAU,IAAI,GAAG;CAE3B,MAAM,MAAM,UAAU;CACtB,MAAM,SAAS,MAAM;EACnB,OAAO,IAAI;EACX,UAAU;EACV;CACF,CAAC;CACD,QAAQ,IAAI,MAAM,MAAM;CACxB,OAAO,GAAG,UAAU,QAAQ;EAC1B,QAAQ,OAAO,IAAI;EACnB,QAAQ,OAAO,MACb,0DAA0D,KAAK,IAAK,IAAc,QAAQ,GAC5F;CACF,CAAC;CAED,IAAI;EACF,MAAM,iBAAiB,IAAI;CAC7B,SAAS,KAAK;EACZ,QAAQ,OAAO,IAAI;EACnB,OAAO,MAAM;EACb,MAAM;CACR;AACF"}
|