From e58afd77905d40ce4526a85e4f59cea4fc33ff50 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Tue, 30 Dec 2025 12:42:54 -0500 Subject: [PATCH 1/2] fix(cliproxy): pass variant port to executor for isolation Variants configured with dedicated ports (8318-8417) were not using their assigned port. The executor always defaulted to 8317. Changes: - Add port field to ProfileDetectionResult interface - Pass variant.port from profile-detector to ccs.ts - Forward port to execClaudeWithCLIProxy options - Update executor priority: CLI flags > variant port > config.yaml > default Closes #228 --- src/auth/profile-detector.ts | 3 +++ src/ccs.ts | 6 +++++- src/cliproxy/cliproxy-executor.ts | 12 +++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index 16accd5f..ad6d4c3f 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -39,6 +39,8 @@ export interface ProfileDetectionResult { message?: string; /** For cliproxy variants: the underlying provider (gemini, codex, agy, qwen) */ provider?: CLIProxyProfileName; + /** For cliproxy variants: dedicated port for isolation (8318-8417) */ + port?: number; /** For unified config profiles: merged env vars (config + secrets) */ env?: Record; /** For copilot profile: the copilot config */ @@ -250,6 +252,7 @@ class ProfileDetector { name: profileName, provider: variant.provider as CLIProxyProfileName, settingsPath: variant.settings, + port: variant.port, }; } diff --git a/src/ccs.ts b/src/ccs.ts index dc2df697..fc1d1d83 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -511,7 +511,11 @@ async function main(): Promise { // CLIPROXY FLOW: OAuth-based profiles (gemini, codex, agy, qwen) or user-defined variants const provider = profileInfo.provider || (profileInfo.name as CLIProxyProvider); const customSettingsPath = profileInfo.settingsPath; // undefined for hardcoded profiles - await execClaudeWithCLIProxy(claudeCli, provider, remainingArgs, { customSettingsPath }); + const variantPort = profileInfo.port; // variant-specific port for isolation + await execClaudeWithCLIProxy(claudeCli, provider, remainingArgs, { + customSettingsPath, + port: variantPort, + }); } else if (profileInfo.type === 'copilot') { // COPILOT FLOW: GitHub Copilot subscription via copilot-api proxy const { executeCopilotProfile } = await import('./copilot'); diff --git a/src/cliproxy/cliproxy-executor.ts b/src/cliproxy/cliproxy-executor.ts index f8ebbf20..d23e3506 100644 --- a/src/cliproxy/cliproxy-executor.ts +++ b/src/cliproxy/cliproxy-executor.ts @@ -154,7 +154,17 @@ export async function execClaudeWithCLIProxy( }); // Use resolved port from proxy config (overrides ExecutorConfig) - if (proxyConfig.port !== CLIPROXY_DEFAULT_PORT) { + // Priority: CLI flags > Variant port (cfg.port) > config.yaml > default + // cfg.port is set if variant has dedicated port; proxyConfig.port comes from CLI/ENV/config.yaml + if (cfg.port && cfg.port !== CLIPROXY_DEFAULT_PORT) { + // Variant port already set via config param - keep it (highest priority after CLI flags) + // CLI flags would have been parsed in resolveProxyConfig and set proxyConfig.port + // So if proxyConfig.port differs from default AND cfg.port differs, CLI wins + if (proxyConfig.port !== CLIPROXY_DEFAULT_PORT) { + cfg.port = proxyConfig.port; // CLI/ENV override variant port + } + // else: keep cfg.port (variant port) + } else if (proxyConfig.port !== CLIPROXY_DEFAULT_PORT) { cfg.port = proxyConfig.port; } From 26253891207d06c52530605f1f2246f366e70f7b Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Tue, 30 Dec 2025 13:12:54 -0500 Subject: [PATCH 2/2] fix(cliproxy): propagate port in unified config and UI preset handlers Edge case fixes identified in codebase review: - Unified config variant detection: add settingsPath and port fields - Provider editor: use variant port in handleApplyPreset/handleCustomPresetApply - preset-utils: add optional port parameter to applyDefaultPreset() --- src/auth/profile-detector.ts | 2 ++ ui/src/components/cliproxy/provider-editor/index.tsx | 6 ++++-- ui/src/lib/preset-utils.ts | 7 +++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/auth/profile-detector.ts b/src/auth/profile-detector.ts index ad6d4c3f..a3fad69b 100644 --- a/src/auth/profile-detector.ts +++ b/src/auth/profile-detector.ts @@ -112,6 +112,8 @@ class ProfileDetector { type: 'cliproxy', name: profileName, provider: variant.provider as CLIProxyProfileName, + settingsPath: variant.settings, + port: variant.port, }; } diff --git a/ui/src/components/cliproxy/provider-editor/index.tsx b/ui/src/components/cliproxy/provider-editor/index.tsx index 17aa3e7d..7e79ffaf 100644 --- a/ui/src/components/cliproxy/provider-editor/index.tsx +++ b/ui/src/components/cliproxy/provider-editor/index.tsx @@ -94,8 +94,9 @@ export function ProviderEditor({ const accounts = authStatus.accounts || []; const handleApplyPreset = (updates: Record) => { + const effectivePort = port ?? CLIPROXY_PORT; updateEnvValues({ - ANTHROPIC_BASE_URL: `http://127.0.0.1:${CLIPROXY_PORT}/api/provider/${provider}`, + ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`, ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ...updates, }); @@ -103,8 +104,9 @@ export function ProviderEditor({ }; const handleCustomPresetApply = (values: ModelMappingValues, presetName?: string) => { + const effectivePort = port ?? CLIPROXY_PORT; updateEnvValues({ - ANTHROPIC_BASE_URL: `http://127.0.0.1:${CLIPROXY_PORT}/api/provider/${provider}`, + ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`, ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ANTHROPIC_MODEL: values.default, ANTHROPIC_DEFAULT_OPUS_MODEL: values.opus, diff --git a/ui/src/lib/preset-utils.ts b/ui/src/lib/preset-utils.ts index 7b74ed6b..80ce9673 100644 --- a/ui/src/lib/preset-utils.ts +++ b/ui/src/lib/preset-utils.ts @@ -13,10 +13,12 @@ export const CLIPROXY_PORT = 8317; * Uses the first model's presetMapping or falls back to using defaultModel for all tiers * * @param provider - The provider ID (e.g., 'gemini', 'codex', 'agy') + * @param port - Optional custom port (defaults to CLIPROXY_PORT) * @returns Object with success status and applied preset name */ export async function applyDefaultPreset( - provider: string + provider: string, + port?: number ): Promise<{ success: boolean; presetName?: string }> { const catalog = MODEL_CATALOGS[provider]; if (!catalog) return { success: false }; @@ -30,9 +32,10 @@ export async function applyDefaultPreset( haiku: catalog.defaultModel, }; + const effectivePort = port ?? CLIPROXY_PORT; const settings = { env: { - ANTHROPIC_BASE_URL: `http://127.0.0.1:${CLIPROXY_PORT}/api/provider/${provider}`, + ANTHROPIC_BASE_URL: `http://127.0.0.1:${effectivePort}/api/provider/${provider}`, ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed', ANTHROPIC_MODEL: mapping.default, ANTHROPIC_DEFAULT_OPUS_MODEL: mapping.opus,