diff --git a/package-lock.json b/package-lock.json index 824b8d98..071a4371 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7337,6 +7337,7 @@ "openai": "6.26.0", "partial-json": "^0.1.7", "proxy-agent": "^6.5.0", + "proxy-from-env": "^1.1.0", "typebox": "^1.1.24", "undici": "^7.19.1", "zod-to-json-schema": "^3.24.6" diff --git a/packages/ai/package.json b/packages/ai/package.json index f8d7b260..1744a2fd 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -73,11 +73,12 @@ "@aws-sdk/client-bedrock-runtime": "^3.1030.0", "@google/genai": "^1.40.0", "@mistralai/mistralai": "^2.2.0", - "typebox": "^1.1.24", "chalk": "^5.6.2", "openai": "6.26.0", "partial-json": "^0.1.7", "proxy-agent": "^6.5.0", + "proxy-from-env": "^1.1.0", + "typebox": "^1.1.24", "undici": "^7.19.1", "zod-to-json-schema": "^3.24.6" }, diff --git a/packages/ai/src/providers/openai-codex-responses.ts b/packages/ai/src/providers/openai-codex-responses.ts index dc6b6463..64542b2c 100644 --- a/packages/ai/src/providers/openai-codex-responses.ts +++ b/packages/ai/src/providers/openai-codex-responses.ts @@ -711,7 +711,35 @@ type WebSocketConstructor = new ( protocols?: string | string[] | { headers?: Record }, ) => WebSocketLike; -function getWebSocketConstructor(): WebSocketConstructor | null { +let _cachedWebsocket: WebSocketConstructor | null = null; +async function getWebSocketConstructor(): Promise { + if (_cachedWebsocket) return _cachedWebsocket; + + // bun doesn't respect http proxy envs, ref: https://github.com/oven-sh/bun/issues/15489 + // TODO: remove this when bun supports proxy envs in websocket. + if ( + process?.versions?.bun && + (process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy) + ) { + const m = await dynamicImport("proxy-from-env"); + const getProxyForUrl = (m as { getProxyForUrl: (url: string | object | URL) => string }).getProxyForUrl; + + _cachedWebsocket = class extends WebSocket { + constructor(url: string | URL, options?: string | string[] | Record) { + let _opts: Record = {}; + if (Array.isArray(options) || typeof options === "string") { + _opts = { protocols: options }; + } else { + _opts = { ...options }; + } + + const proxy = getProxyForUrl(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:")); + super(url, { ..._opts, ...(proxy ? { proxy } : {}) } as any); + } + }; + return _cachedWebsocket; + } + const ctor = (globalThis as { WebSocket?: unknown }).WebSocket; if (typeof ctor !== "function") return null; return ctor as unknown as WebSocketConstructor; @@ -760,7 +788,7 @@ function scheduleSessionWebSocketExpiry(sessionId: string, entry: CachedWebSocke } async function connectWebSocket(url: string, headers: Headers, signal?: AbortSignal): Promise { - const WebSocketCtor = getWebSocketConstructor(); + const WebSocketCtor = await getWebSocketConstructor(); if (!WebSocketCtor) { throw new Error("WebSocket transport is not available in this runtime"); }