codebase-onboarder 0.6.0 → 1.0.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 (526) hide show
  1. package/README.md +284 -53
  2. package/bin/build-if-source.js +23 -0
  3. package/bin/onboarder.js +0 -0
  4. package/cli/agent/command.d.ts +71 -0
  5. package/cli/agent/command.js +177 -0
  6. package/cli/agent/command.js.map +1 -0
  7. package/cli/agent/config.d.ts +31 -0
  8. package/cli/agent/config.js +98 -0
  9. package/cli/agent/config.js.map +1 -0
  10. package/cli/agent/contracts.d.ts +77 -0
  11. package/cli/agent/contracts.js +2 -0
  12. package/cli/agent/contracts.js.map +1 -0
  13. package/cli/agent/files.d.ts +19 -0
  14. package/cli/agent/files.js +109 -0
  15. package/cli/agent/files.js.map +1 -0
  16. package/cli/agent/gitSource.d.ts +18 -0
  17. package/cli/agent/gitSource.js +69 -0
  18. package/cli/agent/gitSource.js.map +1 -0
  19. package/cli/agent/github.d.ts +21 -0
  20. package/cli/agent/github.js +94 -0
  21. package/cli/agent/github.js.map +1 -0
  22. package/cli/agent/mcp.d.ts +15 -0
  23. package/cli/agent/mcp.js +78 -0
  24. package/cli/agent/mcp.js.map +1 -0
  25. package/cli/agent/process.d.ts +18 -0
  26. package/cli/agent/process.js +118 -0
  27. package/cli/agent/process.js.map +1 -0
  28. package/cli/agent/protocol.d.ts +3 -0
  29. package/cli/agent/protocol.js +26 -0
  30. package/cli/agent/protocol.js.map +1 -0
  31. package/cli/agent/runner.d.ts +34 -0
  32. package/cli/agent/runner.js +152 -0
  33. package/cli/agent/runner.js.map +1 -0
  34. package/cli/agent/stdio.d.ts +1 -0
  35. package/cli/agent/stdio.js +62 -0
  36. package/cli/agent/stdio.js.map +1 -0
  37. package/cli/agent/tools.d.ts +7 -0
  38. package/cli/agent/tools.js +201 -0
  39. package/cli/agent/tools.js.map +1 -0
  40. package/cli/chat/app.d.ts +16 -0
  41. package/cli/chat/app.js +345 -0
  42. package/cli/chat/app.js.map +1 -0
  43. package/cli/chat/commands.d.ts +11 -0
  44. package/cli/chat/commands.js +120 -0
  45. package/cli/chat/commands.js.map +1 -0
  46. package/cli/chat/composer.d.ts +75 -0
  47. package/cli/chat/composer.js +467 -0
  48. package/cli/chat/composer.js.map +1 -0
  49. package/cli/chat/controller.d.ts +60 -0
  50. package/cli/chat/controller.js +393 -0
  51. package/cli/chat/controller.js.map +1 -0
  52. package/cli/chat/logo.d.ts +3 -0
  53. package/cli/chat/logo.js +14 -0
  54. package/cli/chat/logo.js.map +1 -0
  55. package/cli/chat/models.d.ts +18 -0
  56. package/cli/chat/models.js +88 -0
  57. package/cli/chat/models.js.map +1 -0
  58. package/cli/chat/profile.d.ts +5 -0
  59. package/cli/chat/profile.js +98 -0
  60. package/cli/chat/profile.js.map +1 -0
  61. package/cli/chat/render.d.ts +40 -0
  62. package/cli/chat/render.js +259 -0
  63. package/cli/chat/render.js.map +1 -0
  64. package/cli/chat/screen.d.ts +26 -0
  65. package/cli/chat/screen.js +117 -0
  66. package/cli/chat/screen.js.map +1 -0
  67. package/cli/chat/source.d.ts +29 -0
  68. package/cli/chat/source.js +116 -0
  69. package/cli/chat/source.js.map +1 -0
  70. package/cli/chat/store.d.ts +43 -0
  71. package/cli/chat/store.js +94 -0
  72. package/cli/chat/store.js.map +1 -0
  73. package/cli/chat/text.d.ts +4 -0
  74. package/cli/chat/text.js +27 -0
  75. package/cli/chat/text.js.map +1 -0
  76. package/cli/commands.d.ts +26 -0
  77. package/cli/commands.js +836 -811
  78. package/cli/commands.js.map +1 -0
  79. package/cli/explorer/advanced.d.ts +14 -0
  80. package/cli/explorer/advanced.js +321 -329
  81. package/cli/explorer/advanced.js.map +1 -0
  82. package/cli/explorer/app.d.ts +2 -0
  83. package/cli/explorer/app.js +404 -314
  84. package/cli/explorer/app.js.map +1 -0
  85. package/cli/explorer/assistant.d.ts +9 -0
  86. package/cli/explorer/assistant.js +58 -0
  87. package/cli/explorer/assistant.js.map +1 -0
  88. package/cli/explorer/commands.d.ts +17 -0
  89. package/cli/explorer/commands.js +373 -302
  90. package/cli/explorer/commands.js.map +1 -0
  91. package/cli/explorer/featureViews.d.ts +124 -0
  92. package/cli/explorer/featureViews.js +179 -0
  93. package/cli/explorer/featureViews.js.map +1 -0
  94. package/cli/explorer/github.d.ts +21 -0
  95. package/cli/explorer/github.js +42 -44
  96. package/cli/explorer/github.js.map +1 -0
  97. package/cli/explorer/graphs.d.ts +7 -0
  98. package/cli/explorer/graphs.js +90 -95
  99. package/cli/explorer/graphs.js.map +1 -0
  100. package/cli/explorer/picker.d.ts +22 -0
  101. package/cli/explorer/picker.js +150 -0
  102. package/cli/explorer/picker.js.map +1 -0
  103. package/cli/explorer/report.d.ts +85 -0
  104. package/cli/explorer/report.js +60 -0
  105. package/cli/explorer/report.js.map +1 -0
  106. package/cli/explorer/session.d.ts +449 -0
  107. package/cli/explorer/session.js +143 -144
  108. package/cli/explorer/session.js.map +1 -0
  109. package/cli/explorer/views.d.ts +19 -0
  110. package/cli/explorer/views.js +465 -482
  111. package/cli/explorer/views.js.map +1 -0
  112. package/cli/explorer/wrap.d.ts +1 -0
  113. package/cli/explorer/wrap.js +28 -44
  114. package/cli/explorer/wrap.js.map +1 -0
  115. package/cli/main.d.ts +1 -0
  116. package/cli/main.js +251 -164
  117. package/cli/main.js.map +1 -0
  118. package/cli/prompt.d.ts +5 -0
  119. package/cli/prompt.js +84 -80
  120. package/cli/prompt.js.map +1 -0
  121. package/cli/scanOptions.d.ts +2 -0
  122. package/cli/scanOptions.js +11 -0
  123. package/cli/scanOptions.js.map +1 -0
  124. package/cli/ui.d.ts +33 -0
  125. package/cli/ui.js +35 -44
  126. package/cli/ui.js.map +1 -0
  127. package/cli/wizard.d.ts +187 -0
  128. package/cli/wizard.js +304 -284
  129. package/cli/wizard.js.map +1 -0
  130. package/docs/HERMES_RESEARCH.md +51 -0
  131. package/package.json +16 -4
  132. package/public/app.d.ts +1 -0
  133. package/public/app.js +1133 -1011
  134. package/public/app.js.map +1 -0
  135. package/public/assets/onboarder-mark.svg +11 -0
  136. package/public/browser.d.ts +71 -0
  137. package/public/browser.js +2 -0
  138. package/public/browser.js.map +1 -0
  139. package/public/index.html +54 -20
  140. package/public/js/about.d.ts +6 -0
  141. package/public/js/about.js +152 -172
  142. package/public/js/about.js.map +1 -0
  143. package/public/js/aiDraft.d.ts +4 -0
  144. package/public/js/aiDraft.js +166 -125
  145. package/public/js/aiDraft.js.map +1 -0
  146. package/public/js/analysisPanel.d.ts +7 -0
  147. package/public/js/analysisPanel.js +166 -166
  148. package/public/js/analysisPanel.js.map +1 -0
  149. package/public/js/analysisReport.d.ts +38 -0
  150. package/public/js/analysisReport.js +105 -110
  151. package/public/js/analysisReport.js.map +1 -0
  152. package/public/js/api.d.ts +29 -0
  153. package/public/js/api.js +198 -166
  154. package/public/js/api.js.map +1 -0
  155. package/public/js/atlas.d.ts +6 -0
  156. package/public/js/atlas.js +50 -58
  157. package/public/js/atlas.js.map +1 -0
  158. package/public/js/blameView.d.ts +2 -0
  159. package/public/js/blameView.js +36 -37
  160. package/public/js/blameView.js.map +1 -0
  161. package/public/js/codeTab.d.ts +3 -0
  162. package/public/js/codeTab.js +175 -180
  163. package/public/js/codeTab.js.map +1 -0
  164. package/public/js/codeViewer.d.ts +5 -0
  165. package/public/js/codeViewer.js +162 -150
  166. package/public/js/codeViewer.js.map +1 -0
  167. package/public/js/components/FileChip.d.ts +9 -0
  168. package/public/js/components/FileChip.js +45 -49
  169. package/public/js/components/FileChip.js.map +1 -0
  170. package/public/js/components/MetricSparkline.d.ts +7 -0
  171. package/public/js/components/MetricSparkline.js +35 -44
  172. package/public/js/components/MetricSparkline.js.map +1 -0
  173. package/public/js/components/RiskBadge.d.ts +9 -0
  174. package/public/js/components/RiskBadge.js +55 -63
  175. package/public/js/components/RiskBadge.js.map +1 -0
  176. package/public/js/deepAnalysisView.d.ts +99 -0
  177. package/public/js/deepAnalysisView.js +282 -302
  178. package/public/js/deepAnalysisView.js.map +1 -0
  179. package/public/js/diagramPane.d.ts +9 -0
  180. package/public/js/diagramPane.js +138 -135
  181. package/public/js/diagramPane.js.map +1 -0
  182. package/public/js/diffView.d.ts +6 -0
  183. package/public/js/diffView.js +222 -209
  184. package/public/js/diffView.js.map +1 -0
  185. package/public/js/docsView.d.ts +2 -0
  186. package/public/js/docsView.js +441 -442
  187. package/public/js/docsView.js.map +1 -0
  188. package/public/js/dom.d.ts +197 -0
  189. package/public/js/dom.js +5 -0
  190. package/public/js/dom.js.map +1 -0
  191. package/public/js/fileSourceBrowser.d.ts +5 -0
  192. package/public/js/fileSourceBrowser.js +33 -41
  193. package/public/js/fileSourceBrowser.js.map +1 -0
  194. package/public/js/flameGraph.d.ts +5 -0
  195. package/public/js/flameGraph.js +62 -72
  196. package/public/js/flameGraph.js.map +1 -0
  197. package/public/js/forceGraph.d.ts +10 -0
  198. package/public/js/forceGraph.js +581 -556
  199. package/public/js/forceGraph.js.map +1 -0
  200. package/public/js/heatmap.d.ts +4 -0
  201. package/public/js/heatmap.js +142 -164
  202. package/public/js/heatmap.js.map +1 -0
  203. package/public/js/highlight.d.ts +2 -0
  204. package/public/js/highlight.js +116 -107
  205. package/public/js/highlight.js.map +1 -0
  206. package/public/js/html.d.ts +2 -0
  207. package/public/js/html.js +14 -44
  208. package/public/js/html.js.map +1 -0
  209. package/public/js/insightsView.d.ts +5 -0
  210. package/public/js/insightsView.js +93 -111
  211. package/public/js/insightsView.js.map +1 -0
  212. package/public/js/inspector.d.ts +37 -0
  213. package/public/js/inspector.js +416 -445
  214. package/public/js/inspector.js.map +1 -0
  215. package/public/js/llm.d.ts +115 -0
  216. package/public/js/llm.js +246 -278
  217. package/public/js/llm.js.map +1 -0
  218. package/public/js/mapView.d.ts +3 -0
  219. package/public/js/mapView.js +121 -120
  220. package/public/js/mapView.js.map +1 -0
  221. package/public/js/markdown.d.ts +3 -0
  222. package/public/js/markdown.js +84 -91
  223. package/public/js/markdown.js.map +1 -0
  224. package/public/js/mindmap.d.ts +32 -0
  225. package/public/js/mindmap.js +244 -259
  226. package/public/js/mindmap.js.map +1 -0
  227. package/public/js/mobilePanels.d.ts +5 -0
  228. package/public/js/mobilePanels.js +50 -0
  229. package/public/js/mobilePanels.js.map +1 -0
  230. package/public/js/repoFiles.d.ts +12 -0
  231. package/public/js/repoFiles.js +29 -28
  232. package/public/js/repoFiles.js.map +1 -0
  233. package/public/js/reviewView.d.ts +28 -0
  234. package/public/js/reviewView.js +256 -0
  235. package/public/js/reviewView.js.map +1 -0
  236. package/public/js/sbomView.d.ts +3 -0
  237. package/public/js/sbomView.js +59 -67
  238. package/public/js/sbomView.js.map +1 -0
  239. package/public/js/scanCache.d.ts +16 -0
  240. package/public/js/scanCache.js +67 -65
  241. package/public/js/scanCache.js.map +1 -0
  242. package/public/js/search.d.ts +7 -0
  243. package/public/js/search.js +868 -800
  244. package/public/js/search.js.map +1 -0
  245. package/public/js/searchLocal.d.ts +42 -0
  246. package/public/js/searchLocal.js +87 -0
  247. package/public/js/searchLocal.js.map +1 -0
  248. package/public/js/serverSettings.d.ts +5 -0
  249. package/public/js/serverSettings.js +153 -164
  250. package/public/js/serverSettings.js.map +1 -0
  251. package/public/js/state.d.ts +798 -0
  252. package/public/js/state.js +114 -122
  253. package/public/js/state.js.map +1 -0
  254. package/public/js/tour.d.ts +6 -0
  255. package/public/js/tour.js +5 -7
  256. package/public/js/tour.js.map +1 -0
  257. package/public/js/transitions.d.ts +2 -0
  258. package/public/js/transitions.js +9 -10
  259. package/public/js/transitions.js.map +1 -0
  260. package/public/js/tree.d.ts +12 -0
  261. package/public/js/tree.js +157 -154
  262. package/public/js/tree.js.map +1 -0
  263. package/public/js/workflowsView.d.ts +3 -0
  264. package/public/js/workflowsView.js +15 -22
  265. package/public/js/workflowsView.js.map +1 -0
  266. package/public/search.css +495 -0
  267. package/public/styles.css +188 -346
  268. package/public/workbench.css +166 -0
  269. package/server/apiAuth.d.ts +3 -0
  270. package/server/apiAuth.js +18 -20
  271. package/server/apiAuth.js.map +1 -0
  272. package/server/apiDiff.d.ts +2 -0
  273. package/server/apiDiff.js +20 -18
  274. package/server/apiDiff.js.map +1 -0
  275. package/server/apiDocs.d.ts +2 -0
  276. package/server/apiDocs.js +38 -40
  277. package/server/apiDocs.js.map +1 -0
  278. package/server/apiFile.d.ts +1 -0
  279. package/server/apiFile.js +14 -17
  280. package/server/apiFile.js.map +1 -0
  281. package/server/apiGitBlame.d.ts +2 -0
  282. package/server/apiGitBlame.js +50 -51
  283. package/server/apiGitBlame.js.map +1 -0
  284. package/server/apiMcp.d.ts +4 -0
  285. package/server/apiMcp.js +64 -66
  286. package/server/apiMcp.js.map +1 -0
  287. package/server/apiReview.d.ts +2 -0
  288. package/server/apiReview.js +45 -0
  289. package/server/apiReview.js.map +1 -0
  290. package/server/apiScan.d.ts +5 -0
  291. package/server/apiScan.js +90 -71
  292. package/server/apiScan.js.map +1 -0
  293. package/server/apiSearch.d.ts +33 -0
  294. package/server/apiSearch.js +184 -200
  295. package/server/apiSearch.js.map +1 -0
  296. package/server/apiSettings.d.ts +4 -0
  297. package/server/apiSettings.js +84 -85
  298. package/server/apiSettings.js.map +1 -0
  299. package/server/apiTools.d.ts +3 -0
  300. package/server/apiTools.js +55 -56
  301. package/server/apiTools.js.map +1 -0
  302. package/server/auth.d.ts +5 -0
  303. package/server/auth.js +54 -51
  304. package/server/auth.js.map +1 -0
  305. package/server/config.d.ts +42 -0
  306. package/server/config.js +189 -194
  307. package/server/config.js.map +1 -0
  308. package/server/daemon.d.ts +12 -0
  309. package/server/daemon.js +124 -114
  310. package/server/daemon.js.map +1 -0
  311. package/server/fileSourceNode.d.ts +2 -0
  312. package/server/fileSourceNode.js +34 -36
  313. package/server/fileSourceNode.js.map +1 -0
  314. package/server/gitClone.d.ts +9 -0
  315. package/server/gitClone.js +59 -61
  316. package/server/gitClone.js.map +1 -0
  317. package/server/gitDiff.d.ts +36 -0
  318. package/server/gitDiff.js +271 -169
  319. package/server/gitDiff.js.map +1 -0
  320. package/server/gitHistory.d.ts +12 -0
  321. package/server/gitHistory.js +80 -80
  322. package/server/gitHistory.js.map +1 -0
  323. package/server/gitRemote.d.ts +12 -0
  324. package/server/gitRemote.js +37 -38
  325. package/server/gitRemote.js.map +1 -0
  326. package/server/hermesChat.d.ts +13 -0
  327. package/server/hermesChat.js +145 -0
  328. package/server/hermesChat.js.map +1 -0
  329. package/server/htmlText.d.ts +4 -0
  330. package/server/htmlText.js +16 -23
  331. package/server/htmlText.js.map +1 -0
  332. package/server/http.d.ts +6 -0
  333. package/server/http.js +27 -34
  334. package/server/http.js.map +1 -0
  335. package/server/httpGuards.d.ts +2 -0
  336. package/server/httpGuards.js +47 -43
  337. package/server/httpGuards.js.map +1 -0
  338. package/server/https.d.ts +34 -0
  339. package/server/https.js +121 -111
  340. package/server/https.js.map +1 -0
  341. package/server/index.d.ts +40 -0
  342. package/server/index.js +139 -147
  343. package/server/index.js.map +1 -0
  344. package/server/layout.d.ts +26 -0
  345. package/server/layout.js +59 -64
  346. package/server/layout.js.map +1 -0
  347. package/server/llmProxy.d.ts +3 -0
  348. package/server/llmProxy.js +137 -135
  349. package/server/llmProxy.js.map +1 -0
  350. package/server/logger.d.ts +38 -0
  351. package/server/logger.js +198 -211
  352. package/server/logger.js.map +1 -0
  353. package/server/mcp/analysis.d.ts +29 -0
  354. package/server/mcp/analysis.js +99 -120
  355. package/server/mcp/analysis.js.map +1 -0
  356. package/server/mcp/http.d.ts +9 -0
  357. package/server/mcp/http.js +128 -137
  358. package/server/mcp/http.js.map +1 -0
  359. package/server/mcp/runner.d.ts +68 -0
  360. package/server/mcp/runner.js +208 -218
  361. package/server/mcp/runner.js.map +1 -0
  362. package/server/mcp/server.d.ts +29 -0
  363. package/server/mcp/server.js +167 -180
  364. package/server/mcp/server.js.map +1 -0
  365. package/server/mcp/standalone.d.ts +1 -0
  366. package/server/mcp/standalone.js +8 -10
  367. package/server/mcp/standalone.js.map +1 -0
  368. package/server/mcp/tools.d.ts +2020 -0
  369. package/server/mcp/tools.js +523 -574
  370. package/server/mcp/tools.js.map +1 -0
  371. package/server/paths.d.ts +3 -0
  372. package/server/paths.js +17 -15
  373. package/server/paths.js.map +1 -0
  374. package/server/pidfile.d.ts +9 -0
  375. package/server/pidfile.js +43 -45
  376. package/server/pidfile.js.map +1 -0
  377. package/server/review.d.ts +5 -0
  378. package/server/review.js +35 -0
  379. package/server/review.js.map +1 -0
  380. package/server/router.d.ts +33 -0
  381. package/server/router.js +195 -197
  382. package/server/router.js.map +1 -0
  383. package/server/scanRequest.d.ts +3 -0
  384. package/server/scanRequest.js +36 -0
  385. package/server/scanRequest.js.map +1 -0
  386. package/server/searchIndex.d.ts +17 -0
  387. package/server/searchIndex.js +83 -80
  388. package/server/searchIndex.js.map +1 -0
  389. package/server/sessions.d.ts +14 -0
  390. package/server/sessions.js +62 -70
  391. package/server/sessions.js.map +1 -0
  392. package/server/startup.d.ts +118 -0
  393. package/server/startup.js +143 -148
  394. package/server/startup.js.map +1 -0
  395. package/server/static.d.ts +6 -0
  396. package/server/static.js +28 -31
  397. package/server/static.js.map +1 -0
  398. package/server/tools/install.d.ts +4 -0
  399. package/server/tools/install.js +176 -185
  400. package/server/tools/install.js.map +1 -0
  401. package/server/tools/parse.d.ts +8 -0
  402. package/server/tools/parse.js +112 -114
  403. package/server/tools/parse.js.map +1 -0
  404. package/server/tools/platform.d.ts +9 -0
  405. package/server/tools/platform.js +49 -47
  406. package/server/tools/platform.js.map +1 -0
  407. package/server/tools/registry.d.ts +137 -0
  408. package/server/tools/registry.js +173 -172
  409. package/server/tools/registry.js.map +1 -0
  410. package/server/tools/scan.d.ts +34 -0
  411. package/server/tools/scan.js +89 -98
  412. package/server/tools/scan.js.map +1 -0
  413. package/server/tools.d.ts +34 -0
  414. package/server/tools.js +94 -107
  415. package/server/tools.js.map +1 -0
  416. package/server/tunnel.d.ts +29 -0
  417. package/server/tunnel.js +61 -66
  418. package/server/tunnel.js.map +1 -0
  419. package/shared/analyzer/docs.d.ts +16 -0
  420. package/shared/analyzer/docs.js +99 -93
  421. package/shared/analyzer/docs.js.map +1 -0
  422. package/shared/analyzer/explainLocal.d.ts +4 -0
  423. package/shared/analyzer/explainLocal.js +122 -142
  424. package/shared/analyzer/explainLocal.js.map +1 -0
  425. package/shared/analyzer/github.d.ts +37 -0
  426. package/shared/analyzer/github.js +41 -43
  427. package/shared/analyzer/github.js.map +1 -0
  428. package/shared/analyzer/graph.d.ts +41 -0
  429. package/shared/analyzer/graph.js +393 -400
  430. package/shared/analyzer/graph.js.map +1 -0
  431. package/shared/analyzer/health.d.ts +33 -0
  432. package/shared/analyzer/health.js +197 -193
  433. package/shared/analyzer/health.js.map +1 -0
  434. package/shared/analyzer/history.d.ts +29 -0
  435. package/shared/analyzer/history.js +110 -111
  436. package/shared/analyzer/history.js.map +1 -0
  437. package/shared/analyzer/jsonConfig.d.ts +2 -0
  438. package/shared/analyzer/jsonConfig.js +61 -0
  439. package/shared/analyzer/jsonConfig.js.map +1 -0
  440. package/shared/analyzer/languages/csharp.d.ts +26 -0
  441. package/shared/analyzer/languages/csharp.js +83 -88
  442. package/shared/analyzer/languages/csharp.js.map +1 -0
  443. package/shared/analyzer/languages/generic.d.ts +23 -0
  444. package/shared/analyzer/languages/generic.js +94 -105
  445. package/shared/analyzer/languages/generic.js.map +1 -0
  446. package/shared/analyzer/languages/go.d.ts +24 -0
  447. package/shared/analyzer/languages/go.js +45 -54
  448. package/shared/analyzer/languages/go.js.map +1 -0
  449. package/shared/analyzer/languages/index.d.ts +6 -0
  450. package/shared/analyzer/languages/index.js +19 -20
  451. package/shared/analyzer/languages/index.js.map +1 -0
  452. package/shared/analyzer/languages/java.d.ts +20 -0
  453. package/shared/analyzer/languages/java.js +73 -78
  454. package/shared/analyzer/languages/java.js.map +1 -0
  455. package/shared/analyzer/languages/javascript.d.ts +12 -0
  456. package/shared/analyzer/languages/javascript.js +258 -209
  457. package/shared/analyzer/languages/javascript.js.map +1 -0
  458. package/shared/analyzer/languages/python.d.ts +23 -0
  459. package/shared/analyzer/languages/python.js +92 -100
  460. package/shared/analyzer/languages/python.js.map +1 -0
  461. package/shared/analyzer/languages/rust.d.ts +17 -0
  462. package/shared/analyzer/languages/rust.js +96 -98
  463. package/shared/analyzer/languages/rust.js.map +1 -0
  464. package/shared/analyzer/languages/typescript.d.ts +11 -0
  465. package/shared/analyzer/languages/typescript.js +117 -116
  466. package/shared/analyzer/languages/typescript.js.map +1 -0
  467. package/shared/analyzer/licenses.d.ts +18 -0
  468. package/shared/analyzer/licenses.js +130 -133
  469. package/shared/analyzer/licenses.js.map +1 -0
  470. package/shared/analyzer/metrics.d.ts +20 -0
  471. package/shared/analyzer/metrics.js +112 -104
  472. package/shared/analyzer/metrics.js.map +1 -0
  473. package/shared/analyzer/pathUtil.d.ts +10 -0
  474. package/shared/analyzer/pathUtil.js +29 -35
  475. package/shared/analyzer/pathUtil.js.map +1 -0
  476. package/shared/analyzer/patterns.d.ts +11 -0
  477. package/shared/analyzer/patterns.js +231 -253
  478. package/shared/analyzer/patterns.js.map +1 -0
  479. package/shared/analyzer/scan.d.ts +62 -0
  480. package/shared/analyzer/scan.js +451 -442
  481. package/shared/analyzer/scan.js.map +1 -0
  482. package/shared/analyzer/scanControl.d.ts +11 -0
  483. package/shared/analyzer/scanControl.js +62 -0
  484. package/shared/analyzer/scanControl.js.map +1 -0
  485. package/shared/analyzer/security.d.ts +18 -0
  486. package/shared/analyzer/security.js +159 -159
  487. package/shared/analyzer/security.js.map +1 -0
  488. package/shared/analyzer/services.d.ts +5 -0
  489. package/shared/analyzer/services.js +141 -138
  490. package/shared/analyzer/services.js.map +1 -0
  491. package/shared/analyzer/stack.d.ts +6 -0
  492. package/shared/analyzer/stack.js +134 -141
  493. package/shared/analyzer/stack.js.map +1 -0
  494. package/shared/analyzer/tour.d.ts +2 -0
  495. package/shared/analyzer/tour.js +45 -53
  496. package/shared/analyzer/tour.js.map +1 -0
  497. package/shared/analyzer/util.d.ts +8 -0
  498. package/shared/analyzer/util.js +130 -76
  499. package/shared/analyzer/util.js.map +1 -0
  500. package/shared/analyzer/workflows.d.ts +8 -0
  501. package/shared/analyzer/workflows.js +139 -134
  502. package/shared/analyzer/workflows.js.map +1 -0
  503. package/shared/contracts.d.ts +250 -0
  504. package/shared/contracts.js +2 -0
  505. package/shared/contracts.js.map +1 -0
  506. package/shared/diagram/aiFacts.d.ts +35 -0
  507. package/shared/diagram/aiFacts.js +102 -105
  508. package/shared/diagram/aiFacts.js.map +1 -0
  509. package/shared/diagram/aiMermaid.d.ts +5 -0
  510. package/shared/diagram/aiMermaid.js +50 -46
  511. package/shared/diagram/aiMermaid.js.map +1 -0
  512. package/shared/diagram/atlas.d.ts +16 -0
  513. package/shared/diagram/atlas.js +51 -60
  514. package/shared/diagram/atlas.js.map +1 -0
  515. package/shared/diagram/mermaid.d.ts +43 -0
  516. package/shared/diagram/mermaid.js +348 -370
  517. package/shared/diagram/mermaid.js.map +1 -0
  518. package/shared/review/contracts.d.ts +95 -0
  519. package/shared/review/contracts.js +2 -0
  520. package/shared/review/contracts.js.map +1 -0
  521. package/shared/review/review.d.ts +10 -0
  522. package/shared/review/review.js +184 -0
  523. package/shared/review/review.js.map +1 -0
  524. package/shared/search/query.d.ts +32 -0
  525. package/shared/search/query.js +395 -388
  526. package/shared/search/query.js.map +1 -0
