codebase-onboarder 0.5.0 → 0.7.0

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 (448) hide show
  1. package/README.md +196 -47
  2. package/bin/build-if-source.js +23 -0
  3. package/cli/commands.d.ts +26 -0
  4. package/cli/commands.js +836 -811
  5. package/cli/commands.js.map +1 -0
  6. package/cli/explorer/advanced.d.ts +14 -0
  7. package/cli/explorer/advanced.js +449 -0
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +396 -229
  11. package/cli/explorer/app.js.map +1 -0
  12. package/cli/explorer/assistant.d.ts +9 -0
  13. package/cli/explorer/assistant.js +58 -0
  14. package/cli/explorer/assistant.js.map +1 -0
  15. package/cli/explorer/commands.d.ts +17 -0
  16. package/cli/explorer/commands.js +400 -162
  17. package/cli/explorer/commands.js.map +1 -0
  18. package/cli/explorer/featureViews.d.ts +124 -0
  19. package/cli/explorer/featureViews.js +179 -0
  20. package/cli/explorer/featureViews.js.map +1 -0
  21. package/cli/explorer/github.d.ts +21 -0
  22. package/cli/explorer/github.js +73 -0
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +173 -0
  26. package/cli/explorer/graphs.js.map +1 -0
  27. package/cli/explorer/picker.d.ts +22 -0
  28. package/cli/explorer/picker.js +150 -0
  29. package/cli/explorer/picker.js.map +1 -0
  30. package/cli/explorer/report.d.ts +85 -0
  31. package/cli/explorer/report.js +60 -0
  32. package/cli/explorer/report.js.map +1 -0
  33. package/cli/explorer/session.d.ts +449 -0
  34. package/cli/explorer/session.js +184 -95
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +496 -412
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +33 -0
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +235 -138
  44. package/cli/main.js.map +1 -0
  45. package/cli/prompt.d.ts +5 -0
  46. package/cli/prompt.js +84 -80
  47. package/cli/prompt.js.map +1 -0
  48. package/cli/scanOptions.d.ts +2 -0
  49. package/cli/scanOptions.js +11 -0
  50. package/cli/scanOptions.js.map +1 -0
  51. package/cli/ui.d.ts +33 -0
  52. package/cli/ui.js +55 -35
  53. package/cli/ui.js.map +1 -0
  54. package/cli/wizard.d.ts +187 -0
  55. package/cli/wizard.js +304 -284
  56. package/cli/wizard.js.map +1 -0
  57. package/package.json +15 -4
  58. package/public/app.d.ts +1 -0
  59. package/public/app.js +1057 -1009
  60. package/public/app.js.map +1 -0
  61. package/public/browser.d.ts +68 -0
  62. package/public/browser.js +2 -0
  63. package/public/browser.js.map +1 -0
  64. package/public/index.html +30 -13
  65. package/public/js/about.d.ts +6 -0
  66. package/public/js/about.js +165 -172
  67. package/public/js/about.js.map +1 -0
  68. package/public/js/aiDraft.d.ts +3 -0
  69. package/public/js/aiDraft.js +116 -124
  70. package/public/js/aiDraft.js.map +1 -0
  71. package/public/js/analysisPanel.d.ts +7 -0
  72. package/public/js/analysisPanel.js +166 -166
  73. package/public/js/analysisPanel.js.map +1 -0
  74. package/public/js/analysisReport.d.ts +38 -0
  75. package/public/js/analysisReport.js +105 -110
  76. package/public/js/analysisReport.js.map +1 -0
  77. package/public/js/api.d.ts +26 -0
  78. package/public/js/api.js +176 -167
  79. package/public/js/api.js.map +1 -0
  80. package/public/js/atlas.d.ts +6 -0
  81. package/public/js/atlas.js +50 -58
  82. package/public/js/atlas.js.map +1 -0
  83. package/public/js/blameView.d.ts +2 -0
  84. package/public/js/blameView.js +36 -37
  85. package/public/js/blameView.js.map +1 -0
  86. package/public/js/codeTab.d.ts +3 -0
  87. package/public/js/codeTab.js +175 -180
  88. package/public/js/codeTab.js.map +1 -0
  89. package/public/js/codeViewer.d.ts +5 -0
  90. package/public/js/codeViewer.js +162 -150
  91. package/public/js/codeViewer.js.map +1 -0
  92. package/public/js/components/FileChip.d.ts +9 -0
  93. package/public/js/components/FileChip.js +45 -49
  94. package/public/js/components/FileChip.js.map +1 -0
  95. package/public/js/components/MetricSparkline.d.ts +7 -0
  96. package/public/js/components/MetricSparkline.js +35 -44
  97. package/public/js/components/MetricSparkline.js.map +1 -0
  98. package/public/js/components/RiskBadge.d.ts +9 -0
  99. package/public/js/components/RiskBadge.js +55 -63
  100. package/public/js/components/RiskBadge.js.map +1 -0
  101. package/public/js/deepAnalysisView.d.ts +99 -0
  102. package/public/js/deepAnalysisView.js +282 -302
  103. package/public/js/deepAnalysisView.js.map +1 -0
  104. package/public/js/diagramPane.d.ts +8 -0
  105. package/public/js/diagramPane.js +133 -135
  106. package/public/js/diagramPane.js.map +1 -0
  107. package/public/js/diffView.d.ts +6 -0
  108. package/public/js/diffView.js +222 -209
  109. package/public/js/diffView.js.map +1 -0
  110. package/public/js/docsView.d.ts +2 -0
  111. package/public/js/docsView.js +441 -442
  112. package/public/js/docsView.js.map +1 -0
  113. package/public/js/dom.d.ts +189 -0
  114. package/public/js/dom.js +5 -0
  115. package/public/js/dom.js.map +1 -0
  116. package/public/js/fileSourceBrowser.d.ts +5 -0
  117. package/public/js/fileSourceBrowser.js +33 -41
  118. package/public/js/fileSourceBrowser.js.map +1 -0
  119. package/public/js/flameGraph.d.ts +5 -0
  120. package/public/js/flameGraph.js +62 -72
  121. package/public/js/flameGraph.js.map +1 -0
  122. package/public/js/forceGraph.d.ts +10 -0
  123. package/public/js/forceGraph.js +581 -556
  124. package/public/js/forceGraph.js.map +1 -0
  125. package/public/js/heatmap.d.ts +4 -0
  126. package/public/js/heatmap.js +142 -164
  127. package/public/js/heatmap.js.map +1 -0
  128. package/public/js/highlight.d.ts +2 -0
  129. package/public/js/highlight.js +116 -107
  130. package/public/js/highlight.js.map +1 -0
  131. package/public/js/html.d.ts +2 -0
  132. package/public/js/html.js +14 -44
  133. package/public/js/html.js.map +1 -0
  134. package/public/js/insightsView.d.ts +5 -0
  135. package/public/js/insightsView.js +93 -111
  136. package/public/js/insightsView.js.map +1 -0
  137. package/public/js/inspector.d.ts +37 -0
  138. package/public/js/inspector.js +416 -445
  139. package/public/js/inspector.js.map +1 -0
  140. package/public/js/llm.d.ts +113 -0
  141. package/public/js/llm.js +233 -272
  142. package/public/js/llm.js.map +1 -0
  143. package/public/js/mapView.d.ts +3 -0
  144. package/public/js/mapView.js +121 -120
  145. package/public/js/mapView.js.map +1 -0
  146. package/public/js/markdown.d.ts +3 -0
  147. package/public/js/markdown.js +84 -91
  148. package/public/js/markdown.js.map +1 -0
  149. package/public/js/mindmap.d.ts +32 -0
  150. package/public/js/mindmap.js +244 -259
  151. package/public/js/mindmap.js.map +1 -0
  152. package/public/js/mobilePanels.d.ts +5 -0
  153. package/public/js/mobilePanels.js +50 -0
  154. package/public/js/mobilePanels.js.map +1 -0
  155. package/public/js/repoFiles.d.ts +12 -0
  156. package/public/js/repoFiles.js +28 -27
  157. package/public/js/repoFiles.js.map +1 -0
  158. package/public/js/reviewView.d.ts +28 -0
  159. package/public/js/reviewView.js +256 -0
  160. package/public/js/reviewView.js.map +1 -0
  161. package/public/js/sbomView.d.ts +3 -0
  162. package/public/js/sbomView.js +59 -67
  163. package/public/js/sbomView.js.map +1 -0
  164. package/public/js/scanCache.d.ts +16 -0
  165. package/public/js/scanCache.js +67 -65
  166. package/public/js/scanCache.js.map +1 -0
  167. package/public/js/search.d.ts +7 -0
  168. package/public/js/search.js +868 -800
  169. package/public/js/search.js.map +1 -0
  170. package/public/js/searchLocal.d.ts +42 -0
  171. package/public/js/searchLocal.js +87 -0
  172. package/public/js/searchLocal.js.map +1 -0
  173. package/public/js/serverSettings.d.ts +5 -0
  174. package/public/js/serverSettings.js +153 -164
  175. package/public/js/serverSettings.js.map +1 -0
  176. package/public/js/state.d.ts +798 -0
  177. package/public/js/state.js +114 -122
  178. package/public/js/state.js.map +1 -0
  179. package/public/js/tour.d.ts +6 -0
  180. package/public/js/tour.js +5 -7
  181. package/public/js/tour.js.map +1 -0
  182. package/public/js/transitions.d.ts +2 -0
  183. package/public/js/transitions.js +9 -10
  184. package/public/js/transitions.js.map +1 -0
  185. package/public/js/tree.d.ts +12 -0
  186. package/public/js/tree.js +157 -154
  187. package/public/js/tree.js.map +1 -0
  188. package/public/js/workflowsView.d.ts +3 -0
  189. package/public/js/workflowsView.js +15 -22
  190. package/public/js/workflowsView.js.map +1 -0
  191. package/public/search.css +495 -0
  192. package/public/styles.css +163 -346
  193. package/public/workbench.css +166 -0
  194. package/server/apiAuth.d.ts +3 -0
  195. package/server/apiAuth.js +18 -20
  196. package/server/apiAuth.js.map +1 -0
  197. package/server/apiDiff.d.ts +2 -0
  198. package/server/apiDiff.js +20 -18
  199. package/server/apiDiff.js.map +1 -0
  200. package/server/apiDocs.d.ts +2 -0
  201. package/server/apiDocs.js +38 -40
  202. package/server/apiDocs.js.map +1 -0
  203. package/server/apiFile.d.ts +1 -0
  204. package/server/apiFile.js +14 -17
  205. package/server/apiFile.js.map +1 -0
  206. package/server/apiGitBlame.d.ts +2 -0
  207. package/server/apiGitBlame.js +50 -51
  208. package/server/apiGitBlame.js.map +1 -0
  209. package/server/apiMcp.d.ts +4 -0
  210. package/server/apiMcp.js +64 -66
  211. package/server/apiMcp.js.map +1 -0
  212. package/server/apiReview.d.ts +2 -0
  213. package/server/apiReview.js +45 -0
  214. package/server/apiReview.js.map +1 -0
  215. package/server/apiScan.d.ts +5 -0
  216. package/server/apiScan.js +90 -71
  217. package/server/apiScan.js.map +1 -0
  218. package/server/apiSearch.d.ts +33 -0
  219. package/server/apiSearch.js +184 -200
  220. package/server/apiSearch.js.map +1 -0
  221. package/server/apiSettings.d.ts +4 -0
  222. package/server/apiSettings.js +84 -85
  223. package/server/apiSettings.js.map +1 -0
  224. package/server/apiTools.d.ts +3 -0
  225. package/server/apiTools.js +55 -56
  226. package/server/apiTools.js.map +1 -0
  227. package/server/auth.d.ts +5 -0
  228. package/server/auth.js +54 -51
  229. package/server/auth.js.map +1 -0
  230. package/server/config.d.ts +42 -0
  231. package/server/config.js +189 -194
  232. package/server/config.js.map +1 -0
  233. package/server/daemon.d.ts +12 -0
  234. package/server/daemon.js +124 -114
  235. package/server/daemon.js.map +1 -0
  236. package/server/fileSourceNode.d.ts +2 -0
  237. package/server/fileSourceNode.js +34 -36
  238. package/server/fileSourceNode.js.map +1 -0
  239. package/server/gitClone.d.ts +9 -0
  240. package/server/gitClone.js +59 -61
  241. package/server/gitClone.js.map +1 -0
  242. package/server/gitDiff.d.ts +36 -0
  243. package/server/gitDiff.js +271 -169
  244. package/server/gitDiff.js.map +1 -0
  245. package/server/gitHistory.d.ts +12 -0
  246. package/server/gitHistory.js +80 -80
  247. package/server/gitHistory.js.map +1 -0
  248. package/server/gitRemote.d.ts +12 -0
  249. package/server/gitRemote.js +60 -0
  250. package/server/gitRemote.js.map +1 -0
  251. package/server/htmlText.d.ts +4 -0
  252. package/server/htmlText.js +16 -23
  253. package/server/htmlText.js.map +1 -0
  254. package/server/http.d.ts +5 -0
  255. package/server/http.js +27 -30
  256. package/server/http.js.map +1 -0
  257. package/server/httpGuards.d.ts +2 -0
  258. package/server/httpGuards.js +47 -43
  259. package/server/httpGuards.js.map +1 -0
  260. package/server/https.d.ts +34 -0
  261. package/server/https.js +121 -111
  262. package/server/https.js.map +1 -0
  263. package/server/index.d.ts +40 -0
  264. package/server/index.js +139 -147
  265. package/server/index.js.map +1 -0
  266. package/server/layout.d.ts +26 -0
  267. package/server/layout.js +59 -37
  268. package/server/layout.js.map +1 -0
  269. package/server/llmProxy.d.ts +3 -0
  270. package/server/llmProxy.js +137 -135
  271. package/server/llmProxy.js.map +1 -0
  272. package/server/logger.d.ts +38 -0
  273. package/server/logger.js +198 -211
  274. package/server/logger.js.map +1 -0
  275. package/server/mcp/analysis.d.ts +29 -0
  276. package/server/mcp/analysis.js +99 -120
  277. package/server/mcp/analysis.js.map +1 -0
  278. package/server/mcp/http.d.ts +9 -0
  279. package/server/mcp/http.js +128 -137
  280. package/server/mcp/http.js.map +1 -0
  281. package/server/mcp/runner.d.ts +68 -0
  282. package/server/mcp/runner.js +208 -218
  283. package/server/mcp/runner.js.map +1 -0
  284. package/server/mcp/server.d.ts +29 -0
  285. package/server/mcp/server.js +166 -179
  286. package/server/mcp/server.js.map +1 -0
  287. package/server/mcp/standalone.d.ts +1 -0
  288. package/server/mcp/standalone.js +8 -10
  289. package/server/mcp/standalone.js.map +1 -0
  290. package/server/mcp/tools.d.ts +2020 -0
  291. package/server/mcp/tools.js +523 -574
  292. package/server/mcp/tools.js.map +1 -0
  293. package/server/paths.d.ts +3 -0
  294. package/server/paths.js +17 -15
  295. package/server/paths.js.map +1 -0
  296. package/server/pidfile.d.ts +9 -0
  297. package/server/pidfile.js +43 -45
  298. package/server/pidfile.js.map +1 -0
  299. package/server/review.d.ts +5 -0
  300. package/server/review.js +35 -0
  301. package/server/review.js.map +1 -0
  302. package/server/router.d.ts +33 -0
  303. package/server/router.js +191 -197
  304. package/server/router.js.map +1 -0
  305. package/server/scanRequest.d.ts +3 -0
  306. package/server/scanRequest.js +36 -0
  307. package/server/scanRequest.js.map +1 -0
  308. package/server/searchIndex.d.ts +17 -0
  309. package/server/searchIndex.js +83 -80
  310. package/server/searchIndex.js.map +1 -0
  311. package/server/sessions.d.ts +14 -0
  312. package/server/sessions.js +62 -70
  313. package/server/sessions.js.map +1 -0
  314. package/server/startup.d.ts +118 -0
  315. package/server/startup.js +143 -148
  316. package/server/startup.js.map +1 -0
  317. package/server/static.d.ts +6 -0
  318. package/server/static.js +28 -31
  319. package/server/static.js.map +1 -0
  320. package/server/tools/install.d.ts +4 -0
  321. package/server/tools/install.js +176 -185
  322. package/server/tools/install.js.map +1 -0
  323. package/server/tools/parse.d.ts +8 -0
  324. package/server/tools/parse.js +112 -114
  325. package/server/tools/parse.js.map +1 -0
  326. package/server/tools/platform.d.ts +9 -0
  327. package/server/tools/platform.js +49 -47
  328. package/server/tools/platform.js.map +1 -0
  329. package/server/tools/registry.d.ts +137 -0
  330. package/server/tools/registry.js +173 -172
  331. package/server/tools/registry.js.map +1 -0
  332. package/server/tools/scan.d.ts +34 -0
  333. package/server/tools/scan.js +89 -98
  334. package/server/tools/scan.js.map +1 -0
  335. package/server/tools.d.ts +34 -0
  336. package/server/tools.js +94 -107
  337. package/server/tools.js.map +1 -0
  338. package/server/tunnel.d.ts +29 -0
  339. package/server/tunnel.js +61 -66
  340. package/server/tunnel.js.map +1 -0
  341. package/shared/analyzer/docs.d.ts +16 -0
  342. package/shared/analyzer/docs.js +99 -93
  343. package/shared/analyzer/docs.js.map +1 -0
  344. package/shared/analyzer/explainLocal.d.ts +4 -0
  345. package/shared/analyzer/explainLocal.js +122 -142
  346. package/shared/analyzer/explainLocal.js.map +1 -0
  347. package/shared/analyzer/github.d.ts +37 -0
  348. package/shared/analyzer/github.js +79 -0
  349. package/shared/analyzer/github.js.map +1 -0
  350. package/shared/analyzer/graph.d.ts +41 -0
  351. package/shared/analyzer/graph.js +393 -400
  352. package/shared/analyzer/graph.js.map +1 -0
  353. package/shared/analyzer/health.d.ts +33 -0
  354. package/shared/analyzer/health.js +197 -193
  355. package/shared/analyzer/health.js.map +1 -0
  356. package/shared/analyzer/history.d.ts +29 -0
  357. package/shared/analyzer/history.js +110 -111
  358. package/shared/analyzer/history.js.map +1 -0
  359. package/shared/analyzer/jsonConfig.d.ts +2 -0
  360. package/shared/analyzer/jsonConfig.js +61 -0
  361. package/shared/analyzer/jsonConfig.js.map +1 -0
  362. package/shared/analyzer/languages/csharp.d.ts +26 -0
  363. package/shared/analyzer/languages/csharp.js +83 -88
  364. package/shared/analyzer/languages/csharp.js.map +1 -0
  365. package/shared/analyzer/languages/generic.d.ts +23 -0
  366. package/shared/analyzer/languages/generic.js +94 -105
  367. package/shared/analyzer/languages/generic.js.map +1 -0
  368. package/shared/analyzer/languages/go.d.ts +24 -0
  369. package/shared/analyzer/languages/go.js +45 -54
  370. package/shared/analyzer/languages/go.js.map +1 -0
  371. package/shared/analyzer/languages/index.d.ts +6 -0
  372. package/shared/analyzer/languages/index.js +19 -20
  373. package/shared/analyzer/languages/index.js.map +1 -0
  374. package/shared/analyzer/languages/java.d.ts +20 -0
  375. package/shared/analyzer/languages/java.js +73 -78
  376. package/shared/analyzer/languages/java.js.map +1 -0
  377. package/shared/analyzer/languages/javascript.d.ts +12 -0
  378. package/shared/analyzer/languages/javascript.js +258 -209
  379. package/shared/analyzer/languages/javascript.js.map +1 -0
  380. package/shared/analyzer/languages/python.d.ts +23 -0
  381. package/shared/analyzer/languages/python.js +92 -100
  382. package/shared/analyzer/languages/python.js.map +1 -0
  383. package/shared/analyzer/languages/rust.d.ts +17 -0
  384. package/shared/analyzer/languages/rust.js +96 -98
  385. package/shared/analyzer/languages/rust.js.map +1 -0
  386. package/shared/analyzer/languages/typescript.d.ts +11 -0
  387. package/shared/analyzer/languages/typescript.js +117 -116
  388. package/shared/analyzer/languages/typescript.js.map +1 -0
  389. package/shared/analyzer/licenses.d.ts +18 -0
  390. package/shared/analyzer/licenses.js +130 -133
  391. package/shared/analyzer/licenses.js.map +1 -0
  392. package/shared/analyzer/metrics.d.ts +20 -0
  393. package/shared/analyzer/metrics.js +112 -104
  394. package/shared/analyzer/metrics.js.map +1 -0
  395. package/shared/analyzer/pathUtil.d.ts +10 -0
  396. package/shared/analyzer/pathUtil.js +29 -35
  397. package/shared/analyzer/pathUtil.js.map +1 -0
  398. package/shared/analyzer/patterns.d.ts +11 -0
  399. package/shared/analyzer/patterns.js +231 -253
  400. package/shared/analyzer/patterns.js.map +1 -0
  401. package/shared/analyzer/scan.d.ts +62 -0
  402. package/shared/analyzer/scan.js +451 -442
  403. package/shared/analyzer/scan.js.map +1 -0
  404. package/shared/analyzer/scanControl.d.ts +11 -0
  405. package/shared/analyzer/scanControl.js +62 -0
  406. package/shared/analyzer/scanControl.js.map +1 -0
  407. package/shared/analyzer/security.d.ts +18 -0
  408. package/shared/analyzer/security.js +159 -159
  409. package/shared/analyzer/security.js.map +1 -0
  410. package/shared/analyzer/services.d.ts +5 -0
  411. package/shared/analyzer/services.js +141 -138
  412. package/shared/analyzer/services.js.map +1 -0
  413. package/shared/analyzer/stack.d.ts +6 -0
  414. package/shared/analyzer/stack.js +134 -141
  415. package/shared/analyzer/stack.js.map +1 -0
  416. package/shared/analyzer/tour.d.ts +2 -0
  417. package/shared/analyzer/tour.js +45 -53
  418. package/shared/analyzer/tour.js.map +1 -0
  419. package/shared/analyzer/util.d.ts +8 -0
  420. package/shared/analyzer/util.js +130 -76
  421. package/shared/analyzer/util.js.map +1 -0
  422. package/shared/analyzer/workflows.d.ts +8 -0
  423. package/shared/analyzer/workflows.js +139 -134
  424. package/shared/analyzer/workflows.js.map +1 -0
  425. package/shared/contracts.d.ts +250 -0
  426. package/shared/contracts.js +2 -0
  427. package/shared/contracts.js.map +1 -0
  428. package/shared/diagram/aiFacts.d.ts +35 -0
  429. package/shared/diagram/aiFacts.js +102 -105
  430. package/shared/diagram/aiFacts.js.map +1 -0
  431. package/shared/diagram/aiMermaid.d.ts +5 -0
  432. package/shared/diagram/aiMermaid.js +46 -45
  433. package/shared/diagram/aiMermaid.js.map +1 -0
  434. package/shared/diagram/atlas.d.ts +16 -0
  435. package/shared/diagram/atlas.js +51 -60
  436. package/shared/diagram/atlas.js.map +1 -0
  437. package/shared/diagram/mermaid.d.ts +43 -0
  438. package/shared/diagram/mermaid.js +348 -370
  439. package/shared/diagram/mermaid.js.map +1 -0
  440. package/shared/review/contracts.d.ts +95 -0
  441. package/shared/review/contracts.js +2 -0
  442. package/shared/review/contracts.js.map +1 -0
  443. package/shared/review/review.d.ts +10 -0
  444. package/shared/review/review.js +184 -0
  445. package/shared/review/review.js.map +1 -0
  446. package/shared/search/query.d.ts +32 -0
  447. package/shared/search/query.js +395 -388
  448. package/shared/search/query.js.map +1 -0
