Small shell wrapper for switching Codex to the best saved ChatGPT auth profile before or during a session.
It keeps auth.json pointed at the best available profile before a session starts. The optional codex shim only does a cached auth selection and then execs the real Codex binary, so normal Codex launches do not create nested runners, live log monitors, or extra MCP/app-server sidecars.
Synthetic demo data. No live credentials or account identifiers.
| Dry-run autoswitch | Earned reset confirmation |
|---|---|
![]() |
![]() |
All captures use the real Textual app with an in-memory synthetic backend.
- Linux or WSL with Bash,
jq,flock, Git, and the official Codex CLI. uvfor the isolated Textual environment.crontabis optional; without it, runcodex-auth maintainafter a direct curl update.- Building the patched Codex generation also needs a Rust/Cargo toolchain and normal native build dependencies.
If Codex came from the official curl installer, install or update Codex first. That installer owns the visible ~/.local/bin/codex link. ./install.sh --wrap-codex installs a one-minute maintenance job that restores the rolling-auth shim after a later curl update and queues the matching patch build.
curl -fsSL https://chatgpt.com/codex/install.sh | shgit clone https://github.com/editnori/codex-rolling-auth.git
cd codex-rolling-auth
./install.sh --wrap-codexThe wrapper resolves the native binary through $CODEX_HOME/packages/standalone/current/bin/codex (and the legacy current/codex layout), so it follows the curl installer's current release instead of falling back to an older Bun/npm copy.
That installs:
codex-auth, the profile manager and rolling runnercodex-auth-tui, the full-screen watcher in a private project.venvcodex, an optional shim that runscodex-auth auto --quiet --no-backgroundand then starts the real Codex binary- patched-Codex selection: when a matching generation exists, the shim uses it with in-process rolling auth enabled; when Codex updates, the shim immediately runs stock Codex and starts one detached patch build
- a marked, idempotent cron entry that runs
codex-auth maintain --quietonce per minute without replacing existing cron jobs
The installer uses uv with the platform's native TLS store to create the TUI environment under PREFIX/lib/codex-auth/tui/.venv. It does not install Python packages globally. Packaging and isolated tests can copy the project without resolving dependencies by setting CODEX_AUTH_TUI_SKIP_BOOTSTRAP=1; normal installs should leave bootstrap enabled.
If you only want the manager and not the codex shim:
./install.shOpen the persistent account monitor without changing auth:
codex-auth watchRun the autoswitch policy in dry-run mode. It refreshes and shows decisions but does not switch accounts:
codex-auth watch --autoAllow the watcher to apply decisions through a generation-bound compare-and-switch transaction:
codex-auth watch --auto --livecodex-auth tui is an alias for codex-auth watch. --threshold 0 means proactively prefer any strictly better ready account. In that mode cooldown is the anti-flap guard; hysteresis applies when the threshold is above zero.
Each auto tick trusts only usage returned by that refresh generation. A profile that needs a new sign-in is marked unavailable instead of keeping an old bar, and an unrelated unavailable profile does not block fresh profiles from being evaluated. If the active session itself is invalidated, live auto can recover to a fresh ready profile through the same generation-bound switch guard.
Watcher keys:
sarms or disarms manual selection.nsaves the current Codex auth as a named profile.iopens saved-profile sign-in without switching the active profile.uchecks and uses an earned rate-limit reset for a selected profile.- Arrow keys or
j/kmove between accounts. Enterconfirms an armed switch and keeps the watcher open.Escdisarms first, then goes back or quits.aopens the autoswitch view.ltoggles LIVE; entering live asks for confirmation, while returning to dry-run is immediate.rforces a refresh.qgoes back or quits.
Live mode changes auth.json, so new Codex processes use the selected account immediately. A stock Codex process that is already running does not hot-reload that file. In-session switching requires a patched Codex build that matches the installed native binary:
codex-auth patch-codex
codex-auth patch-codex --statusThe automatic builder requires the exact rust-v<installed-version> source tag. It never stamps origin/main as a release match. The patch reloads a deliberate account change before Codex can refresh the old account's token, including the unauthorized-recovery path that normally reports that you logged out or signed in to another account. If the installed Codex release changes or a build fails, the shim stays on stock Codex until that exact generation is ready.
Save the current login as a profile:
codex-auth add work --currentFrom codex-auth tui, press n, enter the profile name, and press Enter.
Replacing an existing profile requires confirmation.
Repair an expired or invalid saved login without selecting it as the active profile:
codex-auth reauth workIn the TUI, click sign in again on the profile or press i and choose it. Codex opens its browser login outside the full-screen UI, then writes the new credential only to that saved profile. If the profile is already active, its credential is refreshed in place and the active profile name stays the same.
When Codex reports an earned reset bank, each profile card shows its authoritative remaining count separately from the automatic 5h and weekly reset countdowns. Press u, select a ChatGPT profile, and confirm. The TUI refreshes that profile before confirmation, uses one reset through Codex app-server, then refreshes the usage bars and remaining count. It does not switch the active profile.
The equivalent explicit CLI command requires confirmation:
codex-auth reset work --yesAmbiguous transport retries reuse the same idempotency key, so retrying cannot spend a second reset. alreadyRedeemed is treated as success; nothingToReset and noCredit do not pretend a reset happened. See the official Codex app-server earned reset contract.
Open the usage selector:
codex-auth usage --refresh --selectRun a rolling Codex session explicitly:
codex-auth run -- resume --lastResume a specific session with rolling auth:
codex-auth run -- resume 019e1af9-d95b-7f11-b1f0-aae08a7c4f1dIf you installed the shim with --wrap-codex, normal Codex commands auto-select a cached best profile first:
codex resume 019e1af9-d95b-7f11-b1f0-aae08a7c4f1dCheck for old nested Codex launch trees and MCP sidecars:
codex-auth doctorCheck patched Codex status:
codex-auth patch-codex --statusBuild the patched Codex binary in the foreground:
codex-auth patch-codexReconcile the curl-installed command and queue a missing generation manually:
codex-auth maintainTerminate only the direct MCP sidecars under legacy --yolo Codex processes:
codex-auth doctor --kill-sidecars --yesThis project was directly inspired by claude-swap (cswap) and its multi-account capture, live usage dashboard, and guarded autoswitch workflow for Claude Code. Credit to realiti4 and the claude-swap contributors for the original work and ideas.
- Profiles live under
$CODEX_HOME/auth-profilesby default. - The live auth file stays at
$CODEX_HOME/auth.json. - Saved profiles contain live credentials. Files are written with restrictive permissions, but they must never be committed, uploaded, pasted into issues, or included in captures.
$CODEX_HOME/active-profile.jsonrecords the selected profile using hashed account identity and a credential fingerprint. When Codex rotates that account's refresh token, the wrapper compare-and-swaps the newer auth back into the same saved profile; a real account change or concurrent edit is never overwritten.- Usage probes run in temporary Codex homes. If a probe rotates a refresh token, the rotated auth is persisted before the temporary home is removed and is attached to usage state only after the profile lineage check succeeds.
- Saved-profile sign-in runs in a private temporary Codex home with file-backed credentials. It rejects a different known account identity and compare-and-swaps the result so a concurrent profile update wins.
bin/codex-authis a thin entrypoint. Shell runtime code lives inlib/codex-auth/*.sh; the persistent watcher lives in the isolated project underlib/codex-auth/tuiafter installation.- Set
CODEX_AUTH_CODEX_BIN=/path/to/codexif the wrapper cannot find your real Codex binary. - Set
CODEX_AUTH_AUTO=0to bypass automatic profile selection for one command. codex-auth runis explicit opt-in. It can monitor a bounded session log to retry after usage-limit errors, but the normalcodexshim does not use it.- Set
CODEX_AUTH_ROLL_WATCH=1to enable in-session periodic profile checks forcodex-auth run; it is off by default to avoid background sidecar churn. - Set
CODEX_AUTH_ROLL_LIVE_MONITOR=0to disable live log monitoring forcodex-auth run. - Set
CODEX_AUTH_PATCH_AUTO=0to stop thecodexshim from using or building patched Codex. - Background rebuilds are on by default. Set
CODEX_AUTH_PATCH_BUILD_AUTO=0to use an existing matching patch without building a missing one. - Patched binaries live under
$CODEX_HOME/patched-codex/generations/<stock-key>/codex. Each marker and binary are published together as one immutable directory, and the shim never selects a stale generation. - The builder hashes a stock binary once in the detached worker, reuses a shared Cargo target, strips release debug data, and retains two patched/source generations by default. Set
CODEX_AUTH_PATCH_KEEP_GENERATIONSto change retention. - Build failures back off for 15 minutes by default while stock Codex remains usable. Set
CODEX_AUTH_PATCH_RETRY_SECSto change the retry window. - The maintenance cron waits for the official standalone installer lock, restores the shim only when the curl installer owns the visible command, and queues the new generation. Set
CODEX_AUTH_INSTALL_MAINTAIN_CRON=0during installation to opt out. - Patched builds are stamped as the installed Codex version plus
+local, so they do not appear older to Codex's update check. - Set
CODEX_AUTH_REFRESH_JOBSto tune concurrent usage refreshes. The default is 4 andCODEX_AUTH_REFRESH_JOBS_MAXcaps it at 4 unless you explicitly change the cap;usage --synctemporarily raises the default up to the profile count, capped at 12. codex-auth doctor --kill-sidecars --yesdoes not kill the Codex TUI processes; it only terminates direct MCP sidecars under legacy--yoloCodex roots.- Set
CODEX_AUTH_USAGE_HEADER=1orCODEX_AUTH_USAGE_STATUS=1if you want the full table header or status column back. - The selector defaults to the inline fzf scrolling TUI when fzf is available. It uses adaptive height instead of taking over the whole terminal. Set
CODEX_AUTH_NO_FZF=1orCODEX_AUTH_SELECTOR=numberedfor the plain numbered fallback. codex-auth usage --syncrefreshes usage first, then opens the TUI.codex-auth usage --refresh --selectopens the TUI from cache right away and refreshes in the background.- Set
CODEX_AUTH_SELECTOR_CENTER=1if you want the fallback selector vertically centered in the terminal. - Selector bars default to background-color lanes with thin horizontal row borders. Set
CODEX_AUTH_SELECTOR_BAR_STYLE=glyphif your terminal does not render those cleanly.
Remove the marked maintenance block first so it cannot restore the shim, reinstall stock Codex, then remove the manager files:
crontab -l 2>/dev/null \
| awk '$0 == "# BEGIN codex-auth maintain" {skip=1; next} $0 == "# END codex-auth maintain" {skip=0; next} !skip' \
| crontab -
curl -fsSL https://chatgpt.com/codex/install.sh | sh
rm -f ~/.local/bin/codex-auth ~/.local/bin/codex-auth-tui ~/.local/bin/codex-real
rm -rf ~/.local/lib/codex-authThis intentionally leaves $CODEX_HOME/auth-profiles and other account state in place.
tests/run.shMedia generation is development-only and uses synthetic in-memory profiles. It requires Fontconfig, FFmpeg, ImageMagick, and the locked Playwright/Pillow dependencies:
uv sync --project tui --dev --locked
uv run --project tui playwright install chromium
uv run --project tui python scripts/capture_tui.py
uv run --project tui python scripts/capture_tui.py --check
node scripts/generate-assets.mjs