@@ -6,121 +6,120 @@
6
6
  // status codes, the secret-handling rules (never read the key back out, show a
7
7
  // new one exactly once), and the restart hint — the running server bound its
8
8
  // socket at boot, so a host/port change is real but not yet in effect.
9
-
10
- import {
11
- DEFAULT_SETTINGS, generateAccessKey, normalizeSettings, publicSettings,
12
- readSettings, updateSettings, writeSettings, configPath,
13
- } from './config.js';
9
+ import { DEFAULT_SETTINGS, generateAccessKey, normalizeSettings, publicSettings, readSettings, updateSettings, writeSettings, configPath, } from './config.js';
14
10
  import { sendError, sendJSON } from './http.js';
15
11
  import { sessionCookie } from './auth.js';
16
12
  import { tunnelStatus } from './tunnel.js';
17
13
  import { httpsStatus } from './https.js';
18
-
19
14
  // Where this request's settings live. The router's config object carries the
20
15
  // path when the server was booted with one; tests and a bare `createServer()`
21
16
  // get the defaults-in-memory view instead of somebody's home directory.
22
17
  function settingsFile(config) {
23
- return config?.configPath || null;
18
+ return config?.configPath || null;
24
19
  }
25
-
26
20
  async function currentSettings(config) {
27
- if (typeof config?.getSettings === 'function') return config.getSettings();
28
- const file = settingsFile(config);
29
- if (file) return readSettings(file);
30
- return { ...DEFAULT_SETTINGS, account: { ...DEFAULT_SETTINGS.account }, tunnel: { ...DEFAULT_SETTINGS.tunnel } };
21
+ if (typeof config?.getSettings === 'function')
22
+ return config.getSettings();
23
+ const file = settingsFile(config);
24
+ if (file)
25
+ return readSettings(file);
26
+ return { ...DEFAULT_SETTINGS, account: { ...DEFAULT_SETTINGS.account }, tunnel: { ...DEFAULT_SETTINGS.tunnel } };
31
27
  }
