Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## Unreleased
## 1.6.0 - 2026-07-11

- **Critical** — fixed the quota backfill reverting fresh data and bleeding across accounts. The root `~/.codex/profile.json` copy was only ever written at switch-in, so every write-back (switch, app launch, window close) copied that frozen snapshot over the profile's card and silently reverted the quota / plan updates recorded while it was active — permanently for API-key profiles, which no API refresh repairs; Windows had patched the switch path only (keyed off the possibly-stale `.current_profile` marker) and macOS had nothing. The write-back now folds the freshest live-session usage into the identity-verified target's card and refreshes the root copy first, on both platforms and on all four write-back paths (switch, app launch, window close, current-profile login); the legacy `codex-switch.sh` no longer copies the root `profile.json` back at all, closing its variant of the same clobber. Live-session data is scoped to entries written since the profile's activation (`activated_at` from the active marker): `~/.codex/sessions` is not swapped on switch, so the previously-live account's newer entries used to win the freshness race and show up as the current profile's quota for minutes after a switch — and when a profile has no parseable activation marker (an identity-drift target, a hand-edited marker) the fold is skipped outright rather than attributing unattributable sessions. A present-but-unreadable `profile.json` now aborts the write-back loudly instead of laundering a transient read failure into a blanked card. The duplicated Windows display path (which had already drifted: it lacked the unmanaged-account guard) now re-exports the shared implementation; an empty stored card with a newer timestamp (the deliberate downgrade-to-free clear) is no longer overridden by older live sessions; and switching kicks an immediate silent API refresh for the new card instead of priming its stored timestamp and waiting out the 5-minute tick. Known residual: a codex CLI session started under the previous account that keeps running across the switch keeps appending to a session file whose mtime passes the activation cutoff, so its usage can still be misattributed until that session exits.
- **Critical** — adapted macOS desktop/CLI discovery for the Codex → ChatGPT.app merge. OpenAI now ships Codex inside `/Applications/ChatGPT.app` (bundle id still `com.openai.codex`, process name `ChatGPT`, CLI at `Contents/Resources/codex`). The app previously only probed `Codex.app` and `pgrep -x Codex`, so after the merge: (1) auto-detect / login / app-server fallback reported `REAL_CODEX_NOT_FOUND` even with a healthy install, and (2) profile switch skipped quitting the running desktop host, leaving its in-memory app-server on the old account. Discovery now prefers `ChatGPT.app` over legacy `Codex.app` across both `/Applications` and `~/Applications`, and a bundle only qualifies when it actually embeds the codex CLI — a chat-only consumer ChatGPT.app (`com.openai.chat`) is never selected. is-running / quit key off the stable bundle id, and their process-name fallbacks verify each PID's bundle identity before trusting or signalling it — the consumer ChatGPT chat client is never killed as collateral, and a PID whose identity can't be established still counts as "running" (the switch aborts rather than proceeding over a possibly-live host) but is never signalled; the polite AppleScript quit is fired without waiting (a quit-confirmation dialog or the first-run Automation/TCC consent prompt can no longer hang the switch) and is cancelled once the host is gone so a consent granted later can't close the relaunched app. activate uses the bundle id; open tries the on-disk bundle path, then the bundle id, then the unambiguous legacy `Codex` display name (never `ChatGPT` by name — it could resolve to the consumer chat client), checking each `open`'s exit status so a silent launch failure surfaces as a warning instead of a fake success. macOS-only; Windows Store path still needs a separate pass if the Win host also renamed.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Windows 构建走 CI 的 `tauri build --target x86_64-pc-windows-msvc`;本地

```bash
npm run version:sync # 把 package.json 当前 version 同步到 Cargo / lock
npm run version:set -- 1.5.11 # 一次性 bump 到指定版本
npm run version:set -- 1.6.0 # 一次性 bump 到指定版本
npm run version:check # CI 用:拒绝把 semver 字面量写回 *.html
```

Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ Windows 构建走 CI 的 `tauri build --target x86_64-pc-windows-msvc`;本地

