chain-insights 0.19.0 → 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.
Files changed (138) hide show
  1. package/README.md +44 -121
  2. package/bin/cli.js +4 -4
  3. package/bin/install.cjs +137 -113
  4. package/bin/mcp-proxy.cjs +7 -5
  5. package/dist/{action-log-Dfe88TwV.cjs → action-log-HniMMF7f.cjs} +1 -1
  6. package/dist/action-log-diTHyMbV.mjs.map +1 -1
  7. package/dist/active-BQopLul8.mjs.map +1 -1
  8. package/dist/{active-Bj6dohpc.cjs → active-Bc13hJIm.cjs} +1 -1
  9. package/dist/{app-CJwx_2Rr.mjs → app-7NRCqx_Q.mjs} +2 -2
  10. package/dist/app-7NRCqx_Q.mjs.map +1 -0
  11. package/dist/{app-D4Cm69ob.cjs → app-l9JE6bt3.cjs} +5 -5
  12. package/dist/{artifact-server-sJBFGcum.cjs → artifact-server-CH-asxF5.cjs} +3 -2
  13. package/dist/{artifact-server-D1D-_t5l.mjs → artifact-server-DoxJr0z8.mjs} +4 -3
  14. package/dist/artifact-server-DoxJr0z8.mjs.map +1 -0
  15. package/dist/{capabilities-CxvK7LKZ.mjs → capabilities-BZI6qROg.mjs} +2 -2
  16. package/dist/capabilities-BZI6qROg.mjs.map +1 -0
  17. package/dist/{capabilities-CZYijWbg.cjs → capabilities-CPcrxodx.cjs} +2 -2
  18. package/dist/cli.cjs +42 -216
  19. package/dist/cli.mjs +36 -210
  20. package/dist/cli.mjs.map +1 -1
  21. package/dist/{client-Bjxo7AEa.cjs → client-CqAzD519.cjs} +4 -4
  22. package/dist/{client-i3WtsKA4.mjs → client-D4aM4bzP.mjs} +4 -4
  23. package/dist/client-D4aM4bzP.mjs.map +1 -0
  24. package/dist/{config-DE0dUT3P.cjs → config-BVFf3v8-.cjs} +2 -2
  25. package/dist/{config-DA6KRx60.mjs → config-CnNMdt4M.mjs} +2 -2
  26. package/dist/config-CnNMdt4M.mjs.map +1 -0
  27. package/dist/graph-normalizer-BA3Rbd6l.mjs.map +1 -1
  28. package/dist/graph-reports-B6yoYbPq.mjs.map +1 -1
  29. package/dist/{graph-reports-DSHSp5HL.cjs → graph-reports-_gqzxvvs.cjs} +2 -2
  30. package/dist/{html-generator-Ui29DLSD.cjs → html-generator-B9f_GIi2.cjs} +4 -3
  31. package/dist/{html-generator-ClysZjfY.mjs → html-generator-D2BDzLED.mjs} +3 -2
  32. package/dist/{html-generator-ClysZjfY.mjs.map → html-generator-D2BDzLED.mjs.map} +1 -1
  33. package/dist/index.cjs +9 -9
  34. package/dist/index.d.cts +3 -0
  35. package/dist/index.d.cts.map +1 -1
  36. package/dist/index.d.mts +3 -0
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/index.mjs +9 -9
  39. package/dist/{init-BT3STWBv.mjs → init-Djhml4ih.mjs} +3 -3
  40. package/dist/init-Djhml4ih.mjs.map +1 -0
  41. package/dist/{init-BR4SRTAY.cjs → init-DvrKxK15.cjs} +3 -3
  42. package/dist/limits-D3Mn-gig.mjs.map +1 -1
  43. package/dist/mcp-endpoint-DbWtLcba.mjs.map +1 -1
  44. package/dist/mcp-proxy.cjs +19 -18
  45. package/dist/mcp-proxy.d.cts.map +1 -1
  46. package/dist/mcp-proxy.d.mts.map +1 -1
  47. package/dist/mcp-proxy.mjs +13 -12
  48. package/dist/mcp-proxy.mjs.map +1 -1
  49. package/dist/{merge-CJRZpuZ4.mjs → merge-BZw-pMPN.mjs} +2 -2
  50. package/dist/merge-BZw-pMPN.mjs.map +1 -0
  51. package/dist/{merge-B_5kxbGv.cjs → merge-C52eaAGU.cjs} +1 -1
  52. package/dist/{output-root-C1PFNCqb.cjs → output-root-BewoQaTx.cjs} +2 -2
  53. package/dist/{public-tools-CgiKgYgf.cjs → public-tools-BFs_7U_Z.cjs} +8 -7
  54. package/dist/{public-tools-B9_wXs2o.mjs → public-tools-BjqZGGAC.mjs} +7 -6
  55. package/dist/public-tools-BjqZGGAC.mjs.map +1 -0
  56. package/dist/{rolldown-runtime-DakpK96I.cjs → rolldown-runtime-C0BPl7ul.cjs} +1 -1
  57. package/dist/{schema-DF0aOXII.cjs → schema-BpeV3xDV.cjs} +5 -2
  58. package/dist/{schema-0BVyF-aL.mjs → schema-DOJA2nNi.mjs} +5 -2
  59. package/dist/schema-DOJA2nNi.mjs.map +1 -0
  60. package/dist/{schema-cache-DwDvPy4e.mjs → schema-cache-DJdU4MIP.mjs} +2 -2
  61. package/dist/{schema-cache-DwDvPy4e.mjs.map → schema-cache-DJdU4MIP.mjs.map} +1 -1
  62. package/dist/{schema-cache-CX0r2UQp.cjs → schema-cache-n7Gbhvsj.cjs} +2 -2
  63. package/dist/{server-ta1oyJ2a.cjs → server-CmvzYu7v.cjs} +4 -3
  64. package/dist/{server-BXj6BRI1.mjs → server-mDSRgk1X.mjs} +4 -3
  65. package/dist/server-mDSRgk1X.mjs.map +1 -0
  66. package/dist/tool-visibility-C0ALejXI.mjs.map +1 -1
  67. package/dist/{tool-visibility-DMIEexib.cjs → tool-visibility-DZBAP62b.cjs} +1 -1
  68. package/dist/{tools-CWwX4yth.mjs → tools-BKLnlBG1.mjs} +8 -6
  69. package/dist/tools-BKLnlBG1.mjs.map +1 -0
  70. package/dist/{tools-BPfUpCUR.cjs → tools-lPY6k-vJ.cjs} +8 -6
  71. package/dist/{topup-server-VGl8-Fz5.cjs → topup-server-DAqmAuu1.cjs} +23 -21
  72. package/dist/{topup-server-D2X7My7i.mjs → topup-server-rqI7bqem.mjs} +23 -21
  73. package/dist/topup-server-rqI7bqem.mjs.map +1 -0
  74. package/dist/update-CJUfGCxs.mjs.map +1 -1
  75. package/dist/{usage-status-H1xgCu99.cjs → usage-status-BdwwgYsu.cjs} +2 -2
  76. package/dist/{usage-status-D2uosC7s.mjs → usage-status-CHiOPA-2.mjs} +2 -2
  77. package/dist/usage-status-CHiOPA-2.mjs.map +1 -0
  78. package/dist/version-BA3J8hu4.mjs.map +1 -1
  79. package/dist/{viz-HSwLcKr6.cjs → viz-BJ_kyRjI.cjs} +7 -5
  80. package/dist/{viz-Cq1jSWSH.mjs → viz-Uh_EF-qB.mjs} +6 -4
  81. package/dist/viz-Uh_EF-qB.mjs.map +1 -0
  82. package/dist/{wallet-C2yVjcSY.cjs → wallet-BEvHrHi3.cjs} +3 -2
  83. package/dist/{wallet-BrTTnf7w.mjs → wallet-fMbhvKXT.mjs} +3 -2
  84. package/dist/wallet-fMbhvKXT.mjs.map +1 -0
  85. package/docs/architecture.md +18 -18
  86. package/docs/debugging.md +2 -42
  87. package/docs/development.md +0 -27
  88. package/docs/graph-query-compatibility.md +66 -57
  89. package/docs/graph-tools.md +6 -6
  90. package/docs/investigation-workspaces.md +0 -35
  91. package/docs/mcp-proxy.md +8 -9
  92. package/docs/search-limits.md +8 -8
  93. package/docs/stability.md +4 -5
  94. package/package.json +1 -5
  95. package/skills/chain-insights-cypher/SKILL.md +7 -7
  96. package/skills/chain-insights-cypher/agents/openai.yaml +3 -3
  97. package/skills/chain-insights-schema-bittensor/SKILL.md +26 -26
  98. package/skills/chain-insights-schema-evm/SKILL.md +37 -37
  99. package/dist/app-CJwx_2Rr.mjs.map +0 -1
  100. package/dist/artifact-server-D1D-_t5l.mjs.map +0 -1
  101. package/dist/capabilities-CxvK7LKZ.mjs.map +0 -1
  102. package/dist/cases-C1XewLdx.mjs +0 -155
  103. package/dist/cases-C1XewLdx.mjs.map +0 -1
  104. package/dist/cases-CVtc7Rf8.cjs +0 -160
  105. package/dist/client-i3WtsKA4.mjs.map +0 -1
  106. package/dist/config-CwIb-tav.mjs +0 -33
  107. package/dist/config-CwIb-tav.mjs.map +0 -1
  108. package/dist/config-Cy-qzx4W.cjs +0 -33
  109. package/dist/config-DA6KRx60.mjs.map +0 -1
  110. package/dist/init-9jY5rCcH.cjs +0 -24
  111. package/dist/init-BT3STWBv.mjs.map +0 -1
  112. package/dist/init-BiRfMQcA.mjs +0 -26
  113. package/dist/init-BiRfMQcA.mjs.map +0 -1
  114. package/dist/lock-Cs-a3Ng1.cjs +0 -38
  115. package/dist/lock-D_sWGS7W.mjs +0 -40
  116. package/dist/lock-D_sWGS7W.mjs.map +0 -1
  117. package/dist/merge-CJRZpuZ4.mjs.map +0 -1
  118. package/dist/paths-CffVTUCv.cjs +0 -31
  119. package/dist/paths-_3isihzD.mjs +0 -21
  120. package/dist/paths-_3isihzD.mjs.map +0 -1
  121. package/dist/public-tools-B9_wXs2o.mjs.map +0 -1
  122. package/dist/render-TmSq0f4f.cjs +0 -207
  123. package/dist/render-mxE0Tdsf.mjs +0 -206
  124. package/dist/render-mxE0Tdsf.mjs.map +0 -1
  125. package/dist/report-BFXFrJmj.mjs +0 -36
  126. package/dist/report-BFXFrJmj.mjs.map +0 -1
  127. package/dist/report-BwKQqOCR.cjs +0 -36
  128. package/dist/runner-CSss8Hvy.mjs +0 -33
  129. package/dist/runner-CSss8Hvy.mjs.map +0 -1
  130. package/dist/runner-aErdz2zn.cjs +0 -31
  131. package/dist/schema-0BVyF-aL.mjs.map +0 -1
  132. package/dist/server-BXj6BRI1.mjs.map +0 -1
  133. package/dist/tools-CWwX4yth.mjs.map +0 -1
  134. package/dist/topup-server-D2X7My7i.mjs.map +0 -1
  135. package/dist/usage-status-D2uosC7s.mjs.map +0 -1
  136. package/dist/viz-Cq1jSWSH.mjs.map +0 -1
  137. package/dist/wallet-BrTTnf7w.mjs.map +0 -1
  138. package/docs/monitoring.md +0 -277