32
-
33
28
  // The public body every read returns: settings with the secret masked, plus
34
29
  // the connection picture (URLs, tunnel status) the drawer and CLI both render.
35
30
  async function publicBody(config) {
36
- const settings = await currentSettings(config);
37
- return {
38
- settings: publicSettings(settings),
39
- configFile: settingsFile(config) || configPath(),
40
- node: process.version,
41
- tunnels: tunnelStatus(settings),
42
- https: httpsStatus(settings, settingsFile(config) || configPath()),
43
- };
31
+ const settings = await currentSettings(config);
32
+ return {
33
+ settings: publicSettings(settings),
34
+ configFile: settingsFile(config) || configPath(),
35
+ node: process.version,
36
+ tunnels: tunnelStatus(settings),
37
+ https: httpsStatus(settings, settingsFile(config) || configPath()),
38
+ };
44
39
  }
45
-
46
40
  export async function handleGetSettings(req, res, config) {
47
- try {
48
- sendJSON(res, 200, await publicBody(config));
49
- } catch (err) {
50
- sendError(res, 500, err.message || 'The settings could not be read.');
51
- }
41
+ try {
42
+ sendJSON(res, 200, await publicBody(config));
43
+ }
44
+ catch (err) {
45
+ sendError(res, 500, err.message || 'The settings could not be read.');
46
+ }
52
47
  }
