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.
Files changed (153) hide show
  1. package/README.md +48 -124
  2. package/bin/cli.js +4 -4
  3. package/bin/install.cjs +157 -118
  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-rLNPG-O2.mjs → capabilities-BZI6qROg.mjs} +32 -19
  16. package/dist/capabilities-BZI6qROg.mjs.map +1 -0
  17. package/dist/{capabilities-BR985-bW.cjs → capabilities-CPcrxodx.cjs} +35 -22
  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-DLqndaSv.cjs → client-CqAzD519.cjs} +38 -5
  22. package/dist/{client-BD9FLUt2.mjs → client-D4aM4bzP.mjs} +38 -5
  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-Jp_AIMZ7.mjs → init-Djhml4ih.mjs} +10 -10
  40. package/dist/init-Djhml4ih.mjs.map +1 -0
  41. package/dist/{init-HVwOeOyo.cjs → init-DvrKxK15.cjs} +10 -10
  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 +23 -42
  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 +17 -36
  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 +4 -49
  87. package/docs/development.md +0 -27
  88. package/docs/graph-query-compatibility.md +69 -58
  89. package/docs/graph-tools.md +16 -18
  90. package/docs/investigation-workspaces.md +0 -35
  91. package/docs/mcp-proxy.md +15 -17
  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-address-risk/SKILL.md +20 -77
  96. package/skills/chain-insights-cypher/SKILL.md +92 -213
  97. package/skills/chain-insights-cypher/agents/openai.yaml +3 -3
  98. package/skills/chain-insights-schema-bittensor/SKILL.md +99 -0
  99. package/skills/chain-insights-schema-evm/SKILL.md +104 -0
  100. package/dist/app-CJwx_2Rr.mjs.map +0 -1
  101. package/dist/artifact-server-D1D-_t5l.mjs.map +0 -1
  102. package/dist/capabilities-rLNPG-O2.mjs.map +0 -1
  103. package/dist/cases-C1XewLdx.mjs +0 -155
  104. package/dist/cases-C1XewLdx.mjs.map +0 -1
  105. package/dist/cases-CVtc7Rf8.cjs +0 -160
  106. package/dist/client-BD9FLUt2.mjs.map +0 -1
  107. package/dist/config-CwIb-tav.mjs +0 -33
  108. package/dist/config-CwIb-tav.mjs.map +0 -1
  109. package/dist/config-Cy-qzx4W.cjs +0 -33
  110. package/dist/config-DA6KRx60.mjs.map +0 -1
  111. package/dist/init-9jY5rCcH.cjs +0 -24
  112. package/dist/init-BiRfMQcA.mjs +0 -26
  113. package/dist/init-BiRfMQcA.mjs.map +0 -1
  114. package/dist/init-Jp_AIMZ7.mjs.map +0 -1
  115. package/dist/lock-Cs-a3Ng1.cjs +0 -38
  116. package/dist/lock-D_sWGS7W.mjs +0 -40
  117. package/dist/lock-D_sWGS7W.mjs.map +0 -1
  118. package/dist/merge-CJRZpuZ4.mjs.map +0 -1
  119. package/dist/paths-CffVTUCv.cjs +0 -31
  120. package/dist/paths-_3isihzD.mjs +0 -21
  121. package/dist/paths-_3isihzD.mjs.map +0 -1
  122. package/dist/public-tools-B9_wXs2o.mjs.map +0 -1
  123. package/dist/render-TmSq0f4f.cjs +0 -207
  124. package/dist/render-mxE0Tdsf.mjs +0 -206
  125. package/dist/render-mxE0Tdsf.mjs.map +0 -1
  126. package/dist/report-BFXFrJmj.mjs +0 -36
  127. package/dist/report-BFXFrJmj.mjs.map +0 -1
  128. package/dist/report-BwKQqOCR.cjs +0 -36
  129. package/dist/runner-CSss8Hvy.mjs +0 -33
  130. package/dist/runner-CSss8Hvy.mjs.map +0 -1
  131. package/dist/runner-aErdz2zn.cjs +0 -31
  132. package/dist/schema-0BVyF-aL.mjs.map +0 -1
  133. package/dist/server-BXj6BRI1.mjs.map +0 -1
  134. package/dist/tools-CWwX4yth.mjs.map +0 -1
  135. package/dist/topup-server-D2X7My7i.mjs.map +0 -1
  136. package/dist/usage-status-D2uosC7s.mjs.map +0 -1
  137. package/dist/viz-Cq1jSWSH.mjs.map +0 -1
  138. package/dist/wallet-BrTTnf7w.mjs.map +0 -1
  139. package/docs/monitoring.md +0 -279
  140. package/skills/chain-insights-bittensor-cypher/SKILL.md +0 -240
  141. package/skills/chain-insights-bittensor-cypher/agents/openai.yaml +0 -4
  142. package/skills/chain-insights-cypher/references/gql-translation-matrix.md +0 -22
  143. package/skills/chain-insights-cypher/references/memgraph-examples.md +0 -188
  144. package/skills/chain-insights-developer-experience/SKILL.md +0 -140
  145. package/skills/chain-insights-investigation/SKILL.md +0 -168
  146. package/skills/chain-insights-investigation/agents/openai.yaml +0 -4
  147. package/skills/chain-insights-investigation/scripts/run-target-uat.sh +0 -179
  148. package/skills/chain-insights-monitoring/SKILL.md +0 -160
  149. package/skills/chain-insights-monitoring/references/pm2-scheduling.md +0 -100
  150. package/skills/ci-status/SKILL.md +0 -39
  151. package/skills/test-chain-insights-graph/SKILL.md +0 -110
  152. package/skills/test-chain-insights-graph/agents/openai.yaml +0 -4
  153. 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 = 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')
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) skillsTargets.push({ name: 'Claude Code', dir: path.join(homeDir, '.claude', 'skills') });
46
- if (hasCodex) skillsTargets.push({ name: 'Codex', dir: path.join(homeDir, '.codex', 'skills') });
47
- if (hasHermes) skillsTargets.push({ name: 'Hermes', dir: path.join(homeDir, '.hermes', 'skills', 'chain-insights') });
48
- 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
+ })
49
78
 
50
79
  // ─── 1. Copy agent skills ─────────────────────────────────────────────────
51
80
 
52
81
  function copyCommandsAsClaudeSkills(srcDir, targetDir) {
53
82
  if (!fs.existsSync(srcDir)) {
54
- console.error(`Skills source not found: ${srcDir}`);
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
- // NOTE: do NOT blanket-delete by prefix here. The target is a shared skills
61
- // directory that may hold the user's own skills (including unrelated `ci-*`
62
- // ones). Clean reinstall is handled per shipped skill below — each skill dir
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
- // Recurse into skills/ directory; each subdirectory becomes a skill dir.
80
- const skillDirs = fs.readdirSync(srcDir, { withFileTypes: true })
81
- .filter(e => e.isDirectory());
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 skillDir of skillDirs) {
84
- const skillSrc = path.join(srcDir, skillDir.name);
85
- const skillDest = path.join(targetDir, skillDir.name);
86
- fs.rmSync(skillDest, { recursive: true, force: true });
87
- copyTree(skillSrc, skillDest);
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: 'paid',
108
- serverPort: 4321,
109
- dataDir: dataDir,
110
- version: '1',
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(` ${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
+ )
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(/^mcp_servers:\s*\{\s*\}\s*$/m, ['mcp_servers:', ...serverBlock].join('\n'));
206
- fs.writeFileSync(configFile, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
207
- 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
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((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
+ )
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(`\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"}