chore(release): v3.20.1

This commit is contained in:
Jason committed 2026-08-28 08:19:45 +08:00
1 parent 5ff199b5a9
commit 9485cf2f3d
5 files changed
+56 -4

No files matched your search

+52
View File
@@ -5,6 +5,58 @@ All notable changes to CC Switch will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.20.1] - 2026-08-28
This release is dominated by two Codex storylines. The first is an urgent compatibility break: Codex CLI 0.149 stopped letting custom providers inherit ambient credentials from `auth.json`, turning third-party switches made in the old default mode into 401 errors (#6744). Rather than patching around it, cc-switch now switches Codex providers config-only — the key travels in the provider's own `config.toml` table, `auth.json` returns to being purely the official ChatGPT login file — and a whole family of legacy config shapes 0.149 refuses to load is repaired on every switch, with a new preflight refusing unloadable shapes instead of reporting a "successful" switch Codex cannot start from. The second is account safety: two members of the same ChatGPT Team workspace no longer overwrite each other in the Auth Center (#6780, fixes #2245) — existing managed accounts need one re-login (see upgrade notes). Around them: provider edits now always reach the live config file (#6779), the Codex edit dialog no longer shows another provider's key (#6534), restores no longer wipe hand-written prompt files (#6810), the usage dashboard gains an auto/manual session-scan toggle plus an incremental byte-cursor scanner (a 12 MB active session file: 6.04 s → 9.3 ms) behind this release's schema migration (v17 → v18), and Otty joins the macOS terminal picker (#6620).
**Stats**: 26 commits | 66 files changed | +7,474 insertions | -1,000 deletions
### Added
- **Otty Terminal Support on macOS**: "Otty" joins the macOS terminal picker for session resume, provider terminals and tool commands. Launching first attempts a new tab in the existing Otty window via the Otty CLI, then a new Otty window; provider terminals and tool commands additionally fall back to Terminal.app on failure, while a failed session resume reports the error — including an explicit install hint when the Otty CLI is missing — and copies the command to the clipboard. CLI discovery probes the app bundle (system and per-user), Homebrew paths and PATH. The user manual's macOS terminal tables were corrected along the way — Kaku and Warp were already supported but missing from the lists. (#6620)
- **OpenCode Go Subscription Usage**: The usage-script Token Plan query now recognizes OpenCode Go and shows its 5-hour / weekly / monthly usage percentages with reset times in the usage card and tray, reusing the existing quota tiers. The endpoint is Bearer-auth only (the opposite of the inference side, which only accepts `x-api-key`); a valid key without a Go subscription reports a distinct message (HTTP 403) instead of a generic auth failure, a zero-percent window drops the upstream's placeholder reset time, and an unrecognized response shape reports an error instead of an empty card. Newly added OpenCode Go providers in Claude Code, Claude Desktop, Codex, OpenCode and Pi enable the query automatically. OpenCode Zen pay-as-you-go is deliberately not covered — that plan has no usage API upstream.
- **Auto/Manual Mode for Session-Log Scanning**: The usage dashboard gains an "Auto-Scan Session Logs" card with a switch (on by default, so existing behavior is unchanged). Turning it off stops all background session-log scanning, including the startup pass, and a "Sync Now" button appears as the manual entry point, reporting imported entries, files scanned and an error count in a toast. Proxy-takeover request accounting is real-time and never reads session files, so it keeps recording either way, and the startup cost backfill (which only patches existing rows) still runs in manual mode.
### Changed
- **Codex Third-Party Switching Is Config-Only**: Switching to a third-party Codex provider now writes the API key into the provider's own `[model_providers.*]` table in `config.toml` (as `experimental_bearer_token`, honored since Codex 0.48) and never writes the key into `auth.json`, which returns to being purely the official ChatGPT login file — Codex 0.149 stopped letting custom providers inherit ambient auth from `auth.json`, which had broken third-party switches made in the default mode (key written only to `auth.json`) with a 401. The "Keep official login for direct switches" toggle now has exactly one meaning: ON leaves the ChatGPT login completely untouched across third-party switches; OFF deletes `auth.json` instead of overwriting it with the API key (a failed deletion surfaces a warning that the official login is still on disk in the Codex config directory). Two safety gates now run on every third-party switch, not just in preservation mode: a key with no provider table to hold it, and a keyless config that would fall back to the official login (`requires_openai_auth = true` without its own credentials, or a bare `openai_base_url` reroute), are both refused up front with actionable errors — including third-party cards with an empty config, which previously rode silently in `auth.json`. `requires_openai_auth` on the active keyed third-party table is stamped on each direct switch to match the preservation toggle, so Codex's login screen agrees with what is actually on disk. (#6744, #6746)
- **TeamoRouter Presets Move to teamorouter.cn**: All eight app presets now point at `api.teamorouter.cn`, with the old `.com` endpoint registered as a selectable/speed-testable fallback candidate for Claude Code, Claude Desktop, Codex and Grok Build. Existing saved TeamoRouter providers keep whatever base URL they were saved with.
### Fixed
- **Same-Workspace ChatGPT Accounts No Longer Merge in the Auth Center**: Managed Codex OAuth accounts were keyed by `chatgpt_account_id`, which identifies a ChatGPT workspace rather than a person — two members of one Team workspace collapsed onto a single record, with the later login silently overwriting the earlier one's tokens and provider bindings pointing at whoever logged in last. Accounts are now identified locally with the OIDC subject kept as proof of user identity, so same-workspace logins coexist as separate rows. Requests routed through takeover are additionally validated against the live token of the bound account: a Codex session still holding a different member's login is refused with a "restart Codex" error instead of being forwarded under the wrong identity, and the outgoing workspace header always comes from the account binding rather than the client. Adopting a CLI-rotated refresh token and deleting `auth.json` on account removal both require provable ownership now, so cc-switch can no longer adopt or delete another workspace member's login. Every account row offers in-place re-login (bindings preserved), and cancelling or superseding a device login drops the pending flow inside cc-switch — an abandoned browser authorization can no longer be committed minutes later and silently overwrite an account. An id_token that is not a well-formed JWT yields no identity at all, so a malformed or truncated token can never stand in for a user. (#6780, #6831; fixes #2245)
- **Codex 0.149 Compatibility Repairs for Existing Configs**: A family of config shapes that made Codex 0.149 refuse to load — reported as "cc-switch says switched, Codex won't start" — are now repaired on every provider switch and takeover projection. Leftover `[model_providers.openai]` / `.ollama` / `.lmstudio` tables (written by older takeover projections; overriding a reserved id fails validation) are renamed losslessly to a cc-switch-owned id and normalized into a loadable shape; provider tables with no `name` are backfilled (0.149 rejects the whole config over any of them — Bedrock tables are deliberately left nameless because naming them breaks their built-in merge); legacy top-level `openai_base_url` reroutes carrying a usable API key are migrated into a proper custom provider table (a keyless reroute is refused by the switch-time safety gate instead); and a new preflight rejects table field combinations 0.149 cannot load, naming the offending table, instead of writing them out as a "successful" switch. Proxy takeover of a card routed at the built-in `openai` provider now uses the supported top-level knob instead of creating a reserved table, and takeover of `ollama`/`lmstudio`-routed cards fails with an explicit error. The reserved-id list now matches upstream exactly (case-sensitive; `amazon-bedrock-runtime` added, legacy `oss`/`ollama-chat` recognized as ordinary custom providers so their keys finally reach their tables), and inline `model_providers` tables receive the injected token instead of a dead top-level field.
- **A Refused Codex Switch No Longer Corrupts the Rejected Card**: Live-write validation now runs as a preflight before the current-provider pointer moves. Previously a write-layer refusal landed after `current` had already been committed, so the next switch backfilled the old live config into the refused provider's saved settings.
- **Provider Edits Always Reach the Live Config File** (#6779): A stale proxy-restore backup row — left behind by a crash or failed restore — made saves of the active provider (Claude Desktop excepted) take the takeover path, updating only the database and the backup row while the real config file silently kept the old endpoint and key, indefinitely. Ownership is now decided by a single predicate requiring actual evidence of takeover (a placeholder in the live file, or the proxy enabled and running with a backup, or an in-flight switch holding the per-app lock alongside a backup row); stale backup rows are refreshed to match the edited provider instead of hijacking the write. Universal provider saves now also re-project each generated child into the live config of any app where it is the active provider, and report per-app failures by name instead of claiming success.
- **Codex Edit Dialog No Longer Shows Another Provider's Key** (#6534; fixes #6414): With official-login preservation enabled, `auth.json` is a shared slot with no provider identity, and the edit dialog preferred it when seeding the form — so editing the active Codex provider could display, and on save persist, a key left behind by a different provider, making keys converge across cards sharing a base URL ("model not found" errors). The dialog now rebuilds the key from the provider's own bearer token in `config.toml`; official-category and OAuth-only providers are untouched, and a card whose `config.toml` carries no bearer token of its own — an older or hand-maintained shape, now that every third-party switch writes one — keeps seeding from the live `auth.json`, manual edits included, exactly as before.
- **Restores No Longer Wipe Unmanaged Prompt Files** (#6810; fixes #6778): A WebDAV/S3 download or backup import whose snapshot had no enabled prompt for an app truncated that app's live prompt file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / `SOUL.md`) to empty — destroying hand-written local content that was never part of the sync payload. Such a restore now leaves the file untouched; disabling the last prompt from the Prompts panel still clears it as before.
- **Recovery-Screen Exit Buttons Actually Quit the App**: The `process:allow-exit` capability was missing, so on v3.20.0 the Quit button on the "database version too new" recovery screen and the exit after a config-load failure were silently rejected by the IPC layer: the Quit button did nothing (closing the window still quit the app), and after a config-load failure the app carried on into the normal UI instead of exiting as intended. This was independently discovered and fixed first by SaladDay in #6567.
- **Claude Session-Log Scanning Correctness**: Three data-accuracy fixes landed with the incremental Claude scanner. A log line caught mid-write was permanently skipped by the old line-number cursor (the unfinished tail advanced the cursor, so the completed message was never imported) — the byte cursor only commits past complete lines, so the message is picked up next round. An externally truncated or rewritten session file is never replayed: re-importing entries whose detail rows the 30-day rollup has already pruned would permanently inflate totals, so the cursor is pinned at the new end of file and the skipped range is reported in the sync errors instead of silently dropped (truncation is caught by the cursor overrunning the file; same-size rewrites by a fingerprint of the bytes before the cursor). Mid-file read errors now keep committed progress, resume from the same spot next round, and are reported instead of returning a clean success; a failed cursor prefetch aborts the round instead of behaving like a first-ever scan and double-importing history.
### Performance
- **Incremental Byte-Cursor Scanning for Claude Session Logs**: Each scan round now seeks to the last committed byte offset and reads only what was appended, instead of re-reading changed files end to end — per the change's benchmark, a 12 MB active session file drops from a 6.04 s full parse to a 9.3 ms incremental read. Per-file cursors for Claude, Gemini, OpenCode, Grok Build and Pi are prefetched with a single table read per importer each round instead of one row lookup per file, and on the Claude path each file's imports plus its cursor advance now commit in one transaction. A frozen-snapshot replay over 1,017 session files (409 MB) produced aggregates identical to the old scanner. Requires a schema migration (v17 → v18) adding two nullable columns — the byte cursor and a tail fingerprint; existing line-number cursors are converted in place on the first scan without re-importing anything.
- **Pi Session Dedup Lookups Use the Identity Indexes** (#6667): The combined dedup query (an OR across two identity columns) could only constrain the data-source prefix, scanning the entire Pi portion of the ledger for every parsed record — so Pi imports got slower as usage history grew. It is now split into indexed point lookups with identical results, so import time no longer degrades with history size.
### Internal
- **CI: WSL2 Contract Test Runs From a Prebuilt Binary** (#6472): The Windows + WSL2 job now compiles the test binary once under a native TEMP and executes it directly with TEMP pointed at the WSL UNC path, so the run step can no longer trigger a re-link where `link.exe`/`mt.exe` fail to create manifest temp files (LNK1327). The scheduled nightly full-suite workflow is deliberately unchanged until it can be validated independently.
### Upgrade notes
- **Codex OAuth accounts need one re-login**: Every managed ChatGPT (Codex OAuth) account added before this release is quarantined until you click 重新登录 / Re-login on its row in the Auth Center — older records used the ChatGPT workspace ID as the account key and carry no separately recorded per-user identity, and an ordinary token refresh cannot prove which user an old record belongs to. Provider bindings are preserved; re-login updates the account in place. Use the row's Re-login button: signing in again through "Add account" creates a second row (logins no longer merge by workspace) and leaves the old row — and any provider bound to it — still quarantined. (#6780)
- **Codex releases older than 0.48 lose third-party authentication** under config-only switching: they never read the provider-table token field. Upgrade Codex if you are on a pre-0.48 build.
- With the "Keep official login for direct switches" toggle **OFF (the default), switching to a third-party Codex provider now deletes `auth.json`** rather than overwriting it with the API key. To get the ChatGPT login back, switch to an official provider bound to an Auth Center account (its login is written back from the stored account) — a card that follows the CLI's own login needs `codex login`. Turn the toggle ON to keep the official login across third-party switches.
- **Some previously "working" Codex cards are now refused at switch time**: third-party cards with an empty config (no table to hold the key), and keyless cards relying on `requires_openai_auth = true` or a bare `openai_base_url` reroute to borrow the official login. Add a proper `[model_providers.<id>]` entry or an API key to such cards.
- **Codex configs are rewritten on the next live write** where needed for 0.149: legacy `openai_base_url` reroutes with a usable key become a `[model_providers.cc-switch]` table, stale reserved tables are renamed to a cc-switch id, missing `name` fields are filled in, and `requires_openai_auth` on the active keyed third-party table is overridden on each switch to match the preservation toggle (a hand-set value on that table does not survive a switch).
- **Database schema migrates v17 → v18** on first launch (a byte-cursor and a tail-fingerprint column on `session_log_sync`). Downgrading afterwards hits the schema-too-new guard. Usage entries the old half-line bug had already skipped are not retroactively recovered — replaying them cannot be distinguished from re-importing already-rolled-up history.
- **Truncated or externally rewritten Claude session logs are skipped, permanently and by design**: the rewritten range is not replayed (that would double-count against pruned rollups) and the skip is reported in the sync result's error list.
- **Keys already cross-contaminated before the #6534 fix are not repaired automatically**: if Codex providers sharing a base URL converged on one key, re-enter the correct key on each affected card once.
- **Restore behavior change** (#6810): restoring a snapshot in which an app has no enabled prompt now preserves that app's live prompt file — the client keeps loading its old content even though the Prompts panel shows everything disabled. Enable and then disable a prompt from the panel (or edit the file yourself) if you want it cleared.
- **Universal provider saves can now fail loudly**: if a live config file cannot be written for an app whose active provider is the generated child, the save reports an error naming the app; the database record is still saved — retry the sync or switch that app's provider once.
- **Existing TeamoRouter providers keep `api.teamorouter.com`**: re-add from the preset or edit the base URL to move to `.cn`.
- **OpenCode Go usage auto-enables only for providers added after this release**: for an existing card, open its usage-script settings and pick the Token Plan template → OpenCode Go once.
## [3.20.0] - 2026-08-18
Development since v3.19.2 spans every subsystem, led by three structural additions. Pi becomes the ninth managed app (#6064): provider management over `~/.pi/agent/models.json` with 58 presets, a prompt library with system-prompt and slash-command editors, Skills, a session browser, and — behind a schema migration (`SCHEMA_VERSION` 16 → 17, the first new table since v12 introduced profiles) — session usage imported into the dashboard (#6463). Codex gains real multi-account ChatGPT management (#3879): sign in to several accounts in the Auth Center, bind each provider card to one of them or let it follow the CLI's own login, and switch between them like any other provider — with official cards deliberately leaving auto-failover so a retry can never bill a different account. And Claude Code's built-in WebSearch now works when routed to a Responses provider or the Codex OAuth backend, alongside proxy support for Codex's standalone Alpha Search endpoint (#5681). The urgent fix is for Windows-with-WSL users: v3.19.2's switch to `ReplaceFileW` failed on `\\wsl.localhost` paths with an error the fallback did not catch, so existing configurations could not be updated or switched (#6232) — this release repairs it and adds real WSL2 filesystems to CI (#6233). The same Windows wave overhauls CLI detection to read the registry PATH and standalone installer directories, fixing a five-issue family of "not installed" and stale-version reports (#6284), eliminates the white/black startup flash (#6252), and stops the MSI from writing a garbage registry key it had been writing since v3.4.0 (#6283). Codex reasoning control gets a full pass: per-model reasoning levels in the model catalog (#6228), vendor-documented tiers pre-filled across the presets, and corrected thinking-switch dialects for the aggregator platforms that had been receiving fields their gateways never accepted. A contributor's backup/sync audit lands as two hardening waves — SQL dumps that round-trip every value and refuse truncated imports (#6146), and restores that rebuild every derived file and run strictly one at a time (#6147). Rounding it out: a sweeping provider-form consistency pass, an IME fix that stops macOS Chinese/Japanese input from corrupting form fields (#6308), and DeepSeek V4 repriced to the vendor's new peak-tier rates.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cc-switch",
"version": "3.20.0",
"version": "3.20.1",
"description": "All-in-One Assistant for Claude Code, Codex & Gemini CLI",
"type": "module",
"scripts": {
+1 -1
View File
@@ -758,7 +758,7 @@ dependencies = [
[[package]]
name = "cc-switch"
version = "3.20.0"
version = "3.20.1"
dependencies = [
"anyhow",
"arboard",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "cc-switch"
version = "3.20.0"
version = "3.20.1"
description = "All-in-One Assistant for Claude Code, Codex & Gemini CLI"
authors = ["Jason Young"]
license = "MIT"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "CC Switch",
"version": "3.20.0",
"version": "3.20.1",
"identifier": "com.ccswitch.desktop",
"build": {
"frontendDist": "../dist",