53
-
54
48
  // Which changes the running server cannot absorb. Access-key and tunnel flags
55
49
  // are read live per request. Host/port are bound sockets. Domain/HTTPS change
56
50
  // the managed Caddyfile and certificate, so Caddy must validate/reload too.
57
51
  function restartRequired(config, next) {
58
- const boot = config?.boot;
59
- if (!boot) return [];
60
- const changed = [];
61
- if (next.host !== boot.host) changed.push('host');
62
- if (next.port !== boot.port) changed.push('port');
63
- if (next.domain !== boot.domain) changed.push('domain');
64
- if (next.https !== boot.https) changed.push('https');
65
- return changed;
52
+ const boot = config?.boot;
53
+ if (!boot)
54
+ return [];
55
+ const changed = [];
56
+ if (next.host !== boot.host)
57
+ changed.push('host');
58
+ if (next.port !== boot.port)
59
+ changed.push('port');
60
+ if (next.domain !== boot.domain)
61
+ changed.push('domain');
62
+ if (next.https !== boot.https)
63
+ changed.push('https');
64
+ return changed;
66
65
  }
67
-
68
66
  export async function handleUpdateSettings(res, body, config) {
69
- const file = settingsFile(config);
70
- if (!file) {
71
- return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');
72
- }
73
- if (!body || typeof body !== 'object' || Array.isArray(body)) {
74
- return sendError(res, 400, 'Send a JSON object of settings to change.');
75
- }
76
- // Only known keys may be patched — a typo like "ports" must be a loud 400,
77
- // not a silently ignored write that the UI then claims it saved.
78
- const allowed = new Set(['mode', 'host', 'port', 'domain', 'https', 'autoOpen', 'account', 'tunnel']);
79
- const unknown = Object.keys(body).filter((k) => k !== 'accessKey' && !allowed.has(k));
80
- if (unknown.length) {
81
- return sendError(res, 400, `Unknown setting${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}.`);
82
- }
83
- if ('accessKey' in body) {
84
- return sendError(res, 400, 'The access key is rotated, not patched — POST /api/settings/access-key.');
85
- }
86
- try {
87
- const next = await updateSettings(body, file);
88
- sendJSON(res, 200, {
89
- settings: publicSettings(next),
90
- restartRequired: restartRequired(config, next),
91
- });
92
- } catch (err) {
93
- sendError(res, 400, err.message || 'Those settings did not validate.');
94
- }
67
+ const file = settingsFile(config);
68
+ if (!file) {
69
+ return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');
70
+ }
71
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
72
+ return sendError(res, 400, 'Send a JSON object of settings to change.');
73
+ }
74
+ // Only known keys may be patched — a typo like "ports" must be a loud 400,
75
+ // not a silently ignored write that the UI then claims it saved.
76
+ const allowed = new Set(['mode', 'host', 'port', 'domain', 'https', 'autoOpen', 'account', 'tunnel']);
77
+ const unknown = Object.keys(body).filter((k) => k !== 'accessKey' && !allowed.has(k));
78
+ if (unknown.length) {
79
+ return sendError(res, 400, `Unknown setting${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}.`);
80
+ }
81
+ if ('accessKey' in body) {
82
+ return sendError(res, 400, 'The access key is rotated, not patched — POST /api/settings/access-key.');
83
+ }
84
+ try {
85
+ const next = await updateSettings(body, file);
86
+ sendJSON(res, 200, {
87
+ settings: publicSettings(next),
88
+ restartRequired: restartRequired(config, next),
89
+ });
90
+ }
91
+ catch (err) {
92
+ sendError(res, 400, err.message || 'Those settings did not validate.');
93
+ }
95
94
  }
