Skip to content

KNQuoc/seatswitch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

seatswitch

Fast-switch between ChatGPT Business seats for the Codex CLI and desktop app.

macOS only at the moment. A ~5-second swap between accounts when one hits its 5-hour message limit, instead of the usual 2-minute manual sign-out / sign-in / 2FA / approve dance.

⚠️ Disclaimer

Pooling multiple ChatGPT Business seats for a single user is against OpenAI's Team plan terms of service. OpenAI's auth server actively enforces single-active-session per (workspace, device, client), which means this tool cannot give you concurrent sessions — only fast sequential ones. You are responsible for understanding your obligations under the plan you signed up for.

This project is published for educational and research purposes. Use at your own risk.

What this gives you

  • One command to switch which ChatGPT Business seat your Codex CLI is signed in as, in ~5 seconds.
  • Automatic 5-hour cooldown tracking per seat so you don't switch back to a seat that's still rate-limited.
  • A launchd job that keeps the cooldown clock accurate in the background.
  • A one-shot app-next command that quits Codex.app, swaps seats, and relaunches the app.

What this does not give you:

  • Concurrent sessions across seats. OpenAI's OAuth server revokes prior tokens whenever a new one is issued for the same workspace + device. You can only have one seat alive at a time.
  • Any way around the 5-hour limit itself. The tool just rotates which seat is bearing the limit.

How it works

Each seat is mapped to a dedicated Chrome profile that stays persistently signed into chatgpt.com with one of your accounts. Switching seats runs codex login, intercepts its OAuth URL, and opens the URL in the right Chrome profile via --profile-directory. Because chatgpt.com is already authenticated in that profile, the OAuth callback completes in seconds with zero or one click. Codex writes new tokens to ~/.codex/auth.json, which both the CLI and the desktop app read on launch.

State (current seat, cooldown timestamps, profile assignments) lives in ~/.codex-seats/state.json, guarded by flock.

Requirements

  • macOS (uses launchd, AppleScript, and open -na — Linux/Windows would need a port)
  • Codex CLI (@openai/codex)
  • Python 3 (ships with macOS)
  • Google Chrome (or Chromium-derived browser that honors --profile-directory)
  • A ChatGPT Business workspace with multiple seats you control

Install

git clone https://github.com/<your-user>/seatswitch.git ~/seatswitch
echo 'export PATH="$HOME/seatswitch/bin:$PATH"' >> ~/.zshenv
exec zsh
codex-seat init

Verify the wrapper is on your PATH:

which codex-seat   # should point inside the cloned repo
which codex        # should point inside the cloned repo, BEFORE /opt/homebrew/bin/codex

If your Codex CLI lives somewhere other than /opt/homebrew/bin/codex, set:

export SEATSWITCH_REAL_CODEX=/path/to/codex

Set up Chrome profiles

Open Chrome → profile menu (top right) → "Add". Create one profile per ChatGPT account you want to use. In each profile, navigate to chatgpt.com, sign in with that account, and check "keep me signed in".

List the profiles seatswitch can see:

codex-seat list-profiles

Map each seat slot to a profile:

codex-seat assign seat1 "Default"
codex-seat assign seat2 "Profile 1"
codex-seat assign seat3 "Profile 2"
# ... up to seat6
codex-seat status   # verify

Optional: install the launchd cooldown clearer

Clears expired 5-hour cooldowns once a minute so status is always accurate. Without it, expired cooldowns are still cleared on the next codex-seat next invocation, just lazily.

./scripts/install-launchd.sh   # renders the plist with this repo's path and loads it
tail -f /tmp/seatswitch.log    # watch the ticks

Uninstall with ./scripts/uninstall-launchd.sh.

Usage

codex                          # run Codex CLI against the current seat
codex-seat next                # cool current seat, fast-switch to next, relaunch codex
codex-seat switch seat3        # jump to a specific seat
codex-seat status              # current seat, cooldowns, emails per seat
codex-seat app                 # launch Codex.app on the current seat
codex-seat app-next            # quit app, fast-switch, relaunch app

When you hit the 5-hour limit mid-session

# In Codex CLI: exit (Ctrl-D), then:
codex-seat next                # ~5s

# In Codex.app: just one command, no manual quit:
codex-seat app-next            # ~8s including app relaunch