package/bin/install.cjs CHANGED
@@ -1,50 +1,56 @@
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 = require('fs');
10
- const path = require('path');
11
- const os = require('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 = '\x1b[36m';
15
- const green = '\x1b[32m';
16
- const bold = '\x1b[1m';
17
- const dim = '\x1b[2m';
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 = 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');
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(` ${cyan}--codex${reset} Install Codex skills globally to ~/.codex/skills/ and register MCP`);
32
- console.log(` ${cyan}--hermes${reset} Install Hermes skills globally to ~/.hermes/skills/chain-insights/ and register MCP`);
33
- console.log(` ${cyan}--local${reset} Install skills locally to ./.claude/commands/chain-insights/`);
34
- console.log('');
35
- process.exit(0);
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 = os.homedir();
39
- const dataDir = path.join(homeDir, '.chain-insights');
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')
42
48
  const PUBLIC_SKILL_NAMES = Object.freeze([
43
49
  'chain-insights-address-risk',
44
50
  'chain-insights-cypher',
45
51
  'chain-insights-schema-bittensor',
46
52
  'chain-insights-schema-evm',
47
- ]);
53
+ ])
48
54
  const RETIRED_SKILL_NAMES = Object.freeze([
49
55
  'chain-insights-bittensor-cypher',
50
56
  'chain-insights-developer-experience',
@@ -52,65 +58,74 @@ const RETIRED_SKILL_NAMES = Object.freeze([
52
58
  'chain-insights-monitoring',
53
59
  'ci-status',
54
60
  'test-chain-insights-graph',
55
- ]);
61
+ ])
56
62
 
