feat(cliproxy): add --proxy-timeout CLI option (#220)

Add configurable timeout for remote proxy connection checks.

- Add --proxy-timeout <ms> CLI flag (100-60000ms range)
- Add CCS_PROXY_TIMEOUT environment variable support
- Update help documentation for new option
- Default remains 2000ms for backward compatibility
This commit is contained in:
Shun Kakinoki
2025-12-29 12:58:19 -08:00
committed by GitHub
parent 14df54b60b
commit 4cd9bec9e1
5 changed files with 34 additions and 1 deletions
+2 -1
View File
@@ -142,6 +142,7 @@ export async function execClaudeWithCLIProxy(
port: cliproxyServerConfig.remote.port, port: cliproxyServerConfig.remote.port,
protocol: cliproxyServerConfig.remote.protocol, protocol: cliproxyServerConfig.remote.protocol,
auth_token: cliproxyServerConfig.remote.auth_token, auth_token: cliproxyServerConfig.remote.auth_token,
timeout: cliproxyServerConfig.remote.timeout,
} }
: undefined, : undefined,
local: cliproxyServerConfig?.local local: cliproxyServerConfig?.local
@@ -188,7 +189,7 @@ export async function execClaudeWithCLIProxy(
port: proxyConfig.port, port: proxyConfig.port,
protocol: proxyConfig.protocol, protocol: proxyConfig.protocol,
authToken: proxyConfig.authToken, authToken: proxyConfig.authToken,
timeout: 2000, timeout: proxyConfig.timeout ?? 2000,
allowSelfSigned: proxyConfig.protocol === 'https', allowSelfSigned: proxyConfig.protocol === 'https',
}); });
+26
View File
@@ -16,6 +16,7 @@ export const PROXY_CLI_FLAGS = [
'--proxy-port', '--proxy-port',
'--proxy-protocol', '--proxy-protocol',
'--proxy-auth-token', '--proxy-auth-token',
'--proxy-timeout',
'--local-proxy', '--local-proxy',
'--remote-only', '--remote-only',
] as const; ] as const;
@@ -26,6 +27,7 @@ export const PROXY_ENV_VARS = {
port: 'CCS_PROXY_PORT', port: 'CCS_PROXY_PORT',
protocol: 'CCS_PROXY_PROTOCOL', protocol: 'CCS_PROXY_PROTOCOL',
authToken: 'CCS_PROXY_AUTH_TOKEN', authToken: 'CCS_PROXY_AUTH_TOKEN',
timeout: 'CCS_PROXY_TIMEOUT',
fallbackEnabled: 'CCS_PROXY_FALLBACK_ENABLED', fallbackEnabled: 'CCS_PROXY_FALLBACK_ENABLED',
} as const; } as const;
@@ -35,6 +37,7 @@ interface ParsedProxyFlags {
port?: number; port?: number;
protocol?: 'http' | 'https'; protocol?: 'http' | 'https';
authToken?: string; authToken?: string;
timeout?: number;
localProxy: boolean; localProxy: boolean;
remoteOnly: boolean; remoteOnly: boolean;
} }
@@ -45,6 +48,7 @@ interface EnvProxyConfig {
port?: number; port?: number;
protocol?: 'http' | 'https'; protocol?: 'http' | 'https';
authToken?: string; authToken?: string;
timeout?: number;
fallbackEnabled?: boolean; fallbackEnabled?: boolean;
} }
@@ -96,6 +100,15 @@ export function parseProxyFlags(args: string[]): {
continue; 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') { if (arg === '--local-proxy') {
flags.localProxy = true; flags.localProxy = true;
i += 1; i += 1;
@@ -148,6 +161,14 @@ export function getProxyEnvVars(): EnvProxyConfig {
config.authToken = authToken; 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]; const fallback = process.env[PROXY_ENV_VARS.fallbackEnabled];
if (fallback !== undefined) { if (fallback !== undefined) {
// Accept: '1', 'true', 'yes' as enabled; '0', 'false', 'no' as disabled // Accept: '1', 'true', 'yes' as enabled; '0', 'false', 'no' as disabled
@@ -192,6 +213,7 @@ export function resolveProxyConfig(
port?: number; port?: number;
protocol?: 'http' | 'https'; protocol?: 'http' | 'https';
auth_token?: string; auth_token?: string;
timeout?: number;
fallback_enabled?: boolean; fallback_enabled?: boolean;
}; };
local?: { local?: {
@@ -247,6 +269,9 @@ export function resolveProxyConfig(
// Merge auth token: CLI > ENV > config.yaml // Merge auth token: CLI > ENV > config.yaml
resolved.authToken = cliFlags.authToken ?? envConfig.authToken ?? yamlConfig.remote?.auth_token; 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 // Merge fallback enabled: ENV > config.yaml > default
resolved.fallbackEnabled = resolved.fallbackEnabled =
envConfig.fallbackEnabled ?? yamlConfig.remote?.fallback_enabled ?? true; envConfig.fallbackEnabled ?? yamlConfig.remote?.fallback_enabled ?? true;
@@ -276,6 +301,7 @@ export function hasProxyFlags(args: string[]): boolean {
arg === '--proxy-port' || arg === '--proxy-port' ||
arg === '--proxy-protocol' || arg === '--proxy-protocol' ||
arg === '--proxy-auth-token' || arg === '--proxy-auth-token' ||
arg === '--proxy-timeout' ||
arg === '--local-proxy' || arg === '--local-proxy' ||
arg === '--remote-only' arg === '--remote-only'
); );
+2
View File
@@ -213,4 +213,6 @@ export interface ResolvedProxyConfig {
remoteOnly: boolean; remoteOnly: boolean;
/** --local-proxy flag: force local mode, ignore remote config */ /** --local-proxy flag: force local mode, ignore remote config */
forceLocal: boolean; forceLocal: boolean;
/** Remote proxy connection timeout in ms (default: 2000) */
timeout?: number;
} }
+2
View File
@@ -256,6 +256,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
['--proxy-port <port>', 'Proxy port (default: 8317)'], ['--proxy-port <port>', 'Proxy port (default: 8317)'],
['--proxy-protocol <proto>', 'Protocol: http or https (default: http)'], ['--proxy-protocol <proto>', 'Protocol: http or https (default: http)'],
['--proxy-auth-token <token>', 'Auth token for remote proxy'], ['--proxy-auth-token <token>', 'Auth token for remote proxy'],
['--proxy-timeout <ms>', 'Connection timeout in ms (default: 2000)'],
['--local-proxy', 'Force local mode, ignore remote config'], ['--local-proxy', 'Force local mode, ignore remote config'],
['--remote-only', 'Fail if remote unreachable (no fallback)'], ['--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_PORT', 'Proxy port'],
['CCS_PROXY_PROTOCOL', 'Protocol (http/https)'], ['CCS_PROXY_PROTOCOL', 'Protocol (http/https)'],
['CCS_PROXY_AUTH_TOKEN', 'Auth token'], ['CCS_PROXY_AUTH_TOKEN', 'Auth token'],
['CCS_PROXY_TIMEOUT', 'Connection timeout in ms'],
['CCS_PROXY_FALLBACK_ENABLED', 'Enable local fallback (1/0)'], ['CCS_PROXY_FALLBACK_ENABLED', 'Enable local fallback (1/0)'],
]); ]);
+2
View File
@@ -227,6 +227,8 @@ export interface ProxyRemoteConfig {
protocol: 'http' | 'https'; protocol: 'http' | 'https';
/** Auth token for remote proxy (optional, sent as header) */ /** Auth token for remote proxy (optional, sent as header) */
auth_token: string; auth_token: string;
/** Connection timeout in milliseconds (default: 2000) */
timeout?: number;
} }
/** /**