* 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#636Fixes#583
* fix(ui): sync toast theme with app setting
* feat(settings): add log config management
Fixes#612Fixes#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.
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
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
Use local state + onBlur pattern for ModelIdInput to keep React key
stable during editing. Previously, each keystroke changed the object
key, causing React to unmount/remount the input and lose focus.
OpenCode lacks a dedicated adapter and falls back to Codex adapter,
which has incompatible config structure. Hide the test button in UI
to prevent users from triggering unsupported operations.
OpenCode uses user-provided provider keys as IDs instead of random UUIDs.
When duplicating a provider, generate a unique key by appending "-copy"
suffix (or "-copy-2", "-copy-3", etc. if already exists).
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.
- Add Provider Key input field for OpenCode providers (between icon and name)
- User must manually enter a unique key instead of auto-generating from name
- Real-time validation: format check and duplicate detection
- Key is immutable after creation (disabled in edit mode)
- Remove slugify auto-generation logic from mutations
- Add beforeNameSlot prop to BasicFormFields for extensibility
- Add i18n translations for zh/en/ja
- 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
Separate the confirmation dialogs for "remove from config" and "delete
provider" operations in OpenCode mode to help users understand the
different impacts of each action.
For OpenCode (additive mode), use isInConfig instead of isCurrent to
determine whether to enable usage auto-query. This allows providers
that have been added to the config to have their usage queried
automatically.
OpenCode uses additive mode where the main "Remove" button removes from
live config, while the delete button should delete from database. The
delete button should always be enabled for OpenCode providers.
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
Previously Base URL was only shown for @ai-sdk/openai-compatible.
Now it's always visible to support proxy scenarios for official SDKs
like DeepSeek, Anthropic, etc.
OpenCode's read_live_settings returns the full opencode.json file
instead of just the provider fragment. This caused the edit dialog
to save the complete config structure as settingsConfig, creating
nested provider configurations.
For OpenCode's additive mode, use DB config directly since each
provider's config is stored independently.
- 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
- Add 19 new provider presets for OpenCode (cn_official, aggregator, third_party)
- Add OpenCode handling branch in handlePresetChange to properly populate
form fields (baseURL, apiKey, npm, models) when selecting a preset
- Add OpenCode reset logic in custom mode branch
Add missing OpenCode branch in parse_app_type() and include OpenCode
in all app iteration loops for skills operations (uninstall, scan,
import, migrate).
- Add `enabled` parameter to useCommonConfigSnippet hook
- Skip all loading and auto-merge logic when enabled=false
- Replace CommonConfigEditor with simplified JsonEditor for OpenCode
- Prevent Claude's common config snippet from being injected into OpenCode
- Increase SelectContent z-index from z-50 to z-[100] to appear above FullScreenPanel (z-[60])
- Replace form.watch() with form.getValues() in useCallback handlers for correct react-hook-form usage
- Remove max-w-[56rem] constraints from various panels for consistent full-width layout
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.
- Add opencode to AppType and SkillApps interfaces in skills.ts
- Add OpenCode Switch component to UnifiedMcpPanel list items
- Add OpenCode Switch component to UnifiedSkillsPanel list items
- Include OpenCode in enabled counts and header statistics for both panels
OpenCode uses additive provider management where providers can exist in
the database but not necessarily in the live opencode.json config. This
commit implements proper isInConfig state:
Backend:
- Add get_opencode_live_provider_ids command to query live config
Frontend:
- Add getOpenCodeLiveProviderIds API method
- ProviderList queries live provider IDs and computes isInConfig
- ProviderCard receives and passes isInConfig to ProviderActions
- ProviderActions already handles the add/remove button logic
OpenCode uses additive provider management (multiple providers coexist),
so proxy/failover features are not applicable. This commit:
- Conditionally renders ProxyToggle only for non-OpenCode apps
- Fixes toast message label to properly handle opencode app type
- 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
- 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
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.
Add mcp/opencode.rs with format conversion between CC Switch and OpenCode:
- stdio ↔ local type conversion
- command+args ↔ command array format
- env ↔ environment field mapping
- sse/http ↔ remote type conversion
Public API:
- sync_enabled_to_opencode: Batch sync all enabled servers
- sync_single_server_to_opencode: Sync individual server
- remove_server_from_opencode: Remove from live config
- import_from_opencode: Import servers from OpenCode config
Also fix test files to include new opencode field in McpApps struct.
All 4 unit tests pass for format conversion.
Add OpenCode-specific configuration structures for the AI SDK format:
- OpenCodeProviderConfig: Main config with npm package, options, and models
- OpenCodeProviderOptions: baseURL, apiKey, and headers support
- OpenCodeModel: Model definition with name and token limits
- OpenCodeModelLimit: Context and output token limits
Key features:
- Supports environment variable references (e.g., "{env:API_KEY}")
- Custom headers support for specialized providers
- Model-level token limits for context management
- Helper methods for parsing and validation
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 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>