@jingyi0605/codingns 0.5.1 → 0.6.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/dist/public/assets/{AdaptiveButlerPage-SffCV4Vb.js → AdaptiveButlerPage-uFwDdN-F.js} +3 -3
- package/dist/public/assets/App-BZvapsi8.js +30 -0
- package/dist/public/assets/App-CcDXqFl1.css +1 -0
- package/dist/public/assets/{BootstrapPage--zExdgfM.js → BootstrapPage-gHSoa4JN.js} +1 -1
- package/dist/public/assets/ConversationPage-z3sXtKZ7.js +4 -0
- package/dist/public/assets/{DesktopDetachPreviewPage-DvI9CIKi.js → DesktopDetachPreviewPage-4eMRxiBW.js} +1 -1
- package/dist/public/assets/DesktopWindowPage-CZcoGApB.js +2 -0
- package/dist/public/assets/FileContextPanel-C3qex8bb.js +1 -0
- package/dist/public/assets/GitSidebar-BK6H16XU.js +6 -0
- package/dist/public/assets/{MobileCreateSessionSheet-CXSKMnYn.js → MobileCreateSessionSheet-BYfbvK8o.js} +1 -1
- package/dist/public/assets/MobileSheet-Ckug8hTb.js +1 -0
- package/dist/public/assets/{MobileTopHeaderFrame-BWorAJ1C.js → MobileTopHeaderFrame-Bwv8Ovm_.js} +1 -1
- package/dist/public/assets/MobileWorkspaceSwitcherHeader-RqWrBdn2.js +1 -0
- package/dist/public/assets/RelayConnectEntryPage-D_4YL-YH.js +1 -0
- package/dist/public/assets/ServerSettingsModal-CMSm3BZU.js +1 -0
- package/dist/public/assets/SessionIndexPage-DuK10DL5.js +1 -0
- package/dist/public/assets/SettingsPage-fyD-xaHL.js +1 -0
- package/dist/public/assets/TerminalManagerPanel-CCLr1Ypk.js +1 -0
- package/dist/public/assets/{TerminalPage-CvnHXBhw.js → TerminalPage-DaooFaJ4.js} +19 -19
- package/dist/public/assets/{TerminalRuntimeFallbackModal-D7Aq186N.js → TerminalRuntimeFallbackModal-aUzjEBwP.js} +1 -1
- package/dist/public/assets/ToolFilesPage-CGxBvYG0.js +1 -0
- package/dist/public/assets/ToolGitPage-C264yjS9.js +1 -0
- package/dist/public/assets/ToolProcessesPage-BOP4A1cb.js +1 -0
- package/dist/public/assets/ToolsHomePage-CQxGiKQA.js +1 -0
- package/dist/public/assets/WorkbenchLandingPage-CvAY68ca.js +1 -0
- package/dist/public/assets/WorkbenchLayout-DGm8Tc5M.js +3 -0
- package/dist/public/assets/{WorkbenchModal-B09hC9b5.js → WorkbenchModal-0tPIIhca.js} +1 -1
- package/dist/public/assets/{WorkbenchShellRoute-DsW4mBTX.css → WorkbenchShellRoute-BF0nHWOk.css} +1 -1
- package/dist/public/assets/WorkbenchShellRoute-DBBOsJo9.js +1 -0
- package/dist/public/assets/WorkspaceDebugDetailPage-CDerFYd2.js +1 -0
- package/dist/public/assets/WorkspaceDetailPage-BlJc1CHE.js +1 -0
- package/dist/public/assets/WorkspaceHomePage-BUsKJ3lv.js +1 -0
- package/dist/public/assets/client-runtime-manager-BZpL17fc.js +1 -0
- package/dist/public/assets/{default-session-permission-mode-D0wZ9Jek.js → default-session-permission-mode-DT4SGiwp.js} +1 -1
- package/dist/public/assets/file-tree-icon-Db5LXC8h.js +31 -0
- package/dist/public/assets/index-BZLcEHW3.js +42 -0
- package/dist/public/assets/index-BbspQPC2.css +1 -0
- package/dist/public/assets/login-direct-candidate-resolver-1mxe_Oh8.js +1 -0
- package/dist/public/assets/{preferences-service-gOt2ZjKZ.js → preferences-service-DWnzl5a0.js} +1 -1
- package/dist/public/assets/relay-entry-C5_Iay0I.js +1 -0
- package/dist/public/assets/session-runtime-machine-DdLeDqQr.js +17 -0
- package/dist/public/assets/{styles-BWPBZvze.css → styles-CsEMfdaS.css} +1 -1
- package/dist/public/assets/{terminal-runtime-meta-BMT-rSEe.js → terminal-runtime-meta-cdtWVfCm.js} +1 -1
- package/dist/public/assets/{useRegisteredDebugTemplates-zMcEOGca.js → useRegisteredDebugTemplates-oFAQNIqh.js} +1 -1
- package/dist/public/assets/window-BVUB8gMK.js +1 -0
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +2 -0
- package/dist/server/config/env.js +39 -0
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/modules/client/npm-global-package-service.d.ts +7 -1
- package/dist/server/modules/client/npm-global-package-service.js +149 -43
- package/dist/server/modules/client/npm-global-package-service.js.map +1 -1
- package/dist/server/modules/client/service-update-task-service.js +6 -2
- package/dist/server/modules/client/service-update-task-service.js.map +1 -1
- package/dist/server/modules/client/service-update-types.d.ts +2 -0
- package/dist/server/modules/git/git-controller.d.ts +3 -0
- package/dist/server/modules/git/git-controller.js +3 -0
- package/dist/server/modules/git/git-controller.js.map +1 -1
- package/dist/server/modules/git/git-read-service.js +47 -1
- package/dist/server/modules/git/git-read-service.js.map +1 -1
- package/dist/server/modules/git/git-write-service.d.ts +4 -0
- package/dist/server/modules/git/git-write-service.js +24 -0
- package/dist/server/modules/git/git-write-service.js.map +1 -1
- package/dist/server/modules/git/types.d.ts +1 -0
- package/dist/server/modules/git/workspace-repo-guard.d.ts +2 -0
- package/dist/server/modules/git/workspace-repo-guard.js +24 -10
- package/dist/server/modules/git/workspace-repo-guard.js.map +1 -1
- package/dist/server/modules/parallel-sessions/parallel-session-controller.d.ts +53 -0
- package/dist/server/modules/parallel-sessions/parallel-session-controller.js +70 -0
- package/dist/server/modules/parallel-sessions/parallel-session-controller.js.map +1 -0
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.d.ts +83 -0
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.js +591 -0
- package/dist/server/modules/parallel-sessions/parallel-session-group-service.js.map +1 -0
- package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.d.ts +56 -0
- package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.js +483 -0
- package/dist/server/modules/parallel-sessions/session-isolated-workspace-service.js.map +1 -0
- package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.d.ts +16 -1
- package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-packets.js.map +1 -1
- package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.d.ts +2 -1
- package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js +18 -0
- package/dist/server/modules/relay-tunnel/crypto/relay-tunnel-protocol.js.map +1 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.d.ts +2 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.js +129 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-candidate-endpoints.js.map +1 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.d.ts +13 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js +2 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-client-context.js.map +1 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.d.ts +6 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +110 -10
- package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.d.ts +16 -4
- package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js +220 -102
- package/dist/server/modules/relay-tunnel/relay-tunnel-runtime-adapter.js.map +1 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-service.d.ts +4 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +257 -162
- package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-client.d.ts +3 -0
- package/dist/server/modules/sessions/codex-app-server-helper-client.js +56 -45
- package/dist/server/modules/sessions/codex-app-server-helper-client.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +21 -2
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/session-activity-inspector.js +6 -8
- package/dist/server/modules/sessions/session-activity-inspector.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.d.ts +11 -1
- package/dist/server/modules/sessions/session-history-service.js +204 -21
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +8 -0
- package/dist/server/modules/sessions/session-live-runtime-service.js +208 -25
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/workbench/codex-archive-watcher.d.ts +16 -0
- package/dist/server/modules/workbench/codex-archive-watcher.js +50 -0
- package/dist/server/modules/workbench/codex-archive-watcher.js.map +1 -0
- package/dist/server/modules/workbench/workbench-service.d.ts +43 -4
- package/dist/server/modules/workbench/workbench-service.js +72 -9
- package/dist/server/modules/workbench/workbench-service.js.map +1 -1
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.d.ts +1 -1
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js +26 -3
- package/dist/server/modules/workbench/workspace-panel-snapshot-service.js.map +1 -1
- package/dist/server/modules/workspace/workspace-service.d.ts +3 -1
- package/dist/server/modules/workspace/workspace-service.js +10 -2
- package/dist/server/modules/workspace/workspace-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-base-ref-resolver.d.ts +20 -0
- package/dist/server/modules/worktree/worktree-base-ref-resolver.js +111 -0
- package/dist/server/modules/worktree/worktree-base-ref-resolver.js.map +1 -0
- package/dist/server/modules/worktree/worktree-cleanup-service.js +9 -3
- package/dist/server/modules/worktree/worktree-cleanup-service.js.map +1 -1
- package/dist/server/modules/worktree/worktree-manager.d.ts +0 -1
- package/dist/server/modules/worktree/worktree-manager.js +14 -20
- package/dist/server/modules/worktree/worktree-manager.js.map +1 -1
- package/dist/server/routes/git.js +1 -0
- package/dist/server/routes/git.js.map +1 -1
- package/dist/server/routes/parallel-groups.d.ts +3 -0
- package/dist/server/routes/parallel-groups.js +9 -0
- package/dist/server/routes/parallel-groups.js.map +1 -0
- package/dist/server/server/create-server.d.ts +8 -0
- package/dist/server/server/create-server.js +48 -9
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/server/workbench-runtime-terminal-sync.d.ts +14 -0
- package/dist/server/server/workbench-runtime-terminal-sync.js +17 -0
- package/dist/server/server/workbench-runtime-terminal-sync.js.map +1 -0
- package/dist/server/storage/repositories/parallel-session-group-repository.d.ts +11 -0
- package/dist/server/storage/repositories/parallel-session-group-repository.js +131 -0
- package/dist/server/storage/repositories/parallel-session-group-repository.js.map +1 -0
- package/dist/server/storage/repositories/parallel-session-member-repository.d.ts +12 -0
- package/dist/server/storage/repositories/parallel-session-member-repository.js +150 -0
- package/dist/server/storage/repositories/parallel-session-member-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-isolated-workspace-repository.d.ts +15 -0
- package/dist/server/storage/repositories/session-isolated-workspace-repository.js +230 -0
- package/dist/server/storage/repositories/session-isolated-workspace-repository.js.map +1 -0
- package/dist/server/storage/sqlite/schema.sql +73 -0
- package/dist/server/types/domain.d.ts +72 -0
- package/dist/server/ws/workbench-ws-hub.d.ts +3 -1
- package/dist/server/ws/workbench-ws-hub.js +189 -20
- package/dist/server/ws/workbench-ws-hub.js.map +1 -1
- package/dist/server/ws/ws-server.js +141 -8
- package/dist/server/ws/ws-server.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +1 -0
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +67 -6
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +3 -0
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +46 -22
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +558 -309
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +29 -5
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +1 -1
- package/package.json +1 -1
- package/dist/public/assets/App-DUAg5urj.css +0 -1
- package/dist/public/assets/App-WOLwMld_.js +0 -30
- package/dist/public/assets/ConversationPage-D9pzRmOg.js +0 -2
- package/dist/public/assets/DesktopWindowPage-D8FpOSLE.js +0 -2
- package/dist/public/assets/FileContextPanel-C8T7oqRN.js +0 -1
- package/dist/public/assets/GitSidebar-Bze7DNnc.js +0 -6
- package/dist/public/assets/MobileSheet-Gzc14EpR.js +0 -1
- package/dist/public/assets/MobileWorkspaceSwitcherHeader-DOr4pTUq.js +0 -1
- package/dist/public/assets/ServerSettingsModal-BYB0GvTl.js +0 -1
- package/dist/public/assets/SessionIndexPage-CR3IARXX.js +0 -1
- package/dist/public/assets/SettingsPage-B_BQtnwE.js +0 -1
- package/dist/public/assets/TerminalManagerPanel-PQ-EM64j.js +0 -1
- package/dist/public/assets/ToolFilesPage-Qzkc6K2I.js +0 -1
- package/dist/public/assets/ToolGitPage-BdNDN-cV.js +0 -1
- package/dist/public/assets/ToolProcessesPage-EXJ9DHWI.js +0 -1
- package/dist/public/assets/ToolsHomePage-CjF3CWzR.js +0 -1
- package/dist/public/assets/WorkbenchLandingPage-DZPk4SmX.js +0 -1
- package/dist/public/assets/WorkbenchLayout-rwQib5In.js +0 -3
- package/dist/public/assets/WorkbenchShellRoute-Cerk5uK7.js +0 -1
- package/dist/public/assets/WorkspaceDebugDetailPage-Bcq8s-Ma.js +0 -1
- package/dist/public/assets/WorkspaceDetailPage-DNAa8pKr.js +0 -1
- package/dist/public/assets/WorkspaceHomePage-BoiLuACV.js +0 -1
- package/dist/public/assets/client-runtime-manager-CRQ-F5d2.js +0 -1
- package/dist/public/assets/file-tree-icon-Dp_xhVfD.js +0 -31
- package/dist/public/assets/index-C2G8Gmf1.js +0 -42
- package/dist/public/assets/index-CpPTUeA3.css +0 -1
- package/dist/public/assets/session-runtime-machine-Dq3pW-UF.js +0 -17
- package/dist/public/assets/window-BWqRixxq.js +0 -1
- /package/dist/public/assets/{styles-CSUx5LGe.js → styles-DRVvx_kv.js} +0 -0
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
2
3
|
import { AppError } from "../../shared/errors/app-error.js";
|
|
3
4
|
import { decryptSecret, encryptSecret } from "../../shared/utils/secret-box.js";
|
|
4
5
|
import { nowIso } from "../../shared/utils/time.js";
|
|
5
6
|
import { RelayTunnelIdentityService } from "./crypto/relay-tunnel-identity-service.js";
|
|
7
|
+
import { RelayTunnelRuntimeHttpError } from "./relay-tunnel-runtime-adapter.js";
|
|
6
8
|
import { createTaskManager } from "../tasks/task-manager.js";
|
|
7
9
|
import { HOST_TASK_TYPES } from "../tasks/task-types.js";
|
|
10
|
+
import { buildHostCandidateEndpoints } from "./relay-tunnel-candidate-endpoints.js";
|
|
11
|
+
const DEFAULT_RELAY_TUNNEL_CONTROL_BASE_URL = normalizeHttpBaseUrl("https://channel.codingns.com:1443", "defaultRelayTunnelControlBaseUrl");
|
|
12
|
+
const LEGACY_RELAY_TUNNEL_CONTROL_BASE_URL = normalizeHttpBaseUrl("https://channel.codingns.com:10247", "legacyRelayTunnelControlBaseUrl");
|
|
13
|
+
const DEFAULT_RELAY_TUNNEL_CONTROL_REQUEST_TIMEOUT_MS = 10_000;
|
|
14
|
+
const DEFAULT_RELAY_TUNNEL_CONTROL_TLS_ECDH_CURVE = "X25519";
|
|
15
|
+
// 线上 channel:1443 的 TLS 握手对 Node 默认参数比较挑,控制站请求统一收敛到单个 X25519,
|
|
16
|
+
// 避免首连阶段出现随机握手失败。
|
|
17
|
+
const RELAY_TUNNEL_CONTROL_HTTPS_AGENT = new https.Agent({
|
|
18
|
+
keepAlive: true,
|
|
19
|
+
ecdhCurve: DEFAULT_RELAY_TUNNEL_CONTROL_TLS_ECDH_CURVE
|
|
20
|
+
});
|
|
8
21
|
export class RelayTunnelService {
|
|
9
22
|
db;
|
|
10
23
|
bootstrapStateRepository;
|
|
@@ -12,6 +25,7 @@ export class RelayTunnelService {
|
|
|
12
25
|
defaultLocalTargetBaseUrl;
|
|
13
26
|
legacyLocalTargetBaseUrl;
|
|
14
27
|
controlSessionSecret;
|
|
28
|
+
controlRequestTimeoutMs;
|
|
15
29
|
fetchFn;
|
|
16
30
|
taskManager;
|
|
17
31
|
runtimeAdapter;
|
|
@@ -24,7 +38,8 @@ export class RelayTunnelService {
|
|
|
24
38
|
this.runtimeAdapter = runtimeAdapter;
|
|
25
39
|
this.identityService = new RelayTunnelIdentityService(identityRepository);
|
|
26
40
|
this.controlSessionSecret = normalizeRequiredText(options.controlSessionSecret, "controlSessionSecret");
|
|
27
|
-
this.
|
|
41
|
+
this.controlRequestTimeoutMs = normalizePositiveInt(options.controlRequestTimeoutMs, DEFAULT_RELAY_TUNNEL_CONTROL_REQUEST_TIMEOUT_MS);
|
|
42
|
+
this.fetchFn = resolveRelayTunnelControlFetch(options.fetchFn);
|
|
28
43
|
this.defaultLocalTargetBaseUrl = normalizeHttpBaseUrl(options.defaultLocalTargetBaseUrl, "defaultLocalTargetBaseUrl");
|
|
29
44
|
this.legacyLocalTargetBaseUrl = options.legacyLocalTargetBaseUrl
|
|
30
45
|
? normalizeHttpBaseUrl(options.legacyLocalTargetBaseUrl, "legacyLocalTargetBaseUrl")
|
|
@@ -63,17 +78,17 @@ export class RelayTunnelService {
|
|
|
63
78
|
}
|
|
64
79
|
async updateConfig(input) {
|
|
65
80
|
const snapshot = this.readStateSnapshot();
|
|
81
|
+
const controlBaseUrl = input.controlBaseUrl !== undefined
|
|
82
|
+
? normalizeHttpBaseUrl(input.controlBaseUrl, "controlBaseUrl")
|
|
83
|
+
: snapshot.config.controlBaseUrl;
|
|
66
84
|
const nextConfig = {
|
|
67
85
|
...snapshot.config,
|
|
68
86
|
activated: input.activated !== undefined
|
|
69
87
|
? input.activated
|
|
70
88
|
: snapshot.config.activated,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
controlBaseUrl: input.controlBaseUrl !== undefined
|
|
75
|
-
? normalizeHttpBaseUrl(input.controlBaseUrl, "controlBaseUrl")
|
|
76
|
-
: snapshot.config.controlBaseUrl,
|
|
89
|
+
// relay 入口统一跟随控制站点,同一条地址避免再出现 control 能通、relay 走死路的分裂配置。
|
|
90
|
+
relayBaseUrl: deriveRelayBaseUrlFromControlBaseUrl(controlBaseUrl),
|
|
91
|
+
controlBaseUrl,
|
|
77
92
|
localTargetBaseUrl: input.localTargetBaseUrl !== undefined
|
|
78
93
|
? normalizeHttpBaseUrl(input.localTargetBaseUrl, "localTargetBaseUrl")
|
|
79
94
|
: snapshot.config.localTargetBaseUrl,
|
|
@@ -169,10 +184,6 @@ export class RelayTunnelService {
|
|
|
169
184
|
}
|
|
170
185
|
async bindControlHost(hostLabel) {
|
|
171
186
|
const snapshot = this.readStateSnapshot();
|
|
172
|
-
if (snapshot.config.bindingId && snapshot.config.tunnelDomain) {
|
|
173
|
-
const effectiveConfig = this.resolveConfigWithIdentity(snapshot.config);
|
|
174
|
-
return this.buildStatusDto(snapshot, effectiveConfig, this.resolveEffectiveStatus(effectiveConfig));
|
|
175
|
-
}
|
|
176
187
|
const normalizedHostLabel = normalizeRequiredText(hostLabel, "hostLabel");
|
|
177
188
|
const { controlBaseUrl, accessToken, accountId } = this.requireControlSession(snapshot.config);
|
|
178
189
|
const identity = this.identityService.ensureIdentity();
|
|
@@ -219,17 +230,15 @@ export class RelayTunnelService {
|
|
|
219
230
|
const bindingId = normalizeRequiredText(input.bindingId, "bindingId");
|
|
220
231
|
const tunnelDomain = normalizeTunnelDomain(input.tunnelDomain, "tunnelDomain");
|
|
221
232
|
const identity = this.identityService.ensureIdentity();
|
|
222
|
-
const relayBaseUrl = input.relayBaseUrl !== undefined
|
|
223
|
-
? normalizeWebsocketBaseUrl(input.relayBaseUrl, "relayBaseUrl")
|
|
224
|
-
: snapshot.config.relayBaseUrl;
|
|
225
233
|
const controlBaseUrl = input.controlBaseUrl !== undefined
|
|
226
234
|
? normalizeHttpBaseUrl(input.controlBaseUrl, "controlBaseUrl")
|
|
227
235
|
: snapshot.config.controlBaseUrl;
|
|
236
|
+
const relayBaseUrl = deriveRelayBaseUrlFromControlBaseUrl(controlBaseUrl);
|
|
228
237
|
if (!relayBaseUrl || !controlBaseUrl) {
|
|
229
238
|
throw new AppError({
|
|
230
239
|
statusCode: 400,
|
|
231
240
|
errorCode: "INVALID_INPUT",
|
|
232
|
-
detail: "绑定前必须提供
|
|
241
|
+
detail: "绑定前必须提供 controlBaseUrl"
|
|
233
242
|
});
|
|
234
243
|
}
|
|
235
244
|
const timestamp = nowIso();
|
|
@@ -294,7 +303,7 @@ export class RelayTunnelService {
|
|
|
294
303
|
throw new AppError({
|
|
295
304
|
statusCode: 409,
|
|
296
305
|
errorCode: "RELAY_TUNNEL_NOT_BOUND",
|
|
297
|
-
detail: "
|
|
306
|
+
detail: "当前实例还没有绑定 CodingNS Connect"
|
|
298
307
|
});
|
|
299
308
|
}
|
|
300
309
|
const timestamp = nowIso();
|
|
@@ -352,7 +361,7 @@ export class RelayTunnelService {
|
|
|
352
361
|
});
|
|
353
362
|
}
|
|
354
363
|
readStateSnapshot() {
|
|
355
|
-
const persistedConfig = this.
|
|
364
|
+
const persistedConfig = this.reconcilePersistedConfig(this.repository.findConfig());
|
|
356
365
|
return {
|
|
357
366
|
config: persistedConfig
|
|
358
367
|
?? {
|
|
@@ -360,7 +369,7 @@ export class RelayTunnelService {
|
|
|
360
369
|
enabled: false,
|
|
361
370
|
provider: "codingns_relay",
|
|
362
371
|
relayBaseUrl: null,
|
|
363
|
-
controlBaseUrl:
|
|
372
|
+
controlBaseUrl: DEFAULT_RELAY_TUNNEL_CONTROL_BASE_URL,
|
|
364
373
|
controlAccessTokenCiphertext: null,
|
|
365
374
|
controlAccountEmail: null,
|
|
366
375
|
controlSessionExpiresAt: null,
|
|
@@ -376,26 +385,58 @@ export class RelayTunnelService {
|
|
|
376
385
|
hasPersistedConfig: persistedConfig !== null
|
|
377
386
|
};
|
|
378
387
|
}
|
|
379
|
-
|
|
388
|
+
reconcilePersistedConfig(config) {
|
|
380
389
|
if (!config) {
|
|
381
390
|
return config;
|
|
382
391
|
}
|
|
383
|
-
|
|
384
|
-
|
|
392
|
+
let nextConfig = config;
|
|
393
|
+
let changed = false;
|
|
394
|
+
if ((nextConfig.localTargetBaseUrlSource ?? "default") === "default"
|
|
395
|
+
&& nextConfig.localTargetBaseUrl !== this.defaultLocalTargetBaseUrl) {
|
|
396
|
+
// `default` 源的目标地址由当前运行模式决定,不应该把历史默认值永久粘在库里。
|
|
397
|
+
// 只要默认入口变化了,就在启动时自动收敛到新的默认值;用户显式写入的 custom 配置不动。
|
|
398
|
+
nextConfig = {
|
|
399
|
+
...nextConfig,
|
|
400
|
+
localTargetBaseUrl: this.defaultLocalTargetBaseUrl,
|
|
401
|
+
localTargetBaseUrlSource: "default",
|
|
402
|
+
updatedAt: nowIso()
|
|
403
|
+
};
|
|
404
|
+
changed = true;
|
|
385
405
|
}
|
|
386
|
-
if (
|
|
406
|
+
if (!normalizeOptionalText(nextConfig.controlBaseUrl)) {
|
|
407
|
+
// 正式包已经把官方控制站当成固定入口,Host 侧不能继续允许空值漂着,
|
|
408
|
+
// 否则前端显示的是固定地址,真正发请求时却因为配置为空直接失败。
|
|
409
|
+
nextConfig = {
|
|
410
|
+
...nextConfig,
|
|
411
|
+
controlBaseUrl: DEFAULT_RELAY_TUNNEL_CONTROL_BASE_URL,
|
|
412
|
+
updatedAt: nowIso()
|
|
413
|
+
};
|
|
414
|
+
changed = true;
|
|
415
|
+
}
|
|
416
|
+
if (nextConfig.controlBaseUrl === LEGACY_RELAY_TUNNEL_CONTROL_BASE_URL) {
|
|
417
|
+
// 历史正式版把官方控制站写成了旧端口。正式包又不允许用户手改控制站地址,
|
|
418
|
+
// 所以这里必须在读取配置时自动迁移,否则老用户会永远卡在登录阶段。
|
|
419
|
+
nextConfig = {
|
|
420
|
+
...nextConfig,
|
|
421
|
+
controlBaseUrl: DEFAULT_RELAY_TUNNEL_CONTROL_BASE_URL,
|
|
422
|
+
updatedAt: nowIso()
|
|
423
|
+
};
|
|
424
|
+
changed = true;
|
|
425
|
+
}
|
|
426
|
+
const managedRelayBaseUrl = deriveRelayBaseUrlFromControlBaseUrl(nextConfig.controlBaseUrl);
|
|
427
|
+
if (nextConfig.relayBaseUrl !== managedRelayBaseUrl) {
|
|
428
|
+
nextConfig = {
|
|
429
|
+
...nextConfig,
|
|
430
|
+
relayBaseUrl: managedRelayBaseUrl,
|
|
431
|
+
updatedAt: nowIso()
|
|
432
|
+
};
|
|
433
|
+
changed = true;
|
|
434
|
+
}
|
|
435
|
+
if (!changed) {
|
|
387
436
|
return config;
|
|
388
437
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const migratedConfig = {
|
|
392
|
-
...config,
|
|
393
|
-
localTargetBaseUrl: this.defaultLocalTargetBaseUrl,
|
|
394
|
-
localTargetBaseUrlSource: "default",
|
|
395
|
-
updatedAt: nowIso()
|
|
396
|
-
};
|
|
397
|
-
this.repository.upsertConfig(migratedConfig);
|
|
398
|
-
return migratedConfig;
|
|
438
|
+
this.repository.upsertConfig(nextConfig);
|
|
439
|
+
return nextConfig;
|
|
399
440
|
}
|
|
400
441
|
resolveEffectiveStatus(config) {
|
|
401
442
|
const persisted = this.repository.findStatus();
|
|
@@ -470,6 +511,25 @@ export class RelayTunnelService {
|
|
|
470
511
|
const effectiveConfig = this.resolveConfigWithIdentity(latestSnapshot.config);
|
|
471
512
|
return this.buildStatusDto(latestSnapshot, effectiveConfig, this.resolveEffectiveStatus(effectiveConfig));
|
|
472
513
|
}
|
|
514
|
+
if (shouldResetStaleRelayBinding(error)) {
|
|
515
|
+
const nextConfig = this.clearBoundState(snapshot.config, {
|
|
516
|
+
updatedAt: nowIso()
|
|
517
|
+
});
|
|
518
|
+
const nextStatus = {
|
|
519
|
+
...buildSkeletonStatus("unbound", nextConfig, {
|
|
520
|
+
observedAt: nowIso()
|
|
521
|
+
}),
|
|
522
|
+
lastError: error instanceof Error ? error.message : String(error)
|
|
523
|
+
};
|
|
524
|
+
this.db.transaction(() => {
|
|
525
|
+
this.repository.upsertConfig(nextConfig);
|
|
526
|
+
this.repository.upsertStatus(nextStatus);
|
|
527
|
+
})();
|
|
528
|
+
return this.buildStatusDto({
|
|
529
|
+
config: nextConfig,
|
|
530
|
+
hasPersistedConfig: true
|
|
531
|
+
}, nextConfig, nextStatus);
|
|
532
|
+
}
|
|
473
533
|
const failedStatus = {
|
|
474
534
|
...buildSkeletonStatus("error", snapshot.config, {
|
|
475
535
|
observedAt: nowIso()
|
|
@@ -532,6 +592,14 @@ export class RelayTunnelService {
|
|
|
532
592
|
this.repository.upsertConfig(nextConfig);
|
|
533
593
|
return nextConfig;
|
|
534
594
|
}
|
|
595
|
+
clearBoundState(config, options) {
|
|
596
|
+
return {
|
|
597
|
+
...config,
|
|
598
|
+
bindingId: null,
|
|
599
|
+
tunnelDomain: null,
|
|
600
|
+
updatedAt: options.updatedAt
|
|
601
|
+
};
|
|
602
|
+
}
|
|
535
603
|
isBootstrapInitialized() {
|
|
536
604
|
return this.bootstrapStateRepository.getState().initialized;
|
|
537
605
|
}
|
|
@@ -568,16 +636,23 @@ export class RelayTunnelService {
|
|
|
568
636
|
}
|
|
569
637
|
async requestControlApi(input) {
|
|
570
638
|
let response;
|
|
639
|
+
const controller = new AbortController();
|
|
640
|
+
const timeoutId = setTimeout(() => controller.abort(), this.controlRequestTimeoutMs);
|
|
571
641
|
try {
|
|
572
|
-
|
|
642
|
+
const requestUrl = new URL(input.path, ensureTrailingSlash(input.controlBaseUrl)).toString();
|
|
643
|
+
response = await this.fetchFn(requestUrl, {
|
|
573
644
|
method: input.method,
|
|
574
645
|
headers: input.headers,
|
|
575
|
-
body: input.body
|
|
646
|
+
body: input.body,
|
|
647
|
+
signal: controller.signal
|
|
576
648
|
});
|
|
577
649
|
}
|
|
578
650
|
catch (error) {
|
|
579
651
|
throw buildControlFetchError(error, input.controlBaseUrl, input.failurePrefix);
|
|
580
652
|
}
|
|
653
|
+
finally {
|
|
654
|
+
clearTimeout(timeoutId);
|
|
655
|
+
}
|
|
581
656
|
if (!response.ok) {
|
|
582
657
|
throw await buildControlApiError(response, input.controlBaseUrl, input.failurePrefix);
|
|
583
658
|
}
|
|
@@ -605,133 +680,6 @@ function buildSkeletonStatus(phase, config, overrides) {
|
|
|
605
680
|
observedAt: overrides?.observedAt ?? null
|
|
606
681
|
};
|
|
607
682
|
}
|
|
608
|
-
function buildHostCandidateEndpoints(config) {
|
|
609
|
-
const endpoints = new Map();
|
|
610
|
-
const relayEndpoint = buildRelayPublicUrl(config);
|
|
611
|
-
if (relayEndpoint) {
|
|
612
|
-
endpoints.set(relayEndpoint, {
|
|
613
|
-
endpointId: `relay:${relayEndpoint}`,
|
|
614
|
-
kind: "relay",
|
|
615
|
-
url: relayEndpoint,
|
|
616
|
-
priority: 400,
|
|
617
|
-
expiresAt: null,
|
|
618
|
-
source: "host_reported"
|
|
619
|
-
});
|
|
620
|
-
}
|
|
621
|
-
for (const localCandidateUrl of buildLocalCandidateUrls(config.localTargetBaseUrl)) {
|
|
622
|
-
endpoints.set(localCandidateUrl, {
|
|
623
|
-
endpointId: `host_reported:${localCandidateUrl}`,
|
|
624
|
-
kind: classifyCandidateEndpointKind(localCandidateUrl),
|
|
625
|
-
url: localCandidateUrl,
|
|
626
|
-
priority: resolveCandidateEndpointPriority(localCandidateUrl),
|
|
627
|
-
expiresAt: null,
|
|
628
|
-
source: "host_reported"
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
return Array.from(endpoints.values()).sort((left, right) => {
|
|
632
|
-
if (left.priority !== right.priority) {
|
|
633
|
-
return left.priority - right.priority;
|
|
634
|
-
}
|
|
635
|
-
return left.url.localeCompare(right.url);
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
function buildRelayPublicUrl(config) {
|
|
639
|
-
if (!config.tunnelDomain || !config.controlBaseUrl) {
|
|
640
|
-
return null;
|
|
641
|
-
}
|
|
642
|
-
try {
|
|
643
|
-
const controlUrl = new URL(config.controlBaseUrl);
|
|
644
|
-
controlUrl.hostname = config.tunnelDomain.trim().toLowerCase();
|
|
645
|
-
controlUrl.pathname = "/";
|
|
646
|
-
controlUrl.search = "";
|
|
647
|
-
controlUrl.hash = "";
|
|
648
|
-
return controlUrl.toString().replace(/\/$/, "");
|
|
649
|
-
}
|
|
650
|
-
catch {
|
|
651
|
-
return null;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
function buildLocalCandidateUrls(localTargetBaseUrl) {
|
|
655
|
-
let parsed;
|
|
656
|
-
try {
|
|
657
|
-
parsed = new URL(localTargetBaseUrl);
|
|
658
|
-
}
|
|
659
|
-
catch {
|
|
660
|
-
return [];
|
|
661
|
-
}
|
|
662
|
-
const candidates = new Set();
|
|
663
|
-
const hostname = parsed.hostname.trim().toLowerCase();
|
|
664
|
-
candidates.add(normalizeUrlWithoutTrailingSlash(parsed.toString()));
|
|
665
|
-
if (hostname === "0.0.0.0" || hostname === "::" || hostname === "::0") {
|
|
666
|
-
for (const networkAddress of listPrivateIpv4Addresses()) {
|
|
667
|
-
const candidateUrl = new URL(parsed.toString());
|
|
668
|
-
candidateUrl.hostname = networkAddress;
|
|
669
|
-
candidates.add(normalizeUrlWithoutTrailingSlash(candidateUrl.toString()));
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
if (hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1") {
|
|
673
|
-
for (const networkAddress of listPrivateIpv4Addresses()) {
|
|
674
|
-
const candidateUrl = new URL(parsed.toString());
|
|
675
|
-
candidateUrl.hostname = networkAddress;
|
|
676
|
-
candidates.add(normalizeUrlWithoutTrailingSlash(candidateUrl.toString()));
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return Array.from(candidates);
|
|
680
|
-
}
|
|
681
|
-
function listPrivateIpv4Addresses() {
|
|
682
|
-
const interfaces = os.networkInterfaces();
|
|
683
|
-
const candidates = new Set();
|
|
684
|
-
for (const entries of Object.values(interfaces)) {
|
|
685
|
-
for (const entry of entries ?? []) {
|
|
686
|
-
if (!entry || entry.family !== "IPv4" || entry.internal) {
|
|
687
|
-
continue;
|
|
688
|
-
}
|
|
689
|
-
if (!isPrivateIpv4Address(entry.address)) {
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
candidates.add(entry.address);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
return Array.from(candidates).sort();
|
|
696
|
-
}
|
|
697
|
-
function isPrivateIpv4Address(address) {
|
|
698
|
-
return (/^10\./.test(address)
|
|
699
|
-
|| /^192\.168\./.test(address)
|
|
700
|
-
|| /^172\.(1[6-9]|2\d|3[0-1])\./.test(address));
|
|
701
|
-
}
|
|
702
|
-
function classifyCandidateEndpointKind(candidateUrl) {
|
|
703
|
-
try {
|
|
704
|
-
const hostname = new URL(candidateUrl).hostname.toLowerCase();
|
|
705
|
-
if (hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1") {
|
|
706
|
-
return "loopback";
|
|
707
|
-
}
|
|
708
|
-
if (isPrivateIpv4Address(hostname)) {
|
|
709
|
-
return "lan";
|
|
710
|
-
}
|
|
711
|
-
return "custom";
|
|
712
|
-
}
|
|
713
|
-
catch {
|
|
714
|
-
return "custom";
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
function resolveCandidateEndpointPriority(candidateUrl) {
|
|
718
|
-
const kind = classifyCandidateEndpointKind(candidateUrl);
|
|
719
|
-
switch (kind) {
|
|
720
|
-
case "loopback":
|
|
721
|
-
return 100;
|
|
722
|
-
case "lan":
|
|
723
|
-
return 200;
|
|
724
|
-
case "tailscale":
|
|
725
|
-
return 300;
|
|
726
|
-
case "relay":
|
|
727
|
-
return 400;
|
|
728
|
-
default:
|
|
729
|
-
return 500;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
function normalizeUrlWithoutTrailingSlash(value) {
|
|
733
|
-
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
734
|
-
}
|
|
735
683
|
function isBound(config) {
|
|
736
684
|
return Boolean(config.bindingId && config.tunnelDomain);
|
|
737
685
|
}
|
|
@@ -849,6 +797,14 @@ function normalizeWebsocketBaseUrl(value, field) {
|
|
|
849
797
|
: parsed.protocol;
|
|
850
798
|
return `${normalizedProtocol}//${parsed.host}${pathname}`;
|
|
851
799
|
}
|
|
800
|
+
function deriveRelayBaseUrlFromControlBaseUrl(controlBaseUrl) {
|
|
801
|
+
const normalizedControlBaseUrl = normalizeHttpBaseUrl(controlBaseUrl, "controlBaseUrl");
|
|
802
|
+
if (!normalizedControlBaseUrl) {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
const relayUrl = new URL("relay", ensureTrailingSlash(normalizedControlBaseUrl));
|
|
806
|
+
return normalizeWebsocketBaseUrl(relayUrl.toString(), "relayBaseUrl");
|
|
807
|
+
}
|
|
852
808
|
function requireConfiguredControlBaseUrl(config) {
|
|
853
809
|
const controlBaseUrl = normalizeOptionalText(config.controlBaseUrl);
|
|
854
810
|
if (!controlBaseUrl) {
|
|
@@ -873,6 +829,15 @@ function clearRelayTunnelControlSession(config, options) {
|
|
|
873
829
|
updatedAt: options.updatedAt
|
|
874
830
|
};
|
|
875
831
|
}
|
|
832
|
+
function shouldResetStaleRelayBinding(error) {
|
|
833
|
+
if (!(error instanceof RelayTunnelRuntimeHttpError)) {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
return (error.errorCode === "TUNNEL_NOT_FOUND"
|
|
837
|
+
|| error.errorCode === "BINDING_NOT_FOUND"
|
|
838
|
+
|| error.errorCode === "HOST_AUTH_INVALID"
|
|
839
|
+
|| error.errorCode === "HOST_BINDING_MISMATCH");
|
|
840
|
+
}
|
|
876
841
|
async function buildControlApiError(response, controlBaseUrl, failurePrefix) {
|
|
877
842
|
const detail = await readControlApiErrorDetail(response);
|
|
878
843
|
if (response.status === 401 || response.status === 403) {
|
|
@@ -934,6 +899,9 @@ function readJsonErrorText(value) {
|
|
|
934
899
|
function resolveFetchErrorDetail(error) {
|
|
935
900
|
if (error instanceof Error) {
|
|
936
901
|
const code = readFetchErrorCode(error);
|
|
902
|
+
if (error.name === "AbortError") {
|
|
903
|
+
return "请求超时。";
|
|
904
|
+
}
|
|
937
905
|
if (code === "ECONNREFUSED") {
|
|
938
906
|
return "连接被目标服务器拒绝。";
|
|
939
907
|
}
|
|
@@ -956,6 +924,133 @@ function readFetchErrorCode(error) {
|
|
|
956
924
|
: undefined;
|
|
957
925
|
return typeof cause?.code === "string" ? cause.code : null;
|
|
958
926
|
}
|
|
927
|
+
function normalizePositiveInt(value, fallback) {
|
|
928
|
+
if (value === null || value === undefined || !Number.isInteger(value) || value <= 0) {
|
|
929
|
+
return fallback;
|
|
930
|
+
}
|
|
931
|
+
return value;
|
|
932
|
+
}
|
|
933
|
+
function resolveRelayTunnelControlFetch(fetchFn) {
|
|
934
|
+
if (fetchFn) {
|
|
935
|
+
return fetchFn;
|
|
936
|
+
}
|
|
937
|
+
if (isBuiltinNodeFetch(globalThis.fetch)) {
|
|
938
|
+
return createRelayTunnelControlFetch();
|
|
939
|
+
}
|
|
940
|
+
return globalThis.fetch;
|
|
941
|
+
}
|
|
942
|
+
function isBuiltinNodeFetch(fetchFn) {
|
|
943
|
+
const source = Function.prototype.toString.call(fetchFn);
|
|
944
|
+
return source.includes("internal/deps/undici/undici");
|
|
945
|
+
}
|
|
946
|
+
function createRelayTunnelControlFetch() {
|
|
947
|
+
return async (input, init) => {
|
|
948
|
+
const requestUrl = resolveFetchInputUrl(input);
|
|
949
|
+
const parsedUrl = new URL(requestUrl);
|
|
950
|
+
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
951
|
+
throw new TypeError(`Unsupported protocol for relay tunnel control fetch: ${parsedUrl.protocol}`);
|
|
952
|
+
}
|
|
953
|
+
return await new Promise((resolve, reject) => {
|
|
954
|
+
const transport = parsedUrl.protocol === "https:" ? https : http;
|
|
955
|
+
const headers = normalizeFetchHeaders(init?.headers);
|
|
956
|
+
const body = normalizeFetchBody(init?.body);
|
|
957
|
+
const abortHandler = () => request.destroy(createAbortRequestError());
|
|
958
|
+
const request = transport.request(parsedUrl, {
|
|
959
|
+
method: init?.method ?? "GET",
|
|
960
|
+
headers,
|
|
961
|
+
agent: parsedUrl.protocol === "https:" ? RELAY_TUNNEL_CONTROL_HTTPS_AGENT : undefined
|
|
962
|
+
}, (response) => {
|
|
963
|
+
const chunks = [];
|
|
964
|
+
response.on("data", (chunk) => {
|
|
965
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
966
|
+
});
|
|
967
|
+
response.on("end", () => {
|
|
968
|
+
cleanup();
|
|
969
|
+
resolve(new Response(Buffer.concat(chunks), {
|
|
970
|
+
status: response.statusCode ?? 500,
|
|
971
|
+
headers: buildFetchResponseHeaders(response.headers)
|
|
972
|
+
}));
|
|
973
|
+
});
|
|
974
|
+
response.on("error", (error) => {
|
|
975
|
+
cleanup();
|
|
976
|
+
reject(error);
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
function cleanup() {
|
|
980
|
+
init?.signal?.removeEventListener("abort", abortHandler);
|
|
981
|
+
}
|
|
982
|
+
request.on("error", (error) => {
|
|
983
|
+
cleanup();
|
|
984
|
+
reject(error);
|
|
985
|
+
});
|
|
986
|
+
if (init?.signal) {
|
|
987
|
+
if (init.signal.aborted) {
|
|
988
|
+
cleanup();
|
|
989
|
+
request.destroy(createAbortRequestError());
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
init.signal.addEventListener("abort", abortHandler, { once: true });
|
|
993
|
+
}
|
|
994
|
+
if (body !== null) {
|
|
995
|
+
request.write(body);
|
|
996
|
+
}
|
|
997
|
+
request.end();
|
|
998
|
+
});
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
function resolveFetchInputUrl(input) {
|
|
1002
|
+
if (typeof input === "string") {
|
|
1003
|
+
return input;
|
|
1004
|
+
}
|
|
1005
|
+
if (input instanceof URL) {
|
|
1006
|
+
return input.toString();
|
|
1007
|
+
}
|
|
1008
|
+
return input.url;
|
|
1009
|
+
}
|
|
1010
|
+
function normalizeFetchHeaders(headers) {
|
|
1011
|
+
if (!headers) {
|
|
1012
|
+
return {};
|
|
1013
|
+
}
|
|
1014
|
+
return Object.fromEntries(new Headers(headers).entries());
|
|
1015
|
+
}
|
|
1016
|
+
function normalizeFetchBody(body) {
|
|
1017
|
+
if (body === null || body === undefined) {
|
|
1018
|
+
return null;
|
|
1019
|
+
}
|
|
1020
|
+
if (typeof body === "string" || Buffer.isBuffer(body)) {
|
|
1021
|
+
return body;
|
|
1022
|
+
}
|
|
1023
|
+
if (body instanceof URLSearchParams) {
|
|
1024
|
+
return body.toString();
|
|
1025
|
+
}
|
|
1026
|
+
if (body instanceof ArrayBuffer) {
|
|
1027
|
+
return Buffer.from(body);
|
|
1028
|
+
}
|
|
1029
|
+
if (ArrayBuffer.isView(body)) {
|
|
1030
|
+
return Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
1031
|
+
}
|
|
1032
|
+
throw new TypeError("Unsupported relay tunnel control request body type");
|
|
1033
|
+
}
|
|
1034
|
+
function buildFetchResponseHeaders(headers) {
|
|
1035
|
+
const responseHeaders = new Headers();
|
|
1036
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
1037
|
+
if (Array.isArray(value)) {
|
|
1038
|
+
for (const item of value) {
|
|
1039
|
+
responseHeaders.append(name, item);
|
|
1040
|
+
}
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (typeof value === "string") {
|
|
1044
|
+
responseHeaders.set(name, value);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
return responseHeaders;
|
|
1048
|
+
}
|
|
1049
|
+
function createAbortRequestError() {
|
|
1050
|
+
const error = new Error("This operation was aborted");
|
|
1051
|
+
error.name = "AbortError";
|
|
1052
|
+
return error;
|
|
1053
|
+
}
|
|
959
1054
|
function appendControlApiDetail(detail) {
|
|
960
1055
|
const normalized = normalizeOptionalText(detail);
|
|
961
1056
|
if (!normalized) {
|