@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
325 lines (324 loc) • 13.7 kB
JavaScript
/**
* Enhanced proxy-aware fetch implementation for AI SDK providers
* Supports HTTP/HTTPS, SOCKS4/5, authentication, and NO_PROXY bypass
* Lightweight implementation extracted from research of major proxy packages
*/
import { logger } from "../utils/logger.js";
import { shouldBypassProxy } from "./utils/noProxyUtils.js";
/**
* Mask credentials in proxy URLs for secure logging
* Replaces user:password@ with [CREDENTIALS_MASKED]@
*/
function maskProxyCredentials(proxyUrl) {
if (!proxyUrl || proxyUrl === "NOT_SET") {
return proxyUrl || "NOT_SET";
}
try {
// Handle URLs with credentials: http://user:password@proxy:port
const credentialPattern = /(:\/\/)([^@:]+):([^@]+)@/;
if (credentialPattern.test(proxyUrl)) {
return proxyUrl.replace(credentialPattern, "$1[CREDENTIALS_MASKED]@");
}
// Return original URL if no credentials found
return proxyUrl;
}
catch {
// If URL parsing fails, still mask potential credentials pattern
return proxyUrl.replace(/(:\/\/)([^@:]+):([^@]+)@/, "$1[CREDENTIALS_MASKED]@");
}
}
/**
* Mask all proxy credentials in an environment variables object
*/
function maskProxyEnvVars(envVars) {
const masked = {};
for (const [key, value] of Object.entries(envVars)) {
masked[key] = maskProxyCredentials(value);
}
return masked;
}
/**
* Parse proxy URL with authentication support
*/
function parseProxyUrl(proxyUrl) {
try {
const url = new URL(proxyUrl);
const config = {
protocol: url.protocol,
hostname: url.hostname,
port: parseInt(url.port) || getDefaultPort(url.protocol),
cleanUrl: `${url.protocol}//${url.hostname}:${url.port || getDefaultPort(url.protocol)}`,
};
// Extract authentication if present
if (url.username && url.password) {
config.auth = {
username: decodeURIComponent(url.username),
password: decodeURIComponent(url.password),
};
}
return config;
}
catch (error) {
logger.error("[Proxy] Failed to parse proxy URL", {
proxyUrl: maskProxyCredentials(proxyUrl),
error,
});
throw new Error(`Invalid proxy URL: ${maskProxyCredentials(proxyUrl)}`);
}
}
/**
* Get default port for protocol
*/
function getDefaultPort(protocol) {
switch (protocol) {
case "http:":
return 8080;
case "https:":
return 8080;
case "socks4:":
return 1080;
case "socks5:":
return 1080;
default:
return 8080;
}
}
/**
* Select appropriate proxy URL based on target and environment
*/
function selectProxyUrl(targetUrl) {
// Check NO_PROXY bypass first
if (shouldBypassProxy(targetUrl)) {
logger.debug("[Proxy] Bypassing proxy due to NO_PROXY", { targetUrl });
return null;
}
try {
const url = new URL(targetUrl);
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
const allProxy = process.env.ALL_PROXY || process.env.all_proxy;
const socksProxy = process.env.SOCKS_PROXY || process.env.socks_proxy;
// Priority: Protocol-specific > ALL_PROXY > SOCKS_PROXY
if (url.protocol === "https:" && httpsProxy) {
return httpsProxy;
}
if (url.protocol === "http:" && httpProxy) {
return httpProxy;
}
if (allProxy) {
return allProxy;
}
if (socksProxy) {
return socksProxy;
}
return null;
}
catch (error) {
logger.warn("[Proxy] Error selecting proxy URL", { targetUrl, error });
return null;
}
}
/**
* Create appropriate proxy agent based on protocol
*/
async function createProxyAgent(proxyUrl) {
const parsed = parseProxyUrl(proxyUrl);
logger.debug("[Proxy] Creating proxy agent", {
protocol: parsed.protocol,
hostname: parsed.hostname,
port: parsed.port,
hasAuth: !!parsed.auth,
});
switch (parsed.protocol) {
case "http:":
case "https:": {
// Use existing undici ProxyAgent for HTTP/HTTPS
const { ProxyAgent } = await import("undici");
return new ProxyAgent(proxyUrl);
}
case "socks4:":
case "socks5:": {
// SOCKS proxy support is not included in the build to avoid optional dependencies
throw new Error(`SOCKS proxy support requires 'proxy-agent' package. ` +
`Install it with: npm install proxy-agent`);
}
default:
throw new Error(`Unsupported proxy protocol: ${parsed.protocol}`);
}
}
// ==================== ENHANCED PROXY FETCH FUNCTION ====================
/**
* Create a proxy-aware fetch function with enhanced capabilities
* Supports HTTP/HTTPS, SOCKS4/5, authentication, and NO_PROXY bypass
*/
export function createProxyFetch() {
// Detect ALL proxy-related environment variables
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
const allProxy = process.env.ALL_PROXY || process.env.all_proxy;
const socksProxy = process.env.SOCKS_PROXY || process.env.socks_proxy;
const noProxy = process.env.NO_PROXY || process.env.no_proxy;
// ENHANCED LOGGING: Capture ALL enhanced proxy-related environment variables with credential masking
logger.debug("[Proxy Fetch] 🔍 ENHANCED_PROXY_ENV_DETECTION", {
// Enhanced proxy environment variables (credentials masked)
httpProxy: maskProxyCredentials(httpProxy || "NOT_SET"),
httpsProxy: maskProxyCredentials(httpsProxy || "NOT_SET"),
allProxy: maskProxyCredentials(allProxy || "NOT_SET"),
socksProxy: maskProxyCredentials(socksProxy || "NOT_SET"),
noProxy: noProxy || "NOT_SET", // NO_PROXY doesn't contain credentials
// Legacy variables for compatibility (credentials masked)
originalNodejsHttpProxy: maskProxyCredentials(process.env.nodejs_http_proxy || "NOT_SET"),
originalNodejsHttpsProxy: maskProxyCredentials(process.env.nodejs_https_proxy || "NOT_SET"),
// All potential proxy-related environment variables (credentials masked)
allProxyRelatedEnvVars: maskProxyEnvVars(Object.keys(process.env)
.filter((key) => key.toLowerCase().includes("proxy"))
.reduce((acc, key) => {
acc[key] = process.env[key] || "NOT_SET";
return acc;
}, {})),
message: "Enhanced proxy environment detection with SOCKS, authentication, and NO_PROXY support (credentials masked for security)",
});
// If no proxy configured, return standard fetch
if (!httpsProxy && !httpProxy && !allProxy && !socksProxy) {
logger.debug("[Proxy Fetch] No proxy environment variables found - using standard fetch");
return fetch;
}
logger.debug(`[Proxy Fetch] Configuring enhanced proxy with multiple protocol support`);
logger.debug(`[Proxy Fetch] HTTP_PROXY: ${maskProxyCredentials(httpProxy || "not set")}`);
logger.debug(`[Proxy Fetch] HTTPS_PROXY: ${maskProxyCredentials(httpsProxy || "not set")}`);
logger.debug(`[Proxy Fetch] ALL_PROXY: ${maskProxyCredentials(allProxy || "not set")}`);
logger.debug(`[Proxy Fetch] SOCKS_PROXY: ${maskProxyCredentials(socksProxy || "not set")}`);
logger.debug(`[Proxy Fetch] NO_PROXY: ${noProxy || "not set"}`);
// Return enhanced proxy-aware fetch function
return async (input, init) => {
const requestId = `req-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
// Determine target URL
const targetUrl = typeof input === "string"
? input
: input instanceof URL
? input.href
: input.url;
logger.debug(`[Proxy Fetch] 🚀 ENHANCED REQUEST START`, {
requestId,
targetUrl,
timestamp: new Date().toISOString(),
httpProxy: httpProxy || "NOT_SET",
httpsProxy: httpsProxy || "NOT_SET",
allProxy: allProxy || "NOT_SET",
socksProxy: socksProxy || "NOT_SET",
initHeaders: init?.headers || "NO_HEADERS",
initMethod: init?.method || "GET",
});
try {
// Enhanced proxy selection with NO_PROXY bypass and multiple protocols
const proxyUrl = selectProxyUrl(targetUrl);
if (proxyUrl) {
const url = new URL(targetUrl);
logger.debug(`[Proxy Fetch] 🔗 ENHANCED URL ANALYSIS`, {
requestId,
targetUrl,
urlHostname: url.hostname,
urlProtocol: url.protocol,
urlPort: url.port,
selectedProxyUrl: maskProxyCredentials(proxyUrl), // Hide credentials in logs
timestamp: new Date().toISOString(),
});
logger.debug(`[Proxy Fetch] 🎯 ENHANCED PROXY AGENT CREATION`, {
requestId,
proxyUrl: maskProxyCredentials(proxyUrl), // Hide credentials
targetHostname: url.hostname,
targetProtocol: url.protocol,
aboutToCreateProxyAgent: true,
timestamp: new Date().toISOString(),
});
// Create/reuse proxy agent (HTTP/HTTPS/SOCKS)
const agentCache = globalThis.__NL_PROXY_AGENT_CACHE__ ??
(globalThis.__NL_PROXY_AGENT_CACHE__ = new Map());
const dispatcher = agentCache.get(proxyUrl) || (await createProxyAgent(proxyUrl));
agentCache.set(proxyUrl, dispatcher);
logger.debug(`[Proxy Fetch] ✅ ENHANCED PROXY AGENT CREATED`, {
requestId,
hasDispatcher: !!dispatcher,
dispatcherType: typeof dispatcher,
dispatcherConstructor: dispatcher?.constructor?.name || "unknown",
timestamp: new Date().toISOString(),
});
// Handle Request objects by extracting URL and merging properties
let fetchInput;
let fetchInit = { ...init };
if (input instanceof Request) {
fetchInput = input.url;
fetchInit = {
method: input.method,
headers: input.headers,
body: input.body,
...init, // Allow init to override Request properties
};
}
else {
fetchInput = input;
}
// Use undici fetch with enhanced dispatcher (supports HTTP/HTTPS/SOCKS)
const undici = await import("undici");
const response = await undici.fetch(fetchInput, {
...fetchInit,
dispatcher: dispatcher,
});
logger.debug(`[Proxy Fetch] 🎉 ENHANCED PROXY SUCCESS`, {
requestId,
responseStatus: response?.status,
responseOk: response?.ok,
proxyUsed: true,
timestamp: new Date().toISOString(),
});
logger.debug(`[Proxy Fetch] ✅ Request proxied successfully via enhanced proxy`);
return response;
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.debug(`[Proxy Fetch] 💥 ENHANCED ERROR ANALYSIS`, {
requestId,
error: errorMessage,
errorType: error instanceof Error ? error.constructor.name : typeof error,
willFallback: true,
timestamp: new Date().toISOString(),
});
logger.warn(`[Proxy Fetch] Enhanced proxy failed (${errorMessage}), falling back to direct connection`);
}
// Fallback to standard fetch
logger.debug(`[Proxy Fetch] 🔄 ENHANCED FALLBACK TO STANDARD FETCH`, {
requestId,
fallbackReason: "No proxy configured or proxy failed",
timestamp: new Date().toISOString(),
});
return fetch(input, init);
};
}
/**
* Get enhanced proxy status information
*/
export function getProxyStatus() {
const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
const httpProxy = process.env.HTTP_PROXY || process.env.http_proxy;
const allProxy = process.env.ALL_PROXY || process.env.all_proxy;
const socksProxy = process.env.SOCKS_PROXY || process.env.socks_proxy;
const noProxy = process.env.NO_PROXY || process.env.no_proxy;
return {
enabled: !!(httpsProxy || httpProxy || allProxy || socksProxy),
httpProxy: httpProxy || null,
httpsProxy: httpsProxy || null,
allProxy: allProxy || null,
socksProxy: socksProxy || null,
noProxy: noProxy || null,
method: "enhanced-proxy-agent",
capabilities: [
"HTTP/HTTPS Proxy",
"SOCKS4/SOCKS5 Proxy",
"Proxy Authentication",
"NO_PROXY Bypass",
"CIDR Range Matching",
"Wildcard Domain Matching",
],
};
}