57
63
  // Determine skills targets
58
- const skillsTargets = [];
59
- if (hasClaude) skillsTargets.push({ name: 'Claude Code', dir: path.join(homeDir, '.claude', 'skills') });
60
- if (hasCodex) skillsTargets.push({ name: 'Codex', dir: path.join(homeDir, '.codex', 'skills') });
61
- if (hasHermes) skillsTargets.push({ name: 'Hermes', dir: path.join(homeDir, '.hermes', 'skills', 'chain-insights') });
62
- if (hasLocal) skillsTargets.push({ name: 'Local Claude commands', dir: path.join(process.cwd(), '.claude', 'commands', 'chain-insights') });
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
+ })
63
78
 
64
79
  // ─── 1. Copy agent skills ─────────────────────────────────────────────────
65
80
 
66
81
  function copyCommandsAsClaudeSkills(srcDir, targetDir) {
67
82
  if (!fs.existsSync(srcDir)) {
68
- throw new Error(`Skills source not found: ${srcDir}`);
83
+ throw new Error(`Skills source not found: ${srcDir}`)
69
84
  }
70
85
 
71
- fs.mkdirSync(targetDir, { recursive: true });
86
+ fs.mkdirSync(targetDir, { recursive: true })
72
87
 
73
88
  // The target is shared with user-owned skills. Remove only the exact retired
74
89
  // names from older Chain Insights releases, then replace the reviewed set.
75
90
  // Never delete by prefix or enumerate arbitrary source directories.
76
91
 
77
92
  const copyTree = (src, dest) => {
78
- const stat = fs.statSync(src);
93
+ const stat = fs.statSync(src)
79
94
  if (stat.isDirectory()) {
80
- fs.mkdirSync(dest, { recursive: true });
95
+ fs.mkdirSync(dest, { recursive: true })
81
96
  for (const child of fs.readdirSync(src)) {
82
- copyTree(path.join(src, child), path.join(dest, child));
97
+ copyTree(path.join(src, child), path.join(dest, child))
83
98
  }
84
- return;
99
+ return
85
100
  }
86
101
  if (stat.isFile()) {
87
- fs.copyFileSync(src, dest);
102
+ fs.copyFileSync(src, dest)
88
103
  }
89
- };
104
+ }
90
105
 
