openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
153 lines (152 loc) • 5.89 kB
JavaScript
import { r as racePromiseWithAbortSignal } from "./abort-signal-Z3A36sLL.mjs";
import "./provider-http-errors-xcK_t3aM.mjs";
import { n as buildTimeoutAbortSignal } from "./fetch-timeout-DySwRfSX.mjs";
import { c as shouldUseEnvHttpProxyForUrl } from "./proxy-env-DxV5eJIK.mjs";
import { n as resolveActiveManagedProxyTlsOptions } from "./managed-proxy-undici-BZEfvh5e.mjs";
import { _ as resolvePinnedHostnameWithPolicy, n as assertHostnameAllowedWithPolicy } from "./ssrf-D5TD9F1s.mjs";
import { a as isManagedProxyActive } from "./fetch-guard-CIAMeSj8.mjs";
import "./provider-attribution-DY9zISkn.mjs";
import "./provider-request-config-CsNs_zAb.mjs";
import { t as WebSocket } from "./websocket-CGHaToS5.mjs";
import { n as createNodeProxyAgent, r as resolveEnvNodeProxyUrlForTarget } from "./node-proxy-agent-DKC6bXwK.mjs";
import "./operation-retry-BnETYXi4.mjs";
import "./shared-CplO4ABs.mjs";
import { r as resolveProviderTransportSsrFPolicy } from "./provider-transport-fetch-EOdJgwlN.mjs";
import { inspectTlsCertificateError as inspectTlsCertificateError$1 } from "@openclaw/ai/internal/shared";
import http from "node:http";
import https from "node:https";
//#region src/infra/net/provider-websocket.ts
const DEFAULT_PROVIDER_WEBSOCKET_MAX_PAYLOAD_BYTES = 16777216;
function toHttpUrl(value) {
const url = new URL(value);
if (url.protocol === "ws:") url.protocol = "http:";
else if (url.protocol === "wss:") url.protocol = "https:";
return url.toString();
}
function targetTlsOptions(policy) {
return policy?.mode === "direct" || policy?.mode === "env-proxy" ? { ...policy.connect } : {};
}
function proxyPolicy(policy, allowPrivateProxy) {
if (!policy && !allowPrivateProxy) return;
return {
...policy,
hostnameAllowlist: void 0,
...allowPrivateProxy ? { allowPrivateNetwork: true } : {}
};
}
async function createProxyAgent(params) {
const pinnedProxy = await resolvePinnedHostnameWithPolicy(params.proxyUrl.hostname, {
policy: proxyPolicy(params.policy, params.allowPrivateProxy),
signal: params.signal
});
return createNodeProxyAgent({
mode: "explicit",
proxyUrl: params.proxyUrl,
proxyConnect: {
...params.proxyTls,
lookup: pinnedProxy.lookup
}
});
}
async function createProviderWebSocketAgent(params) {
const { dispatcherPolicy, policy, url, signal } = params;
const canDelegateEnvDns = shouldUseEnvHttpProxyForUrl(toHttpUrl(url.href));
const useManagedProxy = isManagedProxyActive() && canDelegateEnvDns;
const envProxyUrl = useManagedProxy || dispatcherPolicy?.mode !== "direct" ? resolveEnvNodeProxyUrlForTarget(url) : void 0;
let proxyUrl;
if (dispatcherPolicy?.mode === "explicit-proxy") {
try {
proxyUrl = new URL(dispatcherPolicy.proxyUrl);
} catch {
throw new Error("Invalid explicit proxy URL");
}
if (proxyUrl.protocol !== "http:" && proxyUrl.protocol !== "https:") throw new Error("Explicit proxy URL must use http or https");
} else if (dispatcherPolicy?.mode !== "direct") proxyUrl = envProxyUrl;
if (useManagedProxy) proxyUrl = envProxyUrl;
if (!proxyUrl) {
const pinned = await resolvePinnedHostnameWithPolicy(url.hostname, {
policy,
signal
});
const options = {
keepAlive: false,
...targetTlsOptions(dispatcherPolicy),
lookup: pinned.lookup
};
return url.protocol === "wss:" ? new https.Agent(options) : new http.Agent(options);
}
if (!useManagedProxy && (dispatcherPolicy || !canDelegateEnvDns)) await resolvePinnedHostnameWithPolicy(url.hostname, {
policy,
signal
});
else assertHostnameAllowedWithPolicy(url.hostname, policy);
return await createProxyAgent({
policy,
proxyUrl,
proxyTls: !useManagedProxy && (dispatcherPolicy?.mode === "explicit-proxy" || dispatcherPolicy?.mode === "env-proxy") ? dispatcherPolicy.proxyTls : resolveActiveManagedProxyTlsOptions({ proxyUrl: proxyUrl.href }),
allowPrivateProxy: useManagedProxy || dispatcherPolicy?.mode !== "explicit-proxy" || dispatcherPolicy.allowPrivateProxy === true,
signal
});
}
/** Opens a provider WebSocket through the resolved request and network policy. */
async function openProviderWebSocket(params) {
let url;
try {
url = new URL(params.url);
} catch {
throw new Error("Invalid provider WebSocket URL");
}
if (url.protocol !== "ws:" && url.protocol !== "wss:") throw new Error("Provider WebSocket URL must use ws or wss");
const policy = resolveProviderTransportSsrFPolicy({
baseUrl: toHttpUrl(params.baseUrl),
url: toHttpUrl(url.toString()),
allowPrivateNetwork: params.allowPrivateNetwork,
trustConfiguredBaseUrlOrigin: params.trustConfiguredBaseUrlOrigin
});
const { signal, cleanup } = buildTimeoutAbortSignal({
signal: params.signal,
timeoutMs: Math.max(1, params.timeoutMs),
operation: "Provider WebSocket connection"
});
let agent;
try {
signal?.throwIfAborted();
const pending = createProviderWebSocketAgent({
dispatcherPolicy: params.dispatcherPolicy,
policy,
url,
signal
});
pending.then((resolved) => signal?.aborted && resolved.destroy(), () => void 0);
agent = await racePromiseWithAbortSignal(pending, signal);
} catch (error) {
cleanup();
throw error;
}
let socket;
try {
signal?.throwIfAborted();
socket = new WebSocket(url, {
agent,
headers: Object.fromEntries(new Headers(params.headers).entries()),
maxPayload: params.maxPayloadBytes ?? DEFAULT_PROVIDER_WEBSOCKET_MAX_PAYLOAD_BYTES,
perMessageDeflate: false,
...targetTlsOptions(params.dispatcherPolicy)
});
} catch (error) {
cleanup();
agent.destroy();
throw error;
}
const onAbort = () => socket.terminate();
signal?.addEventListener("abort", onAbort, { once: true });
socket.once("open", cleanup);
socket.once("close", () => {
signal?.removeEventListener("abort", onAbort);
cleanup();
agent.destroy();
});
return socket;
}
//#endregion
export { openProviderWebSocket as n, inspectTlsCertificateError$1 as t };