package/server/apiMcp.js CHANGED
@@ -14,10 +14,8 @@
14
14
  // The GET is guarded too: the status includes the command line an agent is
15
15
  // configured with, which a foreign page has no business having even though it is
16
16
  // not a secret.
17
-
18
17
  import { sendJSON } from './http.js';
19
18
  import { mcpCommand, mcpEntryPoint } from './mcp/runner.js';
20
-
21
19
  // Every handler here has the same shape: work out the answer, then let whoever is
22
20
  // driving decide how it gets sent. That split exists so `config.handleMcp` — a test
23
21
  // double, or an embedding that supervises MCP itself — can observe the real result
@@ -27,84 +25,84 @@ import { mcpCommand, mcpEntryPoint } from './mcp/runner.js';
27
25
  // The hook owns the response when present. `server/index.js` supplies none, so the
28
26
  // ordinary path is exactly what it was.
29
27
  function reply(res, config, method, req, result) {
30
- if (typeof config?.handleMcp === 'function') {
31
- config.handleMcp(method, req, res, result);
32
- return;
33
- }
34
- sendJSON(res, result.status, result.body);
28
+ if (typeof config?.handleMcp === 'function') {
29
+ config.handleMcp(method, req, res, result);
30
+ return;
31
+ }
32
+ sendJSON(res, result.status, result.body);
35
33
  }
