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