diff --git a/CHANGELOG.md b/CHANGELOG.md index f5a820909..8cb3c58ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,100 @@ All notable changes to CC Switch will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.9.0-beta.1] - 2025-12-18 + +### Beta Release + +This beta release introduces the **Local API Proxy** feature, along with Skills multi-app support, UI improvements, and numerous bug fixes. + +### Major Features + +#### Local Proxy Server +- **Local HTTP Proxy** - High-performance proxy server built on Axum framework +- **Multi-app Support** - Unified proxy for Claude Code, Codex, and Gemini CLI API requests +- **Per-app Takeover** - Independent control over which apps route through the proxy +- **Live Config Takeover** - Automatically backs up and redirects CLI configurations to local proxy + +#### Auto Failover +- **Circuit Breaker** - Automatically detects provider failures and triggers protection +- **Smart Failover** - Automatically switches to backup provider when current one is unavailable +- **Health Tracking** - Real-time monitoring of provider availability +- **Independent Failover Queues** - Each app maintains its own failover queue + +#### Monitoring +- **Request Logging** - Detailed logging of all proxy requests +- **Usage Statistics** - Token consumption, latency, success rate metrics +- **Real-time Status** - Frontend displays proxy status and statistics + +#### Skills Multi-App Support +- **Multi-app Support** - Skills now support both Claude and Codex (#365) +- **Multi-app Migration** - Existing Skills auto-migrate to multi-app structure (#378) +- **Installation Path Fix** - Use directory basename for skill installation path (#358) + +### Added +- **Provider Icon Colors** - Customize provider icon colors (#385) +- **Deeplink Usage Config** - Import usage query config via deeplink (#400) +- **Error Request Logging** - Detailed logging for proxy requests (#401) +- **Closable Toast** - Added close button to switch notification toast (#350) +- **Icon Color Component** - ProviderIcon component supports color prop (#384) + +### Fixed + +#### Proxy Related +- Takeover Codex base_url via model_provider +- Harden crash recovery with fallback detection +- Sync UI when active provider differs from current setting +- Resolve circuit breaker race condition and error classification +- Stabilize live takeover and provider editing +- Reset health badges when proxy stops +- Retry failover for all HTTP errors including 4xx +- Fix HalfOpen counter underflow and config field inconsistencies +- Resolve circuit breaker state persistence and HalfOpen deadlock +- Auto-recover live config after abnormal exit +- Update live backup when hot-switching provider in proxy mode +- Wait for server shutdown before exiting app +- Disable auto-start on app launch by resetting enabled flag on stop +- Sync live config tokens to database before takeover +- Resolve 404 error and auto-setup proxy targets + +#### MCP Related +- Skip sync when target CLI app is not installed +- Improve upsert and import robustness +- Use browser-compatible platform detection for MCP presets + +#### UI Related +- Restore fade transition for Skills button +- Add close button to all success toasts +- Prevent card jitter when health badge appears +- Update SettingsPage tab styles (#342) + +#### Other +- Fix Azure website link (#407) +- Add fallback to provider config for usage credentials (#360) +- Fix Windows black screen on startup (use system titlebar) +- Add fallback for crypto.randomUUID() on older WebViews +- Use correct npm package for Codex CLI version check +- Security fixes for JavaScript executor and usage script (#151) + +### Improved +- **Proxy Active Theme** - Apply emerald theme when proxy takeover is active +- **Card Animation** - Improved provider card hover animation +- **Remove Restart Prompt** - No longer prompts restart when switching providers + +### Technical +- Implement per-app takeover mode +- Proxy module contains 20+ Rust files with complete layered architecture +- Add 5 new database tables for proxy functionality +- Modularize handlers.rs to reduce code duplication +- Remove is_proxy_target in favor of failover_queue + +### Stats +- 55 commits since v3.8.2 +- 164 files changed +- +22,164 / -570 lines + +--- + ## [3.8.0] - 2025-11-28 ### Major Updates diff --git a/docs/proxy-guide-zh.md b/docs/proxy-guide-zh.md new file mode 100644 index 000000000..c53559a92 --- /dev/null +++ b/docs/proxy-guide-zh.md @@ -0,0 +1,165 @@ +# CC Switch 代理功能使用指南 + +## 功能介绍 + +CC Switch 的代理功能是一个本地 HTTP 代理服务器,可以统一管理 Claude Code、Codex 和 Gemini CLI 的 API 请求。主要特性包括: + +- **统一代理入口** - 所有 CLI 应用的请求通过本地代理转发 +- **自动故障转移** - 当前供应商故障时自动切换到备用供应商 +- **按应用控制** - 可独立控制每个应用是否启用代理 +- **配置保护** - 自动备份原始配置,停止代理时安全恢复 + +## 快速开始 + +### 1. 启动代理 + +在 CC Switch 主界面,点击右上角的 **Proxy** 按钮,可以看到代理控制面板。 + +点击 **启动代理** 按钮启动本地代理服务器。代理默认监听 `127.0.0.1:15721`。 + +### 2. 启用应用接管 + +代理启动后,你可以选择让哪些应用的请求通过代理: + +- **Claude** - 接管 Claude Code 的 API 请求 +- **Codex** - 接管 Codex CLI 的 API 请求 +- **Gemini** - 接管 Gemini CLI 的 API 请求 + +点击对应应用的开关即可启用/禁用接管。 + +> **注意**:启用接管后,CC Switch 会自动修改对应应用的配置文件,将 API 端点指向本地代理。原始配置会被安全备份。 + +### 3. 正常使用 CLI + +启用接管后,你可以正常使用各个 CLI 工具。所有请求都会经过 CC Switch 代理转发到配置的供应商。 + +### 4. 停止代理 + +当你不再需要代理时,点击 **停止代理** 按钮。CC Switch 会: + +1. 安全关闭代理服务器 +2. 自动恢复所有应用的原始配置 +3. 清除代理状态 + +## 自动故障转移 + +### 工作原理 + +代理功能内置了智能故障转移机制: + +1. **健康监控** - 实时监控每个供应商的响应状态 +2. **熔断器** - 连续失败 5 次后触发熔断,暂停使用该供应商 +3. **自动切换** - 熔断后自动切换到列表中的下一个供应商 +4. **自动恢复** - 30 秒后尝试恢复熔断的供应商 + +### 配置故障转移 + +要使用故障转移功能,你需要: + +1. 在对应应用下添加多个供应商(至少 2 个) +2. 启动代理并启用接管 +3. 当主供应商故障时,代理会自动切换到备用供应商 + +### 健康状态指示 + +在供应商卡片上可以看到健康状态指示: + +- **绿色** - 供应商正常 +- **红色** - 供应商故障/熔断中 +- **灰色** - 未使用代理或未检测 + +## 按应用接管 + +v3.9.0 新增了按应用分粒度控制功能: + +- 你可以只接管 Claude,而让 Codex 使用原始配置 +- 每个应用的接管状态独立管理 +- 启用/禁用不会影响其他应用 + +### 接管状态检测 + +CC Switch 通过检测配置备份来判断接管状态: + +- 存在备份 = 已接管 +- 无备份 = 未接管 + +这确保了即使 CC Switch 异常退出,重新启动后也能正确识别状态。 + +## 代理配置 + +在代理面板中,你可以配置以下参数: + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| 监听地址 | 127.0.0.1 | 代理服务器绑定地址 | +| 监听端口 | 15721 | 代理服务器端口 | +| 最大重试 | 3 | 请求失败时的最大重试次数 | +| 请求超时 | 120 秒 | 单个请求的超时时间 | +| 启用日志 | 是 | 是否记录请求日志 | + +## 常见问题 + +### Q: 代理启动失败,提示端口被占用? + +A: 默认端口 15721 可能被其他程序占用。你可以: +- 关闭占用该端口的程序 +- 在代理配置中修改端口号 + +### Q: 启用接管后 CLI 无法使用? + +A: 请检查: +1. 代理服务器是否正常运行(查看代理面板状态) +2. 供应商配置是否正确(API Key 等) +3. 网络连接是否正常 + +### Q: 如何恢复原始配置? + +A: 点击 **停止代理** 按钮,CC Switch 会自动恢复所有应用的原始配置。 + +如果 CC Switch 异常退出,重新启动后会检测到之前的备份,你可以: +- 点击停止代理来恢复配置 +- 或继续使用代理功能 + +### Q: 故障转移没有生效? + +A: 请确保: +1. 配置了至少 2 个供应商 +2. 代理已启动且接管已启用 +3. 故障转移只在代理模式下工作 + +### Q: 代理会影响性能吗? + +A: 本地代理的延迟开销非常小(通常 < 1ms)。但如果启用了请求日志,在高频请求场景下可能会有少量性能影响。 + +## 技术细节 + +### 配置文件位置 + +启用接管后,CC Switch 会修改以下配置文件: + +| 应用 | 配置文件 | 修改内容 | +|------|----------|----------| +| Claude | `~/.claude/settings.json` | `apiBaseUrl` 指向代理 | +| Codex | `~/.codex/config.toml` | `[api] baseUrl` 指向代理 | +| Gemini | `~/.gemini/.env` | `GEMINI_BASE_URL` 指向代理 | + +原始配置备份在 CC Switch 数据库中,停止代理时自动恢复。 + +### 代理模式 + +代理服务器运行在接管模式下,会: + +1. 接收来自 CLI 的 HTTPS 请求 +2. 根据当前供应商配置转发到真实 API 端点 +3. 返回响应给 CLI +4. 记录请求日志和健康状态 + +### 数据库表 + +代理功能使用以下数据库表: + +- `proxy_config` - 代理配置 +- `provider_health` - 供应商健康状态 +- `proxy_request_logs` - 请求日志 +- `circuit_breaker_config` - 熔断器配置 +- `proxy_live_backup` - Live 配置备份 diff --git a/package.json b/package.json index 6cf00b90d..fdb1a8e88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cc-switch", - "version": "3.8.2", + "version": "3.9.0-beta.1", "description": "All-in-One Assistant for Claude Code, Codex & Gemini CLI", "scripts": { "dev": "pnpm tauri dev", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index bffed07fe..b8f2f4724 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -695,7 +695,7 @@ dependencies = [ [[package]] name = "cc-switch" -version = "3.8.2" +version = "3.9.0-beta.1" dependencies = [ "anyhow", "async-stream", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index aba6b349c..c8e00eace 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cc-switch" -version = "3.8.2" +version = "3.9.0-beta.1" description = "All-in-One Assistant for Claude Code, Codex & Gemini CLI" authors = ["Jason Young"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b700b05a3..1a084c5a1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "CC Switch", - "version": "3.8.2", + "version": "3.9.0-beta.1", "identifier": "com.ccswitch.desktop", "build": { "frontendDist": "../dist", diff --git a/src/components/proxy/ProxyToggle.tsx b/src/components/proxy/ProxyToggle.tsx index 20385c58e..0d3be3851 100644 --- a/src/components/proxy/ProxyToggle.tsx +++ b/src/components/proxy/ProxyToggle.tsx @@ -19,13 +19,8 @@ interface ProxyToggleProps { export function ProxyToggle({ className, activeApp }: ProxyToggleProps) { const { t } = useTranslation(); - const { - isRunning, - takeoverStatus, - setTakeoverForApp, - isPending, - status, - } = useProxyStatus(); + const { isRunning, takeoverStatus, setTakeoverForApp, isPending, status } = + useProxyStatus(); const handleToggle = async (checked: boolean) => { await setTakeoverForApp({ appType: activeApp, enabled: checked }); diff --git a/src/config/codexProviderPresets.ts b/src/config/codexProviderPresets.ts index e291aa292..e6ceb2da3 100644 --- a/src/config/codexProviderPresets.ts +++ b/src/config/codexProviderPresets.ts @@ -40,7 +40,7 @@ export function generateThirdPartyAuth(apiKey: string): Record { export function generateThirdPartyConfig( providerName: string, baseUrl: string, - modelName = "gpt-5.1-codex" + modelName = "gpt-5.1-codex", ): string { // 清理供应商名称,确保符合TOML键名规范 const cleanProviderName = @@ -113,7 +113,7 @@ requires_openai_auth = true`, config: generateThirdPartyConfig( "aihubmix", "https://aihubmix.com/v1", - "gpt-5.1-codex" + "gpt-5.1-codex", ), endpointCandidates: [ "https://aihubmix.com/v1", @@ -128,7 +128,7 @@ requires_openai_auth = true`, config: generateThirdPartyConfig( "dmxapi", "https://www.dmxapi.cn/v1", - "gpt-5.1-codex" + "gpt-5.1-codex", ), endpointCandidates: ["https://www.dmxapi.cn/v1"], }, @@ -141,7 +141,7 @@ requires_openai_auth = true`, config: generateThirdPartyConfig( "packycode", "https://www.packyapi.com/v1", - "gpt-5.1-codex" + "gpt-5.1-codex", ), endpointCandidates: [ "https://www.packyapi.com/v1", diff --git a/src/hooks/useProxyStatus.ts b/src/hooks/useProxyStatus.ts index 98aeeb450..cc4c78ab8 100644 --- a/src/hooks/useProxyStatus.ts +++ b/src/hooks/useProxyStatus.ts @@ -6,7 +6,11 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { invoke } from "@tauri-apps/api/core"; import { toast } from "sonner"; import { useTranslation } from "react-i18next"; -import type { ProxyStatus, ProxyServerInfo, ProxyTakeoverStatus } from "@/types/proxy"; +import type { + ProxyStatus, + ProxyServerInfo, + ProxyTakeoverStatus, +} from "@/types/proxy"; import { extractErrorMessage } from "@/utils/errorUtils"; /** @@ -82,13 +86,8 @@ export function useProxyStatus() { // 按应用开启/关闭接管 const setTakeoverForAppMutation = useMutation({ - mutationFn: ({ - appType, - enabled, - }: { - appType: string; - enabled: boolean; - }) => invoke("set_proxy_takeover_for_app", { appType, enabled }), + mutationFn: ({ appType, enabled }: { appType: string; enabled: boolean }) => + invoke("set_proxy_takeover_for_app", { appType, enabled }), onSuccess: (_data, variables) => { const appLabel = variables.appType === "claude"