File size: 4,627 Bytes
f778c12 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | import { normalizeOptionalString } from "@openclaw/normalization-core/string-coerce";
import { requireTlsFingerprint } from "../../../packages/gateway-client/src/client-address-utils.js";
import type { NodeHostConfig, NodeHostGatewayConfig } from "../../node-host/config.js";
import {
nodeHostCloudflareAccessConfigFromEnv,
nodeHostGatewaysShareOrigin,
} from "../../node-host/gateway-cloudflare-access.js";
import { decodePairingSetupCode } from "../../pairing/setup-code.js";
import { parsePort } from "../daemon-cli/shared.js";
type NodeGatewayOptions = {
host?: string;
port?: string | number;
contextPath?: string;
tls?: boolean;
tlsFingerprint?: string;
};
type NodePairGatewayOptions = {
host: string;
port: number;
contextPath?: string;
tls: boolean;
tlsFingerprint?: string;
bootstrapToken: string;
candidates: NodeHostGatewayConfig[];
};
type PairingSetupPayload = ReturnType<typeof decodePairingSetupCode>;
function gatewayConfigFromUrl(url: string, tlsFingerprint?: string): NodeHostGatewayConfig {
const parsed = new URL(url);
const tls = parsed.protocol === "wss:";
return {
host: parsed.hostname,
port: parsed.port ? Number.parseInt(parsed.port, 10) : tls ? 443 : 80,
...(parsed.pathname !== "/" ? { contextPath: parsed.pathname } : {}),
tls,
...(tlsFingerprint ? { tlsFingerprint } : {}),
};
}
export function resolveNodePairGatewayOptions(input: string): NodePairGatewayOptions {
return resolveNodePairGatewayPayload(decodePairingSetupCode(input));
}
/** Project a validated pairing payload into the canonical node-host candidate list. */
export function resolveNodePairGatewayPayload(
payload: PairingSetupPayload,
): NodePairGatewayOptions {
const candidates = (payload.urls ?? [payload.url]).map((url) =>
gatewayConfigFromUrl(url, url === payload.url ? payload.tlsFingerprint : undefined),
);
const primary = candidates[0]!;
return {
host: primary.host ?? "127.0.0.1",
port: primary.port ?? 18789,
...(primary.contextPath ? { contextPath: primary.contextPath } : {}),
tls: primary.tls ?? false,
...(primary.tlsFingerprint ? { tlsFingerprint: primary.tlsFingerprint } : {}),
bootstrapToken: payload.bootstrapToken,
candidates,
};
}
export function resolveNodeGatewayOptions(
options: NodeGatewayOptions,
config: NodeHostConfig | null,
pair?: NodePairGatewayOptions,
env: NodeJS.ProcessEnv = process.env,
) {
const baselineHost = pair?.host ?? config?.gateway?.host ?? "127.0.0.1";
const baselinePort = pair?.port ?? config?.gateway?.port ?? 18789;
const host = normalizeOptionalString(options.host) || baselineHost;
const port = options.port === undefined ? baselinePort : parsePort(options.port);
const endpointChanged = host !== baselineHost || (port !== null && port !== baselinePort);
const baselineTlsFingerprint = pair?.tlsFingerprint ?? config?.gateway?.tlsFingerprint;
const selectedTlsFingerprint =
options.tls === false
? undefined
: options.tlsFingerprint !== undefined
? options.tlsFingerprint
: endpointChanged
? undefined
: baselineTlsFingerprint;
const baselineTls = pair?.tls ?? config?.gateway?.tls;
const tlsFingerprint = selectedTlsFingerprint
? requireTlsFingerprint(selectedTlsFingerprint)
: undefined;
const tls =
typeof options.tls === "boolean"
? options.tls
: Boolean(tlsFingerprint) || (endpointChanged ? undefined : baselineTls);
const contextPath =
normalizeOptionalString(options.contextPath) ??
(options.contextPath !== undefined || endpointChanged
? undefined
: (pair?.contextPath ?? config?.gateway?.contextPath));
const hasExplicitEndpoint =
options.host !== undefined ||
options.port !== undefined ||
options.contextPath !== undefined ||
options.tls !== undefined ||
options.tlsFingerprint !== undefined;
const savedGatewayMatchesBaseline =
!pair ||
(config?.gateway !== undefined &&
nodeHostGatewaysShareOrigin(config.gateway, pair.candidates[0]!));
const cloudflareAccess =
(!endpointChanged && savedGatewayMatchesBaseline
? config?.gateway?.cloudflareAccess
: undefined) ?? nodeHostCloudflareAccessConfigFromEnv(env);
const gatewayCandidates =
pair && !hasExplicitEndpoint
? pair.candidates.map((candidate, index) =>
index === 0 && cloudflareAccess ? { ...candidate, cloudflareAccess } : candidate,
)
: undefined;
return {
host,
port,
contextPath,
tls,
tlsFingerprint,
cloudflareAccess,
gatewayCandidates,
};
}
|