Commit Graph

147 Commits

Author SHA1 Message Date
YoVinchen f2934daa14 merge: integrate main with OpenClaw support and resolve skill conflicts 2026-02-14 17:21:08 +08:00
YoVinchen 1ffbb32484 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
2026-02-14 17:08:36 +08:00
YoVinchen 74e84be1dd feat(skill): scan ~/.cc-switch/skills/ for unmanaged skill discovery and import 2026-02-14 15:55:16 +08:00
Jason 5081206176 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)
2026-02-14 15:32:17 +08:00
Jason bc87f9d9eb 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
2026-02-14 15:32:17 +08:00
Jason 2b1f0f0f7e 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)
2026-02-14 15:32:17 +08:00
Jason 392344e1e9 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.
2026-02-14 15:31:58 +08:00
Jason e85878e95a 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)
2026-02-14 15:31:58 +08:00
Jason 433c86b2d3 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
2026-02-14 15:31:58 +08:00
clx 6098fa7536 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>
2026-02-14 15:24:24 +08:00
YoVinchen c3ef4b4660 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.
2026-02-14 14:44:27 +08:00
YoVinchen 74dcc0bb3e 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
2026-02-14 14:17:53 +08:00
YoVinchen 6d5cb9407d 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
2026-02-14 10:54:31 +08:00
Dex Miller caba5f51ec 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
2026-02-12 21:30:59 +08:00
Dex Miller ea20b0aec2 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
2026-02-09 12:30:17 +08:00
Dex Miller ed47480fc7 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
2026-02-09 12:18:20 +08:00
Dex Miller 87b80c66b2 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
2026-02-06 22:00:33 +08:00
Jassy930 58153333ce 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>
2026-02-02 09:28:04 +08:00
Jason 70a18c1141 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
2026-01-30 16:40:41 +08:00
Jason 987fc46e06 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
2026-01-29 10:09:45 +08:00
Andrew Leng 0dd823ae3a 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>
2026-01-29 09:10:17 +08:00
Dex Miller 785e1b5add 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>
2026-01-27 10:43:05 +08:00
Jason c00f431d67 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
2026-01-26 15:46:51 +08:00
Dex Miller 3434dcb87c 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
2026-01-25 23:35:04 +08:00
Jason 779fefd86d 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.
2026-01-24 18:24:05 +08:00
Jason adb868d0cf 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.
2026-01-23 22:43:07 +08:00
Jason 2d17bde790 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
2026-01-21 16:20:28 +08:00
Jason a187380d6f 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
2026-01-21 11:27:44 +08:00
Dex Miller e7badb1a24 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.
2026-01-20 21:02:44 +08:00
Jason ad6f5b388b 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
2026-01-17 17:51:32 +08:00
Jason 88dbeb5335 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
2026-01-17 15:24:09 +08:00
Jason 2f0998c6c8 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
2026-01-17 11:16:58 +08:00
Jason 2cc36b3950 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).
2026-01-16 21:23:54 +08:00
Jason de3a22535d 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
2026-01-15 19:07:49 +08:00
Jason 7997b2c7b3 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
2026-01-15 16:20:03 +08:00
Jason b8538b211d 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.
2026-01-15 16:15:01 +08:00
Jason 5658d93924 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)
2026-01-15 15:54:29 +08:00
杨永安 07d022ba9f 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>
2026-01-14 15:42:05 +08:00
Dex Miller f9d80b8dc3 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
2026-01-13 11:36:19 +08:00
Dex Miller 8b92982112 Feature/global proxy (#596)
* refactor(proxy): simplify logging for better readability

- Delete 17 verbose debug logs from handlers, streaming, and response_processor
- Convert excessive INFO logs to DEBUG level for internal processing details
- Add 2 critical INFO logs in forwarder.rs for failover scenarios:
  - Log when switching to next provider after failure
  - Log when all providers have been exhausted
- Fix clippy uninlined_format_args warning

This reduces log noise while maintaining visibility into key user-facing decisions.

* fix: replace unsafe unwrap() calls with proper error handling

- database/dao/mcp.rs: Use map_err for serde_json serialization
- database/dao/providers.rs: Use map_err for settings_config and meta serialization
- commands/misc.rs: Use expect() for compile-time regex pattern
- services/prompt.rs: Use unwrap_or_default() for SystemTime
- deeplink/provider.rs: Replace unwrap() with is_none_or pattern for Option checks

Reduces potential panic points from 26 to 1 (static regex init, safe).

* refactor(proxy): simplify verbose logging output

- Remove response JSON full output logging in response_processor
- Remove per-request INFO logs in provider_router (failover status, provider selection)
- Change model mapping log from INFO to DEBUG
- Change usage logging failure from INFO to WARN
- Remove redundant debug logs for circuit breaker operations

Reduces log noise significantly while preserving important warnings and errors.

* feat(proxy): add structured log codes for i18n support

Add error code system to proxy module logs for multi-language support:

- CB-001~006: Circuit breaker state transitions and triggers
- SRV-001~004: Proxy server lifecycle events
- FWD-001~002: Request forwarding and failover
- FO-001~005: Failover switch operations
- USG-001~002: Usage logging errors

Log format: [CODE] Chinese message
Frontend/log tools can map codes to any language.

New file: src/proxy/log_codes.rs - centralized code definitions

* chore: bump version to 3.9.1

* style: format code with prettier and rustfmt

* fix(ui): allow number inputs to be fully cleared before saving

- Convert numeric state to string type for controlled inputs
- Use isNaN() check instead of || fallback to allow 0 values
- Apply fix to ProxyPanel, CircuitBreakerConfigPanel,
  AutoFailoverConfigPanel, and ModelTestConfigPanel

* feat(pricing): support @ separator in model name matching

- Refactor model name cleaning into chained method calls
- Add @ to - replacement (e.g., gpt-5.2-codex@low → gpt-5.2-codex-low)
- Add test case for @ separator matching

* feat(proxy): add global proxy settings support

Add ability to configure a global HTTP/HTTPS proxy for all outbound
requests including provider API calls, speed tests, and stream checks.

* fix(proxy): improve validation and error handling in proxy config panels

- Add StopTimeout/StopFailed error types for proper stop() error reporting
- Replace silent clamp with validation-and-block in config panels
- Add listenAddress format validation in ProxyPanel
- Use log_codes constants instead of hardcoded strings
- Use once_cell::Lazy for regex precompilation

* fix(proxy): harden error handling and input validation

- Handle RwLock poisoning in settings.rs with unwrap_or_else
- Add fallback for dirs::home_dir() in config modules
- Normalize localhost to 127.0.0.1 in ProxyPanel
- Format IPv6 addresses with brackets for valid URLs
- Strict port validation with pure digit regex
- Treat NaN as validation failure in config panels
- Log warning on cost_multiplier parse failure
- Align timeoutSeconds range to [0, 300] across all panels

* feat(proxy): add local proxy auto-scan and fix hot-reload

- Add scan_local_proxies command to detect common proxy ports
- Fix SkillService not using updated proxy after hot-reload
- Move global proxy settings to advanced tab
- Add error handling for scan failures

* fix(proxy): allow localhost input in proxy address field

* fix(proxy): restore request timeout and fix proxy hot-reload issues

- Add URL scheme validation in build_client (http/https/socks5/socks5h)
- Restore per-request timeout for speedtest, stream_check, usage_script, forwarder
- Fix set_global_proxy_url to validate before persisting to DB
- Mask proxy credentials in all log outputs
- Fix forwarder hot-reload by fetching client on each request

* style: format code with prettier

* fix(proxy): improve global proxy stability and error handling

- Fix RwLock silent failures with explicit error propagation
- Handle init() duplicate calls gracefully with warning log
- Align fallback client config with build_client settings
- Make scan_local_proxies async to avoid UI blocking
- Add mixed mode support for Clash 7890 port (http+socks5)
- Use multiple test targets for better proxy connectivity test
- Clear invalid proxy config on init failure
- Restore timeout constraints in usage_script
- Fix mask_url output for URLs without port
- Add structured error codes [GP-001 to GP-009]

* feat(proxy): add username/password authentication support

- Add separate username and password input fields
- Implement password visibility toggle with eye icon
- Add clear button to reset all proxy fields
- Auto-extract auth info from saved URL and merge on save
- Update i18n translations (zh/en/ja)

* fix(proxy): fix double encoding issue in proxy auth and add debug logs

- Remove encodeURIComponent in mergeAuth() since URL object's
  username/password setters already do percent-encoding automatically
- Add GP-010 debug log for database read operations
- Add GP-011 debug log to track incoming URL info (length, has_auth)
- Fix username.trim() in fallback branch for consistent behavior
2026-01-13 10:55:53 +08:00
Dex Miller 8f7423f011 Feat/deeplink multi endpoints (#597)
* feat(deeplink): support comma-separated multiple endpoints in URL

Allow importing multiple API endpoints via single endpoint parameter.
First URL becomes primary endpoint, rest are added as custom endpoints.

* feat(deeplink): add usage query fields to deeplink generator

Add form fields for usage query configuration in deeplink HTML generator:
- usageEnabled, usageBaseUrl, usageApiKey
- usageScript, usageAutoInterval
- usageAccessToken, usageUserId

* fix(deeplink): auto-infer homepage and improve multi-endpoint display

- Auto-infer homepage from primary endpoint when not provided
- Display multiple endpoints as list in import dialog (primary marked)
- Update deeplink parser in deplink.html to show multi-endpoint info
- Add test for homepage inference from endpoint
- Minor log format fix in live.rs

* fix(deeplink): use primary endpoint for usage script base_url

- Fix usage_script.base_url getting comma-separated string when multiple endpoints
- Add i18n support for primary endpoint label in DeepLinkImportDialog
2026-01-12 15:57:45 +08:00
Dex Miller 6dd809701b Refactor/simplify proxy logs (#585)
* refactor(proxy): simplify logging for better readability

- Delete 17 verbose debug logs from handlers, streaming, and response_processor
- Convert excessive INFO logs to DEBUG level for internal processing details
- Add 2 critical INFO logs in forwarder.rs for failover scenarios:
  - Log when switching to next provider after failure
  - Log when all providers have been exhausted
- Fix clippy uninlined_format_args warning

This reduces log noise while maintaining visibility into key user-facing decisions.

* fix: replace unsafe unwrap() calls with proper error handling

- database/dao/mcp.rs: Use map_err for serde_json serialization
- database/dao/providers.rs: Use map_err for settings_config and meta serialization
- commands/misc.rs: Use expect() for compile-time regex pattern
- services/prompt.rs: Use unwrap_or_default() for SystemTime
- deeplink/provider.rs: Replace unwrap() with is_none_or pattern for Option checks

Reduces potential panic points from 26 to 1 (static regex init, safe).

* refactor(proxy): simplify verbose logging output

- Remove response JSON full output logging in response_processor
- Remove per-request INFO logs in provider_router (failover status, provider selection)
- Change model mapping log from INFO to DEBUG
- Change usage logging failure from INFO to WARN
- Remove redundant debug logs for circuit breaker operations

Reduces log noise significantly while preserving important warnings and errors.

* feat(proxy): add structured log codes for i18n support

Add error code system to proxy module logs for multi-language support:

- CB-001~006: Circuit breaker state transitions and triggers
- SRV-001~004: Proxy server lifecycle events
- FWD-001~002: Request forwarding and failover
- FO-001~005: Failover switch operations
- USG-001~002: Usage logging errors

Log format: [CODE] Chinese message
Frontend/log tools can map codes to any language.

New file: src/proxy/log_codes.rs - centralized code definitions

* chore: bump version to 3.9.1

* style: format code with prettier and rustfmt

* fix(ui): allow number inputs to be fully cleared before saving

- Convert numeric state to string type for controlled inputs
- Use isNaN() check instead of || fallback to allow 0 values
- Apply fix to ProxyPanel, CircuitBreakerConfigPanel,
  AutoFailoverConfigPanel, and ModelTestConfigPanel

* feat(pricing): support @ separator in model name matching

- Refactor model name cleaning into chained method calls
- Add @ to - replacement (e.g., gpt-5.2-codex@low → gpt-5.2-codex-low)
- Add test case for @ separator matching

* fix(proxy): improve validation and error handling in proxy config panels

- Add StopTimeout/StopFailed error types for proper stop() error reporting
- Replace silent clamp with validation-and-block in config panels
- Add listenAddress format validation in ProxyPanel
- Use log_codes constants instead of hardcoded strings
- Use once_cell::Lazy for regex precompilation

* fix(proxy): harden error handling and input validation

- Handle RwLock poisoning in settings.rs with unwrap_or_else
- Add fallback for dirs::home_dir() in config modules
- Normalize localhost to 127.0.0.1 in ProxyPanel
- Format IPv6 addresses with brackets for valid URLs
- Strict port validation with pure digit regex
- Treat NaN as validation failure in config panels
- Log warning on cost_multiplier parse failure
- Align timeoutSeconds range to [0, 300] across all panels
2026-01-11 20:50:54 +08:00
Jason 76fa830688 fix(live): sync skills to app directories on config path change
When users change app config directories (claudeConfigDir, codexConfigDir,
geminiConfigDir), MCP servers were being synced to the new paths but Skills
were not. This adds Skill synchronization to sync_current_to_live() to ensure
installed Skills are also copied to the new app directories.
2026-01-11 16:39:50 +08:00
Jason Young a268127f1f Fix/Resolve panic issues in proxy-related code (#560)
* fix(proxy): change default port from 5000 to 15721

Port 5000 conflicts with AirPlay Receiver on macOS 12+.
Also adds error handling for proxy toggle and i18n placeholder updates.

* fix(proxy): replace unwrap/expect with graceful error handling

- Handle HTTP client initialization failure with no_proxy fallback
- Fix potential panic on Unicode slicing in API key preview
- Add proper error handling for response body builder
- Handle edge case where SystemTime is before UNIX_EPOCH

* fix(proxy): handle UTF-8 char boundary when truncating request body log

Rust strings are UTF-8 encoded, slicing at a fixed byte index may cut
in the middle of a multi-byte character (e.g., Chinese, emoji), causing
a panic. Use is_char_boundary() to find the nearest safe cut point.

* fix(proxy): improve robustness and prevent panics

- Add reqwest socks feature to support SOCKS proxy environments
- Fix UTF-8 safety in masked_key/masked_access_token (use chars() instead of byte slicing)
- Fix UTF-8 boundary check in usage_script HTTP response truncation
- Add defensive checks for JSON operations in proxy service
- Remove verbose debug logs that could trigger panic-prone code paths
2026-01-09 13:09:19 +08:00
Dex Miller 847f1c5377 Feat/proxy header improvements (#538)
* fix(proxy): improve header handling for Claude API compatibility

- Streamline header blacklist by removing overly aggressive filtering
  (browser-specific headers like sec-fetch-*, accept-language)
- Ensure anthropic-beta header always includes 'claude-code-20250219'
  marker required by upstream services for request validation
- Centralize anthropic-version header handling in forwarder to prevent
  duplicate headers across different auth strategies
- Add ?beta=true query parameter to /v1/messages endpoint for
  compatibility with certain upstream services (e.g., DuckCoding)
- Remove redundant anthropic-version from ClaudeAdapter auth headers
  as it's now managed exclusively by the forwarder

This improves proxy reliability with various Claude API endpoints
and third-party services that have specific header requirements.

* style(services): use inline format arguments in format strings

Apply Rust 1.58+ format string syntax across provider and skill
services. This replaces format!("msg {}", var) with format!("msg {var}")
for improved readability and consistency with modern Rust idioms.

Changed files:
- services/provider/mod.rs: 1 format string
- services/skill.rs: 10 format strings (error messages, log statements)

No functional changes, purely stylistic improvement.

* fix(proxy): restrict Anthropic headers to Claude adapter only

- Move anthropic-beta and anthropic-version header handling inside
  Claude-specific condition to avoid sending unnecessary headers
  to Codex and Gemini APIs
- Update test cases to reflect ?beta=true query parameter behavior
- Add edge case tests for non-messages endpoints and existing queries
2026-01-08 11:04:42 +08:00
Jason 8979d964d6 fix(proxy): clean up model override env vars when switching providers in takeover mode
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
2026-01-04 16:09:37 +08:00
Jason 2c35372ca0 fix(codex): remove entire model_providers table from common config extraction
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.
2026-01-04 12:27:22 +08:00
Jason 63bb673bf2 refactor(common-config): extract snippet from editor content instead of active provider
- 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
2026-01-04 12:27:22 +08:00
Jason 058f86aff3 fix(codex): prevent extract_common_config from removing MCP servers' base_url
- 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
2026-01-04 12:27:22 +08:00
Jason 188c94f2e3 feat(common-config): add extract from current provider functionality
- 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
2026-01-04 12:27:22 +08:00