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
The common config checkbox was only auto-enabled for the first preset
(custom) because hasInitializedNewMode ref was permanently locked to
true after first initialization.
Add selectedPresetId parameter to all three common config hooks and
reset the initialization flag when preset changes, allowing the
initialization logic to re-run for each preset switch.
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.
- 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
- 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
- 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
- Add import button next to discover button in skills page header
- Expose openImport method via UnifiedSkillsPanel ref
- Show toast instead of dialog when no unmanaged skills found
- Remove redundant buttons from empty state view
The hover:scale-[1.01] effect caused cards to overflow their container
boundaries. Keeping only hover:shadow-lg provides sufficient visual
feedback without the overflow issue.
Use isFetching instead of just isLoading to show the loading spinner.
isLoading is only true on initial load, while isFetching is true
during any fetch operation including refetch.
The refresh button is only needed in the discovery panel (to fetch
latest skills from GitHub). The installed skills panel uses local
database which auto-updates on install/uninstall operations.
- Remove "Claude" prefix from Skills management title
- Update descriptions to include Gemini alongside Claude Code/Codex
- Applied to all three locales: zh, en, ja
- Use same DRAG_BAR_HEIGHT (28px) and HEADER_HEIGHT (64px) as App.tsx
- Remove border-b divider line from header
- Add rounded-lg class to back button for consistency
- Remove duplicate "Repo Manager" button from installed skills view
(should only appear in discovery view)
- Remove redundant back button from SkillsPage component
(header already provides unified navigation)
- Clean up unused openRepoManagerOnDiscovery state and related useEffect
- Remove unused onClose prop and ArrowLeft import from SkillsPage
- 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
* i18n: update cache terminology across all languages
- Change 'Cache Read' to 'Cache Hit' in all languages
- Change 'Cache Write' to 'Cache Creation' in all languages
- Update zh: 缓存读取 → 缓存命中, 缓存写入 → 缓存创建
- Update en: Cache Read → Cache Hit, Cache Write → Cache Creation
- Update ja: キャッシュ読取 → キャッシュヒット, キャッシュ書込 → キャッシュ作成
Affected keys: cacheReadTokens, cacheCreationTokens, cacheReadCost,
cacheWriteCost, cacheRead, cacheWrite
* feat(usage): add cache metrics to trend chart
- Add cache creation tokens visualization (orange line)
- Add cache hit tokens visualization (purple line)
- Add gradient definitions for new cache metrics
- Include cache data in hourly aggregation
- Display cache metrics alongside input/output tokens
This provides better visibility into cache usage patterns over time.
* fix(usage): fix timezone handling in datetime picker
- Add timestampToLocalDatetime() to convert Unix timestamp to local datetime
- Add localDatetimeToTimestamp() with validation for incomplete input
- Fix issue where typing hours/minutes would jump to previous day
- Validate datetime format completeness before conversion
- Use local timezone instead of UTC for datetime-local input
This resolves the issue where users couldn't fine-tune time selection
and the input would jump unexpectedly when editing hours or minutes.
* feat(usage): add auto-refresh for usage statistics
- Add 30-second auto-refresh interval for all usage queries
- Disable background refresh to save resources
- Apply to: summary, trends, provider stats, model stats, request logs
- Queries automatically update when tab is active
- Pause refresh when user switches to another tab
This keeps usage data fresh without manual refresh.
* fix(proxy): improve usage logging and cache token parsing
- Log requests even when usage parsing fails (with default values)
- Add detailed debug logging for usage metrics
- Support cache_read_input_tokens field in Codex responses
- Fallback to input_tokens_details.cached_tokens if needed
- Add test case for cached_tokens in input_tokens_details
- Ensure all requests are tracked in database for analytics
This fixes missing request logs when API responses lack usage data
and improves cache token detection across different response formats.
* style(rust): use inline format args in format! macros
- Replace format!("...", var) with format!("...{var}")
- Update universal provider ID formatting
- Update error message formatting
- Update config.toml generation in Codex provider
Fixes clippy::uninlined_format_args warnings.
* feat(proxy): enhance provider router logging
- Add debug logs for failover queue provider count
- Log circuit breaker state for each provider check
- Add logs for missing current provider scenarios
- Log when no current provider is configured
- Use inline format args for better readability
This improves debugging of provider selection and failover behavior.
* feat(database): update model pricing data
- Update Claude models to full version format (e.g. claude-opus-4-5-20251101)
- Add GPT-5.2 series model pricing (10 models)
- Add GPT-5.1 series model pricing (10 models)
- Add GPT-5 series model pricing (12 models)
- Add Gemini 3 series model pricing (2 models)
- Update Gemini 2.5 series model ID format (use dot separator)
- Unify display names by removing thinking level suffixes
* fix(usage): correct Gemini output token calculation
Fix Gemini API output token parsing to use totalTokenCount - promptTokenCount
instead of candidatesTokenCount alone. This ensures thoughtsTokenCount is
included in output statistics.
- Update from_gemini_response to calculate output from total - input
- Update from_gemini_stream_chunks with same logic for consistency
- Fix from_codex_stream_events to use adjusted token calculation
- Add test case for responses with thoughtsTokenCount
- Update existing tests to match new calculation logic
* fix(usage): correct cache token billing and add Codex format auto-detection
- Avoid double-billing cache tokens by subtracting from input before calculation
- Add smart Codex parser that auto-detects OpenAI vs Codex API format
- Extract model name from Codex responses for accurate tracking
* fix(proxy): improve takeover detection with live config check
- Add live config takeover detection for hot-switch decision
- Rebuild takeover when backup is missing or placeholder remains
- Make detect_takeover_in_live_config_for_app public
- Fix is_takeover_active to use actual takeover status
* refactor(usage): simplify model pricing lookup by removing suffix fallback
Replace complex suffix-stripping fallback with direct prefix/suffix cleanup.
Model IDs are now cleaned by removing vendor prefix (before /) and colon
suffix (after :), then matched exactly against pricing table.
* feat(database): add Chinese AI model pricing data
Add pricing for domestic AI models (CNY/1M tokens):
- Doubao-Seed-Code (ByteDance)
- DeepSeek V3/V3.1/V3.2
- Kimi K2/K2-Thinking/K2-Turbo (Moonshot)
- MiniMax M2/M2.1/M2.1-Lightning
- GLM-4.6/4.7 (Zhipu)
- Mimo V2 Flash (Xiaomi)
Also fix test case to use correct model ID and remove invalid currency column.
* refactor(proxy): improve header forwarding with blacklist approach
Change from whitelist to blacklist mode for request header forwarding.
Only skip headers that will be overridden (auth, host, content-length).
This preserves client's original headers and improves compatibility.
* fix(proxy): bypass timeout and retry configs when failover is disabled
When auto_failover_enabled is false, timeout and retry configurations
should not affect normal request flow. This change ensures:
- create_forwarder: passes 0 for all timeout/retry params when failover
is disabled, effectively bypassing these checks
- streaming_timeout_config: returns 0 for both first_byte_timeout and
idle_timeout when failover is disabled
This prevents unnecessary timeout errors and retry attempts when users
have explicitly disabled the failover feature.
* fix(proxy): handle zero value input in failover config fields
* refactor(proxy): remove retry logic and add enabled check for failover
* refactor(proxy): distinguish circuit-open from no-provider errors
* Align usage stats to sliding windows
* feat(proxy): add body and header filtering for upstream requests
* feat(proxy): enable transparent passthrough for headers
- Passthrough anthropic-beta header as-is from client
- Passthrough anthropic-version header from client
- Passthrough client IP headers (x-forwarded-for, x-real-ip) by default
- Filter private params (underscore-prefixed fields) from request body
- No database changes required
* feat(proxy): extract session ID from client requests for logging
- Add SessionIdExtractor to parse session ID from Claude/Codex requests
- Support extraction from metadata.user_id, headers, previous_response_id
- Pass session_id through RequestContext to usage logger
- Enable request correlation by session in proxy_request_logs
- Change from UTC to local timezone for daily/hourly trends
- Use SQLite 'localtime' modifier for date grouping
- Replace single().unwrap() with earliest().unwrap_or_else()
to handle DST transition edge cases gracefully
On Windows, npx, npm, yarn, pnpm, node, bun, and deno are actually
.cmd batch files that require cmd /c wrapper to execute properly.
This fixes the Claude Code /doctor warning:
"Windows requires 'cmd /c' wrapper to execute npx"
The transformation is applied when exporting MCP config to ~/.claude.json:
- Before: {"command": "npx", "args": ["-y", "foo"]}
- After: {"command": "cmd", "args": ["/c", "npx", "-y", "foo"]}
Uses conditional compilation (#[cfg(windows)]) for zero overhead on
other platforms.
Closes#453
On Windows, opening the Settings > About section would spawn three
terminal windows when checking CLI tool versions (claude, codex, gemini).
Root cause:
- scan_cli_version() directly executed .cmd files, but child processes
(node.exe) spawned by these batch scripts didn't inherit CREATE_NO_WINDOW
- PATH separator used Unix-style ":" instead of Windows ";"
Fix:
- Wrap command execution with `cmd /C` to ensure all child processes
run within the same hidden console session
- Use platform-specific PATH separators via conditional compilation
Two bugs were caused by ref synchronization race condition in commit 7d495aa:
- EditProviderDialog: provider prop was null on first render
- UsageScriptModal: conditional render guard was false on first click
Root cause: useEffect updates ref asynchronously, but render happens before
effect runs. On first click, ref is still null causing components to fail.
Solution: Create useLastValidValue hook that updates ref synchronously during
render phase instead of in useEffect. This ensures ref is always in sync with
state, eliminating the race condition.
Changes:
- Add useLastValidValue hook for preserving last valid value during animations
- Replace manual ref + useEffect pattern with the new hook
- Remove non-null assertions (!) that were needed as workaround
* fix(schema): add missing base columns migration for proxy_config
Add compatibility migration for older databases that may be missing
the basic proxy_config columns (proxy_enabled, listen_address,
listen_port, enable_logging) before adding newer timeout fields.
* fix: add proxy_config base column patches for v3.9.0-2 upgrade
Add base config column patches in create_tables_on_conn():
- proxy_enabled
- listen_address
- listen_port
- enable_logging
Ensures v3.9.0-2 users (user_version=2 but missing columns)
can properly upgrade with all required fields added.
* fix: migrate proxy_config singleton to per-app on startup for v2 databases
Add startup migration for legacy proxy_config tables that still have
singleton structure (no app_type column) even with user_version=2.
This fixes the issue where v3.9.0-2 databases with v2 schema but legacy
proxy_config structure would fail with "no such column: app_type" error.
- Call migrate_proxy_config_to_per_app in create_tables_on_conn
- Add regression test to verify the fix
* style: cargo fmt
---------
Co-authored-by: Jason <farion1231@gmail.com>
* chore: 更新 vite 版本 && 使用 code-inspector-plugin 方便从前端定位到代码位置
* fix: update tailwind config path and conditionally load code-inspector-plugin
- Update components.json to reference tailwind.config.cjs instead of deleted tailwind.config.js
- Load codeInspectorPlugin only in dev mode to avoid unnecessary code in production builds
---------
Co-authored-by: Jason <farion1231@gmail.com>
* fix(mcp): 移除同步Codex Provider时的旧MCP同步调用
sync_enabled_to_codex使用旧的config.mcp.codex结构,
在v3.7.0统一结构中该字段为空,导致MCP配置被错误清除。
MCP同步应通过McpService进行。
Fixes#403
* test(mcp): update test to reflect new MCP sync architecture
Remove MCP-related assertions from sync_codex_provider_writes_auth_and_config
test since provider switching no longer triggers MCP sync in v3.7.0+.
MCP synchronization is now handled independently by McpService,
not as part of the provider switch flow.
---------
Co-authored-by: Jason <farion1231@gmail.com>
On macOS, the auto-launch library requires the .app bundle path (e.g.,
/Applications/CC Switch.app) rather than the binary path inside the bundle
(e.g., .app/Contents/MacOS/CC Switch). Using the binary path directly
causes AppleScript login items to open a terminal window.
This fix extracts the .app bundle path from current_exe() on macOS,
ensuring proper integration with macOS login items.
Closes#375
* feat: add Universal Provider feature
- Add Universal Provider data structures and type definitions
- Implement backend CRUD operations and sync functionality
- Add frontend UI components (UniversalProviderPanel, Card, FormModal)
- Add NewAPI icon and preset configuration
- Support cross-app (Claude/Codex/Gemini) configuration sync
- Add website URL field for providers
- Implement real-time refresh via event notifications
- Add i18n support (Chinese/English/Japanese)
* feat: integrate universal provider presets into add provider dialog
- Add universal provider presets (NewAPI, Custom Gateway) to preset selector
- Show universal presets with Layers icon badge in preset selector
- Open UniversalProviderFormModal when universal preset is clicked
- Pass initialPreset to auto-fill form when opened from add dialog
- Add i18n keys for addSuccess/addFailed messages
- Keep separate universal provider panel for management
* refactor: move universal provider management to add dialog
- Remove Layers button from main navigation header
- Add 'Manage' button next to universal provider presets
- Open UniversalProviderPanel from within add provider dialog
- Add i18n keys for 'manage' in all locales
* style: display universal provider presets on separate line
- Move universal provider section to a new row with border separator
- Add label '统一供应商:' to clarify the section
* style: unify universal provider label style with preset label
- Use FormLabel component for consistent styling
- Add background to 'Manage' button matching preset buttons
- Update icon size and button padding for consistency
* feat: add sync functionality and JSON preview for Universal Provider
* fix: add missing in_failover_queue field to Provider structs
After rebasing to main, the Provider struct gained a new
`in_failover_queue` field. This fix adds the missing field
to the three to_*_provider() methods in UniversalProvider.
* refactor: redesign AddProviderDialog with tab-based layout
- Add tabs to separate app-specific providers and universal providers
- Move "Add Universal Provider" button from panel header to footer
- Remove unused handleAdd callback and clean up imports
- Update emptyHint i18n text to reference the footer button
* fix: append /v1 suffix to Codex base_url in Universal Provider
Codex uses OpenAI-compatible API which requires the /v1 endpoint suffix.
The Universal Provider now automatically appends /v1 to base_url when
generating Codex provider config if not already present.
- Handle trailing slashes to avoid double slashes
- Apply fix to both backend (to_codex_provider) and frontend preview
* feat: auto-sync universal provider to apps on creation
Previously, users had to manually click sync after adding a universal
provider. Now it automatically syncs to Claude/Codex/Gemini on creation,
providing a smoother user experience.
---------
Co-authored-by: Jason <farion1231@gmail.com>
- Make all sponsor logos clickable links in README files (EN/ZH/JA)
- Replace ShanDianShuo with DMXAPI sponsor
- Add DMXAPI logo images (dmx-en.jpg, dmx-zh.jpeg)
- Unify sponsor list across all language versions