Commit Graph

1674 Commits

  • chore(release): surface ccswitch.io in release notes template
    Each tagged release now leads with the canonical official website
    in three languages, ensuring every Release page (which is indexed
    independently by Google) becomes a dofollow backlink to ccswitch.io.
  • docs: add Hermes Agent to README subtitles (en/zh/ja)
    Aligns README subtitles with the GitHub repo description that
    now lists Hermes Agent as a managed application.
  • chore(brand): surface ccswitch.io as the sole official website
    Add an "Only Official Website" header to the three READMEs, an
    About panel button, and a tray menu entry — all pointing to
    ccswitch.io. Consolidates brand and SEO signals on the canonical
    domain across docs, GUI, and system tray.
  • perf(proxy): trim per-request hot-path work and db wait
    - Guard debug body serialization with `log::log_enabled!`; previously
      serialized the filtered body to a throwaway String on every forward,
      even with debug logging off.
    - Skip SSE parse + UTF-8 buffer loop when no usage collector and debug
      is off; the per-chunk `serde_json::from_str::<Value>` ran even in
      pure passthrough mode.
    - Add cheap per-app SSE event pre-filter (string `contains`) so usage
      collectors only parse events that could contain usage (e.g. Claude
      `message_start` / `message_delta`).
    - Skip non-streaming response body JSON parse when usage logging is
      disabled.
    - Move `ProviderRouter::record_result` off the success response path
      via `tokio::spawn` for non-HalfOpen state; that call internally does
      `get_proxy_config_for_app` + `update_provider_health`, two SQLite
      ops that previously blocked TTFB.
    
    Also: dedupe `usage_logging_enabled` (was duplicated in handlers.rs)
    and merge `SseUsageCollector::{new, new_filtered}` into a single
    constructor that takes `Option<StreamUsageEventFilter>`.
  • fix(proxy): improve cache hit rate for Codex/Responses requests
    prompt_cache_key was falling back to provider.id when the client did not
    supply a session, which collapsed every conversation onto a single key
    and defeated upstream prefix caching. Only emit the key when a real
    client-provided session/thread identity is available; otherwise let the
    upstream use its default matching behaviour.
    
    Additional fixes that affect cache stability:
    - Canonicalise (sort) JSON keys in outgoing request bodies and in
      tool_call arguments / tool_result content so semantically identical
      requests produce identical byte sequences for upstream prefix caches.
    - Exempt JSON Schema property maps (properties, patternProperties,
      definitions, \$defs) from the underscore-prefix filter so user-defined
      schema keys like _id and _meta survive.
    - Add a [CacheTrace] debug log with stable hashes for instructions,
      tools, input and include to help diagnose cache misses.
    - Thread session_id into the usage logger for request correlation.
  • fix(proxy): drop empty pages from Read tool input (#2472)
    * fix(proxy): drop empty pages from Read tool input
    
    * fix(proxy): preserve Read args across duplicate tool starts
  • - fix(ci): restore frontend formatting and Linux clippy
    - Format Claude Desktop provider presets with Prettier
    
    - Gate platform-specific Claude Desktop path helpers behind cfg
  • chore(icons): add ClaudeCN and RunAPI raster icons
    Resized to 512x512 to match the existing extracted-icons size convention
    (hermes.png / lemondata.png). Source uploads were oversized (7.3 MB
    8635x8635 for ClaudeCN; 800x800 for RunAPI) and would have bloated the
    bundle if imported as-is.
    
    Note: not yet wired into index.ts / metadata.ts; register there when
    they need to surface in the app UI.
  • docs: add RunAPI sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales linking to runapi.co.
    EN/JA copy adapted from the Chinese original. Banner center-cropped to
    the project standard 1920x798 aspect ratio (preserves the logo and
    slogan, trims the decorative top/bottom padding) and saved as JPEG to
    keep file size reasonable (1.3 MB PNG -> 206 KB JPEG).
  • docs: add ClaudeCN sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales linking to claudecn.top.
    EN/JA copy adapted from the Chinese original. Banner normalized to the
    project standard sponsor image spec (1920x798 RGB on white background)
    and recompressed; alt text unified to "ClaudeCN" across all three files.
  • docs: use Volcengine logo for Chinese README sponsor entry
    Replace byteplus.png with localized huoshan.png (Volcengine/火山引擎)
    in README_ZH.md so Chinese readers see the regional brand. EN/JA
    README continue to use the BytePlus logo and link to byteplus.com.
    
    The new logo is normalized to the project's standard sponsor image
    spec: 1920x798 RGB on a white background.
  • docs: add BytePlus sponsor entry to README (en/zh/ja)
    Insert sponsor row in all three README locales. EN/JA point to
    byteplus.com/modelark; ZH points to volcengine.com/agentplan
    (Volcengine being the China-region counterpart). Logo normalized
    to the project-standard 1920x798 RGB white background and
    recompressed (432K -> 84K).
  • feat(claude-desktop): add 44 provider presets translated from Claude Code
    - New src/config/claudeDesktopProviderPresets.ts with the Claude Code
      preset list re-shaped into Desktop's three-segment route format
      (routeId / upstreamModel / displayName); excludes OAuth providers,
      AWS Bedrock (no SigV4 support) and KAT-Coder (placeholder URL).
    - Non-Claude upstream presets show upstream model id as displayName
      (e.g. deepseek-v4-pro) so the Desktop model list reflects what is
      actually being requested. OpenRouter/TheRouter/PIPELLM keep
      Sonnet/Opus/Haiku since their upstream really is Anthropic Claude.
    - Wire ProviderPresetSelector into ClaudeDesktopProviderForm so
      selecting a preset back-fills baseUrl, mode, routes and apiFormat.
    - Drop the hard-coded ANTHROPIC_AUTH_TOKEN write in handleSubmit so
      ANTHROPIC_API_KEY presets (LemonData / AiHubMix / Gemini Native)
      save under the correct env key, and clear the opposite key on switch.
    - Hide the universal-providers tab for claude-desktop because its
      meta-driven routing has no analogue in the universal flat-env shape.
    - Add apps."claude-desktop" i18n key (zh/en/ja) so the dialog tab
      label resolves instead of showing the literal key.
  • chore(backend): satisfy cargo fmt and clippy --all-targets
    - Apply rustfmt diffs in claude_desktop_config.rs
    - Allow needless_return on current_platform_paths (cfg-mirrored arms)
    - Allow too_many_arguments on RequestForwarder::forward
    - Replace `let mut + reassign` with struct literals in tests
      (settings, backup, provider, response_processor)
    - Use Path::new instead of PathBuf::from to fix cmp_owned in misc tests
    - Replace 3.14 with 3.5 in config test to avoid approx_constant lint
  • docs: update sponsor logos and listings
    - Add ClaudeAPI as new sponsor (all 3 languages)
    - Remove ChefShop sponsor entry (all 3 languages)
    - Convert shengsuanyun logo from SVG to PNG
    - Standardize partner logos to 1920x798 canvas
    - Fix ClaudeAPI alt text typo and spacing
    - Sync sponsor order across zh/en/ja READMEs
  • refactor(claude-desktop): show badge only for providers requiring routing
    Direct-mode providers no longer display a badge since routing is
    optional for them. Proxy-mode providers now show "需要路由" / "Requires
    routing" to clarify that local routing must be active.
  • fix(claude-desktop): match proxy model route without [1M] suffix
    Claude Desktop strips the [1M] suffix from model IDs when sending
    requests, causing route lookup to fail with "model route is not
    configured". Fall back to base-name comparison when exact match misses.
  • refactor(claude-desktop): simplify model mapping UX
    - Remove "Import from Claude" button from main provider list (keep in empty state)
    - Remove "Desktop model" column from proxy mode mapping table; route names are now auto-generated
    - Rename "upstream model" label to "requested model" and equalize column widths
    - Rewrite model mapping hint and toggle description for end-user clarity
    - Update validation messages and remove dead i18n keys (routeModelLabel)
  • refactor(claude-desktop): align provider form UI with Claude Code
    - Rename field labels: "Gateway Base URL" → "API Endpoint", "Bearer Token" → "API Key"
    - Change layout from 2-column grid to vertical sections matching Claude Code
    - Reuse shared EndpointField component with format-aware amber hint box
    - Replace native <datalist> with vendor-grouped ModelDropdown (OpenCode pattern)
    - Move Fetch/Add buttons to section headers with compact sm styling
    - Extract ModelDropdown to shared module, deduplicate from OpenCodeFormFields
    - Extract renderActionButtons helper to eliminate proxy/direct button duplication
    - Remove dead i18n keys (gatewayBaseUrl, bearerToken) from all 3 locales
  • fix(claude-desktop): remove proxy-stopped status alert
    The route toggle in the top-right corner already communicates proxy
    state; the extra warning banner was redundant and inconsistent with
    other managed apps.
  • refactor(claude-desktop): trim duplication in proxy and switch flows
    - services/proxy.rs: collapse 10 repeated `OpenCode | OpenClaw | Hermes |
      ClaudeDesktop` match arms into `_` fallthroughs.
    - claude_desktop_config.rs: extract a `with_rollback` closure shared by
      apply_provider_to_paths and restore_official_at_paths.
    - useProviderActions.ts: replace the triple-nested ternary picking the
      switch-success toast message with a flat let/if/else block.
    
    Net -36 lines. No behavior change; cargo test and pnpm typecheck pass.
  • feat(claude-desktop): add 3P provider switching with proxy gateway
    Adds a new ClaudeDesktop AppType that writes Claude Desktop's third-party
    inference profile under configLibrary/, sharing _meta.json with other
    launchers (Ollama-compatible) so cc-switch can coexist with them.
    
    Two switch modes:
    - direct: provider already exposes claude-* / anthropic/claude-* model
      ids on Anthropic Messages, Claude Desktop connects to it directly.
    - proxy: cc-switch's local proxy acts as the inference gateway,
      presenting only claude-* route names to Claude Desktop and mapping
      them to real upstream models. Required after Anthropic restricted
      Claude Desktop to claude-family ids.
    
    Backend:
    - New module claude_desktop_config with snapshot/rollback, official seed
      bypass, /claude-desktop/v1/{models,messages} routes, and a single
      source of truth for default proxy routes.
    - Gateway token persisted in SQLite, validated on every proxied request.
    - get_claude_desktop_status surfaces drift signals (stale models,
      missing routes, proxy stopped, base URL mismatch, missing token).
    
    Frontend:
    - Slim ClaudeDesktopProviderForm independent from ProviderForm,
      controlled by a top-level appId guard.
    - ProviderList banner consumes the status query (5s polling) and
      renders actionable diagnostics.
    - ClaudeDesktopRouteToggle in the header to start/stop the local
      gateway without touching takeover state.
    - Three-locale i18n synchronised.
  • fix(proxy): reuse pooled HTTPS connections for non-Anthropic backends
    The hyper raw-write path preserves original header casing but rebuilds
    TCP+TLS on every request — there is no connection pool — which was the
    root cause of slow reverse-proxy throughput.
    
    Only Anthropic-native requests actually need exact header-case
    preservation. Route OpenAI/Copilot/Codex/Gemini/codex_oauth requests
    through the pooled reqwest client (pool_max_idle_per_host=10,
    tcp_keepalive=60s) instead, so warm connections get reused.
    
    Streaming requests get a precise first-byte timeout via
    tokio::time::timeout around reqwest's send() (which resolves on
    response headers), with the body phase handed off to response_processor.
    The streaming-detection helper now also covers Gemini SSE endpoints
    and Accept: text/event-stream, not just body.stream.
  • Fix Codex startup live import duplication (#2590)
    * Fix Codex startup live import duplication
    
    * Fix: Prevent duplicate Codex default provider on restart & add startup import tests
  • feat: return reasoning_content with tool_calls for DeepSeek models (#2543)
    * feat: return reasoning_content with tool_calls for DeepSeek models
    
    * fix: correct reasoning_content handling for DeepSeek tool_calls
    
    * test: cover DeepSeek reasoning content round trip
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(ci): pin Claude review checkout to PR head sha
    Prevents `git fetch origin pull/<N>/head:main` failures on fork PRs
    whose head branch is also named `main` — using a SHA puts the runner
    in detached HEAD so the main ref is free for the action's internal fetch.
  • refactor(theme): drop unused MouseEvent param from setTheme
    Now that the view transition animation is gone, setTheme no longer
    needs click coordinates. Reduce the API surface to (theme: Theme) =>
    void and simplify the call sites in mode-toggle and ThemeSettings.
  • refactor(theme): remove circular reveal animation for theme switching
    The View Transitions API used here crashes WebKitGTK with SIGSEGV on
    Linux. Rather than gating document.startViewTransition per platform
    (see PR #2502), drop the animation entirely — it's a low-value visual
    flourish on a low-frequency action that doesn't justify a permanent
    platform branch.
    
    Removes the ::view-transition-* CSS block and the coordinate plumbing
    in setTheme. The optional event parameter is kept on the API surface
    to keep call sites compiling; they'll be cleaned up in a follow-up
    commit.
  • fix(ci): drop --max-turns 5 from Claude review args
    Cap was too tight — review tasks need 8-15 turns to read files, analyze,
    and post results. The first run after the previous prompt change failed
    with error_max_turns at turn 6. The disallowedTools list already keeps
    the agent in read-only mode, so an explicit turn cap is redundant.
  • chore(ci): tune Claude review prompt to reduce nitpick noise
    Previous prompt produced 5-10 findings per PR including dead-code/style
    nits mixed with real bugs. New prompt adds severity tiering, an 80
    confidence threshold, an explicit anti-pattern list, a 5-nit cap, and
    permits zero-comment LGTM as a valid outcome. Calibration follows
    Anthropic Code Review docs and the claude-plugins-official prompt.
  • chore(deps): bump actions/stale from 9 to 10 (#2520)
    Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10.
    - [Release notes](https://github.com/actions/stale/releases)
    - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/actions/stale/compare/v9...v10)
    
    ---
    updated-dependencies:
    - dependency-name: actions/stale
      dependency-version: '10'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump softprops/action-gh-release from 2 to 3 (#2519)
    Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
    - [Release notes](https://github.com/softprops/action-gh-release/releases)
    - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
    - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)
    
    ---
    updated-dependencies:
    - dependency-name: softprops/action-gh-release
      dependency-version: '3'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump pnpm/action-setup from 5 to 6 (#2518)
    Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 5 to 6.
    - [Release notes](https://github.com/pnpm/action-setup/releases)
    - [Commits](https://github.com/pnpm/action-setup/compare/v5...v6)
    
    ---
    updated-dependencies:
    - dependency-name: pnpm/action-setup
      dependency-version: '6'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(deps): bump actions/checkout from 4 to 6 (#2517)
    Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/actions/checkout/compare/v4...v6)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-version: '6'
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • chore(ci): add Claude Code Action for review-only @claude mentions
    - Restrict to repo collaborators via author_association check
    - Use OAuth token for subscription billing
    - Disable Edit/Write tools and set contents:read permission to enforce review-only
  • fix(proxy): derive Claude auth strategy from ANTHROPIC env var name
    Anthropic SDK assigns distinct semantics to the two env vars:
    
    - ANTHROPIC_API_KEY    -> x-api-key
    - ANTHROPIC_AUTH_TOKEN -> Authorization: Bearer
    
    The Claude adapter previously collapsed both into AuthStrategy::Anthropic
    and then emitted Authorization: Bearer regardless, breaking strict
    Anthropic-protocol endpoints (Anthropic official, Cloudflare AI Gateway,
    OpenCode Go, DashScope) and silently overriding the user's intended auth
    scheme.
    
    - claude::extract_auth: infer strategy from env var name
      (ANTHROPIC_AUTH_TOKEN -> ClaudeAuth, ANTHROPIC_API_KEY -> Anthropic),
      matching the precedence already used by extract_key.
    - claude::get_auth_headers: split the Anthropic arm so it emits
      x-api-key, while ClaudeAuth and Bearer continue to use Bearer.
    - stream_check: reuse ClaudeAdapter::get_auth_headers as the single
      source of truth, replacing the prior "always Bearer + maybe x-api-key"
      double injection that produced auth conflicts and false-negative
      health checks.
    - Cover each strategy -> header mapping and env-var precedence with
      new unit tests in claude.rs.
    
    Refs #2368, #2380
  • fix(proxy): strip leading billing header from system content (#2350)
    Claude Code injects a dynamic `x-anthropic-billing-header` line at the
    start of `system` content. Its rotating `cch=` token was forwarded into
    OpenAI Responses `instructions` and Chat system messages, which broke
    upstream prefix prompt cache reuse — a stable ~95k-token prefix was
    getting re-charged on every request.
    
    Strip only the leading occurrence in both anthropic_to_openai and
    anthropic_to_responses; later occurrences are preserved so user-authored
    prompt text containing the same string is not lost.
  • chore(usage): drop Hermes Agent tracking integration
    Hermes aggregates all in-process API calls into a single sessions row
    with the `model` field locked to the initial model, so the usage
    dashboard cannot cleanly surface per-call billing context. Two rounds
    of UI workarounds (raw mapping, then `<model> @ <host>` display) did
    not resolve the user-facing confusion, so the whole tracking
    integration is dropped for now.
    
    Removes session_usage_hermes service (and its 17 tests), sync wiring
    in commands/usage.rs and lib.rs, _hermes_session/hermes_session
    entries in usage_stats SQL (provider_name_coalesce CASE and
    effective_usage_log_filter IN clause), frontend Tab/banner/dropdown/
    icon entries, and four i18n keys per locale.
    
    Hermes app integration outside usage tracking (proxy routing,
    session manager, config) is preserved. Pre-existing hermes rows in
    proxy_request_logs are left as orphans — filtered out by the
    updated SQL and never surfaced in the UI.
  • feat: persist Tauri window state (#2377)
    Add the window-state plugin and explicitly save size and position across app exit, restart, and lightweight-mode transitions.
  • feat(providers): add Baidu Qianfan Coding Plan for Claude Code (#2322)
    * feat(providers): add baidu qianfan coding plan presets
    
    * refactor(providers): align qianfan presets with existing format
    
    * chore(providers): narrow qianfan coding plan scope