Commit Graph

379 Commits

  • feat(opencode): add model-level options editor
    Add support for configuring per-model options like provider routing.
    Each model row now has an expand/collapse toggle to show a key-value
    editor for model-specific options (e.g., provider order, fallbacks).
    
    - Add options field to OpenCodeModel in Rust and TypeScript
    - Add expandable key-value editor UI for each model
    - Use local state pattern for option key input to prevent focus loss
    - Add i18n translations for zh/en/ja
  • feat(opencode): add extra options editor for SDK configuration
    Add key-value pair editor for configuring additional SDK options like
    timeout, setCacheKey, etc. Values are automatically parsed to appropriate
    types (number, boolean, object) on save.
    
    - Add `extra` field with serde flatten in Rust backend
    - Add index signature to OpenCodeProviderOptions type
    - Create ExtraOptionKeyInput component with local state pattern
    - Place extra options section above models configuration
  • fix(opencode): use AGENTS.md as prompt filename
    OpenCode follows the same convention as Codex, using AGENTS.md
    instead of OPENCODE.md for the system prompt file.
  • feat(opencode): import providers and MCP on first launch
    Add startup import logic for OpenCode providers and MCP servers.
    Unlike other apps that use replacement mode, OpenCode uses additive
    mode where multiple providers can coexist in the config file.
  • fix(opencode): fix add/remove provider flow and toast messages
    - Create separate removeFromLiveConfig API for additive mode apps
      (remove only removes from live config, not database)
    - Fix useSwitchProviderMutation to invalidate opencodeLiveProviderIds
      cache so button state updates correctly after add operation
    - Show appropriate toast messages:
      - Add: "已添加到配置" / "Added to config"
      - Remove: "已从配置移除" / "Removed from config"
    - Add i18n texts for addToConfigSuccess and removeFromConfigSuccess
  • fix(opencode): remove current provider concept for additive mode
    OpenCode uses additive mode where all providers coexist in config file,
    so there's no "current" provider concept. This commit:
    
    - Skip setting is_current in switch_normal for OpenCode
    - Return empty string from ProviderService::current for OpenCode
    - Disable active provider highlight in ProviderCard for OpenCode
  • fix(opencode): prevent config nesting and use slugified provider IDs
    - Skip backfill logic for OpenCode (additive mode doesn't need it)
    - Add defensive check in write_live_snapshot to extract provider fragment
    - Use slugified name as provider ID for readable config keys
  • fix(opencode): add OpenCode support to skills functionality
    Add missing OpenCode branch in parse_app_type() and include OpenCode
    in all app iteration loops for skills operations (uninstall, scan,
    import, migrate).
  • chore(opencode): remove unused functions and legacy code
    Remove dead code that was never called after v3.7.0 architecture change:
    - mcp/opencode.rs: sync_enabled_to_opencode, collect_enabled_servers
    - opencode_config.rs: 8 unused utility functions
    - provider.rs: OpenCodeProviderConfig impl block (4 methods)
    
    These functions were designed for batch operations but McpService uses
    per-server sync pattern instead. No functionality affected.
  • feat(opencode): implement isInConfig semantics for additive provider management
    OpenCode uses additive provider management where providers can exist in
    the database but not necessarily in the live opencode.json config. This
    commit implements proper isInConfig state:
    
    Backend:
    - Add get_opencode_live_provider_ids command to query live config
    
    Frontend:
    - Add getOpenCodeLiveProviderIds API method
    - ProviderList queries live provider IDs and computes isInConfig
    - ProviderCard receives and passes isInConfig to ProviderActions
    - ProviderActions already handles the add/remove button logic
  • fix(opencode): address issues found during OpenCode integration review
    - Fix MCP server not removed from opencode.json when unchecked in edit modal
    - Fix Windows atomic write failure when opencode.json already exists
    - Fix i18n keys mismatch in OpenCodeFormFields (use opencode.* namespace)
    - Fix unit test missing apps.opencode field assertion
  • feat(opencode): Phase 6 - Tauri command extensions for OpenCode
    - Add import_opencode_providers_from_live command to provider.rs
    - Register new command in lib.rs invoke_handler
    - Update commands/mcp.rs: include OpenCode in sync_other_side logic
    - Add McpService::import_from_opencode to import_mcp_from_apps
    - Implement MCP sync/remove for OpenCode in services/mcp.rs
      - sync_server_to_app_no_config now calls sync_single_server_to_opencode
      - remove_server_from_app now calls remove_server_from_opencode
  • feat(opencode): complete Phase 5 - provider service layer
    Implement OpenCode-specific provider service logic with additive mode:
    - add(): Always write to live config (no is_current check needed)
    - update(): Always sync changes to live config
    - delete(): Remove from both DB and live config (no is_current check)
    
    New helper functions in live.rs:
    - write_live_snapshot(): Write provider to opencode.json provider section
    - remove_opencode_provider_from_live(): Remove provider from live config
    - import_opencode_providers_from_live(): Import existing providers from
      ~/.config/opencode/opencode.json into CC Switch database
    
    Key design: OpenCode uses additive mode where all providers coexist
    in the config file, unlike Claude/Codex/Gemini which use replacement
    mode with a single active provider.
  • feat(opencode): complete Phase 4 - MCP sync module
    Add mcp/opencode.rs with format conversion between CC Switch and OpenCode:
    - stdio ↔ local type conversion
    - command+args ↔ command array format
    - env ↔ environment field mapping
    - sse/http ↔ remote type conversion
    
    Public API:
    - sync_enabled_to_opencode: Batch sync all enabled servers
    - sync_single_server_to_opencode: Sync individual server
    - remove_server_from_opencode: Remove from live config
    - import_from_opencode: Import servers from OpenCode config
    
    Also fix test files to include new opencode field in McpApps struct.
    All 4 unit tests pass for format conversion.
  • feat(opencode): complete Phase 3 - enhanced config read/write module
    Add typed provider functions and utilities to opencode_config.rs:
    - Typed provider operations (get_typed_providers, get_typed_provider,
      set_typed_provider, set_providers_batch)
    - MCP batch operations (set_mcp_servers_batch, clear_mcp_servers)
    - Utility functions (create_provider_config, validate_provider_config,
      provider_to_opencode_config, opencode_config_to_provider)
    - Enhanced module documentation with config file format examples
    
    The typed API layer provides type-safe access to OpenCode provider
    configurations while the untyped layer allows raw JSON operations
    when needed.
  • feat(opencode): Phase 2 - Add OpenCode provider data structures
    Add OpenCode-specific configuration structures for the AI SDK format:
    
    - OpenCodeProviderConfig: Main config with npm package, options, and models
    - OpenCodeProviderOptions: baseURL, apiKey, and headers support
    - OpenCodeModel: Model definition with name and token limits
    - OpenCodeModelLimit: Context and output token limits
    
    Key features:
    - Supports environment variable references (e.g., "{env:API_KEY}")
    - Custom headers support for specialized providers
    - Model-level token limits for context management
    - Helper methods for parsing and validation
  • feat(opencode): Phase 1 - Backend data structure expansion for OpenCode support
    Add OpenCode as the 4th supported application with additive provider management:
    
    - Add OpenCode variant to AppType enum with all related match statements
    - Add enabled_opencode field to McpApps and SkillApps structures
    - Add opencode field to McpRoot and PromptRoot
    - Add database schema migration v3→v4 with enabled_opencode columns
    - Add settings.rs support for opencode_config_dir and current_provider_opencode
    - Create opencode_config.rs module for config file I/O operations
    - Update all services (proxy, mcp, skill, provider, stream_check) for OpenCode
    - Add OpenCode support to deeplink provider and MCP parsing
    - Update commands/config.rs for OpenCode config status and paths
    
    Key design decisions:
    - OpenCode uses additive mode (no is_current needed, no proxy support)
    - Config path: ~/.config/opencode/opencode.json
    - MCP format: stdio→local, sse/http→remote conversion planned
    - Stream check returns error (not yet implemented for OpenCode)
  • feat(usage): improve custom template system with variable hints and validation fixes (#628)
    * feat(usage): improve custom template with variables display and explicit type detection
    
    Combine two feature improvements:
    1. Display supported variables ({{baseUrl}}, {{apiKey}}) with actual values in custom template mode
    2. Add explicit templateType field for accurate template mode detection
    
    ## Changes
    
    ### Frontend
    - Display template variables with actual values extracted from provider settings
    - Add templateType field to UsageScript for explicit mode detection
    - Support template mode persistence across sessions
    
    ### Backend
    - Add template_type field to UsageScript struct
    - Improve validation logic based on explicit template type
    - Maintain backward compatibility with type inference
    
    ### I18n
    - Add "Supported Variables" section translation (zh/en/ja)
    
    ### Benefits
    - More accurate template mode detection (no more guessing)
    - Better user experience with variable hints
    - Clearer validation rules per template type
    
    * fix(usage): resolve custom template cache and validation issues
    
    Combine three bug fixes to make custom template mode work correctly:
    
    1. **Update cache after test**: Testing usage script successfully now updates the main list cache immediately
    2. **Fix same-origin check**: Custom template mode can now access different domains (SSRF protection still active)
    3. **Fix field naming**: Unified to use autoQueryInterval consistently between frontend and backend
    
    ## Problems Solved
    
    - Main provider list showing "Query failed" after successful test
    - Custom templates blocked by overly strict same-origin validation
    - Auto-query intervals not saved correctly due to inconsistent naming
    
    ## Changes
    
    ### Frontend (UsageScriptModal)
    - Import useQueryClient and update cache after successful test
    - Invalidate usage cache when saving script configuration
    - Use standardized autoQueryInterval field name
    
    ### Backend (usage_script.rs)
    - Allow custom template mode to bypass same-origin checks
    - Maintain SSRF protection for all modes
    
    ### Hooks (useProviderActions)
    - Invalidate usage query cache when saving script
    
    ## Impact
    
    Users can now use custom templates freely while security validations remain intact for general templates.
    
    * fix(usage): correct provider credential field names
    
    - Claude: support both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN
    - Gemini: use GEMINI_API_KEY instead of GOOGLE_GEMINI_API_KEY
    - Codex: use OPENAI_API_KEY and parse base_url from TOML config string
    
    Addresses review feedback from PR #628
    
    * style: format code
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • feat(proxy): add thinking signature rectifier for Claude API (#595)
    * feat(proxy): add thinking signature rectifier for Claude API
    
    Add automatic request rectification when Anthropic API returns signature
    validation errors. This improves compatibility when switching between
    different Claude providers or when historical messages contain incompatible
    thinking block signatures.
    
    - Add thinking_rectifier.rs module with trigger detection and rectification
    - Integrate rectifier into forwarder error handling flow
    - Remove thinking/redacted_thinking blocks and signature fields on retry
    - Delete top-level thinking field when assistant message lacks thinking prefix
    
    * fix(proxy): complete rectifier retry path with failover switch and chain continuation
    
    - Add failover switch trigger on rectifier retry success when provider differs from start
    - Replace direct error return with error categorization on rectifier retry failure
    - Continue failover chain for retryable errors instead of terminating early
    
    * feat(proxy): add rectifier config with master switch
    
    - Add RectifierConfig struct with enabled and requestThinkingSignature fields
    - Update should_rectify_thinking_signature to check master switch first
    - Add tests for master switch functionality
    
    * feat(db): add rectifier config storage in settings table
    
    Store rectifier config as JSON in single key for extensibility
    
    * feat(commands): add get/set rectifier config commands
    
    * feat(ui): add rectifier config panel in advanced settings
    
    - Add RectifierConfigPanel component with master switch and thinking signature toggle
    - Add API wrapper for rectifier config
    - Add i18n translations for zh/en/ja
    
    * feat(proxy): integrate rectifier config into request forwarding
    
    - Load rectifier config from database in RequestContext
    - Pass config to RequestForwarder for runtime checking
    - Use should_rectify_thinking_signature with config parameter
    
    * test(proxy): add nested JSON error detection test for thinking rectifier
    
    * fix(proxy): resolve HalfOpen permit leak and RectifierConfig default values
    
    - Fix RectifierConfig::default() to return enabled=true (was false due to derive)
    - Add release_permit_neutral() for releasing permits without affecting health stats
    - Fix 3 permit leak points in rectifier retry branches
    - Add unit tests for default values and permit release
    
    * style(ui): format ProviderCard style attribute
    
    * fix(rectifier): add detection for signature field required error
    
    Add support for detecting "signature: Field required" error pattern
    in the thinking signature rectifier. This enables automatic request
    rectification when upstream API returns this specific validation error.
  • feat(misc): add WSL tool version detection with security hardening (#… (#627)
    * feat(misc): add WSL tool version detection with security hardening (#608)
    
    - Add WSL distro detection from UNC path (wsl$/wsl.localhost)
    - Add distro name validation to prevent command injection
    - Add defensive assertion for tool parameter
    - Unify cfg macros to target_os = "windows"
    - Standardize error message format to [WSL:{distro}]
    
    Closes #608
    
    * fix(misc): add CREATE_NO_WINDOW flag and unify error messages to English
    
    - Add CREATE_NO_WINDOW flag to wsl.exe command to prevent console window flash
    - Standardize error messages from Chinese to English for consistency
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • feat(stream-check): enhance health check with configurable prompt and CLI-compatible requests (#623)
    - Add configurable test prompt field to StreamCheckConfig
    - Implement Claude CLI-compatible request format with proper headers:
      - Authorization + x-api-key dual auth
      - anthropic-beta, anthropic-version headers
      - x-stainless-* SDK headers with dynamic OS/arch detection
      - URL with ?beta=true parameter
    - Implement Codex CLI-compatible Responses API format:
      - /v1/responses endpoint
      - input array format with reasoning effort support
      - codex_cli_rs user-agent and originator headers
    - Add dynamic OS name and CPU architecture detection
    - Internationalize error messages (Chinese -> English)
    - Add test prompt Textarea UI component with i18n support
    - Remove obsolete testPromptDesc translation key
  • Feature/global proxy (#596)
    * 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.
    
    * 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 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.
    
    * 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
    
    * chore: bump version to 3.9.1
    
    * style: format code with prettier and rustfmt
    
    * fix(ui): allow number inputs to be fully cleared before saving
    
    - Convert numeric state to string type for controlled inputs
    - Use isNaN() check instead of || fallback to allow 0 values
    - Apply fix to ProxyPanel, CircuitBreakerConfigPanel,
      AutoFailoverConfigPanel, and ModelTestConfigPanel
    
    * 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 global proxy settings support
    
    Add ability to configure a global HTTP/HTTPS proxy for all outbound
    requests including provider API calls, speed tests, and stream checks.
    
    * 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(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
    
    * feat(proxy): add local proxy auto-scan and fix hot-reload
    
    - Add scan_local_proxies command to detect common proxy ports
    - Fix SkillService not using updated proxy after hot-reload
    - Move global proxy settings to advanced tab
    - Add error handling for scan failures
    
    * fix(proxy): allow localhost input in proxy address field
    
    * fix(proxy): restore request timeout and fix proxy hot-reload issues
    
    - Add URL scheme validation in build_client (http/https/socks5/socks5h)
    - Restore per-request timeout for speedtest, stream_check, usage_script, forwarder
    - Fix set_global_proxy_url to validate before persisting to DB
    - Mask proxy credentials in all log outputs
    - Fix forwarder hot-reload by fetching client on each request
    
    * style: format code with prettier
    
    * fix(proxy): improve global proxy stability and error handling
    
    - Fix RwLock silent failures with explicit error propagation
    - Handle init() duplicate calls gracefully with warning log
    - Align fallback client config with build_client settings
    - Make scan_local_proxies async to avoid UI blocking
    - Add mixed mode support for Clash 7890 port (http+socks5)
    - Use multiple test targets for better proxy connectivity test
    - Clear invalid proxy config on init failure
    - Restore timeout constraints in usage_script
    - Fix mask_url output for URLs without port
    - Add structured error codes [GP-001 to GP-009]
    
    * feat(proxy): add username/password authentication support
    
    - Add separate username and password input fields
    - Implement password visibility toggle with eye icon
    - Add clear button to reset all proxy fields
    - Auto-extract auth info from saved URL and merge on save
    - Update i18n translations (zh/en/ja)
    
    * fix(proxy): fix double encoding issue in proxy auth and add debug logs
    
    - Remove encodeURIComponent in mergeAuth() since URL object's
      username/password setters already do percent-encoding automatically
    - Add GP-010 debug log for database read operations
    - Add GP-011 debug log to track incoming URL info (length, has_auth)
    - Fix username.trim() in fallback branch for consistent behavior
  • fix(provider): persist endpoint auto-select state (#611)
    - Add endpointAutoSelect field to ProviderMeta for persistence
    - Lift autoSelect state from EndpointSpeedTest to ProviderForm
    - Save auto-select preference when provider is saved
    - Restore preference when editing existing provider
    
    Fixes https://github.com/farion1231/cc-switch/issues/589
  • Feat/deeplink multi endpoints (#597)
    * feat(deeplink): support comma-separated multiple endpoints in URL
    
    Allow importing multiple API endpoints via single endpoint parameter.
    First URL becomes primary endpoint, rest are added as custom endpoints.
    
    * feat(deeplink): add usage query fields to deeplink generator
    
    Add form fields for usage query configuration in deeplink HTML generator:
    - usageEnabled, usageBaseUrl, usageApiKey
    - usageScript, usageAutoInterval
    - usageAccessToken, usageUserId
    
    * fix(deeplink): auto-infer homepage and improve multi-endpoint display
    
    - Auto-infer homepage from primary endpoint when not provided
    - Display multiple endpoints as list in import dialog (primary marked)
    - Update deeplink parser in deplink.html to show multi-endpoint info
    - Add test for homepage inference from endpoint
    - Minor log format fix in live.rs
    
    * fix(deeplink): use primary endpoint for usage script base_url
    
    - Fix usage_script.base_url getting comma-separated string when multiple endpoints
    - Add i18n support for primary endpoint label in DeepLinkImportDialog
  • Merge tianrking/main: feat: add provider-specific terminal button
    Merged PR #452 which adds:
    - Terminal button for Claude providers to launch with provider-specific config
    - Cross-platform support (macOS/Linux/Windows)
    - Auto-cleanup of temporary config files
  • fix(deeplink): prioritize GOOGLE_GEMINI_BASE_URL over GEMINI_BASE_URL
    When merging Gemini config from local env file during deeplink import,
    check GOOGLE_GEMINI_BASE_URL first (official variable name) before
    falling back to GEMINI_BASE_URL.
  • fix(mcp): skip cmd /c wrapper for WSL target paths (#592)
    * fix(mcp): skip cmd /c wrapper for WSL target paths
    
    When the Claude config directory is set to a WSL network path
    (e.g., \wsl$\Ubuntu\home\user\.claude), the MCP export should
    not wrap npx/npm commands with cmd /c since WSL runs Linux.
    
    - Add is_wsl_path() to detect \wsl$\ and \wsl.localhost\ paths
    - Skip wrap_command_for_windows() when target is WSL path
    - Add comprehensive tests for various WSL distributions
    
    * chore(mcp): add debug log for WSL path detection
    
    * refactor(mcp): optimize is_wsl_path with next() and rename variable
  • Refactor/simplify proxy logs (#585)
    * 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.
    
    * 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 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.
    
    * 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
    
    * chore: bump version to 3.9.1
    
    * style: format code with prettier and rustfmt
    
    * fix(ui): allow number inputs to be fully cleared before saving
    
    - Convert numeric state to string type for controlled inputs
    - Use isNaN() check instead of || fallback to allow 0 values
    - Apply fix to ProxyPanel, CircuitBreakerConfigPanel,
      AutoFailoverConfigPanel, and ModelTestConfigPanel
    
    * 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
    
    * 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(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(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
  • 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
  • Merge origin/main into main
    Resolved conflict in src-tauri/src/commands/misc.rs by combining imports from both sides.
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    Co-Authored-By: Claude <noreply@anthropic.com>
  • 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