From e0df6aa90095c8817a06bd679cf2186891af4273 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 28 Nov 2025 20:53:20 -0500 Subject: [PATCH] fix(cliproxy): ensure provider-specific OAuth token detection Fixes critical auth bug where checking any JSON file in the auth directory would incorrectly mark providers as authenticated. Now validates that token files match provider-specific prefixes (gemini-, codex-, antigravity-) to prevent OAuth skipping across providers. Updates help command examples to use placeholder syntax and updates CHANGELOG to reflect Qwen -> Antigravity provider name change. --- CHANGELOG.md | 8 +++---- src/cliproxy/auth-handler.ts | 46 +++++++++++++++++++++++++++++------- src/commands/help-command.ts | 8 +++---- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74665029..51b45d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/) - **CLIProxy OAuth Profiles**: Three new zero-config profiles powered by CLIProxyAPI - `ccs gemini` - Google Gemini via OAuth (zero config) - `ccs codex` - OpenAI Codex via OAuth (zero config) - - `ccs qwen` - Alibaba Qwen via OAuth (zero config) + - `ccs agy` - Antigravity (AGY) via OAuth (zero config) - **Download-on-Demand Binary**: CLIProxyAPI binary (~15MB) downloads automatically on first use - Supports 6 platforms: darwin/linux/windows × amd64/arm64 @@ -25,7 +25,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/) - **CLIProxy Diagnostics** in `ccs doctor`: - Binary installation status + version - Config file validation - - OAuth status per provider (gemini/codex/qwen) + - OAuth status per provider (gemini/codex/agy) - Port 8317 availability check - **Enhanced Error Messages** (`src/utils/error-manager.ts`): @@ -44,7 +44,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/) ### Changed - **Profile Detection**: New priority order - 1. CLIProxy profiles (gemini, codex, qwen) + 1. CLIProxy profiles (gemini, codex, agy) 2. Settings-based profiles (glm, glmt, kimi) 3. Account-based profiles (work, personal) 4. Default Claude CLI @@ -57,7 +57,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/) - **Model Mappings**: - Gemini: gemini-2.0-flash (opus: thinking-exp, haiku: flash-lite) - Codex: gpt-4o (opus: o1, haiku: gpt-4o-mini) - - Qwen: qwen-max (sonnet: qwen-plus, haiku: qwen-turbo) + - Antigravity: agy (sonnet: agy-pro, haiku: agy-turbo) - **Storage**: - Binary: `~/.ccs/bin/cliproxyapi` - Tokens: `~/.ccs/cliproxy-auth//` diff --git a/src/cliproxy/auth-handler.ts b/src/cliproxy/auth-handler.ts index 9a13a18f..e753ebb0 100644 --- a/src/cliproxy/auth-handler.ts +++ b/src/cliproxy/auth-handler.ts @@ -120,10 +120,21 @@ export function getProviderTokenDir(provider: CLIProxyProvider): string { return getProviderAuthDir(provider); } +/** + * Provider-specific auth file prefixes + * CLIProxyAPI names auth files with provider prefix (e.g., "antigravity-user@email.json") + */ +const PROVIDER_AUTH_PREFIXES: Record = { + gemini: ['gemini-', 'google-'], + codex: ['codex-', 'openai-'], + agy: ['antigravity-', 'agy-'], +}; + /** * Check if provider has valid authentication * CLIProxyAPI stores OAuth tokens as JSON files in the auth directory. - * We check for any .json files that could contain tokens. + * Files are named with provider prefix (e.g., "antigravity-user@email.json"). + * We MUST check for provider-specific files, not just any .json file. */ export function isAuthenticated(provider: CLIProxyProvider): boolean { const tokenDir = getProviderTokenDir(provider); @@ -132,13 +143,22 @@ export function isAuthenticated(provider: CLIProxyProvider): boolean { return false; } - // Check for any token files created by CLIProxyAPI + // Get valid prefixes for this provider + const validPrefixes = PROVIDER_AUTH_PREFIXES[provider] || []; + + // Check for provider-specific token files try { const files = fs.readdirSync(tokenDir); - const tokenFiles = files.filter( - (f) => f.endsWith('.json') || f.endsWith('.token') || f === 'credentials' - ); - return tokenFiles.length > 0; + const providerTokenFiles = files.filter((f) => { + // Must be a token-like file + if (!f.endsWith('.json') && !f.endsWith('.token') && f !== 'credentials') { + return false; + } + // Must have provider-specific prefix + const lowerFile = f.toLowerCase(); + return validPrefixes.some((prefix) => lowerFile.startsWith(prefix)); + }); + return providerTokenFiles.length > 0; } catch { return false; } @@ -152,11 +172,19 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus { let tokenFiles: string[] = []; let lastAuth: Date | undefined; + // Get valid prefixes for this provider + const validPrefixes = PROVIDER_AUTH_PREFIXES[provider] || []; + if (fs.existsSync(tokenDir)) { const files = fs.readdirSync(tokenDir); - tokenFiles = files.filter( - (f) => f.endsWith('.json') || f.endsWith('.token') || f === 'credentials' - ); + // Only include provider-specific files + tokenFiles = files.filter((f) => { + if (!f.endsWith('.json') && !f.endsWith('.token') && f !== 'credentials') { + return false; + } + const lowerFile = f.toLowerCase(); + return validPrefixes.some((prefix) => lowerFile.startsWith(prefix)); + }); // Get most recent modification time for (const file of tokenFiles) { diff --git a/src/commands/help-command.ts b/src/commands/help-command.ts index ec6aa9f5..3cecbd09 100644 --- a/src/commands/help-command.ts +++ b/src/commands/help-command.ts @@ -55,10 +55,10 @@ export function handleHelpCommand(): void { ` ${colored('ccs agy', 'yellow')} Antigravity (gemini-3-pro-preview)` ); console.log(''); - console.log(` ${colored('ccs gemini --auth', 'yellow')} Authenticate only`); - console.log(` ${colored('ccs gemini --logout', 'yellow')} Clear authentication`); - console.log(` ${colored('ccs gemini --headless', 'yellow')} Headless auth (for SSH)`); - console.log(` ${colored('ccs gemini "explain code"', 'yellow')} Use with prompt`); + console.log(` ${colored('ccs --auth', 'yellow')} Authenticate only`); + console.log(` ${colored('ccs --logout', 'yellow')} Clear authentication`); + console.log(` ${colored('ccs --headless', 'yellow')} Headless auth (for SSH)`); + console.log(` ${colored('ccs codex "explain code"', 'yellow')} Use with prompt`); console.log(''); // ═══════════════════════════════════════════════════════════════════════════