91
106
  for (const skillName of RETIRED_SKILL_NAMES) {
92
- fs.rmSync(path.join(targetDir, skillName), { recursive: true, force: true });
107
+ fs.rmSync(path.join(targetDir, skillName), { recursive: true, force: true })
93
108
  }
94
109
 
95
110
  for (const skillName of PUBLIC_SKILL_NAMES) {
96
- const skillSrc = path.join(srcDir, skillName);
97
- const skillDest = path.join(targetDir, skillName);
111
+ const skillSrc = path.join(srcDir, skillName)
112
+ const skillDest = path.join(targetDir, skillName)
98
113
  if (!fs.existsSync(skillSrc) || !fs.statSync(skillSrc).isDirectory()) {
99
- throw new Error(`Reviewed skill source not found: ${skillSrc}`);
114
+ throw new Error(`Reviewed skill source not found: ${skillSrc}`)
100
115
  }
101
- fs.rmSync(skillDest, { recursive: true, force: true });
102
- copyTree(skillSrc, skillDest);
116
+ fs.rmSync(skillDest, { recursive: true, force: true })
117
+ copyTree(skillSrc, skillDest)
103
118
  }
104
119
  }
105
120
 
106
121
  for (const target of skillsTargets) {
107
- copyCommandsAsClaudeSkills(srcSkillsDir, target.dir);
122
+ copyCommandsAsClaudeSkills(srcSkillsDir, target.dir)
108
123
  }
