/** * Bookworm Smart Assistant - Node.js 代理引导脚本 * * 解决两个代理穿透问题: * 1. Node.js 20 undici fetch 不自动使用 https_proxy 环境变量 * 2. ws (WebSocket) 库不自动走代理 —— 导致 Browserbase MCP 连接失败 * * 用法: NODE_OPTIONS="--require /path/to/proxy-bootstrap.js" node app.js */ 'use strict'; const proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.HTTP_PROXY; if (proxyUrl) { // 1) Patch undici fetch 走代理 try { const { ProxyAgent, setGlobalDispatcher } = require('undici'); const agent = new ProxyAgent(proxyUrl); setGlobalDispatcher(agent); } catch (e) { // undici 不可用时静默降级 } // 2) Patch ws WebSocket 走代理(解决 Browserbase CDP 连接超时) try { const Module = require('module'); const path = require('path'); const originalLoad = Module._load; // 缓存代理 agent(延迟初始化) let proxyAgent = null; let wsModulePath = null; Module._load = function patchedLoad(request, parent, isMain) { const result = originalLoad.apply(this, arguments); // 只在首次加载 ws 模块时 patch if (request === 'ws' && typeof result === 'function' && !result.__proxyPatched) { // 记录 ws 模块的路径,用于后续解析 https-proxy-agent try { wsModulePath = path.dirname(Module._resolveFilename('ws', parent)); } catch (e) { // ignore } const OrigWS = result; // 用 Proxy 包装,保持完整的原型链和 instanceof 检查 const handler = { construct(target, args) { const address = args[0]; // 只对外部 wss:// 连接注入代理 if (typeof address === 'string' && address.startsWith('wss://')) { // 延迟创建代理 agent,从 ws 所在的 node_modules 解析 https-proxy-agent if (!proxyAgent) { try { const agentPaths = []; if (wsModulePath) agentPaths.push(wsModulePath); // 也尝试 ws 的父目录(node_modules 层级) if (wsModulePath) agentPaths.push(path.join(wsModulePath, '..')); const agentModPath = require.resolve('https-proxy-agent', { paths: agentPaths }); const { HttpsProxyAgent } = require(agentModPath); proxyAgent = new HttpsProxyAgent(proxyUrl); } catch (e) { // 最后尝试全局解析 try { const { HttpsProxyAgent } = require('https-proxy-agent'); proxyAgent = new HttpsProxyAgent(proxyUrl); } catch (e2) { // 无法获取代理 agent,放弃 } } } if (proxyAgent) { if (args.length <= 1) { // new WebSocket(url) → new WebSocket(url, { agent }) args = [address, { agent: proxyAgent }]; } else if (args.length === 2) { const second = args[1]; if (typeof second === 'object' && second !== null && !Array.isArray(second)) { // new WebSocket(url, options) → 注入 agent if (!second.agent) second.agent = proxyAgent; } else { // new WebSocket(url, protocols) → 追加 options args = [address, second, { agent: proxyAgent }]; } } else if (args.length >= 3) { // new WebSocket(url, protocols, options) const opts = args[2]; if (typeof opts === 'object' && opts !== null) { if (!opts.agent) opts.agent = proxyAgent; } else { args[2] = { agent: proxyAgent }; } } } } return new target(...args); } }; const proxied = new Proxy(OrigWS, handler); proxied.__proxyPatched = true; // 更新模块缓存,让后续 require('ws') 也返回 patched 版本 try { const resolvedPath = Module._resolveFilename('ws', parent); if (Module._cache[resolvedPath]) { Module._cache[resolvedPath].exports = proxied; } } catch (e) { // ignore } return proxied; } return result; }; } catch (e) { // ws patch 失败时静默降级 } }