96
-
97
95
  // Rotation returns the new key in the clear, once. It is never in a GET after
98
96
  // that — `hasAccessKey`/`accessKeyMasked` are all the readback there is. The
99
97
  // person copies it from this response into the devices that need it.
100
98
  export async function handleRotateAccessKey(req, res, config) {
101
- const file = settingsFile(config);
102
- if (!file) {
103
- return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');
104
- }
105
- try {
106
- const accessKey = generateAccessKey();
107
- const current = await currentSettings(config);
108
- const next = await writeSettings({ ...current, accessKey }, file);
109
- sendJSON(res, 200, {
110
- accessKey,
111
- settings: publicSettings(next),
112
- note: 'Shown once. This browser is signed in with the new key; update every other device.',
113
- }, {
114
- 'cache-control': 'no-store',
115
- 'set-cookie': sessionCookie(req, next),
116
- });
117
- } catch (err) {
118
- sendError(res, 400, err.message || 'The key could not be rotated.');
119
- }
99
+ const file = settingsFile(config);
100
+ if (!file) {
101
+ return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');
102
+ }
103
+ try {
104
+ const accessKey = generateAccessKey();
105
+ const current = await currentSettings(config);
106
+ const next = await writeSettings({ ...current, accessKey }, file);
107
+ sendJSON(res, 200, {
108
+ accessKey,
109
+ settings: publicSettings(next),
110
+ note: 'Shown once. This browser is signed in with the new key; update every other device.',
111
+ }, {
112
+ 'cache-control': 'no-store',
113
+ 'set-cookie': sessionCookie(req, next),
114
+ });
115
+ }
116
+ catch (err) {
117
+ sendError(res, 400, err.message || 'The key could not be rotated.');
118
+ }
120
119
  }
121
-
122
120
  // Kept exported for the tests: a patch that survives normalizeSettings is the
123
121
  // same patch the wizard and the API would both accept.
124
122
  export function validatePatch(patch) {
125
- return normalizeSettings({ ...DEFAULT_SETTINGS, ...patch });
123
+ return normalizeSettings({ ...DEFAULT_SETTINGS, ...patch });
126
124
  }