```bash
npm run version:sync # 把 package.json 当前 version 同步到 Cargo / lock
npm run version:set -- 1.5.11 # 一次性 bump 到指定版本
npm run version:set -- 1.6.0 # 一次性 bump 到指定版本
npm run version:check # CI 用:拒绝把 semver 字面量写回 *.html
```

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "codex_switch",
"private": true,
"version": "1.5.11",
"version": "1.6.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
41 changes: 41 additions & 0 deletions release-notes/v1.6.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Codex Switch v1.6.0

## 中文

### 账号切换可靠性修复:串号、ChatGPT.app 合并、额度回滚

本版本修复切换链路三类问题:写回盲信 `.current_profile` 标记,账号漂移后被写进错误档案(串号);Codex 并入 `ChatGPT.app` 后按名称探测失效,健康安装报 `REAL_CODEX_NOT_FOUND`;额度回填用切入时冻结的根 `profile.json` 快照覆盖已刷新数据,额度反复回滚。

本版本改动:

- 写回目标按账号身份校验(`tokens.account_id` 与 id_token `email` 双识别),漂移账号路由到真正拥有者;未管理账号清标记并在面板提示
- 适配 `ChatGPT.app` 合并:发现与退出按稳定 bundle id `com.openai.codex`,`ChatGPT.app` 优先、`Codex.app` 兜底,候选须内嵌 codex CLI
- 进程信号先做 PID 归属校验,不误杀消费级 ChatGPT(`com.openai.chat`);AppleScript 退出不再同步等待(TCC 弹窗不再挂起切换),退出后取消 pending 事件
- 每次写回(切换、启动、关窗、重登)前刷新根 `profile.json` 并折算 live 用量,冻结快照不再回滚;元数据损坏时写回响亮失败而非清空卡片
- live 会话按激活时间(`activated_at`)归属,上一账号用量不再串显/串存;切换完成即触发静默额度刷新

### 验证

- `cargo test --lib`(137 通过,含回滚/归属/激活过滤回归用例)
- `npx tsc --noEmit` / `cargo fmt --all -- --check`
- `cargo run --example live_chatgpt_merge_check`

## English

### Reliability fixes: cross-contamination, the ChatGPT.app merge, quota rollback

This release fixes three switching-pipeline defects: the write-back trusted the `.current_profile` marker blindly, so a drifted live account overwrote the wrong profile; the Codex merge into `ChatGPT.app` broke name-based probes, reporting `REAL_CODEX_NOT_FOUND` on healthy installs; and the quota backfill kept copying the switch-in-frozen root `profile.json` snapshot over refreshed data, rolling quotas back.

Changes in this release:

- Write-back targets are identity-verified (matching `tokens.account_id` and the id_token `email`); a drifted account routes to its real owner, an unmanaged one clears the stale marker with a prompt
- The `ChatGPT.app` merge: discovery and lifecycle key off the stable bundle id `com.openai.codex`, preferring `ChatGPT.app` with `Codex.app` fallback; candidates must embed the codex CLI
- Process signalling verifies each PID's bundle first — the consumer ChatGPT client (`com.openai.chat`) is never killed by a name match; the AppleScript quit is no longer awaited (TCC prompts cannot hang a switch) and is cancelled after exit
- Every write-back (switch, launch, window close, re-login) refreshes the root `profile.json` and folds live usage first; corrupt metadata fails loudly instead of blanking the card
- Live sessions are attributed by activation time (`activated_at`), so a previous account's usage is no longer shown or persisted; each switch kicks an immediate silent quota refresh

### Verification

- `cargo test --lib` (137 passed, incl. rollback / attribution / activation-scoping regressions)
- `npx tsc --noEmit` / `cargo fmt --all -- --check`
- `cargo run --example live_chatgpt_merge_check`
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "codex_switch"
version = "1.5.11"
version = "1.6.0"
description = "Native Tauri control panel for Codex account switching"
authors = ["Cmochance"]
edition = "2021"
Expand Down
Loading