@openchambery/web 1.17.1-beta.7 → 1.17.1-beta.9
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 +3 -39
- package/bin/cli-output.js +0 -16
- package/bin/cli.js +3 -66
- package/bin/cli.test.js +4 -81
- package/bin/lib/DOCUMENTATION.md +5 -19
- package/bin/lib/cli-args.js +3 -327
- package/bin/lib/cli-http.js +0 -16
- package/bin/lib/cli-lifecycle.js +2 -111
- package/bin/lib/cli-paths.js +0 -57
- package/dist/assets/{AgentAvatar-htFrIvQx.js → AgentAvatar-C6Hxu5_o.js} +1 -1
- package/dist/assets/{AssistantView-BZiJsNuI.js → AssistantView-BKg8KK5a.js} +1 -1
- package/dist/assets/{ChatContainer-CdmcMnL3.js → ChatContainer-CuxynsBz.js} +5 -5
- package/dist/assets/{DiagramView-B7sq74V-.js → DiagramView-CjMblW4W.js} +1 -1
- package/dist/assets/{FadeInOnReveal-8fY7XL23.js → FadeInOnReveal-CvNA_aGD.js} +1 -1
- package/dist/assets/{Icon-B7eLbnyj.js → Icon-BPJ_quEe.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-DICju9ve.js → MarkdownRendererImpl--oy9PEaq.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-CuxWU1yf.js → MobileDetailNavigation-j5XTy0wI.js} +1 -1
- package/dist/assets/{MobileShareBridge-CPg9Eyro.js → MobileShareBridge-BnF86wui.js} +1 -1
- package/dist/assets/{MobileSurface-D7sDH_tx.js → MobileSurface-D0LTFt1w.js} +1 -1
- package/dist/assets/{MultiRunWindow-MSO0Equt.js → MultiRunWindow-BTTFYUzI.js} +1 -1
- package/dist/assets/{OnboardingScreen-C87u_MLg.js → OnboardingScreen-CdvaEBtt.js} +1 -1
- package/dist/assets/{OverlayScrollbar-Kmmybw-z.js → OverlayScrollbar-DyP73J3X.js} +1 -1
- package/dist/assets/SettingsView-JQxnmG9X.js +25 -0
- package/dist/assets/{SettingsWindow-Bl25cfXX.js → SettingsWindow-Ddr1B6Hc.js} +1 -1
- package/dist/assets/{TerminalView-jR5jP1b0.js → TerminalView-bHOuUIxH.js} +1 -1
- package/dist/assets/{ToolOutputDialog-DCEZb2DZ.js → ToolOutputDialog-CbGv7HQ5.js} +1 -1
- package/dist/assets/{appThemeRegistry-aiRF-s3c.js → appThemeRegistry-DIDGDdQq.js} +2 -2
- package/dist/assets/{button-bYWkDtDI.js → button-BmNJBT13.js} +1 -1
- package/dist/assets/{clipboard-CzpzN4Um.js → clipboard-CXWbHCAk.js} +1 -1
- package/dist/assets/{collapsible-A1PFvMRp.js → collapsible-68IqWBM1.js} +1 -1
- package/dist/assets/{context-menu-CkpHn-Bm.js → context-menu-B-8pAqjS.js} +1 -1
- package/dist/assets/desktopHosts-BpjUSDz4.js +1 -0
- package/dist/assets/{desktopRelayRestore-C5vYnSTd.js → desktopRelayRestore-BzUEk1i7.js} +1 -1
- package/dist/assets/{diagram-BkwKWaTO.js → diagram-YN0GZ1Q0.js} +1 -1
- package/dist/assets/{dialog-DW7-_iqo.js → dialog-BV1ENlLf.js} +1 -1
- package/dist/assets/es-EXZUX1sB.js +15 -0
- package/dist/assets/{fileQueries-P9SX4YwC.js → fileQueries-i4oziMnC.js} +1 -1
- package/dist/assets/fr-jWNPO91J.js +13 -0
- package/dist/assets/{gitApi-CMsZ2v08.js → gitApi-BuTpcy5P.js} +1 -1
- package/dist/assets/{gitApiHttp-DvIIcDAO.js → gitApiHttp-C1DaEMKR.js} +1 -1
- package/dist/assets/{input-B9aNttqV.js → input-BLVhPvhN.js} +1 -1
- package/dist/assets/{insertionBoundaries-Di3rCXgN.js → insertionBoundaries-D8FUCksc.js} +1 -1
- package/dist/assets/ja-Dcb0VtDE.js +15 -0
- package/dist/assets/ko-CX71xNDX.js +15 -0
- package/dist/assets/{main-DY27kMV5.js → main-Bz2igSmw.js} +2 -2
- package/dist/assets/{main-Chtf300b.js → main-C3JiVxSp.js} +5 -5
- package/dist/assets/{markdown-worker-BoB1_DPo.js → markdown-worker-Cq3Twp1A.js} +1 -1
- package/dist/assets/{markdownTheme-om5XcPqq.js → markdownTheme-CCnvI6L0.js} +1 -1
- package/dist/assets/{miniChat-BWbpl5SN.js → miniChat-6EMGKhlG.js} +2 -2
- package/dist/assets/mobile-DP5GfqmN.js +2 -0
- package/dist/assets/{multirun-BfPcVktg.js → multirun-9f6WZ3Jd.js} +1 -1
- package/dist/assets/persistence-C_KCiMcY.js +3 -0
- package/dist/assets/{pl-BSjLn4dm.js → pl-mR_3Sqr8.js} +2 -2
- package/dist/assets/{projectMeta-CiAR7MRG.js → projectMeta-Dsa2wr8n.js} +1 -1
- package/dist/assets/pt-BR-D1RpVOvC.js +15 -0
- package/dist/assets/radio-CrWrJ2b-.js +1 -0
- package/dist/assets/{renderElectronMiniChatApp-CiEXAVSt.js → renderElectronMiniChatApp-4adD-9dD.js} +2 -2
- package/dist/assets/{renderMobileApp-BHvgSVPk.js → renderMobileApp-bSjLTDsy.js} +3 -3
- package/dist/assets/{runtimeConfig-49YbZaqI.js → runtimeConfig-JSaYFA5x.js} +1 -1
- package/dist/assets/{runtimeEndpointReset-CdhtjuPe.js → runtimeEndpointReset-DW-MKG59.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-XeYica3j.js → sortable-tabs-strip-NTPx-T_l.js} +1 -1
- package/dist/assets/src-B8mzksNL.css +2 -0
- package/dist/assets/store-oojlrF4d.js +15 -0
- package/dist/assets/{terminalApi-DAu7xKli.js → terminalApi-BYTA045F.js} +1 -1
- package/dist/assets/{tooltip-BSlxaNrR.js → tooltip-BYGBBTc2.js} +1 -1
- package/dist/assets/uk-UvnVqWv-.js +15 -0
- package/dist/assets/{url-DjkTDrad.js → url-CGHw6D-E.js} +1 -1
- package/dist/assets/useAppFontEffects-BIO3lPVM.js +7 -0
- package/dist/assets/{useDesktopWindowControlsLayout-BwYnhrEy.js → useDesktopWindowControlsLayout-Df2h_cM_.js} +1 -1
- package/dist/assets/{useEffectiveDirectory-CEmsrrEs.js → useEffectiveDirectory-D0vaM832.js} +1 -1
- package/dist/assets/{useFontPreferences-BxMT_kty.js → useFontPreferences-Bu4ME5hk.js} +1 -1
- package/dist/assets/useMobileNavigationStore-7HNp5YHY.js +475 -0
- package/dist/assets/useSessionAutoCleanup-BPNLtDRJ.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-CFW2AoPL.js → useWorkerHighlightedLines-DYfBlGb-.js} +1 -1
- package/dist/assets/{utils-RUACCsOs.js → utils-DyUyiLje.js} +1 -1
- package/dist/assets/zh-CN-9v-RC966.js +15 -0
- package/dist/assets/zh-TW-BZ_wZdC-.js +15 -0
- package/dist/index.html +8 -8
- package/dist/mini-chat.html +8 -8
- package/dist/mobile.html +8 -8
- package/package.json +1 -1
- package/server/index.d.ts +0 -6
- package/server/index.js +5 -153
- package/server/lib/opencode/DOCUMENTATION.md +6 -10
- package/server/lib/opencode/cli-entry-runtime.js +0 -10
- package/server/lib/opencode/cli-options.js +0 -65
- package/server/lib/opencode/core-routes.js +2 -4
- package/server/lib/opencode/routes.test.js +0 -1
- package/server/lib/opencode/server-startup-runtime.js +0 -55
- package/server/lib/opencode/settings-helpers.js +0 -55
- package/server/lib/opencode/settings-helpers.test.js +0 -20
- package/server/lib/opencode/settings-normalization-runtime.js +0 -87
- package/server/lib/opencode/settings-normalization-runtime.test.js +0 -3
- package/server/lib/opencode/settings-runtime.js +9 -102
- package/server/lib/opencode/settings-runtime.test.js +0 -10
- package/server/lib/opencode/shutdown-runtime.js +1 -9
- package/server/lib/opencode/shutdown-runtime.test.js +0 -2
- package/server/lib/opencode/startup-pipeline-runtime.js +0 -20
- package/server/lib/opencode/startup-pipeline-runtime.test.js +2 -3
- package/server/lib/opencode/tunnel-wiring-runtime.js +2 -80
- package/server/lib/relay/DOCUMENTATION.md +1 -1
- package/bin/lib/cli-tunnel-capabilities.js +0 -9
- package/bin/lib/cli-tunnel-profiles.js +0 -366
- package/bin/lib/cli-tunnel-utils.js +0 -300
- package/bin/lib/commands-tunnel.js +0 -1761
- package/dist/assets/SettingsView-5YNu54KI.js +0 -25
- package/dist/assets/desktopHosts-D5m_ggY1.js +0 -1
- package/dist/assets/es-Cei_75Jt.js +0 -15
- package/dist/assets/fr-Dn7WSHh2.js +0 -13
- package/dist/assets/ja-DFARVqVj.js +0 -15
- package/dist/assets/ko-pyLnIyDY.js +0 -15
- package/dist/assets/mobile-DEQeGskq.js +0 -2
- package/dist/assets/persistence-CHA81Od7.js +0 -3
- package/dist/assets/pt-BR-DaWvr_0A.js +0 -15
- package/dist/assets/radio-BnF4XhCC.js +0 -1
- package/dist/assets/src-BtkWqMFu.css +0 -2
- package/dist/assets/store-DIpqFVjs.js +0 -15
- package/dist/assets/uk-CHCRqRKf.js +0 -15
- package/dist/assets/useAppFontEffects-BRNICZS2.js +0 -7
- package/dist/assets/useMobileNavigationStore-DryW-QdG.js +0 -475
- package/dist/assets/useSessionAutoCleanup-DMJ1LiW_.js +0 -1
- package/dist/assets/zh-CN-BWEQZKXk.js +0 -15
- package/dist/assets/zh-TW-BbO-gJq3.js +0 -15
- package/server/lib/cloudflare-tunnel.js +0 -618
- package/server/lib/ngrok-tunnel.js +0 -343
- package/server/lib/ngrok-tunnel.test.js +0 -50
- package/server/lib/tunnels/DOCUMENTATION.md +0 -21
- package/server/lib/tunnels/executable-search.js +0 -140
- package/server/lib/tunnels/executable-search.test.js +0 -96
- package/server/lib/tunnels/index.js +0 -179
- package/server/lib/tunnels/index.test.js +0 -93
- package/server/lib/tunnels/install-help.js +0 -56
- package/server/lib/tunnels/install-help.test.js +0 -31
- package/server/lib/tunnels/managed-config.js +0 -201
- package/server/lib/tunnels/providers/cloudflare.js +0 -266
- package/server/lib/tunnels/providers/ngrok.js +0 -123
- package/server/lib/tunnels/registry.js +0 -51
- package/server/lib/tunnels/routes.js +0 -610
- package/server/lib/tunnels/types.js +0 -245
- package/server/lib/tunnels/types.test.js +0 -34
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
checkCloudflareApiReachability,
|
|
3
|
-
checkCloudflaredAvailable,
|
|
4
|
-
inspectManagedLocalCloudflareConfig,
|
|
5
|
-
normalizeCloudflareTunnelHostname,
|
|
6
|
-
startCloudflareManagedLocalTunnel,
|
|
7
|
-
startCloudflareManagedRemoteTunnel,
|
|
8
|
-
startCloudflareQuickTunnel,
|
|
9
|
-
} from '../../cloudflare-tunnel.js';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
|
13
|
-
TUNNEL_INTENT_PERSISTENT_PUBLIC,
|
|
14
|
-
TUNNEL_MODE_MANAGED_LOCAL,
|
|
15
|
-
TUNNEL_MODE_MANAGED_REMOTE,
|
|
16
|
-
TUNNEL_MODE_QUICK,
|
|
17
|
-
TUNNEL_PROVIDER_CLOUDFLARE,
|
|
18
|
-
TunnelServiceError,
|
|
19
|
-
} from '../types.js';
|
|
20
|
-
import { getTunnelDependencyInstallInfo } from '../install-help.js';
|
|
21
|
-
|
|
22
|
-
export const cloudflareTunnelProviderCapabilities = {
|
|
23
|
-
provider: TUNNEL_PROVIDER_CLOUDFLARE,
|
|
24
|
-
defaults: {
|
|
25
|
-
mode: TUNNEL_MODE_QUICK,
|
|
26
|
-
optionDefaults: {},
|
|
27
|
-
},
|
|
28
|
-
modes: [
|
|
29
|
-
{
|
|
30
|
-
key: TUNNEL_MODE_QUICK,
|
|
31
|
-
label: 'Quick Tunnel',
|
|
32
|
-
intent: TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
|
33
|
-
requires: [],
|
|
34
|
-
supports: ['sessionTTL'],
|
|
35
|
-
stability: 'ga',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
key: TUNNEL_MODE_MANAGED_REMOTE,
|
|
39
|
-
label: 'Managed Remote Tunnel',
|
|
40
|
-
intent: TUNNEL_INTENT_PERSISTENT_PUBLIC,
|
|
41
|
-
requires: ['token', 'hostname'],
|
|
42
|
-
supports: ['customDomain', 'sessionTTL'],
|
|
43
|
-
stability: 'ga',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
key: TUNNEL_MODE_MANAGED_LOCAL,
|
|
47
|
-
label: 'Managed Local Tunnel',
|
|
48
|
-
intent: TUNNEL_INTENT_PERSISTENT_PUBLIC,
|
|
49
|
-
requires: [],
|
|
50
|
-
supports: ['configFile', 'customDomain', 'sessionTTL'],
|
|
51
|
-
stability: 'ga',
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export function createCloudflareTunnelProvider() {
|
|
57
|
-
const validateTokenShape = (value) => {
|
|
58
|
-
if (typeof value !== 'string') {
|
|
59
|
-
return { ok: false, detail: 'Managed remote token is missing.' };
|
|
60
|
-
}
|
|
61
|
-
const trimmed = value.trim();
|
|
62
|
-
if (!trimmed) {
|
|
63
|
-
return { ok: false, detail: 'Managed remote token is missing.' };
|
|
64
|
-
}
|
|
65
|
-
if (/\s/.test(trimmed)) {
|
|
66
|
-
return { ok: false, detail: 'Managed remote token has whitespace; provide the raw token value.' };
|
|
67
|
-
}
|
|
68
|
-
return { ok: true, detail: 'Managed remote token looks valid.' };
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const createModeSummary = (checks) => {
|
|
72
|
-
const failures = checks.filter((entry) => entry.status === 'fail').length;
|
|
73
|
-
const warnings = checks.filter((entry) => entry.status === 'warn').length;
|
|
74
|
-
return {
|
|
75
|
-
ready: failures === 0,
|
|
76
|
-
failures,
|
|
77
|
-
warnings,
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const describeMode = ({ mode, checks }) => {
|
|
82
|
-
const summary = createModeSummary(checks);
|
|
83
|
-
const blockers = checks
|
|
84
|
-
.filter((entry) => entry.status === 'fail' && entry.id !== 'startup_readiness')
|
|
85
|
-
.map((entry) => entry.detail || entry.label || entry.id);
|
|
86
|
-
return {
|
|
87
|
-
mode,
|
|
88
|
-
checks,
|
|
89
|
-
summary,
|
|
90
|
-
ready: summary.ready,
|
|
91
|
-
blockers,
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
id: TUNNEL_PROVIDER_CLOUDFLARE,
|
|
97
|
-
capabilities: cloudflareTunnelProviderCapabilities,
|
|
98
|
-
checkAvailability: async () => {
|
|
99
|
-
const result = await checkCloudflaredAvailable();
|
|
100
|
-
if (result.available) {
|
|
101
|
-
return {
|
|
102
|
-
...result,
|
|
103
|
-
...getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_CLOUDFLARE),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
const installInfo = getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_CLOUDFLARE);
|
|
107
|
-
return {
|
|
108
|
-
...result,
|
|
109
|
-
...installInfo,
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
diagnose: async (request = {}) => {
|
|
113
|
-
const dependency = await checkCloudflaredAvailable();
|
|
114
|
-
const network = await checkCloudflareApiReachability();
|
|
115
|
-
const installInfo = getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_CLOUDFLARE);
|
|
116
|
-
|
|
117
|
-
const providerChecks = [
|
|
118
|
-
{
|
|
119
|
-
id: 'dependency',
|
|
120
|
-
label: 'cloudflared installed',
|
|
121
|
-
status: dependency.available ? 'pass' : 'fail',
|
|
122
|
-
detail: dependency.available
|
|
123
|
-
? (dependency.version || dependency.path || 'cloudflared available')
|
|
124
|
-
: installInfo.message,
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
id: 'network',
|
|
128
|
-
label: 'Cloudflare API reachable',
|
|
129
|
-
status: network.reachable ? 'pass' : 'fail',
|
|
130
|
-
detail: network.reachable
|
|
131
|
-
? (network.status ? `HTTP ${network.status}` : 'Reachable')
|
|
132
|
-
: (network.error || 'Could not reach api.trycloudflare.com'),
|
|
133
|
-
},
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
const startupReady = dependency.available && network.reachable;
|
|
137
|
-
const startupDetail = startupReady
|
|
138
|
-
? 'Provider dependency and network checks passed.'
|
|
139
|
-
: 'Resolve provider checks before starting tunnels.';
|
|
140
|
-
|
|
141
|
-
const quickChecks = [
|
|
142
|
-
{
|
|
143
|
-
id: 'startup_readiness',
|
|
144
|
-
label: 'Provider startup readiness',
|
|
145
|
-
status: startupReady ? 'pass' : 'fail',
|
|
146
|
-
detail: startupDetail,
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
id: 'quick_mode_prerequisites',
|
|
150
|
-
label: 'Quick tunnel prerequisites',
|
|
151
|
-
status: network.reachable ? 'pass' : 'fail',
|
|
152
|
-
detail: network.reachable
|
|
153
|
-
? 'Cloudflare edge is reachable for quick tunnels.'
|
|
154
|
-
: 'Cloudflare edge is not reachable for quick tunnels.',
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
const managedLocalInspection = inspectManagedLocalCloudflareConfig({
|
|
159
|
-
configPath: request.configPath,
|
|
160
|
-
hostname: request.hostname,
|
|
161
|
-
});
|
|
162
|
-
const managedLocalChecks = [
|
|
163
|
-
{
|
|
164
|
-
id: 'startup_readiness',
|
|
165
|
-
label: 'Provider startup readiness',
|
|
166
|
-
status: startupReady ? 'pass' : 'fail',
|
|
167
|
-
detail: startupDetail,
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
id: 'managed_local_config',
|
|
171
|
-
label: 'Managed local config',
|
|
172
|
-
status: managedLocalInspection.ok ? 'pass' : 'fail',
|
|
173
|
-
detail: managedLocalInspection.ok
|
|
174
|
-
? `${managedLocalInspection.effectiveConfigPath}${managedLocalInspection.resolvedHostname ? ` (${managedLocalInspection.resolvedHostname})` : ''}`
|
|
175
|
-
: managedLocalInspection.error,
|
|
176
|
-
},
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const normalizedHost = normalizeCloudflareTunnelHostname(request.hostname);
|
|
180
|
-
const hostnameMissing = !normalizedHost;
|
|
181
|
-
const remoteTokenValidation = validateTokenShape(request.token);
|
|
182
|
-
const tokenMissing = typeof request.token !== 'string' || request.token.trim().length === 0;
|
|
183
|
-
const hasSavedManagedRemoteProfile = request.hasSavedManagedRemoteProfile === true;
|
|
184
|
-
const tokenProvided = request.tokenProvided === true;
|
|
185
|
-
const hostnameProvided = request.hostnameProvided === true;
|
|
186
|
-
const hasExplicitManagedRemoteInput = tokenProvided || hostnameProvided;
|
|
187
|
-
const canUseSavedProfileForHostname = !hasExplicitManagedRemoteInput && hostnameMissing && hasSavedManagedRemoteProfile;
|
|
188
|
-
const canUseSavedProfileForToken = !hasExplicitManagedRemoteInput && tokenMissing && hasSavedManagedRemoteProfile;
|
|
189
|
-
const savedProfileReadyDetail = 'at least one saved profile present';
|
|
190
|
-
const managedRemoteChecks = [
|
|
191
|
-
{
|
|
192
|
-
id: 'startup_readiness',
|
|
193
|
-
label: 'Provider startup readiness',
|
|
194
|
-
status: startupReady ? 'pass' : 'fail',
|
|
195
|
-
detail: startupDetail,
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
id: 'managed_remote_hostname',
|
|
199
|
-
label: 'Managed remote hostname',
|
|
200
|
-
status: normalizedHost || canUseSavedProfileForHostname ? 'pass' : 'fail',
|
|
201
|
-
detail: normalizedHost
|
|
202
|
-
? normalizedHost
|
|
203
|
-
: canUseSavedProfileForHostname
|
|
204
|
-
? savedProfileReadyDetail
|
|
205
|
-
: 'Managed remote hostname is required (use --hostname).',
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
id: 'managed_remote_token',
|
|
209
|
-
label: 'Managed remote token',
|
|
210
|
-
status: remoteTokenValidation.ok || canUseSavedProfileForToken ? 'pass' : 'fail',
|
|
211
|
-
detail: canUseSavedProfileForToken
|
|
212
|
-
? savedProfileReadyDetail
|
|
213
|
-
: remoteTokenValidation.detail,
|
|
214
|
-
},
|
|
215
|
-
];
|
|
216
|
-
|
|
217
|
-
const allModes = [
|
|
218
|
-
describeMode({ mode: TUNNEL_MODE_QUICK, checks: quickChecks }),
|
|
219
|
-
describeMode({ mode: TUNNEL_MODE_MANAGED_REMOTE, checks: managedRemoteChecks }),
|
|
220
|
-
describeMode({ mode: TUNNEL_MODE_MANAGED_LOCAL, checks: managedLocalChecks }),
|
|
221
|
-
];
|
|
222
|
-
|
|
223
|
-
const modeFilter = typeof request.mode === 'string' && request.mode.trim().length > 0
|
|
224
|
-
? request.mode.trim().toLowerCase()
|
|
225
|
-
: null;
|
|
226
|
-
const modes = modeFilter ? allModes.filter((entry) => entry.mode === modeFilter) : allModes;
|
|
227
|
-
|
|
228
|
-
return {
|
|
229
|
-
providerChecks,
|
|
230
|
-
modes,
|
|
231
|
-
};
|
|
232
|
-
},
|
|
233
|
-
start: async (request, context = {}) => {
|
|
234
|
-
if (request.mode === TUNNEL_MODE_MANAGED_REMOTE) {
|
|
235
|
-
return startCloudflareManagedRemoteTunnel({
|
|
236
|
-
token: request.token,
|
|
237
|
-
hostname: request.hostname,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (request.mode === TUNNEL_MODE_MANAGED_LOCAL) {
|
|
242
|
-
return startCloudflareManagedLocalTunnel({
|
|
243
|
-
configPath: request.configPath,
|
|
244
|
-
hostname: request.hostname,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (!context.originUrl) {
|
|
249
|
-
throw new TunnelServiceError('validation_error', 'originUrl is required for quick tunnel mode');
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return startCloudflareQuickTunnel({
|
|
253
|
-
originUrl: context.originUrl,
|
|
254
|
-
port: context.activePort,
|
|
255
|
-
});
|
|
256
|
-
},
|
|
257
|
-
stop: (controller) => {
|
|
258
|
-
controller?.stop?.();
|
|
259
|
-
},
|
|
260
|
-
resolvePublicUrl: (controller) => controller?.getPublicUrl?.() ?? null,
|
|
261
|
-
getMetadata: (controller) => ({
|
|
262
|
-
configPath: controller?.getEffectiveConfigPath?.() ?? null,
|
|
263
|
-
resolvedHostname: controller?.getResolvedHostname?.() ?? null,
|
|
264
|
-
}),
|
|
265
|
-
};
|
|
266
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
checkNgrokApiReachability,
|
|
3
|
-
checkNgrokAuthtokenConfigured,
|
|
4
|
-
checkNgrokAvailable,
|
|
5
|
-
startNgrokQuickTunnel,
|
|
6
|
-
} from '../../ngrok-tunnel.js';
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
|
10
|
-
TUNNEL_MODE_QUICK,
|
|
11
|
-
TUNNEL_PROVIDER_NGROK,
|
|
12
|
-
TunnelServiceError,
|
|
13
|
-
} from '../types.js';
|
|
14
|
-
import { getTunnelDependencyInstallInfo } from '../install-help.js';
|
|
15
|
-
|
|
16
|
-
export const ngrokTunnelProviderCapabilities = {
|
|
17
|
-
provider: TUNNEL_PROVIDER_NGROK,
|
|
18
|
-
defaults: {
|
|
19
|
-
mode: TUNNEL_MODE_QUICK,
|
|
20
|
-
optionDefaults: {},
|
|
21
|
-
},
|
|
22
|
-
modes: [
|
|
23
|
-
{
|
|
24
|
-
key: TUNNEL_MODE_QUICK,
|
|
25
|
-
label: 'Quick Tunnel',
|
|
26
|
-
intent: TUNNEL_INTENT_EPHEMERAL_PUBLIC,
|
|
27
|
-
requires: [],
|
|
28
|
-
supports: ['sessionTTL'],
|
|
29
|
-
stability: 'beta',
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function createNgrokTunnelProvider() {
|
|
35
|
-
return {
|
|
36
|
-
id: TUNNEL_PROVIDER_NGROK,
|
|
37
|
-
capabilities: ngrokTunnelProviderCapabilities,
|
|
38
|
-
checkAvailability: async () => {
|
|
39
|
-
const result = await checkNgrokAvailable();
|
|
40
|
-
if (result.available) {
|
|
41
|
-
return {
|
|
42
|
-
...result,
|
|
43
|
-
...getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_NGROK),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const installInfo = getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_NGROK);
|
|
47
|
-
return {
|
|
48
|
-
...result,
|
|
49
|
-
...installInfo,
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
diagnose: async () => {
|
|
53
|
-
const dependency = await checkNgrokAvailable();
|
|
54
|
-
const authtoken = await checkNgrokAuthtokenConfigured(dependency.path);
|
|
55
|
-
const network = await checkNgrokApiReachability();
|
|
56
|
-
const installInfo = getTunnelDependencyInstallInfo(TUNNEL_PROVIDER_NGROK);
|
|
57
|
-
const startupReady = dependency.available && authtoken.configured && network.reachable;
|
|
58
|
-
const providerChecks = [
|
|
59
|
-
{
|
|
60
|
-
id: 'dependency',
|
|
61
|
-
label: 'ngrok installed',
|
|
62
|
-
status: dependency.available ? 'pass' : 'fail',
|
|
63
|
-
detail: dependency.available
|
|
64
|
-
? (dependency.version || dependency.path || 'ngrok available')
|
|
65
|
-
: installInfo.message,
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
id: 'authtoken',
|
|
69
|
-
label: 'ngrok authtoken configured',
|
|
70
|
-
status: authtoken.configured ? 'pass' : 'fail',
|
|
71
|
-
detail: authtoken.configured
|
|
72
|
-
? authtoken.detail
|
|
73
|
-
: (authtoken.detail || 'Run: ngrok config add-authtoken <your-ngrok-token>'),
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
id: 'network',
|
|
77
|
-
label: 'ngrok API reachable',
|
|
78
|
-
status: network.reachable ? 'pass' : 'fail',
|
|
79
|
-
detail: network.reachable
|
|
80
|
-
? (network.status ? `HTTP ${network.status}` : 'Reachable')
|
|
81
|
-
: (network.error || 'Could not reach api.ngrok.com'),
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
providerChecks,
|
|
87
|
-
modes: [
|
|
88
|
-
{
|
|
89
|
-
mode: TUNNEL_MODE_QUICK,
|
|
90
|
-
checks: [
|
|
91
|
-
{
|
|
92
|
-
id: 'startup_readiness',
|
|
93
|
-
label: 'Provider startup readiness',
|
|
94
|
-
status: startupReady ? 'pass' : 'fail',
|
|
95
|
-
detail: startupReady
|
|
96
|
-
? 'Provider dependency, auth, and network checks passed.'
|
|
97
|
-
: 'Resolve provider checks before starting tunnels.',
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
summary: {
|
|
101
|
-
ready: startupReady,
|
|
102
|
-
failures: startupReady ? 0 : 1,
|
|
103
|
-
warnings: 0,
|
|
104
|
-
},
|
|
105
|
-
ready: startupReady,
|
|
106
|
-
blockers: startupReady ? [] : ['Resolve provider checks before starting tunnels.'],
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
};
|
|
110
|
-
},
|
|
111
|
-
start: async (request, context = {}) => {
|
|
112
|
-
if (request.mode !== TUNNEL_MODE_QUICK) {
|
|
113
|
-
throw new TunnelServiceError('mode_unsupported', `Ngrok only supports '${TUNNEL_MODE_QUICK}' mode right now`);
|
|
114
|
-
}
|
|
115
|
-
return startNgrokQuickTunnel({ port: context.activePort });
|
|
116
|
-
},
|
|
117
|
-
stop: (controller) => {
|
|
118
|
-
controller?.stop?.();
|
|
119
|
-
},
|
|
120
|
-
resolvePublicUrl: (controller) => controller?.getPublicUrl?.() ?? null,
|
|
121
|
-
getMetadata: () => null,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const REQUIRED_PROVIDER_METHODS = ['start', 'stop', 'checkAvailability', 'resolvePublicUrl'];
|
|
2
|
-
|
|
3
|
-
export function createTunnelProviderRegistry(initialProviders = []) {
|
|
4
|
-
const providers = new Map();
|
|
5
|
-
let sealed = false;
|
|
6
|
-
|
|
7
|
-
const register = (provider) => {
|
|
8
|
-
if (sealed) {
|
|
9
|
-
throw new Error('Tunnel provider registry is sealed; no further registrations allowed');
|
|
10
|
-
}
|
|
11
|
-
if (!provider || typeof provider.id !== 'string' || provider.id.trim().length === 0) {
|
|
12
|
-
throw new Error('Tunnel provider must define a non-empty id');
|
|
13
|
-
}
|
|
14
|
-
for (const method of REQUIRED_PROVIDER_METHODS) {
|
|
15
|
-
if (typeof provider[method] !== 'function') {
|
|
16
|
-
throw new Error(`Tunnel provider '${provider.id}' must implement ${method}()`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
const key = provider.id.trim().toLowerCase();
|
|
20
|
-
if (providers.has(key)) {
|
|
21
|
-
throw new Error(`Tunnel provider '${key}' is already registered`);
|
|
22
|
-
}
|
|
23
|
-
providers.set(key, provider);
|
|
24
|
-
return provider;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const get = (providerId) => {
|
|
28
|
-
if (typeof providerId !== 'string' || providerId.trim().length === 0) {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
return providers.get(providerId.trim().toLowerCase()) ?? null;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const list = () => Array.from(providers.values());
|
|
35
|
-
|
|
36
|
-
const listCapabilities = () => list().map((provider) => ({ ...provider.capabilities }));
|
|
37
|
-
|
|
38
|
-
for (const provider of initialProviders) {
|
|
39
|
-
register(provider);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const seal = () => { sealed = true; };
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
register,
|
|
46
|
-
get,
|
|
47
|
-
list,
|
|
48
|
-
listCapabilities,
|
|
49
|
-
seal,
|
|
50
|
-
};
|
|
51
|
-
}
|