Commit Graph

355 Commits

  • fix(proxy): harden error handling and input validation
    - Handle RwLock poisoning in settings.rs with unwrap_or_else
    - Add fallback for dirs::home_dir() in config modules
    - Normalize localhost to 127.0.0.1 in ProxyPanel
    - Format IPv6 addresses with brackets for valid URLs
    - Strict port validation with pure digit regex
    - Treat NaN as validation failure in config panels
    - Log warning on cost_multiplier parse failure
    - Align timeoutSeconds range to [0, 300] across all panels
  • fix(live): sync skills to app directories on config path change
    When users change app config directories (claudeConfigDir, codexConfigDir,
    geminiConfigDir), MCP servers were being synced to the new paths but Skills
    were not. This adds Skill synchronization to sync_current_to_live() to ensure
    installed Skills are also copied to the new app directories.
  • fix(usage): prevent usage script config from leaking between providers
    Add key prop to UsageScriptModal to ensure component remounts when
    switching between different providers. This fixes issue #569 where
    configuring usage query for one provider would incorrectly apply
    the same configuration to all providers.
    
    The root cause was that useState initialization only runs on first
    mount, and due to useLastValidValue hook keeping the modal rendered
    during close animation, the component might not fully unmount when
    switching providers rapidly.
    
    Closes #569
  • refactor(proxy): disable OpenRouter compat mode by default and hide UI toggle
    OpenRouter now natively supports Claude Code compatible API (/v1/messages),
    so format transformation (Anthropic ↔ OpenAI) is no longer needed by default.
    
    - Change default value from `true` to `false` in both frontend and backend
    - Hide the "OpenRouter Compatibility Mode" toggle in provider form
    - Users can still enable it manually by adding `"openrouter_compat_mode": true` in config JSON
    - Update unit tests to reflect new default behavior
  • fix(proxy): improve validation and error handling in proxy config panels
    - Add StopTimeout/StopFailed error types for proper stop() error reporting
    - Replace silent clamp with validation-and-block in config panels
    - Add listenAddress format validation in ProxyPanel
    - Use log_codes constants instead of hardcoded strings
    - Use once_cell::Lazy for regex precompilation
  • fix(gemini): convert timeout params to Gemini CLI format (#580)
    Claude Code/Codex uses startup_timeout_sec and tool_timeout_sec,
    but Gemini CLI only supports a single timeout param in milliseconds.
    
    - Collect startup and tool timeout separately with defaults
      - startup_timeout_sec default: 10s
      - tool_timeout_sec default: 60s
    - Take max of both values as final timeout
    - Support both sec and ms variants
    - Remove original fields and insert Gemini-compatible timeout
  • feat(pricing): support @ separator in model name matching
    - Refactor model name cleaning into chained method calls
    - Add @ to - replacement (e.g., gpt-5.2-codex@low → gpt-5.2-codex-low)
    - Add test case for @ separator matching
  • feat(proxy): add structured log codes for i18n support
    Add error code system to proxy module logs for multi-language support:
    
    - CB-001~006: Circuit breaker state transitions and triggers
    - SRV-001~004: Proxy server lifecycle events
    - FWD-001~002: Request forwarding and failover
    - FO-001~005: Failover switch operations
    - USG-001~002: Usage logging errors
    
    Log format: [CODE] Chinese message
    Frontend/log tools can map codes to any language.
    
    New file: src/proxy/log_codes.rs - centralized code definitions
  • refactor(proxy): simplify verbose logging output
    - Remove response JSON full output logging in response_processor
    - Remove per-request INFO logs in provider_router (failover status, provider selection)
    - Change model mapping log from INFO to DEBUG
    - Change usage logging failure from INFO to WARN
    - Remove redundant debug logs for circuit breaker operations
    
    Reduces log noise significantly while preserving important warnings and errors.
  • fix: replace unsafe unwrap() calls with proper error handling
    - database/dao/mcp.rs: Use map_err for serde_json serialization
    - database/dao/providers.rs: Use map_err for settings_config and meta serialization
    - commands/misc.rs: Use expect() for compile-time regex pattern
    - services/prompt.rs: Use unwrap_or_default() for SystemTime
    - deeplink/provider.rs: Replace unwrap() with is_none_or pattern for Option checks
    
    Reduces potential panic points from 26 to 1 (static regex init, safe).
  • refactor(proxy): simplify logging for better readability
    - Delete 17 verbose debug logs from handlers, streaming, and response_processor
    - Convert excessive INFO logs to DEBUG level for internal processing details
    - Add 2 critical INFO logs in forwarder.rs for failover scenarios:
      - Log when switching to next provider after failure
      - Log when all providers have been exhausted
    - Fix clippy uninlined_format_args warning
    
    This reduces log noise while maintaining visibility into key user-facing decisions.
  • chore: bump version to v3.9.1
    - Update version in package.json, tauri.conf.json, Cargo.toml
    - Update version badges and current version in README files
    - Add v3.9.1 changelog entry with bug fixes and improvements
  • fix(windows): correct window title and remove extra titlebar spacing
    - Add missing "title" field to tauri.windows.conf.json to display
      "CC Switch" instead of default "Tauri app"
    - Make DRAG_BAR_HEIGHT platform-aware: 0px on Windows/Linux (native
      titlebar), 28px on macOS (Overlay mode needs traffic light space)
    - Apply same fix to FullScreenPanel component for consistency
    
    Fixes the issue where Windows showed wrong title and had ~28px extra
    blank space below the native titlebar introduced in v3.9.0.
  • feat(logging): add crash logging and improve log management (#562)
    * feat(logging): add crash logging and improve log management
    
    - Add panic hook to capture crash info to ~/.cc-switch/crash.log
      - Records timestamp, app version, OS/arch, thread info
      - Full stack trace with force_capture for release builds
      - Safe error handling (no nested panics)
    
    - Enable logging for both Debug and Release builds
      - Info level for all builds
      - Output to console and ~/.cc-switch/logs/
      - 5MB max file size with rotation
    
    - Add log cleanup on startup
      - Keep only 2 most recent log files
      - Works on all platforms
    
    - Change panic strategy from "abort" to "unwind"
      - Required for backtrace capture in release builds
    
    * fix(logging): use OnceLock for config dir and add URL redaction
    
    - Use OnceLock to support custom config directory override for crash.log
    - Add redact_url_for_log() to protect sensitive URL parameters in logs
    - Change verbose deep link logs from info to debug level
    - Move Store refresh before panic_hook init to ensure correct path
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • Fix/Resolve panic issues in proxy-related code (#560)
    * fix(proxy): change default port from 5000 to 15721
    
    Port 5000 conflicts with AirPlay Receiver on macOS 12+.
    Also adds error handling for proxy toggle and i18n placeholder updates.
    
    * fix(proxy): replace unwrap/expect with graceful error handling
    
    - Handle HTTP client initialization failure with no_proxy fallback
    - Fix potential panic on Unicode slicing in API key preview
    - Add proper error handling for response body builder
    - Handle edge case where SystemTime is before UNIX_EPOCH
    
    * fix(proxy): handle UTF-8 char boundary when truncating request body log
    
    Rust strings are UTF-8 encoded, slicing at a fixed byte index may cut
    in the middle of a multi-byte character (e.g., Chinese, emoji), causing
    a panic. Use is_char_boundary() to find the nearest safe cut point.
    
    * fix(proxy): improve robustness and prevent panics
    
    - Add reqwest socks feature to support SOCKS proxy environments
    - Fix UTF-8 safety in masked_key/masked_access_token (use chars() instead of byte slicing)
    - Fix UTF-8 boundary check in usage_script HTTP response truncation
    - Add defensive checks for JSON operations in proxy service
    - Remove verbose debug logs that could trigger panic-prone code paths
  • chore(release): prepare v3.9.0 stable release
    - Bump version from 3.9.0-3 to 3.9.0 across all config files
    - Add comprehensive release notes in English, Chinese, and Japanese
    - Update CHANGELOG with v3.9.0 stable and v3.9.0-2 entries
    - Update README badges and release note links to v3.9.0
  • Feat/proxy header improvements (#538)
    * fix(proxy): improve header handling for Claude API compatibility
    
    - Streamline header blacklist by removing overly aggressive filtering
      (browser-specific headers like sec-fetch-*, accept-language)
    - Ensure anthropic-beta header always includes 'claude-code-20250219'
      marker required by upstream services for request validation
    - Centralize anthropic-version header handling in forwarder to prevent
      duplicate headers across different auth strategies
    - Add ?beta=true query parameter to /v1/messages endpoint for
      compatibility with certain upstream services (e.g., DuckCoding)
    - Remove redundant anthropic-version from ClaudeAdapter auth headers
      as it's now managed exclusively by the forwarder
    
    This improves proxy reliability with various Claude API endpoints
    and third-party services that have specific header requirements.
    
    * style(services): use inline format arguments in format strings
    
    Apply Rust 1.58+ format string syntax across provider and skill
    services. This replaces format!("msg {}", var) with format!("msg {var}")
    for improved readability and consistency with modern Rust idioms.
    
    Changed files:
    - services/provider/mod.rs: 1 format string
    - services/skill.rs: 10 format strings (error messages, log statements)
    
    No functional changes, purely stylistic improvement.
    
    * fix(proxy): restrict Anthropic headers to Claude adapter only
    
    - Move anthropic-beta and anthropic-version header handling inside
      Claude-specific condition to avoid sending unnecessary headers
      to Codex and Gemini APIs
    - Update test cases to reflect ?beta=true query parameter behavior
    - Add edge case tests for non-messages endpoints and existing queries
  • fix(database): show error dialog on initialization failure with retry option
    - Add user-friendly dialog when database init or schema migration fails
    - Support retry mechanism instead of crashing the app
    - Include bilingual (Chinese/English) error messages with troubleshooting tips
    - Add comprehensive test for v3.8 → current schema migration path
  • chore(proxy): remove unused body filter helper
    Remove unused `filter_recursive` function from body_filter.rs to fix
    `dead-code` warning from `cargo clippy -- -D warnings`.
  • fix(proxy): clean up model override env vars when switching providers in takeover mode
    When proxy takeover is enabled, switching providers no longer writes to
    the Live config. However, if model override fields (ANTHROPIC_MODEL,
    ANTHROPIC_REASONING_MODEL, etc.) remain in the Live config, Claude Code
    continues sending requests with the old model name, causing failures
    when the new provider doesn't support that model.
    
    This fix:
    - Removes model override env keys from Claude Live config during takeover
    - Adds cleanup when switching providers in takeover mode
    - Fixes has_mapping() to include reasoning_model in the check
    - Adds test coverage for reasoning-only model mapping scenarios
  • fix(codex): remove entire model_providers table from common config extraction
    Previously only removed base_url from model_providers.* tables. Now removes
    the entire model_providers section since all its fields (name, base_url,
    wire_api, requires_openai_auth) are provider-specific configuration.
    
    MCP servers configuration remains preserved as it's provider-agnostic.
  • refactor(common-config): extract snippet from editor content instead of active provider
    - Change extraction source from current active provider to editor's live content
    - Add i18n support for JSON parse error messages via invalid_json_format_error()
    - Simplify API by removing unused providerId parameter
    - Update button labels and error messages in zh/en/ja locales
  • fix(codex): prevent extract_common_config from removing MCP servers' base_url
    - Replace regex patterns with toml_edit for precise field removal
    - Only remove top-level model/model_provider/base_url fields
    - Only remove base_url from [model_providers.*] tables
    - Add regression test to ensure [mcp_servers.*] base_url is preserved
  • feat(common-config): add extract from current provider functionality
    - Add backend command to extract common config snippet from current provider
    - Automatically extract common config on first run after importing default provider
    - Auto-enable common config checkbox in new provider mode when snippet exists
    - Refactor Gemini common config to operate on .env instead of config.json
    - Add "Extract from Current" button to all three common config modals
    - Update i18n translations for new extraction feature
  • feat(proxy): update failover timeout and circuit breaker defaults (#521)
    - Double all timeout values (streaming/non-streaming)
    - Codex/Gemini: circuit_failure_threshold 5→4, error_rate 0.5→0.6
    - Claude: circuit_error_rate_threshold 0.6→0.7
  • fix(skills): skip hidden directories when scanning for skills
    Filter out directories starting with '.' (e.g., .system) during skill
    scanning to avoid exposing internal system directories from Codex.
  • feat(mcp): add import button to import MCP servers from apps
    - Add import_mcp_from_apps command that reuses existing import logic
    - Add Import button in MCP panel header (consistent with Skills)
    - Fix import count to only return truly new servers (not already in DB)
    - Update translations for import success/no-import messages (zh/en/ja)
  • feat(skills): unified management architecture with SSOT and React Query
    - Introduce SSOT (Single Source of Truth) at ~/.cc-switch/skills/
    - Add three-app toggle support (Claude/Codex/Gemini) for each skill
    - Refactor frontend to use TanStack Query hooks instead of manual state
    - Add UnifiedSkillsPanel for managing installed skills with app toggles
    - Add useSkills.ts with declarative data fetching hooks
    - Extend skills.ts API with unified install/uninstall/toggle methods
    - Support importing unmanaged skills from app directories
    - Add v2→v3 database migration for new skills table structure
  • Feat/usage improvements (#508)
    * i18n: update cache terminology across all languages
    
    - Change 'Cache Read' to 'Cache Hit' in all languages
    - Change 'Cache Write' to 'Cache Creation' in all languages
    - Update zh: 缓存读取 → 缓存命中, 缓存写入 → 缓存创建
    - Update en: Cache Read → Cache Hit, Cache Write → Cache Creation
    - Update ja: キャッシュ読取 → キャッシュヒット, キャッシュ書込 → キャッシュ作成
    
    Affected keys: cacheReadTokens, cacheCreationTokens, cacheReadCost,
    cacheWriteCost, cacheRead, cacheWrite
    
    * feat(usage): add cache metrics to trend chart
    
    - Add cache creation tokens visualization (orange line)
    - Add cache hit tokens visualization (purple line)
    - Add gradient definitions for new cache metrics
    - Include cache data in hourly aggregation
    - Display cache metrics alongside input/output tokens
    
    This provides better visibility into cache usage patterns over time.
    
    * fix(usage): fix timezone handling in datetime picker
    
    - Add timestampToLocalDatetime() to convert Unix timestamp to local datetime
    - Add localDatetimeToTimestamp() with validation for incomplete input
    - Fix issue where typing hours/minutes would jump to previous day
    - Validate datetime format completeness before conversion
    - Use local timezone instead of UTC for datetime-local input
    
    This resolves the issue where users couldn't fine-tune time selection
    and the input would jump unexpectedly when editing hours or minutes.
    
    * feat(usage): add auto-refresh for usage statistics
    
    - Add 30-second auto-refresh interval for all usage queries
    - Disable background refresh to save resources
    - Apply to: summary, trends, provider stats, model stats, request logs
    - Queries automatically update when tab is active
    - Pause refresh when user switches to another tab
    
    This keeps usage data fresh without manual refresh.
    
    * fix(proxy): improve usage logging and cache token parsing
    
    - Log requests even when usage parsing fails (with default values)
    - Add detailed debug logging for usage metrics
    - Support cache_read_input_tokens field in Codex responses
    - Fallback to input_tokens_details.cached_tokens if needed
    - Add test case for cached_tokens in input_tokens_details
    - Ensure all requests are tracked in database for analytics
    
    This fixes missing request logs when API responses lack usage data
    and improves cache token detection across different response formats.
    
    * style(rust): use inline format args in format! macros
    
    - Replace format!("...", var) with format!("...{var}")
    - Update universal provider ID formatting
    - Update error message formatting
    - Update config.toml generation in Codex provider
    
    Fixes clippy::uninlined_format_args warnings.
    
    * feat(proxy): enhance provider router logging
    
    - Add debug logs for failover queue provider count
    - Log circuit breaker state for each provider check
    - Add logs for missing current provider scenarios
    - Log when no current provider is configured
    - Use inline format args for better readability
    
    This improves debugging of provider selection and failover behavior.
    
    * feat(database): update model pricing data
    
    - Update Claude models to full version format (e.g. claude-opus-4-5-20251101)
    - Add GPT-5.2 series model pricing (10 models)
    - Add GPT-5.1 series model pricing (10 models)
    - Add GPT-5 series model pricing (12 models)
    - Add Gemini 3 series model pricing (2 models)
    - Update Gemini 2.5 series model ID format (use dot separator)
    - Unify display names by removing thinking level suffixes
    
    * fix(usage): correct Gemini output token calculation
    
    Fix Gemini API output token parsing to use totalTokenCount - promptTokenCount
    instead of candidatesTokenCount alone. This ensures thoughtsTokenCount is
    included in output statistics.
    
    - Update from_gemini_response to calculate output from total - input
    - Update from_gemini_stream_chunks with same logic for consistency
    - Fix from_codex_stream_events to use adjusted token calculation
    - Add test case for responses with thoughtsTokenCount
    - Update existing tests to match new calculation logic
    
    * fix(usage): correct cache token billing and add Codex format auto-detection
    
    - Avoid double-billing cache tokens by subtracting from input before calculation
    - Add smart Codex parser that auto-detects OpenAI vs Codex API format
    - Extract model name from Codex responses for accurate tracking
    
    * fix(proxy): improve takeover detection with live config check
    
    - Add live config takeover detection for hot-switch decision
    - Rebuild takeover when backup is missing or placeholder remains
    - Make detect_takeover_in_live_config_for_app public
    - Fix is_takeover_active to use actual takeover status
    
    * refactor(usage): simplify model pricing lookup by removing suffix fallback
    
    Replace complex suffix-stripping fallback with direct prefix/suffix cleanup.
    Model IDs are now cleaned by removing vendor prefix (before /) and colon
    suffix (after :), then matched exactly against pricing table.
    
    * feat(database): add Chinese AI model pricing data
    
    Add pricing for domestic AI models (CNY/1M tokens):
    - Doubao-Seed-Code (ByteDance)
    - DeepSeek V3/V3.1/V3.2
    - Kimi K2/K2-Thinking/K2-Turbo (Moonshot)
    - MiniMax M2/M2.1/M2.1-Lightning
    - GLM-4.6/4.7 (Zhipu)
    - Mimo V2 Flash (Xiaomi)
    
    Also fix test case to use correct model ID and remove invalid currency column.
    
    * refactor(proxy): improve header forwarding with blacklist approach
    
    Change from whitelist to blacklist mode for request header forwarding.
    Only skip headers that will be overridden (auth, host, content-length).
    This preserves client's original headers and improves compatibility.
    
    * fix(proxy): bypass timeout and retry configs when failover is disabled
    
    When auto_failover_enabled is false, timeout and retry configurations
    should not affect normal request flow. This change ensures:
    
    - create_forwarder: passes 0 for all timeout/retry params when failover
      is disabled, effectively bypassing these checks
    - streaming_timeout_config: returns 0 for both first_byte_timeout and
      idle_timeout when failover is disabled
    
    This prevents unnecessary timeout errors and retry attempts when users
    have explicitly disabled the failover feature.
    
    * fix(proxy): handle zero value input in failover config fields
    
    * refactor(proxy): remove retry logic and add enabled check for failover
    
    * refactor(proxy): distinguish circuit-open from no-provider errors
    
    * Align usage stats to sliding windows
    
    * feat(proxy): add body and header filtering for upstream requests
    
    * feat(proxy): enable transparent passthrough for headers
    
    - Passthrough anthropic-beta header as-is from client
    - Passthrough anthropic-version header from client
    - Passthrough client IP headers (x-forwarded-for, x-real-ip) by default
    - Filter private params (underscore-prefixed fields) from request body
    - No database changes required
    
    * feat(proxy): extract session ID from client requests for logging
    
    - Add SessionIdExtractor to parse session ID from Claude/Codex requests
    - Support extraction from metadata.user_id, headers, previous_response_id
    - Pass session_id through RequestContext to usage logger
    - Enable request correlation by session in proxy_request_logs
  • fix: resolve test failures and clippy warnings
    - tests/App.test.tsx: remove outdated SettingsPage mock, use dynamic import
    - database/tests.rs: remove unused field, use struct init syntax
    - deeplink/tests.rs: use idiomatic assert!() instead of assert_eq!(true)
    - support.rs: add #[allow(dead_code)] for test utilities
    - usage_stats.rs: code formatting
  • fix: use local timezone and robust DST handling in usage stats (#500)
    - Change from UTC to local timezone for daily/hourly trends
    - Use SQLite 'localtime' modifier for date grouping
    - Replace single().unwrap() with earliest().unwrap_or_else()
      to handle DST transition edge cases gracefully
  • fix(windows): wrap npx/npm commands with cmd /c for MCP export
    On Windows, npx, npm, yarn, pnpm, node, bun, and deno are actually
    .cmd batch files that require cmd /c wrapper to execute properly.
    This fixes the Claude Code /doctor warning:
    "Windows requires 'cmd /c' wrapper to execute npx"
    
    The transformation is applied when exporting MCP config to ~/.claude.json:
    - Before: {"command": "npx", "args": ["-y", "foo"]}
    - After:  {"command": "cmd", "args": ["/c", "npx", "-y", "foo"]}
    
    Uses conditional compilation (#[cfg(windows)]) for zero overhead on
    other platforms.
    
    Closes #453
  • fix(windows): prevent terminal windows from appearing during version check
    On Windows, opening the Settings > About section would spawn three
    terminal windows when checking CLI tool versions (claude, codex, gemini).
    
    Root cause:
    - scan_cli_version() directly executed .cmd files, but child processes
      (node.exe) spawned by these batch scripts didn't inherit CREATE_NO_WINDOW
    - PATH separator used Unix-style ":" instead of Windows ";"
    
    Fix:
    - Wrap command execution with `cmd /C` to ensure all child processes
      run within the same hidden console session
    - Use platform-specific PATH separators via conditional compilation
  • fix(schema): add missing base columns migration for proxy_config (#492)
    * fix(schema): add missing base columns migration for proxy_config
    
    Add compatibility migration for older databases that may be missing
    the basic proxy_config columns (proxy_enabled, listen_address,
    listen_port, enable_logging) before adding newer timeout fields.
    
    * fix: add proxy_config base column patches for v3.9.0-2 upgrade
    
    Add base config column patches in create_tables_on_conn():
    - proxy_enabled
    - listen_address
    - listen_port
    - enable_logging
    
    Ensures v3.9.0-2 users (user_version=2 but missing columns)
    can properly upgrade with all required fields added.
    
    * fix: migrate proxy_config singleton to per-app on startup for v2 databases
    
    Add startup migration for legacy proxy_config tables that still have
    singleton structure (no app_type column) even with user_version=2.
    
    This fixes the issue where v3.9.0-2 databases with v2 schema but legacy
    proxy_config structure would fail with "no such column: app_type" error.
    
    - Call migrate_proxy_config_to_per_app in create_tables_on_conn
    - Add regression test to verify the fix
    
    * style: cargo fmt
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.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/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