109
124
 
110
125
  // ─── 2. Create ~/.chain-insights/ config directory ────────────────────────
111
126
 
112
127
  if (!fs.existsSync(dataDir)) {
113
- fs.mkdirSync(dataDir, { recursive: true });
128
+ fs.mkdirSync(dataDir, { recursive: true })
114
129
  }
115
130
 
116
131
  // ─── 3. Write default config.json if absent ───────────────────────────────
@@ -119,20 +134,20 @@ if (!fs.existsSync(configPath)) {
119
134
  const defaultConfig = {
120
135
  graphMcpEndpoint: 'http://127.0.0.1:8012/mcp',
121
136
  walletAddress: '',
122
- graphMcpMode: 'paid',
123
- serverPort: 4321,
124
- dataDir: dataDir,
125
- version: '1',
126
- };
127
- 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')
128
143
  // Owner-readable only — config may contain MCP auth token (ASVS L1 V4.3.2 / T-02-01)
129
- fs.chmodSync(configPath, 0o600);
144
+ fs.chmodSync(configPath, 0o600)
130
145
  }
131
146
 
132
147
  // ─── 4. Register MCP proxy in supported clients ───────────────────────────
133
148
 
134
- const proxyBinPath = path.resolve(__dirname, 'mcp-proxy.cjs');
135
- const { execFileSync } = require('child_process');
149
+ const proxyBinPath = path.resolve(__dirname, 'mcp-proxy.cjs')
150
+ const { execFileSync } = require('child_process')
136
151
 
137
152
  if (hasClaude) {
138
153
  try {
@@ -140,72 +155,74 @@ if (hasClaude) {
140
155
  'claude',
141
156
  ['mcp', 'add', 'chain-insights-proxy', '--scope', 'user', '--', 'node', proxyBinPath],
142
157
  { stdio: 'pipe' }
143
- );
144
- 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}`)
145
160
  } catch {
146
- console.log(` ${dim}Claude MCP:${reset} run manually: claude mcp add chain-insights-proxy --scope user -- node ${proxyBinPath}`);
161
+ console.log(
162
+ ` ${dim}Claude MCP:${reset} run manually: claude mcp add chain-insights-proxy --scope user -- node ${proxyBinPath}`
163
+ )
147
164
  }
148
165
  }
149
166
 
150
167
  function tomlQuoted(value) {
151
- return JSON.stringify(value);
168
+ return JSON.stringify(value)
152
169
  }
153
170
 
154
171
  function installCodexMcp(configFile, proxyPath) {
155
- fs.mkdirSync(path.dirname(configFile), { recursive: true });
156
- 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') : ''
157
174
  const block = [
158
175
  '[mcp_servers.chain-insights]',
159
176
  'command = "node"',
160
177
  `args = [${tomlQuoted(proxyPath)}]`,
161
178
  '',
162
- ].join('\n');
179
+ ].join('\n')
163
180
 
164
- const heading = '[mcp_servers.chain-insights]';
165
- const start = content.indexOf(heading);
181
+ const heading = '[mcp_servers.chain-insights]'
182
+ const start = content.indexOf(heading)
166
183
  if (start >= 0) {
167
- let end = content.length;
168
- const rest = content.slice(start + heading.length);
169
- const nextSection = rest.search(/\n\[/);
170
- if (nextSection >= 0) end = start + heading.length + nextSection + 1;
171
- 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)}`
172
189
  } else {
173
- const separator = content.endsWith('\n') || content.length === 0 ? '' : '\n';
174
- content = `${content}${separator}\n${block}`;
190
+ const separator = content.endsWith('\n') || content.length === 0 ? '' : '\n'
191
+ content = `${content}${separator}\n${block}`
175
192
  }
176
193
 
177
- fs.writeFileSync(configFile, content, 'utf8');
194
+ fs.writeFileSync(configFile, content, 'utf8')
178
195
  }
