Switching, adding, editing, sync, universal providers and entering or
leaving local routing now write Codex through the write engine. Only
routing, credential, model, reasoning, context-window and compatibility
fields are replaced; MCP servers, [projects], comments and the user's own
provider tables stay byte-for-byte. auth.json, config.toml, the model
catalog, the managed-account marker and a device-local login stash commit
as one operation, so a crash at any point rolls forward on startup.
- Third-party routes are always written as [model_providers.custom] with
the key in experimental_bearer_token. Rows that use another id, a
top-level openai_base_url or a legacy reserved table are normalized on
the way out. Switching to an official card leaves a dormant custom table
(local proxy address plus placeholder credential) so older third-party
sessions can still resume.
- requires_openai_auth follows the route's auth and whether a login is on
disk; keyring and ephemeral credential stores are handled separately.
- A switch is refused before anything is written when the active profile
overrides the route, the selected table is missing or built in, a
keyless row would fall back to the official login, or the row has no
auth.
- Provider tables an older version wrote are removed only when they can
be proven to be CC Switch's. Tables a profile references are kept, and
reserved ids that cannot be proven are renamed.
- Provider-exclusive fields, including web_search and a row's own
model_catalog_json, are removed on the way out only when live still
holds the previous provider's value.
- No more backfill. A native ChatGPT login that CC Switch deletes or
overwrites is kept in ~/.cc-switch/codex-login-stash.json (0600, not
synced) and restored when switching back to an official card; logging
out on an official card discards it. Login snapshots already in
official rows seed the stash once. A leftover API key in auth.json is
deleted only when it matches a third-party row's key.
- Managed accounts fetch their token before taking the lock. If the CLI
refreshes the login during a switch, the switch stops before writing
anything. Once files are published, a later failure no longer rolls the
row back; startup finishes the switch.
- Local routing: third-party routes get a fixed local-proxy table and the
official route uses cc-switch-official plus the client's own login. The
contract covers every field written, so a route switch with the same
contract touches no files.
- The Codex editor shows what config.toml will look like after switching.
On save, key fields go to the row and other edits go to live as global
settings with a three-way check. Row fields that never reach live are
listed read-only and can be copied.
- The Codex common-config snippet is frozen like Claude's; its UI and the
TOML snippet command are removed. New Codex rows keep
commonConfigEnabled: true for older versions that sync them.
- The write engine can delete a file as part of an operation.
- Integration-test mutexes tolerate poisoning so one failure no longer
cascades into unrelated tests.
Wire hermes through SkillApps struct, DAO SQL, command parser, and
SKILLS_APP_IDS. Add a Skills entry to the Hermes toolbar. Simplify
skill_sync test fixtures to use SkillApps::default().
- Add content_hash and updated_at fields to 4 InstalledSkill literals in skill_sync.rs
- Add useCheckSkillUpdates and useUpdateSkill to UnifiedSkillsPanel test mock
- Suppress unused import warning in auto_launch.rs test module
Introduce list/restore/delete commands for skill backups created during
uninstall. Restore copies files back to SSOT, saves the DB record, and
syncs to the current app with rollback on failure. Delete removes the
backup directory after a confirmation dialog. ConfirmDialog gains a
configurable zIndex prop to support nested dialog stacking.
Create a local backup under ~/.cc-switch/skill-backups/ before removing
skill directories. The backup includes all skill files and a meta.json
with original skill metadata. Old backups are pruned to keep at most 20.
The backup path is returned to the frontend and shown in the success
toast. Bump version to 3.12.3.
Skills import previously inferred app enablement from filesystem presence,
causing incorrect multi-app activation when the same skill directory existed
under multiple app paths. Now the frontend submits explicit app selections
via ImportSkillSelection, and schema migration preserves a snapshot of
legacy app mappings to avoid lossy reconstruction.
Also adds reconciliation to sync_to_app (removes disabled/orphaned symlinks)
and MCP sync_all_enabled (removes disabled servers from live config).