Commit Graph

405 Commits

Author SHA1 Message Date
Jason ce232a1452 feat(settings): diagnose conflicting tool installations with uninstall hints
Add a "Diagnose installs" action to the About section that enumerates every
installation of each managed CLI, flags real conflicts (diverging versions
or mixed runnable state), and lists them under the affected tool card.

- backend: enumerate_tool_installations + diagnose_tool_installations command,
  sharing build_tool_search_paths with the single-probe scan; resolve the PATH
  default via the same login shell as version probing so the "Default" marker
  matches what the command line (and an upgrade) actually targets
- frontend: global diagnose button left of Refresh, per-card conflict list,
  and a copy-only uninstall command per install -- source-aware (npm/volta/
  bun/pip) and anchored to that install's own npm to avoid removing the wrong
  node version; marked with a red trash icon, never executed
- auto-diagnose silently after an upgrade that leaves the version unchanged
  or installs something that can't run
- i18n: zh/en/ja
2026-05-25 22:20:34 +08:00
Jason ea604a1827 fix(settings): surface "installed but not runnable" state in tool cards
After an install/update command succeeds, re-probe the tool version: if it
still can't be detected the tool is installed-but-unrunnable (e.g. Node too
old), so report a warning toast and show "installed · can't run" with the
cause, instead of a misleading "success" plus a stale version number.

Read the backend `installed_but_broken` flag rather than matching error
text; the card's current-version line, action button (no useless "install"
for a broken tool), and a "check environment" hint all key off it. Drop the
now-redundant version-clearing setState (the refresh merge already nulls it),
flatten the action ternary, and use extractErrorMessage in the catch path.

Adds toolNotRunnable / toolActionInstalledNotRunnable / installedNotRunnable
/ toolCheckEnv i18n keys (zh/en/ja).
2026-05-25 22:20:34 +08:00
Jason f8b4d67b21 fix(settings): run batch updates per-tool and lock actions while busy
Update All previously sent every tool to the backend as one script, where
set -e (or errorlevel on Windows) aborts the whole batch on the first
failure, skipping the rest and not refreshing versions for tools that did
succeed. Run each tool as its own call instead: failures are isolated,
each tool's version refreshes as soon as it finishes, and the result is
summarized in a toast (success / partial / failure) listing which tools
failed.

Also derive an isAnyBusy flag and disable all install/update buttons,
the refresh button and the WSL shell selectors while any action is
running, so users can't trigger concurrent global npm/pip writes.

Add the toolActionPartial i18n key (zh/en/ja).
2026-05-25 22:20:34 +08:00
Jason e3df86587d feat(settings): expand About page into a tool management panel
List all managed apps with current/latest versions, with per-tool install
and update buttons plus an Update All action. Installs and updates now run
silently: the button shows a spinner for the full duration, versions refresh
automatically when done, and failures surface the backend error detail in a
toast.

While loading, the button keeps its label and only swaps the icon for a
spinner, so width stays constant across zh/en/ja instead of jumping when the
text changes (e.g. Update -> Updating...).

Also collapses and renames the install-commands area to Manual Install
Commands, and removes the managed-apps badge row and the Install Missing
batch button.
2026-05-25 22:20:34 +08:00
Jason 44d9aabbf3 feat: adaptive reasoning detection for Codex Chat providers
Auto-detect each Chat-routed Codex provider's reasoning interface from
its name, base URL, and model, then inject the matching thinking
parameter without manual configuration:

- Platform-first inference (OpenRouter, SiliconFlow) overrides model
  rules, since the same model exposes different reasoning controls
  depending on the hosting platform.
