From 13d9cae81f443058cb59bc027dcc62441f19c91a Mon Sep 17 00:00:00 2001 From: neighbads Date: Fri, 16 Jan 2026 00:49:51 +0000 Subject: [PATCH 1/2] feat: add HTTP proxy support via environment variables Support http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY environment variables for all network requests (axios HTTP and socket.io WebSocket). Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- package.json | 1 + src/api/api.ts | 17 +++++++++++++---- src/api/apiMachine.ts | 4 +++- src/api/apiSession.ts | 4 +++- src/api/auth.ts | 6 +++++- src/api/pushNotifications.ts | 5 ++++- src/utils/proxy.ts | 24 ++++++++++++++++++++++++ 7 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 src/utils/proxy.ts diff --git a/package.json b/package.json index fca8547f..887b58c5 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "fastify-type-provider-zod": "4.0.2", "http-proxy": "^1.18.1", "http-proxy-middleware": "^3.0.5", + "https-proxy-agent": "^7.0.6", "ink": "^6.5.1", "open": "^10.2.0", "ps-list": "^8.1.1", diff --git a/src/api/api.ts b/src/api/api.ts index fc381180..ae37296a 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -9,6 +9,7 @@ import { configuration } from '@/configuration'; import chalk from 'chalk'; import { Credentials } from '@/persistence'; import { connectionState, isNetworkError } from '@/utils/serverConnectionErrors'; +import { createProxyAgent } from '@/utils/proxy'; export class ApiClient { @@ -70,7 +71,9 @@ export class ApiClient { 'Authorization': `Bearer ${this.credential.token}`, 'Content-Type': 'application/json' }, - timeout: 60000 // 1 minute timeout for very bad network connections + timeout: 60000, // 1 minute timeout for very bad network connections + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() } ) @@ -190,7 +193,9 @@ export class ApiClient { 'Authorization': `Bearer ${this.credential.token}`, 'Content-Type': 'application/json' }, - timeout: 60000 // 1 minute timeout for very bad network connections + timeout: 60000, // 1 minute timeout for very bad network connections + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() } ); @@ -301,7 +306,9 @@ export class ApiClient { 'Authorization': `Bearer ${this.credential.token}`, 'Content-Type': 'application/json' }, - timeout: 5000 + timeout: 5000, + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() } ); @@ -329,7 +336,9 @@ export class ApiClient { 'Authorization': `Bearer ${this.credential.token}`, 'Content-Type': 'application/json' }, - timeout: 5000 + timeout: 5000, + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() } ); diff --git a/src/api/apiMachine.ts b/src/api/apiMachine.ts index b8e2b570..235953d7 100644 --- a/src/api/apiMachine.ts +++ b/src/api/apiMachine.ts @@ -11,6 +11,7 @@ import { registerCommonHandlers, SpawnSessionOptions, SpawnSessionResult } from import { encodeBase64, decodeBase64, encrypt, decrypt } from './encryption'; import { backoff } from '@/utils/time'; import { RpcHandlerManager } from './rpc/RpcHandlerManager'; +import { createProxyAgent } from '@/utils/proxy'; interface ServerToDaemonEvents { update: (data: Update) => void; @@ -227,7 +228,8 @@ export class ApiMachineClient { path: '/v1/updates', reconnection: true, reconnectionDelay: 1000, - reconnectionDelayMax: 5000 + reconnectionDelayMax: 5000, + agent: createProxyAgent() as any }); this.socket.on('connect', () => { diff --git a/src/api/apiSession.ts b/src/api/apiSession.ts index 187ce5b8..95d94c41 100644 --- a/src/api/apiSession.ts +++ b/src/api/apiSession.ts @@ -10,6 +10,7 @@ import { randomUUID } from 'node:crypto'; import { AsyncLock } from '@/utils/lock'; import { RpcHandlerManager } from './rpc/RpcHandlerManager'; import { registerCommonHandlers } from '../modules/common/registerCommonHandlers'; +import { createProxyAgent } from '@/utils/proxy'; /** * ACP (Agent Communication Protocol) message data types. @@ -91,7 +92,8 @@ export class ApiSessionClient extends EventEmitter { reconnectionDelayMax: 5000, transports: ['websocket'], withCredentials: true, - autoConnect: false + autoConnect: false, + agent: createProxyAgent() as any }); // diff --git a/src/api/auth.ts b/src/api/auth.ts index 642cb8a2..5f54c8d7 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,6 +1,7 @@ import axios from 'axios'; import { encodeBase64, encodeBase64Url, authChallenge } from './encryption'; import { configuration } from '@/configuration'; +import { createProxyAgent } from '@/utils/proxy'; /** * Note: This function is deprecated. Use readPrivateKey/writePrivateKey from persistence module instead. @@ -18,11 +19,14 @@ export async function getOrCreateSecretKey(): Promise { */ export async function authGetToken(secret: Uint8Array): Promise { const { challenge, publicKey, signature } = authChallenge(secret); - + const response = await axios.post(`${configuration.serverUrl}/v1/auth`, { challenge: encodeBase64(challenge), publicKey: encodeBase64(publicKey), signature: encodeBase64(signature) + }, { + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() }); if (!response.data.success || !response.data.token) { diff --git a/src/api/pushNotifications.ts b/src/api/pushNotifications.ts index a194a861..c3464745 100644 --- a/src/api/pushNotifications.ts +++ b/src/api/pushNotifications.ts @@ -1,6 +1,7 @@ import axios from 'axios' import { logger } from '@/ui/logger' import { Expo, ExpoPushMessage } from 'expo-server-sdk' +import { createProxyAgent } from '@/utils/proxy' export interface PushToken { id: string @@ -32,7 +33,9 @@ export class PushNotificationClient { headers: { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json' - } + }, + httpAgent: createProxyAgent(), + httpsAgent: createProxyAgent() } ) diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts new file mode 100644 index 00000000..9c4efa5c --- /dev/null +++ b/src/utils/proxy.ts @@ -0,0 +1,24 @@ +/** + * HTTP proxy configuration utilities + * Reads proxy settings from environment variables (http_proxy, https_proxy, HTTP_PROXY, HTTPS_PROXY) + */ + +import { HttpsProxyAgent } from 'https-proxy-agent'; + +/** + * Get proxy URL from environment variables + * Checks: https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY (in order) + */ +export function getProxyUrl(): string | undefined { + return process.env.https_proxy || process.env.HTTPS_PROXY || + process.env.http_proxy || process.env.HTTP_PROXY; +} + +/** + * Create an HttpsProxyAgent if proxy is configured + */ +export function createProxyAgent(): HttpsProxyAgent | undefined { + const proxyUrl = getProxyUrl(); + if (!proxyUrl) return undefined; + return new HttpsProxyAgent(proxyUrl); +} From e7ad9c77bc863fd3767a2375731d19be0938f2ac Mon Sep 17 00:00:00 2001 From: neighbads Date: Fri, 16 Jan 2026 03:50:56 +0000 Subject: [PATCH 2/2] feat: auto-read proxy config from Claude Code settings Read HTTP_PROXY/HTTPS_PROXY from ~/.claude/settings.json env field when environment variables are not set. Only applies when running Claude agent (not Gemini/Codex). Reuses readClaudeSettings utility. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- src/claude/runClaude.ts | 4 ++++ src/utils/proxy.ts | 47 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/claude/runClaude.ts b/src/claude/runClaude.ts index bcdd74fd..0f44e22d 100644 --- a/src/claude/runClaude.ts +++ b/src/claude/runClaude.ts @@ -27,6 +27,7 @@ import { startOfflineReconnection, connectionState } from '@/utils/serverConnect import { claudeLocal } from '@/claude/claudeLocal'; import { createSessionScanner } from '@/claude/utils/sessionScanner'; import { Session } from './session'; +import { setAgentType } from '@/utils/proxy'; /** JavaScript runtime to use for spawning Claude Code */ export type JsRuntime = 'node' | 'bun' @@ -44,6 +45,9 @@ export interface StartOptions { } export async function runClaude(credentials: Credentials, options: StartOptions = {}): Promise { + // Set agent type for proxy configuration + setAgentType('claude'); + logger.debug(`[CLAUDE] ===== CLAUDE MODE STARTING =====`); logger.debug(`[CLAUDE] This is the Claude agent, NOT Gemini`); diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts index 9c4efa5c..2a8ab028 100644 --- a/src/utils/proxy.ts +++ b/src/utils/proxy.ts @@ -1,17 +1,56 @@ /** * HTTP proxy configuration utilities - * Reads proxy settings from environment variables (http_proxy, https_proxy, HTTP_PROXY, HTTPS_PROXY) + * + * Reads proxy settings from: + * 1. Environment variables (http_proxy, https_proxy, HTTP_PROXY, HTTPS_PROXY) + * 2. Agent-specific config files: + * - Claude: ~/.claude/settings.json (env field) + * - Gemini: TODO - add support + * - Codex: TODO - add support */ import { HttpsProxyAgent } from 'https-proxy-agent'; +type AgentType = 'claude' | 'gemini' | 'codex' | null; +let currentAgentType: AgentType = null; +let claudeProxyCache: { url: string | undefined; loaded: boolean } = { url: undefined, loaded: false }; + +/** + * Set the current agent type (call this at startup) + */ +export function setAgentType(type: AgentType): void { + currentAgentType = type; +} + +/** + * Read proxy URL from Claude Code settings + * Only used when agent type is 'claude' + */ +function getClaudeProxyUrl(): string | undefined { + if (currentAgentType !== 'claude') return undefined; + if (claudeProxyCache.loaded) return claudeProxyCache.url; + claudeProxyCache.loaded = true; + + try { + // Lazy import to avoid circular dependency + const { readClaudeSettings } = require('@/claude/utils/claudeSettings'); + const settings = readClaudeSettings(); + claudeProxyCache.url = settings?.env?.HTTPS_PROXY || settings?.env?.HTTP_PROXY || + settings?.env?.https_proxy || settings?.env?.http_proxy; + return claudeProxyCache.url; + } catch { + return undefined; + } +} + /** - * Get proxy URL from environment variables - * Checks: https_proxy, HTTPS_PROXY, http_proxy, HTTP_PROXY (in order) + * Get proxy URL from environment variables or Claude Code settings + * Priority: env vars > Claude Code settings (only for claude agent) */ export function getProxyUrl(): string | undefined { return process.env.https_proxy || process.env.HTTPS_PROXY || - process.env.http_proxy || process.env.HTTP_PROXY; + process.env.http_proxy || process.env.HTTP_PROXY || + getClaudeProxyUrl(); } /**