From 64a8e86db4be7dd96d19654e1e91827ae62e0f7e Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Thu, 4 Dec 2025 15:51:36 -0500 Subject: [PATCH 1/7] fix(ci): add path filtering to deploy-ccs-worker pull request trigger --- .github/workflows/deploy-ccs-worker.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-ccs-worker.yml b/.github/workflows/deploy-ccs-worker.yml index 6391e91f..d08900c4 100644 --- a/.github/workflows/deploy-ccs-worker.yml +++ b/.github/workflows/deploy-ccs-worker.yml @@ -8,9 +8,13 @@ on: - 'scripts/wrangler.toml' - 'installers/**' - # Run on all PRs to satisfy branch protection (no path filter) + # Only run on PRs that touch installer-related files pull_request: branches: [main] + paths: + - 'scripts/worker.js' + - 'scripts/wrangler.toml' + - 'installers/**' # Allow manual trigger workflow_dispatch: From c96b0f9fb3b32ff7561af58352bd6b512017a671 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Dec 2025 20:53:16 +0000 Subject: [PATCH 2/7] chore(release): 5.6.0-dev.1 [skip ci] --- VERSION | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 1bc788d3..65eb9739 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.6.0 +5.6.0-dev.1 diff --git a/package.json b/package.json index ad99505d..09f54727 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "5.6.0", + "version": "5.6.0-dev.1", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 1471bd2152b8eec376b7c0b5d13499546477c0cb Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 5 Dec 2025 13:29:10 -0500 Subject: [PATCH 3/7] fix(doctor): repair shared settings.json symlink broken by claude cli claude cli's atomic writes (toggle thinking, etc.) replace symlinks with regular files, breaking the settings sync chain. this adds: - `ccs doctor --fix` to detect and repair broken shared symlinks - `ccs sync` now also repairs shared symlinks automatically fixes #57 --- src/ccs.ts | 3 ++- src/commands/doctor-command.ts | 8 ++++++- src/commands/sync-command.ts | 9 +++++++ src/management/doctor.ts | 43 ++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/ccs.ts b/src/ccs.ts index eb6f2d11..ff210f8f 100644 --- a/src/ccs.ts +++ b/src/ccs.ts @@ -228,7 +228,8 @@ async function main(): Promise { // Special case: doctor command if (firstArg === 'doctor' || firstArg === '--doctor') { - await handleDoctorCommand(); + const shouldFix = args.includes('--fix') || args.includes('-f'); + await handleDoctorCommand(shouldFix); return; } diff --git a/src/commands/doctor-command.ts b/src/commands/doctor-command.ts index 5d5ff175..6f4c7c65 100644 --- a/src/commands/doctor-command.ts +++ b/src/commands/doctor-command.ts @@ -6,14 +6,20 @@ /** * Handle doctor command + * @param shouldFix - If true, attempt to fix detected issues */ -export async function handleDoctorCommand(): Promise { +export async function handleDoctorCommand(shouldFix = false): Promise { const DoctorModule = await import('../management/doctor'); const Doctor = DoctorModule.default; const doctor = new Doctor(); await doctor.runAllChecks(); + // Attempt to fix issues if --fix flag provided + if (shouldFix) { + await doctor.fixIssues(); + } + // Exit with error code if unhealthy process.exit(doctor.isHealthy() ? 0 : 1); } diff --git a/src/commands/sync-command.ts b/src/commands/sync-command.ts index 33244096..bb2c60c0 100644 --- a/src/commands/sync-command.ts +++ b/src/commands/sync-command.ts @@ -31,6 +31,15 @@ export async function handleSyncCommand(): Promise { const manager = new ClaudeSymlinkManager(); manager.install(false); + console.log(''); + + // Repair shared symlinks (~/.ccs/shared/ → ~/.claude/) + // This fixes symlinks broken by Claude CLI's atomic writes (e.g., toggle thinking) + const SharedManager = (await import('../management/shared-manager')).default; + const sharedManager = new SharedManager(); + sharedManager.ensureSharedDirectories(); + console.log(colored('[OK]', 'green') + ' Shared symlinks verified'); + console.log(''); console.log(colored('[OK] Sync complete!', 'green')); console.log(''); diff --git a/src/management/doctor.ts b/src/management/doctor.ts index af25862a..3dfaee1b 100644 --- a/src/management/doctor.ts +++ b/src/management/doctor.ts @@ -955,6 +955,49 @@ class Doctor { ); } + /** + * Fix detected issues + * Currently fixes: shared symlinks broken by Claude CLI's atomic writes + */ + async fixIssues(): Promise { + console.log(''); + console.log(header('ATTEMPTING FIXES')); + console.log(''); + + let fixed = 0; + + // Fix shared symlinks (settings.json broken by Claude CLI toggle thinking, etc.) + const sharedSettings = path.join(this.homedir, '.ccs', 'shared', 'settings.json'); + if (fs.existsSync(sharedSettings)) { + try { + const stats = fs.lstatSync(sharedSettings); + if (!stats.isSymbolicLink()) { + const spinner = ora('Fixing shared settings.json symlink').start(); + try { + // Import and use SharedManager to fix symlinks + const SharedManagerModule = await import('./shared-manager'); + const SharedManager = SharedManagerModule.default; + const sharedManager = new SharedManager(); + sharedManager.ensureSharedDirectories(); + spinner.succeed(ok('Fixed') + ' Shared settings.json symlink restored'); + fixed++; + } catch (err) { + spinner.fail(fail('Failed') + ` Could not fix: ${(err as Error).message}`); + } + } + } catch (_err) { + // Ignore stat errors + } + } + + if (fixed > 0) { + console.log(''); + console.log(ok(`[OK] Fixed ${fixed} issue(s)`)); + } else { + console.log(info('[i] No fixable issues detected')); + } + } + /** * Check if the health check results are healthy */ From 8fee4040cb1cbbfefb7051051cbe2f9806697627 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Dec 2025 18:30:54 +0000 Subject: [PATCH 4/7] chore(release): 5.6.0-dev.2 [skip ci] --- VERSION | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 65eb9739..86f215e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.6.0-dev.1 +5.6.0-dev.2 diff --git a/package.json b/package.json index 09f54727..80c60bf4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "5.6.0-dev.1", + "version": "5.6.0-dev.2", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli", From 20bf6266d2817bbceb8a9b5b7914f3ffc9164275 Mon Sep 17 00:00:00 2001 From: hubo Date: Sat, 6 Dec 2025 02:31:42 +0800 Subject: [PATCH 5/7] feat(cliproxy): add iFlow OAuth provider support (#55) Add iFlow as a new OAuth-based provider for CLIProxy. Changes: - Add config/base-iflow.settings.json with iFlow provider configuration - Add iFlow to CLIProxyProvider type - Add iFlow OAuth config (authUrl, scopes, authFlag) - Add iFlow to provider display names, auth prefixes, and type values - Update config-generator to support iFlow provider - Update base-qwen.settings.json models (qwen3-coder-plus) Co-authored-by: kaitranntt --- config/base-iflow.settings.json | 10 ++++++++++ config/base-qwen.settings.json | 8 ++++---- src/cliproxy/auth-handler.ts | 15 ++++++++++++--- src/cliproxy/config-generator.ts | 26 ++++++++++++++++++++++++-- src/cliproxy/types.ts | 5 ++--- 5 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 config/base-iflow.settings.json diff --git a/config/base-iflow.settings.json b/config/base-iflow.settings.json new file mode 100644 index 00000000..2fc56b47 --- /dev/null +++ b/config/base-iflow.settings.json @@ -0,0 +1,10 @@ +{ + "env": { + "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/iflow", + "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", + "ANTHROPIC_MODEL": "deepseek-v3.2", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "kimi-k2-thinking", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v3.2", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "minimax-m2" + } +} diff --git a/config/base-qwen.settings.json b/config/base-qwen.settings.json index 03b3e36c..03c510ca 100644 --- a/config/base-qwen.settings.json +++ b/config/base-qwen.settings.json @@ -2,9 +2,9 @@ "env": { "ANTHROPIC_BASE_URL": "http://127.0.0.1:8317/api/provider/qwen", "ANTHROPIC_AUTH_TOKEN": "ccs-internal-managed", - "ANTHROPIC_MODEL": "qwen3-coder", - "ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen3-coder", - "ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-coder", - "ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen3-coder" + "ANTHROPIC_MODEL": "qwen3-coder-plus", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "qwen3-coder-plus", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "qwen3-coder-plus", + "ANTHROPIC_DEFAULT_HAIKU_MODEL": "qwen3-vl-plus" } } diff --git a/src/cliproxy/auth-handler.ts b/src/cliproxy/auth-handler.ts index 4a192c33..311ad428 100644 --- a/src/cliproxy/auth-handler.ts +++ b/src/cliproxy/auth-handler.ts @@ -12,12 +12,12 @@ * Each provider has its own directory to avoid conflicts. */ +import { execSync, spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { spawn, execSync } from 'child_process'; import { ProgressIndicator } from '../utils/progress-indicator'; -import { getProviderAuthDir, generateConfig } from './config-generator'; import { ensureCLIProxyBinary } from './binary-manager'; +import { generateConfig, getProviderAuthDir } from './config-generator'; import { CLIProxyProvider } from './types'; /** @@ -169,6 +169,13 @@ const OAUTH_CONFIGS: Record = { scopes: ['openid', 'profile', 'email', 'model.completion'], authFlag: '--qwen-login', }, + iflow: { + provider: 'iflow', + displayName: 'iFlow', + authUrl: 'https://iflow.cn/oauth', + scopes: ['phone', 'profile', 'email'], + authFlag: '--iflow-login', + }, }; /** @@ -199,6 +206,7 @@ const PROVIDER_AUTH_PREFIXES: Record = { codex: ['codex-', 'openai-'], agy: ['antigravity-', 'agy-'], qwen: ['qwen-'], + iflow: ['iflow-'], }; /** @@ -210,6 +218,7 @@ const PROVIDER_TYPE_VALUES: Record = { codex: ['codex'], agy: ['antigravity'], qwen: ['qwen'], + iflow: ['iflow'], }; /** @@ -327,7 +336,7 @@ export function getAuthStatus(provider: CLIProxyProvider): AuthStatus { * Get auth status for all providers */ export function getAllAuthStatus(): AuthStatus[] { - const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen']; + const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow']; return providers.map(getAuthStatus); } diff --git a/src/cliproxy/config-generator.ts b/src/cliproxy/config-generator.ts index 9c6cde6c..c267bc29 100644 --- a/src/cliproxy/config-generator.ts +++ b/src/cliproxy/config-generator.ts @@ -12,7 +12,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { getCcsDir } from '../utils/config-manager'; import { CLIProxyProvider, ProviderConfig, ProviderModelMapping } from './types'; -import { getModelMappingFromConfig } from './base-config-loader'; +import { getModelMappingFromConfig, getEnvVarsFromConfig } from './base-config-loader'; /** Settings file structure for user overrides */ interface ProviderSettings { @@ -31,6 +31,7 @@ const PROVIDER_DISPLAY_NAMES: Record = { codex: 'Codex', agy: 'Antigravity', qwen: 'Qwen Code', + iflow: 'iFlow', }; /** @@ -197,7 +198,11 @@ export function getClaudeEnvVars( ): NodeJS.ProcessEnv { const models = getModelMapping(provider); - return { + // Base env vars from config file (includes ANTHROPIC_MAX_TOKENS, etc.) + const baseEnvVars = getEnvVarsFromConfig(provider); + + // Core env vars that we always set dynamically + const coreEnvVars = { // Provider-specific endpoint - routes to correct provider via URL path ANTHROPIC_BASE_URL: `http://127.0.0.1:${port}/api/provider/${provider}`, ANTHROPIC_AUTH_TOKEN: CCS_INTERNAL_API_KEY, @@ -206,6 +211,23 @@ export function getClaudeEnvVars( ANTHROPIC_DEFAULT_SONNET_MODEL: models.sonnetModel || models.claudeModel, ANTHROPIC_DEFAULT_HAIKU_MODEL: models.haikuModel || models.claudeModel, }; + + // 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; + + // Merge core env vars with additional env vars from base config + return { + ...coreEnvVars, + ...additionalEnvVars, // Includes ANTHROPIC_MAX_TOKENS, etc. + }; } /** diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index a9ae85a7..f42e4edc 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -46,8 +46,6 @@ export interface BinaryManagerConfig { maxRetries: number; /** Enable verbose logging */ verbose: boolean; - /** Force specific version (skip auto-upgrade to latest) */ - forceVersion?: boolean; } /** @@ -113,8 +111,9 @@ export interface DownloadResult { * - codex: OpenAI Codex via OAuth * - agy: Antigravity via OAuth (short name for easy usage) * - qwen: Qwen Code via OAuth (qwen3-coder) + * - iflow: iFlow via OAuth */ -export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen'; +export type CLIProxyProvider = 'gemini' | 'codex' | 'agy' | 'qwen' | 'iflow'; /** * CLIProxy config.yaml structure (minimal) From 3bb1ea7541fcf1bd38818b941ef3c5997d8daeb5 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Fri, 5 Dec 2025 13:35:29 -0500 Subject: [PATCH 6/7] fix(types): add forceversion to binarymanagerconfig interface --- src/cliproxy/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cliproxy/types.ts b/src/cliproxy/types.ts index f42e4edc..925842d0 100644 --- a/src/cliproxy/types.ts +++ b/src/cliproxy/types.ts @@ -46,6 +46,8 @@ export interface BinaryManagerConfig { maxRetries: number; /** Enable verbose logging */ verbose: boolean; + /** Force specific version (skip auto-upgrade to latest) */ + forceVersion: boolean; } /** From 656552ecda74a5388e4085f1d6a3db61a10ab772 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Dec 2025 18:36:54 +0000 Subject: [PATCH 7/7] chore(release): 5.6.0-dev.3 [skip ci] --- VERSION | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 86f215e3..0305f287 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.6.0-dev.2 +5.6.0-dev.3 diff --git a/package.json b/package.json index 80c60bf4..b432e6a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "5.6.0-dev.2", + "version": "5.6.0-dev.3", "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6", "keywords": [ "cli",