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.
- package/README.md +196 -47
- package/bin/build-if-source.js +23 -0
- package/cli/commands.d.ts +26 -0
- package/cli/commands.js +836 -811
- package/cli/commands.js.map +1 -0
- package/cli/explorer/advanced.d.ts +14 -0
- package/cli/explorer/advanced.js +449 -0
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +396 -229
- package/cli/explorer/app.js.map +1 -0
- package/cli/explorer/assistant.d.ts +9 -0
- package/cli/explorer/assistant.js +58 -0
- package/cli/explorer/assistant.js.map +1 -0
- package/cli/explorer/commands.d.ts +17 -0
- package/cli/explorer/commands.js +400 -162
- package/cli/explorer/commands.js.map +1 -0
- package/cli/explorer/featureViews.d.ts +124 -0
- package/cli/explorer/featureViews.js +179 -0
- package/cli/explorer/featureViews.js.map +1 -0
- package/cli/explorer/github.d.ts +21 -0
- package/cli/explorer/github.js +73 -0
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +173 -0
- package/cli/explorer/graphs.js.map +1 -0
- package/cli/explorer/picker.d.ts +22 -0
- package/cli/explorer/picker.js +150 -0
- package/cli/explorer/picker.js.map +1 -0
- package/cli/explorer/report.d.ts +85 -0
- package/cli/explorer/report.js +60 -0
- package/cli/explorer/report.js.map +1 -0
- package/cli/explorer/session.d.ts +449 -0
- package/cli/explorer/session.js +184 -95
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +496 -412
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +33 -0
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +235 -138
- package/cli/main.js.map +1 -0
- package/cli/prompt.d.ts +5 -0
- package/cli/prompt.js +84 -80
- package/cli/prompt.js.map +1 -0
- package/cli/scanOptions.d.ts +2 -0
- package/cli/scanOptions.js +11 -0
- package/cli/scanOptions.js.map +1 -0
- package/cli/ui.d.ts +33 -0
- package/cli/ui.js +55 -35
- package/cli/ui.js.map +1 -0
- package/cli/wizard.d.ts +187 -0
- package/cli/wizard.js +304 -284
- package/cli/wizard.js.map +1 -0
- package/package.json +15 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1057 -1009
- package/public/app.js.map +1 -0
- package/public/browser.d.ts +68 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +30 -13
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +165 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +3 -0
- package/public/js/aiDraft.js +116 -124
- package/public/js/aiDraft.js.map +1 -0
- package/public/js/analysisPanel.d.ts +7 -0
- package/public/js/analysisPanel.js +166 -166
- package/public/js/analysisPanel.js.map +1 -0
- package/public/js/analysisReport.d.ts +38 -0
- package/public/js/analysisReport.js +105 -110
- package/public/js/analysisReport.js.map +1 -0
- package/public/js/api.d.ts +26 -0
- package/public/js/api.js +176 -167
- package/public/js/api.js.map +1 -0
- package/public/js/atlas.d.ts +6 -0
- package/public/js/atlas.js +50 -58
- package/public/js/atlas.js.map +1 -0
- package/public/js/blameView.d.ts +2 -0
- package/public/js/blameView.js +36 -37
- package/public/js/blameView.js.map +1 -0
- package/public/js/codeTab.d.ts +3 -0
- package/public/js/codeTab.js +175 -180
- package/public/js/codeTab.js.map +1 -0
- package/public/js/codeViewer.d.ts +5 -0
- package/public/js/codeViewer.js +162 -150
- package/public/js/codeViewer.js.map +1 -0
- package/public/js/components/FileChip.d.ts +9 -0
- package/public/js/components/FileChip.js +45 -49
- package/public/js/components/FileChip.js.map +1 -0
- package/public/js/components/MetricSparkline.d.ts +7 -0
- package/public/js/components/MetricSparkline.js +35 -44
- package/public/js/components/MetricSparkline.js.map +1 -0
- package/public/js/components/RiskBadge.d.ts +9 -0
- package/public/js/components/RiskBadge.js +55 -63
- package/public/js/components/RiskBadge.js.map +1 -0
- package/public/js/deepAnalysisView.d.ts +99 -0
- package/public/js/deepAnalysisView.js +282 -302
- package/public/js/deepAnalysisView.js.map +1 -0
- package/public/js/diagramPane.d.ts +8 -0
- package/public/js/diagramPane.js +133 -135
- package/public/js/diagramPane.js.map +1 -0
- package/public/js/diffView.d.ts +6 -0
- package/public/js/diffView.js +222 -209
- package/public/js/diffView.js.map +1 -0
- package/public/js/docsView.d.ts +2 -0
- package/public/js/docsView.js +441 -442
- package/public/js/docsView.js.map +1 -0
- package/public/js/dom.d.ts +189 -0
- package/public/js/dom.js +5 -0
- package/public/js/dom.js.map +1 -0
- package/public/js/fileSourceBrowser.d.ts +5 -0
- package/public/js/fileSourceBrowser.js +33 -41
- package/public/js/fileSourceBrowser.js.map +1 -0
- package/public/js/flameGraph.d.ts +5 -0
- package/public/js/flameGraph.js +62 -72
- package/public/js/flameGraph.js.map +1 -0
- package/public/js/forceGraph.d.ts +10 -0
- package/public/js/forceGraph.js +581 -556
- package/public/js/forceGraph.js.map +1 -0
- package/public/js/heatmap.d.ts +4 -0
- package/public/js/heatmap.js +142 -164
- package/public/js/heatmap.js.map +1 -0
- package/public/js/highlight.d.ts +2 -0
- package/public/js/highlight.js +116 -107
- package/public/js/highlight.js.map +1 -0
- package/public/js/html.d.ts +2 -0
- package/public/js/html.js +14 -44
- package/public/js/html.js.map +1 -0
- package/public/js/insightsView.d.ts +5 -0
- package/public/js/insightsView.js +93 -111
- package/public/js/insightsView.js.map +1 -0
- package/public/js/inspector.d.ts +37 -0
- package/public/js/inspector.js +416 -445
- package/public/js/inspector.js.map +1 -0
- package/public/js/llm.d.ts +113 -0
- package/public/js/llm.js +233 -272
- package/public/js/llm.js.map +1 -0
- package/public/js/mapView.d.ts +3 -0
- package/public/js/mapView.js +121 -120
- package/public/js/mapView.js.map +1 -0
- package/public/js/markdown.d.ts +3 -0
- package/public/js/markdown.js +84 -91
- package/public/js/markdown.js.map +1 -0
- package/public/js/mindmap.d.ts +32 -0
- package/public/js/mindmap.js +244 -259
- package/public/js/mindmap.js.map +1 -0
- package/public/js/mobilePanels.d.ts +5 -0
- package/public/js/mobilePanels.js +50 -0
- package/public/js/mobilePanels.js.map +1 -0
- package/public/js/repoFiles.d.ts +12 -0
- package/public/js/repoFiles.js +28 -27
- package/public/js/repoFiles.js.map +1 -0
- package/public/js/reviewView.d.ts +28 -0
- package/public/js/reviewView.js +256 -0
- package/public/js/reviewView.js.map +1 -0
- package/public/js/sbomView.d.ts +3 -0
- package/public/js/sbomView.js +59 -67
- package/public/js/sbomView.js.map +1 -0
- package/public/js/scanCache.d.ts +16 -0
- package/public/js/scanCache.js +67 -65
- package/public/js/scanCache.js.map +1 -0
- package/public/js/search.d.ts +7 -0
- package/public/js/search.js +868 -800
- package/public/js/search.js.map +1 -0
- package/public/js/searchLocal.d.ts +42 -0
- package/public/js/searchLocal.js +87 -0
- package/public/js/searchLocal.js.map +1 -0
- package/public/js/serverSettings.d.ts +5 -0
- package/public/js/serverSettings.js +153 -164
- package/public/js/serverSettings.js.map +1 -0
- package/public/js/state.d.ts +798 -0
- package/public/js/state.js +114 -122
- package/public/js/state.js.map +1 -0
- package/public/js/tour.d.ts +6 -0
- package/public/js/tour.js +5 -7
- package/public/js/tour.js.map +1 -0
- package/public/js/transitions.d.ts +2 -0
- package/public/js/transitions.js +9 -10
- package/public/js/transitions.js.map +1 -0
- package/public/js/tree.d.ts +12 -0
- package/public/js/tree.js +157 -154
- package/public/js/tree.js.map +1 -0
- package/public/js/workflowsView.d.ts +3 -0
- package/public/js/workflowsView.js +15 -22
- package/public/js/workflowsView.js.map +1 -0
- package/public/search.css +495 -0
- package/public/styles.css +163 -346
- package/public/workbench.css +166 -0
- package/server/apiAuth.d.ts +3 -0
- package/server/apiAuth.js +18 -20
- package/server/apiAuth.js.map +1 -0
- package/server/apiDiff.d.ts +2 -0
- package/server/apiDiff.js +20 -18
- package/server/apiDiff.js.map +1 -0
- package/server/apiDocs.d.ts +2 -0
- package/server/apiDocs.js +38 -40
- package/server/apiDocs.js.map +1 -0
- package/server/apiFile.d.ts +1 -0
- package/server/apiFile.js +14 -17
- package/server/apiFile.js.map +1 -0
- package/server/apiGitBlame.d.ts +2 -0
- package/server/apiGitBlame.js +50 -51
- package/server/apiGitBlame.js.map +1 -0
- package/server/apiMcp.d.ts +4 -0
- package/server/apiMcp.js +64 -66
- package/server/apiMcp.js.map +1 -0
- package/server/apiReview.d.ts +2 -0
- package/server/apiReview.js +45 -0
- package/server/apiReview.js.map +1 -0
- package/server/apiScan.d.ts +5 -0
- package/server/apiScan.js +90 -71
- package/server/apiScan.js.map +1 -0
- package/server/apiSearch.d.ts +33 -0
- package/server/apiSearch.js +184 -200
- package/server/apiSearch.js.map +1 -0
- package/server/apiSettings.d.ts +4 -0
- package/server/apiSettings.js +84 -85
- package/server/apiSettings.js.map +1 -0
- package/server/apiTools.d.ts +3 -0
- package/server/apiTools.js +55 -56
- package/server/apiTools.js.map +1 -0
- package/server/auth.d.ts +5 -0
- package/server/auth.js +54 -51
- package/server/auth.js.map +1 -0
- package/server/config.d.ts +42 -0
- package/server/config.js +189 -194
- package/server/config.js.map +1 -0
- package/server/daemon.d.ts +12 -0
- package/server/daemon.js +124 -114
- package/server/daemon.js.map +1 -0
- package/server/fileSourceNode.d.ts +2 -0
- package/server/fileSourceNode.js +34 -36
- package/server/fileSourceNode.js.map +1 -0
- package/server/gitClone.d.ts +9 -0
- package/server/gitClone.js +59 -61
- package/server/gitClone.js.map +1 -0
- package/server/gitDiff.d.ts +36 -0
- package/server/gitDiff.js +271 -169
- package/server/gitDiff.js.map +1 -0
- package/server/gitHistory.d.ts +12 -0
- package/server/gitHistory.js +80 -80
- package/server/gitHistory.js.map +1 -0
- package/server/gitRemote.d.ts +12 -0
- package/server/gitRemote.js +60 -0
- package/server/gitRemote.js.map +1 -0
- package/server/htmlText.d.ts +4 -0
- package/server/htmlText.js +16 -23
- package/server/htmlText.js.map +1 -0
- package/server/http.d.ts +5 -0
- package/server/http.js +27 -30
- package/server/http.js.map +1 -0
- package/server/httpGuards.d.ts +2 -0
- package/server/httpGuards.js +47 -43
- package/server/httpGuards.js.map +1 -0
- package/server/https.d.ts +34 -0
- package/server/https.js +121 -111
- package/server/https.js.map +1 -0
- package/server/index.d.ts +40 -0
- package/server/index.js +139 -147
- package/server/index.js.map +1 -0
- package/server/layout.d.ts +26 -0
- package/server/layout.js +59 -37
- package/server/layout.js.map +1 -0
- package/server/llmProxy.d.ts +3 -0
- package/server/llmProxy.js +137 -135
- package/server/llmProxy.js.map +1 -0
- package/server/logger.d.ts +38 -0
- package/server/logger.js +198 -211
- package/server/logger.js.map +1 -0
- package/server/mcp/analysis.d.ts +29 -0
- package/server/mcp/analysis.js +99 -120
- package/server/mcp/analysis.js.map +1 -0
- package/server/mcp/http.d.ts +9 -0
- package/server/mcp/http.js +128 -137
- package/server/mcp/http.js.map +1 -0
- package/server/mcp/runner.d.ts +68 -0
- package/server/mcp/runner.js +208 -218
- package/server/mcp/runner.js.map +1 -0
- package/server/mcp/server.d.ts +29 -0
- package/server/mcp/server.js +166 -179
- package/server/mcp/server.js.map +1 -0
- package/server/mcp/standalone.d.ts +1 -0
- package/server/mcp/standalone.js +8 -10
- package/server/mcp/standalone.js.map +1 -0
- package/server/mcp/tools.d.ts +2020 -0
- package/server/mcp/tools.js +523 -574
- package/server/mcp/tools.js.map +1 -0
- package/server/paths.d.ts +3 -0
- package/server/paths.js +17 -15
- package/server/paths.js.map +1 -0
- package/server/pidfile.d.ts +9 -0
- package/server/pidfile.js +43 -45
- package/server/pidfile.js.map +1 -0
- package/server/review.d.ts +5 -0
- package/server/review.js +35 -0
- package/server/review.js.map +1 -0
- package/server/router.d.ts +33 -0
- package/server/router.js +191 -197
- package/server/router.js.map +1 -0
- package/server/scanRequest.d.ts +3 -0
- package/server/scanRequest.js +36 -0
- package/server/scanRequest.js.map +1 -0
- package/server/searchIndex.d.ts +17 -0
- package/server/searchIndex.js +83 -80
- package/server/searchIndex.js.map +1 -0
- package/server/sessions.d.ts +14 -0
- package/server/sessions.js +62 -70
- package/server/sessions.js.map +1 -0
- package/server/startup.d.ts +118 -0
- package/server/startup.js +143 -148
- package/server/startup.js.map +1 -0
- package/server/static.d.ts +6 -0
- package/server/static.js +28 -31
- package/server/static.js.map +1 -0
- package/server/tools/install.d.ts +4 -0
- package/server/tools/install.js +176 -185
- package/server/tools/install.js.map +1 -0
- package/server/tools/parse.d.ts +8 -0
- package/server/tools/parse.js +112 -114
- package/server/tools/parse.js.map +1 -0
- package/server/tools/platform.d.ts +9 -0
- package/server/tools/platform.js +49 -47
- package/server/tools/platform.js.map +1 -0
- package/server/tools/registry.d.ts +137 -0
- package/server/tools/registry.js +173 -172
- package/server/tools/registry.js.map +1 -0
- package/server/tools/scan.d.ts +34 -0
- package/server/tools/scan.js +89 -98
- package/server/tools/scan.js.map +1 -0
- package/server/tools.d.ts +34 -0
- package/server/tools.js +94 -107
- package/server/tools.js.map +1 -0
- package/server/tunnel.d.ts +29 -0
- package/server/tunnel.js +61 -66
- package/server/tunnel.js.map +1 -0
- package/shared/analyzer/docs.d.ts +16 -0
- package/shared/analyzer/docs.js +99 -93
- package/shared/analyzer/docs.js.map +1 -0
- package/shared/analyzer/explainLocal.d.ts +4 -0
- package/shared/analyzer/explainLocal.js +122 -142
- package/shared/analyzer/explainLocal.js.map +1 -0
- package/shared/analyzer/github.d.ts +37 -0
- package/shared/analyzer/github.js +79 -0
- package/shared/analyzer/github.js.map +1 -0
- package/shared/analyzer/graph.d.ts +41 -0
- package/shared/analyzer/graph.js +393 -400
- package/shared/analyzer/graph.js.map +1 -0
- package/shared/analyzer/health.d.ts +33 -0
- package/shared/analyzer/health.js +197 -193
- package/shared/analyzer/health.js.map +1 -0
- package/shared/analyzer/history.d.ts +29 -0
- package/shared/analyzer/history.js +110 -111
- package/shared/analyzer/history.js.map +1 -0
- package/shared/analyzer/jsonConfig.d.ts +2 -0
- package/shared/analyzer/jsonConfig.js +61 -0
- package/shared/analyzer/jsonConfig.js.map +1 -0
- package/shared/analyzer/languages/csharp.d.ts +26 -0
- package/shared/analyzer/languages/csharp.js +83 -88
- package/shared/analyzer/languages/csharp.js.map +1 -0
- package/shared/analyzer/languages/generic.d.ts +23 -0
- package/shared/analyzer/languages/generic.js +94 -105
- package/shared/analyzer/languages/generic.js.map +1 -0
- package/shared/analyzer/languages/go.d.ts +24 -0
- package/shared/analyzer/languages/go.js +45 -54
- package/shared/analyzer/languages/go.js.map +1 -0
- package/shared/analyzer/languages/index.d.ts +6 -0
- package/shared/analyzer/languages/index.js +19 -20
- package/shared/analyzer/languages/index.js.map +1 -0
- package/shared/analyzer/languages/java.d.ts +20 -0
- package/shared/analyzer/languages/java.js +73 -78
- package/shared/analyzer/languages/java.js.map +1 -0
- package/shared/analyzer/languages/javascript.d.ts +12 -0
- package/shared/analyzer/languages/javascript.js +258 -209
- package/shared/analyzer/languages/javascript.js.map +1 -0
- package/shared/analyzer/languages/python.d.ts +23 -0
- package/shared/analyzer/languages/python.js +92 -100
- package/shared/analyzer/languages/python.js.map +1 -0
- package/shared/analyzer/languages/rust.d.ts +17 -0
- package/shared/analyzer/languages/rust.js +96 -98
- package/shared/analyzer/languages/rust.js.map +1 -0
- package/shared/analyzer/languages/typescript.d.ts +11 -0
- package/shared/analyzer/languages/typescript.js +117 -116
- package/shared/analyzer/languages/typescript.js.map +1 -0
- package/shared/analyzer/licenses.d.ts +18 -0
- package/shared/analyzer/licenses.js +130 -133
- package/shared/analyzer/licenses.js.map +1 -0
- package/shared/analyzer/metrics.d.ts +20 -0
- package/shared/analyzer/metrics.js +112 -104
- package/shared/analyzer/metrics.js.map +1 -0
- package/shared/analyzer/pathUtil.d.ts +10 -0
- package/shared/analyzer/pathUtil.js +29 -35
- package/shared/analyzer/pathUtil.js.map +1 -0
- package/shared/analyzer/patterns.d.ts +11 -0
- package/shared/analyzer/patterns.js +231 -253
- package/shared/analyzer/patterns.js.map +1 -0
- package/shared/analyzer/scan.d.ts +62 -0
- package/shared/analyzer/scan.js +451 -442
- package/shared/analyzer/scan.js.map +1 -0
- package/shared/analyzer/scanControl.d.ts +11 -0
- package/shared/analyzer/scanControl.js +62 -0
- package/shared/analyzer/scanControl.js.map +1 -0
- package/shared/analyzer/security.d.ts +18 -0
- package/shared/analyzer/security.js +159 -159
- package/shared/analyzer/security.js.map +1 -0
- package/shared/analyzer/services.d.ts +5 -0
- package/shared/analyzer/services.js +141 -138
- package/shared/analyzer/services.js.map +1 -0
- package/shared/analyzer/stack.d.ts +6 -0
- package/shared/analyzer/stack.js +134 -141
- package/shared/analyzer/stack.js.map +1 -0
- package/shared/analyzer/tour.d.ts +2 -0
- package/shared/analyzer/tour.js +45 -53
- package/shared/analyzer/tour.js.map +1 -0
- package/shared/analyzer/util.d.ts +8 -0
- package/shared/analyzer/util.js +130 -76
- package/shared/analyzer/util.js.map +1 -0
- package/shared/analyzer/workflows.d.ts +8 -0
- package/shared/analyzer/workflows.js +139 -134
- package/shared/analyzer/workflows.js.map +1 -0
- package/shared/contracts.d.ts +250 -0
- package/shared/contracts.js +2 -0
- package/shared/contracts.js.map +1 -0
- package/shared/diagram/aiFacts.d.ts +35 -0
- package/shared/diagram/aiFacts.js +102 -105
- package/shared/diagram/aiFacts.js.map +1 -0
- package/shared/diagram/aiMermaid.d.ts +5 -0
- package/shared/diagram/aiMermaid.js +46 -45
- package/shared/diagram/aiMermaid.js.map +1 -0
- package/shared/diagram/atlas.d.ts +16 -0
- package/shared/diagram/atlas.js +51 -60
- package/shared/diagram/atlas.js.map +1 -0
- package/shared/diagram/mermaid.d.ts +43 -0
- package/shared/diagram/mermaid.js +348 -370
- package/shared/diagram/mermaid.js.map +1 -0
- package/shared/review/contracts.d.ts +95 -0
- package/shared/review/contracts.js +2 -0
- package/shared/review/contracts.js.map +1 -0
- package/shared/review/review.d.ts +10 -0
- package/shared/review/review.js +184 -0
- package/shared/review/review.js.map +1 -0
- package/shared/search/query.d.ts +32 -0
- package/shared/search/query.js +395 -388
- package/shared/search/query.js.map +1 -0
package/server/router.js
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
// `Sec-Fetch-Site` to stop another page and DNS rebinding from driving it.
|
|
10
10
|
// Self-hosted mode adds real access-key authentication: local browser requests
|
|
11
11
|
// stay open, remote browsers get a signed session, and API clients use Bearer.
|
|
12
|
-
|
|
13
12
|
import { handleDocs } from './apiDocs.js';
|
|
14
13
|
import { handleFile } from './apiFile.js';
|
|
15
14
|
import { handleCleanup, handleScan } from './apiScan.js';
|
|
@@ -20,145 +19,145 @@ import { serveStatic } from './static.js';
|
|
|
20
19
|
import { handleSearch } from './apiSearch.js';
|
|
21
20
|
import { handleBlame } from './apiGitBlame.js';
|
|
22
21
|
import { handleDiff, handleDiffRefs } from './apiDiff.js';
|
|
22
|
+
import { handleReview } from './apiReview.js';
|
|
23
23
|
import { handleToolsInstall, handleToolsRun, handleToolsStatus } from './apiTools.js';
|
|
24
24
|
import { handleMcpStart, handleMcpStatus, handleMcpStop, handleMcpCommand } from './apiMcp.js';
|
|
25
25
|
import { handleGetSettings, handleRotateAccessKey, handleUpdateSettings } from './apiSettings.js';
|
|
26
26
|
import { handleAuthStatus, handleLogin, handleLogout } from './apiAuth.js';
|
|
27
27
|
import { accessKeysMatch, allowedHosts, authReason, bearerToken, DEFAULT_SETTINGS } from './config.js';
|
|
28
28
|
import { hasValidSession } from './auth.js';
|
|
29
|
-
|
|
30
29
|
const ROUTES = [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
30
|
+
{ method: 'POST', path: '/api/review', body: true, run: ({ res, body }) => handleReview(res, body) },
|
|
31
|
+
{
|
|
32
|
+
method: 'GET', path: '/api/diff/refs', sameOrigin: true,
|
|
33
|
+
run: ({ res, url }) => handleDiffRefs(res, url.searchParams.get('scan')),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
method: 'GET', path: '/api/diff', sameOrigin: true,
|
|
37
|
+
run: ({ res, url }) => handleDiff(res, url.searchParams.get('scan'), url.searchParams),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
method: 'POST', path: '/api/search', body: true,
|
|
41
|
+
run: ({ res, body }) => handleSearch(res, body),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
method: 'GET', path: '/api/blame', sameOrigin: true,
|
|
45
|
+
run: ({ res, url }) => handleBlame(res, url.searchParams.get('scan'), url.searchParams.get('path')),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
method: 'POST', path: '/api/scan', body: true,
|
|
49
|
+
run: ({ res, body, config }) => handleScan(res, body, config),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
method: 'DELETE', prefix: '/api/scan/',
|
|
53
|
+
run: ({ res, rest }) => handleCleanup(res, decodeURIComponent(rest)),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
method: 'GET', path: '/api/file', sameOrigin: true,
|
|
57
|
+
run: ({ res, url }) => handleFile(res, url.searchParams.get('scan'), url.searchParams.get('path')),
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
// The proxy is the handler; there is no wrapper to write. It forwards the
|
|
61
|
+
// browser's key to the endpoint the browser chose and never keeps either.
|
|
62
|
+
method: 'POST', path: '/api/explain', body: true,
|
|
63
|
+
run: ({ res, body }) => proxyChat(res, body),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
method: 'POST', path: '/api/doc', body: true,
|
|
67
|
+
run: ({ res, body }) => handleDocs(res, body),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
// Reports which analyzers are installed — environment information a foreign
|
|
71
|
+
// page has no business reading, so it is guarded like /api/file.
|
|
72
|
+
method: 'GET', path: '/api/tools', sameOrigin: true,
|
|
73
|
+
run: ({ res }) => handleToolsStatus(res),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
// Runs real analyzers over a scanned root. `body: true` applies the size
|
|
77
|
+
// limit; the session check in the handler is the capability gate.
|
|
78
|
+
method: 'POST', path: '/api/tools/run', body: true,
|
|
79
|
+
run: ({ res, body }) => handleToolsRun(res, body),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
// Installs a missing analyzer from the GUI. Same-origin like every POST;
|
|
83
|
+
// the tool name is validated against the registry and the install plans
|
|
84
|
+
// are fixed data, so the request body never reaches a command line.
|
|
85
|
+
method: 'POST', path: '/api/tools/install', body: true,
|
|
86
|
+
run: ({ res, body }) => handleToolsInstall(res, body),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
// The MCP button's state, and the command an agent harness is configured with.
|
|
90
|
+
// The config is not secret, but it is a path into this machine and a foreign
|
|
91
|
+
// page has no business reading either one.
|
|
92
|
+
method: 'GET', path: '/api/mcp', sameOrigin: true,
|
|
93
|
+
run: ({ req, res, config }) => handleMcpStatus(req, res, config),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
method: 'GET', path: '/api/mcp/command', sameOrigin: true,
|
|
97
|
+
run: ({ req, res, config }) => handleMcpCommand(req, res, config),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
// Starting and stopping a process is a side effect on the person's machine, so
|
|
101
|
+
// these take a body like every other POST and are checked like every other POST.
|
|
102
|
+
method: 'POST', path: '/api/mcp/start', body: true,
|
|
103
|
+
run: ({ req, res, config }) => handleMcpStart(req, res, config),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
method: 'POST', path: '/api/mcp/stop', body: true,
|
|
107
|
+
run: ({ req, res, config }) => handleMcpStop(req, res, config),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
method: 'GET', path: '/api/health',
|
|
111
|
+
run: ({ res }) => sendJSON(res, 200, { ok: true }),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
method: 'GET', path: '/api/auth/status', sameOrigin: true,
|
|
115
|
+
run: ({ req, res, settings }) => handleAuthStatus(req, res, settings),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
method: 'POST', path: '/api/auth/login', body: true, sameOrigin: true,
|
|
119
|
+
run: ({ req, res, body, settings }) => handleLogin(req, res, body, settings),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
method: 'POST', path: '/api/auth/logout', body: true, sameOrigin: true,
|
|
123
|
+
run: ({ req, res }) => handleLogout(req, res),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
// The settings drawer and the CLI read the same public shape: everything
|
|
127
|
+
// about the configuration except the access key itself.
|
|
128
|
+
method: 'GET', path: '/api/settings', sameOrigin: true,
|
|
129
|
+
run: ({ req, res, config }) => handleGetSettings(req, res, config),
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
// PATCH semantics over a small allow-listed key set; the validation that
|
|
133
|
+
// rejects a bad port or a lawless mode lives in `server/config.js` and is
|
|
134
|
+
// shared with the CLI wizard, so both surfaces enforce one schema.
|
|
135
|
+
method: 'PUT', path: '/api/settings', body: true,
|
|
136
|
+
run: ({ res, body, config }) => handleUpdateSettings(res, body, config),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
// The only way the key changes: generated on the server, shown once in
|
|
140
|
+
// this response, never readable again. In self-hosted mode this endpoint
|
|
141
|
+
// is itself behind the current key, so rotation requires possession.
|
|
142
|
+
method: 'POST', path: '/api/settings/access-key', body: true,
|
|
143
|
+
run: ({ req, res, config }) => handleRotateAccessKey(req, res, config),
|
|
144
|
+
},
|
|
145
145
|
];
|
|
146
|
-
|
|
147
146
|
// The table itself, for the test that walks it. Exported read-only: the routes
|
|
148
147
|
// are decided here, not assembled by whoever imports this.
|
|
149
148
|
export const routes = Object.freeze(ROUTES.map((r) => Object.freeze({ ...r })));
|
|
150
|
-
|
|
151
149
|
export function matchRoute(method, pathname) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
for (const route of ROUTES) {
|
|
151
|
+
if (route.method !== method)
|
|
152
|
+
continue;
|
|
153
|
+
if (route.path === pathname)
|
|
154
|
+
return { route, rest: '' };
|
|
155
|
+
if (route.prefix && pathname.startsWith(route.prefix)) {
|
|
156
|
+
return { route, rest: pathname.slice(route.prefix.length) };
|
|
157
|
+
}
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
-
return null;
|
|
159
|
+
return null;
|
|
160
160
|
}
|
|
161
|
-
|
|
162
161
|
// `config` carries the directories the handlers need — the project root to scan
|
|
163
162
|
// for the demo, and the two roots static serving maps into. Passed in rather
|
|
164
163
|
// than resolved here so a test can point the server somewhere else, and so this
|
|
@@ -169,79 +168,74 @@ export function matchRoute(method, pathname) {
|
|
|
169
168
|
// request without a restart. A bare `createServer()` (the tests) has no getter
|
|
170
169
|
// and sees the local-mode defaults — never somebody's home directory.
|
|
171
170
|
async function liveSettings(config) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
if (typeof config?.getSettings !== 'function')
|
|
172
|
+
return DEFAULT_SETTINGS;
|
|
173
|
+
try {
|
|
174
|
+
return await config.getSettings();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null; // unreadable config: fail closed, every /api call gets a 500
|
|
178
|
+
}
|
|
178
179
|
}
|
|
179
|
-
|
|
180
180
|
export function createRouter(config) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
181
|
+
return async function handleRequest(req, res) {
|
|
182
|
+
try {
|
|
183
|
+
const url = new URL(req.url, 'http://127.0.0.1');
|
|
184
|
+
const settings = await liveSettings(config);
|
|
185
|
+
if (!settings) {
|
|
186
|
+
return sendError(res, 500, 'The settings file could not be read — run `onboarder doctor` to find out why.');
|
|
187
|
+
}
|
|
188
|
+
const wrongHost = rebindingReason(req, allowedHosts(settings));
|
|
189
|
+
if (wrongHost) {
|
|
190
|
+
const scope = settings.mode === 'self-hosted'
|
|
191
|
+
? 'Onboarder only answers to its configured host and domain. '
|
|
192
|
+
: 'Onboarder only answers to localhost. ';
|
|
193
|
+
return sendError(res, 403, scope + wrongHost);
|
|
194
|
+
}
|
|
195
|
+
const found = matchRoute(req.method, url.pathname);
|
|
196
|
+
const publicAuthRoute = ['/api/health', '/api/auth/status', '/api/auth/login', '/api/auth/logout'].includes(url.pathname);
|
|
197
|
+
if (req.method !== 'GET' || found?.route.sameOrigin) {
|
|
198
|
+
const foreign = crossOriginReason(req);
|
|
199
|
+
if (foreign) {
|
|
200
|
+
return sendError(res, 403, 'That request did not come from Onboarder’s own page. ' + foreign);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Bearer clients keep their existing API contract. A browser gets a signed,
|
|
204
|
+
// HttpOnly session from the login form instead of storing the raw key.
|
|
205
|
+
const bearerClient = accessKeysMatch(settings.accessKey, bearerToken(req));
|
|
206
|
+
const browserAuthenticated = hasValidSession(req, settings);
|
|
207
|
+
const authDenied = authReason(req, settings);
|
|
208
|
+
const locallyExempt = !authDenied;
|
|
209
|
+
const authenticated = bearerClient || browserAuthenticated || locallyExempt;
|
|
210
|
+
const remoteSelfHosted = settings.mode === 'self-hosted' && Boolean(authDenied);
|
|
211
|
+
if (remoteSelfHosted && !authenticated && !publicAuthRoute) {
|
|
212
|
+
// API callers keep a machine-readable 401. A browser navigation gets the
|
|
213
|
+
// themed sign-in document so the user never has to paste JSON into a tab.
|
|
214
|
+
const accepts = String(req.headers?.accept || '');
|
|
215
|
+
if (req.method === 'GET' && (url.pathname === '/' || accepts.includes('text/html'))) {
|
|
216
|
+
res.statusCode = 200;
|
|
217
|
+
return await serveStatic(res, '/login.html', config);
|
|
218
|
+
}
|
|
219
|
+
return sendError(res, 401, authDenied || 'Sign in with the Onboarder access key first.');
|
|
220
|
+
}
|
|
221
|
+
if (url.pathname === '/api/auth/logout') {
|
|
222
|
+
// Always clear the browser cookie, even if it had already expired.
|
|
223
|
+
return handleLogout(req, res);
|
|
224
|
+
}
|
|
225
|
+
if (found) {
|
|
226
|
+
const body = found.route.body ? await readBody(req) : null;
|
|
227
|
+
return await found.route.run({ req, res, url, body, rest: found.rest, config, settings });
|
|
228
|
+
}
|
|
229
|
+
if (req.method === 'GET')
|
|
230
|
+
return await serveStatic(res, url.pathname, config);
|
|
231
|
+
sendError(res, 404, 'Nothing lives at ' + url.pathname + '.');
|
|
204
232
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const browserAuthenticated = hasValidSession(req, settings);
|
|
211
|
-
const authDenied = authReason(req, settings);
|
|
212
|
-
const locallyExempt = !authDenied;
|
|
213
|
-
const authenticated = bearerClient || browserAuthenticated || locallyExempt;
|
|
214
|
-
const remoteSelfHosted = settings.mode === 'self-hosted' && Boolean(authDenied);
|
|
215
|
-
|
|
216
|
-
if (remoteSelfHosted && !authenticated && !publicAuthRoute) {
|
|
217
|
-
// API callers keep a machine-readable 401. A browser navigation gets the
|
|
218
|
-
// themed sign-in document so the user never has to paste JSON into a tab.
|
|
219
|
-
const accepts = String(req.headers?.accept || '');
|
|
220
|
-
if (req.method === 'GET' && (url.pathname === '/' || accepts.includes('text/html'))) {
|
|
221
|
-
res.statusCode = 200;
|
|
222
|
-
return await serveStatic(res, '/login.html', config);
|
|
233
|
+
catch (err) {
|
|
234
|
+
// The last resort. Handlers turn their own expected failures into a status
|
|
235
|
+
// that says something useful; anything reaching here is a bug or a broken
|
|
236
|
+
// request body, and the message is more use to the person than "500".
|
|
237
|
+
sendError(res, 500, err.message || 'Something went sideways.');
|
|
223
238
|
}
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (url.pathname === '/api/auth/logout') {
|
|
228
|
-
// Always clear the browser cookie, even if it had already expired.
|
|
229
|
-
return handleLogout(req, res);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (found) {
|
|
233
|
-
const body = found.route.body ? await readBody(req) : null;
|
|
234
|
-
return await found.route.run({ req, res, url, body, rest: found.rest, config, settings });
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (req.method === 'GET') return await serveStatic(res, url.pathname, config);
|
|
238
|
-
|
|
239
|
-
sendError(res, 404, 'Nothing lives at ' + url.pathname + '.');
|
|
240
|
-
} catch (err) {
|
|
241
|
-
// The last resort. Handlers turn their own expected failures into a status
|
|
242
|
-
// that says something useful; anything reaching here is a bug or a broken
|
|
243
|
-
// request body, and the message is more use to the person than "500".
|
|
244
|
-
sendError(res, 500, err.message || 'Something went sideways.');
|
|
245
|
-
}
|
|
246
|
-
};
|
|
239
|
+
};
|
|
247
240
|
}
|
|
241
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/server/router.ts"],"names":[],"mappings":"AAKA,gDAAgD;AAChD,EAAE;AACF,gFAAgF;AAChF,kFAAkF;AAClF,8EAA8E;AAC9E,+DAA+D;AAC/D,EAAE;AACF,uEAAuE;AACvE,2EAA2E;AAC3E,+EAA+E;AAC/E,+EAA+E;AAE/E,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,MAAM,GAAY;IACtB,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE;IACpG;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,IAAI;QACvD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;KACzE;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI;QAClD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC;KACvF;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI;QAC/C,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC;KAChD;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI;QACnD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;KACpG;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI;QAC7C,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;KAC9D;IACD;QACE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY;QACtC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;KACrE;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI;QAClD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;KACnG;IACD;QACE,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI;QAChD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC;KAC7C;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI;QAC5C,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;KAC9C;IACD;QACE,4EAA4E;QAC5E,iEAAiE;QACjE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI;QACnD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,CAAC;KACzC;IACD;QACE,yEAAyE;QACzE,kEAAkE;QAClE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI;QAClD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;KAClD;IACD;QACE,yEAAyE;QACzE,wEAAwE;QACxE,oEAAoE;QACpE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,IAAI;QACtD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC;KACtD;IACD;QACE,+EAA+E;QAC/E,6EAA6E;QAC7E,2CAA2C;QAC3C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI;QACjD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KACjE;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,IAAI;QACzD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KAClE;IACD;QACE,+EAA+E;QAC/E,iFAAiF;QACjF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,IAAI;QAClD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KAChE;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI;QACjD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KAC/D;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa;QAClC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;KACnD;IACD;QACE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,IAAI;QACzD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC;KACtE;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI;QACrE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC;KAC7E;IACD;QACE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI;QACtE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC;KAC9C;IACD;QACE,yEAAyE;QACzE,wDAAwD;QACxD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,IAAI;QACtD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KACnE;IACD;QACE,yEAAyE;QACzE,0EAA0E;QAC1E,mEAAmE;QACnE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI;QAChD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;KACxE;IACD;QACE,uEAAuE;QACvE,yEAAyE;QACzE,qEAAqE;QACrE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,IAAI,EAAE,IAAI;QAC5D,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC;KACvE;CACF,CAAC;AAEF,+EAA+E;AAC/E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEhF,MAAM,UAAU,UAAU,CAAC,MAAM,EAAE,QAAQ;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,SAAS;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACxD,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACtD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,6EAA6E;AAC7E,gFAAgF;AAChF,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,sEAAsE;AACtE,KAAK,UAAU,YAAY,CAAC,MAAM;IAChC,IAAI,OAAO,MAAM,EAAE,WAAW,KAAK,UAAU;QAAE,OAAO,gBAAgB,CAAC;IACvE,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,6DAA6D;IAC5E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAM;IACjC,OAAO,KAAK,UAAU,aAAa,CAAC,GAAG,EAAE,GAAG;QAC1C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;YAEjD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,+EAA+E,CAAC,CAAC;YAC9G,CAAC;YAED,MAAM,SAAS,GAAG,eAAe,CAAC,GAAG,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC/D,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,KAAK,aAAa;oBAC3C,CAAC,CAAC,4DAA4D;oBAC9D,CAAC,CAAC,uCAAuC,CAAC;gBAC5C,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;YAChD,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;YACnD,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC1H,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;gBACvC,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,uDAAuD,GAAG,OAAO,CAAC,CAAC;gBAChG,CAAC;YACH,CAAC;YAED,4EAA4E;YAC5E,uEAAuE;YACvE,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3E,MAAM,oBAAoB,GAAG,eAAe,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC5D,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC7C,MAAM,aAAa,GAAG,CAAC,UAAU,CAAC;YAClC,MAAM,aAAa,GAAG,YAAY,IAAI,oBAAoB,IAAI,aAAa,CAAC;YAC5E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;YAEhF,IAAI,gBAAgB,IAAI,CAAC,aAAa,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC3D,yEAAyE;gBACzE,0EAA0E;gBAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;gBAClD,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;oBACpF,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;oBACrB,OAAO,MAAM,WAAW,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;gBACvD,CAAC;gBACD,OAAO,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,IAAI,8CAA8C,CAAC,CAAC;YAC3F,CAAC;YAED,IAAI,GAAG,CAAC,QAAQ,KAAK,kBAAkB,EAAE,CAAC;gBACxC,mEAAmE;gBACnE,OAAO,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAChC,CAAC;YAED,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC3D,OAAO,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC5F,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK;gBAAE,OAAO,MAAM,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAE9E,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,mBAAmB,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,2EAA2E;YAC3E,0EAA0E;YAC1E,sEAAsE;YACtE,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,IAAI,0BAA0B,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { Settings } from '../shared/contracts.js';\n\ninterface RouteContext { req: IncomingMessage; res: ServerResponse; url: URL; body: any; rest: string; config: any; settings: Settings }\ninterface Route { method: string; path?: string; prefix?: string; body?: boolean; sameOrigin?: boolean; run(context: RouteContext): unknown }\n// The route table and the gates in front of it.\n//\n// Everything arrives here: one function decides whether a request is allowed to\n// be answered at all, then which handler answers it. The routes are a list rather\n// than a ladder of `if` statements so that the whole surface of the server is\n// visible in one screen, and everything else is a static file.\n//\n// There are three concerns. Local mode relies on `Host`, `Origin`, and\n// `Sec-Fetch-Site` to stop another page and DNS rebinding from driving it.\n// Self-hosted mode adds real access-key authentication: local browser requests\n// stay open, remote browsers get a signed session, and API clients use Bearer.\n\nimport { handleDocs } from './apiDocs.js';\nimport { handleFile } from './apiFile.js';\nimport { handleCleanup, handleScan } from './apiScan.js';\nimport { sendError, sendJSON, readBody } from './http.js';\nimport { crossOriginReason, rebindingReason } from './httpGuards.js';\nimport { proxyChat } from './llmProxy.js';\nimport { serveStatic } from './static.js';\nimport { handleSearch } from './apiSearch.js';\nimport { handleBlame } from './apiGitBlame.js';\nimport { handleDiff, handleDiffRefs } from './apiDiff.js';\nimport { handleReview } from './apiReview.js';\nimport { handleToolsInstall, handleToolsRun, handleToolsStatus } from './apiTools.js';\nimport { handleMcpStart, handleMcpStatus, handleMcpStop, handleMcpCommand } from './apiMcp.js';\nimport { handleGetSettings, handleRotateAccessKey, handleUpdateSettings } from './apiSettings.js';\nimport { handleAuthStatus, handleLogin, handleLogout } from './apiAuth.js';\nimport { accessKeysMatch, allowedHosts, authReason, bearerToken, DEFAULT_SETTINGS } from './config.js';\nimport { hasValidSession } from './auth.js';\n\nconst ROUTES: Route[] = [\n { method: 'POST', path: '/api/review', body: true, run: ({ res, body }) => handleReview(res, body) },\n {\n method: 'GET', path: '/api/diff/refs', sameOrigin: true,\n run: ({ res, url }) => handleDiffRefs(res, url.searchParams.get('scan')),\n },\n {\n method: 'GET', path: '/api/diff', sameOrigin: true,\n run: ({ res, url }) => handleDiff(res, url.searchParams.get('scan'), url.searchParams),\n },\n {\n method: 'POST', path: '/api/search', body: true,\n run: ({ res, body }) => handleSearch(res, body),\n },\n {\n method: 'GET', path: '/api/blame', sameOrigin: true,\n run: ({ res, url }) => handleBlame(res, url.searchParams.get('scan'), url.searchParams.get('path')),\n },\n {\n method: 'POST', path: '/api/scan', body: true,\n run: ({ res, body, config }) => handleScan(res, body, config),\n },\n {\n method: 'DELETE', prefix: '/api/scan/',\n run: ({ res, rest }) => handleCleanup(res, decodeURIComponent(rest)),\n },\n {\n method: 'GET', path: '/api/file', sameOrigin: true,\n run: ({ res, url }) => handleFile(res, url.searchParams.get('scan'), url.searchParams.get('path')),\n },\n {\n // The proxy is the handler; there is no wrapper to write. It forwards the\n // browser's key to the endpoint the browser chose and never keeps either.\n method: 'POST', path: '/api/explain', body: true,\n run: ({ res, body }) => proxyChat(res, body),\n },\n {\n method: 'POST', path: '/api/doc', body: true,\n run: ({ res, body }) => handleDocs(res, body),\n },\n {\n // Reports which analyzers are installed — environment information a foreign\n // page has no business reading, so it is guarded like /api/file.\n method: 'GET', path: '/api/tools', sameOrigin: true,\n run: ({ res }) => handleToolsStatus(res),\n },\n {\n // Runs real analyzers over a scanned root. `body: true` applies the size\n // limit; the session check in the handler is the capability gate.\n method: 'POST', path: '/api/tools/run', body: true,\n run: ({ res, body }) => handleToolsRun(res, body),\n },\n {\n // Installs a missing analyzer from the GUI. Same-origin like every POST;\n // the tool name is validated against the registry and the install plans\n // are fixed data, so the request body never reaches a command line.\n method: 'POST', path: '/api/tools/install', body: true,\n run: ({ res, body }) => handleToolsInstall(res, body),\n },\n {\n // The MCP button's state, and the command an agent harness is configured with.\n // The config is not secret, but it is a path into this machine and a foreign\n // page has no business reading either one.\n method: 'GET', path: '/api/mcp', sameOrigin: true,\n run: ({ req, res, config }) => handleMcpStatus(req, res, config),\n },\n {\n method: 'GET', path: '/api/mcp/command', sameOrigin: true,\n run: ({ req, res, config }) => handleMcpCommand(req, res, config),\n },\n {\n // Starting and stopping a process is a side effect on the person's machine, so\n // these take a body like every other POST and are checked like every other POST.\n method: 'POST', path: '/api/mcp/start', body: true,\n run: ({ req, res, config }) => handleMcpStart(req, res, config),\n },\n {\n method: 'POST', path: '/api/mcp/stop', body: true,\n run: ({ req, res, config }) => handleMcpStop(req, res, config),\n },\n {\n method: 'GET', path: '/api/health',\n run: ({ res }) => sendJSON(res, 200, { ok: true }),\n },\n {\n method: 'GET', path: '/api/auth/status', sameOrigin: true,\n run: ({ req, res, settings }) => handleAuthStatus(req, res, settings),\n },\n {\n method: 'POST', path: '/api/auth/login', body: true, sameOrigin: true,\n run: ({ req, res, body, settings }) => handleLogin(req, res, body, settings),\n },\n {\n method: 'POST', path: '/api/auth/logout', body: true, sameOrigin: true,\n run: ({ req, res }) => handleLogout(req, res),\n },\n {\n // The settings drawer and the CLI read the same public shape: everything\n // about the configuration except the access key itself.\n method: 'GET', path: '/api/settings', sameOrigin: true,\n run: ({ req, res, config }) => handleGetSettings(req, res, config),\n },\n {\n // PATCH semantics over a small allow-listed key set; the validation that\n // rejects a bad port or a lawless mode lives in `server/config.js` and is\n // shared with the CLI wizard, so both surfaces enforce one schema.\n method: 'PUT', path: '/api/settings', body: true,\n run: ({ res, body, config }) => handleUpdateSettings(res, body, config),\n },\n {\n // The only way the key changes: generated on the server, shown once in\n // this response, never readable again. In self-hosted mode this endpoint\n // is itself behind the current key, so rotation requires possession.\n method: 'POST', path: '/api/settings/access-key', body: true,\n run: ({ req, res, config }) => handleRotateAccessKey(req, res, config),\n },\n];\n\n// The table itself, for the test that walks it. Exported read-only: the routes\n// are decided here, not assembled by whoever imports this.\nexport const routes = Object.freeze(ROUTES.map((r) => Object.freeze({ ...r })));\n\nexport function matchRoute(method, pathname) {\n for (const route of ROUTES) {\n if (route.method !== method) continue;\n if (route.path === pathname) return { route, rest: '' };\n if (route.prefix && pathname.startsWith(route.prefix)) {\n return { route, rest: pathname.slice(route.prefix.length) };\n }\n }\n return null;\n}\n\n// `config` carries the directories the handlers need — the project root to scan\n// for the demo, and the two roots static serving maps into. Passed in rather\n// than resolved here so a test can point the server somewhere else, and so this\n// module has nothing to say about where it was installed.\n//\n// Settings arrive the same way: `config.getSettings` is read on every request,\n// so a key rotated or a domain changed through the API takes effect on the next\n// request without a restart. A bare `createServer()` (the tests) has no getter\n// and sees the local-mode defaults — never somebody's home directory.\nasync function liveSettings(config) {\n if (typeof config?.getSettings !== 'function') return DEFAULT_SETTINGS;\n try {\n return await config.getSettings();\n } catch {\n return null; // unreadable config: fail closed, every /api call gets a 500\n }\n}\n\nexport function createRouter(config) {\n return async function handleRequest(req, res) {\n try {\n const url = new URL(req.url, 'http://127.0.0.1');\n\n const settings = await liveSettings(config);\n if (!settings) {\n return sendError(res, 500, 'The settings file could not be read — run `onboarder doctor` to find out why.');\n }\n\n const wrongHost = rebindingReason(req, allowedHosts(settings));\n if (wrongHost) {\n const scope = settings.mode === 'self-hosted'\n ? 'Onboarder only answers to its configured host and domain. '\n : 'Onboarder only answers to localhost. ';\n return sendError(res, 403, scope + wrongHost);\n }\n\n const found = matchRoute(req.method, url.pathname);\n const publicAuthRoute = ['/api/health', '/api/auth/status', '/api/auth/login', '/api/auth/logout'].includes(url.pathname);\n if (req.method !== 'GET' || found?.route.sameOrigin) {\n const foreign = crossOriginReason(req);\n if (foreign) {\n return sendError(res, 403, 'That request did not come from Onboarder’s own page. ' + foreign);\n }\n }\n\n // Bearer clients keep their existing API contract. A browser gets a signed,\n // HttpOnly session from the login form instead of storing the raw key.\n const bearerClient = accessKeysMatch(settings.accessKey, bearerToken(req));\n const browserAuthenticated = hasValidSession(req, settings);\n const authDenied = authReason(req, settings);\n const locallyExempt = !authDenied;\n const authenticated = bearerClient || browserAuthenticated || locallyExempt;\n const remoteSelfHosted = settings.mode === 'self-hosted' && Boolean(authDenied);\n\n if (remoteSelfHosted && !authenticated && !publicAuthRoute) {\n // API callers keep a machine-readable 401. A browser navigation gets the\n // themed sign-in document so the user never has to paste JSON into a tab.\n const accepts = String(req.headers?.accept || '');\n if (req.method === 'GET' && (url.pathname === '/' || accepts.includes('text/html'))) {\n res.statusCode = 200;\n return await serveStatic(res, '/login.html', config);\n }\n return sendError(res, 401, authDenied || 'Sign in with the Onboarder access key first.');\n }\n\n if (url.pathname === '/api/auth/logout') {\n // Always clear the browser cookie, even if it had already expired.\n return handleLogout(req, res);\n }\n\n if (found) {\n const body = found.route.body ? await readBody(req) : null;\n return await found.route.run({ req, res, url, body, rest: found.rest, config, settings });\n }\n\n if (req.method === 'GET') return await serveStatic(res, url.pathname, config);\n\n sendError(res, 404, 'Nothing lives at ' + url.pathname + '.');\n } catch (err) {\n // The last resort. Handlers turn their own expected failures into a status\n // that says something useful; anything reaching here is a bug or a broken\n // request body, and the message is more use to the person than \"500\".\n sendError(res, 500, err.message || 'Something went sideways.');\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { normalizeScanOptions } from '../shared/analyzer/scanControl.js';
|
|
2
|
+
/** Validate untrusted JSON before touching the filesystem or cloning anything. */
|
|
3
|
+
export function parseScanRequest(value) {
|
|
4
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
5
|
+
throw new TypeError('Send a path, a gitUrl, or { demo: true }.');
|
|
6
|
+
}
|
|
7
|
+
const body = value;
|
|
8
|
+
const sources = [body.path !== undefined, body.gitUrl !== undefined, body.demo === true].filter(Boolean).length;
|
|
9
|
+
if (sources === 0)
|
|
10
|
+
throw new TypeError('Send a path, a gitUrl, or { demo: true }.');
|
|
11
|
+
if (sources !== 1)
|
|
12
|
+
throw new TypeError('Send exactly one path, gitUrl, or { demo: true }.');
|
|
13
|
+
const options = {};
|
|
14
|
+
if (body.options !== undefined) {
|
|
15
|
+
if (!body.options || typeof body.options !== 'object' || Array.isArray(body.options)) {
|
|
16
|
+
throw new TypeError('Scan options must be an object.');
|
|
17
|
+
}
|
|
18
|
+
const input = body.options;
|
|
19
|
+
for (const key of ['maxFiles', 'maxFileSize', 'readConcurrency']) {
|
|
20
|
+
if (input[key] === undefined)
|
|
21
|
+
continue;
|
|
22
|
+
if (typeof input[key] !== 'number')
|
|
23
|
+
throw new TypeError(`${key} must be a number.`);
|
|
24
|
+
options[key] = input[key];
|
|
25
|
+
}
|
|
26
|
+
normalizeScanOptions(options);
|
|
27
|
+
}
|
|
28
|
+
if (body.demo === true)
|
|
29
|
+
return { demo: true, options };
|
|
30
|
+
if (typeof body.path === 'string' && body.path.trim())
|
|
31
|
+
return { path: body.path.trim(), options };
|
|
32
|
+
if (typeof body.gitUrl === 'string' && body.gitUrl.trim())
|
|
33
|
+
return { gitUrl: body.gitUrl.trim(), options };
|
|
34
|
+
throw new TypeError('The path or gitUrl must be a non-empty string.');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=scanRequest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanRequest.js","sourceRoot":"","sources":["../src/server/scanRequest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,IAAI,GAAG,KAAgC,CAAC;IAC9C,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAChH,IAAI,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;IACpF,IAAI,OAAO,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,mDAAmD,CAAC,CAAC;IAC5F,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACrF,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAkC,CAAC;QACtD,KAAK,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,iBAAiB,CAAU,EAAE,CAAC;YAC1E,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,SAAS;gBAAE,SAAS;YACvC,IAAI,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,MAAM,IAAI,SAAS,CAAC,GAAG,GAAG,oBAAoB,CAAC,CAAC;YACpF,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;QACD,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACvD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC;IAClG,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC;IAC1G,MAAM,IAAI,SAAS,CAAC,gDAAgD,CAAC,CAAC;AACxE,CAAC","sourcesContent":["import type { ScanRequest, ScanRequestOptions } from '../shared/contracts.js';\nimport { normalizeScanOptions } from '../shared/analyzer/scanControl.js';\n\n/** Validate untrusted JSON before touching the filesystem or cloning anything. */\nexport function parseScanRequest(value: unknown): ScanRequest {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new TypeError('Send a path, a gitUrl, or { demo: true }.');\n }\n const body = value as Record<string, unknown>;\n const sources = [body.path !== undefined, body.gitUrl !== undefined, body.demo === true].filter(Boolean).length;\n if (sources === 0) throw new TypeError('Send a path, a gitUrl, or { demo: true }.');\n if (sources !== 1) throw new TypeError('Send exactly one path, gitUrl, or { demo: true }.');\n const options: ScanRequestOptions = {};\n if (body.options !== undefined) {\n if (!body.options || typeof body.options !== 'object' || Array.isArray(body.options)) {\n throw new TypeError('Scan options must be an object.');\n }\n const input = body.options as Record<string, unknown>;\n for (const key of ['maxFiles', 'maxFileSize', 'readConcurrency'] as const) {\n if (input[key] === undefined) continue;\n if (typeof input[key] !== 'number') throw new TypeError(`${key} must be a number.`);\n options[key] = input[key];\n }\n normalizeScanOptions(options);\n }\n if (body.demo === true) return { demo: true, options };\n if (typeof body.path === 'string' && body.path.trim()) return { path: body.path.trim(), options };\n if (typeof body.gitUrl === 'string' && body.gitUrl.trim()) return { gitUrl: body.gitUrl.trim(), options };\n throw new TypeError('The path or gitUrl must be a non-empty string.');\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function buildSearchIndex(source: any, codeFiles: any, options?: Record<string, any>): Promise<{
|
|
2
|
+
index: Map<any, any>;
|
|
3
|
+
docCounts: Map<any, any>;
|
|
4
|
+
totalDocs: number;
|
|
5
|
+
totalBytes: number;
|
|
6
|
+
skipped: {
|
|
7
|
+
tooLarge: number;
|
|
8
|
+
overCap: number;
|
|
9
|
+
readFailed: number;
|
|
10
|
+
binary: number;
|
|
11
|
+
noTokens: number;
|
|
12
|
+
};
|
|
13
|
+
cap: {
|
|
14
|
+
maxFileBytes: number;
|
|
15
|
+
maxTotalBytes: number;
|
|
16
|
+
};
|
|
17
|
+
}>;
|