179
196
 
180
197
  if (hasCodex) {
181
- const codexConfig = path.join(homeDir, '.codex', 'config.toml');
182
- installCodexMcp(codexConfig, proxyBinPath);
183
- 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}`)
184
201
  }
185
202
 
186
203
  function yamlQuoted(value) {
187
- return JSON.stringify(value);
204
+ return JSON.stringify(value)
188
205
  }
189
206
 
190
207
  function findTopLevelSectionEnd(lines, start) {
191
208
  for (let i = start + 1; i < lines.length; i++) {
192
- const line = lines[i];
193
- if (line.trim() === '') continue;
194
- 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
195
212
  }
196
- return lines.length;
213
+ return lines.length
197
214
  }
198
215
 
199
216
  function findHermesServerEnd(lines, start, sectionEnd) {
200
217
  for (let i = start + 1; i < sectionEnd; i++) {
201
- const line = lines[i];
202
- if (/^ [^ ].*:/.test(line)) return i;
218
+ const line = lines[i]
219
+ if (/^ [^ ].*:/.test(line)) return i
203
220
  }
204
- return sectionEnd;
221
+ return sectionEnd
205
222
  }
206
223
 
207
224
  function installHermesMcp(configFile, proxyPath) {
208
- fs.mkdirSync(path.dirname(configFile), { recursive: true });
225
+ fs.mkdirSync(path.dirname(configFile), { recursive: true })
209
226
 
210
227
  const serverBlock = [
211
228
  ' chain-insights:',
@@ -213,50 +230,57 @@ function installHermesMcp(configFile, proxyPath) {
213
230
  ' args:',
214
231
  ` - ${yamlQuoted(proxyPath)}`,
215
232
  ' enabled: true',
216
- ];
233
+ ]
217
234
 
218
- let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : '';
235
+ let content = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : ''
219
236
  if (/^mcp_servers:\s*\{\s*\}\s*$/m.test(content)) {
220
- content = content.replace(/^mcp_servers:\s*\{\s*\}\s*$/m, ['mcp_servers:', ...serverBlock].join('\n'));
221
- fs.writeFileSync(configFile, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
222
- return;
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
223
243
  }
224
244
 
225
- const lines = content.length ? content.split(/\r?\n/) : [];
226
- 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()
227
247
 
228
- const sectionStart = lines.findIndex(line => line === 'mcp_servers:');
248
+ const sectionStart = lines.findIndex((line) => line === 'mcp_servers:')
229
249
  if (sectionStart < 0) {
230
- if (lines.length > 0) lines.push('');
231
- lines.push('mcp_servers:', ...serverBlock);
232
- fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8');
233
- 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
234
254
  }
235
255
 
236
- const sectionEnd = findTopLevelSectionEnd(lines, sectionStart);
237
- const serverStart = lines.findIndex((line, index) => index > sectionStart && index < sectionEnd && line === ' chain-insights:');
256
+ const sectionEnd = findTopLevelSectionEnd(lines, sectionStart)
257
+ const serverStart = lines.findIndex(
258
+ (line, index) => index > sectionStart && index < sectionEnd && line === ' chain-insights:'
259
+ )
238
260
  if (serverStart >= 0) {
239
- const serverEnd = findHermesServerEnd(lines, serverStart, sectionEnd);
240
- lines.splice(serverStart, serverEnd - serverStart, ...serverBlock);
261
+ const serverEnd = findHermesServerEnd(lines, serverStart, sectionEnd)
262
+ lines.splice(serverStart, serverEnd - serverStart, ...serverBlock)
241
263
  } else {
242
- lines.splice(sectionStart + 1, 0, ...serverBlock);
264
+ lines.splice(sectionStart + 1, 0, ...serverBlock)
243
265
  }
244
266
 
245
- fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8');
267
+ fs.writeFileSync(configFile, `${lines.join('\n')}\n`, 'utf8')
246
268
  }
247
269
 
248
270
  if (hasHermes) {
249
- const hermesConfig = path.join(homeDir, '.hermes', 'config.yaml');
250
- installHermesMcp(hermesConfig, proxyBinPath);
251
- 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}`)
252
274
  }
253
275
 
