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
@@ -0,0 +1,166 @@
1
+ /* Shared workbench tokens keep the paper palette with a quieter, clearer hierarchy. */
2
+ :root { --shadow: 0 8px 24px rgba(42,39,33,.06); --shadow-sm: 0 3px 10px rgba(42,39,33,.08); --workbench-radius: 12px; }
3
+ [data-theme="dark"] { --shadow: 0 8px 24px rgba(0,0,0,.18); --shadow-sm: 0 3px 10px rgba(0,0,0,.2); }
4
+ .btn { font-family: var(--sans); font-weight: 550; border-width: 1px; border-radius: 7px; padding: 8px 13px; transition: background .15s, border-color .15s; }
5
+ .btn:hover { transform: none; box-shadow: none; background: var(--paper-2); }
6
+ .btn-ink { background: var(--accent); color: var(--card); border-color: var(--accent); }
7
+ [data-theme="dark"] .btn-ink { color: var(--paper); }
8
+ .btn-ink:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
9
+ .text-input { border: 1px solid var(--line); border-radius: 7px; padding: 11px 12px; }
10
+ :where(button, a, input, select, summary):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
11
+ .topbar { height: 104px; display: grid; grid-template-columns: minmax(160px,1fr) auto; grid-template-rows: 56px 48px; gap: 0 16px; overflow: visible; padding: 0 24px; border-color: var(--line); background: var(--card); }
12
+ .topbar:has(.views[hidden]) { height: 64px; grid-template-rows: 64px; }
13
+ .brand { grid-column: 1; grid-row: 1; }
14
+ .brand-mark { font-size: 25px; letter-spacing: -.8px; }
15
+ .brand-note { font-size: 11px; }
16
+ .topbar-right { grid-column: 2; grid-row: 1; min-width: 0; gap: 4px; }
17
+ .views { grid-column: 1 / -1; grid-row: 2; overflow-x: auto; align-items: stretch; min-width: 0; gap: 8px; scrollbar-width: thin; }
18
+ .view-tab { font: 500 12px var(--sans); white-space: nowrap; border: 0; border-bottom: 3px solid transparent; border-radius: 0; background: transparent; color: var(--ink-2); padding: 12px 13px; }
19
+ .view-tab:hover { background: var(--paper); }
20
+ .view-tab.is-active { border-bottom-color: var(--accent); color: var(--accent); background: transparent; box-shadow: none; font-weight: 650; }
21
+ .explorer { height: calc(100dvh - 104px); }
22
+ .sidebar, .inspector, .stage-head { border-color: var(--line); }
23
+ .landing { min-height: calc(100dvh - 64px); }
24
+ .landing-inner { width: min(1060px, 92vw); margin-top: clamp(36px,7vh,80px); }
25
+ .workbench-eyebrow { font: 600 10px var(--mono); letter-spacing: 1.5px; color: var(--ink-2); display: flex; align-items: center; gap: 9px; }
26
+ .review-dot { width: 7px; height: 7px; background: var(--accent); border-radius: 50%; display: inline-block; flex-shrink: 0; }
27
+ .landing-eyebrow { margin-bottom: 22px; }
28
+ .landing-title { font-size: clamp(38px,5.2vw,64px); letter-spacing: -2px; line-height: 1.08; }
29
+ .landing-title span { color: var(--accent); }
30
+ .landing-sub { line-height: 1.7; max-width: 660px; margin-bottom: 32px; }
31
+ .load-grid { gap: 18px; }
32
+ .load-card { border: 1px solid var(--line); border-radius: var(--workbench-radius); padding: 23px; box-shadow: var(--shadow); gap: 12px; }
33
+ .load-card-title { font: 600 15px var(--sans); }
34
+ .load-card-note { color: var(--ink-2); line-height: 1.6; min-height: 38px; }
35
+ .landing-capabilities { display: flex; flex-wrap: wrap; gap: 12px 24px; margin: 25px 0; color: var(--ink-2); font-size: 12px; }
36
+ .landing-alt { margin: 24px 0; }
37
+ .landing-about { border-color: var(--line); box-shadow: none; border-radius: var(--workbench-radius); margin-top: 32px; }
38
+ .review-workspace { grid-template-columns: minmax(0,1fr); }
39
+ .review-workspace > .sidebar, .review-workspace > .inspector, .review-workspace .stage-head, .review-workspace .stage-foot { display: none !important; }
40
+ .review-workspace .stage { min-width: 0; }
41
+ #reviewView { background: var(--paper); }
42
+ .review-shell { max-width: 1400px; padding: 34px 40px 56px; margin: 0 auto; }
43
+ .review-heading { display: flex; justify-content: space-between; align-items: center; gap: 20px; margin-bottom: 28px; }
44
+ .review-heading h1 { font: 500 clamp(28px,3vw,38px) var(--serif); letter-spacing: -.9px; margin: 10px 0 8px; }
45
+ .review-heading p { margin: 0; color: var(--ink-2); font-size: 13px; }
46
+ .review-local-badge { color: var(--accent); background: var(--accent-soft); border-radius: 25px; font: 500 11px var(--sans); padding: 8px 12px; white-space: nowrap; }
47
+ .review-controls { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px; padding: 18px; background: var(--card); border: 1px solid var(--line); border-radius: var(--workbench-radius); }
48
+ .review-controls label { display: flex; flex: 1 1 165px; flex-direction: column; gap: 7px; font: 600 11px var(--sans); color: var(--ink-2); }
49
+ .review-controls input, .review-controls select, .review-filters select { width: 100%; min-width: 0; height: 38px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 6px; font: 12px var(--sans); color: var(--ink); background: var(--card); }
50
+ .review-controls .review-run { min-height: 38px; padding-inline: 22px; }
51
+ .review-run span { margin-left: 12px; }
52
+ .review-comparison-note { font-size: 11px; color: var(--ink-2); margin: 10px 3px 20px; }
53
+ .review-feedback, .review-error { padding: 12px 16px; margin: 16px 0; font-size: 13px; border-radius: 8px; background: var(--accent-soft); color: var(--accent); }
54
+ .review-error { color: var(--warn); background: var(--warn-soft); border: 1px solid var(--warn); }
55
+ .review-welcome { margin-top: 36px; text-align: center; padding: 32px; border: 1px solid var(--line); background: var(--card); border-radius: var(--workbench-radius); }
56
+ .review-welcome-icon { font: 26px var(--mono); color: var(--accent); width: 56px; height: 56px; margin: 0 auto 20px; border-radius: 15px; background: var(--accent-soft); display: grid; place-items: center; }
57
+ .review-welcome h2 { font: 500 26px var(--serif); margin: 10px 0; }
58
+ .review-welcome > p { font-size: 13px; color: var(--ink-2); max-width: 620px; margin: 0 auto; line-height: 1.7; }
59
+ .review-preview-grid { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); text-align: left; gap: 32px; margin: 36px 0 24px; padding-top: 28px; border-top: 1px solid var(--line); }
60
+ .review-preview-grid span { font: 11px var(--mono); color: var(--accent); }
61
+ .review-preview-grid h3 { font: 600 14px var(--sans); }
62
+ .review-preview-grid p { color: var(--ink-2); font-size: 12px; line-height: 1.7; }
63
+ .review-welcome .review-scope { max-width: none; font-size: 11px; border-top: 1px solid var(--line); padding-top: 20px; }
64
+ .review-summary { display: flex; justify-content: space-between; gap: 24px; align-items: center; margin: 28px 0 20px; }
65
+ .review-status { display: inline-block; font-size: 11px; font-weight: 600; border-radius: 20px; padding: 5px 10px; background: var(--accent-soft); color: var(--accent); }
66
+ .review-status.needs-review { background: var(--warn-soft); color: var(--warn); }
67
+ .review-summary h2 { font: 500 19px var(--sans); line-height: 1.5; max-width: 780px; margin: 12px 0 6px; }
68
+ .review-summary p { color: var(--ink-2); font: 11px var(--mono); }
69
+ .review-export { display: flex; flex-shrink: 0; gap: 5px; }
70
+ .review-metrics { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 16px; margin-bottom: 24px; }
71
+ .review-metrics article { background: var(--card); padding: 20px; border: 1px solid var(--line); border-radius: var(--workbench-radius); }
72
+ .review-metrics span { font-size: 12px; color: var(--ink-2); }
73
+ .review-metrics strong { display: block; font: 500 32px var(--sans); margin: 8px 0; }
74
+ .review-metrics em { font-size: 18px; font-style: normal; color: var(--faint); }
75
+ .review-metrics small { font-size: 11px; color: var(--ink-2); }
76
+ .review-add { color: var(--accent); font-weight: 500; }
77
+ .review-del { color: var(--warn); font-weight: 500; }
78
+ .review-columns { display: grid; grid-template-columns: minmax(0,1fr) 300px; gap: 20px; align-items: start; }
79
+ .review-card { background: var(--card); border: 1px solid var(--line); padding: 22px; border-radius: var(--workbench-radius); min-width: 0; }
80
+ .review-card h2 { font: 600 17px var(--sans); margin: 9px 0 18px; }
81
+ .review-count { font: 11px var(--mono); border-radius: 6px; padding: 3px 6px; background: var(--paper-2); vertical-align: middle; }
82
+ .review-filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 20px; }
83
+ .review-filters .text-input { flex: 1 1 160px; width: auto; font: 12px var(--sans); }
84
+ .review-filters select { width: auto; }
85
+ .review-notes-toggle, .review-ack { color: var(--ink-2); font-size: 11px; display: flex; align-items: center; gap: 6px; }
86
+ .review-finding { padding: 20px 0; border-top: 1px solid var(--line); }
87
+ .review-finding-meta { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; color: var(--ink-2); font: 10px var(--mono); }
88
+ .review-severity, .review-risk { border-radius: 5px; padding: 4px 7px; font: 600 10px var(--sans); }
89
+ .sev-critical { background: var(--sev-critical-bg); color: var(--sev-critical-ink); }
90
+ .sev-high, .risk-high { background: var(--sev-high-bg); color: var(--sev-high-ink); }
91
+ .sev-medium, .risk-medium { background: var(--sev-medium-bg); color: var(--sev-medium-ink); }
92
+ .sev-low, .risk-low { background: var(--sev-low-bg); color: var(--sev-low-ink); }
93
+ .sev-info { background: var(--sev-info-bg); color: var(--ink-2); }
94
+ .review-location { margin-left: auto; overflow-wrap: anywhere; text-align: left; }
95
+ .review-finding h3 { font: 600 14px var(--sans); margin: 13px 0; }
96
+ .review-finding pre { font: 11px/1.6 var(--mono); padding: 12px; background: var(--paper); border-radius: 6px; white-space: pre-wrap; overflow-wrap: anywhere; }
97
+ .review-suggestion { border-left: 2px solid var(--accent); padding: 4px 12px; margin: 16px 0; }
98
+ .review-suggestion strong { color: var(--accent); font-size: 11px; }
99
+ .review-suggestion p { font-size: 12px; line-height: 1.65; margin: 6px 0; }
100
+ .review-finding.is-acknowledged { opacity: .7; }
101
+ .review-aside { display: grid; gap: 18px; }
102
+ .review-check { display: flex; align-items: flex-start; gap: 10px; margin-top: 18px; }
103
+ .review-check-icon { display: grid; place-items: center; width: 21px; height: 21px; flex-shrink: 0; border-radius: 50%; font: 600 11px var(--sans); background: var(--paper-2); color: var(--ink-2); }
104
+ .check-pass { background: var(--accent-soft); color: var(--accent); }
105
+ .check-fail { background: var(--warn-soft); color: var(--warn); }
106
+ .check-warn { background: var(--hub-soft); color: var(--hub); }
107
+ .review-check strong { font-size: 12px; }
108
+ .review-check p, .review-small { color: var(--ink-2); font-size: 11px; line-height: 1.65; margin: 5px 0; }
109
+ .review-impact-file { display: block; text-align: left; font: 11px/1.7 var(--mono); overflow-wrap: anywhere; }
110
+ .review-section-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
111
+ .review-walkthrough, .review-ai { margin-top: 24px; }
112
+ .review-table-wrap { overflow-x: auto; }
113
+ .review-walkthrough table { width: 100%; border-collapse: collapse; text-align: left; font-size: 12px; }
114
+ .review-walkthrough th { font: 500 10px var(--mono); color: var(--ink-2); border-bottom: 1px solid var(--line); padding: 12px 8px; }
115
+ .review-walkthrough td { padding: 14px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
116
+ .review-walkthrough td:first-child { min-width: 160px; max-width: 450px; overflow-wrap: anywhere; }
117
+ .review-walkthrough td:nth-child(3) { white-space: nowrap; }
118
+ .review-walkthrough small { display: block; color: var(--ink-2); font-size: 10px; margin-top: 5px; }
119
+ .review-empty { text-align: center; padding: 36px 12px; color: var(--ink-2); }
120
+ .review-empty > span { font-size: 27px; color: var(--accent); }
121
+ .review-empty h3 { font-size: 14px; }
122
+ .review-empty p { font-size: 12px; max-width: 400px; margin: auto; line-height: 1.7; }
123
+ .review-ai-text { white-space: pre-wrap; font: 13px/1.7 var(--sans); margin-top: 15px; overflow-wrap: anywhere; }
124
+ .review-limitations { padding: 22px 4px; color: var(--ink-2); font-size: 11px; line-height: 1.7; }
125
+ .review-limitations summary { cursor: pointer; }
126
+ @media (max-width: 1100px) { .brand-note, .me-links { display: none; } .review-shell { padding: 28px 24px; } .review-columns { grid-template-columns: minmax(0,1fr) 270px; } }
127
+ @media (max-width: 900px) {
128
+ .topbar { height: 100px; grid-template-rows: 50px 50px; padding-inline: 12px; gap: 0 8px; grid-template-columns: minmax(0,1fr) minmax(0,auto); }
129
+ .topbar:has(.views[hidden]) { height: 56px; grid-template-rows: 56px; }
130
+ .topbar-right { max-width: min(64vw,500px); overflow-x: auto; }
131
+ .topbar-right > * { flex: none; }
132
+ .views { gap: 2px; }
133
+ .explorer { height: calc(100dvh - 100px); }
134
+ .landing { min-height: calc(100dvh - 56px); }
135
+ .review-shell { padding: 24px 18px 40px; }
136
+ .review-heading { align-items: flex-start; }
137
+ .review-local-badge { font-size: 10px; }
138
+ .review-columns { grid-template-columns: minmax(0,1fr); }
139
+ .review-aside { grid-template-columns: repeat(2,minmax(0,1fr)); }
140
+ .review-summary { align-items: flex-start; }
141
+ .review-metrics { gap: 10px; }
142
+ .review-metrics article { padding: 16px; }
143
+ }
144
+ @media (max-width: 600px) {
145
+ .landing-title { letter-spacing: -1px; }
146
+ .landing-capabilities { font-size: 11px; gap: 12px 18px; }
147
+ .review-heading { flex-direction: column; gap: 12px; margin-bottom: 20px; }
148
+ .review-controls { padding: 14px; gap: 12px; }
149
+ .review-controls label { flex-basis: 100%; }
150
+ .review-controls .review-run { flex: 1; }
151
+ .review-preview-grid, .review-aside { grid-template-columns: minmax(0,1fr); gap: 16px; }
152
+ .review-welcome { padding: 22px; margin-top: 24px; }
153
+ .review-welcome h2 { font-size: 24px; }
154
+ .review-preview-grid { margin-top: 26px; }
155
+ .review-metrics { grid-template-columns: repeat(2,minmax(0,1fr)); }
156
+ .review-summary, .review-section-heading { flex-direction: column; align-items: flex-start; gap: 8px; }
157
+ .review-card { padding: 18px; }
158
+ .review-filters .text-input { flex-basis: 100%; }
159
+ .review-location { margin-left: 0; width: 100%; }
160
+ .review-filters select { min-height: 40px; }
161
+ .review-ack input, .review-notes-toggle input { width: 18px; height: 18px; flex-shrink: 0; }
162
+ .review-export .btn { min-height: 40px; }
163
+ .review-shell { padding-inline: 14px; }
164
+ }
165
+ @media (prefers-reduced-motion: reduce) { .btn { transition: none; } }
166
+ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
@@ -0,0 +1,3 @@
1
+ export declare function handleAuthStatus(req: any, res: any, settings: any): void;
2
+ export declare function handleLogin(req: any, res: any, body: any, settings: any): void;
3
+ export declare function handleLogout(req: any, res: any): void;
package/server/apiAuth.js CHANGED
@@ -1,32 +1,30 @@
1
1
  // The access-key form. Kept tiny and independent from the main app: an
2
2
  // unauthenticated visitor must be able to load this page without loading any
3
3
  // analyzer, settings drawer, or other application surface.
4
-
5
4
  import { accessKeysMatch, authReason } from './config.js';
6
5
  import { hasValidSession, sessionCookie } from './auth.js';
7
6
  import { sendError, sendJSON } from './http.js';
8
-
9
7
  export function handleAuthStatus(req, res, settings) {
10
- sendJSON(res, 200, {
11
- authenticated: !authReason(req, settings) || hasValidSession(req, settings),
12
- configured: Boolean(settings.accessKey),
13
- });
8
+ sendJSON(res, 200, {
9
+ authenticated: !authReason(req, settings) || hasValidSession(req, settings),
10
+ configured: Boolean(settings.accessKey),
11
+ });
14
12
  }
15
-
16
13
  export function handleLogin(req, res, body, settings) {
17
- if (!settings.accessKey) return sendError(res, 503, 'This server has no access key configured. Run `onboarder setup` on the server.');
18
- if (!accessKeysMatch(settings.accessKey, body?.accessKey)) {
19
- return sendError(res, 401, 'That access key is not correct.');
20
- }
21
- sendJSON(res, 200, { ok: true }, {
22
- 'cache-control': 'no-store',
23
- 'set-cookie': sessionCookie(req, settings),
24
- });
14
+ if (!settings.accessKey)
15
+ return sendError(res, 503, 'This server has no access key configured. Run `onboarder setup` on the server.');
16
+ if (!accessKeysMatch(settings.accessKey, body?.accessKey)) {
17
+ return sendError(res, 401, 'That access key is not correct.');
18
+ }
19
+ sendJSON(res, 200, { ok: true }, {
20
+ 'cache-control': 'no-store',
21
+ 'set-cookie': sessionCookie(req, settings),
22
+ });
25
23
  }
26
-
27
24
  export function handleLogout(req, res) {
28
- sendJSON(res, 200, { ok: true }, {
29
- 'cache-control': 'no-store',
30
- 'set-cookie': sessionCookie(req, {}, { clear: true }),
31
- });
25
+ sendJSON(res, 200, { ok: true }, {
26
+ 'cache-control': 'no-store',
27
+ 'set-cookie': sessionCookie(req, {}, { clear: true }),
28
+ });
32
29
  }
30
+ //# sourceMappingURL=apiAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiAuth.js","sourceRoot":"","sources":["../src/server/apiAuth.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,6EAA6E;AAC7E,2DAA2D;AAE3D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,UAAU,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ;IACjD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;QACjB,aAAa,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC;QAC3E,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;KACxC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ;IAClD,IAAI,CAAC,QAAQ,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,gFAAgF,CAAC,CAAC;IACtI,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC;QAC1D,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,iCAAiC,CAAC,CAAC;IAChE,CAAC;IACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;QAC/B,eAAe,EAAE,UAAU;QAC3B,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC;KAC3C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAG,EAAE,GAAG;IACnC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;QAC/B,eAAe,EAAE,UAAU;QAC3B,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KACtD,CAAC,CAAC;AACL,CAAC","sourcesContent":["// The access-key form. Kept tiny and independent from the main app: an\n// unauthenticated visitor must be able to load this page without loading any\n// analyzer, settings drawer, or other application surface.\n\nimport { accessKeysMatch, authReason } from './config.js';\nimport { hasValidSession, sessionCookie } from './auth.js';\nimport { sendError, sendJSON } from './http.js';\n\nexport function handleAuthStatus(req, res, settings) {\n sendJSON(res, 200, {\n authenticated: !authReason(req, settings) || hasValidSession(req, settings),\n configured: Boolean(settings.accessKey),\n });\n}\n\nexport function handleLogin(req, res, body, settings) {\n if (!settings.accessKey) return sendError(res, 503, 'This server has no access key configured. Run `onboarder setup` on the server.');\n if (!accessKeysMatch(settings.accessKey, body?.accessKey)) {\n return sendError(res, 401, 'That access key is not correct.');\n }\n sendJSON(res, 200, { ok: true }, {\n 'cache-control': 'no-store',\n 'set-cookie': sessionCookie(req, settings),\n });\n}\n\nexport function handleLogout(req, res) {\n sendJSON(res, 200, { ok: true }, {\n 'cache-control': 'no-store',\n 'set-cookie': sessionCookie(req, {}, { clear: true }),\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function handleDiffRefs(res: any, scanId: any): Promise<void>;
2
+ export declare function handleDiff(res: any, scanId: any, queryParams?: Record<string, any>): Promise<void>;
package/server/apiDiff.js CHANGED
@@ -1,25 +1,27 @@
1
1
  // GET /api/diff and GET /api/diff/refs — Git diff and branch endpoints.
2
-
3
- import { getGitDiff, getGitRefs } from './gitDiff.js';
2
+ import { getGitDiff, getGitRefs, GitDiffError } from './gitDiff.js';
4
3
  import { sendError, sendJSON } from './http.js';
5
4
  import { getSession } from './sessions.js';
6
-
7
5
  export async function handleDiffRefs(res, scanId) {
8
- const session = getSession(scanId);
9
- if (!session) return sendError(res, 404, 'Scan session not found.');
10
-
11
- const refs = await getGitRefs(session.root);
12
- sendJSON(res, 200, refs);
6
+ const session = getSession(scanId);
7
+ if (!session)
8
+ return sendError(res, 404, 'Scan session not found.');
9
+ const refs = await getGitRefs(session.root);
10
+ sendJSON(res, 200, refs);
13
11
  }
14
-
15
12
  export async function handleDiff(res, scanId, queryParams = {}) {
16
- const session = getSession(scanId);
17
- if (!session) return sendError(res, 404, 'Scan session not found.');
18
-
19
- const base = queryParams.get('base') || '';
20
- const head = queryParams.get('head') || '';
21
- const file = queryParams.get('file') || '';
22
-
23
- const diff = await getGitDiff(session.root, { base, head, file });
24
- sendJSON(res, 200, diff);
13
+ const session = getSession(scanId);
14
+ if (!session)
15
+ return sendError(res, 404, 'Scan session not found.');
16
+ const base = queryParams.get('base') || '';
17
+ const head = queryParams.get('head') || '';
18
+ const file = queryParams.get('file') || '';
19
+ try {
20
+ const diff = await getGitDiff(session.root, { base, head, file });
21
+ sendJSON(res, 200, diff);
22
+ }
23
+ catch (error) {
24
+ sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Comparison failed.');
25
+ }
25
26
  }
27
+ //# sourceMappingURL=apiDiff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiDiff.js","sourceRoot":"","sources":["../src/server/apiDiff.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAG,EAAE,MAAM;IAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,yBAAyB,CAAC,CAAC;IAEpE,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,GAAwB,EAAE;IACjF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,yBAAyB,CAAC,CAAC;IAEpE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IAE3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,SAAS,CAAC,GAAG,EAAE,KAAK,YAAY,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC;IACpI,CAAC;AACH,CAAC","sourcesContent":["// GET /api/diff and GET /api/diff/refs — Git diff and branch endpoints.\n\nimport { getGitDiff, getGitRefs, GitDiffError } from './gitDiff.js';\nimport { sendError, sendJSON } from './http.js';\nimport { getSession } from './sessions.js';\n\nexport async function handleDiffRefs(res, scanId) {\n const session = getSession(scanId);\n if (!session) return sendError(res, 404, 'Scan session not found.');\n\n const refs = await getGitRefs(session.root);\n sendJSON(res, 200, refs);\n}\n\nexport async function handleDiff(res, scanId, queryParams: Record<string, any> = {}) {\n const session = getSession(scanId);\n if (!session) return sendError(res, 404, 'Scan session not found.');\n\n const base = queryParams.get('base') || '';\n const head = queryParams.get('head') || '';\n const file = queryParams.get('file') || '';\n\n try {\n const diff = await getGitDiff(session.root, { base, head, file });\n sendJSON(res, 200, diff);\n } catch (error) {\n sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Comparison failed.');\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function isAllowedDocHost(hostname: any): boolean;
2
+ export declare function handleDocs(res: any, body: any, fetchImpl?: typeof fetch): Promise<void>;
package/server/apiDocs.js CHANGED
@@ -10,20 +10,16 @@
10
10
  // means the set of fetchable hosts is exactly the set of hosts a scanned
11
11
  // dependency can point at. Nothing else is reachable: no private addresses, no
12
12
  // loopback, no arbitrary sites, and no host that drifted into a hand-kept list.
13
-
14
13
  import { DOC_HOSTS } from '../shared/analyzer/stack.js';
15
14
  import { htmlToText } from './htmlText.js';
16
15
  import { sendError, sendJSON } from './http.js';
17
-
18
16
  const FETCH_TIMEOUT_MS = 12_000;
19
-
20
17
  export function isAllowedDocHost(hostname) {
21
- const host = String(hostname).replace(/^www\./, '').toLowerCase();
22
- // Subdomains of an allowed host are allowed — docs sites move between
23
- // `example.com` and `docs.example.com` and the allowlist names one of them.
24
- return DOC_HOSTS.some((allowed) => host === allowed || host.endsWith('.' + allowed));
18
+ const host = String(hostname).replace(/^www\./, '').toLowerCase();
19
+ // Subdomains of an allowed host are allowed — docs sites move between
20
+ // `example.com` and `docs.example.com` and the allowlist names one of them.
21
+ return DOC_HOSTS.some((allowed) => host === allowed || host.endsWith('.' + allowed));
25
22
  }
26
-
27
23
  // A failure here is not fatal to the caller: `about.js` falls back to summarising
28
24
  // the library from the model's own knowledge when no text comes back. That is
29
25
  // what makes it safe to refuse in the ambiguous cases below rather than guess.
@@ -31,37 +27,39 @@ export function isAllowedDocHost(hostname) {
31
27
  // `fetchImpl` is injectable so the redirect check can be tested without a
32
28
  // docs site that redirects.
33
29
  export async function handleDocs(res, body, fetchImpl = fetch) {
34
- if (!body || typeof body.url !== 'string') return sendError(res, 400, 'Bad url.');
35
-
36
- let url;
37
- try {
38
- url = new URL(body.url);
39
- } catch {
40
- return sendError(res, 400, 'Bad url.');
41
- }
42
-
43
- if (url.protocol !== 'https:') return sendError(res, 400, 'Only https docs are fetched.');
44
- if (!isAllowedDocHost(url.hostname)) {
45
- return sendError(res, 403, 'That is not an allowed docs site.');
46
- }
47
-
48
- try {
49
- const response = await fetchImpl(url.toString(), {
50
- signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
51
- // Redirects are followed because docs sites are full of them, and checked
52
- // again on the way out. An allowlisted host with an open redirect would
53
- // otherwise be a way through this guard to anywhere — including the
54
- // link-local metadata address that is the reason the guard exists.
55
- redirect: 'follow',
56
- headers: { 'user-agent': 'onboarder/1.0' },
57
- });
58
- const landed = new URL(response.url || url.toString());
59
- if (!isAllowedDocHost(landed.hostname)) {
60
- return sendError(res, 403, 'That page redirected somewhere we do not fetch from.');
30
+ if (!body || typeof body.url !== 'string')
31
+ return sendError(res, 400, 'Bad url.');
32
+ let url;
33
+ try {
34
+ url = new URL(body.url);
35
+ }
36
+ catch {
37
+ return sendError(res, 400, 'Bad url.');
38
+ }
39
+ if (url.protocol !== 'https:')
40
+ return sendError(res, 400, 'Only https docs are fetched.');
41
+ if (!isAllowedDocHost(url.hostname)) {
42
+ return sendError(res, 403, 'That is not an allowed docs site.');
43
+ }
44
+ try {
45
+ const response = await fetchImpl(url.toString(), {
46
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
47
+ // Redirects are followed because docs sites are full of them, and checked
48
+ // again on the way out. An allowlisted host with an open redirect would
49
+ // otherwise be a way through this guard to anywhere — including the
50
+ // link-local metadata address that is the reason the guard exists.
51
+ redirect: 'follow',
52
+ headers: { 'user-agent': 'onboarder/1.0' },
53
+ });
54
+ const landed = new URL(response.url || url.toString());
55
+ if (!isAllowedDocHost(landed.hostname)) {
56
+ return sendError(res, 403, 'That page redirected somewhere we do not fetch from.');
57
+ }
58
+ const { title, text } = htmlToText(await response.text());
59
+ sendJSON(res, 200, { url: landed.toString(), title, text, status: response.status });
60
+ }
61
+ catch (err) {
62
+ sendError(res, 502, 'Could not fetch: ' + err.message);
61
63
  }
62
- const { title, text } = htmlToText(await response.text());
63
- sendJSON(res, 200, { url: landed.toString(), title, text, status: response.status });
64
- } catch (err) {
65
- sendError(res, 502, 'Could not fetch: ' + err.message);
66
- }
67
64
  }
65
+ //# sourceMappingURL=apiDocs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiDocs.js","sourceRoot":"","sources":["../src/server/apiDocs.ts"],"names":[],"mappings":"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","sourcesContent":["// POST /api/doc — fetch a library's official documentation page as text.\n//\n// This exists because the browser cannot: docs sites do not send CORS headers,\n// so the page has no way to read them. That makes this endpoint a fetcher acting\n// on behalf of whoever can reach it, which is the definition of an SSRF hazard,\n// and the reason for the two gates below.\n//\n// https only, and a host allowlist. The allowlist is not written here — it is\n// derived from the docs URLs `shared/analyzer/stack.js` already knows, which\n// means the set of fetchable hosts is exactly the set of hosts a scanned\n// dependency can point at. Nothing else is reachable: no private addresses, no\n// loopback, no arbitrary sites, and no host that drifted into a hand-kept list.\n\nimport { DOC_HOSTS } from '../shared/analyzer/stack.js';\nimport { htmlToText } from './htmlText.js';\nimport { sendError, sendJSON } from './http.js';\n\nconst FETCH_TIMEOUT_MS = 12_000;\n\nexport function isAllowedDocHost(hostname) {\n const host = String(hostname).replace(/^www\\./, '').toLowerCase();\n // Subdomains of an allowed host are allowed — docs sites move between\n // `example.com` and `docs.example.com` and the allowlist names one of them.\n return DOC_HOSTS.some((allowed) => host === allowed || host.endsWith('.' + allowed));\n}\n\n// A failure here is not fatal to the caller: `about.js` falls back to summarising\n// the library from the model's own knowledge when no text comes back. That is\n// what makes it safe to refuse in the ambiguous cases below rather than guess.\n//\n// `fetchImpl` is injectable so the redirect check can be tested without a\n// docs site that redirects.\nexport async function handleDocs(res, body, fetchImpl = fetch) {\n if (!body || typeof body.url !== 'string') return sendError(res, 400, 'Bad url.');\n\n let url;\n try {\n url = new URL(body.url);\n } catch {\n return sendError(res, 400, 'Bad url.');\n }\n\n if (url.protocol !== 'https:') return sendError(res, 400, 'Only https docs are fetched.');\n if (!isAllowedDocHost(url.hostname)) {\n return sendError(res, 403, 'That is not an allowed docs site.');\n }\n\n try {\n const response = await fetchImpl(url.toString(), {\n signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n // Redirects are followed because docs sites are full of them, and checked\n // again on the way out. An allowlisted host with an open redirect would\n // otherwise be a way through this guard to anywhere — including the\n // link-local metadata address that is the reason the guard exists.\n redirect: 'follow',\n headers: { 'user-agent': 'onboarder/1.0' },\n });\n const landed = new URL(response.url || url.toString());\n if (!isAllowedDocHost(landed.hostname)) {\n return sendError(res, 403, 'That page redirected somewhere we do not fetch from.');\n }\n const { title, text } = htmlToText(await response.text());\n sendJSON(res, 200, { url: landed.toString(), title, text, status: response.status });\n } catch (err) {\n sendError(res, 502, 'Could not fetch: ' + err.message);\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function handleFile(res: any, scanId: any, relPath: any): Promise<void>;
package/server/apiFile.js CHANGED
@@ -8,29 +8,26 @@
8
8
  // enough that reading it is not itself the problem. The router adds a fourth —
9
9
  // it is the single GET that the cross-origin check applies to, because a page on
10
10
  // another site could otherwise read files through the browser.
11
-
12
11
  import { promises as fs } from 'node:fs';
13
-
14
12
  import { sendError, sendText } from './http.js';
15
13
  import { resolveInside } from './paths.js';
16
14
  import { getSession } from './sessions.js';
17
-
18
15
  // Code, not assets. Past this the viewer would choke anyway, and a 200 MB
19
16
  // checked-in binary should not be buffered into a response to find that out.
20
17
  const MAX_FILE_BYTES = 200 * 1024;
21
-
22
18
  export async function handleFile(res, scanId, relPath) {
23
- const session = getSession(scanId);
24
- if (!session) return sendError(res, 404, 'That scan is gone. Rescan the repo.');
25
-
26
- const abs = resolveInside(session.root, relPath);
27
- if (!abs) return sendError(res, 400, 'Bad path.');
28
-
29
- const stat = await fs.stat(abs).catch(() => null);
30
- if (!stat?.isFile()) return sendError(res, 404, 'No such file in the repo.');
31
- if (stat.size > MAX_FILE_BYTES) {
32
- return sendError(res, 413, 'That file is too large to send around.');
33
- }
34
-
35
- sendText(res, 200, await fs.readFile(abs, 'utf8'));
19
+ const session = getSession(scanId);
20
+ if (!session)
21
+ return sendError(res, 404, 'That scan is gone. Rescan the repo.');
22
+ const abs = resolveInside(session.root, relPath);
23
+ if (!abs)
24
+ return sendError(res, 400, 'Bad path.');
25
+ const stat = await fs.stat(abs).catch(() => null);
26
+ if (!stat?.isFile())
27
+ return sendError(res, 404, 'No such file in the repo.');
28
+ if (stat.size > MAX_FILE_BYTES) {
29
+ return sendError(res, 413, 'That file is too large to send around.');
30
+ }
31
+ sendText(res, 200, await fs.readFile(abs, 'utf8'));
36
32
  }
33
+ //# sourceMappingURL=apiFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiFile.js","sourceRoot":"","sources":["../src/server/apiFile.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,0BAA0B;AAC1B,EAAE;AACF,0EAA0E;AAC1E,gFAAgF;AAChF,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,iFAAiF;AACjF,+DAA+D;AAE/D,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,0EAA0E;AAC1E,6EAA6E;AAC7E,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO;IACnD,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,qCAAqC,CAAC,CAAC;IAEhF,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;IAElD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,2BAA2B,CAAC,CAAC;IAC7E,IAAI,IAAI,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,wCAAwC,CAAC,CAAC;IACvE,CAAC;IAED,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;AACrD,CAAC","sourcesContent":["// GET /api/file?scan=…&path=… — one file's text, for the code viewer and for the\n// AI explainer's context.\n//\n// This is the only endpoint that returns the contents of something on the\n// person's disk, which makes it the one worth being strict at. Three gates, and\n// none of them is authentication: the scan id has to name a live session, the\n// path has to resolve inside that session's root, and the file has to be small\n// enough that reading it is not itself the problem. The router adds a fourth —\n// it is the single GET that the cross-origin check applies to, because a page on\n// another site could otherwise read files through the browser.\n\nimport { promises as fs } from 'node:fs';\n\nimport { sendError, sendText } from './http.js';\nimport { resolveInside } from './paths.js';\nimport { getSession } from './sessions.js';\n\n// Code, not assets. Past this the viewer would choke anyway, and a 200 MB\n// checked-in binary should not be buffered into a response to find that out.\nconst MAX_FILE_BYTES = 200 * 1024;\n\nexport async function handleFile(res, scanId, relPath) {\n const session = getSession(scanId);\n if (!session) return sendError(res, 404, 'That scan is gone. Rescan the repo.');\n\n const abs = resolveInside(session.root, relPath);\n if (!abs) return sendError(res, 400, 'Bad path.');\n\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat?.isFile()) return sendError(res, 404, 'No such file in the repo.');\n if (stat.size > MAX_FILE_BYTES) {\n return sendError(res, 413, 'That file is too large to send around.');\n }\n\n sendText(res, 200, await fs.readFile(abs, 'utf8'));\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function parsePorcelainBlame(stdout: any): any[];
2
+ export declare function handleBlame(res: any, scanId: any, filePath: any): Promise<void>;
@@ -4,60 +4,59 @@ import { resolveInside } from './paths.js';
4
4
  import { getSession } from './sessions.js';
5
5
  import { sendError, sendJSON } from './http.js';
6
6
  import { promises as fs } from 'node:fs';
7
-
8
7
  const execAsync = promisify(execFile);
9
-
10
8
  export function parsePorcelainBlame(stdout) {
11
- const lines = [];
12
- const linesArr = stdout.split('\n');
13
- let currentCommit = {};
14
- let currentLine = null;
15
- const commits = new Map();
16
-
17
- for (let i = 0; i < linesArr.length; i++) {
18
- const line = linesArr[i];
19
- if (!line) continue;
20
-
21
- if (/^[0-9a-f]{40} \d+ \d+/.test(line)) {
22
- const parts = line.split(' ');
23
- const sha = parts[0];
24
- const lineNo = parseInt(parts[2], 10);
25
-
26
- currentLine = { lineNo, sha, content: '' };
27
-
28
- if (!commits.has(sha)) {
29
- commits.set(sha, { sha });
30
- }
31
- currentCommit = commits.get(sha);
32
- } else if (line.startsWith('author ')) {
33
- currentCommit.author = line.slice(7);
34
- } else if (line.startsWith('author-time ')) {
35
- currentCommit.date = new Date(parseInt(line.slice(12), 10) * 1000).toISOString();
36
- } else if (line.startsWith('\t')) {
37
- if (currentLine) {
38
- currentLine.content = line.slice(1);
39
- currentLine.author = currentCommit.author || 'Unknown';
40
- currentLine.date = currentCommit.date || '';
41
- lines.push(currentLine);
42
- currentLine = null;
43
- }
9
+ const lines = [];
10
+ const linesArr = stdout.split('\n');
11
+ let currentCommit = {};
12
+ let currentLine = null;
13
+ const commits = new Map();
14
+ for (let i = 0; i < linesArr.length; i++) {
15
+ const line = linesArr[i];
16
+ if (!line)
17
+ continue;
18
+ if (/^[0-9a-f]{40} \d+ \d+/.test(line)) {
19
+ const parts = line.split(' ');
20
+ const sha = parts[0];
21
+ const lineNo = parseInt(parts[2], 10);
22
+ currentLine = { lineNo, sha, content: '' };
23
+ if (!commits.has(sha)) {
24
+ commits.set(sha, { sha });
25
+ }
26
+ currentCommit = commits.get(sha);
27
+ }
28
+ else if (line.startsWith('author ')) {
29
+ currentCommit.author = line.slice(7);
30
+ }
31
+ else if (line.startsWith('author-time ')) {
32
+ currentCommit.date = new Date(parseInt(line.slice(12), 10) * 1000).toISOString();
33
+ }
34
+ else if (line.startsWith('\t')) {
35
+ if (currentLine) {
36
+ currentLine.content = line.slice(1);
37
+ currentLine.author = currentCommit.author || 'Unknown';
38
+ currentLine.date = currentCommit.date || '';
39
+ lines.push(currentLine);
40
+ currentLine = null;
41
+ }
42
+ }
44
43
  }
45
- }
46
- return lines;
44
+ return lines;
47
45
  }
48
-
49
46
  export async function handleBlame(res, scanId, filePath) {
50
- const session = getSession(scanId);
51
- if (!session) return sendError(res, 404, 'Scan not found.');
52
-
53
- const absPath = resolveInside(session.root, filePath);
54
- if (!absPath) return sendError(res, 400, 'Bad path.');
55
-
56
- try {
57
- const { stdout } = await execAsync('git', ['blame', '--porcelain', absPath], { cwd: session.root });
58
- const lines = parsePorcelainBlame(stdout);
59
- return sendJSON(res, 200, { lines });
60
- } catch (err) {
61
- return sendJSON(res, 200, { available: false, reason: 'Git blame failed or not a git repository.' });
62
- }
47
+ const session = getSession(scanId);
48
+ if (!session)
49
+ return sendError(res, 404, 'Scan not found.');
50
+ const absPath = resolveInside(session.root, filePath);
51
+ if (!absPath)
52
+ return sendError(res, 400, 'Bad path.');
53
+ try {
54
+ const { stdout } = await execAsync('git', ['blame', '--porcelain', absPath], { cwd: session.root });
55
+ const lines = parsePorcelainBlame(stdout);
56
+ return sendJSON(res, 200, { lines });
57
+ }
58
+ catch (err) {
59
+ return sendJSON(res, 200, { available: false, reason: 'Git blame failed or not a git repository.' });
60
+ }
63
61
  }
62
+ //# sourceMappingURL=apiGitBlame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiGitBlame.js","sourceRoot":"","sources":["../src/server/apiGitBlame.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { resolveInside } from './paths.js';\nimport { getSession } from './sessions.js';\nimport { sendError, sendJSON } from './http.js';\nimport { promises as fs } from 'node:fs';\n\nconst execAsync = promisify(execFile);\n\nexport function parsePorcelainBlame(stdout) {\n const lines = [];\n const linesArr = stdout.split('\\n');\n let currentCommit: Record<string, any> = {};\n let currentLine = null;\n const commits = new Map();\n\n for (let i = 0; i < linesArr.length; i++) {\n const line = linesArr[i];\n if (!line) continue;\n\n if (/^[0-9a-f]{40} \\d+ \\d+/.test(line)) {\n const parts = line.split(' ');\n const sha = parts[0];\n const lineNo = parseInt(parts[2], 10);\n \n currentLine = { lineNo, sha, content: '' };\n \n if (!commits.has(sha)) {\n commits.set(sha, { sha });\n }\n currentCommit = commits.get(sha);\n } else if (line.startsWith('author ')) {\n currentCommit.author = line.slice(7);\n } else if (line.startsWith('author-time ')) {\n currentCommit.date = new Date(parseInt(line.slice(12), 10) * 1000).toISOString();\n } else if (line.startsWith('\\t')) {\n if (currentLine) {\n currentLine.content = line.slice(1);\n currentLine.author = currentCommit.author || 'Unknown';\n currentLine.date = currentCommit.date || '';\n lines.push(currentLine);\n currentLine = null;\n }\n }\n }\n return lines;\n}\n\nexport async function handleBlame(res, scanId, filePath) {\n const session = getSession(scanId);\n if (!session) return sendError(res, 404, 'Scan not found.');\n\n const absPath = resolveInside(session.root, filePath);\n if (!absPath) return sendError(res, 400, 'Bad path.');\n\n try {\n const { stdout } = await execAsync('git', ['blame', '--porcelain', absPath], { cwd: session.root });\n const lines = parsePorcelainBlame(stdout);\n return sendJSON(res, 200, { lines });\n } catch (err) {\n return sendJSON(res, 200, { available: false, reason: 'Git blame failed or not a git repository.' });\n }\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export declare function handleMcpStatus(req: any, res: any, config: any): void;
2
+ export declare function handleMcpStart(req: any, res: any, config: any): Promise<void>;
3
+ export declare function handleMcpStop(req: any, res: any, config: any): Promise<void>;
4
+ export declare function handleMcpCommand(req: any, res: any, config: any): void;