Commit Graph

956 Commits

  • feat: block official provider switching during proxy takeover
    Prevent users from switching to official providers (Anthropic/OpenAI/Google)
    when proxy takeover is active, as using a proxy with official APIs may cause
    account bans.
    
    Defense-in-depth across 4 layers:
    - Backend: ProviderService::switch(), hot_switch_provider(), switch_proxy_provider command
    - Frontend: useProviderActions soft guard with error toast
    - UI: ProviderActions button disabled with ShieldAlert icon
    - Tray menu: official provider items disabled with  indicator
    
    Also warns when enabling proxy takeover while current provider is official.
  • Stop sending prompt cache keys on Claude chat conversions (#2003)
    Responses conversions still use promptCacheKey, but chat completions now stay a pure shape transform. This keeps Claude -> chat requests aligned with providers that do not understand the field and keeps stream checks consistent with production behavior.
    
    Constraint: Issue #1919 requires removing prompt_cache_key from Claude -> OpenAI Chat requests
    Rejected: Add a runtime toggle for chat injection | requested behavior is unconditional removal
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep promptCacheKey limited to Claude -> Responses conversions unless a provider-specific contract is proven
    Tested: cargo test anthropic_to_openai
    Tested: cargo test anthropic_to_responses_with_cache_key
    Tested: cargo test transform_claude_request_for_api_format_responses
    Not-tested: Full src-tauri test suite
    Related: #1919
  • 添加应用级别窗口按钮,以改善linux wayland下系统窗口按钮失效的问题 (#1119)
    * feat(window): add app-level window controls with settings toggle
    
    Add a persistent settings toggle to enable app-level minimize/maximize/close controls and hide system decorations when enabled, providing a Wayland-friendly fallback for broken native titlebar interactions.
    
    Co-authored-by: Cursor <cursoragent@cursor.com>
    
    * fix(window): restrict app-level window controls to Linux only and fix startup flicker
    
    - Guard useAppWindowControls with isLinux() in App.tsx so it's always
      false on macOS/Windows even if persisted as true
    - Wrap set_decorations call in lib.rs with #[cfg(target_os = "linux")]
    - Only show the toggle in WindowSettings on Linux
    - Skip setDecorations effect while settingsData is still loading to
      prevent the Rust-side decoration state from being overridden by the
      undefined->false fallback, which caused a brief title bar flicker
    
    ---------
    
    Co-authored-by: wzk <wx13571681304@outlook.com>
    Co-authored-by: Cursor <cursoragent@cursor.com>
    Co-authored-by: Jason <farion1231@gmail.com>
  • feat: update PIPELLM preset with full config, add Codex support and icon
    Update base URL to cc-api.pipellm.ai, add complete model definitions
    (Opus/Sonnet/Haiku), add Codex preset with custom TOML config, add
    pipellm PNG icon, and set apiKeyUrl to referral link.
  • feat: add E-FlowCode provider preset across all five apps
    Multi-protocol provider: Anthropic for Claude, OpenAI Responses for
    Codex/OpenClaw, OpenAI for OpenCode, custom Gemini config. Includes
    PNG icon via URL import and provider-specific settings like effortLevel,
    enabledPlugins, and custom TOML config for Codex.
  • feat: add Shengsuanyun provider preset with partner promotion
    Add Shengsuanyun (胜算云) as an aggregator partner across all five apps,
    positioned right after official providers. Uses anthropic-messages
    protocol for OpenCode/OpenClaw. Includes URL-based icon import (217KB
    SVG), partner promotion i18n for zh/en/ja, and localized display name.
  • feat: add LionCCAPI provider preset with partner promotion
    Add LionCCAPI as a third-party partner provider across all five apps
    (Claude, Codex, Gemini, OpenCode, OpenClaw) with anthropic-messages
    protocol for OpenCode and OpenClaw. Include partner promotion i18n
    entries for zh/en/ja locales and lioncc icon.
  • feat: replace x-code icon with high-res xcode icon via URL import
    Replace the old low-res x-code inline SVG (7.6KB) with a new high-res
    xcode.svg (286KB) loaded via Vite URL import. Update all three provider
    preset files to reference the new icon name.
  • feat: support URL-based icons for large SVGs and raster images
    Add dual rendering mode to the icon system: small optimized SVGs
    continue to be inlined via dangerouslySetInnerHTML, while large SVGs
    and raster images (png/jpg/webp/etc) use Vite URL imports rendered
    as <img> tags. Added dds.svg (1.4MB) as the first URL-based icon.
    
    Updated generate-icon-index.js to support multi-format icons with
    a manual URL_ICONS control list. Updated ProviderIcon to handle
    both inline SVG and URL-based rendering paths.
  • feat: add ddshub provider preset with partner promotion
    Add ddshub as a third-party partner provider for Claude, including
    SVG icon and i18n promotion text in zh/en/ja locales.
  • Restore first-class OMO Slim council support (#1981) (#1982)
    cc-switch could already persist arbitrary OMO Slim agent keys and top-level fields, but the built-in metadata and UI copy still reflected the pre-council agent set. This made the upstream council feature look unsupported and pushed users toward manual JSON-only setup.
    
    Promote council to a built-in OMO Slim agent, add copy that points top-level plugin settings at Other Fields, and lock the behavior with regression tests.
    
    Constraint: oh-my-opencode-slim exposes council through both agents.council and top-level council config
    Rejected: Add a dedicated council editor UI now | too much surface area for issue #1981
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep OMO Slim built-in agent metadata aligned with upstream agent additions before shipping UI support
    Tested: pnpm exec vitest run tests/utils/omoConfig.test.ts tests/components/OmoFormFields.mergeCustomModelsIntoStore.test.ts
    Tested: pnpm typecheck
    Not-tested: End-to-end validation against a live oh-my-opencode-slim installation
    Related: farion1231/cc-switch#1981
  • Let Kaku users launch sessions from their chosen terminal (#1954) (#1983)
    Kaku is a WezTerm-derived macOS terminal, so reusing the existing WezTerm-compatible launch path keeps the change small while making it selectable in settings and session resume flows.
    
    Constraint: Kaku support should stay macOS-only and avoid introducing a separate launcher model
    Rejected: Treat Kaku as a silent WezTerm fallback | users could not explicitly choose it in settings
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep Kaku on the shared WezTerm-compatible launch path unless upstream drops the start-compatible CLI
    Tested: pnpm typecheck; pnpm format:check; cargo check --manifest-path src-tauri/Cargo.toml; cargo fmt --manifest-path src-tauri/Cargo.toml --check; cargo test --manifest-path src-tauri/Cargo.toml --lib session_manager::terminal::tests
    Not-tested: End-to-end launch against a locally installed Kaku.app
    Related: #1954
  • Align Thinking fallback with main-model-only Claude mappings (#1984)
    The Claude provider form reopened with an empty Thinking model after users saved only a main model. This updates model-state hydration to mirror the existing Haiku-style fallback semantics: read ANTHROPIC_REASONING_MODEL when present, otherwise display ANTHROPIC_MODEL, without writing a synthetic reasoning field back into config.
    
    Constraint: Existing Haiku, Sonnet, and Opus selectors already rely on read-time fallback behavior
    Rejected: Persist ANTHROPIC_REASONING_MODEL from ANTHROPIC_MODEL automatically | would diverge from Haiku behavior and silently rewrite saved config
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: Keep Thinking fallback read-only unless all model-mapping fields are intentionally migrated to write-through semantics
    Tested: pnpm typecheck
    Tested: pnpm test:unit (1 unrelated pre-existing failure in tests/components/UnifiedSkillsPanel.test.tsx mock setup)
    Not-tested: Manual add-provider reopen flow in the desktop UI
  • Restore auth tab localization in settings (#1985)
    The settings page already routes the auth tab label through the shared i18n key, but the locale bundles never defined that key. Adding the missing entries fixes the label with the same simple pattern used by the other tabs.
    
    Constraint: Keep the fix aligned with the existing settings-tab i18n flow
    Rejected: Add component-level bilingual rendering | unnecessary for a missing translation key
    Confidence: high
    Scope-risk: narrow
    Reversibility: clean
    Directive: When adding settings tabs, define locale keys in every bundled language before relying on fallback text
    Tested: pnpm format:check; pnpm typecheck
    Not-tested: Manual verification in the desktop UI
  • feat(provider): add TheRouter presets for Claude, Codex, and Gemini (#1891)
    * feat(provider): add TheRouter presets for Claude and Codex
    
    * feat(provider): add TheRouter Gemini preset
    
    ---------
    
    Co-authored-by: max <me19@qq.com>
  • fix(session-manager): improve session search accuracy and Chinese support
    - Pre-filter sessions by provider before indexing to prevent result
      truncation when FlexSearch limit cuts across providers
    - Switch tokenizer from "forward" to "full" for Chinese substring matching
    - Preserve FlexSearch relevance ranking when search query is present
  • feat(common-config): show first-run notice dialog when editing providers
    Display a one-time informational dialog explaining the Common Config
    Snippet feature when users first open the add/edit provider form.
    Uses a derived isOpen state from settings to avoid race conditions.
    Adds commonConfigConfirmed flag to both TS and Rust settings types.
  • feat(common-config): add guide info and empty state to common config editor
    Add an informational alert block at the top of the common config snippet
    editor modal (Claude/Codex/Gemini) explaining what the feature is, why
    it exists, and how to use it. Also add an empty state prompt when no
    snippet has been extracted yet, guiding users to click "Extract from
    Editor". Includes i18n support for zh/en/ja.
  • fix(usage): only show CLI subscription quota for active provider
    CLI-credential-based subscriptions (Claude/Codex/Gemini) read from a
    single global credential file, so the quota always reflects the last
    CLI login rather than a specific provider. Showing it on non-current
    cards is misleading when multiple official subscriptions exist.
    
    Apply the same isCurrent + autoQuery pattern already used by Copilot
    and Codex OAuth: only query and render the quota footer when the
    provider is the currently active one.
  • fix(notifications): remove duplicate toast when switching to proxy providers
    When switching to Copilot/ChatGPT/OpenAI-format providers with the proxy
    not running, two toasts appeared: a "proxy required" warning followed by
    a "switch success" toast. Unify the post-switch toast logic so that all
    provider types show a single success toast, and skip it entirely when
    a proxy-required warning was already shown.
  • feat(welcome): show first-run welcome dialog on fresh install
    Introduce a one-time welcome dialog that explains CC Switch's workflow
    to new users: how their existing config is preserved as a "default"
    provider and how the bundled "Official" preset enables one-click revert.
    Upgrade users are excluded by checking is_providers_empty() at startup
    and never see the dialog.
    
    Persistence follows the existing *_confirmed convention in AppSettings
    (proxy/usage/stream_check/failover), stored in settings.json. The field
    is only written when the user explicitly clicks the confirm button,
    keeping its semantics strictly about user acknowledgement.
    
    Also adds two reusable DAO helpers:
    - Database::is_providers_empty for fresh-install detection, using
      EXISTS(SELECT 1) for a short-circuit query.
    - Database::get_bool_flag accepting "true" | "1", with
      init_default_official_providers migrated to use it.
    
    Dialog copy in zh/en/ja uses conditional phrasing so it stays
    accurate whether or not existing live config was found.
  • fix(usage): only auto-poll Copilot/ChatGPT quota for current provider
    CopilotQuotaFooter and CodexOauthQuotaFooter called their hooks with a
    hardcoded `enabled: true` plus an unconditional 5-minute refetch and
    refetchOnWindowFocus, so non-current reverse-proxy cards kept polling
    in violation of the project's "only the active provider auto-queries
    on cooldown" rule. With multiple Copilot or ChatGPT accounts bound to
    different cards, every card kept hitting its own usage endpoint.
    
    Adopt the same pattern as useUsageQuery: keep `enabled` independent of
    isCurrent so first-fetch and manual refresh still work, but gate
    refetchInterval / refetchIntervalInBackground / refetchOnWindowFocus on
    a new `autoQuery` option, and thread `isCurrent` from ProviderCard
    through the footers into the hooks.
  • feat(stream-check): support OpenCode via npm package mapping
    Phase 3: implement stream check for OpenCode providers by mapping the
    `settings_config.npm` (AI SDK package name) to the corresponding API
    protocol and delegating to the existing stream checkers.
    
    Package mapping:
    - @ai-sdk/openai-compatible → openai_chat
    - @ai-sdk/openai            → openai_responses
    - @ai-sdk/anthropic         → anthropic (ClaudeAuth strategy)
    - @ai-sdk/google            → gemini (Google strategy)
    - @ai-sdk/amazon-bedrock    → not supported (phase 4 message polish)
    
    Note: OpenCode nests baseURL/apiKey under `settings_config.options`
    (different from OpenClaw's root-level fields) and uses `baseURL` with
    a capital L. Three new extractors (base_url / api_key / npm) encode
    these shape differences so check_opencode_stream stays symmetric with
    check_openclaw_stream.
    
    Frontend: drop the remaining `appId !== "opencode"` filter in
    ProviderList.tsx — both apps can now test providers.
  • feat(stream-check): support OpenClaw openai-completions protocol
    Phase 1 of extending stream health check to OpenCode/OpenClaw apps.
    
    - Add early-dispatch path for OpenCode/OpenClaw in check_once so they
      bypass the adapter layer (which only knows Claude/Codex/Gemini
      settings_config shapes).
    - Introduce check_openclaw_stream dispatcher that reads the `api` field
      from settings_config and routes to the existing check_claude_stream
      with api_format="openai_chat" for "openai-completions". Other
      protocols return localized errors to be lit up in phases 2 and 4.
    - Extract build_stream_check_result helper to avoid duplicating the
      StreamCheckResult construction logic between the two code paths.
    - Unblock the test button for OpenClaw providers in ProviderList.tsx.
    
    OpenCode still returns the "not yet supported" error; it will be
    enabled in phase 3.
  • fix(providers): disable test/usage buttons for Copilot and Codex OAuth cards
    These OAuth providers ship with non-empty ANTHROPIC_BASE_URL, so the
    isOfficialProvider() heuristic (which checks for a missing base URL)
    returned false and left the health-check and usage-config buttons
    enabled — inconsistent with other official OAuth cards. Extend the
    button disabling logic at the call site with isCopilot / isCodexOauth,
    matching the pattern already used for the quota footer branch above.
  • chore(presets): bump Codex OAuth preset to GPT-5.4 family
    Update the "Codex (ChatGPT Plus/Pro)" entry in Claude Code presets to
    the new GPT-5.4 naming, which drops the legacy `-codex` suffix. Map the
    Haiku tier to `gpt-5.4-mini` for lower-cost lightweight calls while
    keeping Sonnet/Opus on the standard `gpt-5.4`.
  • fix(linux): repair unresponsive UI on startup and full-screen panels
    Linux users reported the window UI (including native title bar buttons)
    couldn't receive clicks until manually maximizing and restoring the
    window. Root causes: (1) Tauri webview did not acquire focus on startup
    so first clicks were consumed by X11/Wayland click-to-activate
    (Tauri #10746, wry #637); (2) GTK surface input region failed to
    renegotiate on the visible:false + show() path under some
    WebKitGTK/compositor combinations.
    
    - Add linux_fix::nudge_main_window helper that performs set_focus plus
      a ±1px no-op resize after window show, with a 500ms reconciliation
      readback to compensate for dropped resize requests on slow
      compositors.
    - Wire the helper into every window re-show path: normal startup,
      deeplink, single_instance, tray show_main, and lightweight exit.
    - Set WEBKIT_DISABLE_COMPOSITING_MODE=1 at startup to avoid resize
      crashes and Wayland surface negotiation issues.
    - Remove data-tauri-drag-region on Linux from App.tsx header and the
      shared FullScreenPanel (used by all provider/MCP/workspace forms)
      to avoid Tauri #13440 in Wayland sessions. Extract drag-region
      constants to src/lib/platform.ts for reuse.
    
    All Rust changes are gated by #[cfg(target_os = "linux")]; frontend
    changes preserve macOS/Windows behavior via runtime isLinux() checks.
    Known limitation: tiling Wayland compositors ignore set_size, so
    GDK_BACKEND=x11 remains the user-side workaround.
  • i18n(zh): unify Skills terminology in settings labels
    Use "Skills" consistently in skillStorage title/description and
    skillSync title to match the upstream Agent Skills wording and the
    existing English label style used elsewhere on the settings page.
  • refactor: tighten OAuth Auth Center copy, layout, and icon
    - Trim Auth Center section descriptions to focus on user intent
    - Remove duplicate outer heading on the auth settings tab
    - Swap Sparkles glyph for CodexIcon on the ChatGPT card
    - Generalize codexOauth.authStatus to a neutral "Auth status"
    - Register settings.authCenter.* keys across zh/en/ja locales
  • feat: display subscription quota for Codex OAuth provider cards
    Codex OAuth (ChatGPT Plus/Pro) providers previously fell through to the
    default UsageFooter branch and showed no quota at all, while Copilot and
    official Codex providers already had a wham/usage-backed quota footer.
    
    This wires up the same five-hour / seven-day tier badges for codex_oauth
    provider cards by reusing the existing query_codex_quota function and
    SubscriptionQuotaFooter rendering, parameterized to keep both the CLI
    credential path ("codex") and the cc-switch managed OAuth path
    ("codex_oauth") working from a single source of truth.
    
    - Parameterize services::subscription::query_codex_quota with tool_label
      and expired_message; promote SubscriptionQuota constructors to
      pub(crate). The CLI path keeps its existing "codex" label and the
      "re-login with Codex CLI" message; the new path passes "codex_oauth"
      and a cc-switch-specific re-login hint.
    - Add a new get_codex_oauth_quota Tauri command in commands/codex_oauth.rs
      that resolves the ChatGPT account (explicit binding > default account
      > not_found), pulls a valid access_token from CodexOAuthManager
      (auto-refresh handled), and delegates to query_codex_quota.
    - Extract SubscriptionQuotaFooter's render body into a pure
      SubscriptionQuotaView component (props: quota / loading / refetch /
      appIdForExpiredHint / inline). The existing SubscriptionQuotaFooter
      becomes a thin wrapper with identical props and behavior, so
      CopilotQuotaFooter and the official Claude/Codex/Gemini paths are
      untouched. This avoids duplicating ~280 lines of five-state rendering.
    - Add CodexOauthQuotaFooter, a 38-line wrapper that calls the new
      useCodexOauthQuota hook and forwards to SubscriptionQuotaView.
    - ProviderCard inserts an isCodexOauth branch between isCopilot and
      isOfficial, keyed off PROVIDER_TYPES.CODEX_OAUTH (newly added to
      config/constants.ts to centralize the previously scattered string).
    - Frontend hook caches per (codex_oauth, accountId) so multiple cards
      bound to the same ChatGPT account share one fetch via react-query
      dedup; cards bound to different accounts get independent fetches.
    - No new i18n keys: existing subscription.fiveHour / sevenDay / expired /
      refresh / queryFailed / expiredHint are reused.
  • feat: add Codex OAuth (ChatGPT Plus/Pro) reverse proxy support
    Adds a new managed OAuth provider that lets Claude Code route requests
    through a user's ChatGPT Plus/Pro subscription via the chatgpt.com
    backend-api/codex endpoint.
    
    - CodexOAuthManager: OpenAI Device Code flow with multi-account support,
      JWT-based account identification, and automatic access_token refresh.
    - Reuses the generic managed-auth command surface (auth_start_login,
      auth_poll_for_account, etc.) via provider dispatch in commands/auth.rs.
    - ClaudeAdapter detects codex_oauth providers, forces the base URL to
      the ChatGPT backend, pins api_format to openai_responses, and emits
      Authorization + originator headers; the forwarder injects the dynamic
      access_token and ChatGPT-Account-Id per request.
    - transform_responses gains an is_codex_oauth path that aligns the body
      with OpenAI's codex-rs ResponsesApiRequest contract: sets store:false,
      appends reasoning.encrypted_content to include, strips max_output_tokens
      / temperature / top_p, injects default instructions/tools/parallel_tool_calls,
      and forces stream:true. Covered by 9 new unit tests plus regression
      guards for the non-Codex path.
    - Stream check reuses the same transform flag so detection matches the
      production request shape.
    - Frontend adds CodexOAuthSection + useCodexOauth hook, integrates it
      into ClaudeFormFields / ProviderForm / AuthCenterPanel, ships a new
      "Codex (ChatGPT Plus/Pro)" preset, and adds zh/en/ja i18n strings.
  • fix: resolve rustfmt formatting and clippy warnings
    - Apply cargo fmt across schema.rs, session_usage*.rs, skill.rs, usage_stats.rs
    - Fix clippy::for_kv_map: use messages.values() instead of (_, msg) pattern
    - Suppress clippy::only_used_in_recursion for intentional recursive base path
    - Fix prettier formatting in UsageScriptModal.tsx
  • feat: display Copilot premium interactions quota on provider card
    Copilot usage query API was implemented but never surfaced on the main
    provider list. Add CopilotQuotaFooter component that auto-detects
    github_copilot providers and displays premium interaction utilization
    inline, reusing the existing TierBadge UI from SubscriptionQuotaFooter.
  • 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.
  • 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.
  • 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)
  • 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
  • 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
  • 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.
  • 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.
  • 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.
  • 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.