Commit Graph

158 Commits

  • feat: apply common config as runtime overlay instead of materialized merge
    Common config snippets are now dynamically overlaid when writing live
    files, rather than being pre-merged into provider snapshots at edit time.
    This ensures that updating a snippet immediately takes effect for the
    current provider and automatically propagates to other providers on
    their next switch.
    
    Key changes:
    - Add write_live_with_common_config() overlay pipeline
    - Strip common config from live before backfilling provider snapshots
    - Normalize provider snapshots on save to keep them snippet-free
    - Add explicit commonConfigEnabled flag in ProviderMeta (Option<bool>)
    - Migrate legacy providers on snippet save (infer flag from subset check)
    - Add Codex TOML snippet validation in set_common_config_snippet
    - Stabilize onConfigChange callbacks with useCallback in ProviderForm
  • feat: add dual-layer versioning to WebDAV sync (protocol v2 + db-v6)
    Separate protocol version from database compatibility version in WebDAV
    sync paths. Upload writes to v2/db-v6/<profile>, download falls back to
    legacy v2/<profile> when current path has no data. Extend manifest with
    optional dbCompatVersion field and add legacy layout detection to UI.
  • feat: add usage daily rollups, incremental auto-vacuum, and sync-aware backup
    - Add usage_daily_rollups table (schema v6) to aggregate proxy request
      logs into daily summaries, reducing query overhead for statistics
    - Add rollup_and_prune DAO that aggregates old detail logs (>N days)
      into rollup rows and deletes the originals
    - Update all usage stats queries to UNION detail logs with rollup data
    - Introduce incremental auto-vacuum for SQLite, with startup and
      periodic cleanup of old stream_check_logs and request log rollups
    - Split backup export/import into full vs sync variants: WebDAV sync
      now skips local-only table data (proxy_request_logs,
      stream_check_logs, provider_health, proxy_live_backup,
      usage_daily_rollups) while preserving them on import
    - Add enable_logging guard to skip request log writes when disabled
    - Apply cargo fmt formatting fixes across multiple modules
  • fix: support openai_chat api_format in stream check
    Stream Check always used Anthropic Messages API format, causing false
    failures for providers with api_format="openai_chat" (e.g. NVIDIA).
    Now detects api_format from provider meta/settings_config and uses
    the correct endpoint (/v1/chat/completions) and headers accordingly.
  • revert: restore full config overwrite + Common Config Snippet (revert 992dda5c)
    Revert the partial key-field merging refactoring introduced in 992dda5c,
    along with two dependent commits (24fa8a18, 87604b18) that referenced
    the now-removed ClaudeQuickToggles component.
    
    The whitelist-based partial merge approach had critical issues:
    - Non-whitelisted custom fields were lost during provider switching
    - Backfill permanently stripped non-key fields from the database
    - Whitelist required constant maintenance to track upstream changes
    
    This restores the proven "full config overwrite + Common Config Snippet"
    architecture where each provider stores its complete configuration and
    shared settings are managed via a separate snippet mechanism.
    
    Reverted commits:
    - 24fa8a18: context-aware JSON editor hint + hide quick toggles
    - 87604b18: hide ClaudeQuickToggles when creating
    - 992dda5c: partial key-field merging refactoring
    
    Restored:
    - Full config snapshot write (write_live_snapshot) for Claude/Codex/Gemini
    - Full config backfill (settings_config = live_config)
    - Common Config Snippet UI and backend commands
    - 6 frontend components/hooks for common config editing
    - configApi barrel export and DB snippet methods
    
    Removed:
    - ClaudeQuickToggles component
    - write_live_partial / backfill_key_fields / patch_claude_live
    - All KEY_FIELDS constants
  • fix: remove last-provider deletion restriction for OMO/OMO Slim plugins
    OMO and OMO Slim are OpenCode plugins, not standalone apps — users
    should be able to fully remove them. Remove the count-based guard that
    prevented deleting the last active provider, and clean up the now-unused
    provider-count API surface across the full stack.
  • refactor: remove OMO common config two-layer merge system
    Each OMO provider now stores its complete configuration directly in
    settings_config.otherFields instead of relying on a shared OmoGlobalConfig
    merged at write time. This simplifies the data flow from a 4-tuple
    (agents, categories, otherFields, useCommonConfig) to a 3-tuple and
    eliminates an entire DB table, two Tauri commands, and ~1700 lines of
    merge/sync code across frontend and backend.
    
    Backend:
    - Delete database/dao/omo.rs (OmoGlobalConfig struct + get/save methods)
    - Remove get/set_config_snippet from settings DAO
    - Remove get/set_common_config_snippet Tauri commands
    - Replace merge_config() with build_config() in services/omo.rs
    - Simplify OmoVariant (remove config_key, known_keys)
    - Simplify import_from_local and build_local_file_data
    - Rewrite all OMO service tests
    
    Frontend:
    - Delete OmoCommonConfigEditor.tsx and OmoGlobalConfigFields.tsx
    - Delete src/lib/api/config.ts
    - Remove OmoGlobalConfig type and merge preview functions
    - Remove useGlobalConfig/useSaveGlobalConfig query hooks
    - Simplify useOmoDraftState (remove all common config state)
    - Replace OmoCommonConfigEditor with read-only JsonEditor preview
    - Clean i18n keys (zh/en/ja)
  • fix: enforce OMO ↔ OMO Slim cross-category mutual exclusion
    When activating an OMO provider, deactivate all OMO Slim providers
    in the same transaction and delete the Slim config file, and vice
    versa. This prevents both plugin variants from being active
    simultaneously.
  • chore: pre-release cleanup — remove debug logs, fix clippy warning, add missing ja translations, and format code
    - Remove 2 console.log statements from DeepLinkImportDialog
    - Fix clippy unnecessary_map_or: use is_some_and in live.rs
    - Add 17 missing Japanese i18n keys (skills, proxy, circuitBreaker, universalProvider)
    - Run prettier and cargo fmt to fix pre-existing formatting drift
  • fix(opencode): add missing omo-slim category checks across add/form/mutation paths
    Several code paths only checked for "omo" category but missed "omo-slim",
    causing OMO Slim providers to be treated as regular OpenCode providers
    (triggering invalid write_live_snapshot, requiring manual provider key,
    and showing wrong form fields).
  • refactor(provider): switch from full config overwrite to partial key-field merging (#1098)
    * refactor(provider): switch from full config overwrite to partial key-field merging
    
    Replace the provider switching mechanism for Claude/Codex/Gemini from
    full settings_config overwrite to partial key-field replacement, preserving
    user's non-provider settings (plugins, MCP, permissions, etc.) across switches.
    
    - Add write_live_partial() with per-app implementations for Claude (JSON env
      merge), Codex (auth replace + TOML partial merge), and Gemini (env merge)
    - Add backfill_key_fields() to extract only provider-specific fields when
      saving live config back to provider entries
    - Update switch_normal, sync_current_to_live, add, update to use partial merge
    - Remove common config snippet feature for Claude/Codex/Gemini (no longer
      needed with partial merging); preserve OMO common config
    - Delete 6 frontend files (3 components + 3 hooks), clean up 11 modified files
    - Remove backend extract_common_config_* methods, 3 Tauri commands,
      CommonConfigSnippets struct, and related migration code
    - Update integration tests to validate key-field-only backfill behavior
    
    * refactor(cleanup): remove dead code and redundant MCP sync after partial-merge refactor
    
    - Remove ConfigService legacy full-overwrite sync methods (~150 lines)
    - Remove redundant McpService::sync_all_enabled from switch_normal
    - Switch proxy fallback recovery from write_live_snapshot to write_live_partial
    - Remove dead ProviderService::write_gemini_live wrapper
    - Update tests to reflect partial-merge behavior (MCP preserved, not re-synced)
    
    * feat(claude): add Quick Toggles for common Claude Code preferences
    
    Add checkbox toggles for hideAttribution, alwaysThinking, and
    enableTeammates that write directly to the live settings file via
    RFC 7396 JSON Merge Patch. Mirror changes to the form editor using
    form.watch for reactive updates.
    
    * fix(provider): add missing key fields to partial-merge constants
    
    Add provider-specific fields verified against official docs to prevent
    key residue or loss during provider switching:
    
    - Claude: CLAUDE_CODE_SUBAGENT_MODEL (env), model (top-level)
    - Codex: review_model, plan_mode_reasoning_effort
    - Gemini: GOOGLE_API_KEY (official alternative to GEMINI_API_KEY)
    
    * fix(provider): expand partial-merge key fields for Bedrock, Vertex, Foundry and behavior settings
    
    Add missing env/top-level fields to CLAUDE_KEY_ENV_FIELDS and
    CLAUDE_KEY_TOP_LEVEL so that provider switching correctly replaces
    (and clears) credentials and flags for AWS Bedrock, Google Vertex AI,
    Microsoft Foundry, and provider behavior overrides like max output
    tokens and prompt caching.
    
    * feat(provider): add auth field selector for Claude providers (AUTH_TOKEN / API_KEY)
    
    Allow users to choose between ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY
    when creating or editing custom Claude providers, persisted in meta.apiKeyField.
    
    * refactor(preset): remove AiHubMix hardcoded API_KEY in favor of generic auth selector
    
    AiHubMix was the only preset that hardcoded ANTHROPIC_API_KEY before the
    generic auth field selector was introduced. Now that users can freely
    choose between AUTH_TOKEN and API_KEY via the UI, remove the special-case
    and default AiHubMix to the standard ANTHROPIC_AUTH_TOKEN.
  • feat(backup): add pre-migration backup, periodic backup, backfill warning, and backup management UI
    Four improvements to the database backup mechanism:
    
    1. Auto backup before schema migration - creates a snapshot when
       upgrading from an older database version, providing a safety net
       beyond the existing SAVEPOINT rollback mechanism.
    
    2. Periodic startup backup - checks on app launch whether the latest
       backup is older than 24 hours and creates a new one if needed,
       ensuring all users have recent backups regardless of usage patterns.
    
    3. Backfill failure notification - switch now returns SwitchResult with
       warnings instead of silently ignoring backfill errors, so users are
       informed when their manual config changes may not have been saved.
    
    4. Backup management UI - new BackupListSection in Settings > Data
       Management showing all backup snapshots with restore capability,
       including a confirmation dialog and automatic safety backup before
       restore.
  • refactor(omo): deduplicate OMO/OMO Slim via OmoVariant parameterization
    Introduce OmoVariant struct with STANDARD/SLIM constants to eliminate
    ~250 lines of copy-pasted code across DAO, service, commands, and
    frontend layers. Adding a new OMO variant now requires only a single
    const declaration instead of duplicating ~400 lines.
  • feat(omo): add OMO Slim (oh-my-opencode-slim) support
    Implement full OMO Slim profile management to align with ai-toolbox:
    - Backend: Slim service methods, DAO, Tauri commands, plugin conflict handling
    - Frontend: types, API, query hooks, form integration with isSlim parameterization
    - Slim variant: 6 agents (no categories), separate config file and plugin name
    - Mutual exclusion: standard OMO and Slim cannot coexist as plugins
    - i18n: zh/en/ja translations for all Slim agent descriptions
  • feat(webdav): follow-up 补齐自动同步与大文件防护 (#1043)
    * feat(webdav): add robust auto sync with failure feedback
    
    (cherry picked from commit bb6760124a62a964b36902c004e173534910728f)
    
    * fix(webdav): enforce bounded download and extraction size
    
    (cherry picked from commit 7777d6ec2b9bba07c8bbba9b04fe3ea6b15e0e79)
    
    * fix(webdav): only show auto-sync callout for auto-source errors
    
    * refactor(webdav): remove services->commands auto-sync dependency
  • Fix/skill zip symlink resolution (#1040)
    * fix(skill): resolve symlinks in ZIP extraction for GitHub repos (#1001)
    
    - detect symlink entries via is_symlink() during ZIP extraction and collect target paths
    
    - add resolve_symlinks_in_dir() to copy symlink target content into link location
    
    - canonicalize base_dir to fix macOS /tmp → /private/tmp path comparison issue
    
    - add path traversal safety check to block symlinks pointing outside repo boundary
    
    - apply symlink resolution to both download_and_extract and extract_local_zip paths
    
    Closes https://github.com/farion1231/cc-switch/issues/1001
    
    * fix(skill): change search to match name and repo instead of description
    
    * feat(skill): support importing skills from ~/.agents/skills/ directory
    
    - Scan ~/.agents/skills/ in scan_unmanaged() for skill discovery
    - Parse ~/.agents/.skill-lock.json to extract repo owner/name metadata
    - Auto-add discovered repos to skill_repos management on import
    - Add path field to UnmanagedSkill to show discovered location in UI
    
    Closes #980
    
    * fix(skill): use metadata name or ZIP filename for root-level SKILL.md imports (#1000)
    
    When a ZIP contains SKILL.md at the root without a wrapper directory,
    the install name was derived from the temp directory name (e.g. .tmpDZKGpF).
    Now falls back to SKILL.md frontmatter name, then ZIP filename stem.
    
    * feat(skill): scan ~/.cc-switch/skills/ for unmanaged skill discovery and import
    
    * refactor(skill): unify scan/import logic with lock file skillPath and repo saving
    
    - Deduplicate scan_unmanaged and import_from_apps using shared source list
    - Replace hand-written AppType match with as_str() and AppType::all()
    - Extract read_skill_name_desc, build_repo_info_from_lock, save_repos_from_lock helpers
    - Add SkillApps::from_labels for building enable state from source labels
    - Parse skillPath from .skill-lock.json for correct readme URLs
    - Save skill repos to skill_repos table in both import and migration paths
    
    * fix(skill): resolve symlink and path traversal issues in ZIP skill import
    
    * fix(skill): separate source path validation and add canonicalization for symlink safety
  • fix(openclaw): address code review findings across P0-P3 issues
    - Add 25 missing i18n keys for OpenClawFormFields in all 3 locales (P0)
    - Replace key={index} with stable crypto.randomUUID() keys in EnvPanel,
      ToolsPanel, and OpenClawFormFields to prevent list state bugs (P1)
    - Exclude openclaw from ProxyToggle/FailoverToggle in App.tsx (P1)
    - Add merge_additive_config() for openclaw/opencode deep link imports (P1)
    - Normalize serde(flatten) field naming to `extra` + HashMap (P2)
    - Add directory existence check in remove_openclaw_provider_from_live (P2)
    - Remove dead code in import_default_config and openclaw API methods (P2)
    - Add duplicate key validation in EnvPanel before save (P2)
    - Add openclawConfigDir to Settings type (P2)
    - Add staleTime to OpenClaw query hooks (P3)
    - Fix type-unsafe delete via destructuring in mutations.ts (P3)
  • fix(openclaw): address code review findings for robustness
    - Fix EnvPanel visibleKeys using entry key name instead of array index
      to prevent visibility state corruption after deletion
    - Add NaN guard in AgentsDefaultsPanel numeric field parsing
    - Validate provider id and models before importing from live config
    - Upgrade import failure log level from debug to warn for OpenCode/OpenClaw
  • feat(openclaw): add Env/Tools/Agents config panels
    - Migrate OpenClaw commands from provider.rs to dedicated commands/openclaw.rs
    - Add backend types and read/write for env, tools, agents.defaults sections
    - Create EnvPanel (API key + custom vars KV editor)
    - Create ToolsPanel (profile selector + allow/deny lists)
    - Create AgentsDefaultsPanel (default model + runtime parameters)
    - Extend App.tsx menu bar with Env/Tools/Agents buttons
    - Remove Prompts button for OpenClaw (overlaps with Workspace AGENTS.md)
  • fix(openclaw): prevent creating default provider on first launch
    Add additive mode guard in import_default_config() to skip OpenCode
    and OpenClaw apps, which should use their dedicated import functions.
  • feat(services): add OpenClaw branches to backend services
    - Add OpenClaw branches to proxy service (not supported)
    - Add OpenClaw to MCP service (skip sync, MCP still in development)
    - Add OpenClaw skills directory path
    - Update ProxyTakeoverStatus with openclaw field
    - Add OpenClaw to stream check (not supported)
  • feat(provider): add OpenClaw provider service support
    - Add import_openclaw_providers_from_live() function
    - Add remove_openclaw_provider_from_live() function
    - Update write_live_snapshot() for OpenClaw
    - Add openclaw fields to VisibleApps and AppSettings
    - Add get_openclaw_override_dir() function
  • Webdav (#923)
    * feat: WebDAV backup/restore
    
    - Add WebDAV test/backup/restore commands and settings\n- Fix ja i18n missing keys; decode PROPFIND href as UTF-8\n- Stabilize Windows prompt auto-import tests via CC_SWITCH_TEST_HOME
    
    * chore: format and minor cleanups
    
    * fix: update build config
    
    * feat(webdav): unify sync UX and hardening fixes
    
    * fix(webdav): harden sync flow and stabilize sync UX/tests
    
    * fix(webdav): add resource limits to skills.zip extraction
    
    Prevent zip bomb / resource exhaustion by enforcing:
    - MAX_EXTRACT_ENTRIES (10,000 files)
    - MAX_EXTRACT_BYTES (512 MB cumulative)
    
    * refactor(webdav): drop deviceId and display deviceName only
    
    ---------
    
    Co-authored-by: small-lovely-cat <77799160+small-lovely-cat@users.noreply.github.com>
    Co-authored-by: saladday <1203511142@qq.com>
  • feat(omo): improve agent model selection UX and fix lowercase keys (#1004)
    * fix(omo): use lowercase keys for builtin agent definitions
    
    OMO config schema expects all agent keys to be lowercase.
    Updated OMO_BUILTIN_AGENTS keys (Sisyphus → sisyphus, Hephaestus →
    hephaestus, etc.) and aligned Rust test fixtures accordingly.
    
    * feat(omo): add i18n support and tooltips for agent/category descriptions
    
    * feat(omo): add preset model variants for thinking level support
    
    Add OPENCODE_PRESET_MODEL_VARIANTS constant with variant definitions
    for Google, OpenAI, and Anthropic models. The omoModelVariantsMap
    builder now falls back to presets when config-defined variants are
    absent, enabling the variant selector for supported models.
    
    * feat(omo): replace model select with searchable combobox and improve fallback handling
    
    * feat(omo): enrich preset model defaults and metadata fallback
    
    * fix(omo): preserve custom fields and align otherFields import/validation
    
    * fix: resolve omo clippy warnings and include app update
  • fix(skill): correct skill doc URL branch and path resolution (#977)
    Use the actual branch returned by download_repo instead of the
    configured branch, fixing 404s when repos default to master but
    the URL was hardcoded to main. Also switch URL format from /tree/
    to /blob/ and always point to the SKILL.md file.
    
    Closes farion1231/cc-switch#968
  • feat(omo): integrate Oh My OpenCode profile management (#972)
    * feat(omo): integrate Oh My OpenCode profile management into Provider system
    
    Adds full-stack OMO support: backend config read/write/import, OMO-specific
    provider CRUD with exclusive switching, frontend profile editor with
    agent/category/model configuration, global config management, and i18n support.
    
    * feat(omo): add model/variant dropdowns from enabled providers
    
    Replace model text inputs with Select dropdowns sourced from enabled
    OpenCode providers, add thinking-level variant selection, and prevent
    auto-enabling newly added OMO providers.
    
    * fix(omo): use standard provider action styles for OMO switch button
    
    * fix(omo): replace hardcoded isZh strings with proper i18n t() calls
  • feat(usage): enhance dashboard with auto-refresh control and robust formatting (#942)
    * style: format code and apply clippy lint fixes
    
    * feat(usage): enhance dashboard with auto-refresh control and robust formatting
    
    - Add configurable auto-refresh interval toggle (off/5s/10s/30s/60s) to usage dashboard
    - Extract shared format utilities (fmtUsd, fmtInt, parseFiniteNumber, getLocaleFromLanguage)
    - Refactor request log time filtering to rolling vs fixed mode with validation
    - Use stable serializable query keys instead of filter objects
    - Handle NaN/Infinity safely in number formatting across all usage components
    - Use RFC 3339 date format in backend trend data
  • fix(stream_check): respect auth_mode for Claude health checks (#824)
    Previously, check_claude_stream always added the x-api-key header,
    ignoring the provider's auth_mode setting. This caused health check
    failures for proxy services that only support Bearer authentication.
    
    Now the function respects the auth.strategy field:
    - AuthStrategy::Anthropic: Authorization Bearer + x-api-key
    - AuthStrategy::ClaudeAuth: Authorization Bearer only
    - AuthStrategy::Bearer: Authorization Bearer only
    
    This aligns with the behavior of ClaudeAdapter::add_auth_headers
    and fixes health checks for proxy providers with auth_mode="bearer_only".
    
    Changes:
    - Modified check_claude_stream to conditionally add x-api-key header
    - Added AuthStrategy import
    - Added test_auth_strategy_imports unit test
    
    Tests: All passing (7/7 for stream_check module)
    
    Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
  • refactor(claude): migrate api_format from settings_config to meta
    Move api_format storage from settings_config to ProviderMeta to prevent
    polluting ~/.claude/settings.json when switching providers.
    
    - Add api_format field to ProviderMeta (Rust + TypeScript)
    - Update ProviderForm to read/write apiFormat from meta
    - Maintain backward compatibility for legacy settings_config.api_format
      and openrouter_compat_mode fields (read-only fallback)
    - Strip api_format from settings_config before writing to live config
  • feat(skills): add install from ZIP file feature
    - Add open_zip_file_dialog command for selecting ZIP files
    - Add install_from_zip service method with recursive skill scanning
    - Add install_skills_from_zip Tauri command
    - Add frontend API methods and useInstallSkillsFromZip hook
    - Add "Install from ZIP" button in Skills management page
    - Support local skill ID format: local:{directory}
    - Add i18n translations for new feature and error messages
  • fix(codex): fix 404 errors and connection timeout with custom base_url (#760)
    * fix(proxy): fix Codex 404 errors with custom base_url prefixes
    
    - handlers.rs:268: Remove hardcoded /v1 prefix in Codex forwarding
    - codex.rs:140: Only add /v1 for origin-only base_urls, dedupe /v1/v1
    - stream_check.rs:364: Try /responses first, fallback to /v1/responses
    - provider.rs:427: Don't force /v1 for custom prefix base_urls
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(codex): always add /v1 for custom prefix base_urls
    
    Changed logic to always add /v1 prefix unless base_url already ends with /v1.
    This fixes 504 timeout errors with relay services that expect /v1 in the path.
    
    - Most relay services follow OpenAI standard format: /v1/responses
    - Users can opt-out by adding /v1 to their base_url configuration
    - Updated test case to reflect new behavior
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(proxy): allow system proxy on localhost with different ports
    
    - Only bypass system proxy if it points to CC Switch's own port (15721)
    - Allow other localhost proxies (e.g., Clash on 7890) to be used
    - Add INFO level logging for request URLs to aid debugging
    
    This fixes connection timeout issues when using local proxy tools.
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(codex): don't add /v1 for custom prefix base_urls
    
    Reverted logic to not add /v1 for base_urls with custom prefixes.
    Many relay services use custom paths without /v1.
    
    - Pure origin (e.g., https://api.openai.com) → adds /v1
    - With /v1 (e.g., https://api.openai.com/v1) → no change
    - Custom prefix (e.g., https://example.com/openai) → no /v1
    
    This fixes 404 errors with relay services that don't use /v1 in their paths.
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(proxy): use dynamic port for system proxy detection
    
    Instead of hardcoding port 15721, now uses the actual configured
    listen_port from proxy settings.
    
    - Added set_proxy_port() to update the port when proxy server starts
    - Added get_proxy_port() to retrieve current port for detection
    - Updated server.rs to call set_proxy_port() on startup
    - Updated tests to reflect new behavior
    
    This allows users to change the proxy port in settings without
    breaking the system proxy detection logic.
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(proxy): change default proxy port from 15721 to 5000
    
    Update the default fallback port in get_proxy_port() from 15721 to 5000
    to match the project's standard default port configuration.
    
    Also updated test cases to use port 5000 consistently.
    
    Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
    
    * fix(proxy): revert default port back to 15721
    
    Revert the default fallback port in get_proxy_port() from 5000 back to 15721
    to align with the project's updated default port configuration.
    
    Also updated test cases to use port 15721 consistently.
    
    Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: ozbombor <ozbombor@users.noreply.github.com>
    Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
  • Feat/pricing config enhancement (#781)
    * feat(db): add pricing config fields to proxy_config table
    
    - Add default_cost_multiplier field per app type
    - Add pricing_model_source field (request/response)
    - Add request_model field to proxy_request_logs table
    - Implement schema migration v5
    
    * feat(api): add pricing config commands and provider meta fields
    
    - Add get/set commands for default cost multiplier
    - Add get/set commands for pricing model source
    - Extend ProviderMeta with cost_multiplier and pricing_model_source
    - Register new commands in Tauri invoke handler
    
    * fix(proxy): apply cost multiplier to total cost only
    
    - Move multiplier calculation from per-item to total cost
    - Add resolve_pricing_config for provider-level override
    - Include request_model and cost_multiplier in usage logs
    - Return new fields in get_request_logs API
    
    * feat(ui): add pricing config UI and usage log enhancements
    
    - Add pricing config section to provider advanced settings
    - Refactor PricingConfigPanel to compact table layout
    - Display all three apps (Claude/Codex/Gemini) in one view
    - Add multiplier column and request model display to logs
    - Add frontend API wrappers for pricing config
    
    * feat(i18n): add pricing config translations
    
    - Add zh/en/ja translations for pricing defaults config
    - Add translations for multiplier, requestModel, responseModel
    - Add provider pricing config translations
    
    * fix(pricing): align backfill cost calculation with real-time logic
    
    - Fix backfill to deduct cache_read_tokens from input (avoid double billing)
    - Apply multiplier only to total cost, not to each item
    - Add multiplier display in request detail panel with i18n support
    - Use AppError::localized for backend error messages
    - Fix init_proxy_config_rows to use per-app default values
    - Fix silent failure in set_default_cost_multiplier/set_pricing_model_source
    - Add clippy allow annotation for test mutex across await
    
    * style: format code with cargo fmt and prettier
    
    * fix(tests): correct error type assertions in proxy DAO tests
    
    The tests expected AppError::InvalidInput but the DAO functions use
    AppError::localized() which returns AppError::Localized variant.
    Updated assertions to match the correct error type with key validation.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • feat(opencode): sync all providers to live config on directory change
    Add additive mode support for OpenCode in sync_current_to_live:
    - Add AppType::is_additive_mode() to distinguish switch vs additive mode
    - Add AppType::all() iterator to avoid hardcoding app lists
    - Add sync_all_providers_to_live() for additive mode apps
    - Refactor sync_current_to_live to handle both modes
    
    Frontend changes (directory settings):
    - Track opencodeDirChanged in useDirectorySettings
    - Trigger syncCurrentProvidersLiveSafe when OpenCode dir changes
    - Add i18n strings for OpenCode directory settings
  • fix(skills): prevent duplicate skill installation from different repos (#778)
    - Add directory conflict detection before installation
    - Fix installed status check to match repo owner and name
    - Add i18n translations for conflict error messages
  • feat(skills): add skill sync method setting (symlink/copy)
    - Add SyncMethod enum (Auto/Symlink/Copy) in Rust backend
    - Implement sync_to_app_dir with symlink support (cross-platform)
    - Add SkillSyncMethodSettings UI component (simplified 2-button selector)
    - Add i18n support for zh/en/ja
    - Replace copy_to_app with configurable sync_to_app_dir
    - Add skill_sync_method field to AppSettings
    
    User can now choose between symlink (disk space saving) or copy (best compatibility) in Settings > General.
  • fix(prompt): clear prompt file when all prompts are disabled
    When disabling a prompt, check if any other prompts remain enabled.
    If all prompts are disabled, clear the prompt file to ensure UI state
    matches the actual configuration that Claude Code reads.
  • feat(skills): add baoyu-skills preset repo and auto-supplement missing defaults
    - Add JimLiu/baoyu-skills to default skill repositories
    - Change init_default_skill_repos() from "first-run only" to "supplement missing"
    - New preset repos will now auto-appear for existing users on upgrade
  • fix(failover): switch to P1 immediately when enabling auto failover
    Previously, enabling auto failover kept using the current provider until
    the first failure, causing inconsistency when the current provider was
    not in the failover queue. When stopping proxy, the restored config
    would not match user expectations.
    
    New behavior:
    - Enable auto failover = immediately switch to queue P1
    - Subsequent routing follows queue order (P1→P2→...)
    - Auto-add current provider to queue if queue is empty
    
    Changes:
    - Add switch_proxy_target() for hot-switching during proxy mode
    - Update provider_router to use queue order when failover enabled
    - Sync tray menu Auto click with the same logic
    - Update UI tooltips to reflect new semantics
    - Add tests for queue-only routing scenario
  • Feat/provider individual config (#663)
    * refactor(ui): simplify UpdateBadge to minimal dot indicator
    
    * feat(provider): add individual test and proxy config for providers
    
    Add support for provider-specific model test and proxy configurations:
    
    - Add ProviderTestConfig and ProviderProxyConfig types in Rust and TypeScript
    - Create ProviderAdvancedConfig component with collapsible panels
    - Update stream_check service to merge provider config with global config
    - Proxy config UI follows global proxy style (single URL input)
    
    Provider-level configs stored in meta field, no database schema changes needed.
    
    * feat(ui): add failover toggle and improve proxy controls
    
    - Add FailoverToggle component with slide animation
    - Simplify ProxyToggle style to match FailoverToggle
    - Add usage statistics button when proxy is active
    - Fix i18n parameter passing for failover messages
    - Add missing failover translation keys (inQueue, addQueue, priority)
    - Replace AboutSection icon with app logo
    
    * fix(proxy): support system proxy fallback and provider-level proxy config
    
    - Remove no_proxy() calls in http_client.rs to allow system proxy fallback
    - Add get_for_provider() to build HTTP client with provider-specific proxy
    - Update forwarder.rs and stream_check.rs to use provider proxy config
    - Fix EditProviderDialog.tsx to include provider.meta in useMemo deps
    - Add useEffect in ProviderAdvancedConfig.tsx to sync expand state
    
    Fixes #636
    Fixes #583
    
    * fix(ui): sync toast theme with app setting
    
    * feat(settings): add log config management
    
    Fixes #612
    Fixes #514
    
    * fix(proxy): increase request body size limit to 200MB
    
    Fixes #666
    
    * docs(proxy): update timeout config descriptions and defaults
    
    Fixes #612
    
    * fix(proxy): filter x-goog-api-key header to prevent duplication
    
    * fix(proxy): prevent proxy recursion when system proxy points to localhost
    
    Detect if HTTP_PROXY, HTTPS_PROXY, or ALL_PROXY environment variables
    point to loopback addresses (localhost, 127.0.0.1), and bypass system
    proxy in such cases to avoid infinite request loops.
    
    * fix(i18n): add providerAdvanced i18n keys and fix failover toast parameter
    
    - Add providerAdvanced.* i18n keys to en.json, zh.json, and ja.json
    - Fix failover toggleFailed toast to pass detail parameter
    - Remove Chinese fallback text from UI for English/Japanese users
    
    * fix(tray): restore tray-provider events and enable Auto failover properly
    
    - Emit provider-switched event on tray provider click (backward compatibility)
    - Auto button now: starts proxy, takes over live config, enables failover
    
    * fix(log): enable dynamic log level and single file mode
    
    - Initialize log at Trace level for dynamic adjustment
    - Change rotation strategy to KeepSome(1) for single file
    - Set max file size to 1GB
    - Delete old log file on startup for clean start
    
    * fix(tray): fix clippy uninlined format args warning
    
    Use inline format arguments: {app_type_str} instead of {}
    
    * fix(provider): allow typing :// in endpoint URL inputs
    
    Change input type from "url" to "text" to prevent browser
    URL validation from blocking :// input.
    
    Closes #681
    
    * fix(stream-check): use Gemini native streaming API format
    
    - Change endpoint from OpenAI-compatible to native streamGenerateContent
    - Add alt=sse parameter for SSE format response
    - Use x-goog-api-key header instead of Bearer token
    - Convert request body to Gemini contents/parts format
    
    * feat(proxy): add request logging for debugging
    
    Add debug logs for outgoing requests including URL and body content
    with byte size, matching the existing response logging format.
    
    * fix(log): prevent usize underflow in KeepSome rotation strategy
    
    KeepSome(n) internally computes n-2, so n=1 causes underflow.
    Use KeepSome(2) as the minimum safe value.
  • 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).
  • 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): 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(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