Commit Graph

321 Commits

  • refactor(commands): improve terminal launch code structure and fix env vars
    重构 open_provider_terminal 相关代码,提升可维护性和可读性。
    
    主要改进:
    - 将 launch_terminal_with_env 拆分为多个职责单一的小函数
      * write_claude_config: 写入配置文件
      * escape_shell_path: 转义 shell 路径
      * generate_wrapper_script: 生成包装脚本
      * launch_macos_terminal / launch_linux_terminal / launch_windows_terminal: 平台特定启动逻辑
    - 使用 let Some else 提前返回模式,减少嵌套
    - 修复 Gemini 环境变量名为 GEMINI_API_KEY(而非 GOOGLE_API_KEY)
    - 完善临时文件清理逻辑:
      * macOS/Linux: 使用 trap EXIT 自动清理
      * Windows: 批处理文件自删除
    - 代码格式化和 import 排序优化
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
  • fix(commands): add auto-cleanup for temp config files when terminal closes
    在 open_provider_terminal 功能中添加了临时配置文件的自动清理逻辑,
    确保在用户关闭终端窗口时自动删除创建的临时配置文件。
    
    修改内容:
    - Linux: 使用 bash -c 嵌套包装脚本,通过 trap EXIT 信号在 shell 退出时清理配置文件
    - macOS: 同样使用嵌套 bash + trap 机制来处理清理
    - Windows: 保持原有的批处理文件自删除逻辑(del 命令)
    
    技术细节:
    - 之前使用 sh -c "...; exec $SHELL" 会导致 trap 失效
    - 现在使用 bash -c 'trap ... EXIT; ...; exec bash --norc --noprofile'
    - exec 会替换进程但保留 trap 信号处理器
    - 当用户关闭终端时,EXIT 信号触发清理操作
    
    影响范围:
    - src-tauri/src/commands/misc.rs (launch_terminal_with_env 函数)
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
  • Merge upstream/main into main
    Resolved conflicts in src-tauri/src/lib.rs:
    - Kept both: open_provider_terminal (my feature) and universal provider commands (upstream)
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
  • fix: 移除已废弃的 sync_enabled_to_codex 调用 (#460)
    * fix(mcp): 移除同步Codex Provider时的旧MCP同步调用
    
    sync_enabled_to_codex使用旧的config.mcp.codex结构,
    在v3.7.0统一结构中该字段为空,导致MCP配置被错误清除。
    MCP同步应通过McpService进行。
    
    Fixes #403
    
    * test(mcp): update test to reflect new MCP sync architecture
    
    Remove MCP-related assertions from sync_codex_provider_writes_auth_and_config
    test since provider switching no longer triggers MCP sync in v3.7.0+.
    
    MCP synchronization is now handled independently by McpService,
    not as part of the provider switch flow.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix: MCP同步时优雅处理无效的Codex config.toml (#461)
    * fix(mcp): 移除同步Codex Provider时的旧MCP同步调用
    
    sync_enabled_to_codex使用旧的config.mcp.codex结构,
    在v3.7.0统一结构中该字段为空,导致MCP配置被错误清除。
    MCP同步应通过McpService进行。
    
    Fixes #403
    
    * fix(mcp): 优雅处理Codex配置文件解析失败的情况
    
    当~/.codex/config.toml存在但内容无效时,MCP同步操作会失败,
    导致后续provider切换等操作也失败。
    
    修改sync_single_server_to_codex和remove_server_from_codex函数,
    在配置文件解析失败时进行容错处理而不是返回错误。
    
    Fixes #393
  • fix(macos): use .app bundle path for autostart to prevent terminal window (#462)
    On macOS, the auto-launch library requires the .app bundle path (e.g.,
    /Applications/CC Switch.app) rather than the binary path inside the bundle
    (e.g., .app/Contents/MacOS/CC Switch). Using the binary path directly
    causes AppleScript login items to open a terminal window.
    
    This fix extracts the .app bundle path from current_exe() on macOS,
    ensuring proper integration with macOS login items.
    
    Closes #375
  • feat: add Universal Provider feature (#348)
    * feat: add Universal Provider feature
    
    - Add Universal Provider data structures and type definitions
    - Implement backend CRUD operations and sync functionality
    - Add frontend UI components (UniversalProviderPanel, Card, FormModal)
    - Add NewAPI icon and preset configuration
    - Support cross-app (Claude/Codex/Gemini) configuration sync
    - Add website URL field for providers
    - Implement real-time refresh via event notifications
    - Add i18n support (Chinese/English/Japanese)
    
    * feat: integrate universal provider presets into add provider dialog
    
    - Add universal provider presets (NewAPI, Custom Gateway) to preset selector
    - Show universal presets with Layers icon badge in preset selector
    - Open UniversalProviderFormModal when universal preset is clicked
    - Pass initialPreset to auto-fill form when opened from add dialog
    - Add i18n keys for addSuccess/addFailed messages
    - Keep separate universal provider panel for management
    
    * refactor: move universal provider management to add dialog
    
    - Remove Layers button from main navigation header
    - Add 'Manage' button next to universal provider presets
    - Open UniversalProviderPanel from within add provider dialog
    - Add i18n keys for 'manage' in all locales
    
    * style: display universal provider presets on separate line
    
    - Move universal provider section to a new row with border separator
    - Add label '统一供应商:' to clarify the section
    
    * style: unify universal provider label style with preset label
    
    - Use FormLabel component for consistent styling
    - Add background to 'Manage' button matching preset buttons
    - Update icon size and button padding for consistency
    
    * feat: add sync functionality and JSON preview for Universal Provider
    
    * fix: add missing in_failover_queue field to Provider structs
    
    After rebasing to main, the Provider struct gained a new
    `in_failover_queue` field. This fix adds the missing field
    to the three to_*_provider() methods in UniversalProvider.
    
    * refactor: redesign AddProviderDialog with tab-based layout
    
    - Add tabs to separate app-specific providers and universal providers
    - Move "Add Universal Provider" button from panel header to footer
    - Remove unused handleAdd callback and clean up imports
    - Update emptyHint i18n text to reference the footer button
    
    * fix: append /v1 suffix to Codex base_url in Universal Provider
    
    Codex uses OpenAI-compatible API which requires the /v1 endpoint suffix.
    The Universal Provider now automatically appends /v1 to base_url when
    generating Codex provider config if not already present.
    
    - Handle trailing slashes to avoid double slashes
    - Apply fix to both backend (to_codex_provider) and frontend preview
    
    * feat: auto-sync universal provider to apps on creation
    
    Previously, users had to manually click sync after adding a universal
    provider. Now it automatically syncs to Claude/Codex/Gemini on creation,
    providing a smoother user experience.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(database): add backward compatibility check for proxy_config seed insert
    Add has_column check before inserting seed data into proxy_config table.
    This prevents SQL errors when upgrading from older databases where
    proxy_config was a singleton table without the app_type column.
    
    The migration function will handle the table structure upgrade and
    insert the three rows after converting to the new schema.
  • Feat/usage model extraction (#455)
    * feat(proxy): extract model name from API response for accurate usage tracking
    
    - Add model field extraction in TokenUsage parsing for Claude, OpenAI, and Codex
    - Prioritize response model over request model in usage logging
    - Update model extractors to use parsed usage.model first
    - Add tests for model extraction in stream and non-stream responses
    
    * feat(proxy): implement streaming timeout control with validation
    
    - Add first byte timeout (0 or 1-180s) for streaming requests
    - Add idle timeout (0 or 60-600s) for streaming data gaps
    - Add non-streaming timeout (0 or 60-1800s) for total request
    - Implement timeout logic in response processor
    - Add 1800s global timeout fallback when disabled
    - Add database schema migration for timeout fields
    - Add i18n translations for timeout settings
    
    * feat(proxy): add model mapping module for provider-based model substitution
    
    - Add model_mapper.rs with ModelMapping struct to extract model configs from Provider
    - Support ANTHROPIC_MODEL, ANTHROPIC_REASONING_MODEL, and default models for haiku/sonnet/opus
    - Implement thinking mode detection for reasoning model priority
    - Include comprehensive unit tests for all mapping scenarios
    
    * fix(proxy): bypass circuit breaker for single provider scenario
    
    When failover is disabled (single provider), circuit breaker open state
    would block all requests causing poor UX. Now bypasses circuit breaker
    check in this scenario. Also integrates model mapping into request flow.
    
    * feat(ui): add reasoning model field to Claude provider form
    
    Add ANTHROPIC_REASONING_MODEL configuration field for Claude providers,
    allowing users to specify a dedicated model for thinking/reasoning tasks.
    
    * feat(proxy): add openrouter_compat_mode for optional format conversion
    
    Add configurable OpenRouter compatibility mode that enables Anthropic to
    OpenAI format conversion. When enabled, rewrites endpoint to /v1/chat/completions
    and transforms request/response formats. Defaults to enabled for OpenRouter.
    
    * feat(ui): add OpenRouter compatibility mode toggle
    
    Add UI toggle for OpenRouter providers to enable/disable compatibility
    mode which uses OpenAI Chat Completions format with SSE conversion.
    
    * feat(stream-check): use provider-configured model for health checks
    
    Extract model from provider's settings_config (ANTHROPIC_MODEL, GEMINI_MODEL,
    or Codex config.toml) instead of always using default test models.
    
    * refactor(ui): remove timeout settings from AutoFailoverConfigPanel
    
    Remove streaming/non-streaming timeout configuration from failover panel
    as these settings have been moved to a dedicated location.
    
    * refactor(database): migrate proxy_config to per-app three-row structure
    
    Replace singleton proxy_config table with app_type primary key structure,
    allowing independent proxy settings for Claude, Codex, and Gemini.
    Add GlobalProxyConfig queries and per-app config management in DAO layer.
    
    * feat(proxy): add GlobalProxyConfig and AppProxyConfig types
    
    Add new type definitions for the refactored proxy configuration:
    - GlobalProxyConfig: shared settings (enabled, address, port, logging)
    - AppProxyConfig: per-app settings (failover, timeouts, circuit breaker)
    
    * refactor(proxy): update service layer for per-app config structure
    
    Adapt proxy service, handler context, and provider router to use
    the new per-app configuration model. Read enabled/timeout settings
    from proxy_config table instead of settings table.
    
    * feat(commands): add global and per-app proxy config commands
    
    Add new Tauri commands for the refactored proxy configuration:
    - get_global_proxy_config / update_global_proxy_config
    - get_proxy_config_for_app / update_proxy_config_for_app
    Update startup restore logic to read from proxy_config table.
    
    * feat(api): add frontend API and Query hooks for proxy config
    
    Add TypeScript wrappers and TanStack Query hooks for:
    - Global proxy config (address, port, logging)
    - Per-app proxy config (failover, timeouts, circuit breaker)
    - Proxy takeover status management
    
    * refactor(ui): redesign proxy panel with inline config controls
    
    Replace ProxySettingsDialog with inline controls in ProxyPanel.
    Add per-app takeover switches and global address/port settings.
    Simplify AutoFailoverConfigPanel by removing timeout settings.
    
    * feat(i18n): add proxy takeover translations and update types
    
    Add i18n strings for proxy takeover status in zh/en/ja.
    Update TypeScript types for GlobalProxyConfig and AppProxyConfig.
    
    * refactor(proxy): load circuit breaker config per-app instead of globally
    
    Extract app_type from router key and read circuit breaker settings
    from the corresponding proxy_config row for each application.
  • feat: add provider-specific terminal button
    Add a terminal button next to each provider card that opens a new terminal
    window with that provider's specific API configuration. This allows using
    different providers independently without changing the global setting.
    
    Changes:
    - Backend: Add `open_provider_terminal` command that extracts provider
      config and creates a temporary claude settings file
    - Frontend: Add terminal button to provider cards with proper callback
      propagation through component hierarchy
    - Support macOS (Terminal.app), Linux (gnome-terminal, konsole, etc.),
      and Windows (cmd)
    
    Each provider gets a unique config file named `claude_<providerId>_<pid>.json`
    in the temp directory, containing the provider's API configuration.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
  • Feat/auto failover switch (#440)
    * feat(failover): add auto-failover master switch with proxy integration
    
    - Add persistent auto_failover_enabled setting in database
    - Add get/set_auto_failover_enabled commands
    - Provider router respects master switch state
    - Proxy shutdown automatically disables failover
    - Enabling failover auto-starts proxy server
    - Optimistic updates for failover queue toggle
    
    * feat(proxy): persist proxy takeover state across app restarts
    
    - Add proxy_takeover_{app_type} settings for per-app state tracking
    - Restore proxy takeover state automatically on app startup
    - Preserve state on normal exit, clear on manual stop
    - Add stop_with_restore_keep_state method for graceful shutdown
    
    * fix(proxy): set takeover state for all apps in start_with_takeover
    
    * fix(windows): hide console window when checking CLI versions
    
    Add CREATE_NO_WINDOW flag to prevent command prompt from flashing
    when detecting claude/codex/gemini CLI versions on Windows.
    
    * refactor(failover): make auto-failover toggle per-app independent
    
    - Change setting key from 'auto_failover_enabled' to 'auto_failover_enabled_{app_type}'
    - Update provider_router to check per-app failover setting
    - When failover disabled, use current provider only; when enabled, use queue order
    - Add unit tests for failover enabled/disabled behavior
    
    * feat(failover): auto-switch to higher priority provider on recovery
    
    - After circuit breaker reset, check if recovered provider has higher priority
    - Automatically switch back if queue_order is lower (higher priority)
    - Stream health check now resets circuit breaker on success/degraded
    
    * chore: remove unused start_proxy_with_takeover command
    
    - Remove command registration from lib.rs
    - Add comment clarifying failover queue is preserved on proxy stop
    
    * feat(ui): integrate failover controls into provider cards
    
    - Add failover toggle button to provider card actions
    - Show priority badge (P1, P2, ...) for queued providers
    - Highlight active provider with green border in failover mode
    - Sync drag-drop order with failover queue
    - Move per-app failover toggle to FailoverQueueManager
    - Simplify SettingsPage failover section
    
    * test(providers): add mocks for failover hooks in ProviderList tests
    
    * refactor(failover): merge failover_queue table into providers
    
    - Add in_failover_queue field to providers table
    - Remove standalone failover_queue table and related indexes
    - Simplify queue ordering by reusing sort_index field
    - Remove reorder_failover_queue and set_failover_item_enabled commands
    - Update frontend to use simplified FailoverQueueItem type
    
    * fix(database): ensure in_failover_queue column exists for v2 databases
    
    Add column check in create_tables to handle existing v2 databases
    that were created before the failover queue refactor.
    
    * fix(ui): differentiate active provider border color by proxy mode
    
    - Use green border/gradient when proxy takeover is active
    - Use blue border/gradient in normal mode (no proxy)
    - Improves visual distinction between proxy and non-proxy states
    
    * fix(database): clear provider health record when removing from failover queue
    
    When a provider is removed from the failover queue, its health monitoring
    is no longer needed. This change ensures the health record is also deleted
    from the database to prevent stale data.
    
    * fix(failover): improve cache cleanup for provider health and circuit breaker
    
    - Use removeQueries instead of invalidateQueries when stopping proxy to
      completely clear health and circuit breaker caches
    - Clear provider health and circuit breaker caches when removing from
      failover queue
    - Refresh failover queue after drag-sort since queue order depends on
      sort_index
    - Only show health badge when provider is in failover queue
    
    * style: apply prettier formatting to App.tsx and ProviderList.tsx
    
    * fix(proxy): handle missing health records and clear health on proxy stop
    
    - Return default healthy state when provider health record not found
    - Add clear_provider_health_for_app to clear health for specific app
    - Clear app health records when stopping proxy takeover
    
    * fix(proxy): track actual provider used in forwarding for accurate logging
    
    Introduce ForwardResult and ForwardError structs to return the actual
    provider that handled the request. This ensures usage statistics and
    error logs reflect the correct provider after failover.
  • feat(failover): add auto-failover master switch with proxy integration (#427)
    * feat(failover): add auto-failover master switch with proxy integration
    
    - Add persistent auto_failover_enabled setting in database
    - Add get/set_auto_failover_enabled commands
    - Provider router respects master switch state
    - Proxy shutdown automatically disables failover
    - Enabling failover auto-starts proxy server
    - Optimistic updates for failover queue toggle
    
    * feat(proxy): persist proxy takeover state across app restarts
    
    - Add proxy_takeover_{app_type} settings for per-app state tracking
    - Restore proxy takeover state automatically on app startup
    - Preserve state on normal exit, clear on manual stop
    - Add stop_with_restore_keep_state method for graceful shutdown
    
    * fix(proxy): set takeover state for all apps in start_with_takeover
  • feat(settings): add option to skip Claude Code first-run confirmation
    Add a new setting to automatically skip Claude Code's onboarding screen
    by writing hasCompletedOnboarding=true to ~/.claude.json. The setting
    defaults to enabled for better user experience.
    
    - Add set/clear_has_completed_onboarding functions in claude_mcp.rs
    - Add Tauri commands and frontend API integration
    - Add toggle in WindowSettings with i18n support (en/zh/ja)
    - Fix hardcoded Chinese text in tests to use i18n keys
  • chore: bump version to 3.9.0-2 for second test release
    - Update version in package.json, Cargo.toml, tauri.conf.json
    - Fix clippy too_many_arguments warning in forwarder.rs
  • refactor(proxy): switch OpenRouter to passthrough mode for native Claude API
    OpenRouter now supports Claude Code compatible endpoint (/v1/messages),
    eliminating the need for Anthropic ↔ OpenAI format conversion.
    
    - Disable format transformation for OpenRouter (keep old logic as fallback)
    - Pass through original endpoint instead of redirecting to /v1/chat/completions
    - Add anthropic-version header for ClaudeAuth and Bearer strategies
    - Update tests to reflect new passthrough behavior
  • fix(window): add minWidth/minHeight to Windows platform config
    Tauri 2.0 platform config merging is shallow, not deep. The Windows
    config only specified titleBarStyle, causing minWidth/minHeight to
    be missing on Windows. This allowed users to resize the window below
    900px, causing header elements to misalign.
  • fix(proxy): respect existing token field when syncing Claude config
    - Add support for ANTHROPIC_API_KEY in Claude auth extraction
    - Only update existing token fields during sync, avoid adding fields
      that weren't originally configured by the user
    - Add tests for both scenarios
  • fix(proxy): add fallback recovery for orphaned takeover state
    - Detect takeover residue in Live configs even when proxy is not running
    - Implement 3-tier fallback: backup → SSOT → cleanup placeholders
    - Only delete backup after successful restore to prevent data loss
    - Fix EditProviderDialog to check current app's takeover status only
  • refactor(proxy): remove global auto-start flag
    - Remove global proxy auto-start flag from config and UI.
    - Simplify per-app takeover start/stop and stop server when the last takeover is disabled.
    - Restore live takeover detection used for crash recovery.
    - Keep proxy_config.enabled column but always write 0 for compatibility.
    - Tests: not run (not requested).
  • fix(backup): restrict SQL import to CC Switch exported backups only
    - Add validation to reject SQL files without CC Switch export header
    - Remove redundant sanitize_import_sql (sqlite_* objects already excluded at export time)
    - Fix backup filename collision by appending counter suffix
    - Update i18n hints to clarify import restriction
  • Fix/about section UI (#419)
    * fix(ui): improve AboutSection styling and version detection
    
    - Add framer-motion animations for smooth page transitions
    - Unify button sizes and add icons for consistency
    - Add gradient backgrounds and hover effects to cards
    - Add notInstalled i18n translations (zh/en/ja)
    - Fix version detection when stdout/stderr is empty
    
    * fix(proxy): persist per-app takeover state across app restarts
    
    - Fix proxy toggle color to reflect current app's takeover state only
    - Restore proxy service on startup if Live config is still in takeover state
    - Preserve per-app backup records instead of clearing all on restart
    - Only recover Live config when proxy service fails to start
  • chore: rename version to 3.9.0-1 for MSI compatibility
    MSI installer requires numeric-only pre-release identifiers.
    Changed from 3.9.0-beta.1 to 3.9.0-1.
  • fix: import RunEvent for all platforms
    The #[cfg(target_os = "macos")] restriction was a historical artifact
    from when RunEvent was only used for macOS-specific events (Reopen, Opened).
    After c9ea13a added ExitRequested handling for all platforms, the import
    should have been updated but was overlooked.
  • chore: bump version to 3.9.0-beta.1
    - Update version in package.json, Cargo.toml, tauri.conf.json
    - Add CHANGELOG entry for v3.9.0-beta.1 with:
      - Local Proxy Server feature
      - Auto Failover with circuit breaker
      - Skills multi-app support
      - Provider icon colors
      - 25+ bug fixes
    - Add proxy feature guide documentation (Chinese)
  • fix(mcp): skip sync when target CLI app is not installed
    Add guard functions to check if Claude/Codex/Gemini CLI has been
    initialized before attempting to sync MCP configurations. This prevents
    creating unwanted config files in directories that don't exist.
    
    - Claude: check ~/.claude dir OR ~/.claude.json file exists
    - Codex: check ~/.codex dir exists
    - Gemini: check ~/.gemini dir exists
    
    When the target app is not installed, sync operations now silently
    succeed without writing any files, allowing users to manage MCP servers
    for apps they actually use without side effects on others.
  • fix(mcp): improve upsert and import robustness
    - Remove server from live config when app is disabled during upsert
    - Merge enabled flags instead of overwriting when importing from multiple apps
    - Normalize Gemini MCP type field (url-only → sse, command → stdio)
    - Use atomic write for Codex config updates
    - Add tests for disable-removal, multi-app merge, and Gemini SSE import
  • feat(proxy): implement per-app takeover mode
    Replace global live takeover with granular per-app control:
    - Add start_proxy_server command (start without takeover)
    - Add get_proxy_takeover_status to query each app's state
    - Add set_proxy_takeover_for_app for individual app control
    - Use live backup existence as SSOT for takeover state
    - Refactor sync_live_to_provider to eliminate code duplication
    - Update ProxyToggle to show status per active app
  • fix(proxy): takeover Codex base_url via model_provider
    - Update Codex `model_providers.<model_provider>.base_url` to the proxy origin with `/v1`
    - Add route fallbacks for `/responses` and `/chat/completions` (plus double-`/v1` safeguard)
    - Add unit tests for the TOML base_url takeover logic
  • fix(proxy): harden crash recovery with fallback detection
    - Set takeover flag before writing proxy config to fix race condition
      where crash during takeover left Live configs corrupted but flag unset
    - Add fallback detection by checking for placeholder tokens in Live
      configs when backups exist but flag is false (handles legacy/edge cases)
    - Improve error handling with proper rollback at each stage of startup
    - Clean up stale backups when Live configs are not in takeover state
      to avoid long-term storage of sensitive tokens
  • fix(proxy): sync UI when active provider differs from current setting
    Previously, UI sync was triggered only when failover happened (retry count > 1).
    This missed cases where the first provider in the failover queue succeeded but
    was different from the user's selected provider in settings.
    
    Now we capture the current provider ID at request start and compare it with
    the actually used provider. This ensures UI/tray always reflects the real
    provider handling requests.
  • fix(proxy): resolve circuit breaker race condition and error classification
    This commit addresses two critical issues in the proxy failover logic:
    
    1. Circuit Breaker HalfOpen Concurrency Bug:
       - Introduced `AllowResult` struct to track half-open permit usage
       - Added state guard in `transition_to_half_open()` to prevent duplicate resets
       - Replaced `fetch_sub` with CAS loop in `release_half_open_permit()` to prevent underflow
       - Separated `is_available()` (routing) from `allow_request()` (permit acquisition)
    
    2. Error Classification Conflation:
       - Split retry logic into `should_retry_same_provider()` and `categorize_proxy_error()`
       - Same-provider retry: only for transient errors (timeout, 429, 5xx)
       - Cross-provider failover: now includes ConfigError, TransformError, AuthError
       - 4xx errors (401/403) no longer waste retries on the same provider
  • fix(proxy): stabilize live takeover and provider editing
    - Skip live writes when takeover is active and proxy is running
    - Refresh live backups from provider edits during takeover
    - Sync live tokens to DB without clobbering real keys with placeholders
    - Avoid injecting extra placeholder keys into Claude live env
    - Reapply takeover after proxy listen address/port changes
    - In takeover mode, edit dialog uses DB config and keeps API key state in sync
  • Feature/error request logging (#401)
    * feat(proxy): add error mapper for HTTP status code mapping
    
    - Add error_mapper.rs module to map ProxyError to HTTP status codes
    - Implement map_proxy_error_to_status() for error classification
    - Implement get_error_message() for user-friendly error messages
    - Support all error types: upstream, timeout, connection, provider failures
    - Include comprehensive unit tests for all mappings
    
    * feat(proxy): enhance error logging with context support
    
    - Add log_error_with_context() method for detailed error recording
    - Support streaming flag, session_id, and provider_type fields
    - Remove dead_code warning from log_error() method
    - Enable comprehensive error request tracking in database
    
    * feat(proxy): implement error capture and logging in all handlers
    
    - Capture and log all failed requests in handle_messages (Claude)
    - Capture and log all failed requests in handle_gemini (Gemini)
    - Capture and log all failed requests in handle_responses (Codex)
    - Capture and log all failed requests in handle_chat_completions (Codex)
    - Record error status codes, messages, and latency for all failures
    - Generate unique session_id for each request
    - Support both streaming and non-streaming error scenarios
    
    * style: fix clippy warnings and typescript errors
    
    - Add allow(dead_code) for CircuitBreaker::get_state (reserved for future)
    - Fix all uninlined format string warnings (27 instances)
    - Use inline format syntax for better readability
    - Fix unused import and parameter warnings in ProviderActions.tsx
    - Achieve zero warnings in both Rust and TypeScript
    
    * style: apply code formatting
    
    - Remove trailing whitespace in misc.rs
    - Add trailing comma in App.tsx
    - Format multi-line className in ProviderCard.tsx
    
    * feat(proxy): add settings button to proxy panel
    
    Add configuration buttons in both running and stopped states to
    provide easy access to proxy settings dialog.
    
    * fix(speedtest): skip client build for invalid inputs
    
    * chore(clippy): fix uninlined format args
    
    * Merge branch 'main' into feature/error-request-logging
  • refactor(proxy): remove is_proxy_target in favor of failover_queue
    - Remove `is_proxy_target` field from Provider struct (Rust & TypeScript)
    - Remove related DAO methods: get_proxy_target_provider, set_proxy_target
    - Remove deprecated Tauri commands: get_proxy_targets, set_proxy_target
    - Add `is_available()` method to CircuitBreaker for availability checks
      without consuming HalfOpen probe permits (used in select_providers)
    - Keep `allow_request()` for actual request gating with permit tracking
    - Update stream_check to use failover_queue instead of is_proxy_target
    - Clean up commented-out reset circuit breaker button in ProviderActions
    - Remove unused useProxyTargets and useSetProxyTarget hooks
  • fix(proxy): reset health badges when proxy stops
    Clear all provider_health records when stopping the proxy server,
    ensuring health badges reset to "healthy" state. This fixes the
    inconsistency where circuit breakers (in memory) would reset on
    stop but health badges (in database) would retain stale state.
  • fix(proxy): retry failover for all HTTP errors including 4xx
    Previously, only 429, 408, and 5xx errors triggered failover to the next
    provider. Other 4xx errors (like 400) were considered non-retryable and
    caused immediate disconnection.
    
    This was problematic because different providers have different restrictions
    (e.g., "Do not use this API outside Claude Code CLI"), and a 400 error from
    one provider doesn't mean other providers will fail.
    
    Now all upstream HTTP errors trigger failover, allowing the system to try
    all configured providers before giving up.
  • feat(proxy): sync UI when failover succeeds
    Add FailoverSwitchManager to handle provider switching after successful
    failover. This ensures the UI reflects the actual provider in use:
    
    - Create failover_switch.rs with deduplication and async switching logic
    - Pass AppHandle through ProxyService -> ProxyServer -> RequestForwarder
    - Update is_current in database when failover succeeds
    - Emit provider-switched event for frontend refresh
    - Update tray menu and live backup synchronously
    
    The switching runs asynchronously via tokio::spawn to avoid blocking
    API responses while still providing immediate UI feedback.
  • fix(usage): add fallback to provider config for usage credentials (#360)
    - Make usage script credential fields optional with provider config fallback
    - Optimize multi-plan card display: show plan count by default, expandable for details
    - Add hint text to explain credential fallback mechanism
  • feat(deeplink): 深链支持用量查询配置 (#400)
    ## 新增功能
    - 深链导入支持用量查询配置参数:
      - `usageEnabled`: 是否启用用量查询
      - `usageScript`: Base64 编码的用量查询脚本
      - `usageApiKey`: 用量查询专用 API Key
      - `usageBaseUrl`: 用量查询专用 Base URL
      - `usageAccessToken`: 访问令牌(NewAPI 模板)
      - `usageUserId`: 用户 ID(NewAPI 模板)
      - `usageAutoInterval`: 自动查询间隔(分钟)
    
    ## 修改文件
    - **mod.rs**: DeepLinkImportRequest 结构体添加用量查询字段
    - **parser.rs**: 解析 URL 中的用量查询参数
    - **provider.rs**: 构建 ProviderMeta 包含 UsageScript 配置
    - **deeplink.ts**: 添加 TypeScript 类型定义
    - **DeepLinkImportDialog.tsx**: 确认对话框显示用量查询配置
    
    ## Bug 修复
    - **formatters.ts**: 修复 formatJSON() 格式化时删除 "env" 键的问题
    
    ## 深链格式示例
    ```
    ccswitch://v1/import?resource=provider&app=claude&name=xxx&usageEnabled=true&usageScript={base64}&usageAutoInterval=30
    ```
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
  • fix(proxy): resolve HalfOpen counter underflow and config field inconsistencies
    - Fix HalfOpen counter underflow: increment half_open_requests when
      transitioning from Open to HalfOpen to prevent underflow in
      record_success/record_failure
    
    - Fix Gemini config field names: unify to GEMINI_API_KEY and
      GOOGLE_GEMINI_BASE_URL (removed GOOGLE_API_KEY and GEMINI_API_BASE)
    
    - Fix Codex proxy takeover: write base_url to config.toml instead of
      OPENAI_BASE_URL in auth.json (Codex CLI reads from config.toml)
  • fix(proxy): resolve circuit breaker state persistence and HalfOpen deadlock
    This commit addresses several critical issues in the failover system:
    
    **Circuit breaker state persistence (previous fix)**
    - Promote ProviderRouter to ProxyState for cross-request state sharing
    - Remove redundant router.rs module
    - Fix 429 errors to be retryable (rate limiting should try other providers)
    
    **Hot-update circuit breaker config**
    - Add update_circuit_breaker_configs() to ProxyServer and ProxyService
    - Connect update_circuit_breaker_config command to running circuit breakers
    - Add reset_provider_circuit_breaker() for manual breaker reset
    
    **Fix HalfOpen deadlock bug**
    - Change half_open_requests from cumulative count to in-flight count
    - Release quota in record_success()/record_failure() when in HalfOpen state
    - Prevents permanent deadlock when success_threshold > 1
    
    **Fix duplicate select_providers() call**
    - Store providers list in RequestContext, pass to forward_with_retry()
    - Avoid consuming HalfOpen quota twice per request
    - Single call to select_providers() per request lifecycle
    
    **Add per-provider retry with exponential backoff**
    - Implement forward_with_provider_retry() with configurable max_retries
    - Backoff delays: 100ms, 200ms, 400ms, etc.
  • feat(proxy): implement independent failover queue management
    Add a new failover queue system that operates independently from provider
    sortIndex, allowing users to configure failover order per app type.
    
    Backend changes:
    - Add failover_queue table to schema.rs for persistent storage
    - Create dao/failover.rs with CRUD operations for queue management
    - Add Tauri commands for queue operations (get, add, remove, reorder, toggle)
    - Refactor provider_router.rs select_providers() to use failover queue:
      - Current provider always takes first priority
      - Queue providers ordered by queue_order as fallback
      - Only providers with open circuit breakers are included
    
    Frontend changes:
    - Add FailoverQueueItem type to proxy.ts
    - Extend failover.ts API with queue management methods
    - Add React Query hooks for queue data fetching and mutations
    - Create FailoverQueueManager component with drag-and-drop reordering
    - Integrate queue management into SettingsPage under "Auto Failover"
    - Add i18n translations for zh and en locales
  • fix(proxy): auto-recover live config after abnormal exit
    When the app crashes or is force-killed while proxy mode is active,
    the live config files remain pointing to the dead proxy server with
    placeholder tokens, causing CLI tools to fail.
    
    This change adds startup detection:
    - Check `live_takeover_active` flag on app launch
    - If flag is true but proxy is not running → abnormal exit detected
    - Automatically restore live configs from database backup
    - Clear takeover flag and delete backups
    
    The recovery runs before auto-start, ensuring correct sequence even
    when proxy auto-start is enabled.
  • refactor(proxy): modularize handlers.rs to reduce code duplication
    Extract common request handling logic into dedicated modules:
    - handler_config.rs: Usage parser configurations for each API type
    - handler_context.rs: Request lifecycle context management
    - response_processor.rs: Unified streaming/non-streaming response handling
    
    Reduces handlers.rs from ~1130 lines to ~418 lines (-63%), eliminating
    repeated initialization and response processing patterns across the
    four API handlers (Claude, Codex Chat, Codex Responses, Gemini).
  • fix(proxy): update live backup when hot-switching provider in proxy mode
    When proxy is active, switching providers only updated the database flags
    but not the live backup. This caused the wrong provider config to be
    restored when stopping the proxy.
    
    Added `update_live_backup_from_provider()` method to ProxyService that
    generates backup from provider's settings_config instead of reading from
    live files (which are already taken over by proxy).
  • fix(proxy): wait for server shutdown before exiting app
    The previous cleanup logic only sent a shutdown signal but didn't wait
    for the proxy server to actually stop. This caused a race condition
    where the app would exit before cleanup completed, leaving Live configs
    in an inconsistent state.
    
    Changes:
    - Add `server_handle` field to ProxyServer to track the spawned task
    - Modify `stop()` to wait for server task completion (5s timeout)
    - Add 100ms delay before process exit to ensure I/O flush
    - Export ProxyService and fix test files that were missing proxy_service field