diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index 99d31f97..f8ebbf20 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -142,6 +142,7 @@ export async function execClaudeWithCLIProxy( port: cliproxyServerConfig.remote.port, protocol: cliproxyServerConfig.remote.protocol, auth_token: cliproxyServerConfig.remote.auth_token, + timeout: cliproxyServerConfig.remote.timeout, } : undefined, local: cliproxyServerConfig?.local @@ -188,7 +189,7 @@ export async function execClaudeWithCLIProxy( port: proxyConfig.port, protocol: proxyConfig.protocol, authToken: proxyConfig.authToken, - timeout: 2000, + timeout: proxyConfig.timeout ?? 2000, allowSelfSigned: proxyConfig.protocol === 'https', }); diff --git a/src/cliproxy/proxy-config-resolver.ts b/src/cliproxy/proxy-config-resolver.ts index aebeb948..4b9fb52a 100644 --- a/src/cliproxy/proxy-config-resolver.ts +++ b/src/cliproxy/proxy-config-resolver.ts @@ -16,6 +16,7 @@ export const PROXY_CLI_FLAGS = [ '--proxy-port', '--proxy-protocol', '--proxy-auth-token', + '--proxy-timeout', '--local-proxy', '--remote-only', ] as const; @@ -26,6 +27,7 @@ export const PROXY_ENV_VARS = { port: 'CCS_PROXY_PORT', protocol: 'CCS_PROXY_PROTOCOL', authToken: 'CCS_PROXY_AUTH_TOKEN', + timeout: 'CCS_PROXY_TIMEOUT', fallbackEnabled: 'CCS_PROXY_FALLBACK_ENABLED', } as const; @@ -35,6 +37,7 @@ interface ParsedProxyFlags { port?: number; protocol?: 'http' | 'https'; authToken?: string; + timeout?: number; localProxy: boolean; remoteOnly: boolean; } @@ -45,6 +48,7 @@ interface EnvProxyConfig { port?: number; protocol?: 'http' | 'https'; authToken?: string; + timeout?: number; fallbackEnabled?: boolean; } @@ -96,6 +100,15 @@ export function parseProxyFlags(args: string[]): { continue; } + if (arg === '--proxy-timeout' && args[i + 1] && !args[i + 1].startsWith('-')) { + const timeout = parseInt(args[i + 1], 10); + if (!isNaN(timeout) && timeout >= 100 && timeout <= 60000) { + flags.timeout = timeout; + } + i += 2; + continue; + } + if (arg === '--local-proxy') { flags.localProxy = true; i += 1; @@ -148,6 +161,14 @@ export function getProxyEnvVars(): EnvProxyConfig { config.authToken = authToken; } + const timeout = process.env[PROXY_ENV_VARS.timeout]; + if (timeout) { + const timeoutNum = parseInt(timeout, 10); + if (!isNaN(timeoutNum) && timeoutNum >= 100 && timeoutNum <= 60000) { + config.timeout = timeoutNum; + } + } + const fallback = process.env[PROXY_ENV_VARS.fallbackEnabled]; if (fallback !== undefined) { // Accept: '1', 'true', 'yes' as enabled; '0', 'false', 'no' as disabled @@ -192,6 +213,7 @@ export function resolveProxyConfig( port?: number; protocol?: 'http' | 'https'; auth_token?: string; + timeout?: number; fallback_enabled?: boolean; }; local?: { @@ -247,6 +269,9 @@ export function resolveProxyConfig( // Merge auth token: CLI > ENV > config.yaml resolved.authToken = cliFlags.authToken ?? envConfig.authToken ?? yamlConfig.remote?.auth_token; + // Merge timeout: CLI > ENV > config.yaml > default (2000ms in executor) + resolved.timeout = cliFlags.timeout ?? envConfig.timeout ?? yamlConfig.remote?.timeout; + // Merge fallback enabled: ENV > config.yaml > default resolved.fallbackEnabled = envConfig.fallbackEnabled ?? yamlConfig.remote?.fallback_enabled ?? true; @@ -276,6 +301,7 @@ export function hasProxyFlags(args: string[]): boolean { arg === '--proxy-port' || arg === '--proxy-protocol' || arg === '--proxy-auth-token' || + arg === '--proxy-timeout' || arg === '--local-proxy' || arg === '--remote-only' ); diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index b501fa71..c6daf020 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -213,4 +213,6 @@ export interface ResolvedProxyConfig { remoteOnly: boolean; /** --local-proxy flag: force local mode, ignore remote config */ forceLocal: boolean; + /** Remote proxy connection timeout in ms (default: 2000) */ + timeout?: number; } diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index abb4ee01..f3c8334b 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -256,6 +256,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); ['--proxy-port ', 'Proxy port (default: 8317)'], ['--proxy-protocol ', 'Protocol: http or https (default: http)'], ['--proxy-auth-token ', 'Auth token for remote proxy'], + ['--proxy-timeout ', 'Connection timeout in ms (default: 2000)'], ['--local-proxy', 'Force local mode, ignore remote config'], ['--remote-only', 'Fail if remote unreachable (no fallback)'], ]); @@ -266,6 +267,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim(); ['CCS_PROXY_PORT', 'Proxy port'], ['CCS_PROXY_PROTOCOL', 'Protocol (http/https)'], ['CCS_PROXY_AUTH_TOKEN', 'Auth token'], + ['CCS_PROXY_TIMEOUT', 'Connection timeout in ms'], ['CCS_PROXY_FALLBACK_ENABLED', 'Enable local fallback (1/0)'], ]); diff --git a/src/config/unified-config-types.ts b/src/config/unified-config-types.ts index b2d7fc6f..18d5e3d5 100644 --- a/src/config/unified-config-types.ts +++ b/src/config/unified-config-types.ts @@ -227,6 +227,8 @@ export interface ProxyRemoteConfig { protocol: 'http' | 'https'; /** Auth token for remote proxy (optional, sent as header) */ auth_token: string; + /** Connection timeout in milliseconds (default: 2000) */ + timeout?: number; } /**