Commit Graph

916 Commits

Author SHA1 Message Date
Jason 687ffc237d feat: add per-app usage filtering (Claude/Codex/Gemini)
Add dashboard-level app type filter to usage statistics, replacing the
DataSourceBar with a more useful segmented control. All components
(summary cards, trend chart, provider stats, model stats, request logs)
now respond to the selected app filter.

Backend: add optional app_type parameter to get_usage_summary,
get_daily_trends, get_provider_stats, and get_model_stats queries.
Frontend: new AppTypeFilter type, updated query keys with appType
dimension for proper cache separation, and RequestLogTable local
filter auto-locks when dashboard filter is active.
2026-04-09 16:49:13 +08:00
Jason f5d7064d57 feat: add Gemini CLI session log usage tracking
Parse ~/.gemini/tmp/*/chats/session-*.json for precise per-message
token data (input/output/cached/thoughts). Integrates with existing
background sync and manual sync button alongside Claude and Codex.
2026-04-09 16:49:13 +08:00
Jason 2ea6a307fc feat: replace Codex estimated usage with precise JSONL session log parsing
Replace the 70/30 input/output token estimation from state_5.sqlite
with precise parsing of Codex CLI JSONL session logs (~/.codex/sessions/).

- Parse event_msg (token_count), turn_context, and session_meta events
- Compute exact input/output/cached token deltas from cumulative totals
- Reuse session_log_sync table for incremental file scanning
- Pre-filter lines with string contains() before JSON deserialization
- Add codex_session data source to DataSourceBar with i18n (zh/en/ja)
2026-04-09 16:49:13 +08:00
Jason 154342ca00 feat: add session log usage tracking without proxy
Parse Claude Code JSONL session files (~/.claude/projects/) and Codex
SQLite database (~/.codex/state_5.sqlite) to track API usage without
requiring proxy interception. This enables usage statistics for users
who don't use the proxy feature.

Key changes:
- Add session_usage.rs: incremental JSONL parser with message.id dedup
- Add session_usage_codex.rs: import thread-level token data from Codex
- Add data_source column to proxy_request_logs (proxy/session_log/codex_db)
- Add session_log_sync table for tracking parse offsets
- Background sync every 60s + manual sync via DataSourceBar UI
- Schema migration v7→v8
- i18n support for zh/en/ja
2026-04-09 16:49:13 +08:00
Jason 2d581bce91 fix: hide empty description and fix broken skill link for skills.sh results
- Hide "暂无描述" text when skill has no description (skills.sh API
  doesn't return descriptions), show empty spacer instead
- Change skills.sh result link from guessed subdirectory path to repo
  root URL, since skillId doesn't reflect the actual nested path
2026-04-09 16:49:13 +08:00
Jason d51e774b20 feat: integrate skills.sh search for discovering skills from public registry
Add skills.sh API integration allowing users to search and install from
a catalog of 91K+ agent skills directly within CC Switch. The search
results are converted to DiscoverableSkill objects and reuse the existing
install pipeline. Includes fallback directory search for repos where
skills are nested in subdirectories, and filters out non-GitHub sources.
2026-04-09 16:49:13 +08:00
Jason 33f5d56afd fix: move skill settings above window settings in general tab 2026-04-09 16:49:13 +08:00
Jason 8cfce8abfc feat: add skill storage location toggle between CC Switch and ~/.agents/skills
Allow users to choose between storing skills in CC Switch's managed
directory (~/.cc-switch/skills/) or the Agent Skills open standard
directory (~/.agents/skills/). Includes migration logic that safely
moves files before updating settings, with confirmation dialog for
non-empty installations.
2026-04-09 16:49:13 +08:00
Jason 6d220b2528 fix: animate "Update All" button sliding in from the left of "Check Updates"
Use max-width + opacity CSS transition so the button smoothly expands
into view instead of popping in abruptly.
2026-04-09 16:49:13 +08:00
Jason b7e99014c9 feat: add "Update All" button for batch skill updates
Show an "Update All (N)" button next to "Check Updates" when updates
are available. Sequentially updates each skill and reports results.
2026-04-09 16:49:13 +08:00
Jason e3179ad9e4 feat: add skill update detection via SHA-256 content hashing
- Add content_hash and updated_at fields to skills table (DB migration v6→v7)
- Compute directory content hash on install/import/restore for version tracking
- Add check_updates command: downloads repos, compares hashes, returns update list
- Add update_skill command: backs up old files, re-downloads and replaces SSOT
- Backfill content_hash for existing skills on first update check
- Add "Check Updates" button and per-skill update badge/button in UnifiedSkillsPanel
- Add i18n keys for zh/en/ja
2026-04-09 16:49:13 +08:00
Jason 46488ecd93 fix: set default auto-query interval to 5min and fix number input clearing
- Change default autoQueryInterval from 0 (disabled) to 5 minutes for
  new usage scripts (Token Plan, Balance, and general templates)
- Fix controlled number inputs (timeout & interval) that couldn't be
  cleared: defer validation to onBlur so users can delete and retype
2026-04-09 16:49:13 +08:00
Jason 9192b6f988 fix: align usage display across provider cards by always rendering action buttons
Test and ConfigureUsage buttons are now always rendered instead of
conditionally, with a disabled style for providers that don't support
them (e.g. official subscriptions). This ensures consistent button
container width so the usage display aligns uniformly across all cards.
2026-04-09 16:49:13 +08:00
Jason f1fb3351c1 feat: add official balance query for DeepSeek, StepFun, SiliconFlow, OpenRouter, Novita AI
Add a new "Official" (官方) template type in the usage query panel that
queries account balance via each provider's native API endpoint.
Follows the same zero-script pattern as Token Plan — Rust handles the
HTTP call, frontend auto-detects the provider from base URL.

Supported providers and endpoints:
- DeepSeek: GET /user/balance
- StepFun: GET /v1/accounts
- SiliconFlow: GET /v1/user/info (cn + com)
- OpenRouter: GET /api/v1/credits
- Novita AI: GET /v3/user/balance
2026-04-09 16:49:13 +08:00
Satoru 24555275bb fix: add padding to toolbar container to prevent add button shadow clipping (#1951)
The orange add button's circular shadow appeared square because the
parent overflow-x-hidden container was clipping it at the edges.
2026-04-08 21:17:19 +08:00
Jason ca6a187745 fix: correct MiniMax quota calculation and improve Token Plan display
- Fix MiniMax usage_count being treated as remaining (was inverted)
- Add MiniMax weekly quota tier extraction
- Remove Zhipu TIME_LIMIT (tools usage), keep only TOKENS_LIMIT
- Improve Kimi parsing with extract_reset_time and parse_f64 helpers
- Reuse TierBadge for Token Plan inline rendering
- Clean up unused i18n keys and debug println
2026-04-05 13:35:54 +08:00
Jason bfdac2a22a feat: add Token Plan quota query for Kimi, Zhipu GLM, and MiniMax
Add a new "Token Plan" template type in the usage query panel that
natively queries quota/usage from Chinese coding plan providers
(Kimi For Coding, Zhipu GLM, MiniMax) without requiring custom scripts.

- Rust backend: new coding_plan service with provider-specific API
  queries (Kimi /v1/usages, Zhipu /api/monitor/usage/quota/limit,
  MiniMax /coding_plan/remains) normalized into UsageResult
- Frontend: Token Plan template in UsageScriptModal with auto-detection
  of provider based on ANTHROPIC_BASE_URL pattern matching
- Follows the same pattern as GitHub Copilot template (dedicated API
  path in queryProviderUsage, no JS script needed)
2026-04-05 11:39:29 +08:00
Jason 8d38f0ee4f fix: allow provider switch without proxy, show warning instead of blocking
Remove the hard block that prevented switching to providers requiring
proxy (OpenAI format, Copilot, full URL mode) when the proxy is not
running. Now the switch proceeds with a warning toast. Also deduplicate
the proxy hint info toast so it doesn't appear alongside the warning.
2026-04-05 09:39:51 +08:00
Jason 159279bf44 feat: restore Copilot preset and auth center tab in settings
Re-enable GitHub Copilot provider preset and the OAuth auth center tab
that were temporarily hidden due to abnormal consumption rates. The
Copilot optimizer introduced in the previous commit addresses the
underlying issue.
2026-04-05 08:34:16 +08:00
Jason bcc14bd07d fix: remove hover push animation on provider cards
Keep usage display fixed in place and show action buttons with
a simple opacity fade instead of sliding in and pushing content.
2026-04-04 22:53:20 +08:00
Jason b31bf43e15 fix: hide usage config and health check buttons for official providers
Official providers use built-in subscription quota display instead of
custom usage scripts, and stream check is not applicable. Hide both
action buttons when isOfficialProvider is true.
2026-04-04 22:53:20 +08:00
Jason 98230c3970 feat: add official subscription quota display for Gemini
- Read Gemini OAuth credentials from macOS Keychain (gemini-cli-oauth)
  or legacy file (~/.gemini/oauth_creds.json)
- Auto-refresh expired access tokens using refresh_token (Google OAuth
  tokens expire in ~1h, unlike Claude/Codex)
- Two-step API: loadCodeAssist for project ID, then retrieveUserQuota
  for per-model quota buckets
- Classify models into Pro/Flash/Flash Lite categories, show min
  remaining fraction as utilization percentage
- Extend isOfficialProvider() for Gemini (no API key + no base URL)
- Parameterize expiredHint i18n key with tool name for all three apps
2026-04-04 22:53:20 +08:00
Jason 0200fe79ae feat: add official subscription quota display for Codex
Read Codex OAuth credentials from ~/.codex/auth.json (with macOS
Keychain fallback) and query chatgpt.com/backend-api/wham/usage to
show rate limit utilization on official Codex provider cards. Reuses
the same tier naming (five_hour, seven_day) for frontend i18n compat.
2026-04-04 22:53:20 +08:00
Jason b30f3c27ad feat: display official subscription quota on Claude provider cards
Read Claude OAuth credentials from macOS Keychain (with file fallback)
and query the Anthropic usage API to show quota utilization inline on
official provider cards. Includes compact countdown timer for reset
windows and hides the rarely-used seven_day_sonnet tier in inline mode.
2026-04-04 22:53:20 +08:00
Jason 84998aa217 feat: differentiate fetch models error messages by failure type
Distinguish between missing API key, missing endpoint, auth failure,
unsupported provider (404/405), and timeout errors instead of showing
a generic failure toast for all cases.
2026-04-04 22:53:20 +08:00
Jason f200feebe4 fix(i18n): move fetchModels keys from copilot to providerForm namespace 2026-04-04 22:53:20 +08:00
Jason 5017002938 feat: add auto-fetch models from provider's /v1/models endpoint
Add ability to fetch available models from third-party aggregation
providers (SiliconFlow, OpenRouter, etc.) via OpenAI-compatible
GET /v1/models endpoint. Users can click "Fetch Models" button in
the provider form, then select models from a dropdown on each
model input field.

- Backend: new model_fetch service + Tauri command (Rust)
- Frontend: ModelInputWithFetch shared component
- Integrated into all 5 app forms (Claude/Codex/Gemini/OpenCode/OpenClaw)
- i18n support for zh/en/ja
2026-04-04 22:53:20 +08:00
Zhou Mengze de49f6fbbe fix(copilot): 修复 GitHub Copilot 认证和代理问题 (#1854)
* fix(copilot): 修复 GitHub Copilot 400 认证错误

问题:使用 GitHub Copilot provider 时报错 400 bad request

根因:与 copilot-api 项目对比发现多处差异

修复内容:
- 更新版本号 0.26.7 到 0.38.2
- 更新 API 版本 2025-04-01 到 2025-10-01
- 添加缺失的关键 headers
- 修正 openai-intent 值
- 添加动态 API endpoint 支持
- 同步更新 stream_check.rs headers

Closes #1777

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: flush stream after write_all in hyper_client proxy

Add explicit flush() calls after write_all() for TLS stream, plain TCP
stream, and CONNECT tunnel requests to ensure buffered data is sent
immediately, preventing connection hangs in Copilot auth header flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* 修复登录时的剪切板在mac与linux端可能没复制验证码

* fix: flush stream after write_all in hyper_client proxy

Add explicit flush() calls after write_all() for TLS stream, plain TCP
stream, and CONNECT tunnel requests to ensure buffered data is sent
immediately, preventing connection hangs in Copilot auth header flow.

* 修复登录时的剪切板在mac与linux端可能没复制验证码

* 1、修复不同类型的个人商业等不同类型的copilot账号问题
2、将验证码复制改为异步操作

* fix: address PR review comments for Copilot auth                                                      │
│                                                                                                                      │
│ - Fix clipboard blocking by using spawn_blocking for arboard ops                                                     │
│ - Implement dynamic endpoint routing for enterprise Copilot users                                                    │
│ - Add api_endpoints cache cleanup in remove_account() and clear_auth()                                               │
│ - Change API endpoint log level from info to debug                                                                   │
│ - Fix clear_auth() to continue cleanup even if file deletion fails                                                   │
│ - Add 9 unit tests for Copilot detection and api_endpoints cachin

* style: fix cargo fmt formatting

* Fix Copilot dynamic endpoint handling

* fix: restore clear_auth() memory-first cleanup order and fix cache leaks

- Restore clear_auth() to clean memory state before deleting the storage
  file. The previous order (file deletion first) caused a regression where
  users could get stuck in a "cannot log out" state if file removal failed.

- Add missing copilot_models.clear() in clear_auth() — this cache was
  cleaned in remove_account() but never in the full clear path.

- Add endpoint_locks cleanup in both remove_account() and clear_auth()
  to prevent minor in-process memory leaks.

- Update test to assert the correct behavior: memory should be cleaned
  even when file deletion fails.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: 周梦泽 <mengze.zhou@dafeng-tech.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jason <farion1231@gmail.com>
2026-04-04 22:52:23 +08:00
Jason bfe238f301 style: fix formatting issues caught by CI 2026-04-02 17:32:33 +08:00
Jason fee8577032 feat: hide GitHub Copilot provider preset and auth tab
Users reported that Copilot support causes excessively fast token
consumption. Temporarily hide the feature by adding a `hidden` field
to ProviderPreset interface and commenting out the auth center tab
in settings. Existing Copilot providers in DB still work via proxy.
2026-04-02 17:15:11 +08:00
ifNil 2f5bf1cea9 fix: avoid resetting usage query fields when editing extractor code (#1771) 2026-04-02 09:25:33 +08:00
Dex Miller 3553d05bf0 feat(provider): additive provider key lifecycle & fix openclaw serializer panic (#1724)
* feat(provider): support additive provider key lifecycle management

Add `addToLive` parameter to add_provider so callers can opt out of
writing to the live config (e.g. when duplicating an inactive provider).
Add `originalId` parameter to update_provider to support provider key
renames — the old key is removed from live config before the new one
is written.

Frontend: ProviderForm now exposes provider-key input for openclaw app
type, and EditProviderDialog forwards originalId on save. Deep-link
import passes addToLive=true to preserve existing behavior.

* test(provider): add integration tests for additive provider key flows

Cover openclaw provider duplication scenario to verify that a generated
provider key is assigned automatically. Add MSW handlers for
get_openclaw_live_provider_ids, get_openclaw_default_model,
scan_openclaw_config_health, and check_env_conflicts endpoints.
Update EditProviderDialog mock to pass originalId alongside provider.

* fix(openclaw): replace json-five serializer to prevent panic on empty collections

json-five 0.3.1 panics when pretty-printing nested empty maps/arrays.
Switch value_to_rt_value() to serde_json::to_string_pretty() which
produces valid JSON5 output without the panic. Add regression test for
removing the last provider (empty providers map).

* style: apply rustfmt formatting to proxy and provider modules

Reformat chained .header() calls in ClaudeAdapter and StreamCheckService
for consistent alignment. Reorder imports alphabetically in stream_check.
Fix trailing whitespace in transform.rs and merge import lines in
provider/mod.rs.

* style: fix clippy warnings in live.rs and tray.rs

* refactor(provider): simplify live_config_managed and deduplicate tolerant live config checks

- Change live_config_managed from Option<bool> to bool with #[serde(default)]
- Extract repeated tolerant live config query into check_live_config_exists helper
- Fix duplicate key generation to also check live-only provider IDs
- Fix updateProvider test to match new { provider, originalId } call signature
- Add streaming_responses test type annotation for compiler inference

* fix(provider): distinguish legacy providers from db-only when tolerating live config errors

Change `ProviderMeta.live_config_managed` from `bool` to `Option<bool>`
to introduce a three-state semantic:
- `Some(true)`: provider has been written to live config
- `Some(false)`: explicitly db-only, never written to live config
- `None`: legacy data or unknown state (pre-existing providers)

Previously, legacy providers defaulted to `live_config_managed = false`
via `#[serde(default)]`, which silently swallowed live config parse
errors. This could mask genuine configuration issues for providers that
had actually been synced to live config before the field was introduced.

Now, only providers with an explicit `Some(false)` marker tolerate parse
errors; legacy `None` providers surface errors as before, preserving
safety for already-managed configurations.

Also wrap the `ensureQueryData` call for live provider IDs during
duplication in a try/catch so that a malformed config file shows a
user-facing toast instead of silently failing.

Add tests for both the legacy error propagation path and the frontend
duplication failure scenario.

* refactor(provider): unify OMO variant updates with atomic file-then-db writes and rollback

Consolidate the duplicated omo/omo-slim update branches into a single
match on the variant. Write the OMO config file from the in-memory
provider state *before* persisting to the database, so a file-write or
plugin-sync failure leaves the database unchanged. If `add_plugin`
fails after the config file is already written, roll back to the
previous on-disk contents via snapshot/restore.

Also:
- `sync_all_providers_to_live` now skips db-only providers
  (`live_config_managed == Some(false)`) instead of attempting to write
  them to live config.
- `import_{opencode,openclaw}_providers_from_live` mark imported
  providers as `live_config_managed: Some(true)` so they are correctly
  recognized during subsequent syncs.
- Extract OmoService helpers: `profile_data_from_provider`,
  `snapshot_config_file`, `restore_config_file`, `write_profile_config`,
  and the new public `write_provider_config_to_file`.
- Add 9 new tests covering sync skip, legacy restore, import marking,
  OMO persistence, file-write failure, and plugin-sync rollback.

* fix(provider): fix additive provider delete/switch regressions and redundancy

- fix(delete): replace stale live_config_managed flag check with
  check_live_config_exists so providers written to live before the
  flag-flip logic was introduced are still cleaned up on delete
- fix(switch): make write_live_with_common_config return Err instead of
  silently returning Ok when config structure is invalid, preventing
  live_config_managed from being incorrectly flipped to true
- fix(update): block provider key rename for OMO/OMO Slim categories to
  prevent orphaned current-state markers breaking OMO file syncs
- fix(switch): flip live_config_managed to true after successful live
  write for DB-only additive providers so sync_all_providers_to_live
  includes them on future syncs; roll back live write if DB update fails
- refactor(delete): merge symmetric OMO/OMO-Slim blocks into single
  match-on-variant path; hoist DB read to top of additive branch
- refactor(remove_from_live_config): merge OMO/OMO-Slim if/else-if
  into single match-on-variant path
- refactor(switch_normal): merge two OMO/OMO-Slim if blocks into one
  OpenCode guard with (enable, disable) variant pair
- fix(update): remove redundant duplicate return Ok(true) after OMO
  current-state write

* fix(test): use preferred_filename after OMO field rename

The merge from main brought in #1746 which renamed
OmoVariant.filename → preferred_filename, but the test helper
omo_config_path() was not updated, breaking compilation of all
new provider tests.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-04-01 21:16:41 +08:00
Dex Miller 6a083cdd1c fix(omo): adapt to oh-my-openagent rename with backward compatibility (#1746)
* fix(omo): adapt to oh-my-openagent rename with backward compatibility

Closes https://github.com/farion1231/cc-switch/issues/1733

* fix(omo): prioritize oh-my-openagent config over legacy oh-my-opencode
2026-03-31 16:32:49 +08:00
Dex Miller c68476d0ea fix(provider): show persistent config highlight for additive-mode providers (#1747)
* fix(provider): show persistent config highlight for additive-mode providers

Closes https://github.com/farion1231/cc-switch/issues/1692

* fix(provider): limit persistent config highlight to opencode only
2026-03-31 16:21:04 +08:00
Dex Miller 96a0a37331 feat(terminal): add directory picker before launching Claude terminal (#1752)
* Add directory picker before launching Claude terminal

* fix(terminal): preserve cwd path and strip Windows verbatim prefix

- Stop trimming non-empty paths so directories with leading/trailing
  spaces on Unix are handled correctly
- Strip \\?\ extended-length prefix from canonicalized paths on Windows
  to prevent batch script cd failures

* fix(terminal): restore UNC paths when stripping Windows verbatim prefix

Handle \\?\UNC\server\share form separately from regular \\?\ prefix,
converting it back to \\server\share so network/WSL directory paths
remain valid in batch cd commands.

* fix(terminal): use pushd for UNC paths in Windows batch launcher

`cmd.exe` cannot set a UNC path (e.g. `\\wsl$\...`) as the current
directory via `cd /d`; it errors with "CMD does not support UNC paths
as current directories". Switch to `pushd` which temporarily maps the
UNC share to a drive letter.

Rename `build_windows_cd_command` → `build_windows_cwd_command` to
reflect the broader semantics. Extract `build_windows_cwd_command_str`
and `is_windows_unc_path` helpers for testability, and add unit tests
covering drive paths, UNC paths, and batch metacharacter escaping.

Also fix minor style issues: sort mod declarations alphabetically,
add missing EOF newline in lightweight.rs, add explicit type annotation
in streaming_responses test, and reformat tray menu builder chain.
2026-03-31 15:10:08 +08:00
Alexlangl 8e2ffbc845 feat: add bulk delete for session manager (#1693)
* feat: add bulk delete for session manager

* fix: address batch delete review issues

* fix: keep session list in sync after batch delete
2026-03-30 22:15:57 +08:00
Alexlangl 4f7ea76347 fix: preserve WebDAV password display and validate MKCOL 405 (#1685)
* fix: preserve WebDAV password display and validate MKCOL 405

* fix: scope WebDAV password preservation to post-save refresh
2026-03-30 22:13:21 +08:00
Dex Miller 67e074c0a7 refactor(proxy): transparent header forwarding via hyper client (#1714)
* style(frontend): reformat provider forms, constants and hooks

Apply prettier formatting across 5 frontend files. No logic changes.

Changed files:
- AddProviderDialog.tsx: reformat generic type annotation and callback
- ClaudeFormFields.tsx: consolidate multi-line useState and Collapsible props
- CodexConfigSections.tsx: expand single-line React imports to multi-line,
  collapse removeCodexTopLevelField() call
- constants.ts: merge TemplateType into single line
- useSkills.ts: expand single-line TanStack Query imports to multi-line,
  reformat uninstallSkill mutationFn chain

* deps(proxy): add hyper ecosystem crates and manual decompression libs

reqwest internally normalizes all header names to lowercase and does not
preserve insertion order, causing proxied requests to differ from the
original client requests. To achieve transparent header forwarding with
original casing and order, introduce lower-level hyper HTTP client libs.

New dependencies:
- hyper-util 0.1: TokioExecutor + legacy Client with
  preserve_header_case support for HTTP/1.1
- hyper-rustls 0.27: rustls-based TLS connector for hyper
- http 1 / http-body 1 / http-body-util 0.1: HTTP type crates for
  hyper 1.x request/response construction
- flate2 1: manual gzip/deflate decompression (replaces reqwest auto)
- brotli 7: manual brotli decompression

Changed dependencies:
- serde_json: enable preserve_order feature to keep JSON field order
- reqwest: drop gzip feature to prevent reqwest from overriding the
  client's original accept-encoding header

* refactor(proxy): use hyper client for header-case preserving forwarding

Previously the proxy used reqwest for all upstream requests. reqwest
normalizes header names to lowercase and reorders them internally,
making proxied requests distinguishable from direct CLI requests.
Some upstream providers are sensitive to these differences.

This commit replaces reqwest with a hyper-based HTTP client on the
default (non-proxy) path, achieving wire-level header fidelity:

Server layer (server.rs):
- Replace axum::serve with a manual hyper HTTP/1.1 accept loop
- Enable preserve_header_case(true) so incoming header casing is
  captured in a HeaderCaseMap extension on each request
- Bridge hyper requests to axum Router via tower::Service

New hyper client module (hyper_client.rs):
- Lazy-initialized hyper-util Client with preserve_header_case
- ProxyResponse enum wrapping both hyper::Response and reqwest::Response
  behind a unified interface (status, headers, bytes, bytes_stream)
- send_request() builds requests with ordered HeaderMap + case map

Request handlers (handlers.rs):
- Switch from (HeaderMap, Json<Value>) extractors to raw
  axum::extract::Request to preserve Extensions (containing the
  HeaderCaseMap from the accept loop)
- Pass extensions through the forwarding chain

Forwarder (forwarder.rs):
- Remove HEADER_BLACKLIST array; replace with ordered header iteration
  that preserves original header sequence and casing
- Build ordered_headers by iterating client headers, skipping only
  auth/host/content-length, and inserting auth headers at the original
  authorization position to maintain order
- Handle anthropic-beta (ensure claude-code-20250219 tag) and
  anthropic-version (passthrough or default) inline during iteration
- Remove should_force_identity_encoding() — accept-encoding is now
  transparently forwarded to upstream
- Use hyper client by default; fall back to reqwest only when an
  HTTP/SOCKS5 proxy tunnel is configured

Provider adapters (adapter.rs, claude.rs, codex.rs, gemini.rs):
- Replace add_auth_headers(RequestBuilder) -> RequestBuilder with
  get_auth_headers(AuthInfo) -> Vec<(HeaderName, HeaderValue)>
- Adapters now return header pairs instead of mutating a reqwest builder
- Claude adapter: merge Anthropic/ClaudeAuth/Bearer into single branch;
  move Copilot fingerprint headers into get_auth_headers

Response processing (response_processor.rs):
- Add manual decompression (gzip/deflate/brotli via flate2 + brotli)
  for non-streaming responses, since reqwest auto-decompression is now
  disabled to allow accept-encoding passthrough
- Add compressed-SSE warning log for streaming responses
- Accept ProxyResponse instead of reqwest::Response

HTTP client (http_client.rs):
- Disable reqwest auto-decompression (.no_gzip/.no_brotli/.no_deflate)
  on both global and per-provider clients

Streaming adapters (streaming.rs, streaming_responses.rs):
- Generalize stream error type from reqwest::Error to generic E: Error

Misc:
- log_codes.rs: add SRV-005 (ACCEPT_ERR) and SRV-006 (CONN_ERR)
- stream_check.rs: reformat copilot header lines
- transform.rs: fix trailing whitespace alignment

* fix(lint): resolve 35 clippy warnings across Rust codebase

Fix all clippy warnings reported by `cargo clippy --lib`:

- codex_config.rs: fix doc_overindented_list_items (3 spaces -> 2)
- commands/copilot.rs: inline format args in 2 log::error! calls
- commands/provider.rs: inline format args in 3 map_err closures
- proxy/hyper_client.rs: inline format arg in log::debug! call
- proxy/providers/copilot_auth.rs: inline format args in 16 locations
  (log macros, format! in headers, error constructors)
- proxy/thinking_optimizer.rs: inline format args in 2 log::info! calls
- services/skill.rs: inline format args in log::debug! call
- services/webdav_sync.rs: inline format args in 6 format! calls
  (version compat messages, download limit messages)
- services/webdav_sync/archive.rs: inline format args in 2 format! calls
- session_manager/providers/opencode.rs: inline format args in
  source_path format!

All fixes use the clippy::uninlined_format_args suggestion pattern:
  format!("msg: {}", var)  ->  format!("msg: {var}")

* deps(proxy): add raw HTTP write and native TLS cert dependencies

Add crates required for the raw TCP/TLS write path that bypasses
hyper's header encoder to preserve original header name casing:

- httparse: parse raw TCP peek bytes to capture header casings
- tokio-rustls + rustls: direct TLS connections for raw write path
- webpki-roots: Mozilla CA bundle baseline
- rustls-native-certs: load system keychain CAs (trusts proxy MITM
  certificates from Clash, mitmproxy, etc.)

* fix(proxy): address code review feedback on response handling

Fixes from PR #1714 code review:

- Extract `read_decoded_body()` and `strip_entity_headers_for_rebuilt_body()`
  in response_processor to properly clean content-encoding/content-length
  headers after decompression
- Reuse `read_decoded_body()` in handlers.rs for Claude transform path,
  ensuring compressed responses are decoded before format conversion
- Make `build_proxy_url_from_config()` public so forwarder can pass proxy
  URL to the hyper raw write path
- Add `has_system_proxy_env()` utility with test coverage
- Add 50ms backoff after accept() failures in server.rs to prevent
  tight-loop CPU spin on transient socket errors

* feat(proxy): implement raw TCP/TLS write with HTTP CONNECT tunnel

Rewrite hyper_client with a two-tier strategy for header case preservation:

Primary path (raw write):
- Peek raw TCP bytes in server.rs to capture OriginalHeaderCases before
  hyper lowercases them
- Build raw HTTP/1.1 request bytes with exact original header name casing
- Write directly to TLS stream, then use WriteFilter to let hyper parse
  the response while discarding its duplicate request writes
- Support HTTP CONNECT tunneling through upstream proxies, so header case
  is preserved even when a proxy (Clash, V2Ray) is configured

Fallback path (hyper-util Client):
- Used when OriginalHeaderCases is empty or raw write fails
- Configured with title_case_headers(true) for best-effort casing

TLS improvements:
- Load native system certificates alongside webpki roots so proxy MITM
  CAs (installed in system keychain) are trusted through CONNECT tunnels

Key types added:
- OriginalHeaderCases: maps lowercase name → original wire-casing bytes
- WriteFilter<S>: AsyncRead+AsyncWrite wrapper that discards writes
- connect_via_proxy(): HTTP CONNECT tunnel establishment
- ExtensionDebugMarker: diagnostic marker for extension chain debugging

* refactor(proxy): route requests through hyper with proxy-aware forwarding

Rework forwarder request dispatch to always prefer the hyper raw write
path (header case preservation) over reqwest:

Request routing:
- HTTP/HTTPS proxy: hyper raw write through CONNECT tunnel (case preserved)
- SOCKS5 proxy: reqwest fallback (CONNECT not supported for SOCKS5)
- No proxy: hyper raw write direct connection

Header handling improvements:
- Replace host header in-place at original position instead of
  skip-and-append, preserving client's header ordering
- Preserve client's original accept-encoding for transparent passthrough;
  only force identity encoding when transform path needs decompression
- Add should_force_identity_encoding() to centralize the decision
- Remove hardcoded 'br, gzip, deflate' override that masked client values

Proxy URL resolution (priority order):
1. Provider-specific proxy config (if enabled)
2. Global proxy URL configured in CC Switch
3. Direct connection (no proxy)

* chore(proxy): remove dead code, redundant tests and debug scaffolding

- Inline should_force_identity_encoding() (was just `needs_transform`)
  and delete its 5 test cases
- Remove ExtensionDebugMarker diagnostic type
- Remove unused has_system_proxy_env() and its test
- Remove strip_entity_headers test
- Simplify hyper path: remove redundant is_socks_proxy ternary
- Update hyper_client module doc to reflect CONNECT tunnel support

* fix(proxy): block direct-connect fallback and complete CONNECT tunnel support

* feat(hooks): improve proxy requirement warnings with specific reasons

- Remove redundant OpenAI format hint toast messages
- Add detailed reason detection for proxy requirements (OpenAI Chat, OpenAI Responses, full URL mode)
- Update i18n files with new reason-specific keys

* style(*): format code with prettier

- Remove extra whitespace in http_client.rs
- Fix formatting issues in useProviderActions.ts

* fix(proxy): post-merge fixes for forward return type and clippy warnings

- Restore forward() return type to (ProxyResponse, Option<String>)
  to pass claude_api_format through to callers
- Inline format args in log::warn! macro (clippy::uninlined_format_args)
- Suppress too_many_arguments for check_claude_stream

* refactor(proxy): preserve original header wire order and add non-streaming body timeout

- Rewrite build_raw_request to emit headers in original
  client-sent sequence instead of hash-map order
- Remove unused OriginalHeaderCases::get_all method
- Add body_timeout to read_decoded_body to prevent
  requests hanging when upstream stalls after headers
2026-03-29 20:26:15 +08:00
makoMakoGo 3b33e6921b fix: correct opencode kimi-for-coding preset (#1738) 2026-03-29 10:31:23 +08:00
Zhou Mengze 8d5f72757e fix: 修复 Copilot 作为 Claude 时 OpenAI 模型的 Responses 分流 (#1735)
* fix: route copilot claude openai models to responses

* fix(i18n): add copilotProxyHint translation key for all locales

The copilotProxyHint message was using inline defaultValue with Chinese
text, which would show Chinese to English and Japanese users. Added
proper translation keys in zh/en/ja locale files and removed the
hardcoded defaultValue fallback.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-03-29 10:29:54 +08:00
Dex Miller 8cae7b7b73 feat(proxy): add full URL mode and refactor endpoint rewriting (#1561)
* feat(proxy): add full URL mode and refactor endpoint rewriting

- Add `isFullUrl` provider meta to treat base_url as complete API endpoint
- Remove hardcoded `?beta=true` from Claude adapter, pass through from client
- Refactor forwarder endpoint rewriting with proper query string handling
- Block provider switching when proxy is required but not running
- Add full URL toggle UI in endpoint field with i18n (zh/en/ja)

* refactor(proxy): remove beta query handling

* fix(proxy): strip beta query when rewriting Claude endpoints

* feat(codex): complete full URL support

* refactor(ui): refine full URL endpoint hint
2026-03-28 15:52:02 +08:00
Sky 90812e7f3a feat(opencode): add StepFun Step Plan provider preset (#1668)
Add StepFun Step Plan (阶跃星辰编程计划) as an OpenCode provider preset.

StepFun Step Plan is a subscription-based coding AI service that uses a
dedicated API endpoint separate from the standard StepFun provider.

- Base URL: https://api.stepfun.com/step_plan/v1
- Model: step-3.5-flash (196B MoE, optimized for agent and coding tasks)
- Category: cn_official
- Supports tool_call, reasoning, temperature control

Ref: https://platform.stepfun.com/docs/zh/step-plan/overview
Made-with: Cursor

Co-authored-by: sky-wang-salvation <sky-wang-salvation@users.noreply.github.com>
2026-03-25 21:22:05 +08:00
Jason 10be929f33 feat(claude): add "Disable Auto-Upgrade" checkbox to provider config editor
Add a toggle for DISABLE_AUTOUPDATER env var in CommonConfigEditor,
following the same pattern as the existing Teammates mode toggle.
2026-03-24 15:17:09 +08:00
Jason 0a301a497c fix: prevent WebDAV password from being silently cleared by unrelated saves
Two components (ProviderList, UsageScriptModal) directly spread the full
settings object from the query into settingsApi.save(), which includes
webdavSync with an empty password (cleared by get_settings_for_frontend
for security). The backend merge_settings_for_save only preserved
existing WebDAV config when the incoming field was None, not when it was
present with an empty password.

Frontend fix: strip webdavSync before saving in both components, matching
the pattern already used by useSettings hook.

Backend defense-in-depth: merge_settings_for_save now backfills the
existing password when the incoming one is empty, preventing future
regressions from similar oversights.
2026-03-23 16:47:17 +08:00
TangZhiZzz 8aa6ec784b feat(skills): 优化技能安装/卸载的缓存更新策略 (#1573)
- 修改安装、卸载、导入、ZIP安装等操作的缓存更新逻辑,从invalidateQueries改为直接setQueryData
- 为已安装和可发现技能查询添加keepPreviousData和staleTime: Infinity配置
- 修复会话管理页面布局滚动问题,添加min-h-0防止内容溢出
2026-03-23 16:05:13 +08:00
Jason 3e78fe8305 chore: update MiniMax preset model from M2.5 to M2.7 2026-03-20 21:05:56 +08:00
Jason 6f170305b8 chore: update Claude 4.6 context window to 1M (GA)
Claude Opus 4.6 and Sonnet 4.6 1M context window is now GA and no
longer requires a beta header. Update contextWindow from 200k to 1M
for all OpenClaw/OpenCode presets (27 entries in OpenClaw, 1 in
OpenCode Bedrock). Also add claude-sonnet-4-6 model pricing seed.
2026-03-20 21:00:22 +08:00
Jason 552f7abee4 refactor(ui): remove duplicate OAuth tab from AddProviderDialog
- Remove AuthCenterPanel import and OAuth TabsContent
- Narrow activeTab type from three values to "app-specific" | "universal"
- Simplify footer by removing oauth branch, reducing to two-way conditional
- Change TabsList from grid-cols-3 to grid-cols-2
- OAuth authentication remains available in settings page and CopilotAuthSection
2026-03-20 15:23:18 +08:00
Jason fd2b232f1c chore: update Xiaomi MiMo preset model from mimo-v2-flash to mimo-v2-pro 2026-03-20 08:59:41 +08:00
Zhou Mengze 8ccfbd36d6 feat(copilot): add GitHub Copilot reverse proxy support (#930)
* refactor(toolsearch): replace binary patch with ENABLE_TOOL_SEARCH env var toggle

- Remove toolsearch_patch.rs binary patching mechanism (~590 lines)
  - Delete `toolsearch_patch.rs` and `commands/toolsearch.rs`
  - Remove auto-patch startup logic and command registration from lib.rs
  - Remove `tool_search_bypass` field from settings.rs
  - Remove frontend settings ToggleRow, useSettings hook sync logic, and API methods
  - Clean up zh/en/ja i18n keys (notifications + settings)

- Add ENABLE_TOOL_SEARCH toggle to Claude provider form
  - Add checkbox in CommonConfigEditor.tsx (alongside teammates toggle)
  - When enabled, writes `"env": { "ENABLE_TOOL_SEARCH": "true" }`
  - When disabled, removes the key; takes effect on provider switch
  - Add zh/en/ja i18n key: `claudeConfig.enableToolSearch`

Claude Code 2.1.76+ natively supports this env var, eliminating the need for binary patching.

* feat(claude): add effortLevel high toggle to provider form

- Add "high-effort thinking" checkbox to Claude provider config form
- When checked, writes `"effortLevel": "high"`; when unchecked, removes the field
- Add zh/en/ja i18n translations

* refactor(claude): remove deprecated alwaysThinking toggle

- Claude Code now enables extended thinking by default; alwaysThinkingEnabled is a no-op
- Thinking control is now handled via effortLevel (added in prior commit)
- Remove state, switch case, and checkbox UI from CommonConfigEditor
- Clean up alwaysThinking i18n keys across zh/en/ja locales

* feat(opencode): add setCacheKey: true to all provider presets

- Add setCacheKey: true to options in all 33 regular presets
- Add setCacheKey: true to OPENCODE_DEFAULT_CONFIG for custom providers
- Exclude 2 OMO presets (Oh My OpenCode / Slim) which have their own config mechanism

Closes #1523

* fix(codex): resolve 1M context window toggle causing MCP editor flicker

- Add localValueRef to short-circuit duplicate CodeMirror updateListener callbacks,
  breaking the React state → CodeMirror → stale onChange → React state feedback loop
- Use localValueRef.current in handleContextWindowToggle and handleCompactLimitChange
  to avoid stale closure reads
- Change compact limit input from type="number" to type="text" with inputMode="numeric"
  to remove unnecessary spinner buttons

* feat(codex): add 1M context window toggle utilities and i18n keys

- Add extractCodexTopLevelInt, setCodexTopLevelInt, removeCodexTopLevelField
  TOML helpers in providerConfigUtils.ts
- Add i18n keys for contextWindow1M, autoCompactLimit in zh/en/ja locales

* feat(claude): collapse model mapping fields by default

- Wrap 5 model mapping inputs in a Collapsible, collapsed by default
- Auto-expand when any model value is present (including preset-filled)
- Show hint text when collapsed explaining most users need no config
- Add zh/en/ja i18n keys for toggle label and collapsed hint
- Use variant={null} to avoid ghost button hover style clash in dark mode

* feat(claude): merge advanced fields into single collapsible section

- Merge API format, auth field, and model mapping into a unified "Advanced Options" collapsible
- Extend smart-expand logic to detect non-default values across all advanced fields
- Preserve model mapping sub-header and hint with a separator line
- Update zh/en/ja i18n keys (advancedOptionsToggle, advancedOptionsHint, modelMappingLabel, modelMappingHint)

* feat(copilot): add GitHub Copilot reverse proxy support

Add GitHub Copilot as a Claude provider variant with OAuth device code
authentication and Anthropic ↔ OpenAI format transformation.

Backend:
- Add CopilotAuthManager for GitHub OAuth device code flow
- Implement Copilot token auto-refresh (60s before expiry)
- Persist GitHub token to ~/.cc-switch/copilot_auth.json
- Add ProviderType::GitHubCopilot and AuthStrategy::GitHubCopilot
- Modify forwarder to use /chat/completions for Copilot
- Add Copilot-specific headers (Editor-Version, Editor-Plugin-Version)

Frontend:
- Add CopilotAuthSection component for OAuth UI
- Add useCopilotAuth hook for OAuth state management
- Auto-copy user code to clipboard and open browser
- Use 8-second polling interval to avoid GitHub rate limits
- Skip API Key validation for Copilot providers
- Add GitHub Copilot preset with claude-sonnet-4 model

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix(copilot): remove is_expired() calls from tests

Remove references to deleted is_expired() method in test code.
Only is_expiring_soon() is needed for token refresh logic.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat(copilot): add real-time model listing from Copilot API

- Add fetch_models() to CopilotAuthManager calling GET /models endpoint
- Add copilot_get_models Tauri command
- Add copilotGetModels() frontend API wrapper
- Modify ClaudeFormFields to show model dropdown for Copilot providers
  - Fetches available models on component mount when isCopilotPreset
  - Groups models by vendor (Anthropic, OpenAI, Google, etc.)
  - Input + dropdown button combo allows both manual entry and selection
  - Non-Copilot providers keep original plain Input behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(copilot): add usage query integration

- Add Copilot usage API integration (fetch_usage method)
- Add copilot_get_usage Tauri command
- Add GitHub Copilot template in usage query modal
- Unify naming: copilot → github_copilot
- Add constants management (TEMPLATE_TYPES, PROVIDER_TYPES)
- Improve error handling with detailed error messages
- Add database migration (v5 → v6) for template type update
- Add i18n translations (zh, en, ja)
- Improve type safety with TemplateType
- Apply code formatting (cargo fmt, prettier)

* 修复github 登录和注销问题 ,模型选择问题

* feat(copilot): add multi-account support for GitHub Copilot

- Add multi-account storage structure with v1 to v2 migration
- Add per-account token caching and auto-refresh
- Add new Tauri commands for account management
- Integrate account selection in Proxy forwarder
- Add account selection UI in CopilotAuthSection
- Save githubAccountId to ProviderMeta
- Add i18n translations for multi-account features (zh/en/ja)

* 修复用量查询Reset字段出现多余字符

* refactor(auth-binding): introduce generic provider auth binding primitives

- add shared authBinding types in Rust and TypeScript while keeping githubAccountId as a compatibility field\n- resolve Copilot token, models, and usage through provider-bound account lookup instead of only the implicit default account\n- fix the Unix build regression in settings.rs by restoring std::io::Write for write_all()\n- remove the accidental .github ignore entry and drop leftover Copilot form debug logs\n- keep the first migration step non-breaking by writing both authBinding and the legacy githubAccountId field from the form

* refactor(auth-service): add managed auth command surface and explicit default account state

- introduce generic managed auth commands and frontend auth API wrappers for provider-scoped login, status, account listing, removal, logout, and default-account selection\n- store an explicit Copilot default_account_id instead of relying on HashMap iteration order, and use it consistently for fallback token/model/usage resolution\n- sort managed accounts deterministically and surface default-account state to the UI\n- refactor the Copilot form hook to wrap a generic useManagedAuth implementation while preserving the existing component contract\n- add default-account controls to the Copilot auth section and extend Copilot auth status serialization/tests for the new state

* feat(auth-center): add a dedicated settings entrypoint for managed OAuth accounts

- add an Auth Center tab to Settings so managed OAuth accounts are no longer hidden inside individual provider forms\n- introduce a first AuthCenterPanel that hosts GitHub Copilot account management as the initial managed auth provider\n- keep the provider form experience intact while establishing a global account-management surface for future providers such as OpenAI\n- validate that the new settings tab works cleanly with the generic managed auth hook and existing Copilot account controls

* feat(add-provider): expose managed OAuth sources alongside universal providers

- add an OAuth tab to the Add Provider flow so managed auth sources sit beside app-specific and universal providers\n- reuse the new Auth Center panel inside the dialog, keeping account management discoverable during provider creation\n- make the dialog footer adapt to the OAuth tab so account setup does not pretend to create a provider directly\n- align the add-provider UX with the new architecture where OAuth accounts are global assets and providers bind to them later

* fix(auth-reliability): harden managed auth persistence and refresh behavior

- replace direct Copilot auth store writes with private temp-file writes and atomic rename semantics, and document the local token storage limitation\n- add per-account refresh locks plus a double-check path so concurrent requests do not stampede GitHub token refresh\n- surface legacy migration failures through auth status, expose them in the UI, and add translated copy for the new account-state labels\n- stop writing the legacy githubAccountId field from the provider form while keeping compatibility reads in place\n- add logout error recovery and Copilot model-load toasts so auth failures are no longer silently swallowed

* refactor(copilot-detection): prefer provider type before URL fallbacks

- update forwarder endpoint rewriting to treat providerType as the primary GitHub Copilot signal\n- keep githubcopilot.com string matching only as a compatibility fallback for older provider records without providerType\n- reduce one more path where Copilot behavior depended purely on URL heuristics

* fix(copilot-auth): add cancel button to error state in CopilotAuthSection

- 错误状态下仅有"重试"按钮,用户无法退出(如不可恢复的 403 未订阅错误)
- 新增"取消"按钮,复用已有的 cancelAuth 逻辑重置为 idle 状态

* 修复打包后github账号头像显示异常

* 修复github copilot 来源的模型测试报错

* feat(copilot-preset): add default model presets for GitHub Copilot

- 补充 Copilot 预设的默认模型配置,用户选完预设即可直接使用
- ANTHROPIC_MODEL: claude-opus-4.6
- ANTHROPIC_DEFAULT_HAIKU_MODEL: claude-haiku-4.5
- ANTHROPIC_DEFAULT_SONNET_MODEL: claude-sonnet-4.6
- ANTHROPIC_DEFAULT_OPUS_MODEL: claude-opus-4.6

---------

Co-authored-by: Jason <farion1231@gmail.com>
Co-authored-by: 周梦泽 <mengze.zhou@dafeng-tech.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-03-17 23:57:58 +08:00