Commands

command what it does
codex [args...] run Codex CLI against the live auth (shim around codex-seat run)
codex-seat init create state dirs and empty state.json
codex-seat list-profiles list Chrome profile directory names
codex-seat assign seatN PROFILE map a seat to a Chrome profile directory name
codex-seat status current seat, profiles, cooldowns, decoded emails
codex-seat switch seatN fast-switch to a specific seat; relaunches Codex.app if running
codex-seat next [args...] cool current, fast-switch to next eligible, launch codex
codex-seat exhausted [seat] mark a seat exhausted manually (default: current, 5h cooldown)
codex-seat clean-cooldowns clear expired cooldowns (launchd calls this every minute)
codex-seat app launch Codex.app against the current seat
codex-seat app-quit quit Codex.app via AppleScript
codex-seat app-next quit app, fast-switch to next eligible, relaunch app
codex-seat login seatN manual interactive codex login + snapshot (rarely needed)
codex-seat run [args...] what the codex shim invokes; exec's real codex

Environment

variable default meaning
SEATSWITCH_REAL_CODEX /opt/homebrew/bin/codex path to the real codex binary
SEATSWITCH_BROWSER Google Chrome application name passed to open -a

Files

~/.codex-seats/
├── state.json          # currentSeat, lastAuthMtime, per-seat cooldownUntil/lastUsed/browserProfile
├── .lock               # flock target
├── .last-login.log     # captured stdout/stderr of the most recent `codex login` (for debugging)
└── seat1/.../seat6/
    └── auth.json       # snapshot of that seat's tokens after most recent login (informational only)

Caveats

  • Chrome profile signed out → OAuth doesn't auto-complete and codex login times out at 90s. Fix: open the affected Chrome profile, re-sign-in to chatgpt.com, retry.
  • Desktop app and CLI share ~/.codex/auth.json. They can't be on different seats at the same time. The desktop app caches auth in memory at launch, so swapping seats while the app is running has no effect until the app is quit and relaunched — codex-seat app-next handles this for you.
  • auth.json snapshots in ~/.codex-seats/seatN/ are not used to restore sessions. OpenAI revokes them as soon as a different seat logs in. They're kept around for the status command's email column.
  • codex login --device-auth is not used. We rely on the standard OAuth callback flow so that chatgpt.com cookies in the Chrome profile carry the authentication.
  • Two simultaneous codex processes could race over ~/.codex/auth.json token refreshes. The flock only protects state.json, not the live auth file written by codex itself. Either run one at a time, or accept the small chance of needing to swap again.

Architecture notes

If you want to understand why this design is what it is — see the long version above. Short version: OpenAI's OAuth server enforces single-active-session per (workspace, device, client). We confirmed empirically that logging in seat N revokes seat N-1's refresh token. The only way to get fast seat switching on a single machine is to make each fresh login fast — which is what the pre-signed Chrome profile trick accomplishes.

Contributing

Issues and PRs welcome. The whole thing is one Python file plus a bash shim, so it's easy to dive into.

Porting to Linux or Windows

The bits that hard-code macOS, with where to look:

  • open_url_in_chrome_profile() in bin/codex-seat — uses open -na "Google Chrome" --args. Linux: google-chrome --profile-directory=.... Windows: chrome.exe --profile-directory=....
  • list_chrome_profiles() in bin/codex-seat — uses ~/Library/Application Support/Google/Chrome/. Linux: ~/.config/google-chrome/. Windows: %LOCALAPPDATA%\Google\Chrome\User Data\.
  • is_codex_app_running() / quit_codex_app() / launch_codex_app() — uses osascript and open -a. Linux: pgrep + pkill + the desktop app binary. Windows: PowerShell Get-Process / Stop-Process / Start-Process.
  • CODEX_APP_PATH and the default SEATSWITCH_REAL_CODEX — platform-dependent paths.
  • launchd/ and scripts/install-launchd.sh — replace with a systemd user unit on Linux or a Task Scheduler XML on Windows.

The CLI swap half is fairly tight — the porting work is mostly the desktop-app half and the background scheduler. Firefox container support would replace the Chrome profile mechanism with ext+container: URLs.

License

MIT

About

Fast-switch between ChatGPT Business seats for the Codex CLI and desktop app (macOS)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors