codebase-onboarder 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -46
- package/bin/build-if-source.js +23 -0
- package/cli/commands.d.ts +26 -0
- package/cli/commands.js +836 -811
- package/cli/commands.js.map +1 -0
- package/cli/explorer/advanced.d.ts +14 -0
- package/cli/explorer/advanced.js +321 -329
- package/cli/explorer/advanced.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +384 -313
- package/cli/explorer/app.js.map +1 -0
- package/cli/explorer/assistant.d.ts +9 -0
- package/cli/explorer/assistant.js +58 -0
- package/cli/explorer/assistant.js.map +1 -0
- package/cli/explorer/commands.d.ts +17 -0
- package/cli/explorer/commands.js +368 -302
- package/cli/explorer/commands.js.map +1 -0
- package/cli/explorer/featureViews.d.ts +124 -0
- package/cli/explorer/featureViews.js +179 -0
- package/cli/explorer/featureViews.js.map +1 -0
- package/cli/explorer/github.d.ts +21 -0
- package/cli/explorer/github.js +42 -44
- package/cli/explorer/github.js.map +1 -0
- package/cli/explorer/graphs.d.ts +7 -0
- package/cli/explorer/graphs.js +90 -95
- package/cli/explorer/graphs.js.map +1 -0
- package/cli/explorer/picker.d.ts +22 -0
- package/cli/explorer/picker.js +150 -0
- package/cli/explorer/picker.js.map +1 -0
- package/cli/explorer/report.d.ts +85 -0
- package/cli/explorer/report.js +60 -0
- package/cli/explorer/report.js.map +1 -0
- package/cli/explorer/session.d.ts +449 -0
- package/cli/explorer/session.js +143 -144
- package/cli/explorer/session.js.map +1 -0
- package/cli/explorer/views.d.ts +19 -0
- package/cli/explorer/views.js +465 -482
- package/cli/explorer/views.js.map +1 -0
- package/cli/explorer/wrap.d.ts +1 -0
- package/cli/explorer/wrap.js +28 -44
- package/cli/explorer/wrap.js.map +1 -0
- package/cli/main.d.ts +1 -0
- package/cli/main.js +214 -161
- package/cli/main.js.map +1 -0
- package/cli/prompt.d.ts +5 -0
- package/cli/prompt.js +84 -80
- package/cli/prompt.js.map +1 -0
- package/cli/scanOptions.d.ts +2 -0
- package/cli/scanOptions.js +11 -0
- package/cli/scanOptions.js.map +1 -0
- package/cli/ui.d.ts +33 -0
- package/cli/ui.js +35 -44
- package/cli/ui.js.map +1 -0
- package/cli/wizard.d.ts +187 -0
- package/cli/wizard.js +304 -284
- package/cli/wizard.js.map +1 -0
- package/package.json +15 -4
- package/public/app.d.ts +1 -0
- package/public/app.js +1057 -1009
- package/public/app.js.map +1 -0
- package/public/browser.d.ts +68 -0
- package/public/browser.js +2 -0
- package/public/browser.js.map +1 -0
- package/public/index.html +30 -13
- package/public/js/about.d.ts +6 -0
- package/public/js/about.js +152 -172
- package/public/js/about.js.map +1 -0
- package/public/js/aiDraft.d.ts +3 -0
- package/public/js/aiDraft.js +116 -124
- package/public/js/aiDraft.js.map +1 -0
- package/public/js/analysisPanel.d.ts +7 -0
- package/public/js/analysisPanel.js +166 -166
- package/public/js/analysisPanel.js.map +1 -0
- package/public/js/analysisReport.d.ts +38 -0
- package/public/js/analysisReport.js +105 -110
- package/public/js/analysisReport.js.map +1 -0
- package/public/js/api.d.ts +26 -0
- package/public/js/api.js +176 -167
- package/public/js/api.js.map +1 -0
- package/public/js/atlas.d.ts +6 -0
- package/public/js/atlas.js +50 -58
- package/public/js/atlas.js.map +1 -0
- package/public/js/blameView.d.ts +2 -0
- package/public/js/blameView.js +36 -37
- package/public/js/blameView.js.map +1 -0
- package/public/js/codeTab.d.ts +3 -0
- package/public/js/codeTab.js +175 -180
- package/public/js/codeTab.js.map +1 -0
- package/public/js/codeViewer.d.ts +5 -0
- package/public/js/codeViewer.js +162 -150
- package/public/js/codeViewer.js.map +1 -0
- package/public/js/components/FileChip.d.ts +9 -0
- package/public/js/components/FileChip.js +45 -49
- package/public/js/components/FileChip.js.map +1 -0
- package/public/js/components/MetricSparkline.d.ts +7 -0
- package/public/js/components/MetricSparkline.js +35 -44
- package/public/js/components/MetricSparkline.js.map +1 -0
- package/public/js/components/RiskBadge.d.ts +9 -0
- package/public/js/components/RiskBadge.js +55 -63
- package/public/js/components/RiskBadge.js.map +1 -0
- package/public/js/deepAnalysisView.d.ts +99 -0
- package/public/js/deepAnalysisView.js +282 -302
- package/public/js/deepAnalysisView.js.map +1 -0
- package/public/js/diagramPane.d.ts +8 -0
- package/public/js/diagramPane.js +133 -135
- package/public/js/diagramPane.js.map +1 -0
- package/public/js/diffView.d.ts +6 -0
- package/public/js/diffView.js +222 -209
- package/public/js/diffView.js.map +1 -0
- package/public/js/docsView.d.ts +2 -0
- package/public/js/docsView.js +441 -442
- package/public/js/docsView.js.map +1 -0
- package/public/js/dom.d.ts +189 -0
- package/public/js/dom.js +5 -0
- package/public/js/dom.js.map +1 -0
- package/public/js/fileSourceBrowser.d.ts +5 -0
- package/public/js/fileSourceBrowser.js +33 -41
- package/public/js/fileSourceBrowser.js.map +1 -0
- package/public/js/flameGraph.d.ts +5 -0
- package/public/js/flameGraph.js +62 -72
- package/public/js/flameGraph.js.map +1 -0
- package/public/js/forceGraph.d.ts +10 -0
- package/public/js/forceGraph.js +581 -556
- package/public/js/forceGraph.js.map +1 -0
- package/public/js/heatmap.d.ts +4 -0
- package/public/js/heatmap.js +142 -164
- package/public/js/heatmap.js.map +1 -0
- package/public/js/highlight.d.ts +2 -0
- package/public/js/highlight.js +116 -107
- package/public/js/highlight.js.map +1 -0
- package/public/js/html.d.ts +2 -0
- package/public/js/html.js +14 -44
- package/public/js/html.js.map +1 -0
- package/public/js/insightsView.d.ts +5 -0
- package/public/js/insightsView.js +93 -111
- package/public/js/insightsView.js.map +1 -0
- package/public/js/inspector.d.ts +37 -0
- package/public/js/inspector.js +416 -445
- package/public/js/inspector.js.map +1 -0
- package/public/js/llm.d.ts +113 -0
- package/public/js/llm.js +233 -272
- package/public/js/llm.js.map +1 -0
- package/public/js/mapView.d.ts +3 -0
- package/public/js/mapView.js +121 -120
- package/public/js/mapView.js.map +1 -0
- package/public/js/markdown.d.ts +3 -0
- package/public/js/markdown.js +84 -91
- package/public/js/markdown.js.map +1 -0
- package/public/js/mindmap.d.ts +32 -0
- package/public/js/mindmap.js +244 -259
- package/public/js/mindmap.js.map +1 -0
- package/public/js/mobilePanels.d.ts +5 -0
- package/public/js/mobilePanels.js +50 -0
- package/public/js/mobilePanels.js.map +1 -0
- package/public/js/repoFiles.d.ts +12 -0
- package/public/js/repoFiles.js +28 -27
- package/public/js/repoFiles.js.map +1 -0
- package/public/js/reviewView.d.ts +28 -0
- package/public/js/reviewView.js +256 -0
- package/public/js/reviewView.js.map +1 -0
- package/public/js/sbomView.d.ts +3 -0
- package/public/js/sbomView.js +59 -67
- package/public/js/sbomView.js.map +1 -0
- package/public/js/scanCache.d.ts +16 -0
- package/public/js/scanCache.js +67 -65
- package/public/js/scanCache.js.map +1 -0
- package/public/js/search.d.ts +7 -0
- package/public/js/search.js +868 -800
- package/public/js/search.js.map +1 -0
- package/public/js/searchLocal.d.ts +42 -0
- package/public/js/searchLocal.js +87 -0
- package/public/js/searchLocal.js.map +1 -0
- package/public/js/serverSettings.d.ts +5 -0
- package/public/js/serverSettings.js +153 -164
- package/public/js/serverSettings.js.map +1 -0
- package/public/js/state.d.ts +798 -0
- package/public/js/state.js +114 -122
- package/public/js/state.js.map +1 -0
- package/public/js/tour.d.ts +6 -0
- package/public/js/tour.js +5 -7
- package/public/js/tour.js.map +1 -0
- package/public/js/transitions.d.ts +2 -0
- package/public/js/transitions.js +9 -10
- package/public/js/transitions.js.map +1 -0
- package/public/js/tree.d.ts +12 -0
- package/public/js/tree.js +157 -154
- package/public/js/tree.js.map +1 -0
- package/public/js/workflowsView.d.ts +3 -0
- package/public/js/workflowsView.js +15 -22
- package/public/js/workflowsView.js.map +1 -0
- package/public/search.css +495 -0
- package/public/styles.css +163 -346
- package/public/workbench.css +166 -0
- package/server/apiAuth.d.ts +3 -0
- package/server/apiAuth.js +18 -20
- package/server/apiAuth.js.map +1 -0
- package/server/apiDiff.d.ts +2 -0
- package/server/apiDiff.js +20 -18
- package/server/apiDiff.js.map +1 -0
- package/server/apiDocs.d.ts +2 -0
- package/server/apiDocs.js +38 -40
- package/server/apiDocs.js.map +1 -0
- package/server/apiFile.d.ts +1 -0
- package/server/apiFile.js +14 -17
- package/server/apiFile.js.map +1 -0
- package/server/apiGitBlame.d.ts +2 -0
- package/server/apiGitBlame.js +50 -51
- package/server/apiGitBlame.js.map +1 -0
- package/server/apiMcp.d.ts +4 -0
- package/server/apiMcp.js +64 -66
- package/server/apiMcp.js.map +1 -0
- package/server/apiReview.d.ts +2 -0
- package/server/apiReview.js +45 -0
- package/server/apiReview.js.map +1 -0
- package/server/apiScan.d.ts +5 -0
- package/server/apiScan.js +90 -71
- package/server/apiScan.js.map +1 -0
- package/server/apiSearch.d.ts +33 -0
- package/server/apiSearch.js +184 -200
- package/server/apiSearch.js.map +1 -0
- package/server/apiSettings.d.ts +4 -0
- package/server/apiSettings.js +84 -85
- package/server/apiSettings.js.map +1 -0
- package/server/apiTools.d.ts +3 -0
- package/server/apiTools.js +55 -56
- package/server/apiTools.js.map +1 -0
- package/server/auth.d.ts +5 -0
- package/server/auth.js +54 -51
- package/server/auth.js.map +1 -0
- package/server/config.d.ts +42 -0
- package/server/config.js +189 -194
- package/server/config.js.map +1 -0
- package/server/daemon.d.ts +12 -0
- package/server/daemon.js +124 -114
- package/server/daemon.js.map +1 -0
- package/server/fileSourceNode.d.ts +2 -0
- package/server/fileSourceNode.js +34 -36
- package/server/fileSourceNode.js.map +1 -0
- package/server/gitClone.d.ts +9 -0
- package/server/gitClone.js +59 -61
- package/server/gitClone.js.map +1 -0
- package/server/gitDiff.d.ts +36 -0
- package/server/gitDiff.js +271 -169
- package/server/gitDiff.js.map +1 -0
- package/server/gitHistory.d.ts +12 -0
- package/server/gitHistory.js +80 -80
- package/server/gitHistory.js.map +1 -0
- package/server/gitRemote.d.ts +12 -0
- package/server/gitRemote.js +37 -38
- package/server/gitRemote.js.map +1 -0
- package/server/htmlText.d.ts +4 -0
- package/server/htmlText.js +16 -23
- package/server/htmlText.js.map +1 -0
- package/server/http.d.ts +5 -0
- package/server/http.js +27 -30
- package/server/http.js.map +1 -0
- package/server/httpGuards.d.ts +2 -0
- package/server/httpGuards.js +47 -43
- package/server/httpGuards.js.map +1 -0
- package/server/https.d.ts +34 -0
- package/server/https.js +121 -111
- package/server/https.js.map +1 -0
- package/server/index.d.ts +40 -0
- package/server/index.js +139 -147
- package/server/index.js.map +1 -0
- package/server/layout.d.ts +26 -0
- package/server/layout.js +59 -64
- package/server/layout.js.map +1 -0
- package/server/llmProxy.d.ts +3 -0
- package/server/llmProxy.js +137 -135
- package/server/llmProxy.js.map +1 -0
- package/server/logger.d.ts +38 -0
- package/server/logger.js +198 -211
- package/server/logger.js.map +1 -0
- package/server/mcp/analysis.d.ts +29 -0
- package/server/mcp/analysis.js +99 -120
- package/server/mcp/analysis.js.map +1 -0
- package/server/mcp/http.d.ts +9 -0
- package/server/mcp/http.js +128 -137
- package/server/mcp/http.js.map +1 -0
- package/server/mcp/runner.d.ts +68 -0
- package/server/mcp/runner.js +208 -218
- package/server/mcp/runner.js.map +1 -0
- package/server/mcp/server.d.ts +29 -0
- package/server/mcp/server.js +166 -179
- package/server/mcp/server.js.map +1 -0
- package/server/mcp/standalone.d.ts +1 -0
- package/server/mcp/standalone.js +8 -10
- package/server/mcp/standalone.js.map +1 -0
- package/server/mcp/tools.d.ts +2020 -0
- package/server/mcp/tools.js +523 -574
- package/server/mcp/tools.js.map +1 -0
- package/server/paths.d.ts +3 -0
- package/server/paths.js +17 -15
- package/server/paths.js.map +1 -0
- package/server/pidfile.d.ts +9 -0
- package/server/pidfile.js +43 -45
- package/server/pidfile.js.map +1 -0
- package/server/review.d.ts +5 -0
- package/server/review.js +35 -0
- package/server/review.js.map +1 -0
- package/server/router.d.ts +33 -0
- package/server/router.js +191 -197
- package/server/router.js.map +1 -0
- package/server/scanRequest.d.ts +3 -0
- package/server/scanRequest.js +36 -0
- package/server/scanRequest.js.map +1 -0
- package/server/searchIndex.d.ts +17 -0
- package/server/searchIndex.js +83 -80
- package/server/searchIndex.js.map +1 -0
- package/server/sessions.d.ts +14 -0
- package/server/sessions.js +62 -70
- package/server/sessions.js.map +1 -0
- package/server/startup.d.ts +118 -0
- package/server/startup.js +143 -148
- package/server/startup.js.map +1 -0
- package/server/static.d.ts +6 -0
- package/server/static.js +28 -31
- package/server/static.js.map +1 -0
- package/server/tools/install.d.ts +4 -0
- package/server/tools/install.js +176 -185
- package/server/tools/install.js.map +1 -0
- package/server/tools/parse.d.ts +8 -0
- package/server/tools/parse.js +112 -114
- package/server/tools/parse.js.map +1 -0
- package/server/tools/platform.d.ts +9 -0
- package/server/tools/platform.js +49 -47
- package/server/tools/platform.js.map +1 -0
- package/server/tools/registry.d.ts +137 -0
- package/server/tools/registry.js +173 -172
- package/server/tools/registry.js.map +1 -0
- package/server/tools/scan.d.ts +34 -0
- package/server/tools/scan.js +89 -98
- package/server/tools/scan.js.map +1 -0
- package/server/tools.d.ts +34 -0
- package/server/tools.js +94 -107
- package/server/tools.js.map +1 -0
- package/server/tunnel.d.ts +29 -0
- package/server/tunnel.js +61 -66
- package/server/tunnel.js.map +1 -0
- package/shared/analyzer/docs.d.ts +16 -0
- package/shared/analyzer/docs.js +99 -93
- package/shared/analyzer/docs.js.map +1 -0
- package/shared/analyzer/explainLocal.d.ts +4 -0
- package/shared/analyzer/explainLocal.js +122 -142
- package/shared/analyzer/explainLocal.js.map +1 -0
- package/shared/analyzer/github.d.ts +37 -0
- package/shared/analyzer/github.js +41 -43
- package/shared/analyzer/github.js.map +1 -0
- package/shared/analyzer/graph.d.ts +41 -0
- package/shared/analyzer/graph.js +393 -400
- package/shared/analyzer/graph.js.map +1 -0
- package/shared/analyzer/health.d.ts +33 -0
- package/shared/analyzer/health.js +197 -193
- package/shared/analyzer/health.js.map +1 -0
- package/shared/analyzer/history.d.ts +29 -0
- package/shared/analyzer/history.js +110 -111
- package/shared/analyzer/history.js.map +1 -0
- package/shared/analyzer/jsonConfig.d.ts +2 -0
- package/shared/analyzer/jsonConfig.js +61 -0
- package/shared/analyzer/jsonConfig.js.map +1 -0
- package/shared/analyzer/languages/csharp.d.ts +26 -0
- package/shared/analyzer/languages/csharp.js +83 -88
- package/shared/analyzer/languages/csharp.js.map +1 -0
- package/shared/analyzer/languages/generic.d.ts +23 -0
- package/shared/analyzer/languages/generic.js +94 -105
- package/shared/analyzer/languages/generic.js.map +1 -0
- package/shared/analyzer/languages/go.d.ts +24 -0
- package/shared/analyzer/languages/go.js +45 -54
- package/shared/analyzer/languages/go.js.map +1 -0
- package/shared/analyzer/languages/index.d.ts +6 -0
- package/shared/analyzer/languages/index.js +19 -20
- package/shared/analyzer/languages/index.js.map +1 -0
- package/shared/analyzer/languages/java.d.ts +20 -0
- package/shared/analyzer/languages/java.js +73 -78
- package/shared/analyzer/languages/java.js.map +1 -0
- package/shared/analyzer/languages/javascript.d.ts +12 -0
- package/shared/analyzer/languages/javascript.js +258 -209
- package/shared/analyzer/languages/javascript.js.map +1 -0
- package/shared/analyzer/languages/python.d.ts +23 -0
- package/shared/analyzer/languages/python.js +92 -100
- package/shared/analyzer/languages/python.js.map +1 -0
- package/shared/analyzer/languages/rust.d.ts +17 -0
- package/shared/analyzer/languages/rust.js +96 -98
- package/shared/analyzer/languages/rust.js.map +1 -0
- package/shared/analyzer/languages/typescript.d.ts +11 -0
- package/shared/analyzer/languages/typescript.js +117 -116
- package/shared/analyzer/languages/typescript.js.map +1 -0
- package/shared/analyzer/licenses.d.ts +18 -0
- package/shared/analyzer/licenses.js +130 -133
- package/shared/analyzer/licenses.js.map +1 -0
- package/shared/analyzer/metrics.d.ts +20 -0
- package/shared/analyzer/metrics.js +112 -104
- package/shared/analyzer/metrics.js.map +1 -0
- package/shared/analyzer/pathUtil.d.ts +10 -0
- package/shared/analyzer/pathUtil.js +29 -35
- package/shared/analyzer/pathUtil.js.map +1 -0
- package/shared/analyzer/patterns.d.ts +11 -0
- package/shared/analyzer/patterns.js +231 -253
- package/shared/analyzer/patterns.js.map +1 -0
- package/shared/analyzer/scan.d.ts +62 -0
- package/shared/analyzer/scan.js +451 -442
- package/shared/analyzer/scan.js.map +1 -0
- package/shared/analyzer/scanControl.d.ts +11 -0
- package/shared/analyzer/scanControl.js +62 -0
- package/shared/analyzer/scanControl.js.map +1 -0
- package/shared/analyzer/security.d.ts +18 -0
- package/shared/analyzer/security.js +159 -159
- package/shared/analyzer/security.js.map +1 -0
- package/shared/analyzer/services.d.ts +5 -0
- package/shared/analyzer/services.js +141 -138
- package/shared/analyzer/services.js.map +1 -0
- package/shared/analyzer/stack.d.ts +6 -0
- package/shared/analyzer/stack.js +134 -141
- package/shared/analyzer/stack.js.map +1 -0
- package/shared/analyzer/tour.d.ts +2 -0
- package/shared/analyzer/tour.js +45 -53
- package/shared/analyzer/tour.js.map +1 -0
- package/shared/analyzer/util.d.ts +8 -0
- package/shared/analyzer/util.js +130 -76
- package/shared/analyzer/util.js.map +1 -0
- package/shared/analyzer/workflows.d.ts +8 -0
- package/shared/analyzer/workflows.js +139 -134
- package/shared/analyzer/workflows.js.map +1 -0
- package/shared/contracts.d.ts +250 -0
- package/shared/contracts.js +2 -0
- package/shared/contracts.js.map +1 -0
- package/shared/diagram/aiFacts.d.ts +35 -0
- package/shared/diagram/aiFacts.js +102 -105
- package/shared/diagram/aiFacts.js.map +1 -0
- package/shared/diagram/aiMermaid.d.ts +5 -0
- package/shared/diagram/aiMermaid.js +46 -45
- package/shared/diagram/aiMermaid.js.map +1 -0
- package/shared/diagram/atlas.d.ts +16 -0
- package/shared/diagram/atlas.js +51 -60
- package/shared/diagram/atlas.js.map +1 -0
- package/shared/diagram/mermaid.d.ts +43 -0
- package/shared/diagram/mermaid.js +348 -370
- package/shared/diagram/mermaid.js.map +1 -0
- package/shared/review/contracts.d.ts +95 -0
- package/shared/review/contracts.js +2 -0
- package/shared/review/contracts.js.map +1 -0
- package/shared/review/review.d.ts +10 -0
- package/shared/review/review.js +184 -0
- package/shared/review/review.js.map +1 -0
- package/shared/search/query.d.ts +32 -0
- package/shared/search/query.js +395 -388
- package/shared/search/query.js.map +1 -0
package/server/config.js
CHANGED
|
@@ -1,161 +1,151 @@
|
|
|
1
1
|
// Durable, user-scoped settings for the CLI/server. This file deliberately does
|
|
2
2
|
// not know about HTTP: the CLI and the settings API use the same validation and
|
|
3
3
|
// write path so a browser can change a setting without inventing a second schema.
|
|
4
|
-
|
|
5
4
|
import { promises as fs } from 'node:fs';
|
|
6
5
|
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
7
6
|
import os from 'node:os';
|
|
8
7
|
import path from 'node:path';
|
|
9
|
-
|
|
10
8
|
export const CONFIG_VERSION = 2;
|
|
11
9
|
export const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);
|
|
12
10
|
export const DEFAULT_SETTINGS = Object.freeze({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
version: CONFIG_VERSION,
|
|
12
|
+
mode: 'local',
|
|
13
|
+
host: '127.0.0.1',
|
|
14
|
+
port: 4310,
|
|
15
|
+
domain: '',
|
|
16
|
+
// When a domain is configured, ask Caddy to obtain and renew a trusted TLS
|
|
17
|
+
// certificate and reverse-proxy this app. The certificate itself belongs to
|
|
18
|
+
// Caddy; the server still speaks plain HTTP to its local upstream.
|
|
19
|
+
https: false,
|
|
20
|
+
accessKey: '',
|
|
21
|
+
// Whether `onboarder start` opens the app in a browser once it is listening.
|
|
22
|
+
// Off in the schema so `npm start` from a checkout stays quiet; the setup
|
|
23
|
+
// wizard offers to turn it on, which is where the people who want it are.
|
|
24
|
+
autoOpen: false,
|
|
25
|
+
account: Object.freeze({ name: '', email: '', provider: 'openai-compatible', baseUrl: '', model: '' }),
|
|
26
|
+
tunnel: Object.freeze({ cloudflare: false, tailscale: false }),
|
|
29
27
|
});
|
|
30
|
-
|
|
31
28
|
export function configHome(env = process.env) {
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
if (env.XDG_CONFIG_HOME)
|
|
30
|
+
return path.resolve(env.XDG_CONFIG_HOME, 'onboarder');
|
|
31
|
+
return path.join(os.homedir(), '.config', 'onboarder');
|
|
34
32
|
}
|
|
35
|
-
|
|
36
33
|
export function configPath(env = process.env) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
// An explicit override first: the CLI's --config flag and the test suite both
|
|
35
|
+
// need a config file that is not the one in the person's home directory.
|
|
36
|
+
if (env.ONBOARDER_CONFIG)
|
|
37
|
+
return path.resolve(env.ONBOARDER_CONFIG);
|
|
38
|
+
return path.join(configHome(env), 'config.json');
|
|
41
39
|
}
|
|
42
|
-
|
|
43
40
|
export function isLoopbackHost(host) {
|
|
44
|
-
|
|
41
|
+
return LOOPBACK_HOSTS.has(String(host || '').trim().toLowerCase());
|
|
45
42
|
}
|
|
46
|
-
|
|
47
43
|
function cleanString(value, max = 240) {
|
|
48
|
-
|
|
44
|
+
return String(value ?? '').trim().slice(0, max);
|
|
49
45
|
}
|
|
50
|
-
|
|
51
46
|
function normalizePort(value) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const port = Number(value);
|
|
48
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
49
|
+
throw new Error('Port must be a whole number from 1 to 65535.');
|
|
50
|
+
return port;
|
|
55
51
|
}
|
|
56
|
-
|
|
57
52
|
function normalizeDomain(value) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
const domain = cleanString(value).toLowerCase();
|
|
54
|
+
if (!domain)
|
|
55
|
+
return '';
|
|
56
|
+
if (domain.length > 253 || domain.includes('/') || domain.includes('\\') || domain.includes(' ') || domain.includes('@') || domain.startsWith('.')) {
|
|
57
|
+
throw new Error('Domain must be a hostname such as map.example.com.');
|
|
58
|
+
}
|
|
59
|
+
return domain;
|
|
64
60
|
}
|
|
65
|
-
|
|
66
61
|
function normalizeAccount(value = {}) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
return {
|
|
63
|
+
name: cleanString(value.name, 80),
|
|
64
|
+
email: cleanString(value.email, 160),
|
|
65
|
+
provider: cleanString(value.provider || 'openai-compatible', 80),
|
|
66
|
+
baseUrl: cleanString(value.baseUrl, 500),
|
|
67
|
+
model: cleanString(value.model, 160),
|
|
68
|
+
};
|
|
74
69
|
}
|
|
75
|
-
|
|
76
70
|
function normalizeTunnel(value = {}) {
|
|
77
|
-
|
|
71
|
+
return { cloudflare: Boolean(value.cloudflare), tailscale: Boolean(value.tailscale) };
|
|
78
72
|
}
|
|
79
|
-
|
|
80
73
|
export function normalizeSettings(value = {}) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
74
|
+
const mode = value.mode === 'self-hosted' ? 'self-hosted' : 'local';
|
|
75
|
+
const host = cleanString(value.host || DEFAULT_SETTINGS.host, 120);
|
|
76
|
+
const settings = {
|
|
77
|
+
version: CONFIG_VERSION,
|
|
78
|
+
mode,
|
|
79
|
+
host,
|
|
80
|
+
port: normalizePort(value.port ?? DEFAULT_SETTINGS.port),
|
|
81
|
+
domain: normalizeDomain(value.domain),
|
|
82
|
+
https: Boolean(value.https),
|
|
83
|
+
accessKey: cleanString(value.accessKey, 256),
|
|
84
|
+
autoOpen: value.autoOpen === undefined ? DEFAULT_SETTINGS.autoOpen : Boolean(value.autoOpen),
|
|
85
|
+
account: normalizeAccount(value.account),
|
|
86
|
+
tunnel: normalizeTunnel(value.tunnel),
|
|
87
|
+
};
|
|
88
|
+
if (!host)
|
|
89
|
+
throw new Error('Host cannot be empty.');
|
|
90
|
+
if (settings.mode === 'local' && !isLoopbackHost(settings.host)) {
|
|
91
|
+
throw new Error('Local mode only binds to 127.0.0.1, localhost, or ::1. Choose self-hosted mode for a network bind.');
|
|
92
|
+
}
|
|
93
|
+
if (settings.https && !settings.domain) {
|
|
94
|
+
throw new Error('HTTPS needs a domain such as map.example.com. A public IP alone cannot be used for a normal trusted certificate.');
|
|
95
|
+
}
|
|
96
|
+
// Self-hosted needs no domain: a bare-IP bind (a VPS with no DNS name) is a
|
|
97
|
+
// legitimate layout. Every API call needs the access key either way.
|
|
98
|
+
return settings;
|
|
105
99
|
}
|
|
106
|
-
|
|
107
100
|
export function generateAccessKey() {
|
|
108
|
-
|
|
101
|
+
return `ob_${randomBytes(32).toString('base64url')}`;
|
|
109
102
|
}
|
|
110
|
-
|
|
111
103
|
// Every URL a person might reach this server through, derived from the settings
|
|
112
104
|
// so the CLI banner, the settings drawer, and `onboarder config` never disagree
|
|
113
105
|
// about what to print.
|
|
114
106
|
export function serverUrls(value = DEFAULT_SETTINGS) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
const settings = normalizeSettings(value);
|
|
108
|
+
const urls = { local: `http://localhost:${settings.port}` };
|
|
109
|
+
if (!isLoopbackHost(settings.host)) {
|
|
110
|
+
urls.network = settings.host === '0.0.0.0' || settings.host === '::'
|
|
111
|
+
? `http://<this-machine>:${settings.port} (every interface)`
|
|
112
|
+
: `http://${settings.host}:${settings.port}`;
|
|
113
|
+
}
|
|
114
|
+
if (settings.domain)
|
|
115
|
+
urls.domain = settings.https ? `https://${settings.domain}` : `http://${settings.domain}:${settings.port}`;
|
|
116
|
+
return urls;
|
|
124
117
|
}
|
|
125
|
-
|
|
126
118
|
// The key, safe to print in a list: enough to recognize it, not enough to use it.
|
|
127
119
|
export function maskAccessKey(key) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
const value = String(key || '');
|
|
121
|
+
if (value.length <= 8)
|
|
122
|
+
return value ? '…' : '';
|
|
123
|
+
return `${value.slice(0, 6)}…${value.slice(-4)}`;
|
|
131
124
|
}
|
|
132
|
-
|
|
133
125
|
export function browserUrl(settings) {
|
|
134
|
-
|
|
126
|
+
return new URL(serverUrls(settings).local).toString();
|
|
135
127
|
}
|
|
136
|
-
|
|
137
128
|
export function publicSettings(value = DEFAULT_SETTINGS) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
129
|
+
const settings = normalizeSettings(value);
|
|
130
|
+
return {
|
|
131
|
+
version: settings.version,
|
|
132
|
+
mode: settings.mode,
|
|
133
|
+
host: settings.host,
|
|
134
|
+
port: settings.port,
|
|
135
|
+
domain: settings.domain,
|
|
136
|
+
https: settings.https,
|
|
137
|
+
autoOpen: settings.autoOpen,
|
|
138
|
+
hasAccessKey: Boolean(settings.accessKey),
|
|
139
|
+
accessKeyMasked: maskAccessKey(settings.accessKey),
|
|
140
|
+
account: settings.account,
|
|
141
|
+
tunnel: settings.tunnel,
|
|
142
|
+
urls: serverUrls(settings),
|
|
143
|
+
security: {
|
|
144
|
+
remoteAuth: settings.mode === 'self-hosted',
|
|
145
|
+
loopback: isLoopbackHost(settings.host),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
157
148
|
}
|
|
158
|
-
|
|
159
149
|
// Hostnames the rebinding guard should accept beyond loopback: the configured
|
|
160
150
|
// domain, and a non-loopback bind address itself. A loopback-only local server
|
|
161
151
|
// gets an empty list, which is exactly the old behavior.
|
|
@@ -172,104 +162,109 @@ export function publicSettings(value = DEFAULT_SETTINGS) {
|
|
|
172
162
|
// call time (the router asks per request), so a DHCP lease change does not
|
|
173
163
|
// strand the guard on a stale address.
|
|
174
164
|
function ownInterfaceHosts() {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
165
|
+
const hosts = [];
|
|
166
|
+
for (const list of Object.values(os.networkInterfaces())) {
|
|
167
|
+
for (const addr of list || []) {
|
|
168
|
+
if (addr.internal)
|
|
169
|
+
continue; // loopback is always allowed anyway
|
|
170
|
+
hosts.push(addr.address);
|
|
171
|
+
}
|
|
180
172
|
}
|
|
181
|
-
|
|
182
|
-
return hosts;
|
|
173
|
+
return hosts;
|
|
183
174
|
}
|
|
184
|
-
|
|
185
175
|
export function allowedHosts(value = DEFAULT_SETTINGS) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
176
|
+
const settings = normalizeSettings(value);
|
|
177
|
+
const hosts = new Set();
|
|
178
|
+
if (settings.domain)
|
|
179
|
+
hosts.add(settings.domain);
|
|
180
|
+
if (!isLoopbackHost(settings.host)) {
|
|
181
|
+
// A wildcard bind is useful as a literal Host value to diagnostics, even
|
|
182
|
+
// though browsers normally address the machine by one of its real IPs.
|
|
183
|
+
hosts.add(settings.host.toLowerCase());
|
|
184
|
+
// Any network bind can sit behind NAT: a VPS may be configured as a private
|
|
185
|
+
// interface address while browsers address its public/NAT IP. Accept every IP
|
|
186
|
+
// literal, never arbitrary DNS names; the access key protects remote data.
|
|
187
|
+
hosts.add('ip:*');
|
|
188
|
+
for (const ip of ownInterfaceHosts())
|
|
189
|
+
if (!isLoopbackHost(ip))
|
|
190
|
+
hosts.add(ip);
|
|
191
|
+
}
|
|
192
|
+
if (settings.tunnel.cloudflare)
|
|
193
|
+
hosts.add('*.trycloudflare.com');
|
|
194
|
+
if (settings.tunnel.tailscale)
|
|
195
|
+
hosts.add('*.ts.net');
|
|
196
|
+
return [...hosts];
|
|
202
197
|
}
|
|
203
|
-
|
|
204
198
|
export function accessKeysMatch(expected, actual) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
199
|
+
const a = Buffer.from(String(expected || ''));
|
|
200
|
+
const b = Buffer.from(String(actual || ''));
|
|
201
|
+
return a.length > 0 && a.length === b.length && timingSafeEqual(a, b);
|
|
208
202
|
}
|
|
209
|
-
|
|
210
203
|
export function bearerToken(req) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
204
|
+
const value = String(req.headers?.authorization || '');
|
|
205
|
+
const match = value.match(/^Bearer\s+(.+)$/i);
|
|
206
|
+
return match ? match[1].trim() : '';
|
|
214
207
|
}
|
|
215
|
-
|
|
216
208
|
export function requestIsLoopback(req) {
|
|
217
|
-
|
|
218
|
-
|
|
209
|
+
const remote = String(req.socket?.remoteAddress || '').replace(/^::ffff:/, '');
|
|
210
|
+
return remote === '127.0.0.1' || remote === '::1';
|
|
219
211
|
}
|
|
220
|
-
|
|
221
212
|
export function authReason(req, settings = DEFAULT_SETTINGS) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
213
|
+
const normalized = normalizeSettings(settings);
|
|
214
|
+
if (normalized.mode !== 'self-hosted')
|
|
215
|
+
return null;
|
|
216
|
+
// A self-hosted server can still be opened on the same machine. Caddy and
|
|
217
|
+
// tunnels arrive from loopback but keep the public Host, so both signals must
|
|
218
|
+
// agree before a login page is skipped.
|
|
219
|
+
const host = String(req.headers?.host || '').toLowerCase();
|
|
220
|
+
const hostIsLoopback = isLoopbackHost(host.replace(/:\d+$/, '')) || /^localhost:\d+$/.test(host);
|
|
221
|
+
if (requestIsLoopback(req) && hostIsLoopback)
|
|
222
|
+
return null;
|
|
223
|
+
if (!normalized.accessKey)
|
|
224
|
+
return 'Self-hosted mode has no access key configured.';
|
|
225
|
+
const token = bearerToken(req);
|
|
226
|
+
if (!accessKeysMatch(normalized.accessKey, token))
|
|
227
|
+
return 'A valid access key is required.';
|
|
228
|
+
return null;
|
|
234
229
|
}
|
|
235
|
-
|
|
236
230
|
// Has a config ever been written? The wizard asks this to decide between
|
|
237
231
|
// "start from the defaults" and "start from what you already have".
|
|
238
232
|
export async function configExists(file = configPath()) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
233
|
+
try {
|
|
234
|
+
await fs.access(file);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
245
240
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
241
|
export async function readSettings(file = configPath()) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
242
|
+
try {
|
|
243
|
+
const raw = await fs.readFile(file, 'utf8');
|
|
244
|
+
return normalizeSettings(JSON.parse(raw));
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
if (err.code === 'ENOENT')
|
|
248
|
+
return { ...DEFAULT_SETTINGS, account: { ...DEFAULT_SETTINGS.account }, tunnel: { ...DEFAULT_SETTINGS.tunnel } };
|
|
249
|
+
if (err instanceof SyntaxError)
|
|
250
|
+
throw new Error(`Config is not valid JSON: ${file}`);
|
|
251
|
+
throw err;
|
|
252
|
+
}
|
|
257
253
|
}
|
|
258
|
-
|
|
259
254
|
export async function writeSettings(value, file = configPath()) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
255
|
+
const settings = normalizeSettings(value);
|
|
256
|
+
const dir = path.dirname(file);
|
|
257
|
+
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
|
|
258
|
+
const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
259
|
+
const body = JSON.stringify(settings, null, 2) + '\n';
|
|
260
|
+
await fs.writeFile(temp, body, { encoding: 'utf8', mode: 0o600 });
|
|
261
|
+
await fs.rename(temp, file);
|
|
262
|
+
await fs.chmod(file, 0o600);
|
|
263
|
+
return settings;
|
|
269
264
|
}
|
|
270
|
-
|
|
271
265
|
export async function updateSettings(patch = {}, file = configPath()) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
266
|
+
const current = await readSettings(file);
|
|
267
|
+
const next = normalizeSettings({ ...current, ...patch, account: { ...current.account, ...(patch.account || {}) }, tunnel: { ...current.tunnel, ...(patch.tunnel || {}) } });
|
|
268
|
+
return writeSettings(next, file);
|
|
275
269
|
}
|
|
270
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/server/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Settings, SettingsInput, ServerUrls } from '../shared/contracts.js';\n// Durable, user-scoped settings for the CLI/server. This file deliberately does\n// not know about HTTP: the CLI and the settings API use the same validation and\n// write path so a browser can change a setting without inventing a second schema.\n\nimport { promises as fs } from 'node:fs';\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const CONFIG_VERSION = 2;\nexport const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);\nexport const DEFAULT_SETTINGS: Readonly<Settings> = Object.freeze({\n version: CONFIG_VERSION,\n mode: 'local',\n host: '127.0.0.1',\n port: 4310,\n domain: '',\n // When a domain is configured, ask Caddy to obtain and renew a trusted TLS\n // certificate and reverse-proxy this app. The certificate itself belongs to\n // Caddy; the server still speaks plain HTTP to its local upstream.\n https: false,\n accessKey: '',\n // Whether `onboarder start` opens the app in a browser once it is listening.\n // Off in the schema so `npm start` from a checkout stays quiet; the setup\n // wizard offers to turn it on, which is where the people who want it are.\n autoOpen: false,\n account: Object.freeze({ name: '', email: '', provider: 'openai-compatible', baseUrl: '', model: '' }),\n tunnel: Object.freeze({ cloudflare: false, tailscale: false }),\n});\n\nexport function configHome(env = process.env) {\n if (env.XDG_CONFIG_HOME) return path.resolve(env.XDG_CONFIG_HOME, 'onboarder');\n return path.join(os.homedir(), '.config', 'onboarder');\n}\n\nexport function configPath(env = process.env) {\n // An explicit override first: the CLI's --config flag and the test suite both\n // need a config file that is not the one in the person's home directory.\n if (env.ONBOARDER_CONFIG) return path.resolve(env.ONBOARDER_CONFIG);\n return path.join(configHome(env), 'config.json');\n}\n\nexport function isLoopbackHost(host) {\n return LOOPBACK_HOSTS.has(String(host || '').trim().toLowerCase());\n}\n\nfunction cleanString(value, max = 240) {\n return String(value ?? '').trim().slice(0, max);\n}\n\nfunction normalizePort(value) {\n const port = Number(value);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('Port must be a whole number from 1 to 65535.');\n return port;\n}\n\nfunction normalizeDomain(value) {\n const domain = cleanString(value).toLowerCase();\n if (!domain) return '';\n if (domain.length > 253 || domain.includes('/') || domain.includes('\\\\') || domain.includes(' ') || domain.includes('@') || domain.startsWith('.')) {\n throw new Error('Domain must be a hostname such as map.example.com.');\n }\n return domain;\n}\n\nfunction normalizeAccount(value: SettingsInput['account'] = {}) {\n return {\n name: cleanString(value.name, 80),\n email: cleanString(value.email, 160),\n provider: cleanString(value.provider || 'openai-compatible', 80),\n baseUrl: cleanString(value.baseUrl, 500),\n model: cleanString(value.model, 160),\n };\n}\n\nfunction normalizeTunnel(value: SettingsInput['tunnel'] = {}) {\n return { cloudflare: Boolean(value.cloudflare), tailscale: Boolean(value.tailscale) };\n}\n\nexport function normalizeSettings(value: SettingsInput = {}): Settings {\n const mode = value.mode === 'self-hosted' ? 'self-hosted' : 'local';\n const host = cleanString(value.host || DEFAULT_SETTINGS.host, 120);\n const settings: Settings = {\n version: CONFIG_VERSION,\n mode,\n host,\n port: normalizePort(value.port ?? DEFAULT_SETTINGS.port),\n domain: normalizeDomain(value.domain),\n https: Boolean(value.https),\n accessKey: cleanString(value.accessKey, 256),\n autoOpen: value.autoOpen === undefined ? DEFAULT_SETTINGS.autoOpen : Boolean(value.autoOpen),\n account: normalizeAccount(value.account),\n tunnel: normalizeTunnel(value.tunnel),\n };\n if (!host) throw new Error('Host cannot be empty.');\n if (settings.mode === 'local' && !isLoopbackHost(settings.host)) {\n throw new Error('Local mode only binds to 127.0.0.1, localhost, or ::1. Choose self-hosted mode for a network bind.');\n }\n if (settings.https && !settings.domain) {\n throw new Error('HTTPS needs a domain such as map.example.com. A public IP alone cannot be used for a normal trusted certificate.');\n }\n // Self-hosted needs no domain: a bare-IP bind (a VPS with no DNS name) is a\n // legitimate layout. Every API call needs the access key either way.\n return settings;\n}\n\nexport function generateAccessKey() {\n return `ob_${randomBytes(32).toString('base64url')}`;\n}\n\n// Every URL a person might reach this server through, derived from the settings\n// so the CLI banner, the settings drawer, and `onboarder config` never disagree\n// about what to print.\nexport function serverUrls(value: SettingsInput = DEFAULT_SETTINGS) {\n const settings = normalizeSettings(value);\n const urls: ServerUrls = { local: `http://localhost:${settings.port}` };\n if (!isLoopbackHost(settings.host)) {\n urls.network = settings.host === '0.0.0.0' || settings.host === '::'\n ? `http://<this-machine>:${settings.port} (every interface)`\n : `http://${settings.host}:${settings.port}`;\n }\n if (settings.domain) urls.domain = settings.https ? `https://${settings.domain}` : `http://${settings.domain}:${settings.port}`;\n return urls;\n}\n\n// The key, safe to print in a list: enough to recognize it, not enough to use it.\nexport function maskAccessKey(key) {\n const value = String(key || '');\n if (value.length <= 8) return value ? '…' : '';\n return `${value.slice(0, 6)}…${value.slice(-4)}`;\n}\n\nexport function browserUrl(settings) {\n return new URL(serverUrls(settings).local).toString();\n}\n\nexport function publicSettings(value: SettingsInput = DEFAULT_SETTINGS) {\n const settings = normalizeSettings(value);\n return {\n version: settings.version,\n mode: settings.mode,\n host: settings.host,\n port: settings.port,\n domain: settings.domain,\n https: settings.https,\n autoOpen: settings.autoOpen,\n hasAccessKey: Boolean(settings.accessKey),\n accessKeyMasked: maskAccessKey(settings.accessKey),\n account: settings.account,\n tunnel: settings.tunnel,\n urls: serverUrls(settings),\n security: {\n remoteAuth: settings.mode === 'self-hosted',\n loopback: isLoopbackHost(settings.host),\n },\n };\n}\n\n// Hostnames the rebinding guard should accept beyond loopback: the configured\n// domain, and a non-loopback bind address itself. A loopback-only local server\n// gets an empty list, which is exactly the old behavior.\n//\n// Enabled tunnels add wildcard suffixes: a Cloudflare quick tunnel's name is\n// random (https://<something>.trycloudflare.com) and a Tailscale machine name\n// belongs to the tailnet, so neither can be configured ahead of time. This is\n// safe because it can only widen Host acceptance in self-hosted mode, where\n// every API call already needs the access key — the guard's job there is\n// keeping drive-by traffic off the static files, not authentication.\n// A wildcard bind (0.0.0.0 / ::) has no name of its own, so the names it\n// honestly answers to are this machine's own interface addresses — a request\n// addressed to 10.0.0.2 or a public VPS IP really did arrive here. Read at\n// call time (the router asks per request), so a DHCP lease change does not\n// strand the guard on a stale address.\nfunction ownInterfaceHosts() {\n const hosts = [];\n for (const list of Object.values(os.networkInterfaces())) {\n for (const addr of list || []) {\n if (addr.internal) continue; // loopback is always allowed anyway\n hosts.push(addr.address);\n }\n }\n return hosts;\n}\n\nexport function allowedHosts(value: SettingsInput = DEFAULT_SETTINGS) {\n const settings = normalizeSettings(value);\n const hosts = new Set();\n if (settings.domain) hosts.add(settings.domain);\n if (!isLoopbackHost(settings.host)) {\n // A wildcard bind is useful as a literal Host value to diagnostics, even\n // though browsers normally address the machine by one of its real IPs.\n hosts.add(settings.host.toLowerCase());\n // Any network bind can sit behind NAT: a VPS may be configured as a private\n // interface address while browsers address its public/NAT IP. Accept every IP\n // literal, never arbitrary DNS names; the access key protects remote data.\n hosts.add('ip:*');\n for (const ip of ownInterfaceHosts()) if (!isLoopbackHost(ip)) hosts.add(ip);\n }\n if (settings.tunnel.cloudflare) hosts.add('*.trycloudflare.com');\n if (settings.tunnel.tailscale) hosts.add('*.ts.net');\n return [...hosts];\n}\n\nexport function accessKeysMatch(expected, actual) {\n const a = Buffer.from(String(expected || ''));\n const b = Buffer.from(String(actual || ''));\n return a.length > 0 && a.length === b.length && timingSafeEqual(a, b);\n}\n\nexport function bearerToken(req) {\n const value = String(req.headers?.authorization || '');\n const match = value.match(/^Bearer\\s+(.+)$/i);\n return match ? match[1].trim() : '';\n}\n\nexport function requestIsLoopback(req) {\n const remote = String(req.socket?.remoteAddress || '').replace(/^::ffff:/, '');\n return remote === '127.0.0.1' || remote === '::1';\n}\n\nexport function authReason(req, settings: SettingsInput = DEFAULT_SETTINGS) {\n const normalized = normalizeSettings(settings);\n if (normalized.mode !== 'self-hosted') return null;\n // A self-hosted server can still be opened on the same machine. Caddy and\n // tunnels arrive from loopback but keep the public Host, so both signals must\n // agree before a login page is skipped.\n const host = String(req.headers?.host || '').toLowerCase();\n const hostIsLoopback = isLoopbackHost(host.replace(/:\\d+$/, '')) || /^localhost:\\d+$/.test(host);\n if (requestIsLoopback(req) && hostIsLoopback) return null;\n if (!normalized.accessKey) return 'Self-hosted mode has no access key configured.';\n const token = bearerToken(req);\n if (!accessKeysMatch(normalized.accessKey, token)) return 'A valid access key is required.';\n return null;\n}\n\n// Has a config ever been written? The wizard asks this to decide between\n// \"start from the defaults\" and \"start from what you already have\".\nexport async function configExists(file = configPath()) {\n try {\n await fs.access(file);\n return true;\n } catch {\n return false;\n }\n}\n\n\nexport async function readSettings(file = configPath()) {\n try {\n const raw = await fs.readFile(file, 'utf8');\n return normalizeSettings(JSON.parse(raw));\n } catch (err) {\n if (err.code === 'ENOENT') return { ...DEFAULT_SETTINGS, account: { ...DEFAULT_SETTINGS.account }, tunnel: { ...DEFAULT_SETTINGS.tunnel } };\n if (err instanceof SyntaxError) throw new Error(`Config is not valid JSON: ${file}`);\n throw err;\n }\n}\n\nexport async function writeSettings(value, file = configPath()) {\n const settings = normalizeSettings(value);\n const dir = path.dirname(file);\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n const temp = `${file}.${process.pid}.${Date.now()}.tmp`;\n const body = JSON.stringify(settings, null, 2) + '\\n';\n await fs.writeFile(temp, body, { encoding: 'utf8', mode: 0o600 });\n await fs.rename(temp, file);\n await fs.chmod(file, 0o600);\n return settings;\n}\n\nexport async function updateSettings(patch: SettingsInput = {}, file = configPath()) {\n const current = await readSettings(file);\n const next = normalizeSettings({ ...current, ...patch, account: { ...current.account, ...(patch.account || {}) }, tunnel: { ...current.tunnel, ...(patch.tunnel || {}) } });\n return writeSettings(next, file);\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const CLI_ENTRY: string;
|
|
2
|
+
export declare function logPath(configFile?: string): string;
|
|
3
|
+
export declare const MAX_LOG_BYTES: number;
|
|
4
|
+
export declare function rotateLogIfNeeded(file?: string, maxBytes?: number): Promise<boolean>;
|
|
5
|
+
export declare function readLog(file?: string, bytes?: number): Promise<string>;
|
|
6
|
+
export declare function tailLog(file?: string, count?: number): Promise<string[]>;
|
|
7
|
+
export declare function logExists(file?: string): Promise<boolean>;
|
|
8
|
+
export declare function probe(url: any, timeoutMs?: number): Promise<unknown>;
|
|
9
|
+
export declare function waitForReady(url: any, { timeoutMs, intervalMs, check }?: Record<string, any>): Promise<boolean>;
|
|
10
|
+
export declare function spawnDetached({ configFile, entry, env, log }?: Record<string, any>): number;
|
|
11
|
+
export declare function waitForPidFile(configFile: any, { timeoutMs, intervalMs, readPid, isAlive }?: Record<string, any>): Promise<any>;
|
|
12
|
+
export declare function followLog(file: any, onLine: any, { intervalMs, from }?: Record<string, any>): () => void;
|