36
-
37
34
  // The runner, however the caller named it. `mcp` is what `server/index.js` sets;
38
35
  // `runner` is accepted because that is the obvious name and a test reaching for it
39
36
  // should not have to read the index to find the convention.
40
37
  function runnerFor(config) {
41
- return config?.mcp || config?.runner || null;
38
+ return config?.mcp || config?.runner || null;
42
39
  }
43
-
44
40
  export function handleMcpStatus(req, res, config) {
45
- const runner = runnerFor(config);
46
- if (!runner) {
47
- // The app is running without the control surface (a test, or an embedding
48
- // that drives MCP itself). Say so rather than pretending it is stopped.
49
- return reply(res, config, 'GET /api/mcp', req, {
50
- status: 200,
51
- body: {
52
- running: false,
53
- state: 'unavailable',
54
- reason: 'MCP supervision is not enabled in this process.',
55
- tools: [],
56
- },
57
- });
58
- }
59
- reply(res, config, 'GET /api/mcp', req, { status: 200, body: runner.status() });
41
+ const runner = runnerFor(config);
42
+ if (!runner) {
43
+ // The app is running without the control surface (a test, or an embedding
44
+ // that drives MCP itself). Say so rather than pretending it is stopped.
45
+ return reply(res, config, 'GET /api/mcp', req, {
46
+ status: 200,
47
+ body: {
48
+ running: false,
49
+ state: 'unavailable',
50
+ reason: 'MCP supervision is not enabled in this process.',
51
+ tools: [],
52
+ },
53
+ });
54
+ }
55
+ reply(res, config, 'GET /api/mcp', req, { status: 200, body: runner.status() });
60
56
  }