- Effort tiers are forwarded only to providers that support them
  (DeepSeek, OpenRouter, and StepFun's step-3.5-flash-2603); on/off-only
  providers (Kimi, GLM, Qwen, MiniMax, MiMo, SiliconFlow) drop the level
  instead of sending a field the upstream rejects.
- OpenRouter uses the native reasoning:{effort} object, clamps max to
  xhigh (its enum has no max), and forwards an explicit effort:"none" so
  reasoning can be turned off.
- StepFun falls back to inference so per-model effort support is honored
  (the static preset would have forced effort on step-3.5-flash too).

Includes the Codex provider-form reasoning controls, i18n strings
(zh/en/ja), and response-side reasoning extraction.
2026-05-25 22:20:33 +08:00
Jason 955904f719 refactor: rewrite Codex local routing toggle hints for clarity
Reframe OFF/ON hints as action guidance (when to enable) rather than
scenario descriptions, mirroring the Claude Desktop model mapping copy
style. Synced across zh/en/ja locales and the component-level
defaultValue fallback. The switch label "Needs Local Routing" is kept
unchanged to preserve the routing-layer semantics specific to Codex.
2026-05-25 22:20:33 +08:00
Jason 791ced0034 fix: Codex model catalog WYSIWYG and config consolidation
- Remove mergeCodexDefaultCatalogModelForSave implicit injection (P1)
  The model mapping table is now the single source of truth; no hidden
  entries are prepended on save.

- Sync first catalog row model into config.toml on save
  Ensures Codex default request model matches the table's first entry
  instead of retaining a stale template value.

- Remove API Format selector from CodexFormFields (P3)
  wire_api is always 'responses'; the selector confused users into
  thinking they were changing the upstream protocol. Only the 'Needs
  Local Routing' toggle remains.

- Add restart hint to model mapping i18n text (P2)
  model_catalog_json is loaded at Codex startup; users are now informed
  that a restart is needed after changes.

- Unify write_codex_live_with_catalog helper (P4)
  Replaces three scattered prepare+write call sites in config.rs,
  provider/live.rs, and proxy.rs with a single entry point.

- Clean up useCodexConfigState dead state (P3 follow-up)
  Remove codexModelName, codexContextWindow, codexAutoCompactLimit and
  their handlers/effects since no component consumes them after the UI
  consolidation.
2026-05-25 22:20:33 +08:00
Jason 74acf1e387 Add Codex Chat-to-Responses bridge
- Add Codex provider API format selection and model mapping for Chat-only upstreams.
- Convert Codex Responses requests to Chat Completions and rebuild Chat responses as Responses output.
- Preserve reasoning_content across non-streaming, streaming, tool calls, and previous_response_id follow-ups.
- Add a bounded Codex Chat history cache for restoring tool calls before tool outputs.
- Cover Chat bridge, compact routing, streaming, and history recovery with focused tests.
2026-05-25 22:20:33 +08:00
Jason 1c82b8a3fa Add Chat Completions routing for Codex providers
- Add a Codex API format selector and routing badge for Chat Completions providers.
- Convert Codex Responses requests to upstream Chat Completions when routing is required.
- Convert Chat Completions JSON and SSE responses back to Responses format.
- Keep generated Codex wire_api values on Responses for Codex compatibility.
- Add i18n labels, provider metadata handling, and focused conversion tests.
2026-05-19 10:23:48 +08:00
Jason 76b4c8b509 chore: remove LionCC sponsor and presets
- Remove LionCC sponsor entry from all README files (en/zh/ja)
- Remove LionCCAPI presets from all provider configs
- Remove lionccapi i18n keys from all locales
- Keep lioncc.png icon file as requested
2026-05-18 16:59:13 +08:00
Jason 9e3f168903 chore(release): bump version to 3.15.0 2026-05-16 11:21:18 +08:00
Jason 4a631b28c7 chore(i18n): simplify modelMappingOffHint to action-oriented copy 2026-05-15 23:33:48 +08:00
Jason 9050442b65 feat(presets): add BytePlus provider preset as partner
Add BytePlus (international Volcengine) to Claude, Claude Desktop,
Hermes, OpenCode, and OpenClaw with byteplus icon, 256K context window,
and trilingual promotion text.
2026-05-15 22:45:04 +08:00
Jason d94eb6729b feat(presets): add 火山Agentplan provider preset as partner
Add Volcengine Ark Agentplan to Claude, Claude Desktop, Hermes,
OpenCode, and OpenClaw with huoshan icon and trilingual promotion.
2026-05-15 22:28:26 +08:00
Jason a0131c9a2b chore(presets): update DouBaoSeed endpoint, links, and add as partner
Switch Anthropic-format base URL from /api/coding to /api/compatible,
update website/apiKey URLs to Volcengine console with tracking params,
and promote DouBaoSeed to partner with trilingual promotion text.
2026-05-15 22:00:55 +08:00
Jason 18ffddbfa5 feat(presets): add RunAPI provider presets
Add RunAPI as a new partner provider with support for:
- Claude Code preset (Anthropic native protocol)
- Codex preset (gpt-5.5 model)
- Claude Desktop preset (direct mode with passthrough routes)
- OpenCode preset (@ai-sdk/anthropic)
- OpenClaw preset (anthropic-messages protocol)
- Hermes preset (anthropic_messages mode)
- Icon configuration (runapi.jpg)
- i18n support (zh/en/ja) with ¥14 free credit promotion

RunAPI is a high-performance AI model API gateway supporting 150+
mainstream models (OpenAI, Claude, Gemini, DeepSeek, Grok) with
prices as low as 10% of official rates.
2026-05-15 17:20:46 +08:00
Jason d6bbbf72f0 feat(presets): add ClaudeCN provider presets
Add ClaudeCN as a new partner provider with support for:
- Claude Code preset (Anthropic native protocol)
- Codex preset (gpt-5.5 model)
- Claude Desktop preset (direct mode with passthrough routes)
- OpenCode preset (@ai-sdk/anthropic)
- OpenClaw preset (anthropic-messages protocol)
- Hermes preset (anthropic_messages mode)
- Icon configuration (claudecn.png)
- i18n support (zh/en/ja) with enterprise service promotion

ClaudeCN is an enterprise-grade AI gateway operated by a registered
company, supporting enterprise procurement processes with corporate
payments, contracts, and compliance guarantees.
2026-05-14 23:28:07 +08:00
Jason df11df4d9c feat(presets): add ClaudeAPI provider presets
Add ClaudeAPI as a new partner provider with support for:
- Claude Code preset (using ANTHROPIC_AUTH_TOKEN field)
- Claude Desktop preset (direct mode with passthrough routes)
- Icon configuration (ClaudeApi.png)
- i18n support (zh/en/ja) with test credit promotion

ClaudeAPI provides official Anthropic API keys and AWS Bedrock
routing with support for Tool Use and 1M context.
2026-05-14 23:07:49 +08:00
Jason 08cd5ab56e feat(presets): add PatewayAI provider presets
Add PatewayAI as a new partner provider with support for:
- Claude Code preset (using ANTHROPIC_API_KEY field)
- Codex preset (gpt-5.5 model)
- Claude Desktop preset (proxy mode with passthrough routes)
- Icon configuration (pateway.jpg)
- i18n support (zh/en/ja) with $3 registration bonus promotion

PatewayAI provides reliable API routing services for Claude Code,
Codex, and Gemini models.
2026-05-14 22:36:45 +08:00
Jason 940161fb0e feat(providers): add routing support badges for Claude Code and Codex
Add visual indicators for routing capabilities on provider cards:
- Claude Code: "Needs Routing" badge for non-official providers with non-anthropic API formats
- Claude Code: "No Routing Support" badge for official providers
- Codex: "No Routing Support" badge for official providers

The badges help users understand which providers support format conversion through routing.
2026-05-14 17:51:42 +08:00
Jason 99304ffcfd chore(partners): remove DDSHub partner integration
Remove DDSHub from all provider presets (Claude, Claude Desktop, Codex, Hermes),
i18n files (zh/en/ja), README docs, and icon system. Physical assets retained for
potential future restoration.
2026-05-14 15:35:35 +08:00
Jason 3c35972548 fix(proxy-ui): accept IPv6 listen addresses in ProxyPanel validation
The backend already understands `::` -> `::1` and wraps IPv6 literals
in brackets (services/proxy.rs), but the panel's save-time validator
only accepted localhost, 0.0.0.0, and IPv4 dotted-quads. Users who
wanted to listen on an IPv6 loopback had to bypass the UI and edit
config directly.

Add an isValidIpv6 helper that requires at least one ':' and round-trips
through `new URL('http://[<addr>]/')` so the platform's built-in IPv6
parser does the heavy lifting (covers compressed `::`, full 8-group
form, zone IDs). Update the invalidAddress copy in zh / en / ja so the
error message reflects the new accepted set.
2026-05-13 23:36:44 +08:00
Jason 4b57f7e113 feat(claude-code): role-based model mapping with display names and 1M flag
- Replace the four flat env inputs with a Sonnet/Opus/Haiku role table.
  Each row exposes ANTHROPIC_DEFAULT_*_MODEL plus a new display name
  field ANTHROPIC_DEFAULT_*_MODEL_NAME, and Sonnet/Opus gain a
  "Declare 1M" checkbox that toggles the [1M] suffix.
- Strip the [1M] context-capability marker before forwarding non-Copilot
  requests upstream. Copilot keeps its existing [1m]->-1m normalization.
- Claude Desktop import now consumes ANTHROPIC_DEFAULT_*_MODEL_NAME as
  label_override, closing the Claude Code -> Claude Desktop displayName
  pipeline; add_route's merge logic is shared between hashmap branches.
- Unify the [1M] marker as ONE_M_CONTEXT_MARKER across
  claude_desktop_config and proxy::model_mapper; rename the strip
  helper to strip_one_m_suffix_for_upstream.
- Collapse useModelState's seven duplicated useState initializers and
  the useEffect parse block into a single parseModelsFromConfig call.
- Add tests/hooks/useModelState.test.tsx and a Claude Desktop import
  test covering Kimi K2 -> label_override. i18n (en/ja/zh) updated.
2026-05-13 17:06:05 +08:00
Jason 84bac6dce6 refactor(claude-desktop): lock route IDs to sonnet/opus/haiku roles
Adapt to Claude Desktop 1.6259.1+ fail-all validation which only
accepts claude-(sonnet|opus|haiku)-* route IDs. Branded model names
(DeepSeek, Kimi, GLM, etc.) now live in a new labelOverride field
instead of being embedded in route IDs.

- Backend auto-repairs legacy unsafe routes to the next free
  sonnet/opus/haiku slot instead of erroring
- Frontend swaps the free-form route input for a role dropdown plus
  menu display name field
- Add CLAUDE_DESKTOP_ROLE_ROUTE_IDS as the single source of truth
  for role-to-route mapping; presets and form both consume it
- Drop the dead displayName alias on ClaudeDesktopModelRoute and the
  ineffective /v1/models display_name injection (UI ignores it)
- Update i18n (en/ja/zh) and form focus test for the new fields
2026-05-13 15:22:23 +08:00
Jason edf28b6422 feat(usage): filter-driven Hero with cache-normalized totals
- Normalize OpenAI/Gemini input_tokens semantics in SQL via the new
  fresh_input_sql helper (cache_read subtracted at query time, no data
  migration). Recovers correct cache hit rates for Codex/Gemini.
- Add get_usage_summary_by_app endpoint for per-app split (single
  UNION ALL + GROUP BY, avoids N+1).
- Replace UsageSummaryCards + AppBreakdownRail with a single
  filter-driven UsageHero card; clicking a filter button now truly
  changes the displayed numbers and the title accent color.
- Tighten KNOWN_APP_TYPES to the 3 app_types whose token data is
  reliably collected (claude/codex/gemini); hide claude-desktop,
  hermes, opencode, openclaw filter buttons and i18n keys.
- Flag cache_creation as N/A for OpenAI-style protocols (Codex,
  Gemini); show a "partial" tooltip when the All view mixes both
  protocol families.
2026-05-13 10:27:29 +08:00
Jason c12364a940 feat(claude-desktop): rework Claude Code import flow
- Derive route keys from the upstream model name (pass-through style)
  instead of fixed Claude aliases, and translate the legacy [1M] suffix
  into the supports1m field at the import boundary. Three Claude aliases
  mapped to the same upstream now collapse to a single route (e.g.
  MiniMax-M2 across SONNET/OPUS/HAIKU env produces one
  claude-MiniMax-M2 -> MiniMax-M2 row), with [1M] OR-aggregated.
- Add an import-time safety net that rebuilds claude-desktop-official
  when missing, so users who deleted it can recover via the normal
  import button without losing customizations on other providers.
- Hide API key and endpoint URL inputs in the official provider edit
  form to mirror Claude Code's behavior and prevent user confusion.
- Reword the empty-state import button label for clarity.
2026-05-12 21:31:30 +08:00
Jason 60a3628360 refactor(claude-desktop): replace [1M] suffix with supports1m field
inferenceModels entries now emit {name, supports1m: true} objects when
1M is enabled (plain strings otherwise), instead of appending a " [1M]"
suffix to model IDs. Route IDs and upstream model IDs are stored
verbatim; the suffix is rejected on input rather than silently stripped,
and proxy request mapping now requires an exact route_id match.
2026-05-12 17:40:32 +08:00
Jason 953b7cdcf9 refactor(claude-desktop): drop displayName from model route schema
Claude Desktop's new model menu reads model IDs directly and ignores the
display_name field, so a separate displayName slot added UI noise without
any product value. Collapse the routeId / model / displayName tuple down
to routeId / model, and let the route ID carry the user-visible name
through a non-editable claude- prefix rendered next to the input.

Drop display_name from ClaudeDesktopModelRoute, ClaudeDesktopDefaultRoute,
and ResolvedModelRoute on the Rust side plus the matching TS interfaces,
stop emitting it in /v1/models responses, derive route IDs from upstream
model IDs when picked via the model dropdown, and update zh/en/ja copy to
describe the new two-field layout.
2026-05-12 10:46:35 +08:00
Jason 968c75bdbe feat(ui): use "Claude Code" label in app visibility settings
The app visibility section in Settings showed "Claude" for the first
entry, identical to "Claude Desktop" at a glance. Add a dedicated
i18n key apps.claudeCode and point the settings panel at it, while
leaving apps.claude untouched so other panels (MCP, Skills, Usage,
etc.) keep their shorter "Claude" label.
2026-05-11 23:15:30 +08:00
Jason 2fc6753e42 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.
2026-05-11 15:25:48 +08:00
Jason 5bbd83f7ca 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.
2026-05-09 17:19:09 +08:00
Jason 309f7609a5 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.
2026-05-08 22:34:46 +08:00
Jason 21b9eb0430 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)
2026-05-08 22:34:46 +08:00
Jason 2deee1097b 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
2026-05-08 22:34:46 +08:00
Jason 34698723e3 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.
2026-05-08 22:34:46 +08:00
Jason 8b3ad9caf9 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.
2026-05-08 22:34:46 +08:00
Butui Hu 1d44b1ba41 feat(universal-provider): add duplicate action for universal providers (#2416)
Signed-off-by: Hu Butui <hot123tea123@gmail.com>
2026-05-02 21:36:07 +08:00
Jason 518d945eb8 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.
2026-05-01 23:07:46 +08:00
jimmy db66348ff8 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
2026-05-01 17:35:18 +08:00
tison 608ee35ecd feat: support launch warp and execute session (#2466)
* feat: support launch warp and execute session

Signed-off-by: tison <wander4096@gmail.com>

* other wires

Signed-off-by: tison <wander4096@gmail.com>

* for launch with provider

Signed-off-by: tison <wander4096@gmail.com>

* fixup indirection

Signed-off-by: tison <wander4096@gmail.com>

* clippy

Signed-off-by: tison <wander4096@gmail.com>

* address comments

Signed-off-by: tison <wander4096@gmail.com>

---------

Signed-off-by: tison <wander4096@gmail.com>
2026-04-30 10:33:50 +08:00
Jason f061b777b7 feat(usage): add Hermes Agent tracking + fix zero-cost bug + perf
Hermes:
- Parse ~/.hermes/state.db sessions (incl. profiles/*/state.db) into
  proxy_request_logs with data_source='hermes_session', WAL-aware
  incremental sync, Hermes-reported cost preferred over model_pricing
  fallback

Zero-cost bug (dashboard showed \$0 totals):
- GPT-5.5 family default pricing (~83% of affected rows used GPT-5.5)
- find_model_pricing_row: ASCII-lowercase normalization so
  "OpenAI/GPT-5.5@HIGH" matches seeded "gpt-5.5"
- Startup cost backfill in async task: scan rows where total_cost <= 0
  but tokens > 0, recompute via model_pricing in a single transaction

Performance:
- Add (app_type, created_at DESC) covering index for dashboard range
  queries
- Add expression index on COALESCE(data_source, 'proxy') so dedup EXISTS
  subqueries use index lookup instead of full scan; drop superseded
  idx_request_logs_dedup_lookup

Refactor:
- row_to_request_log_detail helper (3-way de-dup; fixes cost_multiplier
  \"1\" vs \"1.0\" drift between callers)
- Promote get_sync_state/update_sync_state to shared session_usage
  module (4 copies -> 1)
- run_step helper in lib.rs replaces 9 if-let-Err blocks
- maybe_backfill_log_costs returns bool to skip duplicate total_cost
  parsing in caller
2026-04-29 22:41:46 +08:00
allen_xln 85f0be9e1d feat(provider-form): soften validation with "save anyway" prompt (#2307)
* feat(provider-form): soften business-rule validation with "save anyway" prompt

Refactor handleSubmit so empty-field / missing-item validations (provider
name, endpoint, API key, opencode model, template variables, provider key
required) no longer hard-reject with toast.error. Instead they are collected
into an issues list and presented via a ConfirmDialog; the user can cancel
or choose "Save anyway" to proceed.

Integrity constraints stay as hard rejections:
- providerKey regex / duplicate (would corrupt other providers)
- Copilot / Codex OAuth not authenticated (no token, cannot establish)
- omo Other Fields JSON not an object / parse failure

This aligns the frontend with the backend's existing "relaxed save / strict
switch" split (see gemini_config.rs: validate_gemini_settings vs
validate_gemini_settings_strict) and unblocks legitimate configs such as
AWS Bedrock, Vertex AI, and custom Gemini base URLs that the UI previously
refused to save.

Refs: #2196, #1204

* fix(provider-form): address review feedback on soft-validation

P1: move empty providerKey back to hard rejection for OpenCode / OpenClaw /
Hermes. Since providerKey is the primary identity for these apps and the
mutations layer throws "Provider key is required" when absent, letting users
click "save anyway" would surface a generic error toast instead of a
precise, actionable one. Treat empty providerKey as an integrity constraint
alongside regex / duplicate checks.

P2: give the soft-confirm submit path its own submitting state. The
confirm-dialog path bypassed react-hook-form's isSubmitting lifecycle, so
slow or failing saves left the outer submit button responsive and could
spawn unhandled rejections. Now the confirm handler awaits performSubmit
inside try/catch/finally, uses an isConfirmSubmitting flag to gate both
confirm and cancel clicks, and folds the flag into the outer disabled
state and onSubmittingChange callback.

Refs: #2307 review comments

* chore(clippy): use push for single char '…' in truncate_body

Clippy 1.95 added single_char_add_str which flagged the push_str("…")
in truncate_body. Rebased onto latest upstream/main and applied the
suggested fix so the Backend Checks clippy job passes.

Unrelated to this PR's core changes; bundled in so the PR is mergeable
without waiting for a separate upstream fix.

---------

Co-authored-by: Allen <allen@AllenMacBook-M4-Pro.local>
2026-04-25 09:28:28 +08:00
Jason 08e2b29b8a feat(compshare): add Coding Plan preset across claude/codex/hermes/openclaw
Introduce a dedicated "Compshare Coding Plan" variant pointing to
https://cp.compshare.cn (with /v1 for OpenAI-compatible apps). Reuses
the existing ucloud icon and promotion copy, while adding a new
providerForm.presets.ucloudCoding key in zh/en/ja.
2026-04-24 23:57:04 +08:00
Jason 67dbfc0a8c fix(model-fetch): support /models for Anthropic-compat subpath providers
Providers like DeepSeek, Kimi, Zhipu GLM and MiniMax expose the
Anthropic-compatible API on a subpath (e.g. /anthropic) while the
OpenAI-style /models endpoint lives at the API root. The previous
heuristic blindly appended /v1/models to the Base URL, so every such
provider returned 404 and the UI mislabeled it as "provider does not
support fetching models".

Backend now generates a candidate list and tries them in order:
preset override -> baseURL /v1/models -> stripped-subpath /v1/models ->
stripped-subpath /models. Non-404/405 responses (auth, network) stop
immediately so we never retry against hostile status codes. Known
compat suffixes are kept in a length-descending constant so the
longest match wins; response bodies are truncated to 512 chars to
avoid HTML 404 pages bloating the error string.

Preset type gains an optional modelsUrl (DeepSeek points at
https://api.deepseek.com/models). Frontend threads the override
through fetchModelsForConfig when the current Base URL still matches
the preset default. A new fetchModelsEndpointNotFound i18n key
replaces the misleading "not supported" toast for exhausted-candidate
and 404/405 cases (zh/en/ja).
2026-04-24 23:20:10 +08:00
Jason cdcc423122 refactor(hermes): drop config health check scanner
The Hermes config.yaml schema has stabilized and users have migrated to
the current provider fields, so the value of scanning for model.provider
dangling references, custom_providers shape errors, v12 migration residue
etc. no longer justifies the maintenance surface — and the scan produces
false positives when users keep some providers under Hermes' v12+
providers: dict (Hermes' runtime merges both shapes, but CC Switch's
scanner only looked at the list form).

Removes the whole HermesHealthWarning type, scan_hermes_config_health
command, HermesHealthBanner React component, useHermesHealth hook,
warnings field on HermesWriteOutcome, and the three helper functions
(yaml_as_non_empty_str, collect_mapping_string_keys, hermes_warning)
that only served the scanner. Drops the matching i18n keys in
zh/en/ja and the fixInWebUI button label that only the banner used.
2026-04-23 16:18:38 +08:00
Jesus Díaz Rivas 10e0772d8c feat: Add Codex OAuth FAST mode toggle (#2210)
* Add Codex OAuth FAST mode toggle

* fix(codex-oauth): default FAST mode to off to avoid surprise quota burn

service_tier="priority" consumes ChatGPT subscription quota at a higher
rate. Users must now opt in explicitly rather than inherit FAST mode
silently when this feature ships.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-04-23 12:05:17 +08:00
Jason f4a9021461 feat(presets): add LemonData provider across all six apps
Register LemonData (third-party partner) preset for Claude, Codex,
Gemini, OpenCode, OpenClaw, and Hermes, plus icon assets and i18n
partner promotion copy (zh/en/ja). Claude preset uses
ANTHROPIC_API_KEY auth; OpenAI-compatible apps target gpt-5.4.
2026-04-22 10:39:17 +08:00
Jason 8e9452b7ec feat(hermes): launch dashboard from toolbar when Web UI is offline
When the Hermes Web UI probe fails, the toolbar entry now opens an info
confirm dialog offering to run `hermes dashboard` in the user's preferred
terminal. Accepting spawns it via a temp bash/batch script; `hermes
dashboard` itself opens the browser once ready, so we do not poll.
The Memory panel and Health banner keep the existing toast behavior.

Also corrects the stale `hermes web` hint in the offline toast (the real
command is `hermes dashboard`) and reorders Linux terminal detection to
try `which` before stat'ing /usr/bin, /bin, /usr/local/bin.
2026-04-21 21:11:15 +08:00
Jason 0be75668cc feat(hermes): align provider schema with Hermes Agent 0.10.0
Hermes 0.10.0 tightened custom_providers validation (commit 2cdae233):
invalid base_urls are rejected, unknown fields produce warnings, and
new fields (rate_limit_delay, bedrock_converse, key_env) landed.

- Add bedrock_converse to the api_mode selector (and i18n labels)
- Expose rate_limit_delay in a provider-level advanced panel
- Validate base_url client-side (URL shape, template-token friendly)
- Drop per-model max_tokens — not in _VALID_CUSTOM_PROVIDER_FIELDS
- Round-trip test asserts set_provider preserves rate_limit_delay /
  key_env / any unknown forward-compat field
2026-04-21 11:57:07 +08:00
Jason 21a1518f9f refactor(hermes): drop "Auto" api_mode and require explicit protocol
Hermes' built-in api_mode detection only matches a handful of official
endpoints (api.openai.com, api.anthropic.com, api.x.ai, AWS Bedrock);
third-party / proxy endpoints silently fall back to chat_completions,
which causes opaque 401/404s on Anthropic-protocol or Codex-Responses
providers. The "Auto" option was misleading for the common third-party
case.

- Drop the "Auto" option from the API Mode dropdown; remove the
  HermesApiModeChoice sentinel type so writes always emit api_mode.
- Default new providers and legacy entries lacking api_mode to
  chat_completions (only persisted on user save).
- Deeplink imports now write api_mode: chat_completions explicitly
  instead of relying on URL heuristics; test renamed accordingly.
- Rename the "Codex Responses (Copilot / OpenCode)" label to
  "OpenAI Responses" to match OpenAI's /v1/responses naming.
2026-04-21 11:57:07 +08:00