125
+ //# sourceMappingURL=apiSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiSettings.js","sourceRoot":"","sources":["../src/server/apiSettings.ts"],"names":[],"mappings":"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","sourcesContent":["// The settings endpoints: read the public shape, apply a patch, rotate the key.\n//\n// All three go through `server/config.js` for validation and the atomic 0600\n// write, so the CLI wizard, `onboarder config set`, and this API can never save\n// three dialects of the same file. What lives here is only what HTTP adds:\n// status codes, the secret-handling rules (never read the key back out, show a\n// new one exactly once), and the restart hint — the running server bound its\n// socket at boot, so a host/port change is real but not yet in effect.\n\nimport {\n DEFAULT_SETTINGS, generateAccessKey, normalizeSettings, publicSettings,\n readSettings, updateSettings, writeSettings, configPath,\n} from './config.js';\nimport { sendError, sendJSON } from './http.js';\nimport { sessionCookie } from './auth.js';\nimport { tunnelStatus } from './tunnel.js';\nimport { httpsStatus } from './https.js';\n\n// Where this request's settings live. The router's config object carries the\n// path when the server was booted with one; tests and a bare `createServer()`\n// get the defaults-in-memory view instead of somebody's home directory.\nfunction settingsFile(config) {\n return config?.configPath || null;\n}\n\nasync function currentSettings(config) {\n if (typeof config?.getSettings === 'function') return config.getSettings();\n const file = settingsFile(config);\n if (file) return readSettings(file);\n return { ...DEFAULT_SETTINGS, account: { ...DEFAULT_SETTINGS.account }, tunnel: { ...DEFAULT_SETTINGS.tunnel } };\n}\n\n// The public body every read returns: settings with the secret masked, plus\n// the connection picture (URLs, tunnel status) the drawer and CLI both render.\nasync function publicBody(config) {\n const settings = await currentSettings(config);\n return {\n settings: publicSettings(settings),\n configFile: settingsFile(config) || configPath(),\n node: process.version,\n tunnels: tunnelStatus(settings),\n https: httpsStatus(settings, settingsFile(config) || configPath()),\n };\n}\n\nexport async function handleGetSettings(req, res, config) {\n try {\n sendJSON(res, 200, await publicBody(config));\n } catch (err) {\n sendError(res, 500, err.message || 'The settings could not be read.');\n }\n}\n\n// Which changes the running server cannot absorb. Access-key and tunnel flags\n// are read live per request. Host/port are bound sockets. Domain/HTTPS change\n// the managed Caddyfile and certificate, so Caddy must validate/reload too.\nfunction restartRequired(config, next) {\n const boot = config?.boot;\n if (!boot) return [];\n const changed = [];\n if (next.host !== boot.host) changed.push('host');\n if (next.port !== boot.port) changed.push('port');\n if (next.domain !== boot.domain) changed.push('domain');\n if (next.https !== boot.https) changed.push('https');\n return changed;\n}\n\nexport async function handleUpdateSettings(res, body, config) {\n const file = settingsFile(config);\n if (!file) {\n return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');\n }\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n return sendError(res, 400, 'Send a JSON object of settings to change.');\n }\n // Only known keys may be patched — a typo like \"ports\" must be a loud 400,\n // not a silently ignored write that the UI then claims it saved.\n const allowed = new Set(['mode', 'host', 'port', 'domain', 'https', 'autoOpen', 'account', 'tunnel']);\n const unknown = Object.keys(body).filter((k) => k !== 'accessKey' && !allowed.has(k));\n if (unknown.length) {\n return sendError(res, 400, `Unknown setting${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}.`);\n }\n if ('accessKey' in body) {\n return sendError(res, 400, 'The access key is rotated, not patched — POST /api/settings/access-key.');\n }\n try {\n const next = await updateSettings(body, file);\n sendJSON(res, 200, {\n settings: publicSettings(next),\n restartRequired: restartRequired(config, next),\n });\n } catch (err) {\n sendError(res, 400, err.message || 'Those settings did not validate.');\n }\n}\n\n// Rotation returns the new key in the clear, once. It is never in a GET after\n// that — `hasAccessKey`/`accessKeyMasked` are all the readback there is. The\n// person copies it from this response into the devices that need it.\nexport async function handleRotateAccessKey(req, res, config) {\n const file = settingsFile(config);\n if (!file) {\n return sendError(res, 400, 'This server was started without a config file, so there is nothing to save to.');\n }\n try {\n const accessKey = generateAccessKey();\n const current = await currentSettings(config);\n const next = await writeSettings({ ...current, accessKey }, file);\n sendJSON(res, 200, {\n accessKey,\n settings: publicSettings(next),\n note: 'Shown once. This browser is signed in with the new key; update every other device.',\n }, {\n 'cache-control': 'no-store',\n 'set-cookie': sessionCookie(req, next),\n });\n } catch (err) {\n sendError(res, 400, err.message || 'The key could not be rotated.');\n }\n}\n\n// Kept exported for the tests: a patch that survives normalizeSettings is the\n// same patch the wizard and the API would both accept.\nexport function validatePatch(patch) {\n return normalizeSettings({ ...DEFAULT_SETTINGS, ...patch });\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function handleToolsStatus(res: any): void;
2
+ export declare function handleToolsRun(res: any, body: any): Promise<void>;
3
+ export declare function handleToolsInstall(res: any, body: any): Promise<void>;
@@ -7,46 +7,42 @@
7
7
  // else. `tools.js` spawns with argument arrays and hard timeouts, so a crafted
8
8
  // path becomes an argument and a hung tool becomes a failed pass, not a stuck
9
9
  // server.
10
-
11
10
  import { sendError, sendJSON } from './http.js';
12
11
  import { getSession } from './sessions.js';
13
12
  import { runExternalAnalysis, toolsStatus } from './tools/scan.js';
14
13
  import { installTool, isInstalling, plansFor } from './tools/install.js';
15
14
  import { TOOL_DEFS } from './tools/registry.js';
16
-
17
15
  const KNOWN_TOOLS = new Set(TOOL_DEFS.map((d) => d.id));
18
-
19
16
  export function handleToolsStatus(res) {
20
- sendJSON(res, 200, { tools: toolsStatus() });
17
+ sendJSON(res, 200, { tools: toolsStatus() });
21
18
  }
22
-
23
19
  export async function handleToolsRun(res, body) {
24
- const { scanId, kinds, tools, options } = body || {};
25
- if (!scanId) return sendError(res, 400, 'Missing scanId.');
26
- const session = getSession(scanId);
27
- if (!session) return sendError(res, 404, 'That scan is gone. Rescan the repo.');
28
-
29
- // `kinds` narrows by purpose, `tools` to named engines. An unknown value is a
30
- // typo the caller would rather hear about than have silently ignored.
31
- let kindFilter = null;
32
- if (Array.isArray(kinds) && kinds.length) {
33
- const ok = kinds.filter((k) => k === 'security' || k === 'dead-code');
34
- if (!ok.length) return sendError(res, 400, 'kinds must be "security" and/or "dead-code".');
35
- kindFilter = ok;
36
- }
37
- let toolFilter = null;
38
- if (Array.isArray(tools) && tools.length) {
39
- toolFilter = tools.map((t) => String(t));
40
- }
41
-
42
- // `options` is the per-engine GUI form. Validation happens against the
43
- // registry schema inside the run — the handler just passes it along.
44
- const report = await runExternalAnalysis(session.root, {
45
- kinds: kindFilter, tools: toolFilter, options,
46
- });
47
- return sendJSON(res, 200, report);
20
+ const { scanId, kinds, tools, options } = body || {};
21
+ if (!scanId)
22
+ return sendError(res, 400, 'Missing scanId.');
23
+ const session = getSession(scanId);
24
+ if (!session)
25
+ return sendError(res, 404, 'That scan is gone. Rescan the repo.');
26
+ // `kinds` narrows by purpose, `tools` to named engines. An unknown value is a
27
+ // typo the caller would rather hear about than have silently ignored.
28
+ let kindFilter = null;
29
+ if (Array.isArray(kinds) && kinds.length) {
30
+ const ok = kinds.filter((k) => k === 'security' || k === 'dead-code');
31
+ if (!ok.length)
32
+ return sendError(res, 400, 'kinds must be "security" and/or "dead-code".');
33
+ kindFilter = ok;
34
+ }
35
+ let toolFilter = null;
36
+ if (Array.isArray(tools) && tools.length) {
37
+ toolFilter = tools.map((t) => String(t));
38
+ }
39
+ // `options` is the per-engine GUI form. Validation happens against the
40
+ // registry schema inside the run — the handler just passes it along.
41
+ const report = await runExternalAnalysis(session.root, {
42
+ kinds: kindFilter, tools: toolFilter, options,
43
+ });
44
+ return sendJSON(res, 200, report);
48
45
  }
49
-
50
46
  // POST /api/tools/install — install a missing engine, streaming progress.
51
47
  //
52
48
  // This is the one endpoint that changes the machine, so it is worth spelling
