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/apiMcp.js
CHANGED
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
// The GET is guarded too: the status includes the command line an agent is
|
|
15
15
|
// configured with, which a foreign page has no business having even though it is
|
|
16
16
|
// not a secret.
|
|
17
|
-
|
|
18
17
|
import { sendJSON } from './http.js';
|
|
19
18
|
import { mcpCommand, mcpEntryPoint } from './mcp/runner.js';
|
|
20
|
-
|
|
21
19
|
// Every handler here has the same shape: work out the answer, then let whoever is
|
|
22
20
|
// driving decide how it gets sent. That split exists so `config.handleMcp` — a test
|
|
23
21
|
// double, or an embedding that supervises MCP itself — can observe the real result
|
|
@@ -27,84 +25,84 @@ import { mcpCommand, mcpEntryPoint } from './mcp/runner.js';
|
|
|
27
25
|
// The hook owns the response when present. `server/index.js` supplies none, so the
|
|
28
26
|
// ordinary path is exactly what it was.
|
|
29
27
|
function reply(res, config, method, req, result) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
if (typeof config?.handleMcp === 'function') {
|
|
29
|
+
config.handleMcp(method, req, res, result);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
sendJSON(res, result.status, result.body);
|
|
35
33
|
}
|
|
36
|
-
|
|
37
34
|
// The runner, however the caller named it. `mcp` is what `server/index.js` sets;
|
|
38
35
|
// `runner` is accepted because that is the obvious name and a test reaching for it
|
|
39
36
|
// should not have to read the index to find the convention.
|
|
40
37
|
function runnerFor(config) {
|
|
41
|
-
|
|
38
|
+
return config?.mcp || config?.runner || null;
|
|
42
39
|
}
|
|
43
|
-
|
|
44
40
|
export function handleMcpStatus(req, res, config) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
const runner = runnerFor(config);
|
|
42
|
+
if (!runner) {
|
|
43
|
+
// The app is running without the control surface (a test, or an embedding
|
|
44
|
+
// that drives MCP itself). Say so rather than pretending it is stopped.
|
|
45
|
+
return reply(res, config, 'GET /api/mcp', req, {
|
|
46
|
+
status: 200,
|
|
47
|
+
body: {
|
|
48
|
+
running: false,
|
|
49
|
+
state: 'unavailable',
|
|
50
|
+
reason: 'MCP supervision is not enabled in this process.',
|
|
51
|
+
tools: [],
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
reply(res, config, 'GET /api/mcp', req, { status: 200, body: runner.status() });
|
|
60
56
|
}
|
|
61
|
-
|
|
62
57
|
export async function handleMcpStart(req, res, config) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
const runner = runnerFor(config);
|
|
59
|
+
if (!runner)
|
|
60
|
+
return reply(res, config, 'POST /api/mcp/start', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
|
|
61
|
+
try {
|
|
62
|
+
const status = await runner.start();
|
|
63
|
+
reply(res, config, 'POST /api/mcp/start', req, { status: 200, body: status });
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// A start that cannot happen is usually a missing Node, a bad path, or a
|
|
67
|
+
// child that failed its handshake — the message says which, because the
|
|
68
|
+
// person clicking the button can only fix one of those three.
|
|
69
|
+
reply(res, config, 'POST /api/mcp/start', req, { status: 500, body: { error: err.message || 'The MCP server could not be started.' } });
|
|
70
|
+
}
|
|
74
71
|
}
|
|
75
|
-
|
|
76
72
|
export async function handleMcpStop(req, res, config) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
73
|
+
const runner = runnerFor(config);
|
|
74
|
+
if (!runner)
|
|
75
|
+
return reply(res, config, 'POST /api/mcp/stop', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });
|
|
76
|
+
try {
|
|
77
|
+
const status = await runner.stop();
|
|
78
|
+
reply(res, config, 'POST /api/mcp/stop', req, { status: 200, body: status });
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
reply(res, config, 'POST /api/mcp/stop', req, { status: 500, body: { error: err.message || 'The MCP server could not be stopped.' } });
|
|
82
|
+
}
|
|
85
83
|
}
|
|
86
|
-
|
|
87
84
|
// The command an agent harness is configured with, shown in the panel so nobody
|
|
88
85
|
// has to read the README to find it. The paths come from the same function that
|
|
89
86
|
// starts the child, so the copy-paste text cannot drift from what actually runs.
|
|
90
87
|
export function handleMcpCommand(req, res, config) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
88
|
+
// The absolute path, not the relative one the runner uses: this text gets
|
|
89
|
+
// pasted into a client config that spawns the command from the harness's own
|
|
90
|
+
// working directory, where a relative path does not resolve.
|
|
91
|
+
const entry = mcpEntryPoint();
|
|
92
|
+
reply(res, config, 'GET /api/mcp/command', req, {
|
|
93
|
+
status: 200,
|
|
94
|
+
body: {
|
|
95
|
+
command: entry,
|
|
96
|
+
relative: mcpCommand(),
|
|
97
|
+
transports: ['stdio'],
|
|
98
|
+
// The two config shapes a client is overwhelmingly likely to want. Both use
|
|
99
|
+
// the absolute path, for the reason above.
|
|
100
|
+
examples: [
|
|
101
|
+
{ name: 'node', command: process.execPath, args: [entry] },
|
|
102
|
+
{ name: 'node (assuming node is on PATH)', command: 'node', args: [entry] },
|
|
103
|
+
],
|
|
104
|
+
docs: 'https://modelcontextprotocol.io/docs/develop/connect-local-servers',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
110
107
|
}
|
|
108
|
+
//# sourceMappingURL=apiMcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiMcp.js","sourceRoot":"","sources":["../src/server/apiMcp.ts"],"names":[],"mappings":"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","sourcesContent":["// The endpoints behind the topbar button:\n//\n// GET /api/mcp — is it running, what is it offering, what happened\n// POST /api/mcp/start — start the child process\n// POST /api/mcp/stop — stop it\n//\n// The control surface and the protocol are deliberately separate. This process\n// never speaks MCP itself; it supervises a child that does. That is what makes\n// \"stop\" real — closing stdin lets the server drain and exit on its own terms\n// rather than leaving a half-torn-down process holding the scan cache.\n//\n// Starting a server is a side effect on the person's machine, so start and stop\n// take a body like every other POST and are same-origin like every other POST.\n// The GET is guarded too: the status includes the command line an agent is\n// configured with, which a foreign page has no business having even though it is\n// not a secret.\n\nimport { sendJSON } from './http.js';\nimport { mcpCommand, mcpEntryPoint } from './mcp/runner.js';\n\n// Every handler here has the same shape: work out the answer, then let whoever is\n// driving decide how it gets sent. That split exists so `config.handleMcp` — a test\n// double, or an embedding that supervises MCP itself — can observe the real result\n// of the real action rather than standing in for it. A hook that skipped the\n// action would make a start endpoint that never starts anything look fine.\n//\n// The hook owns the response when present. `server/index.js` supplies none, so the\n// ordinary path is exactly what it was.\nfunction reply(res, config, method, req, result) {\n if (typeof config?.handleMcp === 'function') {\n config.handleMcp(method, req, res, result);\n return;\n }\n sendJSON(res, result.status, result.body);\n}\n\n// The runner, however the caller named it. `mcp` is what `server/index.js` sets;\n// `runner` is accepted because that is the obvious name and a test reaching for it\n// should not have to read the index to find the convention.\nfunction runnerFor(config) {\n return config?.mcp || config?.runner || null;\n}\n\nexport function handleMcpStatus(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) {\n // The app is running without the control surface (a test, or an embedding\n // that drives MCP itself). Say so rather than pretending it is stopped.\n return reply(res, config, 'GET /api/mcp', req, {\n status: 200,\n body: {\n running: false,\n state: 'unavailable',\n reason: 'MCP supervision is not enabled in this process.',\n tools: [],\n },\n });\n }\n reply(res, config, 'GET /api/mcp', req, { status: 200, body: runner.status() });\n}\n\nexport async function handleMcpStart(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) return reply(res, config, 'POST /api/mcp/start', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });\n try {\n const status = await runner.start();\n reply(res, config, 'POST /api/mcp/start', req, { status: 200, body: status });\n } catch (err) {\n // A start that cannot happen is usually a missing Node, a bad path, or a\n // child that failed its handshake — the message says which, because the\n // person clicking the button can only fix one of those three.\n reply(res, config, 'POST /api/mcp/start', req, { status: 500, body: { error: err.message || 'The MCP server could not be started.' } });\n }\n}\n\nexport async function handleMcpStop(req, res, config) {\n const runner = runnerFor(config);\n if (!runner) return reply(res, config, 'POST /api/mcp/stop', req, { status: 501, body: { error: 'MCP supervision is not enabled in this process.' } });\n try {\n const status = await runner.stop();\n reply(res, config, 'POST /api/mcp/stop', req, { status: 200, body: status });\n } catch (err) {\n reply(res, config, 'POST /api/mcp/stop', req, { status: 500, body: { error: err.message || 'The MCP server could not be stopped.' } });\n }\n}\n\n// The command an agent harness is configured with, shown in the panel so nobody\n// has to read the README to find it. The paths come from the same function that\n// starts the child, so the copy-paste text cannot drift from what actually runs.\nexport function handleMcpCommand(req, res, config) {\n // The absolute path, not the relative one the runner uses: this text gets\n // pasted into a client config that spawns the command from the harness's own\n // working directory, where a relative path does not resolve.\n const entry = mcpEntryPoint();\n reply(res, config, 'GET /api/mcp/command', req, {\n status: 200,\n body: {\n command: entry,\n relative: mcpCommand(),\n transports: ['stdio'],\n // The two config shapes a client is overwhelmingly likely to want. Both use\n // the absolute path, for the reason above.\n examples: [\n { name: 'node', command: process.execPath, args: [entry] },\n { name: 'node (assuming node is on PATH)', command: 'node', args: [entry] },\n ],\n docs: 'https://modelcontextprotocol.io/docs/develop/connect-local-servers',\n },\n });\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getSession } from './sessions.js';
|
|
2
|
+
import { sendError, sendJSON } from './http.js';
|
|
3
|
+
import { GitDiffError } from './gitDiff.js';
|
|
4
|
+
import { runReview } from './review.js';
|
|
5
|
+
import { reviewProfile } from '../shared/review/review.js';
|
|
6
|
+
export async function handleReview(res, body) {
|
|
7
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
8
|
+
return sendError(res, 400, 'Provide a review request object.');
|
|
9
|
+
const b = body;
|
|
10
|
+
if (typeof b.scanId !== 'string')
|
|
11
|
+
return sendError(res, 400, 'Choose a scanned repository.');
|
|
12
|
+
const session = getSession(b.scanId);
|
|
13
|
+
if (!session)
|
|
14
|
+
return sendError(res, 404, 'Scan session expired. Scan this repository again.');
|
|
15
|
+
if (b.mode !== undefined && !['working', 'staged', 'range'].includes(b.mode))
|
|
16
|
+
return sendError(res, 400, 'Choose working, staged, or range review.');
|
|
17
|
+
for (const key of ['base', 'head'])
|
|
18
|
+
if (b[key] !== undefined && (typeof b[key] !== 'string' || b[key].length > 200))
|
|
19
|
+
return sendError(res, 400, 'References must be strings of at most 200 characters.');
|
|
20
|
+
let profile;
|
|
21
|
+
try {
|
|
22
|
+
if (b.profile !== undefined)
|
|
23
|
+
profile = reviewProfile(b.profile);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return sendError(res, 400, error.message);
|
|
27
|
+
}
|
|
28
|
+
const abort = new AbortController();
|
|
29
|
+
const close = () => { if (!res.writableEnded)
|
|
30
|
+
abort.abort(); };
|
|
31
|
+
res.once('close', close);
|
|
32
|
+
try {
|
|
33
|
+
const report = await runReview(session.root, { base: b.base || 'HEAD', head: b.head || '', mode: b.mode || 'working', profile, signal: abort.signal });
|
|
34
|
+
if (!abort.signal.aborted)
|
|
35
|
+
sendJSON(res, 200, report);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (!abort.signal.aborted)
|
|
39
|
+
sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Review failed.');
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
res.off('close', close);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=apiReview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiReview.js","sourceRoot":"","sources":["../src/server/apiReview.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport { getSession } from './sessions.js';\nimport { sendError, sendJSON } from './http.js';\nimport { GitDiffError } from './gitDiff.js';\nimport { runReview } from './review.js';\nimport { reviewProfile } from '../shared/review/review.js';\nimport type { ReviewMode, ReviewProfile } from '../shared/review/contracts.js';\n\nexport async function handleReview(res: ServerResponse, body: unknown) {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return sendError(res, 400, 'Provide a review request object.');\n const b = body as Record<string, unknown>;\n if (typeof b.scanId !== 'string') return sendError(res, 400, 'Choose a scanned repository.');\n const session = getSession(b.scanId);\n if (!session) return sendError(res, 404, 'Scan session expired. Scan this repository again.');\n if (b.mode !== undefined && !['working', 'staged', 'range'].includes(b.mode as string)) return sendError(res, 400, 'Choose working, staged, or range review.');\n for (const key of ['base', 'head']) if (b[key] !== undefined && (typeof b[key] !== 'string' || (b[key] as string).length > 200)) return sendError(res, 400, 'References must be strings of at most 200 characters.');\n let profile: ReviewProfile | undefined;\n try { if (b.profile !== undefined) profile = reviewProfile(b.profile); }\n catch (error) { return sendError(res, 400, (error as Error).message); }\n const abort = new AbortController();\n const close = () => { if (!res.writableEnded) abort.abort(); };\n res.once('close', close);\n try {\n const report = await runReview(session.root, { base: b.base as string || 'HEAD', head: b.head as string || '', mode: b.mode as ReviewMode || 'working', profile, signal: abort.signal });\n if (!abort.signal.aborted) sendJSON(res, 200, report);\n } catch (error) {\n if (!abort.signal.aborted) sendError(res, error instanceof GitDiffError ? error.status : 500, error instanceof Error ? error.message : 'Review failed.');\n } finally { res.off('close', close); }\n}\n"]}
|
package/server/apiScan.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseScanRequest } from './scanRequest.js';
|
|
2
|
+
import { throwIfAborted } from '../shared/analyzer/scanControl.js';
|
|
1
3
|
// POST /api/scan and DELETE /api/scan/:cloneId — the endpoint the whole app
|
|
2
4
|
// starts from.
|
|
3
5
|
//
|
|
@@ -13,10 +15,8 @@
|
|
|
13
15
|
// hop inside a filter keystroke. The search index travels the same way: built
|
|
14
16
|
// once at scan time, attached to the session, and queried without re-walking
|
|
15
17
|
// the tree.
|
|
16
|
-
|
|
17
18
|
import { promises as fs } from 'node:fs';
|
|
18
19
|
import path from 'node:path';
|
|
19
|
-
|
|
20
20
|
import { computeFacts } from '../shared/analyzer/graph.js';
|
|
21
21
|
import { scanRepo } from '../shared/analyzer/scan.js';
|
|
22
22
|
import { detectManifest } from '../shared/analyzer/services.js';
|
|
@@ -28,92 +28,111 @@ import { gitLog, parseGitLog } from './gitHistory.js';
|
|
|
28
28
|
import { sendError, sendJSON } from './http.js';
|
|
29
29
|
import { expandHome } from './paths.js';
|
|
30
30
|
import { closeClone, isCloneId, openSession } from './sessions.js';
|
|
31
|
-
|
|
32
31
|
export async function handleScan(res, body, { projectRoot }) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
let request;
|
|
33
|
+
try {
|
|
34
|
+
request = parseScanRequest(body);
|
|
35
|
+
if (request.gitUrl)
|
|
36
|
+
assertGitUrl(request.gitUrl);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return sendError(res, 400, error.message);
|
|
40
|
+
}
|
|
41
|
+
const controller = new AbortController();
|
|
42
|
+
const disconnected = () => { if (!res.writableEnded)
|
|
43
|
+
controller.abort(); };
|
|
44
|
+
res.once('close', disconnected);
|
|
45
|
+
let cloneDir;
|
|
46
|
+
try {
|
|
47
|
+
const target = await resolveTarget(request, projectRoot);
|
|
48
|
+
if (target.error)
|
|
49
|
+
return sendError(res, 400, target.error);
|
|
50
|
+
const { root, cloneId, gitMeta } = target;
|
|
51
|
+
cloneDir = target.cloneDir;
|
|
52
|
+
throwIfAborted(controller.signal);
|
|
53
|
+
const source = nodeFileSource(root);
|
|
54
|
+
const scan = await scanRepo(source, { ...request.options, signal: controller.signal });
|
|
55
|
+
if (gitMeta) {
|
|
56
|
+
// Show the repo's real name up top; keep the temp dir name for honesty,
|
|
57
|
+
// so a path in the UI is still a path you could go and look at.
|
|
58
|
+
scan.name = gitMeta.repoName;
|
|
59
|
+
scan.tempId = path.basename(root);
|
|
60
|
+
scan.gitUrl = gitMeta.gitUrl;
|
|
61
|
+
}
|
|
62
|
+
const manifest = await detectManifest(source);
|
|
63
|
+
const facts = computeFacts(scan, manifest);
|
|
64
|
+
// The search index is built from the same files the scanner accepted.
|
|
65
|
+
// Doing it here, against the same FileSource, means a search request
|
|
66
|
+
// becomes a Map lookup plus a scoring pass; the per-file reads the old
|
|
67
|
+
// `/api/search` did on first use are gone. Cap behaviour and skip rules
|
|
68
|
+
// live in `searchIndex.js` and are tested there.
|
|
69
|
+
const codeFiles = scan.files.map((f) => f.path);
|
|
70
|
+
const searchIndexData = await buildSearchIndex(source, codeFiles);
|
|
71
|
+
const history = await collectHistory(root, scan);
|
|
72
|
+
throwIfAborted(controller.signal);
|
|
73
|
+
const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });
|
|
74
|
+
sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
// A clone that was never scanned successfully has no session to evict it
|
|
78
|
+
// later, so it has to go now or it is a temp directory nobody owns.
|
|
79
|
+
if (cloneDir)
|
|
80
|
+
await removeClone(cloneDir);
|
|
81
|
+
if (!controller.signal.aborted)
|
|
82
|
+
sendError(res, 500, 'The scan failed: ' + err.message);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
res.removeListener('close', disconnected);
|
|
46
86
|
}
|
|
47
|
-
const manifest = await detectManifest(source);
|
|
48
|
-
const facts = computeFacts(scan, manifest);
|
|
49
|
-
|
|
50
|
-
// The search index is built from the same files the scanner accepted.
|
|
51
|
-
// Doing it here, against the same FileSource, means a search request
|
|
52
|
-
// becomes a Map lookup plus a scoring pass; the per-file reads the old
|
|
53
|
-
// `/api/search` did on first use are gone. Cap behaviour and skip rules
|
|
54
|
-
// live in `searchIndex.js` and are tested there.
|
|
55
|
-
const codeFiles = scan.files.map((f) => f.path);
|
|
56
|
-
const searchIndexData = await buildSearchIndex(source, codeFiles);
|
|
57
|
-
|
|
58
|
-
const history = await collectHistory(root, scan);
|
|
59
|
-
const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });
|
|
60
|
-
sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });
|
|
61
|
-
} catch (err) {
|
|
62
|
-
// A clone that was never scanned successfully has no session to evict it
|
|
63
|
-
// later, so it has to go now or it is a temp directory nobody owns.
|
|
64
|
-
if (cloneDir) await removeClone(cloneDir);
|
|
65
|
-
sendError(res, 500, 'The scan failed: ' + err.message);
|
|
66
|
-
}
|
|
67
87
|
}
|
|
68
|
-
|
|
69
88
|
// History is a bonus layer over the scan, never a reason for it to fail. When
|
|
70
89
|
// git is missing, the folder has no .git, or the log somehow blows up, the
|
|
71
90
|
// payload carries the honest unavailable shape instead — the UI says why
|
|
72
91
|
// rather than inventing zeros.
|
|
73
92
|
async function collectHistory(root, scan) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
try {
|
|
94
|
+
const log = await gitLog(root);
|
|
95
|
+
if (!log.ok)
|
|
96
|
+
return unavailableHistory(log.reason);
|
|
97
|
+
return analyzeHistory(scan, parseGitLog(log.text), { totalCommits: log.totalCommits });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return unavailableHistory('The history could not be read — the scan itself is unaffected.');
|
|
101
|
+
}
|
|
81
102
|
}
|
|
82
|
-
|
|
83
103
|
// The three request shapes, reduced to a directory. Cloning happens here, which
|
|
84
104
|
// is why this returns rather than sends: the caller owns cleanup if the scan that
|
|
85
105
|
// follows fails.
|
|
86
106
|
async function resolveTarget(body, projectRoot) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
if (body.demo)
|
|
108
|
+
return { root: projectRoot };
|
|
109
|
+
if (body.path) {
|
|
110
|
+
const root = expandHome(String(body.path));
|
|
111
|
+
const stat = await fs.stat(root).catch(() => null);
|
|
112
|
+
if (!stat?.isDirectory()) {
|
|
113
|
+
return { error: 'No folder at ' + root + '. Check the path and try again.' };
|
|
114
|
+
}
|
|
115
|
+
return { root };
|
|
116
|
+
}
|
|
117
|
+
if (body.gitUrl) {
|
|
118
|
+
const url = assertGitUrl(body.gitUrl);
|
|
119
|
+
const clone = await cloneRepo(url);
|
|
120
|
+
return {
|
|
121
|
+
root: clone.dir,
|
|
122
|
+
cloneDir: clone.dir,
|
|
123
|
+
cloneId: clone.id,
|
|
124
|
+
gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },
|
|
125
|
+
};
|
|
94
126
|
}
|
|
95
|
-
return {
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (body.gitUrl) {
|
|
99
|
-
const url = assertGitUrl(body.gitUrl);
|
|
100
|
-
const clone = await cloneRepo(url);
|
|
101
|
-
return {
|
|
102
|
-
root: clone.dir,
|
|
103
|
-
cloneDir: clone.dir,
|
|
104
|
-
cloneId: clone.id,
|
|
105
|
-
gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return { error: 'Send a path, a gitUrl, or { demo: true }.' };
|
|
127
|
+
return { error: 'Send a path, a gitUrl, or { demo: true }.' };
|
|
110
128
|
}
|
|
111
|
-
|
|
112
129
|
// The page sends this when it is done with a cloned repo. Nothing is reported
|
|
113
130
|
// about whether the clone was there: the browser cannot act on the difference,
|
|
114
131
|
// and an unload-time request that 404s looks like a bug in the console.
|
|
115
132
|
export async function handleCleanup(res, cloneId) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
133
|
+
if (!isCloneId(cloneId))
|
|
134
|
+
return sendError(res, 400, 'That is not a clone id.');
|
|
135
|
+
await closeClone(cloneId);
|
|
136
|
+
sendJSON(res, 200, { ok: true });
|
|
119
137
|
}
|
|
138
|
+
//# sourceMappingURL=apiScan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiScan.js","sourceRoot":"","sources":["../src/server/apiScan.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport type { ScanRequest } from '../shared/contracts.js';\nimport { parseScanRequest } from './scanRequest.js';\nimport { throwIfAborted } from '../shared/analyzer/scanControl.js';\n// POST /api/scan and DELETE /api/scan/:cloneId — the endpoint the whole app\n// starts from.\n//\n// Three ways in, one way out. A local path, a git URL to clone, or `demo: true`\n// meaning \"scan Onboarder itself\", which is how the app has something to show\n// before you have chosen anything. All three end at a root directory, and from\n// there the work is the same three calls the browser would make against its own\n// file source: scan, detect the manifest, compute the facts.\n//\n// The response is deliberately whole. The front end gets scan, facts and\n// manifest in one round trip and holds them for the session, because every view\n// is a projection of those three and re-fetching per view would put a network\n// hop inside a filter keystroke. The search index travels the same way: built\n// once at scan time, attached to the session, and queried without re-walking\n// the tree.\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { computeFacts } from '../shared/analyzer/graph.js';\nimport { scanRepo } from '../shared/analyzer/scan.js';\nimport { detectManifest } from '../shared/analyzer/services.js';\nimport { analyzeHistory, unavailableHistory } from '../shared/analyzer/history.js';\nimport { buildSearchIndex } from './searchIndex.js';\nimport { nodeFileSource } from './fileSourceNode.js';\nimport { assertGitUrl, cloneRepo, removeClone, repoNameFromUrl } from './gitClone.js';\nimport { gitLog, parseGitLog } from './gitHistory.js';\nimport { sendError, sendJSON } from './http.js';\nimport { expandHome } from './paths.js';\nimport { closeClone, isCloneId, openSession } from './sessions.js';\n\nexport async function handleScan(res: ServerResponse, body: unknown, { projectRoot }: { projectRoot: string }) {\n let request: ScanRequest;\n try { request = parseScanRequest(body); if (request.gitUrl) assertGitUrl(request.gitUrl); }\n catch (error) { return sendError(res, 400, error.message); }\n const controller = new AbortController();\n const disconnected = () => { if (!res.writableEnded) controller.abort(); };\n res.once('close', disconnected);\n let cloneDir: string | undefined;\n try {\n const target = await resolveTarget(request, projectRoot);\n if (target.error) return sendError(res, 400, target.error);\n const { root, cloneId, gitMeta } = target;\n cloneDir = target.cloneDir;\n throwIfAborted(controller.signal);\n const source = nodeFileSource(root);\n const scan = await scanRepo(source, { ...request.options, signal: controller.signal });\n if (gitMeta) {\n // Show the repo's real name up top; keep the temp dir name for honesty,\n // so a path in the UI is still a path you could go and look at.\n scan.name = gitMeta.repoName;\n scan.tempId = path.basename(root);\n scan.gitUrl = gitMeta.gitUrl;\n }\n const manifest = await detectManifest(source);\n const facts = computeFacts(scan, manifest);\n\n // The search index is built from the same files the scanner accepted.\n // Doing it here, against the same FileSource, means a search request\n // becomes a Map lookup plus a scoring pass; the per-file reads the old\n // `/api/search` did on first use are gone. Cap behaviour and skip rules\n // live in `searchIndex.js` and are tested there.\n const codeFiles = scan.files.map((f) => f.path);\n const searchIndexData = await buildSearchIndex(source, codeFiles);\n\n const history = await collectHistory(root, scan);\n throwIfAborted(controller.signal);\n const scanId = openSession({ root, cloneDir, searchIndex: searchIndexData });\n sendJSON(res, 200, { scan, facts, manifest, history, scanId, cloneId });\n } catch (err) {\n // A clone that was never scanned successfully has no session to evict it\n // later, so it has to go now or it is a temp directory nobody owns.\n if (cloneDir) await removeClone(cloneDir);\n if (!controller.signal.aborted) sendError(res, 500, 'The scan failed: ' + err.message);\n } finally {\n res.removeListener('close', disconnected);\n }\n}\n\n// History is a bonus layer over the scan, never a reason for it to fail. When\n// git is missing, the folder has no .git, or the log somehow blows up, the\n// payload carries the honest unavailable shape instead — the UI says why\n// rather than inventing zeros.\nasync function collectHistory(root, scan) {\n try {\n const log = await gitLog(root);\n if (!log.ok) return unavailableHistory(log.reason);\n return analyzeHistory(scan, parseGitLog(log.text), { totalCommits: log.totalCommits });\n } catch {\n return unavailableHistory('The history could not be read — the scan itself is unaffected.');\n }\n}\n\n// The three request shapes, reduced to a directory. Cloning happens here, which\n// is why this returns rather than sends: the caller owns cleanup if the scan that\n// follows fails.\nasync function resolveTarget(body: ScanRequest, projectRoot: string): Promise<{ root?: string; error?: string; cloneDir?: string; cloneId?: string; gitMeta?: { gitUrl: string; repoName: string } }> {\n if (body.demo) return { root: projectRoot };\n\n if (body.path) {\n const root = expandHome(String(body.path));\n const stat = await fs.stat(root).catch(() => null);\n if (!stat?.isDirectory()) {\n return { error: 'No folder at ' + root + '. Check the path and try again.' };\n }\n return { root };\n }\n\n if (body.gitUrl) {\n const url = assertGitUrl(body.gitUrl);\n const clone = await cloneRepo(url);\n return {\n root: clone.dir,\n cloneDir: clone.dir,\n cloneId: clone.id,\n gitMeta: { gitUrl: url, repoName: repoNameFromUrl(url) },\n };\n }\n\n return { error: 'Send a path, a gitUrl, or { demo: true }.' };\n}\n\n// The page sends this when it is done with a cloned repo. Nothing is reported\n// about whether the clone was there: the browser cannot act on the difference,\n// and an unload-time request that 404s looks like a bug in the console.\nexport async function handleCleanup(res, cloneId) {\n if (!isCloneId(cloneId)) return sendError(res, 400, 'That is not a clone id.');\n await closeClone(cloneId);\n sendJSON(res, 200, { ok: true });\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare function buildTfIdfIndex(documents: any): {
|
|
2
|
+
index: Map<any, any>;
|
|
3
|
+
docCounts: Map<any, any>;
|
|
4
|
+
totalDocs: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function searchIndex(indexData: any, queryString: any, limit?: number): any[];
|
|
7
|
+
export declare function searchDocuments(indexData: any, rawQuery: any, options?: Record<string, any>): {
|
|
8
|
+
results: any[];
|
|
9
|
+
total: number;
|
|
10
|
+
indexed: any;
|
|
11
|
+
capped: boolean;
|
|
12
|
+
advanced: any;
|
|
13
|
+
query: {
|
|
14
|
+
terms: any;
|
|
15
|
+
phrases: any;
|
|
16
|
+
negatives: any;
|
|
17
|
+
exts: any;
|
|
18
|
+
paths: any;
|
|
19
|
+
kinds: any;
|
|
20
|
+
isTest: any;
|
|
21
|
+
isSource: any;
|
|
22
|
+
regex: string;
|
|
23
|
+
caseSensitive: any;
|
|
24
|
+
advanced: any;
|
|
25
|
+
};
|
|
26
|
+
error: any;
|
|
27
|
+
stats: {
|
|
28
|
+
bytes: any;
|
|
29
|
+
skipped: any;
|
|
30
|
+
cap: any;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export declare function handleSearch(res: any, body: any): Promise<void>;
|