Commit Graph

420 Commits

  • fix(config): resolve common config sync issues across frontend and backend
    - Fix backfill pollution: extract custom config from live when common config enabled
    - Fix proxy backup: merge common config before backup to preserve full config
    - Unify null override semantics: null no longer overrides in both frontend and backend
    - Add missing i18n keys for common config UI
    - Hide format button in readonly JsonEditor
    - Add mapGeminiWarningToI18n for user-friendly warning messages
  • 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>
  • merge: resolve conflict in live.rs, keep write_live_snapshot_with_merge
    - Combine doc comments from both branches
    - Keep write_live_snapshot_with_merge for common config runtime merge
    - Fix indentation to match main branch style
  • fix(config): improve Gemini common config parsing robustness
    - Add ENV format quote stripping (KEY="value" -> value)
    - Add toast warnings for filtered forbidden keys
    - Unify error codes with GEMINI_CONFIG_ERROR_CODES constants
    - Remove duplicate isPlainObject, use shared implementation
    - Fix type guard for filter entries
  • fix(config): improve Gemini merge and remove duplicate logging
    Address multiple audit findings:
    
    1. config_merge.rs:701 - Gemini merge now initializes env from common
       config when custom config has no env field, ensuring common API keys
       and base URLs are applied correctly.
    
    2. Remove duplicate logging - merge functions now only return warnings,
       callers (live.rs, proxy.rs) handle logging in one place.
    
    3. live.rs:169 - Unify error message style to plain English descriptions
       instead of error codes embedded in messages.
    
    Changes:
    - Add fallback to initialize env from common_env when missing
    - Remove log::warn! calls from merge functions
    - Use descriptive error messages consistently
  • 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
  • refactor(config): extract common config merge to shared function
    Address code audit findings #1, #2, #6, #7, #11:
    
    - Extract merge logic to merge_config_for_live() in config_merge.rs
    - Add is_common_config_enabled() helper function
    - Add MergeResult struct with config and optional warning
    - Update live.rs and proxy.rs to use the shared function
    - Add logging for JSON parse failures instead of silent fallback
    - Convert Chinese error messages to error codes (CODEX_CONFIG_*)
    - Support both flat and wrapped formats for Gemini common config
    
    This eliminates code duplication between live.rs and proxy.rs,
    making future maintenance easier and preventing behavioral drift.
  • fix(deeplink): disable common config for deeplink imported providers
    Set common_config_enabled to false by default for providers imported
    via deeplink URLs. This prevents unexpected configuration merging when
    users import providers from external sources.
    
    Deeplink imported providers should use their settings_config directly
    without merging with the global common config snippet, as the imported
    configuration is expected to be complete and self-contained.
    
    Changes:
    - Always return ProviderMeta with common_config_enabled = false
    - Refactor build_provider_meta to handle usage_script conditionally
    - Add documentation explaining the design decision
  • feat(proxy): add common config merge to proxy live config restore
    Extend the proxy service's restore_live_config function to support
    common config runtime merge when restoring live configuration after
    proxy takeover ends.
    
    This ensures that when the proxy releases control and restores the
    original provider configuration, the common config merge is applied
    consistently with the main provider switching logic.
    
    The merge follows the same pattern as live.rs:
    - Check common_config_enabled flag per app type
    - Merge common snippet with provider's custom config
    - customConfig overrides commonConfig
  • feat(provider): add common config runtime merge to live config sync
    Implement runtime merge of common config snippet with provider's custom
    config when writing to live configuration files. This enables shared
    configuration templates across providers while preserving provider-specific
    overrides.
    
    Key changes:
    - Add write_live_snapshot_with_merge() function that performs runtime merge
    - Support JSON merge for Claude, TOML merge for Codex, and env merge for Gemini
    - Update sync_current_to_live() to use the new merge function
    - Update provider switch and update operations to use merge function
    - Merge rule: customConfig (provider-specific) overrides commonConfig (shared)
    
    The merge is only performed when common_config_enabled is true for the
    provider and the corresponding app type.
  • feat(config): add core utilities for config merge and difference extraction
    Add comprehensive config merge utilities for both Rust backend and TypeScript frontend
    to support the new common config architecture where customConfig overrides commonConfig.
    
    Backend (Rust):
    - Add config_merge.rs module with JSON and TOML merge functions
    - Support deep merge where source overrides target for nested objects
    - Add extract_difference functions to identify custom-only keys
    - Include compute_final_*_config functions for runtime merge calculation
    - Register module in lib.rs
    
    Frontend (TypeScript):
    - Add configMerge.ts with isPlainObject, deepClone, deepEqual, deepMerge utilities
    - Implement computeFinalConfig for merging common + custom configs
    - Add extractDifference to identify keys unique to live config
    - Add tomlConfigMerge.ts with TOML-specific merge/extract functions
    - Use smol-toml for parsing and serialization
    
    These utilities form the foundation for the refactored common config system
    where providers store only custom config and merge with shared templates at runtime.
  • refactor(config): consolidate get_home_dir into single public function
    Follow-up to #644: Extract duplicate get_home_dir() implementations
    into a single pub fn in config.rs, reducing code duplication across
    codex_config.rs, gemini_config.rs, and settings.rs.
    
    Also adds documentation comments to build.rs explaining the Windows
    manifest workaround for test binaries.
  • feat(settings): add preferred terminal selection
    Allow users to choose their preferred terminal app for opening
    provider terminals. Supports platform-specific options:
    - macOS: Terminal.app, iTerm2, Alacritty, Kitty, Ghostty
    - Windows: cmd, PowerShell, Windows Terminal
    - Linux: GNOME Terminal, Konsole, Xfce4, Alacritty, Kitty, Ghostty
    
    Falls back to system default if selected terminal is unavailable.
  • refactor(terminal): consolidate redundant terminal launch functions
    Merge macOS Alacritty/Kitty/Ghostty launchers into a single generic
    `launch_macos_open_app` function with a `use_e_flag` parameter.
    Replace Windows cmd/PowerShell/wt launchers with a shared
    `run_windows_start_command` helper. Reduces ~98 lines of duplicate code.
  • fix(windows): stabilize test environment (#644)
    * fix(windows): embed common-controls manifest
    
    * fix(windows): prefer HOME for config paths
    
    * test(windows): fix export_sql invalid path
    
    * fix(windows): remove unused env import in config.rs
  • 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(backend): add CommonConfigEnabledByApp to ProviderMeta
    - Add CommonConfigEnabledByApp struct for per-app tracking
    - Add common_config_enabled and common_config_enabled_by_app to ProviderMeta
    - Add is_empty() method to CommonConfigSnippets
    - Skip serializing empty common_config_snippets in config.json
  • 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.
  • fix(opencode): preserve extra model fields during serialization
    Add `extra` field with serde flatten to OpenCodeModel struct to capture
    custom fields like cost, modalities, thinking, and variants that were
    previously lost during save operations.
  • chore: release v3.10.1
    - Bump version to 3.10.1 across all config files
    - Update CHANGELOG with all fixes since v3.10.0
    - Fix Rust Clippy warning by using derive(Default)
    - Apply code formatting
  • fix(proxy): change rectifier default state to disabled
    Change the default state of the rectifier from enabled to disabled.
    This allows users to opt-in to the rectifier feature rather than having it enabled by default.
    
    Changes:
    - Set RectifierConfig::default() enabled and request_thinking_signature to false
    - Update serde default attributes from default_true to default
    - Update unit tests to reflect new default behavior
  • fix(settings): reorder window settings and change default values
    Move "minimize to tray on close" to the bottom of window settings.
    Change skipClaudeOnboarding default to false.
  • fix(terminal): keep Windows terminal window open after execution
    Use /K instead of /C flag to prevent cmd window from closing
    immediately after batch file execution. Also remove the buggy
    errorlevel check that was checking del command's return value
    instead of claude's.
    
    Fixes #726
  • fix(config): correct OpenCode config path on Windows
    OpenCode uses ~/.config/opencode on all platforms, not %APPDATA%\opencode
    on Windows. Remove the platform-specific path handling to use unified
    ~/.config/opencode path across all operating systems.
  • 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
  • chore: fix code formatting and test setup
    - Format Rust code with rustfmt (misc.rs, types.rs)
    - Format TypeScript/React code with Prettier (4 files)
    - Fix ProviderList test by wrapping with QueryClientProvider
  • fix(terminal): use temp script files to avoid escaping issues on all platforms
    - macOS: Replace inline AppleScript commands with temp .sh script file
    - Linux: Use temp .sh script file instead of inline bash -c command
    - Windows: Add proper error capturing with output() instead of spawn()
    - Remove unused escape_shell_path and generate_wrapper_script functions
    - Unified approach across platforms for better maintainability
  • feat(tray): sync tray menu with app visibility settings
    Apply visibleApps setting to filter tray menu sections, so hidden apps
    no longer appear in the system tray menu.
  • feat(settings): add app visibility settings
    Allow users to choose which apps (Claude, Codex, Gemini, OpenCode) to display on the homepage.
    
    - Add VisibleApps type and settings field in both frontend and backend
    - Refactor AppSwitcher to render apps dynamically based on visibility
    - Extract ToggleRow component for reuse
    - Add i18n support for app visibility settings
  • 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.
  • 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.