@@ -61,30 +57,33 @@ export async function handleToolsRun(res, body) {
61
57
  // * The stream is SSE in the same `data:`-framed shape the explain proxy
62
58
  // uses, so the front end reads install progress exactly like AI output.
63
59
  export async function handleToolsInstall(res, body) {
64
- const tool = String(body?.tool || '');
65
- if (!KNOWN_TOOLS.has(tool)) {
66
- return sendError(res, 400, `Unknown tool "${tool}". Known: ${[...KNOWN_TOOLS].join(', ')}.`);
67
- }
68
- if (!plansFor(tool).length) {
69
- return sendError(res, 400, `Onboarder has no install plan for ${tool} on ${process.platform}.`);
70
- }
71
- if (isInstalling(tool)) {
72
- return sendError(res, 409, `${tool} is already being installed.`);
73
- }
74
-
75
- res.writeHead(200, {
76
- 'content-type': 'text/event-stream; charset=utf-8',
77
- 'cache-control': 'no-cache',
78
- connection: 'keep-alive',
79
- });
80
- const send = (event) => {
81
- try { res.write('data: ' + JSON.stringify(event) + '\n\n'); } catch { /* client went away */ }
82
- };
83
-
84
- try {
85
- await installTool(tool, send);
86
- } catch (err) {
87
- send({ type: 'done', ok: false, error: err.message || 'The install failed.' });
88
- }
89
- res.end();
60
+ const tool = String(body?.tool || '');
61
+ if (!KNOWN_TOOLS.has(tool)) {
62
+ return sendError(res, 400, `Unknown tool "${tool}". Known: ${[...KNOWN_TOOLS].join(', ')}.`);
63
+ }
64
+ if (!plansFor(tool).length) {
65
+ return sendError(res, 400, `Onboarder has no install plan for ${tool} on ${process.platform}.`);
66
+ }
67
+ if (isInstalling(tool)) {
68
+ return sendError(res, 409, `${tool} is already being installed.`);
69
+ }
70
+ res.writeHead(200, {
71
+ 'content-type': 'text/event-stream; charset=utf-8',
72
+ 'cache-control': 'no-cache',
73
+ connection: 'keep-alive',
74
+ });
75
+ const send = (event) => {
76
+ try {
77
+ res.write('data: ' + JSON.stringify(event) + '\n\n');
78
+ }
79
+ catch { /* client went away */ }
80
+ };
81
+ try {
82
+ await installTool(tool, send);
83
+ }
84
+ catch (err) {
85
+ send({ type: 'done', ok: false, error: err.message || 'The install failed.' });
86
+ }
87
+ res.end();
90
88
  }
89
+ //# sourceMappingURL=apiTools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiTools.js","sourceRoot":"","sources":["../src/server/apiTools.ts"],"names":[],"mappings":"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","sourcesContent":["// GET /api/tools — which analyzers this machine can run, for the engines panel.\n// POST /api/tools/run — run the available analyzers against a live scan and\n// return the normalized findings.\n//\n// The scan id is a capability the same way it is for `/api/file`: it names a\n// root the person chose, and the analyzers are run inside that root and nowhere\n// else. `tools.js` spawns with argument arrays and hard timeouts, so a crafted\n// path becomes an argument and a hung tool becomes a failed pass, not a stuck\n// server.\n\nimport { sendError, sendJSON } from './http.js';\nimport { getSession } from './sessions.js';\nimport { runExternalAnalysis, toolsStatus } from './tools/scan.js';\nimport { installTool, isInstalling, plansFor } from './tools/install.js';\nimport { TOOL_DEFS } from './tools/registry.js';\n\nconst KNOWN_TOOLS = new Set(TOOL_DEFS.map((d) => d.id));\n\nexport function handleToolsStatus(res) {\n sendJSON(res, 200, { tools: toolsStatus() });\n}\n\nexport async function handleToolsRun(res, body) {\n const { scanId, kinds, tools, options } = body || {};\n if (!scanId) return sendError(res, 400, 'Missing scanId.');\n const session = getSession(scanId);\n if (!session) return sendError(res, 404, 'That scan is gone. Rescan the repo.');\n\n // `kinds` narrows by purpose, `tools` to named engines. An unknown value is a\n // typo the caller would rather hear about than have silently ignored.\n let kindFilter = null;\n if (Array.isArray(kinds) && kinds.length) {\n const ok = kinds.filter((k) => k === 'security' || k === 'dead-code');\n if (!ok.length) return sendError(res, 400, 'kinds must be \"security\" and/or \"dead-code\".');\n kindFilter = ok;\n }\n let toolFilter = null;\n if (Array.isArray(tools) && tools.length) {\n toolFilter = tools.map((t) => String(t));\n }\n\n // `options` is the per-engine GUI form. Validation happens against the\n // registry schema inside the run — the handler just passes it along.\n const report = await runExternalAnalysis(session.root, {\n kinds: kindFilter, tools: toolFilter, options,\n });\n return sendJSON(res, 200, report);\n}\n\n// POST /api/tools/install — install a missing engine, streaming progress.\n//\n// This is the one endpoint that changes the machine, so it is worth spelling\n// out why that is safe enough for a local, self-hosted app:\n//\n// * The route is a POST, which the router's same-origin gate already\n// refuses for any page that is not Onboarder's own — a foreign tab cannot\n// drive it.\n// * `tool` must name a registry entry; the install plans are fixed data in\n// `tools/install.js`, spawned as argument arrays. Nothing from the\n// request body ever becomes part of a command line.\n// * The stream is SSE in the same `data:`-framed shape the explain proxy\n// uses, so the front end reads install progress exactly like AI output.\nexport async function handleToolsInstall(res, body) {\n const tool = String(body?.tool || '');\n if (!KNOWN_TOOLS.has(tool)) {\n return sendError(res, 400, `Unknown tool \"${tool}\". Known: ${[...KNOWN_TOOLS].join(', ')}.`);\n }\n if (!plansFor(tool).length) {\n return sendError(res, 400, `Onboarder has no install plan for ${tool} on ${process.platform}.`);\n }\n if (isInstalling(tool)) {\n return sendError(res, 409, `${tool} is already being installed.`);\n }\n\n res.writeHead(200, {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache',\n connection: 'keep-alive',\n });\n const send = (event) => {\n try { res.write('data: ' + JSON.stringify(event) + '\\n\\n'); } catch { /* client went away */ }\n };\n\n try {\n await installTool(tool, send);\n } catch (err) {\n send({ type: 'done', ok: false, error: err.message || 'The install failed.' });\n }\n res.end();\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const SESSION_COOKIE = "onboarder_session";
2
+ export declare const SESSION_TTL_SECONDS: number;
3
+ export declare function createSession(settings: any, now?: number): string;
4
+ export declare function hasValidSession(req: any, settings: any, now?: number): boolean;
5
+ export declare function sessionCookie(req: any, settings: any, { clear }?: Record<string, any>): string;
package/server/auth.js CHANGED
@@ -4,69 +4,72 @@
4
4
  // opaque timestamp + HMAC using the configured key as the signing secret. The
5
5
  // browser gets an HttpOnly cookie, JavaScript cannot read it, and rotating the
6
6
  // access key invalidates every old session immediately.
7
-
8
7
  import { createHmac, timingSafeEqual } from 'node:crypto';
9
-
10
8
  export const SESSION_COOKIE = 'onboarder_session';
11
9
  export const SESSION_TTL_SECONDS = 7 * 24 * 60 * 60;
12
-
13
10
  function signature(expires, accessKey) {
14
- return createHmac('sha256', accessKey)
15
- .update(`onboarder-session-v1:${expires}`)
16
- .digest('base64url');
11
+ return createHmac('sha256', accessKey)
12
+ .update(`onboarder-session-v1:${expires}`)
13
+ .digest('base64url');
17
14
  }
18
-
19
15
  export function createSession(settings, now = Date.now()) {
20
- const key = String(settings?.accessKey || '');
21
- if (!key) return '';
22
- const expires = Math.floor(now / 1000) + SESSION_TTL_SECONDS;
23
- return `${expires}.${signature(expires, key)}`;
16
+ const key = String(settings?.accessKey || '');
17
+ if (!key)
18
+ return '';
19
+ const expires = Math.floor(now / 1000) + SESSION_TTL_SECONDS;
20
+ return `${expires}.${signature(expires, key)}`;
24
21
  }
25
-
26
22
  function cookieValue(req, name) {
27
- const header = String(req.headers?.cookie || '');
28
- for (const part of header.split(';')) {
29
- const at = part.indexOf('=');
30
- if (at === -1) continue;
31
- if (part.slice(0, at).trim() !== name) continue;
32
- try { return decodeURIComponent(part.slice(at + 1).trim()); } catch { return ''; }
33
- }
34
- return '';
23
+ const header = String(req.headers?.cookie || '');
24
+ for (const part of header.split(';')) {
25
+ const at = part.indexOf('=');
26
+ if (at === -1)
27
+ continue;
28
+ if (part.slice(0, at).trim() !== name)
29
+ continue;
30
+ try {
31
+ return decodeURIComponent(part.slice(at + 1).trim());
32
+ }
33
+ catch {
34
+ return '';
35
+ }
36
+ }
37
+ return '';
35
38
  }
36
-
37
39
  export function hasValidSession(req, settings, now = Date.now()) {
38
- const key = String(settings?.accessKey || '');
39
- const token = cookieValue(req, SESSION_COOKIE);
40
- const dot = token.indexOf('.');
41
- if (!key || dot < 1) return false;
42
-
43
- const expires = Number(token.slice(0, dot));
44
- const supplied = token.slice(dot + 1);
45
- if (!Number.isSafeInteger(expires) || expires <= Math.floor(now / 1000) || !supplied) return false;
46
-
47
- const expected = signature(expires, key);
48
- const a = Buffer.from(supplied);
49
- const b = Buffer.from(expected);
50
- return a.length === b.length && timingSafeEqual(a, b);
40
+ const key = String(settings?.accessKey || '');
41
+ const token = cookieValue(req, SESSION_COOKIE);
42
+ const dot = token.indexOf('.');
43
+ if (!key || dot < 1)
44
+ return false;
45
+ const expires = Number(token.slice(0, dot));
46
+ const supplied = token.slice(dot + 1);
47
+ if (!Number.isSafeInteger(expires) || expires <= Math.floor(now / 1000) || !supplied)
48
+ return false;
49
+ const expected = signature(expires, key);
50
+ const a = Buffer.from(supplied);
51
+ const b = Buffer.from(expected);
52
+ return a.length === b.length && timingSafeEqual(a, b);
51
53
  }
52
-
53
54
  function requestIsSecure(req) {
54
- if (req.socket?.encrypted) return true;
55
- const forwarded = String(req.headers?.['x-forwarded-proto'] || '').split(',')[0].trim().toLowerCase();
56
- return forwarded === 'https';
55
+ if (req.socket?.encrypted)
56
+ return true;
57
+ const forwarded = String(req.headers?.['x-forwarded-proto'] || '').split(',')[0].trim().toLowerCase();
58
+ return forwarded === 'https';
57
59
  }
58
-
59
60
  export function sessionCookie(req, settings, { clear = false } = {}) {
60
- const parts = [
61
- `${SESSION_COOKIE}=${clear ? '' : encodeURIComponent(createSession(settings))}`,
62
- 'Path=/',
63
- 'HttpOnly',
64
- 'SameSite=Strict',
65
- `Max-Age=${clear ? 0 : SESSION_TTL_SECONDS}`,
66
- ];
67
- // A direct bare-IP deployment may still be plain HTTP, where Secure would make
68
- // the cookie unusable. Caddy forwards the original scheme, so domain HTTPS
69
- // gets the production-safe flag automatically.
70
- if (requestIsSecure(req)) parts.push('Secure');
71
- return parts.join('; ');
61
+ const parts = [
62
+ `${SESSION_COOKIE}=${clear ? '' : encodeURIComponent(createSession(settings))}`,
63
+ 'Path=/',
64
+ 'HttpOnly',
65
+ 'SameSite=Strict',
66
+ `Max-Age=${clear ? 0 : SESSION_TTL_SECONDS}`,
67
+ ];
68
+ // A direct bare-IP deployment may still be plain HTTP, where Secure would make
69
+ // the cookie unusable. Caddy forwards the original scheme, so domain HTTPS
70
+ // gets the production-safe flag automatically.
71
+ if (requestIsSecure(req))
72
+ parts.push('Secure');
73
+ return parts.join('; ');
72
74
  }
75
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/server/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// Browser sessions for the self-hosted access-key gate.\n//\n// The access key itself never becomes a cookie. A successful login mints an\n// opaque timestamp + HMAC using the configured key as the signing secret. The\n// browser gets an HttpOnly cookie, JavaScript cannot read it, and rotating the\n// access key invalidates every old session immediately.\n\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport const SESSION_COOKIE = 'onboarder_session';\nexport const SESSION_TTL_SECONDS = 7 * 24 * 60 * 60;\n\nfunction signature(expires, accessKey) {\n return createHmac('sha256', accessKey)\n .update(`onboarder-session-v1:${expires}`)\n .digest('base64url');\n}\n\nexport function createSession(settings, now = Date.now()) {\n const key = String(settings?.accessKey || '');\n if (!key) return '';\n const expires = Math.floor(now / 1000) + SESSION_TTL_SECONDS;\n return `${expires}.${signature(expires, key)}`;\n}\n\nfunction cookieValue(req, name) {\n const header = String(req.headers?.cookie || '');\n for (const part of header.split(';')) {\n const at = part.indexOf('=');\n if (at === -1) continue;\n if (part.slice(0, at).trim() !== name) continue;\n try { return decodeURIComponent(part.slice(at + 1).trim()); } catch { return ''; }\n }\n return '';\n}\n\nexport function hasValidSession(req, settings, now = Date.now()) {\n const key = String(settings?.accessKey || '');\n const token = cookieValue(req, SESSION_COOKIE);\n const dot = token.indexOf('.');\n if (!key || dot < 1) return false;\n\n const expires = Number(token.slice(0, dot));\n const supplied = token.slice(dot + 1);\n if (!Number.isSafeInteger(expires) || expires <= Math.floor(now / 1000) || !supplied) return false;\n\n const expected = signature(expires, key);\n const a = Buffer.from(supplied);\n const b = Buffer.from(expected);\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\nfunction requestIsSecure(req) {\n if (req.socket?.encrypted) return true;\n const forwarded = String(req.headers?.['x-forwarded-proto'] || '').split(',')[0].trim().toLowerCase();\n return forwarded === 'https';\n}\n\nexport function sessionCookie(req, settings, { clear = false }: Record<string, any> = {}) {\n const parts = [\n `${SESSION_COOKIE}=${clear ? '' : encodeURIComponent(createSession(settings))}`,\n 'Path=/',\n 'HttpOnly',\n 'SameSite=Strict',\n `Max-Age=${clear ? 0 : SESSION_TTL_SECONDS}`,\n ];\n // A direct bare-IP deployment may still be plain HTTP, where Secure would make\n // the cookie unusable. Caddy forwards the original scheme, so domain HTTPS\n // gets the production-safe flag automatically.\n if (requestIsSecure(req)) parts.push('Secure');\n return parts.join('; ');\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import type { Settings, SettingsInput, ServerUrls } from '../shared/contracts.js';
2
+ export declare const CONFIG_VERSION = 2;
3
+ export declare const LOOPBACK_HOSTS: Set<string>;
4
+ export declare const DEFAULT_SETTINGS: Readonly<Settings>;
5
+ export declare function configHome(env?: NodeJS.ProcessEnv): string;
6
+ export declare function configPath(env?: NodeJS.ProcessEnv): string;
7
+ export declare function isLoopbackHost(host: any): boolean;
8
+ export declare function normalizeSettings(value?: SettingsInput): Settings;
9
+ export declare function generateAccessKey(): string;
10
+ export declare function serverUrls(value?: SettingsInput): ServerUrls;
11
+ export declare function maskAccessKey(key: any): string;
12
+ export declare function browserUrl(settings: any): string;
13
+ export declare function publicSettings(value?: SettingsInput): {
14
+ version: number;
15
+ mode: "local" | "self-hosted";
16
+ host: string;
17
+ port: number;
18
+ domain: string;
19
+ https: boolean;
20
+ autoOpen: boolean;
21
+ hasAccessKey: boolean;
22
+ accessKeyMasked: string;
23
+ account: import("../shared/contracts.js").AccountSettings;
24
+ tunnel: {
25
+ cloudflare: boolean;
26
+ tailscale: boolean;
27
+ };
28
+ urls: ServerUrls;
29
+ security: {
30
+ remoteAuth: boolean;
31
+ loopback: boolean;
32
+ };
33
+ };
34
+ export declare function allowedHosts(value?: SettingsInput): unknown[];
35
+ export declare function accessKeysMatch(expected: any, actual: any): boolean;
36
+ export declare function bearerToken(req: any): string;
37
+ export declare function requestIsLoopback(req: any): boolean;
38
+ export declare function authReason(req: any, settings?: SettingsInput): "A valid access key is required." | "Self-hosted mode has no access key configured.";
39
+ export declare function configExists(file?: string): Promise<boolean>;
40
+ export declare function readSettings(file?: string): Promise<Settings>;
41
+ export declare function writeSettings(value: any, file?: string): Promise<Settings>;
42
+ export declare function updateSettings(patch?: SettingsInput, file?: string): Promise<Settings>;