61
-
62
57
  export async function handleMcpStart(req, res, config) {
63
- const runner = runnerFor(config);
64
- if (!runner) return reply(res, config, 'POST /api/mcp/start', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
65
- try {
66
- const status = await runner.start();
67
- reply(res, config, 'POST /api/mcp/start', req, { status: 200, body: status });
68
- } catch (err) {
69
- // A start that cannot happen is usually a missing Node, a bad path, or a
70
- // child that failed its handshake — the message says which, because the
71
- // person clicking the button can only fix one of those three.
72
- reply(res, config, 'POST /api/mcp/start', req, { status: 500, body: { error: err.message || 'The MCP server could not be started.' } });
73
- }
58
+ const runner = runnerFor(config);
59
+ if (!runner)
60
+ return reply(res, config, 'POST /api/mcp/start', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
61
+ try {
62
+ const status = await runner.start();
63
+ reply(res, config, 'POST /api/mcp/start', req, { status: 200, body: status });
64
+ }
65
+ catch (err) {
66
+ // A start that cannot happen is usually a missing Node, a bad path, or a
67
+ // child that failed its handshake — the message says which, because the
68
+ // person clicking the button can only fix one of those three.
69
+ reply(res, config, 'POST /api/mcp/start', req, { status: 500, body: { error: err.message || 'The MCP server could not be started.' } });
70
+ }
74
71
  }
75
-
76
72
  export async function handleMcpStop(req, res, config) {
77
- const runner = runnerFor(config);
78
- if (!runner) return reply(res, config, 'POST /api/mcp/stop', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
79
- try {
80
- const status = await runner.stop();
81
- reply(res, config, 'POST /api/mcp/stop', req, { status: 200, body: status });
82
- } catch (err) {
83
- reply(res, config, 'POST /api/mcp/stop', req, { status: 500, body: { error: err.message || 'The MCP server could not be stopped.' } });
84
- }
73
+ const runner = runnerFor(config);
74
+ if (!runner)
75
+ return reply(res, config, 'POST /api/mcp/stop', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
76
+ try {
77
+ const status = await runner.stop();
78
+ reply(res, config, 'POST /api/mcp/stop', req, { status: 200, body: status });
79
+ }
80
+ catch (err) {
81
+ reply(res, config, 'POST /api/mcp/stop', req, { status: 500, body: { error: err.message || 'The MCP server could not be stopped.' } });
82
+ }
85
83
  }
86
-
87
84
  // The command an agent harness is configured with, shown in the panel so nobody
88
85
  // has to read the README to find it. The paths come from the same function that
89
86
  // starts the child, so the copy-paste text cannot drift from what actually runs.
90
87
  export function handleMcpCommand(req, res, config) {
91
- // The absolute path, not the relative one the runner uses: this text gets
92
- // pasted into a client config that spawns the command from the harness's own
93
- // working directory, where a relative path does not resolve.
94
- const entry = mcpEntryPoint();
95
- reply(res, config, 'GET /api/mcp/command', req, {
96
- status: 200,
97
- body: {
98
- command: entry,
99
- relative: mcpCommand(),
100
- transports: ['stdio'],
101
- // The two config shapes a client is overwhelmingly likely to want. Both use
102
- // the absolute path, for the reason above.
103
- examples: [
104
- { name: 'node', command: process.execPath, args: [entry] },
105
- { name: 'node (assuming node is on PATH)', command: 'node', args: [entry] },
106
- ],
107
- docs: 'https://modelcontextprotocol.io/docs/develop/connect-local-servers',
108
- },
109
- });
88
+ // The absolute path, not the relative one the runner uses: this text gets
89
+ // pasted into a client config that spawns the command from the harness's own
90
+ // working directory, where a relative path does not resolve.
91
+ const entry = mcpEntryPoint();
92
+ reply(res, config, 'GET /api/mcp/command', req, {
93
+ status: 200,
94
+ body: {
95
+ command: entry,
96
+ relative: mcpCommand(),
97
+ transports: ['stdio'],
98
+ // The two config shapes a client is overwhelmingly likely to want. Both use
99
+ // the absolute path, for the reason above.
100
+ examples: [
101
+ { name: 'node', command: process.execPath, args: [entry] },
102
+ { name: 'node (assuming node is on PATH)', command: 'node', args: [entry] },
103
+ ],
104
+ docs: 'https://modelcontextprotocol.io/docs/develop/connect-local-servers',
105
+ },
106
+ });
110
107
  }
108
+ //# sourceMappingURL=apiMcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiMcp.js","sourceRoot":"","sources":["../src/server/apiMcp.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,6EAA6E;AAC7E,mDAAmD;AACnD,mCAAmC;AACnC,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,uEAAuE;AACvE,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,2EAA2E;AAC3E,iFAAiF;AACjF,gBAAgB;AAEhB,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE5D,kFAAkF;AAClF,oFAAoF;AACpF,mFAAmF;AACnF,6EAA6E;AAC7E,2EAA2E;AAC3E,EAAE;AACF,mFAAmF;AACnF,wCAAwC;AACxC,SAAS,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;IAC7C,IAAI,OAAO,MAAM,EAAE,SAAS,KAAK,UAAU,EAAE,CAAC;QAC5C,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO;IACT,CAAC;IACD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,iFAAiF;AACjF,mFAAmF;AACnF,4DAA4D;AAC5D,SAAS,SAAS,CAAC,MAAM;IACvB,OAAO,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,MAAM,IAAI,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM;IAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,0EAA0E;QAC1E,wEAAwE;QACxE,OAAO,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE;YAC7C,MAAM,EAAE,GAAG;YACX,IAAI,EAAE;gBACJ,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,aAAa;gBACpB,MAAM,EAAE,iDAAiD;gBACzD,KAAK,EAAE,EAAE;aACV;SACF,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM;IACnD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,iDAAiD,EAAE,EAAE,CAAC,CAAC;IACxJ,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,yEAAyE;QACzE,wEAAwE;QACxE,8DAA8D;QAC9D,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,sCAAsC,EAAE,EAAE,CAAC,CAAC;IAC1I,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM;IAClD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,iDAAiD,EAAE,EAAE,CAAC,CAAC;IACvJ,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACnC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,oBAAoB,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,IAAI,sCAAsC,EAAE,EAAE,CAAC,CAAC;IACzI,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM;IAC/C,0EAA0E;IAC1E,6EAA6E;IAC7E,6DAA6D;IAC7D,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,sBAAsB,EAAE,GAAG,EAAE;QAC9C,MAAM,EAAE,GAAG;QACX,IAAI,EAAE;YACJ,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,UAAU,EAAE;YACtB,UAAU,EAAE,CAAC,OAAO,CAAC;YACrB,4EAA4E;YAC5E,2CAA2C;YAC3C,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE;gBAC1D,EAAE,IAAI,EAAE,iCAAiC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE;aAC5E;YACD,IAAI,EAAE,oEAAoE;SAC3E;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["// The endpoints behind the topbar button:\n//\n// GET /api/mcp — is it running, what is it offering, what happened\n// POST /api/mcp/start — start the child process\n// POST /api/mcp/stop — stop it\n//\n// The control surface and the protocol are deliberately separate. This process\n// never speaks MCP itself; it supervises a child that does. That is what makes\n// \"stop\" real — closing stdin lets the server drain and exit on its own terms\n// rather than leaving a half-torn-down process holding the scan cache.\n//\n// Starting a server is a side effect on the person's machine, so start and stop\n// take a body like every other POST and are same-origin like every other POST.\n// The GET is guarded too: the status includes the command line an agent is\n// configured with, which a foreign page has no business having even though it is\n// not a secret.\n\nimport { sendJSON } from './http.js';\nimport { mcpCommand, mcpEntryPoint } from './mcp/runner.js';\n\n// Every handler here has the same shape: work out the answer, then let whoever is\n// driving decide how it gets sent. That split exists so `config.handleMcp` — a test\n// double, or an embedding that supervises MCP itself — can observe the real result\n// of the real action rather than standing in for it. A hook that skipped the\n// action would make a start endpoint that never starts anything look fine.\n//\n// The hook owns the response when present. `server/index.js` supplies none, so the\n// ordinary path is exactly what it was.\nfunction reply(res, config, method, req, result) {\n if (typeof config?.handleMcp === 'function') {\n config.handleMcp(method, req, res, result);\n return;\n }\n sendJSON(res, result.status, result.body);\n}\n\n// The runner, however the caller named it. `mcp` is what `server/index.js` sets;\n// `runner` is accepted because that is the obvious name and a test reaching for it\n// should not have to read the index to find the convention.\nfunction runnerFor(config) {\n return config?.mcp || config?.runner || null;\n}\n\nexport function handleMcpStatus(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) {\n // The app is running without the control surface (a test, or an embedding\n // that drives MCP itself). Say so rather than pretending it is stopped.\n return reply(res, config, 'GET /api/mcp', req, {\n status: 200,\n body: {\n running: false,\n state: 'unavailable',\n reason: 'MCP supervision is not enabled in this process.',\n tools: [],\n },\n });\n }\n reply(res, config, 'GET /api/mcp', req, { status: 200, body: runner.status() });\n}\n\nexport async function handleMcpStart(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) return reply(res, config, 'POST /api/mcp/start', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });\n try {\n const status = await runner.start();\n reply(res, config, 'POST /api/mcp/start', req, { status: 200, body: status });\n } catch (err) {\n // A start that cannot happen is usually a missing Node, a bad path, or a\n // child that failed its handshake — the message says which, because the\n // person clicking the button can only fix one of those three.\n reply(res, config, 'POST /api/mcp/start', req, { status: 500, body: { error: err.message || 'The MCP server could not be started.' } });\n }\n}\n\nexport async function handleMcpStop(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) return reply(res, config, 'POST /api/mcp/stop', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });\n try {\n const status = await runner.stop();\n reply(res, config, 'POST /api/mcp/stop', req, { status: 200, body: status });\n } catch (err) {\n reply(res, config, 'POST /api/mcp/stop', req, { status: 500, body: { error: err.message || 'The MCP server could not be stopped.' } });\n }\n}\n\n// The command an agent harness is configured with, shown in the panel so nobody\n// has to read the README to find it. The paths come from the same function that\n// starts the child, so the copy-paste text cannot drift from what actually runs.\nexport function handleMcpCommand(req, res, config) {\n // The absolute path, not the relative one the runner uses: this text gets\n // pasted into a client config that spawns the command from the harness's own\n // working directory, where a relative path does not resolve.\n const entry = mcpEntryPoint();\n reply(res, config, 'GET /api/mcp/command', req, {\n status: 200,\n body: {\n command: entry,\n relative: mcpCommand(),\n transports: ['stdio'],\n // The two config shapes a client is overwhelmingly likely to want. Both use\n // the absolute path, for the reason above.\n examples: [\n { name: 'node', command: process.execPath, args: [entry] },\n { name: 'node (assuming node is on PATH)', command: 'node', args: [entry] },\n ],\n docs: 'https://modelcontextprotocol.io/docs/develop/connect-local-servers',\n },\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ export declare function handleReview(res: ServerResponse, body: unknown): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import { getSession } from './sessions.js';
2
+ import { sendError, sendJSON } from './http.js';
3
+ import { GitDiffError } from './gitDiff.js';
4
+ import { runReview } from './review.js';
5
+ import { reviewProfile } from '../shared/review/review.js';
6
+ export async function handleReview(res, body) {
7
+ if (!body || typeof body !== 'object' || Array.isArray(body))
8
+ return sendError(res, 400, 'Provide a review request object.');
9
+ const b = body;
10
+ if (typeof b.scanId !== 'string')
11
+ return sendError(res, 400, 'Choose a scanned repository.');
12
+ const session = getSession(b.scanId);
13
+ if (!session)
14
+ return sendError(res, 404, 'Scan session expired. Scan this repository again.');
15
+ if (b.mode !== undefined && !['working', 'staged', 'range'].includes(b.mode))
16
+ return sendError(res, 400, 'Choose working, staged, or range review.');
17
+ for (const key of ['base', 'head'])
18
+ if (b[key] !== undefined && (typeof b[key] !== 'string' || b[key].length > 200))
19
+ return sendError(res, 400, 'References must be strings of at most 200 characters.');
20
+ let profile;
21
+ try {
22
+ if (b.profile !== undefined)
23
+ profile = reviewProfile(b.profile);
24
+ }
25
+ catch (error) {
26
+ return sendError(res, 400, error.message);
27
+ }
28
+ const abort = new AbortController();
29
+ const close = () => { if (!res.writableEnded)
30
+ abort.abort(); };
31
+ res.once('close', close);
32
+ try {
33
+ const report = await runReview(session.root, { base: b.base || 'HEAD', head: b.head || '', mode: b.mode || 'working', profile, signal: abort.signal });
34
+ if (!abort.signal.aborted)
35
+ sendJSON(res, 200, report);
36
+ }
37
+ catch (error) {
38
+ if (!abort.signal.aborted)
39
+ sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Review failed.');
40
+ }
41
+ finally {
42
+ res.off('close', close);
43
+ }
44
+ }
45
+ //# sourceMappingURL=apiReview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiReview.js","sourceRoot":"","sources":["../src/server/apiReview.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getSession } from './sessions.js';\nimport { sendError, sendJSON } from './http.js';\nimport { GitDiffError } from './gitDiff.js';\nimport { runReview } from './review.js';\nimport { reviewProfile } from '../shared/review/review.js';\nimport type { ReviewMode, ReviewProfile } from '../shared/review/contracts.js';\n\nexport async function handleReview(res: ServerResponse, body: unknown) {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return sendError(res, 400, 'Provide a review request object.');\n const b = body as Record<string, unknown>;\n if (typeof b.scanId !== 'string') return sendError(res, 400, 'Choose a scanned repository.');\n const session = getSession(b.scanId);\n if (!session) return sendError(res, 404, 'Scan session expired. Scan this repository again.');\n if (b.mode !== undefined && !['working', 'staged', 'range'].includes(b.mode as string)) return sendError(res, 400, 'Choose working, staged, or range review.');\n for (const key of ['base', 'head']) if (b[key] !== undefined && (typeof b[key] !== 'string' || (b[key] as string).length > 200)) return sendError(res, 400, 'References must be strings of at most 200 characters.');\n let profile: ReviewProfile | undefined;\n try { if (b.profile !== undefined) profile = reviewProfile(b.profile); }\n catch (error) { return sendError(res, 400, (error as Error).message); }\n const abort = new AbortController();\n const close = () => { if (!res.writableEnded) abort.abort(); };\n res.once('close', close);\n try {\n const report = await runReview(session.root, { base: b.base as string || 'HEAD', head: b.head as string || '', mode: b.mode as ReviewMode || 'working', profile, signal: abort.signal });\n if (!abort.signal.aborted) sendJSON(res, 200, report);\n } catch (error) {\n if (!abort.signal.aborted) sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Review failed.');\n } finally { res.off('close', close); }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ export declare function handleScan(res: ServerResponse, body: unknown, { projectRoot }: {
3
+ projectRoot: string;
4
+ }): Promise<void>;
5
+ export declare function handleCleanup(res: any, cloneId: any): Promise<void>;
package/server/apiScan.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { parseScanRequest } from './scanRequest.js';
2
+ import { throwIfAborted } from '../shared/analyzer/scanControl.js';
1
3
  // POST /api/scan and DELETE /api/scan/:cloneId — the endpoint the whole app
2
4
  // starts from.
3
5
  //
@@ -13,10 +15,8 @@
13
15
  // hop inside a filter keystroke. The search index travels the same way: built
14
16
  // once at scan time, attached to the session, and queried without re-walking
15
17
  // the tree.
16
-
17
18
  import { promises as fs } from 'node:fs';
18
19
  import path from 'node:path';
19
-
20
20
  import { computeFacts } from '../shared/analyzer/graph.js';
21
21
  import { scanRepo } from '../shared/analyzer/scan.js';
22
22
  import { detectManifest } from '../shared/analyzer/services.js';
@@ -28,92 +28,111 @@ import { gitLog, parseGitLog } from './gitHistory.js';
28
28
  import { sendError, sendJSON } from './http.js';
29
29
  import { expandHome } from './paths.js';
30
30
  import { closeClone, isCloneId, openSession } from './sessions.js';
31
-
32
31
  export async function handleScan(res, body, { projectRoot }) {
33
- const target = await resolveTarget(body, projectRoot);
34
- if (target.error) return sendError(res, 400, target.error);
35
-
36
- const { root, cloneDir, cloneId, gitMeta } = target;
37
- try {
38
- const source = nodeFileSource(root);
39
- const scan = await scanRepo(source);
40
- if (gitMeta) {
41
- // Show the repo's real name up top; keep the temp dir name for honesty,
42
- // so a path in the UI is still a path you could go and look at.
43
- scan.name = gitMeta.repoName;
44
- scan.tempId = path.basename(root);
45
- scan.gitUrl = gitMeta.gitUrl;
32
+ let request;
33
+ try {
34
+ request = parseScanRequest(body);
35
+ if (request.gitUrl)
36
+ assertGitUrl(request.gitUrl);
37
+ }
38
+ catch (error) {
39
+ return sendError(res, 400, error.message);
40
+ }
41
+ const controller = new AbortController();
42
+ const disconnected = () => { if (!res.writableEnded)
43
+ controller.abort(); };
44
+ res.once('close', disconnected);
45
+ let cloneDir;
46
+ try {
47
+ const target = await resolveTarget(request, projectRoot);
48
+ if (target.error)
49
+ return sendError(res, 400, target.error);
50
+ const { root, cloneId, gitMeta } = target;
51
+ cloneDir = target.cloneDir;
52
+ throwIfAborted(controller.signal);
53
+ const source = nodeFileSource(root);
54
+ const scan = await scanRepo(source, { ...request.options, signal: controller.signal });
55
+ if (gitMeta) {
56
+ // Show the repo's real name up top; keep the temp dir name for honesty,
57
+ // so a path in the UI is still a path you could go and look at.
58
+ scan.name = gitMeta.repoName;
59
+ scan.tempId = path.basename(root);
60
+ scan.gitUrl = gitMeta.gitUrl;
61
+ }
62
+ const manifest = await detectManifest(source);
63
+ const facts = computeFacts(scan, manifest);
64
+ // The search index is built from the same files the scanner accepted.
65
+ // Doing it here, against the same FileSource, means a search request
66
+ // becomes a Map lookup plus a scoring pass; the per-file reads the old
67
+ // `/api/search` did on first use are gone. Cap behaviour and skip rules
68
+ // live in `searchIndex.js` and are tested there.
69
+ const codeFiles = scan.files.map((f) => f.path);
70
+ const searchIndexData = await buildSearchIndex(source, codeFiles);
71
+ const history = await collectHistory(root, scan);
72
+ throwIfAborted(controller.signal);
73
+ const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });
74
+ sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });
75
+ }
76
+ catch (err) {
77
+ // A clone that was never scanned successfully has no session to evict it
78
+ // later, so it has to go now or it is a temp directory nobody owns.
79
+ if (cloneDir)
80
+ await removeClone(cloneDir);
81
+ if (!controller.signal.aborted)
82
+ sendError(res, 500, 'The scan failed: ' + err.message);
83
+ }
84
+ finally {
85
+ res.removeListener('close', disconnected);
46
86
  }
47
- const manifest = await detectManifest(source);
48
- const facts = computeFacts(scan, manifest);
49
-
50
- // The search index is built from the same files the scanner accepted.
51
- // Doing it here, against the same FileSource, means a search request
52
- // becomes a Map lookup plus a scoring pass; the per-file reads the old
53
- // `/api/search` did on first use are gone. Cap behaviour and skip rules
54
- // live in `searchIndex.js` and are tested there.
55
- const codeFiles = scan.files.map((f) => f.path);
56
- const searchIndexData = await buildSearchIndex(source, codeFiles);
57
-
58
- const history = await collectHistory(root, scan);
59
- const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });
60
- sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });
61
- } catch (err) {
62
- // A clone that was never scanned successfully has no session to evict it
63
- // later, so it has to go now or it is a temp directory nobody owns.
64
- if (cloneDir) await removeClone(cloneDir);
65
- sendError(res, 500, 'The scan failed: ' + err.message);
66
- }
67
87
  }