254
276
  // ─── 5. Print installation summary ────────────────────────────────────────
255
277
 
256
- console.log(`\n${bold}${green}Chain Insights installed${reset}`);
278
+ console.log(`\n${bold}${green}Chain Insights installed${reset}`)
257
279
  for (const target of skillsTargets) {
258
- console.log(` ${cyan}${target.name} skills:${reset} ${target.dir}`);
280
+ console.log(` ${cyan}${target.name} skills:${reset} ${target.dir}`)
259
281
  }
260
- console.log(` ${cyan}Config:${reset} ${configPath}`);
261
- console.log(` ${cyan}Data dir:${reset} ${dataDir}`);
262
- console.log(`\n${dim}Run ${reset}${cyan}chain-insights status${reset}${dim} to verify the installation.${reset}\n`);
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').then(({ createProxy }) => createProxy()).catch((err) => {
8
- process.stderr.write(`Failed to load chain-insights MCP proxy: ${err.message}\n`);
9
- process.exit(1);
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-DakpK96I.cjs");
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): { warnings?: string[]; search_limits?: Record<string, unknown> } {\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 = 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({ timestamp: startedAt, tool, args: toolArgs, outcome: 'ok', duration_ms: Date.now() - startedAt, warnings, search_limits })\n return result\n } catch (err) {\n await appendActionLog({ timestamp: startedAt, tool, args: toolArgs, outcome: 'error', duration_ms: Date.now() - startedAt, error: (err as Error).message })\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,QAAmF;CAC5H,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;CAIhC,OAAO;EAAE;EAAU,eAHG,SAAS,KAAK,KAAK,SAAS,MAAM,gBAAgB,IACnE,MAAM,mBACP,KAAA;CAC6B;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;IAAE,WAAW;IAAW;IAAM,MAAM;IAAU,SAAS;IAAM,aAAa,KAAK,IAAI,IAAI;IAAW;IAAU;GAAc,CAAC;GACjJ,OAAO;EACT,SAAS,KAAK;GACZ,MAAM,gBAAgB;IAAE,WAAW;IAAW;IAAM,MAAM;IAAU,SAAS;IAAS,aAAa,KAAK,IAAI,IAAI;IAAW,OAAQ,IAAc;GAAQ,CAAC;GAC1J,MAAM;EACR;CACF;CACA,OAAO,WAAW;CAClB,OAAO;AACT"}
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,QAAQ,UAA2B,OAAO,UAAU,YAAY,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC,IAC3G,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;AAUA,SAAgB,cAAc,iBAAkC;CAC9D,OAAO,oBAAoB,CAAC,EAAE,QAAQ,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB;AACpG"}
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-DakpK96I.cjs");
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-DA6KRx60.mjs").then((n) => n.t);
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-CJwx_2Rr.mjs.map
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-DakpK96I.cjs");
2
- const require_output_root = require("./output-root-C1PFNCqb.cjs");
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-DE0dUT3P.cjs")).then((n) => n.config_exports);
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-C1PFNCqb.cjs")).then((n) => n.output_root_exports);
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-C1PFNCqb.cjs")).then((n) => n.output_root_exports);
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-D4Cm69ob.cjs");
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: require_app.createApp().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-CJwx_2Rr.mjs";
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: createApp().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-D1D-_t5l.mjs.map
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"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { a as resolveGraphMcpEndpoint, n as applyMcpAuthHeaders } from "./client-i3WtsKA4.mjs";
2
+ import { a as resolveGraphMcpEndpoint, n as applyMcpAuthHeaders } from "./client-D4aM4bzP.mjs";
3
3
  //#region src/mcp/capabilities.ts
4
4
  var capabilities_exports = /* @__PURE__ */ __exportAll({
5
5
  PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS: () => PUBLIC_CHAIN_INSIGHTS_TOOL_STATUS,
@@ -126,4 +126,4 @@ function formatNetworkCapabilities(document) {
126
126
  //#endregion
127
127
  export { mirrorGraphNetworkCapabilities as n, capabilities_exports as t };
128
128
 
129
- //# sourceMappingURL=capabilities-CxvK7LKZ.mjs.map
129
+ //# sourceMappingURL=capabilities-BZI6qROg.mjs.map