* fix(ci): run backend checks on Windows/macOS and repair platform-gated tests
The backend CI job ran only on ubuntu-22.04, so every #[cfg(target_os =
"windows")] / macOS path — tests and clippy lints alike — was never
compiled or run. As a result main shipped 8 failing Windows unit tests and
a hard `cargo clippy -- -D warnings` failure on Windows, all invisible to
CI because it only builds and tests Linux.
Changes:
- ci: run the backend job on a {ubuntu-22.04, windows-latest, macos-latest}
matrix (fail-fast: false); gate the apt install step on runner.os ==
'Linux' and use `shell: bash` for the dist placeholder so it works on all
three runners.
- misc.rs: fix 6 stale `anchored_upgrade_windows` tests. Commit 5092fe51
removed codex from `prefers_official_update`, so codex now anchors to the
package manager with no `codex update ||` prefix; update the five
package-manager expectations accordingly, and retarget the no-sibling
"official-update-without-fallback" test to `claude` (still in the list) so
that branch stays covered instead of being silently dropped.
- codex_state_db.rs / codex_history_migration.rs: the two sqlite_home tests
built TOML basic strings from Windows paths, whose backslashes are invalid
escape sequences and made the override fail to parse; switch to TOML
literal (single-quoted) strings so the path is taken verbatim.
- clippy (13 Windows-only warnings): move `use std::io::Write` into the
#[cfg(unix)] block that actually uses it; convert 3 unneeded `return`s in
Windows-gated tail positions to expressions; mark 9 POSIX-shell helpers
#[cfg_attr(windows, allow(dead_code))] since they are used only by the
macOS/Linux terminal launchers.
Verified locally (Windows 11, Rust 1.95.0):
cargo test --lib → 1748 passed; 0 failed (was 1740/8)
cargo clippy -- -D warnings → clean (was 13 errors)
cargo fmt --check → clean
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(skills): resolve home via get_home_dir so tests isolate on Windows
services/skill.rs called dirs::home_dir() directly in five places. On
Windows dirs::home_dir() resolves through the Known Folder API and
ignores HOME/USERPROFILE, so the CC_SWITCH_TEST_HOME override set by the
test harness never applied: the skill_sync integration tests scanned the
runner's real user profile, found no skills, and failed (the first panic
then poisoned the shared test mutex, cascading to all seven). On Unix
the harness also sets HOME, which dirs::home_dir() honors, so the suite
passed there by accident.
Route all five call sites through crate::config::get_home_dir(), the
crate-wide home resolver that prefers CC_SWITCH_TEST_HOME. The three
now-unreachable GET_HOME_DIR_FAILED error branches are dropped:
get_home_dir() is infallible, matching every other config-dir resolver.
Production behavior is unchanged (CC_SWITCH_TEST_HOME is unset outside
tests, so get_home_dir falls through to dirs::home_dir).
Add a regression test that discriminates on every platform by pointing
CC_SWITCH_TEST_HOME at a temp dir different from $HOME; it is marked
#[serial_test::serial] to stay mutually exclusive with the other tests
mutating the same process-global variable. Verified the test fails
against the old code on macOS with the same failure mode as Windows CI.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jason <farion1231@gmail.com>
* fix(codex): display renamed session titles
* fix(codex): resolve custom state db for titles
* refactor(codex): dedupe state-db resolution and harden title lookup
- Extract a shared `codex_state_db` module for `state_5.sqlite` path
resolution (config `sqlite_home` / `CODEX_SQLITE_HOME`), previously
copy-pasted verbatim between codex_history_migration and the codex
session provider. `state_5.sqlite` is now a single source of truth.
- Add `busy_timeout` to the read-only title query; without it a read
during a concurrent Codex write fails immediately (SQLITE_BUSY) and
renamed titles silently fall back to the first user message.
- Push the `title == first_user_message` comparison into the SQL WHERE
clause (NULL-safe, aligning with Codex's `distinct_thread_metadata_title`
semantics) and stop SELECTing `first_user_message`, which can hold
large values (openai/codex#29007) — the comparison belongs in SQL
anyway, so the column no longer needs to cross into Rust.
The vendor renamed its domain, API endpoints, and trademark from CTok to
ETok. Update all user-facing branding accordingly:
- Presets (claude, claude-desktop, codex, gemini, hermes, openclaw,
opencode): ctok.ai -> etok.ai, api.ctok.ai -> api.etok.ai, internal
provider id ctok -> etok, display name CTok.ai -> ETok.ai
- Icons: replace inline placeholder SVG with the new ETok brand PNG,
moved from `icons` to `iconUrls`; key ctok -> etok; banner logo
ctok.png -> etok.png
- README (en/zh/ja/de) partner banner: new logo, links, and copy
- i18n (en/ja/zh/zh-TW): partnerPromotion key ctok -> etok
Keep "ctok" in the Codex history-migration whitelist as a legacy id and
add "etok", so existing users' local session history stays correctly
bucketed after the rename.
The session-history migration only scanned ~/.codex/state_5.sqlite and the
config.toml `sqlite_home` location. When Codex's SQLite state is relocated via
the CODEX_SQLITE_HOME env var, the state DB was never scanned and its threads
kept their old provider bucket. Add a CODEX_SQLITE_HOME fallback (config
`sqlite_home` still takes precedence) in the shared codex_state_db_paths helper
used by both the third-party and unified-session history migrations, plus serial
env tests.
- Enable dialog gains a checkbox (default off) to migrate existing
official sessions from the built-in "openai" bucket into the shared
"custom" bucket, with per-generation backups; failed migrations retry
at startup
- Disable dialog offers a precise restore driven by the backup ledger:
only sessions recorded as "openai" in backups are flipped back, and
sessions created while the toggle was on are never touched
- Completion marker and backup generations are bound to the canonical
Codex config dir; migrate/restore serialize on an op lock and the
marker is written conditionally inside the settings write lock
- save_settings rolls back the toggle and fails the save when the live
rewrite fails; migration additionally requires the live config to
actually route to the shared bucket (skips with live_not_unified so
refused injection or proxy takeover can't split history)
- Restore refuses to run while the toggle is (re-)enabled and reports
nothing_to_restore instead of a zero-count success; local migration
markers are now backend-owned in merge_settings_for_save so stale
frontend payloads can't resurrect them
- Settings autosave reverts optimistic form state on failure so a
failed toggle change can't be replayed by an unrelated save
- ConfirmDialog supports an optional checkbox; all four locales updated
- Always emit `model_provider = "custom"` from deep link, UniversalProvider, and the universal form modal so future writes share one stable routing key.
- Add `codex_provider_template_v1` local migration that rewrites legacy keys (aihubmix/ccswitch/...) under `[model_providers.custom]`, updates profile refs, and backs up the original settings_config under `~/.cc-switch/backups/<timestamp>/providers/`.
- Tighten history migration source detection to a whitelist plus `[model_providers.<id>]` existence check so user-authored keys are never rewritten in jsonl/state DB.
- Encode deep link name/model/endpoint through `toml_edit::Value` so display names containing quotes or backslashes no longer break the generated config.toml.
- Stabilize provider settings backup filename hash with Sha256 (was process-random SipHash).
Codex CLI filters conversations by model_provider — after normalizing
all third-party providers to "custom", old sessions with their original
provider IDs become invisible. This migration rewrites JSONL session
files and state_5.sqlite threads to use the unified ID.
Uses a three-tier trust model for source ID collection:
- Known legacy preset whitelist (46 IDs, case-insensitive)
- cc-switch-created custom providers (identified by created_at)
- User-maintained custom providers are explicitly skipped
Adds scanned_history_files flag to prevent stale v1 markers from
blocking re-runs with the improved source ID collection logic.
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.