68
-
69
88
  // History is a bonus layer over the scan, never a reason for it to fail. When
70
89
  // git is missing, the folder has no .git, or the log somehow blows up, the
71
90
  // payload carries the honest unavailable shape instead — the UI says why
72
91
  // rather than inventing zeros.
73
92
  async function collectHistory(root, scan) {
74
- try {
75
- const log = await gitLog(root);
76
- if (!log.ok) return unavailableHistory(log.reason);
77
- return analyzeHistory(scan, parseGitLog(log.text), { totalCommits: log.totalCommits });
78
- } catch {
79
- return unavailableHistory('The history could not be read — the scan itself is unaffected.');
80
- }
93
+ try {
94
+ const log = await gitLog(root);
95
+ if (!log.ok)
96
+ return unavailableHistory(log.reason);
97
+ return analyzeHistory(scan, parseGitLog(log.text), { totalCommits: log.totalCommits });
98
+ }
99
+ catch {
100
+ return unavailableHistory('The history could not be read — the scan itself is unaffected.');
101
+ }
81
102
  }
82
-
83
103
  // The three request shapes, reduced to a directory. Cloning happens here, which
84
104
  // is why this returns rather than sends: the caller owns cleanup if the scan that
85
105
  // follows fails.
86
106
  async function resolveTarget(body, projectRoot) {
87
- if (body.demo) return { root: projectRoot };
88
-
89
- if (body.path) {
90
- const root = expandHome(String(body.path));
91
- const stat = await fs.stat(root).catch(() => null);
92
- if (!stat?.isDirectory()) {
93
- return { error: 'No folder at ' + root + '. Check the path and try again.' };
107
+ if (body.demo)
108
+ return { root: projectRoot };
109
+ if (body.path) {
110
+ const root = expandHome(String(body.path));
111
+ const stat = await fs.stat(root).catch(() => null);
112
+ if (!stat?.isDirectory()) {
113
+ return { error: 'No folder at ' + root + '. Check the path and try again.' };
114
+ }
115
+ return { root };
116
+ }
117
+ if (body.gitUrl) {
118
+ const url = assertGitUrl(body.gitUrl);
119
+ const clone = await cloneRepo(url);
120
+ return {
121
+ root: clone.dir,
122
+ cloneDir: clone.dir,
123
+ cloneId: clone.id,
124
+ gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },
125
+ };
94
126
  }
95
- return { root };
96
- }
97
-
98
- if (body.gitUrl) {
99
- const url = assertGitUrl(body.gitUrl);
100
- const clone = await cloneRepo(url);
101
- return {
102
- root: clone.dir,
103
- cloneDir: clone.dir,
104
- cloneId: clone.id,
105
- gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },
106
- };
107
- }
108
-
109
- return { error: 'Send a path, a gitUrl, or { demo: true }.' };
127
+ return { error: 'Send a path, a gitUrl, or { demo: true }.' };
110
128
  }
