codebase-onboarder 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +284 -53
- package/bin/build-if-source.js +23 -0
- package/bin/onboarder.js +0 -0
- package/cli/agent/command.d.ts +71 -0
- package/cli/agent/command.js +177 -0
- package/cli/agent/command.js.map +1 -0
- package/cli/agent/config.d.ts +31 -0
- package/cli/agent/config.js +98 -0
- package/cli/agent/config.js.map +1 -0
- package/cli/agent/contracts.d.ts +77 -0
- package/cli/agent/contracts.js +2 -0
- package/cli/agent/contracts.js.map +1 -0
- package/cli/agent/files.d.ts +19 -0
- package/cli/agent/files.js +109 -0
- package/cli/agent/files.js.map +1 -0
- package/cli/agent/gitSource.d.ts +18 -0
- package/cli/agent/gitSource.js +69 -0
- package/cli/agent/gitSource.js.map +1 -0
- package/cli/agent/github.d.ts +21 -0
- package/cli/agent/github.js +94 -0
- package/cli/agent/github.js.map +1 -0
- package/cli/agent/mcp.d.ts +15 -0
- package/cli/agent/mcp.js +78 -0
- package/cli/agent/mcp.js.map +1 -0
- package/cli/agent/process.d.ts +18 -0
- package/cli/agent/process.js +118 -0
- package/cli/agent/process.js.map +1 -0
- package/cli/agent/protocol.d.ts +3 -0
- package/cli/agent/protocol.js +26 -0
- package/cli/agent/protocol.js.map +1 -0
- package/cli/agent/runner.d.ts +34 -0
- package/cli/agent/runner.js +152 -0
- package/cli/agent/runner.js.map +1 -0
- package/cli/agent/stdio.d.ts +1 -0
- package/cli/agent/stdio.js +62 -0
- package/cli/agent/stdio.js.map +1 -0
- package/cli/agent/tools.d.ts +7 -0
- package/cli/agent/tools.js +201 -0
- package/cli/agent/tools.js.map +1 -0
- package/cli/chat/app.d.ts +16 -0
- package/cli/chat/app.js +345 -0
- package/cli/chat/app.js.map +1 -0
- package/cli/chat/commands.d.ts +11 -0
- package/cli/chat/commands.js +120 -0
- package/cli/chat/commands.js.map +1 -0
- package/cli/chat/composer.d.ts +75 -0
- package/cli/chat/composer.js +467 -0
- package/cli/chat/composer.js.map +1 -0
- package/cli/chat/controller.d.ts +60 -0
- package/cli/chat/controller.js +393 -0
- package/cli/chat/controller.js.map +1 -0
- package/cli/chat/logo.d.ts +3 -0
- package/cli/chat/logo.js +14 -0
- package/cli/chat/logo.js.map +1 -0
- package/cli/chat/models.d.ts +18 -0
- package/cli/chat/models.js +88 -0
- package/cli/chat/models.js.map +1 -0
- package/cli/chat/profile.d.ts +5 -0
- package/cli/chat/profile.js +98 -0
- package/cli/chat/profile.js.map +1 -0
- package/cli/chat/render.d.ts +40 -0
- package/cli/chat/render.js +259 -0
- package/cli/chat/render.js.map +1 -0
- package/cli/chat/screen.d.ts +26 -0
- package/cli/chat/screen.js +117 -0
- package/cli/chat/screen.js.map +1 -0
- package/cli/chat/source.d.ts +29 -0
- package/cli/chat/source.js +116 -0
- package/cli/chat/source.js.map +1 -0
- package/cli/chat/store.d.ts +43 -0
- package/cli/chat/store.js +94 -0
- package/cli/chat/store.js.map +1 -0
- package/cli/chat/text.d.ts +4 -0
- package/cli/chat/text.js +27 -0
- package/cli/chat/text.js.map +1 -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 +2 -0
- package/cli/explorer/app.js +404 -314
- 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 +373 -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 +251 -164
- 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/docs/HERMES_RESEARCH.md +51 -0
- package/package.json +16 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1133 -1011
- package/public/app.js.map +1 -0
- package/public/assets/onboarder-mark.svg +11 -0
- package/public/browser.d.ts +71 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +54 -20
- 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 +4 -0
- package/public/js/aiDraft.js +166 -125
- 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 +29 -0
- package/public/js/api.js +198 -166
- 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 +9 -0
- package/public/js/diagramPane.js +138 -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 +197 -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 +115 -0
- package/public/js/llm.js +246 -278
- 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 +29 -28
- 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 +188 -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/hermesChat.d.ts +13 -0
- package/server/hermesChat.js +145 -0
- package/server/hermesChat.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 +6 -0
- package/server/http.js +27 -34
- 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 +167 -180
- 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 +195 -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 +50 -46
- 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/http.js
CHANGED
|
@@ -2,54 +2,47 @@
|
|
|
2
2
|
// does. Pulled out of the router so a handler can be read on its own, and so
|
|
3
3
|
// the body limit is one number in one place rather than a check repeated per
|
|
4
4
|
// route and forgotten on the next one.
|
|
5
|
-
|
|
6
|
-
// A scan request carries a path or a URL; an explain request carries a prompt
|
|
7
|
-
// and a slice of facts. Two megabytes is far more than either needs, and the
|
|
8
|
-
// point is to have a ceiling at all: a local server with no auth should not let
|
|
9
|
-
// a stray fetch hold memory open.
|
|
10
5
|
export const MAX_BODY_BYTES = 2 * 1024 * 1024;
|
|
11
|
-
|
|
12
6
|
// Resolves to the parsed JSON body, or `{}` for an empty one — a POST with no
|
|
13
7
|
// body is a valid "just the defaults" request and every caller would otherwise
|
|
14
8
|
// need the same guard. Rejects on oversized or malformed input; the router turns
|
|
15
9
|
// that into a 500 with the message.
|
|
16
10
|
export function readBody(req, limit = MAX_BODY_BYTES) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
let size = 0;
|
|
13
|
+
const chunks = [];
|
|
14
|
+
req.on('data', (chunk) => {
|
|
15
|
+
size += chunk.length;
|
|
16
|
+
if (size > limit) {
|
|
17
|
+
reject(new Error('Request body too large.'));
|
|
18
|
+
req.destroy();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
chunks.push(chunk);
|
|
22
|
+
});
|
|
23
|
+
req.on('end', () => {
|
|
24
|
+
try {
|
|
25
|
+
resolve(chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : {});
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
reject(new Error('The request body was not valid JSON.'));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
req.on('error', reject);
|
|
28
32
|
});
|
|
29
|
-
req.on('end', () => {
|
|
30
|
-
try {
|
|
31
|
-
resolve(chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : {});
|
|
32
|
-
} catch {
|
|
33
|
-
reject(new Error('The request body was not valid JSON.'));
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
req.on('error', reject);
|
|
37
|
-
});
|
|
38
33
|
}
|
|
39
|
-
|
|
40
34
|
export function sendJSON(res, status, data, headers = {}) {
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', ...headers });
|
|
36
|
+
res.end(JSON.stringify(data));
|
|
43
37
|
}
|
|
44
|
-
|
|
45
38
|
// Every error the UI shows comes back through here, so the shape is fixed:
|
|
46
39
|
// `{ error }` with a sentence a person can act on. The front end reads `.error`
|
|
47
40
|
// and nothing else.
|
|
48
41
|
export function sendError(res, status, message) {
|
|
49
|
-
|
|
42
|
+
sendJSON(res, status, { error: message });
|
|
50
43
|
}
|
|
51
|
-
|
|
52
44
|
export function sendText(res, status, text) {
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
res.writeHead(status, { 'content-type': 'text/plain; charset=utf-8' });
|
|
46
|
+
res.end(text);
|
|
55
47
|
}
|
|
48
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/server/http.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,6EAA6E;AAC7E,uCAAuC;AAQvC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,oCAAoC;AACpC,MAAM,UAAU,QAAQ,CAAC,GAAoB,EAAE,KAAK,GAAG,cAAc;IACnE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/B,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;YACrB,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBAC7C,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACjB,IAAI,CAAC;gBACH,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACnF,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAa,EAAE,OAAO,GAA+C,EAAE;IACnI,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;IACzF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,2EAA2E;AAC3E,gFAAgF;AAChF,oBAAoB;AACpB,MAAM,UAAU,SAAS,CAAC,GAAmB,EAAE,MAAc,EAAE,OAAe;IAC5E,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAmB,EAAE,MAAc,EAAE,IAAY;IACxE,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,2BAA2B,EAAE,CAAC,CAAC;IACvE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC","sourcesContent":["// Reading a request body and writing a response — the two things every route\n// does. Pulled out of the router so a handler can be read on its own, and so\n// the body limit is one number in one place rather than a check repeated per\n// route and forgotten on the next one.\n\n// A scan request carries a path or a URL; an explain request carries a prompt\n// and a slice of facts. Two megabytes is far more than either needs, and the\n// point is to have a ceiling at all: a local server with no auth should not let\n// a stray fetch hold memory open.\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nexport const MAX_BODY_BYTES = 2 * 1024 * 1024;\n\n// Resolves to the parsed JSON body, or `{}` for an empty one — a POST with no\n// body is a valid \"just the defaults\" request and every caller would otherwise\n// need the same guard. Rejects on oversized or malformed input; the router turns\n// that into a 500 with the message.\nexport function readBody(req: IncomingMessage, limit = MAX_BODY_BYTES): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let size = 0;\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => {\n size += chunk.length;\n if (size > limit) {\n reject(new Error('Request body too large.'));\n req.destroy();\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n try {\n resolve(chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : {});\n } catch {\n reject(new Error('The request body was not valid JSON.'));\n }\n });\n req.on('error', reject);\n });\n}\n\nexport function sendJSON(res: ServerResponse, status: number, data: unknown, headers: Record<string, string | number | string[]> = {}) {\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', ...headers });\n res.end(JSON.stringify(data));\n}\n\n// Every error the UI shows comes back through here, so the shape is fixed:\n// `{ error }` with a sentence a person can act on. The front end reads `.error`\n// and nothing else.\nexport function sendError(res: ServerResponse, status: number, message: string) {\n sendJSON(res, status, { error: message });\n}\n\nexport function sendText(res: ServerResponse, status: number, text: string) {\n res.writeHead(status, { 'content-type': 'text/plain; charset=utf-8' });\n res.end(text);\n}\n"]}
|
package/server/httpGuards.js
CHANGED
|
@@ -6,22 +6,18 @@
|
|
|
6
6
|
// authenticate. Both exist to stop some *other* page the user happens to have
|
|
7
7
|
// open from driving this server on their behalf. Binding to 127.0.0.1 keeps
|
|
8
8
|
// the network out; it does nothing about the browser, which is already inside.
|
|
9
|
-
|
|
10
9
|
import net from 'node:net';
|
|
11
|
-
|
|
12
10
|
const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
|
|
13
|
-
|
|
14
11
|
// "localhost:4310" -> "localhost", "[::1]:4310" -> "[::1]"
|
|
15
12
|
function hostnameOf(hostHeader) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
const raw = String(hostHeader ?? '').trim().toLowerCase();
|
|
14
|
+
if (raw.startsWith('[')) {
|
|
15
|
+
const close = raw.indexOf(']');
|
|
16
|
+
return close === -1 ? raw : raw.slice(0, close + 1);
|
|
17
|
+
}
|
|
18
|
+
const colon = raw.lastIndexOf(':');
|
|
19
|
+
return colon === -1 ? raw : raw.slice(0, colon);
|
|
23
20
|
}
|
|
24
|
-
|
|
25
21
|
// DNS rebinding, the attack this server's loopback bind does not cover: a page
|
|
26
22
|
// on evil.com whose DNS answer flips to 127.0.0.1 reaches us anyway, and from
|
|
27
23
|
// then on the browser treats evil.com as the same origin as everything we
|
|
@@ -38,25 +34,31 @@ function hostnameOf(hostHeader) {
|
|
|
38
34
|
//
|
|
39
35
|
// Returns null when the request is fine, or a short reason when it isn't.
|
|
40
36
|
export function rebindingReason(req, extraHosts = []) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
const host = req.headers?.host;
|
|
38
|
+
if (!host)
|
|
39
|
+
return 'The request arrived with no Host header.';
|
|
40
|
+
const name = hostnameOf(host);
|
|
41
|
+
if (LOOPBACK_HOSTS.has(name))
|
|
42
|
+
return null;
|
|
43
|
+
for (const extra of extraHosts) {
|
|
44
|
+
if (extra === 'ip:*' && net.isIP(name.replace(/^\[|\]$/g, '')) > 0)
|
|
45
|
+
return null;
|
|
46
|
+
if (extra === 'ipv4:*' && net.isIP(name) === 4)
|
|
47
|
+
return null;
|
|
48
|
+
if (extra === 'ipv6:*' && net.isIP(name.replace(/^\[|\]$/g, '')) === 6)
|
|
49
|
+
return null;
|
|
50
|
+
if (extra.startsWith('*.')) {
|
|
51
|
+
// "*.trycloudflare.com" matches "abc.trycloudflare.com" but not the bare
|
|
52
|
+
// suffix itself and not "evil-trycloudflare.com".
|
|
53
|
+
if (name.endsWith(extra.slice(1)) && name.length > extra.length)
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
else if (name === extra) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
|
-
|
|
57
|
-
return `It arrived addressed to ${name}.`;
|
|
60
|
+
return `It arrived addressed to ${name}.`;
|
|
58
61
|
}
|
|
59
|
-
|
|
60
62
|
// Cross-site requests: any page in the browser can POST here without being
|
|
61
63
|
// able to read the answer, and "without being able to read the answer" is not
|
|
62
64
|
// the same as harmless — /api/scan walks a directory the caller names, and
|
|
@@ -74,19 +76,21 @@ export function rebindingReason(req, extraHosts = []) {
|
|
|
74
76
|
// A request carrying neither header isn't a browser at all — curl, a test, a
|
|
75
77
|
// script — and there is no cross-site story for those, so they pass.
|
|
76
78
|
export function crossOriginReason(req) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
const site = req.headers?.['sec-fetch-site'];
|
|
80
|
+
if (site && site !== 'same-origin' && site !== 'none') {
|
|
81
|
+
return `The browser reported it as ${site}.`;
|
|
82
|
+
}
|
|
83
|
+
const origin = req.headers?.origin;
|
|
84
|
+
if (origin) {
|
|
85
|
+
// The host was just vetted by the rebinding check, so the origin only has
|
|
86
|
+
// to agree with it. Both schemes are accepted: a self-hosted server behind
|
|
87
|
+
// a tunnel is reached over https while the page's fetches still arrive
|
|
88
|
+
// here addressed to the same host the tunnel forwarded.
|
|
89
|
+
const host = String(req.headers.host ?? '').trim().toLowerCase();
|
|
90
|
+
const expected = new Set(['http://' + host, 'https://' + host]);
|
|
91
|
+
if (!expected.has(String(origin).trim().toLowerCase()))
|
|
92
|
+
return `It came from ${origin}.`;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
92
95
|
}
|
|
96
|
+
//# sourceMappingURL=httpGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpGuards.js","sourceRoot":"","sources":["../src/server/httpGuards.ts"],"names":[],"mappings":"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","sourcesContent":["// Two questions worth asking about a request before it is allowed to do\n// anything: did it arrive at a name that really is this machine, and did it\n// come from this app's own page?\n//\n// Neither is authentication — a single-user local tool has nobody to\n// authenticate. Both exist to stop some *other* page the user happens to have\n// open from driving this server on their behalf. Binding to 127.0.0.1 keeps\n// the network out; it does nothing about the browser, which is already inside.\n\nimport net from 'node:net';\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n\n// \"localhost:4310\" -> \"localhost\", \"[::1]:4310\" -> \"[::1]\"\nfunction hostnameOf(hostHeader) {\n const raw = String(hostHeader ?? '').trim().toLowerCase();\n if (raw.startsWith('[')) {\n const close = raw.indexOf(']');\n return close === -1 ? raw : raw.slice(0, close + 1);\n }\n const colon = raw.lastIndexOf(':');\n return colon === -1 ? raw : raw.slice(0, colon);\n}\n\n// DNS rebinding, the attack this server's loopback bind does not cover: a page\n// on evil.com whose DNS answer flips to 127.0.0.1 reaches us anyway, and from\n// then on the browser treats evil.com as the same origin as everything we\n// return — so the origin check below is looking at `same-origin` and sees\n// nothing wrong. What still gives it away is the Host header, which the page\n// cannot set and which still says evil.com.\n//\n// `extraHosts` is the self-hosted escape hatch: the domain and bind address\n// from the settings file, so a server that was deliberately put on the network\n// answers to its own name and nothing else. A local server passes nothing and\n// keeps the loopback-only rule. Entries may be exact names or `*.suffix`\n// wildcards — enabled tunnels put their random upstream names here, which\n// cannot be known ahead of time.\n//\n// Returns null when the request is fine, or a short reason when it isn't.\nexport function rebindingReason(req, extraHosts = []) {\n const host = req.headers?.host;\n if (!host) return 'The request arrived with no Host header.';\n const name = hostnameOf(host);\n if (LOOPBACK_HOSTS.has(name)) return null;\n for (const extra of extraHosts) {\n if (extra === 'ip:*' && net.isIP(name.replace(/^\\[|\\]$/g, '')) > 0) return null;\n if (extra === 'ipv4:*' && net.isIP(name) === 4) return null;\n if (extra === 'ipv6:*' && net.isIP(name.replace(/^\\[|\\]$/g, '')) === 6) return null;\n if (extra.startsWith('*.')) {\n // \"*.trycloudflare.com\" matches \"abc.trycloudflare.com\" but not the bare\n // suffix itself and not \"evil-trycloudflare.com\".\n if (name.endsWith(extra.slice(1)) && name.length > extra.length) return null;\n } else if (name === extra) {\n return null;\n }\n }\n return `It arrived addressed to ${name}.`;\n}\n\n// Cross-site requests: any page in the browser can POST here without being\n// able to read the answer, and \"without being able to read the answer\" is not\n// the same as harmless — /api/scan walks a directory the caller names, and\n// /api/explain will POST to any URL the caller names.\n//\n// Two signals, and a request only has to fail one to be refused:\n//\n// Sec-Fetch-Site set by the browser, unsettable by page script. `none`\n// means the user did it themselves — typed the URL, opened\n// a bookmark — which no attacking page can arrange.\n// Origin sent on every fetch that isn't GET/HEAD. Compared against\n// this server's own origin, which is `http://` plus the Host\n// we just confirmed is loopback.\n//\n// A request carrying neither header isn't a browser at all — curl, a test, a\n// script — and there is no cross-site story for those, so they pass.\nexport function crossOriginReason(req) {\n const site = req.headers?.['sec-fetch-site'];\n if (site && site !== 'same-origin' && site !== 'none') {\n return `The browser reported it as ${site}.`;\n }\n const origin = req.headers?.origin;\n if (origin) {\n // The host was just vetted by the rebinding check, so the origin only has\n // to agree with it. Both schemes are accepted: a self-hosted server behind\n // a tunnel is reached over https while the page's fetches still arrive\n // here addressed to the same host the tunnel forwarded.\n const host = String(req.headers.host ?? '').trim().toLowerCase();\n const expected = new Set(['http://' + host, 'https://' + host]);\n if (!expected.has(String(origin).trim().toLowerCase())) return `It came from ${origin}.`;\n }\n return null;\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare function caddyfilePath(configFile?: string): string;
|
|
2
|
+
export declare function renderCaddyfile(settings: any): string;
|
|
3
|
+
export declare function httpsReadiness(settings: any, { configFile, resolve, probe, tool, running }?: Record<string, any>): Promise<{
|
|
4
|
+
ok: boolean;
|
|
5
|
+
checks: {
|
|
6
|
+
id: string;
|
|
7
|
+
ok: boolean;
|
|
8
|
+
required: boolean;
|
|
9
|
+
detail: string;
|
|
10
|
+
}[];
|
|
11
|
+
caddy: any;
|
|
12
|
+
addresses: any[];
|
|
13
|
+
local: any[];
|
|
14
|
+
}>;
|
|
15
|
+
export declare function httpsStatus(settings: any, configFile?: string): {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
domain: string;
|
|
18
|
+
url: string;
|
|
19
|
+
caddyfile: string;
|
|
20
|
+
caddyInstalled: boolean;
|
|
21
|
+
caddyVersion: string;
|
|
22
|
+
configured: boolean;
|
|
23
|
+
command: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function writeCaddyfile(settings: any, configFile?: string): Promise<string>;
|
|
26
|
+
export declare function caddyValidate(settings: any, configFile?: string): Promise<{
|
|
27
|
+
file: string;
|
|
28
|
+
output: string;
|
|
29
|
+
}>;
|
|
30
|
+
export declare function caddyRun(settings: any, configFile?: string, action?: string): {
|
|
31
|
+
action: string;
|
|
32
|
+
file: string;
|
|
33
|
+
output: string;
|
|
34
|
+
};
|
package/server/https.js
CHANGED
|
@@ -4,139 +4,149 @@ import net from 'node:net';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { spawnSync } from 'node:child_process';
|
|
7
|
-
|
|
8
7
|
import { configPath, normalizeSettings } from './config.js';
|
|
9
8
|
import { findOnPath, installHint } from './tunnel.js';
|
|
10
|
-
|
|
11
9
|
export function caddyfilePath(configFile = configPath()) {
|
|
12
|
-
|
|
10
|
+
return path.join(path.dirname(configFile), 'Caddyfile');
|
|
13
11
|
}
|
|
14
|
-
|
|
15
12
|
export function renderCaddyfile(settings) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const s = normalizeSettings(settings);
|
|
14
|
+
if (!s.domain)
|
|
15
|
+
throw new Error('HTTPS needs a domain. Run `onboarder setup` and enter one first.');
|
|
16
|
+
return `# Managed by Onboarder. Edits are replaced by \`onboarder https setup\`.\n${s.domain} {\n reverse_proxy 127.0.0.1:${s.port}\n}\n`;
|
|
19
17
|
}
|
|
20
|
-
|
|
21
18
|
function portIsFree(port, host = '0.0.0.0') {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const probe = net.createServer();
|
|
21
|
+
probe.once('error', () => resolve(false));
|
|
22
|
+
probe.listen(port, host, () => probe.close(() => resolve(true)));
|
|
23
|
+
});
|
|
27
24
|
}
|
|
28
|
-
|
|
29
25
|
async function resolveDomain(domain) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
const [v4, v6] = await Promise.allSettled([dns.resolve4(domain), dns.resolve6(domain)]);
|
|
27
|
+
const addresses = [
|
|
28
|
+
...(v4.status === 'fulfilled' ? v4.value : []),
|
|
29
|
+
...(v6.status === 'fulfilled' ? v6.value : []),
|
|
30
|
+
];
|
|
31
|
+
return [...new Set(addresses)];
|
|
36
32
|
}
|
|
37
|
-
|
|
38
33
|
function localAddresses() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const found = [];
|
|
35
|
+
for (const list of Object.values(os.networkInterfaces())) {
|
|
36
|
+
for (const address of list || [])
|
|
37
|
+
if (!address.internal)
|
|
38
|
+
found.push(address.address);
|
|
39
|
+
}
|
|
40
|
+
return found;
|
|
44
41
|
}
|
|
45
|
-
|
|
46
42
|
async function caddyIsRunning(probe = portIsFree) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
// If 80 and 443 are both occupied, probe Caddy's local admin endpoint. This
|
|
44
|
+
// distinguishes the Ubuntu service from an unknown listener without sudo.
|
|
45
|
+
if (await probe(80))
|
|
46
|
+
return false;
|
|
47
|
+
if (await probe(443))
|
|
48
|
+
return false;
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
const socket = net.connect(2019, '127.0.0.1');
|
|
51
|
+
const finish = (value) => { socket.destroy(); resolve(value); };
|
|
52
|
+
socket.setTimeout(800, () => finish(false));
|
|
53
|
+
socket.once('connect', () => finish(true));
|
|
54
|
+
socket.once('error', () => finish(false));
|
|
55
|
+
});
|
|
58
56
|
}
|
|
59
|
-
|
|
60
57
|
export async function httpsReadiness(settings, { configFile = configPath(), resolve = resolveDomain, probe = portIsFree, tool = findOnPath, running = caddyIsRunning } = {}) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
58
|
+
const s = normalizeSettings(settings);
|
|
59
|
+
const caddy = tool('caddy');
|
|
60
|
+
const checks = [{ id: 'domain', ok: Boolean(s.domain), required: true, detail: s.domain || 'not configured' }];
|
|
61
|
+
if (!s.domain)
|
|
62
|
+
return { ok: false, checks, caddy, addresses: [], local: [] };
|
|
63
|
+
let addresses = [];
|
|
64
|
+
let dnsError = '';
|
|
65
|
+
try {
|
|
66
|
+
addresses = await resolve(s.domain);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
dnsError = error.code || error.message;
|
|
70
|
+
}
|
|
71
|
+
checks.push({ id: 'dns', ok: addresses.length > 0, required: true, detail: addresses.length ? addresses.join(', ') : `does not resolve${dnsError ? ` (${dnsError})` : ''}` });
|
|
72
|
+
const local = localAddresses();
|
|
73
|
+
const matches = addresses.some((address) => local.includes(address));
|
|
74
|
+
checks.push({ id: 'dns-target', ok: matches, required: false, detail: matches ? 'resolves to this machine' : 'does not match a local interface address (NAT/public-IP and proxy setups can still be valid)' });
|
|
75
|
+
const serviceRunning = await running(probe);
|
|
76
|
+
for (const port of [80, 443]) {
|
|
77
|
+
const free = await probe(port);
|
|
78
|
+
const ownedByCaddy = !free && serviceRunning;
|
|
79
|
+
checks.push({
|
|
80
|
+
id: `port-${port}`,
|
|
81
|
+
ok: free || ownedByCaddy,
|
|
82
|
+
required: true,
|
|
83
|
+
detail: free ? 'available for Caddy' : ownedByCaddy ? 'in use by the running Caddy service' : 'already in use — stop the existing web server or proxy',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
checks.push({ id: 'caddy', ok: caddy.installed, required: true, detail: caddy.installed ? caddy.version : 'not installed — ' + installHint('caddy') });
|
|
87
|
+
return { ok: checks.every((check) => check.ok || !check.required), checks, caddy, addresses, local };
|
|
87
88
|
}
|
|
88
|
-
|
|
89
89
|
export function httpsStatus(settings, configFile = configPath()) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
90
|
+
const s = normalizeSettings(settings);
|
|
91
|
+
const file = caddyfilePath(configFile);
|
|
92
|
+
const caddy = findOnPath('caddy');
|
|
93
|
+
let configured = false;
|
|
94
|
+
try {
|
|
95
|
+
configured = Boolean(statSync(file));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
configured = false;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
enabled: Boolean(s.domain && s.https),
|
|
102
|
+
domain: s.domain,
|
|
103
|
+
url: s.domain && s.https ? `https://${s.domain}` : '',
|
|
104
|
+
caddyfile: file,
|
|
105
|
+
caddyInstalled: caddy.installed,
|
|
106
|
+
caddyVersion: caddy.version || '',
|
|
107
|
+
configured,
|
|
108
|
+
command: s.domain && s.https ? `caddy start --config ${file}` : '',
|
|
109
|
+
};
|
|
105
110
|
}
|
|
106
|
-
|
|
107
111
|
export async function writeCaddyfile(settings, configFile = configPath()) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const file = caddyfilePath(configFile);
|
|
113
|
+
await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
114
|
+
await fs.writeFile(file, renderCaddyfile(settings), { encoding: 'utf8', mode: 0o600 });
|
|
115
|
+
return file;
|
|
112
116
|
}
|
|
113
|
-
|
|
114
117
|
export async function caddyValidate(settings, configFile = configPath()) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
const s = normalizeSettings(settings);
|
|
119
|
+
if (!s.domain)
|
|
120
|
+
throw new Error('HTTPS needs a domain. Run `onboarder setup` and enter one first.');
|
|
121
|
+
const caddy = findOnPath('caddy');
|
|
122
|
+
if (!caddy.installed)
|
|
123
|
+
throw new Error('Caddy is not installed. ' + installHint('caddy'));
|
|
124
|
+
const file = caddyfilePath(configFile);
|
|
125
|
+
const result = spawnSync('caddy', ['validate', '--config', file], { encoding: 'utf8' });
|
|
126
|
+
if (result.error)
|
|
127
|
+
throw result.error;
|
|
128
|
+
if (result.status !== 0)
|
|
129
|
+
throw new Error(String(result.stderr || result.stdout || 'Caddy configuration is invalid.').trim());
|
|
130
|
+
return { file, output: String(result.stdout || result.stderr || '').trim() };
|
|
124
131
|
}
|
|
125
|
-
|
|
126
132
|
export function caddyRun(settings, configFile = configPath(), action = 'start') {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
const s = normalizeSettings(settings);
|
|
134
|
+
if (!s.domain || !s.https)
|
|
135
|
+
throw new Error('Enable HTTPS with a domain first: `onboarder setup`.');
|
|
136
|
+
const caddy = findOnPath('caddy');
|
|
137
|
+
if (!caddy.installed)
|
|
138
|
+
throw new Error('Caddy is not installed. ' + installHint('caddy'));
|
|
139
|
+
const file = caddyfilePath(configFile);
|
|
140
|
+
const result = spawnSync('caddy', [action, '--config', file], { encoding: 'utf8' });
|
|
141
|
+
if (result.error)
|
|
142
|
+
throw result.error;
|
|
143
|
+
if (result.status !== 0) {
|
|
144
|
+
const message = String(result.stderr || result.stdout || `caddy ${action} failed`).trim();
|
|
145
|
+
if (/permission denied|address already in use/i.test(message) && process.platform !== 'win32') {
|
|
146
|
+
throw new Error(`${message}\nStart the packaged service with: sudo systemctl enable --now caddy`);
|
|
147
|
+
}
|
|
148
|
+
throw new Error(message);
|
|
138
149
|
}
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
return { action, file, output: String(result.stdout || result.stderr || '').trim() };
|
|
150
|
+
return { action, file, output: String(result.stdout || result.stderr || '').trim() };
|
|
142
151
|
}
|
|
152
|
+
//# sourceMappingURL=https.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"https.js","sourceRoot":"","sources":["../src/server/https.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as dns } from 'node:dns';\nimport { promises as fs, statSync } from 'node:fs';\nimport net from 'node:net';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { spawnSync } from 'node:child_process';\n\nimport { configPath, normalizeSettings } from './config.js';\nimport { findOnPath, installHint } from './tunnel.js';\n\nexport function caddyfilePath(configFile = configPath()) {\n return path.join(path.dirname(configFile), 'Caddyfile');\n}\n\nexport function renderCaddyfile(settings) {\n const s = normalizeSettings(settings);\n if (!s.domain) throw new Error('HTTPS needs a domain. Run `onboarder setup` and enter one first.');\n return `# Managed by Onboarder. Edits are replaced by \\`onboarder https setup\\`.\\n${s.domain} {\\n reverse_proxy 127.0.0.1:${s.port}\\n}\\n`;\n}\n\nfunction portIsFree(port, host = '0.0.0.0') {\n return new Promise((resolve) => {\n const probe = net.createServer();\n probe.once('error', () => resolve(false));\n probe.listen(port, host, () => probe.close(() => resolve(true)));\n });\n}\n\nasync function resolveDomain(domain) {\n const [v4, v6] = await Promise.allSettled([dns.resolve4(domain), dns.resolve6(domain)]);\n const addresses = [\n ...(v4.status === 'fulfilled' ? v4.value : []),\n ...(v6.status === 'fulfilled' ? v6.value : []),\n ];\n return [...new Set(addresses)];\n}\n\nfunction localAddresses() {\n const found = [];\n for (const list of Object.values(os.networkInterfaces())) {\n for (const address of list || []) if (!address.internal) found.push(address.address);\n }\n return found;\n}\n\nasync function caddyIsRunning(probe = portIsFree) {\n // If 80 and 443 are both occupied, probe Caddy's local admin endpoint. This\n // distinguishes the Ubuntu service from an unknown listener without sudo.\n if (await probe(80)) return false;\n if (await probe(443)) return false;\n return new Promise((resolve) => {\n const socket = net.connect(2019, '127.0.0.1');\n const finish = (value) => { socket.destroy(); resolve(value); };\n socket.setTimeout(800, () => finish(false));\n socket.once('connect', () => finish(true));\n socket.once('error', () => finish(false));\n });\n}\n\nexport async function httpsReadiness(settings, { configFile = configPath(), resolve = resolveDomain, probe = portIsFree, tool = findOnPath, running = caddyIsRunning }: Record<string, any> = {}) {\n const s = normalizeSettings(settings);\n const caddy = tool('caddy');\n const checks = [{ id: 'domain', ok: Boolean(s.domain), required: true, detail: s.domain || 'not configured' }];\n if (!s.domain) return { ok: false, checks, caddy, addresses: [], local: [] };\n\n let addresses = [];\n let dnsError = '';\n try { addresses = await resolve(s.domain); } catch (error) { dnsError = error.code || error.message; }\n checks.push({ id: 'dns', ok: addresses.length > 0, required: true, detail: addresses.length ? addresses.join(', ') : `does not resolve${dnsError ? ` (${dnsError})` : ''}` });\n const local = localAddresses();\n const matches = addresses.some((address) => local.includes(address));\n checks.push({ id: 'dns-target', ok: matches, required: false, detail: matches ? 'resolves to this machine' : 'does not match a local interface address (NAT/public-IP and proxy setups can still be valid)' });\n\n const serviceRunning = await running(probe);\n for (const port of [80, 443]) {\n const free = await probe(port);\n const ownedByCaddy = !free && serviceRunning;\n checks.push({\n id: `port-${port}`,\n ok: free || ownedByCaddy,\n required: true,\n detail: free ? 'available for Caddy' : ownedByCaddy ? 'in use by the running Caddy service' : 'already in use — stop the existing web server or proxy',\n });\n }\n checks.push({ id: 'caddy', ok: caddy.installed, required: true, detail: caddy.installed ? caddy.version : 'not installed — ' + installHint('caddy') });\n return { ok: checks.every((check) => check.ok || !check.required), checks, caddy, addresses, local };\n}\n\nexport function httpsStatus(settings, configFile = configPath()) {\n const s = normalizeSettings(settings);\n const file = caddyfilePath(configFile);\n const caddy = findOnPath('caddy');\n let configured = false;\n try { configured = Boolean(statSync(file)); } catch { configured = false; }\n return {\n enabled: Boolean(s.domain && s.https),\n domain: s.domain,\n url: s.domain && s.https ? `https://${s.domain}` : '',\n caddyfile: file,\n caddyInstalled: caddy.installed,\n caddyVersion: caddy.version || '',\n configured,\n command: s.domain && s.https ? `caddy start --config ${file}` : '',\n };\n}\n\nexport async function writeCaddyfile(settings, configFile = configPath()) {\n const file = caddyfilePath(configFile);\n await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });\n await fs.writeFile(file, renderCaddyfile(settings), { encoding: 'utf8', mode: 0o600 });\n return file;\n}\n\nexport async function caddyValidate(settings, configFile = configPath()) {\n const s = normalizeSettings(settings);\n if (!s.domain) throw new Error('HTTPS needs a domain. Run `onboarder setup` and enter one first.');\n const caddy = findOnPath('caddy');\n if (!caddy.installed) throw new Error('Caddy is not installed. ' + installHint('caddy'));\n const file = caddyfilePath(configFile);\n const result = spawnSync('caddy', ['validate', '--config', file], { encoding: 'utf8' });\n if (result.error) throw result.error;\n if (result.status !== 0) throw new Error(String(result.stderr || result.stdout || 'Caddy configuration is invalid.').trim());\n return { file, output: String(result.stdout || result.stderr || '').trim() };\n}\n\nexport function caddyRun(settings, configFile = configPath(), action = 'start') {\n const s = normalizeSettings(settings);\n if (!s.domain || !s.https) throw new Error('Enable HTTPS with a domain first: `onboarder setup`.');\n const caddy = findOnPath('caddy');\n if (!caddy.installed) throw new Error('Caddy is not installed. ' + installHint('caddy'));\n const file = caddyfilePath(configFile);\n const result = spawnSync('caddy', [action, '--config', file], { encoding: 'utf8' });\n if (result.error) throw result.error;\n if (result.status !== 0) {\n const message = String(result.stderr || result.stdout || `caddy ${action} failed`).trim();\n if (/permission denied|address already in use/i.test(message) && process.platform !== 'win32') {\n throw new Error(`${message}\\nStart the packaged service with: sudo systemctl enable --now caddy`);\n }\n throw new Error(message);\n }\n return { action, file, output: String(result.stdout || result.stderr || '').trim() };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
export declare const CONFIG: {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
publicDir: string;
|
|
5
|
+
sharedDir: string;
|
|
6
|
+
mcp: import("./mcp/runner.js").McpRunner;
|
|
7
|
+
};
|
|
8
|
+
export declare function createServer(config?: typeof CONFIG & {
|
|
9
|
+
configPath?: string;
|
|
10
|
+
getSettings?: () => Promise<import('../shared/contracts.js').Settings>;
|
|
11
|
+
boot?: {
|
|
12
|
+
host: string;
|
|
13
|
+
port: number;
|
|
14
|
+
domain: string;
|
|
15
|
+
https: boolean;
|
|
16
|
+
};
|
|
17
|
+
}): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
18
|
+
export declare function startupBanner(settings: any, { configFile, columns }?: Record<string, any>): string;
|
|
19
|
+
export declare function openInBrowser(url: any): void;
|
|
20
|
+
export declare function listenError(error: any, { host, port, pid }?: Record<string, any>): any;
|
|
21
|
+
export declare function startServer({ configFile, openBrowser, log }?: Record<string, any>): Promise<{
|
|
22
|
+
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
23
|
+
settings: {
|
|
24
|
+
version: number;
|
|
25
|
+
mode: 'local' | 'self-hosted';
|
|
26
|
+
domain: string;
|
|
27
|
+
https: boolean;
|
|
28
|
+
accessKey: string;
|
|
29
|
+
autoOpen: boolean;
|
|
30
|
+
account: import("../shared/contracts.js").AccountSettings;
|
|
31
|
+
tunnel: {
|
|
32
|
+
cloudflare: boolean;
|
|
33
|
+
tailscale: boolean;
|
|
34
|
+
};
|
|
35
|
+
host: string;
|
|
36
|
+
port: number;
|
|
37
|
+
};
|
|
38
|
+
host: string;
|
|
39
|
+
port: number;
|
|
40
|
+
}>;
|