Commit Graph

1101 Commits

  • merge: resolve conflicts between fix/common-config-sync-improvements and main
    Merged both feature sets:
    - common-config: commonConfigEnabled, commonConfigEnabledByApp fields
    - api-format: apiFormat field for Claude provider OpenAI compatibility
    
    Key resolution in live.rs: apply sanitize_claude_settings_for_live()
    to merged config before writing to ensure internal fields are stripped.
  • fix(common-config): improve error handling, layer separation, and reliability across sync system
    P0 Fixes:
    - Enable toml `preserve_order` feature for reliable key ordering
    - Extract pure detection functions to utils/commonConfigDetection.ts (fix layer inversion)
    - Return structured result from preserveCodexConfigFormat to prevent data loss
    
    P1 Fixes:
    - Handle pure TOML strings in Codex config instead of silent skip
    - Surface extractDiff errors in useCodexCommonConfig instead of swallowing
    - Log parse errors in detectCommonConfigEnabledByContent for debugging
    
    P2 Fixes:
    - Use parsing-based assertions in Rust TOML ordering tests
    - Add schema warning when Codex config field is wrong type
    - Add depth/node limits to isSubset function (prevent stack overflow)
    - Replace hardcoded "{}" check with adapter.hasValidContent()
    
    P3 Fixes:
    - Refactor Gemini errors to structured GeminiConfigErrorInfo format
    - Add mapGeminiErrorToI18n for type-safe error-to-i18n mapping
  • feat(ci): add Linux ARM64 build support
    - Add ubuntu-22.04-arm runner to build matrix
    - Rename Linux artifacts with architecture suffix (x86_64/arm64)
    - Update pnpm cache key with runner.arch to avoid cross-arch pollution
    - Add linux-aarch64 platform to latest.json for Tauri updater
  • fix(mutations): use extractErrorMessage for complete error display
    Provider add/update/delete mutations were using error.message directly,
    which doesn't extract Tauri invoke errors properly. Now using the same
    extractErrorMessage pattern as the rest of the codebase.
  • chore: bump version to 3.10.3
    - Update version in package.json, Cargo.toml, and tauri.conf.json
    - Add missing changelog entries for OpenCode API key link and AICodeMirror preset
    - Fix Prettier formatting for new hook files
  • feat(opencode): add API key link support for OpenCode provider form
    Enable API key link feature for OpenCode app, allowing users to access
    provider websites for key registration directly from the form.
  • feat(providers): add AICodeMirror partner preset for all apps
    Register AICodeMirror icon, add i18n partner promotion texts (zh/en/ja),
    and fix trailing whitespace in codexProviderPresets.
  • refactor(common-config): consolidate hasContent methods into adapters
    - Add hasContent method to CommonConfigAdapter interface
    - Move isSubset utility to configMerge.ts for reuse
    - Export preserveCodexConfigFormat from adapters for hook use
    - Add hasContentByAppType dispatcher for unified content detection
    - Remove dead code from providerConfigUtils (hasCommonConfigSnippet,
      hasTomlCommonConfigSnippet, hasGeminiCommonConfigSnippet)
  • fix(codex): reorder merged TOML keys to put custom config first
    When merging TOML configs, common config keys were appearing before
    custom config keys. Add reorderMergedKeys() to ensure custom config
    keys (model_provider, model, etc.) appear at the top, with common-only
    keys appended after.
  • docs(changelog): release v3.10.3
    Add changelog entry for v3.10.3 feature release including:
    - API format selector and presets
    - Pricing config enhancement
    - Skills ZIP install
    - Preferred terminal selection
    - Silent startup option
    - OpenCode environment check and directory sync
    - NVIDIA NIM and n1n.ai presets
    - Multiple bug fixes for Codex, proxy URL, tray menu, etc.
  • feat(settings): add OpenCode support to environment check and one-click install
    - Add OpenCode version detection with Go path scanning
    - Add GitHub Releases API for fetching latest OpenCode version
    - Add OpenCode install command to one-click install section
    - Update i18n hints to include OpenCode across all locales
    - Fix SettingsPage indentation formatting
  • feat(claude): show proxy hint when switching to OpenAI Chat format provider
    Display an info toast when switching to a provider that uses OpenAI Chat
    format (apiFormat === "openai_chat"), reminding users to enable the proxy
    service. Move toast logic from mutation to useProviderActions for better
    control over notification content based on provider properties.
  • refactor(proxy): remove DeepSeek max_tokens clamp from transform layer
    The max_tokens restriction was too aggressive and should be handled
    upstream or by the provider itself. Simplify anthropic_to_openai by
    removing provider parameter since model mapping is already done by
    proxy::model_mapper.
  • feat(providers): add NVIDIA NIM preset for Claude and OpenCode
    - Add NVIDIA NIM provider preset with API configuration
    - Add nvidia.svg icon and register in icon system
    - Add nvidia metadata with keywords and default color (#74B71B)
  • feat(claude): add apiFormat support for provider presets
    Allow preset providers to specify API format (anthropic or openai_chat),
    enabling third-party proxy services that use OpenAI Chat Completions format.
  • fix(proxy): improve URL building and remove redundant model mapping
    - Add model parameter to request logs for better debugging
    - Fix duplicate /v1/v1 in URL when both base_url and endpoint have version
    - Extend ?beta=true parameter to /v1/chat/completions endpoint
    - Remove model mapping from transform layer (now handled by model_mapper)
    - Add DeepSeek max_tokens clamping (1-8192 range)
  • 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
  • fix(i18n): update apiFormatOpenAIChat label to mention proxy requirement
    Change label from "Requires conversion" to "Requires proxy" for clarity:
    - zh: "需转换" → "需开启代理"
    - en: "Requires conversion" → "Requires proxy"
    - ja: "変換が必要" → "プロキシが必要"
  • fix(claude): improve backward compatibility for openrouter_compat_mode
    Extend backward compatibility support for legacy openrouter_compat_mode field:
    - Support number type (1 = enabled, 0 = disabled)
    - Support string type ("true"/"1" = enabled)
    - Add corresponding test cases for number and string types
  • feat(claude): add API format selector for third-party providers
    Replace the OpenRouter-specific compatibility toggle with a generic
    API format selector that allows all Claude providers to choose between:
    
    - Anthropic Messages (native): Direct passthrough, no conversion
    - OpenAI Chat Completions: Enables Anthropic ↔ OpenAI format conversion
    
    Changes:
    - Add ClaudeApiFormat type ("anthropic" | "openai_chat") to types.ts
    - Replace openRouterCompatToggle with apiFormat dropdown in ClaudeFormFields
    - Update ProviderForm to manage apiFormat state via settingsConfig.api_format
    - Refactor claude.rs: add get_api_format() method, update needs_transform()
    - Maintain backward compatibility with legacy openrouter_compat_mode field
    - Update i18n translations (zh, en, ja)
  • fix(codex): show custom config before common config in merge preview
    Replace deep_merge_toml with deep_merge_toml_preserve_order so that
    custom config keys appear at the top and common-only keys appear below.
  • refactor(common-config): consolidate hooks and migrate Gemini to ENV format
    - Delete redundant wrapper hooks (useCommonConfigSnippet, useGeminiCommonConfig)
    - Change Gemini common config from JSON to ENV format (.env style)
    - Add backend validation with forbidden keys filtering (GEMINI_API_KEY, GOOGLE_GEMINI_BASE_URL)
    - Fix localStorage migration to skip empty parsed snippets
    - Add error handling for silent JSON parse failures
    - Clean up debug logs and unused types
  • fix(test): move orphaned test into describe block
    Move "clears loading flag when all mutations idle" test inside
    the describe("useProviderActions") block for proper test isolation.
  • chore: remove dead code from providerConfigUtils and config API
    - Remove unused functions: updateCommonConfigSnippet, replaceCommonConfigSnippet,
      replaceGeminiCommonConfigSnippet, updateTomlCommonConfigSnippet,
      replaceTomlCommonConfigSnippet and their internal helpers (~600 lines)
    - Remove deprecated getClaudeCommonConfigSnippet and setClaudeCommonConfigSnippet
    - Remove unused imports (deepClone, deepMerge)
  • refactor: unify common config hooks with generic base hook and adapters
    - Create useCommonConfigBase generic hook (~300 lines)
    - Create commonConfigAdapters for Claude (JSON), Codex (TOML), Gemini (ENV/JSON)
    - Refactor three hooks from ~1370 lines to ~430 lines (-940 lines)
    - Extract useDarkMode hook from three ConfigSections components
    - Remove dead code: backend _str functions, frontend JSON/TOML unused exports
    - Deduplicate deepClone/deepMerge utilities
    - Fix duplicate mod tests in provider.rs
  • fix(codex): fix config.toml not updated after extracting common config
    The handleExtract function assumed codexConfig was JSON format, but it's
    actually a pure TOML string from useCodexConfigState. JSON.parse() failed
    silently, causing onConfigChange not to be called.
    
    Changes:
    - Handle both pure TOML string and legacy JSON format
    - Add missing i18n key for zh.json
  • 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
  • feat(settings): prioritize native install path for Claude Code detection
    - Move ~/.local/bin to first position in version scan search paths
    - Update one-click install commands to use official native installation
      (curl script) instead of npm for Claude Code
  • 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(ui): add auto-height support for JsonEditor component
    Add autoHeight prop to dynamically resize editor based on content lines.
    Apply to config preview sections in Codex, Gemini, and CommonConfig forms.
  • fix(config): defer common config save until form submission
    - Change handleExtract and handleCommonConfigSnippetChange to use delayed save pattern
    - Add hasUnsavedCommonConfig state to track pending changes
    - Add getPendingCommonConfigSnippet and markCommonConfigSaved helper functions
    - Remove immediate backend API calls, save only on form submit
    - Add extractSuccessNeedSave and saveCommonConfigFailed i18n keys
  • fix(settings): correct Gemini default visibility to true (#818)
    Frontend fallback value for visibleApps had gemini: false, which was
    inconsistent with backend default (gemini: true). This caused Gemini
    to be hidden by default on first install.
    
    Fixes #817
    
    Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
  • chore(presets): upgrade Kimi/Moonshot providers to k2.5 model
    - Update Moonshot preset models from kimi-k2-thinking to kimi-k2.5
    - Update OpenCode Kimi preset name and model to k2.5
    - Remove explicit model config from Kimi preset (use API defaults)
  • docs(sponsors): add AICodeMirror and reorder sponsor list
    - Add AICodeMirror as new sponsor with logo
    - Sync changes across all README languages (EN/ZH/JA)
  • 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
  • fix(settings): correct footer layout in advanced settings tab
    - Establish proper flexbox height chain from App to SettingsPage
    - Move save button to fixed footer outside scrollable area
    - Align footer styling with FullScreenPanel for consistency
  • 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