diff --git a/open-sse/index.js b/open-sse/index.js index fe73da0..60eb3ef 100644 --- a/open-sse/index.js +++ b/open-sse/index.js @@ -1,3 +1,6 @@ +// Patch global fetch with proxy support (must be first) +import "./utils/proxyFetch.js"; + // Config export { PROVIDERS, OAUTH_ENDPOINTS, CACHE_TTL, DEFAULT_MAX_TOKENS, CLAUDE_SYSTEM_PROMPT, COOLDOWN_MS, BACKOFF_CONFIG } from "./config/constants.js"; export { diff --git a/open-sse/utils/proxyFetch.js b/open-sse/utils/proxyFetch.js new file mode 100644 index 0000000..0e4acc4 --- /dev/null +++ b/open-sse/utils/proxyFetch.js @@ -0,0 +1,84 @@ + +const isCloud = typeof caches !== "undefined" && typeof caches === "object"; + +const originalFetch = globalThis.fetch; +let proxyAgent = null; +let socksAgent = null; + +/** + * Get proxy URL from environment + */ +function getProxyUrl(targetUrl) { + const noProxy = process.env.NO_PROXY || process.env.no_proxy; + + if (noProxy) { + const hostname = new URL(targetUrl).hostname.toLowerCase(); + const patterns = noProxy.split(",").map(p => p.trim().toLowerCase()); + + const shouldBypass = patterns.some(pattern => { + if (pattern === "*") return true; + if (pattern.startsWith(".")) return hostname.endsWith(pattern) || hostname === pattern.slice(1); + return hostname === pattern || hostname.endsWith(`.${pattern}`); + }); + + if (shouldBypass) return null; + } + + const protocol = new URL(targetUrl).protocol; + + if (protocol === "https:") { + return process.env.HTTPS_PROXY || process.env.https_proxy || + process.env.ALL_PROXY || process.env.all_proxy; + } + + return process.env.HTTP_PROXY || process.env.http_proxy || + process.env.ALL_PROXY || process.env.all_proxy; +} + +/** + * Create proxy agent lazily + */ +async function getAgent(proxyUrl) { + const proxyProtocol = new URL(proxyUrl).protocol; + + if (proxyProtocol === "socks:" || proxyProtocol === "socks5:" || proxyProtocol === "socks4:") { + if (!socksAgent) { + const { SocksProxyAgent } = await import("socks-proxy-agent"); + socksAgent = new SocksProxyAgent(proxyUrl); + } + return socksAgent; + } + + if (!proxyAgent) { + const { HttpsProxyAgent } = await import("https-proxy-agent"); + proxyAgent = new HttpsProxyAgent(proxyUrl); + } + return proxyAgent; +} + +/** + * Patched fetch with proxy support and fallback to direct connection + */ +async function patchedFetch(url, options = {}) { + const targetUrl = typeof url === "string" ? url : url.toString(); + const proxyUrl = getProxyUrl(targetUrl); + + if (proxyUrl) { + try { + const agent = await getAgent(proxyUrl); + return await originalFetch(url, { ...options, dispatcher: agent }); + } catch (proxyError) { + // Fallback to direct connection if proxy fails + console.warn(`[ProxyFetch] Proxy failed, falling back to direct: ${proxyError.message}`); + return originalFetch(url, options); + } + } + + return originalFetch(url, options); +} + +if (!isCloud) { + globalThis.fetch = patchedFetch; +} + +export default isCloud ? originalFetch : patchedFetch; diff --git a/package.json b/package.json index 19ab009..96404ee 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "express": "^5.2.1", "fs": "^0.0.1-security", "http-proxy-middleware": "^3.0.5", + "https-proxy-agent": "^7.0.6", "jose": "^6.1.3", "lowdb": "^7.0.1", "next": "^15.2.0", @@ -21,6 +22,7 @@ "ora": "^5.4.1", "react": "19.2.1", "react-dom": "19.2.1", + "socks-proxy-agent": "^8.0.4", "undici": "^7.16.0", "uuid": "^13.0.0", "zustand": "^5.0.9"