A keyboard-driven TUI for keeping independent sets of Claude Code and Codex
ChatGPT accounts. Use Left and Right to change provider, inspect cached/live quotas,
and switch only the selected provider's authentication.
Unofficial. This project is not affiliated with Anthropic or OpenAI. Claude account authorization is resolved through Claude's OAuth flow and official CLI; Codex authentication and quotas use the official Codex App Server. Claude's quota endpoint remains undocumented and is therefore best-effort. Use only accounts you own.
| Behavior | Claude | Codex |
|---|---|---|
| Active credentials | Claude credential/config files | ~/.codex/auth.json only |
| Add account | portable paste-code OAuth flow | App Server account/login/start |
| Identity | Claude live files/status | App Server account/read + account_id |
| Quotas | cached best-effort Claude usage | App Server account/rateLimits/read |
| Maintenance | serialized OAuth rotation | forced App Server token refresh |
| Shared data | Claude projects/settings preserved | all of ~/.codex except auth.json preserved |
Claude and Codex have separate stores, active profile IDs, credentials, tombstones and
locks. A Codex operation never writes Claude files, and a Claude operation never writes
~/.codex.
Requires Node.js >=22.0.0 and the official Claude/Codex CLIs for the providers you use.
Windows:
setup.cmd # install dependencies and build
switch.cmd # launch; also builds automatically when neededmacOS and Linux:
npm install
npm run build
node dist/cli.jsOn first launch, the setup screen can configure deterministic file-backed Codex switching
(when Codex account evidence exists), create Desktop/menu shortcuts, and schedule a
cross-platform maintenance run every six hours. Existing config.toml content is preserved
and backed up before the single top-level Codex setting is changed. Open setup later with S.
switch.cmd install
switch.cmd uninstallOn macOS and Linux, use node dist/cli.js install and
node dist/cli.js uninstall instead. The installer reports shortcut and scheduler results
independently, so a platform integration failure is visible without hiding successful steps.
Interactive shortcuts target the source-controlled launcher.cjs bootstrap rather than the
disposable dist/cli.js build output. If dependencies or the compiled CLI are missing, the
bootstrap restores them with structured, shell-free child processes; an interactive failure stays
visible instead of making the terminal window disappear immediately.
Linux follows the XDG base-directory and user-directory conventions. Scheduled maintenance uses
a validated, persistent user systemd timer with missed-run recovery; cron is used only when no
user systemd manager is available. Generated units and Desktop Entries launch Node directly,
without sh -c or interpolated credential paths.
The account actions apply only to the visible provider.
| Key | Action |
|---|---|
| Left/Right | switch between Claude and Codex tabs |
| Up/Down or j/k | move that provider's independent cursor |
| PageUp/PageDown, g/G | move by a viewport / jump to first or last account |
| / | find the next account by label, email or plan |
| ? | open the complete nine-page TUI and CLI command reference |
| Enter | switch to the selected account |
| a | copy a remote authorization URL, then paste the returned code/callback |
| A (Claude) | capture an optional machine-bound Claude Desktop session |
| i / I | open the provider inbox / paste or drag credentials or a full migration folder |
| e / E | export selected / all portable credentials for the visible provider |
| r | rename the selected account |
| d | archive the selected non-active account without destroying credentials |
| z | restore the most recently archived account for the visible provider |
| l | highlight the account with the most raw quota headroom |
| b | refresh quotas, then choose the reset-aware Best Now account |
| u | refresh quotas for the visible provider (duplicate requests are coalesced) |
| S | setup shortcuts and scheduled maintenance |
| M | show the complete encrypted Windows → Linux migration workflow |
| q | quit |
Moving the cursor previews that account's cached or freshly-read quota without switching
accounts. A 0% five-hour bucket with no provider reset timestamp is shown as
available now: the rolling window has not started, so there is no honest clock time to
display yet.
The TUI removes duplicate label/email columns, numbers and separates dense account rows, and
keeps only essential hints in the footer. Press ? for the complete nine-page reference:
navigation, quota decisions, accounts, imports, exports, contextual controls, every public CLI
command, and safety rules. Arrows, PageUp/PageDown or j/k change pages; 1-9 jumps directly to a
category, while every list shortcut remains available without entering a submenu. One
low-frequency animation clock drives the provider mascot, active marker and selection cursor.
Set NO_ANIMATION=1 or REDUCE_MOTION=1 to render every decorative element statically.
An unobtrusive ⧉ beside an account means its login session was imported from another
machine; the selected-account panel and help explain the concurrent-machine tradeoff.
Codex plan labels prefer the effective entitlement returned by
account/rateLimits/read over account/read, because the account projection can lag after
an upgrade. OpenAI's internal prolite value is displayed as the customer-facing PRO plan;
the raw provider value remains available in doctor codex for diagnostics. Claude plan labels
come from the official claude auth status --json projection or the validated read-only
provider profile returned for an imported access token.
Codex quota columns are derived from the rolling windows actually returned by the official App Server instead of assuming that both counters always exist. OpenAI currently documents a shared five-hour window, but if that window is temporarily omitted for an account, the TUI hides it; a later successful refresh restores it automatically.
Best Now is deliberately different from l. It trusts fresh, complete quota snapshots
before cached or partial ones, keeps a 5% reserve in every applicable window, then spends
useful capacity from the window that resets soonest. Equivalent choices keep the active
account to avoid an unnecessary process restart. Low-confidence data is shown as an
estimate but never triggers an automatic switch. When every account is exhausted, it
reports the first real upcoming reset instead of switching to a blocked account.
When only the protected final 5% remains, it reports that reserve separately instead of
calling the accounts exhausted.
The list is vertically windowed, so hundreds of accounts do not produce an unbounded Ink
render; / jumps directly by label, email or plan. Narrow terminals automatically hide
secondary columns while the selected-account panel retains email, plan, quota, renewal
and active-state details.
Codex switching is performed by a detached worker. It validates the target first, closes
detected Codex CLI sessions, asks the desktop app to close gracefully, then terminates only
the revalidated Codex processes allowed by the safety policy if needed. It swaps auth.json atomically, validates the
result through App Server, and rolls back on failure. The confirmation warns that unsaved
Codex work can be lost; Claude processes are never force-killed.
Codex code-mode and App Server hosts are protected helpers: migration and credential writes wait
for them to exit and never treat them as ordinary CLI processes that may be force-terminated.
Pressing a never opens a browser. The authorization URL is copied to the clipboard and
also shown in the TUI so it can be sent to another computer.
- Claude: authorize remotely, copy the final authorization code, paste it into the TUI, then press Enter.
- Codex: authorize remotely, copy the complete final
http://localhost:...callback URL, paste it into the TUI, then press Enter. The switcher forwards it only to the exact local callback origin/path created for that login attempt. - Escape cancels either flow before its one-shot result is submitted, without changing saved
accounts. After Enter, Claude finishes the exchange and durable checkpoint before honoring
any cancellation intent; the UI says this explicitly instead of pretending a consumed code
can be undone. Failed or cancelled Codex sandboxes are retained with a non-secret reason
manifest under
backups/codex-abandoned/. If App Server shutdown cannot be proven, its home stays at the exact original path until recovery can safely establish that no helper still owns it.
On Windows, switch.cmd login claude remains an isolated wrapper around the official
Claude CLI login when Anthropic changes the portable paste-code flow. Linux provides the
same fallback as node dist/cli.js login claude. The resulting rotating credential is
committed before the isolated login home is removed.
On macOS, Claude Code stores OAuth in the login Keychain. Because current provider
documentation does not establish that CLAUDE_CONFIG_DIR isolates that Keychain entry,
parked-account add, switching and transactional restore fail clearly instead of risking
the live account. Stored profile metadata, import/export and diagnostics remain available,
but changing the live Claude login on macOS must use Anthropic's official
claude auth login directly. This is a documented provider limitation rather than
pretending file and Keychain auth have identical lifecycle semantics.
- OAuth refreshes are single-flight in-process and locked across processes.
- A newly-issued Claude refresh token is checkpointed into a recoverable pending profile before identity probing; an isolated official-login home is retained if its commit fails.
- A live Claude chain whose identity cannot be attributed is checkpointed into a distinct, non-switchable recovery profile. Workspace organization IDs are never treated as account identity because Team/Enterprise members can share them; the known account envelope and the ambiguous candidate both remain intact.
- Parked Claude rotations are written first to mirrored, independently readable per-account envelopes and an append-before-replace CAS generation. For the active account, the switcher may rotate only while two process checks prove that no official Claude client is running; it atomically updates the official live file first, then promotes the saved envelope with an exact predecessor CAS. Metadata never changes before a durable credential copy exists, stale writers cannot replace newer generations, and valid history is bounded to the newest 24.
- If Claude is running, it exclusively owns the active refresh token and the switcher preserves
cached quota instead of rotating underneath it. After a reboot or while Claude is closed,
startup,
u, and scheduled maintenance can safely renew the expired active token under the provider lock, so the active account no longer remains stale merely because the PC was off. - Codex does not need this Claude-specific quiescence path: active Codex renewal stays inside
the official App Server
account/read(refreshToken=true)lifecycle and its isolated adapter. - If the running official client replaces the active OAuth chain completely, reconciliation promotes it only when the stable account UUID plus the official status e-mail and organization all identify the same saved profile. A mismatch remains quarantined; e-mail or a shared organization alone can never overwrite a saved credential.
- Sensitive writes use a shared fail-closed primitive: a unique same-directory temp file,
0600, file flush, then atomic rename. There is no direct-write fallback that could truncate a valid credential. Stores use last-known-good mirrors, actually-read recovery snapshots and reversible tombstones. A stale writer cannot silently remove or resurrect a profile; an explicitzrestore uses a recoverable two-phase marker and records a newer event, so a crash at either metadata boundary leaves the account visible or the restore retryable. Portable imports cannot silently resurrect voluntarily archived Claude identities. - Claude's two live auth files are covered by a durable transaction journal anchored to the exact rollback-manifest SHA-256. Startup reclaims only provably abandoned switch/live locks, rejects substituted recovery generations, and restores the outgoing pair byte-for-byte after a crash between the two atomic renames.
- Claude Desktop captures use a versioned, complete session scope. Every present file or deterministic directory tree and every explicit absence is covered by the v2 manifest; application validates it before and after the swap. A durable transaction journal restores the exact outgoing bundle after an interrupted apply/recapture, while unrelated Desktop settings remain untouched. Legacy v1 captures remain listed for recovery but must be recaptured before switching because they have no integrity fingerprints.
- A manually typed Desktop email never auto-links or replaces a Claude Code credential. Desktop captures default to independent rows; machine-bound sessions are explicitly reported as skipped by portable exports.
- Last known quotas remain visible as
stalewhen a live refresh fails. - Credential exports take provider and per-account rotation locks, reconcile and reread the durable stores, and refuse to run while process safety is unknown. They therefore cannot serialize an invalid predecessor from a stale TUI object. Export names are timestamped and never overwrite an older recovery artifact.
- Claude and Codex have separate private import inboxes. A complete source is validated before
its first account is committed. After a successful inbox import, the exact consumed files are
moved under
import/processed/<provider>/with a secret-free receipt and SHA-256 fingerprints. Files selected from the Desktop, USB media, or a shared/network directory are intentionally left in place; importing never silently deletes an arbitrary source. - Imports immediately attempt a bounded, read-only provider metadata recovery. Claude sends
only the current access token to its profile projection (never the refresh token); Codex uses
the official App Server with forced refresh disabled. Responses are schema-validated before
replacing account IDs, e-mail, organization or plan. Network/provider failure never rolls
back a valid file import: missing details retry on startup and with
u. - Cancelled/failed Codex login sandboxes remain under
backups/codex-abandoned/;doctor codexinventories valid and damaged evidence, whilezcan explicitly recover the newest valid login without deleting its diagnostic archive. - Each Claude/Codex account has a separate credential envelope under
~/.claude-switch/credentials/.
Maintenance cannot guarantee an authorization forever. Anthropic documents expiring login
sessions and warns shortly before renewal is required; an organization administrator can
also enforce a shorter session. OpenAI documents that Codex caches ChatGPT login either in
auth.json or the OS credential store and refreshes ChatGPT tokens automatically. Either
provider can still revoke a session server-side. The switcher therefore promises durable
profile retention, not immortal OAuth grants: metadata, last-known quota and recovery
copies remain present and the row is marked for re-authentication.
Current provider references:
Claude Code and Codex CLI run natively on Linux, but the current vendor Desktop packages do not list Arch/CachyOS as a supported target. Claude Desktop provides Debian/Ubuntu packages, while the ChatGPT/Codex Linux preview lists specific Ubuntu, Debian and Fedora releases. This project therefore uses an Ubuntu 24.04 Distrobox for both indispensable graphical apps on CachyOS instead of pretending an unofficial Arch repack is vendor-supported.
Nothing privileged is run automatically. Display the exact setup, then execute only the commands you approve:
node dist/cli.js linux-desktop guideThe commands shown by the guide install Podman/Distrobox and the XDG/Wayland clipboard tools on the host, create the
Ubuntu box, points to the two current official .deb download pages, exports claude-desktop and
chatgpt to the host menu, and finally registers structured no-shell launch descriptors:
node dist/cli.js linux-desktop configure claude-codex-desktop
node dist/cli.js linux-desktop doctor
node dist/cli.js doctor allDistrobox shares the user's home and forwards Wayland/X11/audio integration. Each Linux Desktop app still needs one initial Linux sign-in per provider account. Windows Electron cookies, DPAPI material and Chromium databases are archived as recovery evidence, but are never injected into the Linux profile: doing so would be unreliable and could corrupt or expose a session.
Provider e/E exports remain useful for moving individual accounts. The migration workflow is
the full-machine path: one versioned archive contains the switcher's account metadata, independent
credential envelopes, backups, tombstones and Desktop captures, plus portable Claude/Codex settings,
projects, sessions, histories, plugins, skills and memories. Windows-only Desktop state is included
under a recovery-only scope. Locks, sockets, transient jobs, temporary files, rebuildable Desktop
caches and symbolic links are not copied; every exclusion and its reason appears in the encrypted
manifest.
Windows-only provider executables and scripts (.exe, .dll, .cmd, .bat, .ps1) are moved to
the recovery-only scope instead of being installed into the Linux live homes. Known Claude/Codex
configuration files containing Windows drive or UNC paths remain available, but the manifest flags
them for review and import writes a portability-review.json under migration recovery backups.
Codex .sandbox/*.log files are active runtime diagnostics rather than portable account data; they
are excluded explicitly with their reason in the encrypted manifest, while durable sandbox markers
and configuration remain included.
On Windows, inventory first and read every warning:
switch.cmd migration prepareClose Claude Code/Desktop and Codex CLI/Desktop normally, then create the archive. The passphrase is read without echo and is never accepted in a command-line argument or environment variable:
switch.cmd migration export
# Non-interactive alternative: --passphrase-file C:\private\migration-passphraseOn CachyOS, clone/build this project and copy either the .ccswitch-migration file or its portable
folder. The CLI accepts both forms and requires exactly one archive directly inside a folder:
node dist/cli.js migration inspect /path/to/Coder
node dist/cli.js migration verify /path/to/Coder
node dist/cli.js migration import /path/to/CoderThe interactive path is shorter: press I and paste or drag the Coder folder. When the folder
contains Claude-Codex-Coder-Recovery-Key.txt beside its single archive, the TUI reads that key
automatically without displaying or logging it. Otherwise it opens the masked passphrase prompt.
The TUI authenticates and hashes the complete payload before live writes, creates rollback backups
for differing existing files, and leaves the external folder untouched.
The portable folder is intentionally a one-step user workflow: its adjacent key is managed by the
software and import detects it automatically. Internally, the archive still uses scrypt key
derivation, AES-256-GCM authenticated encryption, fast streaming Brotli compression and a SHA-256
for every file; this prevents raw account tokens from being left as ordinary files and detects
corruption without asking the user to handle a separate secret. Import decrypts and validates the complete archive into a
private temporary payload before touching live data, then streams bounded file ranges into atomic
target writes without loading large histories into memory. Different existing files are refused by default.
After inspecting the archive, --replace-existing explicitly authorizes transactional replacement;
every replaced target is backed up, writes are atomic, all installed hashes are rechecked and a failed
operation rolls back.
For a normal passphrase file on Linux, run chmod 600 first. Store that file outside
~/.claude-switch, ~/.claude and ~/.codex; export rejects a passphrase file under any source
root. Keeping the archive and passphrase separately provides the strongest protection. The named
portable key above deliberately trades that theft protection for a one-folder recovery workflow:
anyone who obtains the complete folder can decrypt it. The archive remains authenticated and its
contents are still never stored as plaintext in that folder.
Large histories and recovery-only Desktop stores can take several minutes to hash and encrypt. The
CLI reports that the inventory is running; keep enough free space for the encrypted archive and one
private decrypted payload on the target. Temporary payloads use the switcher's private
migration-work/ directory on the home-data filesystem instead of assuming /tmp has enough space.
Decompressed payloads are rejected above the explicit 64 GiB safety ceiling.
Migration preserves every saved account, including rows already marked needs re-add. It cannot make
a provider-revoked or expired OAuth grant valid again. Codex file-backed auth.json sessions and valid
Claude .credentials.json sessions are portable, but either provider may require official re-login;
the switcher retains the account and reports that requirement instead of deleting it. This is the
honest boundary behind “lose no account”: all recoverable data is retained, but server-side login
validity cannot be manufactured offline.
Press i on either provider tab to open its guided inbox and o to open that folder in
Explorer/Finder/the desktop file manager. Copy or drag a credential/export into the folder,
return to the switcher, press r, select the detected source, and press Enter. Press I instead
to type, paste, or drag an existing file/folder path directly into the terminal.
For Claude Code on Windows and Linux, the official subscription credential is the single
file ~/.claude/.credentials.json (or .credentials.json under CLAUDE_CONFIG_DIR). That file
is enough to import the reusable login. The separate ~/.claude.json file is optional identity
metadata that can improve the initial e-mail/label; it is not a second required credential.
macOS stores Claude credentials in Keychain, so use this switcher's e/E portable export on
the source Mac instead of copying a raw file. The undotted credentials.json name remains
accepted only as an explicit legacy import alias; it is never treated as Claude's live store.
For Codex, raw migration uses ~/.codex/auth.json only when the official effective
cli_auth_credentials_store is file. OpenAI documents copying that cache to a headless
machine; keyring-backed credentials cannot be reconstructed from a nonexistent file. The
provider-tagged *.ccswitch.json and *.codexswitch.json formats are preferred because they
carry unambiguous provider/account metadata and support one-account or export-all bundles.
After validation, the switcher asks the provider for current account metadata and updates the label/e-mail, stable identity, organization and plan when those fields are available. Claude's profile projection is provider-internal rather than a documented compatibility contract, so its response is treated as optional and strictly validated. An unavailable or changed endpoint does not make the import fail; the saved file metadata remains usable and recovery retries later.
Raw credential copying duplicates a session, not an account. The original PC does not stop working merely because a file was copied, but two computers can then hold the same rotating OAuth chain. No local switcher can coordinate refresh-token rotation across disconnected machines. For long-running concurrent use, authenticate independently on each PC; use raw copy primarily for migration/recovery. Provider revocation or documented login expiry can still require re-authentication.
Codex profiles deliberately store file-backed auth.json copies. Managed reconciliation,
refresh and switching therefore require this effective setting in ~/.codex/config.toml:
cli_auth_credentials_store = "file"Every switch additionally starts the official App Server without overriding that setting
and verifies that Codex's effective credential projection matches the selected account.
An auto/keyring/config mismatch fails closed instead of associating workspaces by email;
doctor all reports the effective credential-store setting.
switch.cmd login claude
switch.cmd login codex
switch.cmd import --provider claude <path>
switch.cmd import --provider codex <path>
switch.cmd import-all --provider claude <bundle-or-folder>
switch.cmd import-all --provider codex <bundle-or-folder>
switch.cmd export-all claude
switch.cmd export-all codex
switch.cmd migration prepare
switch.cmd migration export [--output <file>] [--passphrase-file <0600-file>]
switch.cmd migration inspect|verify <archive-or-folder> [--passphrase-file <0600-file>]
switch.cmd migration import <archive-or-folder> [--replace-existing] [--passphrase-file <0600-file>]
switch.cmd linux-desktop guide|configure|doctor
switch.cmd doctor all
switch.cmd keep-alive
switch.cmd --dry-run
switch.cmd restore claude [backup-path]
switch.cmd restore codex [backup-path]
switch.cmd --help
Those examples use the Windows launcher. On macOS and Linux, replace switch.cmd with
node dist/cli.js.
doctor all reports both providers without printing tokens. keep-alive isolates Claude
and Codex failures: both providers run even if one is corrupt, and the command exits
non-zero with an aggregate summary when either provider fails.
Everything managed by the switcher lives under ~/.claude-switch/ by default. Set
CLAUDE_SWITCH_HOME to place the switcher store elsewhere; --switch-home <path> is the
equivalent per-invocation flag. Scheduled maintenance captures the resolved switcher,
Claude and Codex homes when it is installed. This switcher store is independent from
CLAUDE_CONFIG_DIR and CODEX_HOME:
profiles.json/.bak: Claude metadata, active account and tombstones (no OAuth token)codex-profiles.json/.bak: Codex metadata, active account and tombstonescredentials/claude/<id>/credentials.json: one Claude OAuth envelope per accountcredentials/claude/<id>/generations/: bounded CAS history for rotating Claude credentialscredentials/codex/<id>/auth.json: one Codex ChatGPT auth file per accounttransactions/claude-live-auth.json: pending two-file recovery journal (normally absent)backups/: account-set, deleted-account and pre-switch rollback snapshotslogs/switch.log: activity log with secret values redactedimport/claude/andimport/codex/: provider-specific active inboxes containing secretsimport/processed/<provider>/: consumed inbox evidence plus secret-free import receiptsexports/: timestamped portable files containing secrets; older exports are never overwrittenexports/*.ccswitch-migration: encrypted full-machine archives; passphrases are never stored herebackups/migration-import-*: rollback copies for files explicitly replaced during a full importbackups/migration-recovery/: Windows-only Desktop evidence never restored into Linux live state
Credential envelopes are plain JSON protected by user-directory permissions, not application-level encryption. Never commit or share them. Legacy profile stores are migrated atomically. Legacy backups remain retained as recovery evidence, but automatic restore refuses formats that cannot prove a complete integrity-scoped generation.
On Windows and Linux, live Claude Code OAuth is always read from the provider-owned
~/.claude/.credentials.json (or .credentials.json under CLAUDE_CONFIG_DIR). An undotted
credentials.json sibling is ignored and preserved as recovery evidence; it never blocks quota
refresh, login reconciliation or account switching.
npm test
npm run typecheck
npm run build
node dist/cli.js doctor all
npm audit
The test suite also covers fail-closed atomic replacement, recursive log redaction, corrupt store recovery, strict Codex imports, transaction rollback including initially absent live files, quota confidence/reserve/hysteresis, large-list viewport navigation, provider isolation, remote callback validation and abandoned-login preservation.
Copyright © 2026 LightZirconite. The project is free software under the GNU Affero General Public License v3.0 or later. Keep the copyright, license and source notices when redistributing it.
The copyright holder may also offer a separate commercial license to organizations that want to integrate or redistribute the project without the AGPL copyleft obligations. See COMMERCIAL-LICENSE.md. Open-source availability and attribution do not guarantee revenue; paid licensing, support and sponsorship remain separate business options controlled by LightZirconite.
