The uninstall hint feature added in f6bdd4bb / 89e2339b printed a
copy-pasteable shell command (e.g. `brew uninstall <formula>`,
`<sibling-npm> rm -g <pkg>; [ -e bin ] && rm -f bin && rm -rf pkg_dir`)
under each detected install in the multi-install conflict panel. Even
with copy-only-never-execute UX, putting destructive commands one
button away from the user is risk we don't want to ship — `rm -rf`
under a misclassified path would be unrecoverable.
Conflict diagnostics themselves are kept: users still see the warning
banner + per-install row (source / path / version / Default badge), so
the "you have multiple installs and the upgrade only touches one"
signal survives. They just don't get a one-click destructive command.
Backend (src-tauri/src/commands/misc.rs):
- Remove `uninstall_command_from_paths` (POSIX + Windows pair).
- Remove `posix_quote_for_user_shell` and `win_quote_path_for_user_shell`
— both were uninstall-only quoters (whitelist + cmd-only forms).
`win_quote_path_for_batch` and `shell_single_quote` stay because the
anchored upgrade path and `build_shell_cd_command` still use them.
- Remove `quoted_sibling_or_bare` and `quoted_sibling_or_bare_with_ext`
thin wrappers (only callers were uninstall branches).
- Drop `uninstall_command` and `uninstall_command_needs_cmd_hint` from
`ToolInstallation`. Drop the eager `infer_install_source` ->
`uninstall_command_from_paths` call in `enumerate_tool_installations`
(saves ~16μs per probe but the real point is fewer surprises).
- Remove 14 `#[test]` cases for uninstall command shape (brew /
homebrew-npm-global / nvm / npm-fallback quoting / pnpm / volta / bun /
hermes / system / Windows percent-path) plus 4 `win_quote_path_for_user_shell`
helper tests and the `expect_user_shell_quoted_path` test mirror.
- Drop the `src()` test helper that was only used to feed the deleted
uninstall tests (`infer_install_source` is still called directly by
surviving tests).
- Cross-references in `anchored_command_from_paths` doc to its now-gone
uninstall "dual" are stripped.
Frontend (src/components/settings/AboutSection.tsx):
- Conflicts `<ul>` keeps the diagnostic header + per-install `ToolInstallRow`
but loses the inline command box, the trash + copy icons, the per-row
PowerShell-call-operator hint, and the `handleCopyCommand` callback.
- Remove the `Trash2` lucide import; `Copy` stays (the install-commands
copy button at line 1100 still uses it).
API (src/lib/api/settings.ts):
- Drop `uninstall_command` and `uninstall_command_needs_cmd_hint` from
the `ToolInstallation` TS interface (matches the Rust struct change).
i18n (zh / en / ja):
- Drop `settings.toolUninstallCopyHint`, `settings.toolUninstallCopied`,
`settings.toolUninstallPwshHint`. Other `tool*` and `skills.uninstall*`
keys are untouched (Skill uninstall is a separate, non-destructive
feature for managing the Skills repo and is unaffected).
Stats: 858 deletions, 5 insertions across 6 files.
Validated:
- cargo test --lib: 1322 passed, 0 failed
- cargo fmt --check: clean
- cargo build: clean
- cargo check --tests: clean
- pnpm typecheck: 0 errors
- JSON.parse on all three locale files: clean
Move uninstall hint command generation from frontend (which used a lossy
`source` string and couldn't tell brew formula from homebrew-npm-global apart)
into the Rust backend, where `brew_formula_from_path` + `infer_install_source`
together produce correct commands for each install. Conflict UI now reads
`inst.uninstall_command` directly.
Backend (src-tauri/src/commands/misc.rs):
- New `uninstall_command_from_paths` (POSIX + Windows) mirrors the
`anchored_command_from_paths` family. POSIX branches on brew formula /
volta / bun / pnpm / nvm|fnm|mise|homebrew / system. Windows branches on
volta / pnpm / npm.
- New `quoted_sibling_or_bare` (POSIX) + `quoted_sibling_or_bare_with_ext`
(Windows) deduplicate the `sibling_bin().map(quote).unwrap_or_else(bare)`
pattern across all uninstall branches.
- New `posix_quote_for_user_shell` whitelists `[A-Za-z0-9._/+=:@-]`; any
other character routes through `shell_single_quote`. Replaces
`quote_path_if_spaced` on the uninstall side because the fallback chain
is destructive (`rm -rf <pkg_dir>`), so a `;` / `$` / backtick in the
home dir would have let the chain inject extra commands.
- New `win_quote_path_for_user_shell` (no `%` 4x escape) splits from
`win_quote_path_for_batch`. Uninstall hints are copy-paste targets, not
`.bat + call` payloads, so the two-round percent expansion that
motivates the 4x escape doesn't apply.
- POSIX `nvm|fnm|mise|homebrew` branch appends physical-delete fallback
`; [ -e <bin> ] && rm -f <bin> && rm -rf <pkg_dir>` because nvm v18.20.8
was observed to silently no-op `npm rm -g` (exit 0 with `up to date`
message, no filesystem change). Anchored `<node_root>/{bin,lib}` layout
assumption documented as branch-specific.
- `ToolInstallation` gains `uninstall_command: String` and
`uninstall_command_needs_cmd_hint: bool`. The bool is computed as
`cfg!(target_os = "windows") && uninstall_command.contains('"')` — a
compile-time platform gate avoids false positives from POSIX
`shell_single_quote`'s `'"'"'` escape form, which also contains `"`.
- `enumerate_tool_installations` computes `source` once and threads it
into `uninstall_command_from_paths`, removing a redundant
`infer_install_source` call (review N1).
Frontend (src/components/settings/AboutSection.tsx + src/lib/api/settings.ts):
- Remove the JS helpers `buildUninstallCommand`, `dirOfPath`,
`isWindowsPath`, `shellQuote`, `TOOL_NPM_PACKAGES`. Conflict rows now
read `inst.uninstall_command` directly.
- Render a PowerShell-call-operator hint when
`inst.uninstall_command_needs_cmd_hint` is true. Avoids string-match
inference from the command (which fails on macOS user names containing
`'`, e.g. `o'leary`, because `shell_single_quote`'s escape contains `"`).
- `ToolInstallation` TS interface gains the two new fields.
i18n (zh/en/ja):
- New key `settings.toolUninstallPwshHint` documents the PowerShell
`&` (call operator) requirement for quoted paths.
Tests: 30 new cases under `commands::misc::tests::anchored_upgrade` cover
each POSIX branch (brew formula extraction, npm anchored with fallback,
pnpm, volta, bun, hermes), strong-quoting under `;` / `'` paths, the
fallback-only-for-anchored-branches inverse guard, plus mirror Windows
helpers for the user-shell quoter and a `path%foo%` integration case
demonstrating the literal `%` preservation contract. Total
`commands::misc` test count: 68.
- misc: extend sibling_bin derivation to claude/brew/volta/bun in addition
to npm. Return Option<String> so the anchored-path invariant cannot be
silently violated — when bin_path has no parent directory, propagate
None and fall back to the static command at the call site instead of
emitting a bare command that depends on the GUI process PATH.
- misc: factor out quote_path_if_spaced to keep all five anchored
branches consistent when the resolved path contains spaces.
- AboutSection: add preflightTools lock + try/finally around
probeToolInstallations so fast double-clicks cannot race two probe
rounds into concurrent executeRun calls or duplicate confirm dialogs.
- Tests: cover sibling_bin None branch and space-quoting for every
anchored branch (claude/brew/volta/bun).
Rework the About tool-upgrade flow so upgrades target the install the
command line is actually using, instead of running bare `npm i -g` and
landing on PATH's first npm — which used to move upgrades to the wrong
location or fail outright when the resolved bin had no sibling npm.
Anchored upgrade command (update path, non-Windows):
- claude native installer (~/.local/share/claude/versions/) -> `claude update`
- Homebrew formula (canonical path under /Cellar/) -> `brew upgrade <formula>`
- volta / bun -> `volta install` / `bun add -g`
- Node managers with sibling npm (nvm/fnm/mise/homebrew-npm) -> that
directory's npm with `i -g <pkg>@latest`
- system / unknown sources (e.g. opencode install.sh under ~/.opencode/bin,
~/go/bin) -> fall back to the bare command, surfaced as anchored=false
so the UI shows an honest "default entry not determinable" hint instead
of pretending the upgrade is targeted.
Multi-install confirmation:
- When the probe finds >=2 installs, pop ToolUpgradeConfirmDialog showing
each install (source badge + path + version + Default badge) and the
resolved command before executing. Top-level text only asserts what is
universally true ("won't update all of them; see each tool below");
per-card text adapts to anchored vs unanchored.
- Pending upgrade state stores the full upgrade set; the dialog only
shows the subset that needs confirmation, so non-conflicting tools in
a batch still get upgraded after confirm.
Unified probe command:
- Merge the previous diagnose_tool_installations and plan_tool_upgrades
into a single probe_tool_installations returning installs + is_conflict
+ needs_confirmation + command + anchored. Diagnose button, post-upgrade
auto-diagnose, and pre-upgrade confirmation all share one IPC. The
diagnose button's previous Promise.all of 6 IPCs is now a single call.
resolve_path_default robustness:
- The previous lines().next() was poisoned by interactive .zshrc output
(welcome banners, p10k instant prompts, etc.), causing default-install
detection to fail and dropping multi-install scenarios into the
unanchored fallback even when one install was clearly the native one.
- Replaced with first_abs_path_line that scans for the first
absolute-path line and ignores noise, mirroring how try_get_version
already tolerates banner noise via a regex.
Diagnostic staleness fix:
- diagnoseToolSilently now clears stale diagnostics in the else branch
when the conflict no longer holds (previously only wrote on conflict,
never cleared, so externally resolving a conflict left the card stuck
on a stale list until the user clicked Diagnose).
- Auto-diagnose now triggers after any successful update, not only when
the version didn't change, so the card reflects the latest conflict
state even when an anchored upgrade succeeds while another install
remains.
Other:
- Shared ToolInstallRow between the conflict list and the confirmation
dialog removes the duplicated per-install row JSX.
- Drop the new shell_quote_path helper in favor of reusing the file's
existing POSIX-correct shell_single_quote, gated on whitespace.
- Collapse displayName lookup into a stable module-level helper to remove
the `as ToolName` cast and avoid recreating the closure each render.
- 17 backend unit tests covering anchored command generation across each
source, brew formula extraction, is_conflicting thresholds, default
install resolution, and the welcome-banner scenario.
- i18n (zh/en/ja): new keys for confirmation dialog
title/hint/will-run/confirm-button and the unanchored hint.
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
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).
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).
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.
Expand tool version detection to all six managed apps (including Hermes
via PyPI), and add a run_tool_lifecycle_action command that installs or
updates CLI tools silently: it captures the subprocess output and blocks
until the command actually finishes instead of popping a terminal window
(Windows uses CREATE_NO_WINDOW). On failure the last lines of stderr are
returned so the UI can surface a useful message.
Also restores the Windows version-detection path by gating try_get_version
behind cfg(not(windows)) so it no longer risks triggering the App
Execution Alias / protocol handler that previously disabled Windows.
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.
The Kimi For Coding preset pins users to a single coding-only model
(kimi-for-coding), restricting model selection. Drop it from the Codex
presets and trim the corresponding row in the trilingual user manual.
The general Kimi preset (Moonshot platform) is kept, and presets for
other apps are unchanged.
ClaudeAPI supports the health/stream check, but the third_party
category triggers the isClaudeThirdParty gate in ProviderCard that
disables the model test button for third-party Claude providers.
Recategorizing as aggregator restores the test action; the partner
star is unaffected since it is driven by isPartner, not category.
Applied to both the Claude Code and Claude Desktop presets.
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.
Enable openai_chat routing with explicit model catalogs across the major
Chinese/Asian providers (DeepSeek, Zhipu GLM, Kimi, MiniMax, Baidu Qianfan,
Bailian, StepFun, Volcengine Agent Plan, BytePlus, DouBaoSeed, ModelScope,
Longcat, BaiLing, Xiaomi MiMo, SiliconFlow, Novita AI, Nvidia, etc.). Each
preset declares its context window so the UI can size catalog rows when the
preset is picked.
Also lands two consistency fixes uncovered along the way:
- Include setCodexCatalogModels in resetCodexConfig's useCallback deps to
match the new third parameter it consumes.
- Realign TheRouter Codex test to the "custom" model_provider bucket
established by the recent third-party unification; the previous assertion
predated that refactor and had been failing on HEAD.
Codex filters resume history by `model_provider`, so switching between
provider-specific ids like `rightcode` and `aihubmix` made past sessions
appear to vanish. Collapse all third-party providers into a single
stable bucket so cross-switch history stays visible.
- Normalize live `model_provider` to "custom" on every Codex write
(reserved built-in ids like openai/ollama are preserved).
- Add device-level one-shot migration that rewrites historical JSONL
session files and the `state_5.sqlite` threads table from legacy
provider ids into the "custom" bucket. Backs up originals under
`~/.cc-switch/backups/codex-history-provider-migration-v1/` and uses
the SQLite Backup API for the state DB.
- Record completion in `settings.json` under `localMigrations` so the
migration is strictly idempotent across launches.
- Update Codex provider preset templates to emit `model_provider = "custom"`
out of the box.
Break bidirectional sync cycle between catalogRows (child) and catalogModels (parent):
- Remove catalogModels from child→parent effect dependencies
- Track last sent data in ref to avoid redundant callbacks
- Sync ref when parent pushes new data to prevent false positives
Fixes severe UI jittering when adding/editing catalog entries.
- 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.
- 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.
- 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.
- 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
* fix(skills): install correct skill from skills.sh search results
When multiple skills share the same directory name across different repos,
SkillCard was passing directory to onInstall/onUninstall, causing handleInstall
to always match the first result. Switch to using the unique key field
(directory:repoOwner:repoName) for precise identification.
* test(skills): add regression test for skills.sh install by key
Verifies that clicking install on the second card when two skills share
the same directory name correctly installs the second skill, not the first.
---------
Co-authored-by: mrzhao <mrzhao@iflytek.com>
The step was 0.01, preventing input of prices like DeepSeek's cache read
cost ($0.0028/million tokens). Extract step value to a constant and apply
to all four price fields.
Closes#2503
* feat: add Xiaomi MiMo token plan presets
* fix: update Xiaomi MiMo provider presets
* fix: align MiMo V2.5 model specs with official documentation
- Update maxTokens from 32000 to 131072 (128K) for mimo-v2.5-pro and mimo-v2.5
- Update contextWindow from 262144 to 1048576 (1M) for mimo-v2.5
- Aligns with official specs from Xiaomi MiMo documentation
- Ensures consistency between OpenClaw and OpenCode presets
---------
Co-authored-by: Jason <farion1231@gmail.com>
- Add active flag pattern to 3 useEffect hooks in App.tsx to prevent
event listener leaks when component unmounts before async setup completes
- Add guard check in useSettings.ts to prevent undefined from being
stored in localStorage when payload.language is missing
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
- Add Claude Desktop Official to the Claude Desktop preset list.
- Treat selected official presets as official mode in the form.
- Cover the official preset with a preset-order regression test.
Remove the category-based grouping logic from ProviderPresetSelector,
letting the array position in each preset config file be the single
source of truth for display order. Move partner presets (PatewayAI,
火山Agentplan, BytePlus, DouBaoSeed) right after Shengsuanyun across
all 6 config files so they appear earlier in the UI.
Add BytePlus (international Volcengine) to Claude, Claude Desktop,
Hermes, OpenCode, and OpenClaw with byteplus icon, 256K context window,
and trilingual promotion text.
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.
Add RelaxyCode as a new third-party provider with support for:
- Claude Code preset (Anthropic native protocol)
- Codex preset (gpt-5.5 model)
- Claude Desktop preset (direct mode with passthrough routes)
RelaxyCode is an enterprise-grade AI programming platform providing
unified access to Claude Code, Codex, and Gemini CLI models.
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.
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.
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.
- Change mode from "proxy" to "direct" for 20 third-party/aggregator providers
- Simplify PipeLLM from mappedRoutes to passthroughRoutes for consistency
- Reduces unnecessary proxy layer overhead for providers that support direct API calls
Affected providers: ShengSuanYun, AIHubMix, DMXAPI, PackyAPI, PatewayAI,
Cubence, AIGoCode, RightCodes, AICodeMirror, AICoding, CrazyRouter,
SSSAICode, ModelVerse, CompShare, MicuAPI, CTOK, E-FlowCode,
VibeCodingAPI, LemonData, PipeLLM
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.
- Forwarder buffers non-streaming bodies and primes streaming first
chunk before signaling success, so body timeouts and SSE first-chunk
failures route through the circuit breaker instead of being recorded
as success on response-header arrival
- Atomic enable-failover: switch to P1 before persisting the flag, and
roll back auto-added queue entries when the switch is rejected
(e.g. official providers)
- Hot-reload circuit breaker config on per-app proxy config change
instead of waiting for a proxy restart
- FailoverToggle / FailoverQueueManager / AutoFailoverConfigPanel
require proxy takeover for the active app; the backend command also
rejects enabling when takeover is off
- ProviderHealthBadge consumes the backend is_healthy flag instead of
hardcoding the 5-failure threshold
Cleanup:
- impl From<&AppProxyConfig> for CircuitBreakerConfig and use it from
the command layer
- Collapse three identical TabsContent blocks into a single map
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.
Replace all openclaudecode.cn URLs with micuapi.ai across all
provider presets (Claude, Codex, Hermes, OpenClaw, OpenCode,
Claude Desktop). This includes website URLs, API key URLs, and
base URLs.
Update all CrazyRouter baseURL configurations from crazyrouter.com
to cn.crazyrouter.com across all supported applications (Claude,
Codex, Gemini, Hermes, OpenClaw, OpenCode, Claude Desktop).
Website and registration URLs remain unchanged.
Most third-party Claude Code providers now reject requests from
non-official clients, so the model test button would just produce
noisy failures (or worse, trigger risk controls on the provider
side). Treat third-party Claude providers the same way as official /
Copilot / Codex OAuth: pass onTest=undefined so ProviderActions
renders the test button in its existing disabled visual state.
- Add `get_codex_oauth_models` Tauri command reusing the managed OAuth
access token to hit `chatgpt.com/backend-api/codex/models`; HTTP and
multi-shape JSON parsing live in `services::codex_oauth_models` so the
command stays thin.
- Unify the Claude form's "fetch models" button across normal / Copilot /
Codex OAuth presets, drop the auto-load effect for Copilot in favor of
explicit clicks, and guard against stale responses with a requestId ref.
- Add Vitest coverage for both Copilot and Codex OAuth paths asserting no
request on mount and the correct account id on click; add Rust unit
tests for the four model-list payload shapes.
* model pricing routing: extend prefix-match families (gpt-/o1-o5/
gemini-/deepseek-/qwen-/glm-/kimi-/minimax-) with per-family dash
thresholds so short base IDs like gpt-5 no longer mis-match
gpt-5-mini; strip ISO and 8-digit date suffixes via UTF-8-safe
byte matching so claude-haiku-4-5-20251001 falls back to
claude-haiku-4-5 pricing
* SSE collector: SseUsageFinishGuard (RAII) guarantees finish() on
early return or panic; AtomicBool fast path lets push() skip the
Mutex once first-event time is recorded
* validation: shared validate_cost_multiplier / validate_pricing_source
helpers across DAO and service layers; PRICING_SOURCE_RESPONSE /
PRICING_SOURCE_REQUEST constants replace string literals; price
fields in update_model_pricing now reject empty / non-decimal /
negative input before INSERT
* backfill: add backfill_missing_usage_costs_for_model so a single
price edit only scans matching rows instead of the full log table;
startup backfill remains full-scan
* session_usage{,_codex,_gemini}: share find_model_pricing helper from
usage_stats; metadata_modified_nanos centralizes mtime precision
* frontend: NON_NEGATIVE_DECIMAL_REGEX + isNonNegativeDecimalString
replace three copies of the same multiplier regex; isUnpricedUsage
surfaces zero-cost rows that have usage tokens (cached per row to
avoid double evaluation); invalidate usageKeys.all on pricing mutate
so backfilled rows refresh
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.
- 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.
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