111
-
112
129
  // The page sends this when it is done with a cloned repo. Nothing is reported
113
130
  // about whether the clone was there: the browser cannot act on the difference,
114
131
  // and an unload-time request that 404s looks like a bug in the console.
115
132
  export async function handleCleanup(res, cloneId) {
116
- if (!isCloneId(cloneId)) return sendError(res, 400, 'That is not a clone id.');
117
- await closeClone(cloneId);
118
- sendJSON(res, 200, { ok: true });
133
+ if (!isCloneId(cloneId))
134
+ return sendError(res, 400, 'That is not a clone id.');
135
+ await closeClone(cloneId);
136
+ sendJSON(res, 200, { ok: true });
119
137
  }
138
+ //# sourceMappingURL=apiScan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiScan.js","sourceRoot":"","sources":["../src/server/apiScan.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport type { ScanRequest } from '../shared/contracts.js';\nimport { parseScanRequest } from './scanRequest.js';\nimport { throwIfAborted } from '../shared/analyzer/scanControl.js';\n// POST /api/scan and DELETE /api/scan/:cloneId — the endpoint the whole app\n// starts from.\n//\n// Three ways in, one way out. A local path, a git URL to clone, or `demo: true`\n// meaning \"scan Onboarder itself\", which is how the app has something to show\n// before you have chosen anything. All three end at a root directory, and from\n// there the work is the same three calls the browser would make against its own\n// file source: scan, detect the manifest, compute the facts.\n//\n// The response is deliberately whole. The front end gets scan, facts and\n// manifest in one round trip and holds them for the session, because every view\n// is a projection of those three and re-fetching per view would put a network\n// hop inside a filter keystroke. The search index travels the same way: built\n// once at scan time, attached to the session, and queried without re-walking\n// the tree.\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { computeFacts } from '../shared/analyzer/graph.js';\nimport { scanRepo } from '../shared/analyzer/scan.js';\nimport { detectManifest } from '../shared/analyzer/services.js';\nimport { analyzeHistory, unavailableHistory } from '../shared/analyzer/history.js';\nimport { buildSearchIndex } from './searchIndex.js';\nimport { nodeFileSource } from './fileSourceNode.js';\nimport { assertGitUrl, cloneRepo, removeClone, repoNameFromUrl } from './gitClone.js';\nimport { gitLog, parseGitLog } from './gitHistory.js';\nimport { sendError, sendJSON } from './http.js';\nimport { expandHome } from './paths.js';\nimport { closeClone, isCloneId, openSession } from './sessions.js';\n\nexport async function handleScan(res: ServerResponse, body: unknown, { projectRoot }: { projectRoot: string }) {\n let request: ScanRequest;\n try { request = parseScanRequest(body); if (request.gitUrl) assertGitUrl(request.gitUrl); }\n catch (error) { return sendError(res, 400, error.message); }\n const controller = new AbortController();\n const disconnected = () => { if (!res.writableEnded) controller.abort(); };\n res.once('close', disconnected);\n let cloneDir: string | undefined;\n try {\n const target = await resolveTarget(request, projectRoot);\n if (target.error) return sendError(res, 400, target.error);\n const { root, cloneId, gitMeta } = target;\n cloneDir = target.cloneDir;\n throwIfAborted(controller.signal);\n const source = nodeFileSource(root);\n const scan = await scanRepo(source, { ...request.options, signal: controller.signal });\n if (gitMeta) {\n // Show the repo's real name up top; keep the temp dir name for honesty,\n // so a path in the UI is still a path you could go and look at.\n scan.name = gitMeta.repoName;\n scan.tempId = path.basename(root);\n scan.gitUrl = gitMeta.gitUrl;\n }\n const manifest = await detectManifest(source);\n const facts = computeFacts(scan, manifest);\n\n // The search index is built from the same files the scanner accepted.\n // Doing it here, against the same FileSource, means a search request\n // becomes a Map lookup plus a scoring pass; the per-file reads the old\n // `/api/search` did on first use are gone. Cap behaviour and skip rules\n // live in `searchIndex.js` and are tested there.\n const codeFiles = scan.files.map((f) => f.path);\n const searchIndexData = await buildSearchIndex(source, codeFiles);\n\n const history = await collectHistory(root, scan);\n throwIfAborted(controller.signal);\n const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });\n sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });\n } catch (err) {\n // A clone that was never scanned successfully has no session to evict it\n // later, so it has to go now or it is a temp directory nobody owns.\n if (cloneDir) await removeClone(cloneDir);\n if (!controller.signal.aborted) sendError(res, 500, 'The scan failed: ' + err.message);\n } finally {\n res.removeListener('close', disconnected);\n }\n}\n\n// History is a bonus layer over the scan, never a reason for it to fail. When\n// git is missing, the folder has no .git, or the log somehow blows up, the\n// payload carries the honest unavailable shape instead — the UI says why\n// rather than inventing zeros.\nasync function collectHistory(root, scan) {\n try {\n const log = await gitLog(root);\n if (!log.ok) return unavailableHistory(log.reason);\n return analyzeHistory(scan, parseGitLog(log.text), { totalCommits: log.totalCommits });\n } catch {\n return unavailableHistory('The history could not be read — the scan itself is unaffected.');\n }\n}\n\n// The three request shapes, reduced to a directory. Cloning happens here, which\n// is why this returns rather than sends: the caller owns cleanup if the scan that\n// follows fails.\nasync function resolveTarget(body: ScanRequest, projectRoot: string): Promise<{ root?: string; error?: string; cloneDir?: string; cloneId?: string; gitMeta?: { gitUrl: string; repoName: string } }> {\n if (body.demo) return { root: projectRoot };\n\n if (body.path) {\n const root = expandHome(String(body.path));\n const stat = await fs.stat(root).catch(() => null);\n if (!stat?.isDirectory()) {\n return { error: 'No folder at ' + root + '. Check the path and try again.' };\n }\n return { root };\n }\n\n if (body.gitUrl) {\n const url = assertGitUrl(body.gitUrl);\n const clone = await cloneRepo(url);\n return {\n root: clone.dir,\n cloneDir: clone.dir,\n cloneId: clone.id,\n gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },\n };\n }\n\n return { error: 'Send a path, a gitUrl, or { demo: true }.' };\n}\n\n// The page sends this when it is done with a cloned repo. Nothing is reported\n// about whether the clone was there: the browser cannot act on the difference,\n// and an unload-time request that 404s looks like a bug in the console.\nexport async function handleCleanup(res, cloneId) {\n if (!isCloneId(cloneId)) return sendError(res, 400, 'That is not a clone id.');\n await closeClone(cloneId);\n sendJSON(res, 200, { ok: true });\n}\n"]}
@@ -0,0 +1,33 @@
1
+ export declare function buildTfIdfIndex(documents: any): {
2
+ index: Map<any, any>;
3
+ docCounts: Map<any, any>;
4
+ totalDocs: number;
5
+ };
6
+ export declare function searchIndex(indexData: any, queryString: any, limit?: number): any[];
7
+ export declare function searchDocuments(indexData: any, rawQuery: any, options?: Record<string, any>): {
8
+ results: any[];
9
+ total: number;
10
+ indexed: any;
11
+ capped: boolean;
12
+ advanced: any;
13
+ query: {
14
+ terms: any;
15
+ phrases: any;
16
+ negatives: any;
17
+ exts: any;
18
+ paths: any;
19
+ kinds: any;
20
+ isTest: any;
21
+ isSource: any;
22
+ regex: string;
23
+ caseSensitive: any;
24
+ advanced: any;
25
+ };
26
+ error: any;
27
+ stats: {
28
+ bytes: any;
29
+ skipped: any;
30
+ cap: any;
31
+ };
32
+ };
33
+ export declare function handleSearch(res: any, body: any): Promise<void>;