From 4ee31006225ccec03ba6cf46a2257e122e1af79a Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sun, 21 Dec 2025 04:29:49 -0500 Subject: [PATCH] fix(cliproxy): respect user model settings in remote proxy mode getRemoteEnvVars was ignoring user settings and always loading models from base config. Now follows same priority as local mode: 1. Custom settings path (variant configs) 2. User settings file (~/.ccs/{provider}.settings.json) 3. Base config defaults This fixes the issue where selecting Opus 4.5 in UI resulted in Gemini 3 preview being loaded when using remote proxy mode. --- src/cliproxy/cliproxy-executor.ts | 16 ++++--- src/cliproxy/config-generator.ts | 79 +++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index 521b439a..e895d251 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -547,12 +547,16 @@ export async function execClaudeWithCLIProxy( : undefined; const envVars = useRemoteProxy - ? getRemoteEnvVars(provider, { - host: proxyConfig.host ?? 'localhost', - port: proxyConfig.port, - protocol: proxyConfig.protocol, - authToken: proxyConfig.authToken, - }) + ? getRemoteEnvVars( + provider, + { + host: proxyConfig.host ?? 'localhost', + port: proxyConfig.port, + protocol: proxyConfig.protocol, + authToken: proxyConfig.authToken, + }, + cfg.customSettingsPath + ) : getEffectiveEnvVars(provider, cfg.port, cfg.customSettingsPath, remoteRewriteConfig); const webSearchEnv = getWebSearchHookEnv(); const env = { diff --git a/src/cliproxy/config-generator.ts b/src/cliproxy/config-generator.ts index 5ce6ac86..57022dcd 100644 --- a/src/cliproxy/config-generator.ts +++ b/src/cliproxy/config-generator.ts @@ -540,48 +540,89 @@ export function ensureProviderSettings(provider: CLIProxyProvider): void { /** * Get environment variables for remote proxy mode. * Uses the remote proxy's provider endpoint as the base URL. + * Respects user model settings from custom settings path or provider settings file. * * @param provider CLIProxy provider (gemini, codex, agy, qwen, iflow) * @param remoteConfig Remote proxy connection details + * @param customSettingsPath Optional path to user's custom settings file * @returns Environment variables for Claude CLI */ export function getRemoteEnvVars( provider: CLIProxyProvider, - remoteConfig: { host: string; port?: number; protocol: 'http' | 'https'; authToken?: string } + remoteConfig: { host: string; port?: number; protocol: 'http' | 'https'; authToken?: string }, + customSettingsPath?: string ): Record { // Build URL with smart port handling - omit if using protocol default const defaultPort = remoteConfig.protocol === 'https' ? 443 : 80; const effectivePort = remoteConfig.port ?? defaultPort; const portSuffix = effectivePort === defaultPort ? '' : `:${effectivePort}`; const baseUrl = `${remoteConfig.protocol}://${remoteConfig.host}${portSuffix}/api/provider/${provider}`; - const models = getModelMapping(provider); // Get global env vars (DISABLE_TELEMETRY, etc.) const globalEnv = getGlobalEnvVars(); - // Get additional env vars from base config (ANTHROPIC_MAX_TOKENS, etc.) - const baseEnvVars = getEnvVarsFromConfig(provider); + // Load user settings with priority: custom path > user settings file > base config + let userEnvVars: Record = {}; - // Filter out core env vars from base config to avoid conflicts - const { - ANTHROPIC_BASE_URL: _baseUrl, - ANTHROPIC_AUTH_TOKEN: _authToken, - ANTHROPIC_MODEL: _model, - ANTHROPIC_DEFAULT_OPUS_MODEL: _opusModel, - ANTHROPIC_DEFAULT_SONNET_MODEL: _sonnetModel, - ANTHROPIC_DEFAULT_HAIKU_MODEL: _haikuModel, - ...additionalEnvVars - } = baseEnvVars; + // Priority 1: Custom settings path (for user-defined variants) + if (customSettingsPath) { + const expandedPath = customSettingsPath.replace(/^~/, require('os').homedir()); + if (fs.existsSync(expandedPath)) { + try { + const content = fs.readFileSync(expandedPath, 'utf-8'); + const settings: ProviderSettings = JSON.parse(content); + if (settings.env && typeof settings.env === 'object') { + userEnvVars = settings.env as Record; + } + } catch { + // Invalid JSON - fall through to provider defaults + console.warn(warn(`Invalid settings file: ${customSettingsPath}`)); + } + } + } + // Priority 2: Default provider settings file (~/.ccs/{provider}.settings.json) + if (Object.keys(userEnvVars).length === 0) { + const settingsPath = getProviderSettingsPath(provider); + if (fs.existsSync(settingsPath)) { + try { + const content = fs.readFileSync(settingsPath, 'utf-8'); + const settings: ProviderSettings = JSON.parse(content); + if (settings.env && typeof settings.env === 'object') { + userEnvVars = settings.env as Record; + } + } catch { + // Invalid JSON - fall through to base config + } + } + } + + // Priority 3: Base config defaults + if (Object.keys(userEnvVars).length === 0) { + const models = getModelMapping(provider); + const baseEnvVars = getEnvVarsFromConfig(provider); + // Filter out URL/auth from base config (we'll set those from remote config) + const { + ANTHROPIC_BASE_URL: _baseUrl, + ANTHROPIC_AUTH_TOKEN: _authToken, + ...additionalEnvVars + } = baseEnvVars; + userEnvVars = { + ...additionalEnvVars, + ANTHROPIC_MODEL: models.claudeModel, + ANTHROPIC_DEFAULT_OPUS_MODEL: models.opusModel || models.claudeModel, + ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnetModel || models.claudeModel, + ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haikuModel || models.claudeModel, + }; + } + + // Build final env: global + user settings + remote URL/auth override const env: Record = { ...globalEnv, - ...additionalEnvVars, + ...userEnvVars, + // Always override URL and auth token with remote config ANTHROPIC_BASE_URL: baseUrl, ANTHROPIC_AUTH_TOKEN: remoteConfig.authToken || CCS_INTERNAL_API_KEY, - ANTHROPIC_MODEL: models.claudeModel, - ANTHROPIC_DEFAULT_OPUS_MODEL: models.opusModel || models.claudeModel, - ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnetModel || models.claudeModel, - ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haikuModel || models.claudeModel, }; return env;