Observed since 21 Oct, 09:50 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Unified multi-account manager for Gemini CLI and Codex CLI on Ubuntu/Linux
Switch between multiple Google Gemini and OpenAI Codex accounts instantly — no re-login required. AI Account Switcher manages OAuth tokens, API keys, keyring credentials, and shell wrappers so you can jump between work, personal, and test accounts in a single command.
switcher gemini quota shows quota used per OAuth profile with visual progress barsstopHookActive flag to prevent deadlocksswitcher gemini menu launches a numbered TUI for profile management without memorising commandsenv.sh sourced per invocation via shell wrappers and aliasesswitcher gemini pool health/export/status for managing the rotation poolswitcher alerts tails recent error-log entries for quick diagnosticsswitcher version --check checks PyPI for a newer releaseerrors.log for error-only entries and commands.log for per-invocation timing~/.config/ai-account-switcher/| Requirement | Details |
|---|---|
| Python | 3.10 or newer |
| OS | Linux (Ubuntu 22.04+ recommended) |
| CLI tools | Gemini CLI and/or Codex CLI |
| Optional | GNOME Keyring or KWallet for secure credential storage |
# 1. Clone the repository
git clone https://github.com/your-user/ai-account-switcher.git
cd ai-account-switcher
# 2. Install from PyPI
pip install ai-account-switcher
# --- OR install from source (development) ---
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
# 3. Install shell integration, hooks, and the `switcher` bin symlink
switcher install
# 4. Reload your shell
source ~/.bashrc # or ~/.zshrc
switcher version
# ai-account-switcher 0.3.0
switcher
# Shows the status dashboard
switcher uninstall # removes shell hooks, aliases, bin symlink
pip uninstall ai-account-switcher
# Import your existing Gemini login as a named profile
switcher gemini import ~/.gemini/oauth_creds.json work
# Import a second account
switcher gemini import ~/backup/oauth_creds.json personal
# See all profiles
switcher gemini list
# Switch to the personal account
switcher gemini switch personal
# View the dashboard
switcher
switcher gemini add my-account --type oauth
# Follow the OAuth flow in your browser, then import the generated credentials
switcher gemini add my-api-key --type apikey
# The command creates a profile directory — add your API key to the file shown in output
# By label
switcher gemini switch work
# By index number (shown in `list`)
switcher gemini switch 2
# Rotate to the next profile in order
switcher gemini next
# /change command — no target rotates, label/number switches directly
switcher gemini change
switcher gemini change personal
switcher gemini change 2
switcher gemini remove personal
switcher gemini remove 3
switcher codex add work-key --type apikey
# Add your OPENAI_API_KEY to the file shown in output
switcher codex import ~/.codex/auth.json my-chatgpt
switcher codex switch work-key
switcher codex next
When switching Codex profiles, the following are automatically snapshotted and restored:
policy.toml is saved per profile and restored on switch# Export by label — writes to current directory
switcher gemini export work
# Export to a specific path
switcher gemini export work ~/backups/gemini-work.json
# Export by index number
switcher gemini export 1 ~/backups/
# Import with a custom label
switcher gemini import /path/to/oauth_creds.json work-account
# Import with auto-detected label
switcher codex import /path/to/auth.json
The importer auto-detects the auth type (OAuth vs API key) from the file contents.
You can export profiles on one machine and import them on another:
# ── Machine A ──
switcher gemini export work /tmp/work-creds.json
# Transfer the file securely (scp, USB, etc.)
scp /tmp/work-creds.json user@machine-b:/tmp/
# ── Machine B ──
switcher gemini import /tmp/work-creds.json work
What transfers:
oauth_creds.json with refresh tokens)api_key.txt or auth.json)What does NOT transfer:
Security notes:
Auto-rotation uses Gemini CLI's hook system to detect quota exhaustion and automatically switch to the next available profile.
switcher config set auto_rotate.enabled true
# Maximum retry attempts per session (default: 3)
switcher config set auto_rotate.max_retries 3
# Quota threshold percentage for proactive switching (default: 10)
switcher config set auto_rotate.threshold 10
# Rotation strategy: "conservative" (default) or "gemini3-first"
switcher config set auto_rotate.strategy conservative
# Cache quota check results for N minutes (default: 5)
switcher config set auto_rotate.cache_minutes 5
# Enable proactive pre-check before each request (default: true)
switcher config set auto_rotate.pre_check true
# After rotating, instruct Gemini CLI to restart the agent (default: false)
switcher config set auto_rotate.restart_on_switch true
switcher gemini next. After rotating, writes a short-lived handoff flag so the next hook invocation skips its API call. Safely exits with {} when stopHookActive is set.{} when stopHookActive is set.Hooks are registered in ~/.gemini/settings.json by switcher install. Re-running switcher install is safe — hooks are updated in-place without duplicates.
View real-time quota usage for all Gemini OAuth profiles:
switcher gemini quota
Sample output:
ℹ️ Fetching quota for 2 OAuth profile(s)...
──────────────────────────────────────────────────────────────
work ●
(you@work.com) [Tier: Standard]
gemini-2.0-flash ██░░░░░░░░░░░░ 14.7% used
personal
(you@personal.com)
gemini-2.0-flash ████████████░░ 87.9% used ⚠️ resets in 4 h
● marks the active profile⚠️ appears when quota used exceeds 80%Launch a numbered TUI for profile management — useful when you don't remember the exact command:
switcher gemini menu
# or
switcher codex menu
Gemini Profile Manager
Active: work
────────────────────────────────────
1) List all profiles
2) Switch to a profile
3) Rotate to next profile
4) Show live quota usage
5) Check profile health
6) Add a new profile
7) Import credentials
8) Toggle auto-rotate on/off
q) Quit
Choice:
When stdin is not a TTY (e.g. piped or in a script), the menu prints a compact command reference instead of prompting.
switcher install configures the following CLI-native slash commands:
/change → alias for switcher gemini change — no argument rotates to next profile, a label or number switches directly~/.gemini/commands/change.tomlai-account-switcher currently.sw codex next).Validate that your profiles' credentials are still working:
# Check all Gemini profiles
switcher gemini health
# Check all Codex profiles
switcher codex health
Health checks verify:
healthy, expired, invalid, rate_limited, unknownThe pool sub-commands offer a focused view of the rotation pool:
# List all pool profiles
switcher gemini pool list
# Run health checks across all pool profiles
switcher gemini pool health
# Export all pool profiles to a directory
switcher gemini pool export --dest ~/backups/gemini-pool/
# Show rotation state and next-up profile
switcher gemini pool status
# Tail the last 20 error-log entries (default)
switcher alerts
# Show more lines
switcher alerts --lines 50
# Check for a newer release on PyPI
switcher version --check
Discrete log files:
| File | Contents |
|---|---|
~/.config/ai-account-switcher/logs/switcher.log | Full application log (all levels) |
~/.config/ai-account-switcher/logs/errors.log | Error-only entries for quick triage |
~/.config/ai-account-switcher/logs/commands.log | Per-invocation timing and exit status |
All configuration is stored in ~/.config/ai-account-switcher/config.toml:
[general]
log_level = "info" # debug, info, warning, error
[auto_rotate]
enabled = false
max_retries = 3
threshold = 10
strategy = "conservative" # or "gemini3-first"
cache_minutes = 5
pre_check = true
restart_on_switch = false # ask Gemini CLI to restart agent after rotation
# Show all configuration
switcher config
# Get a specific value
switcher config get general.log_level
# Set a value
switcher config set general.log_level debug
| Command | Description |
|---|---|
switcher or switcher status | Show status dashboard for all CLIs |
switcher config | Show current configuration |
switcher config set <key> <value> | Set a config value |
switcher install | Install shell integration, hooks, and bin symlink |
switcher uninstall | Remove all integration |
switcher alerts [--lines N] | Tail recent error-log entries |
switcher version | Print version |
switcher version --check | Print version and check PyPI for updates |
| Command | Description |
|---|---|
switcher gemini list | List all Gemini profiles with status |
switcher gemini switch <n|label> | Switch to a profile by index or label |
switcher gemini next | Rotate to the next profile |
switcher gemini change [n|label] | Slash-command parity: no arg → next, label/index → switch |
switcher gemini menu | Launch interactive profile management menu |
switcher gemini quota | Show live quota usage (% used) for all OAuth profiles |
switcher gemini add [label] [--type oauth|apikey] | Create a new empty profile |
switcher gemini remove <n|label> | Delete a profile |
switcher gemini import <path> [label] | Import credentials from a file |
switcher gemini export <n|label> [dest] | Export profile credentials to a file |
switcher gemini health | Run health checks on all profiles |
switcher gemini pool | Alias for list |
switcher gemini pool list | List rotation pool profiles |
switcher gemini pool health | Run health checks across pool profiles |
switcher gemini pool export [--dest DIR] | Export all pool profiles to a directory |
switcher gemini pool status | Show rotation state and next-up profile |
switcher gemini pool add [label] | Alias for add |
switcher gemini pool remove <n|label> | Alias for remove |
switcher gemini pool import <path> [label] | Alias for import |
| Command | Description |
|---|---|
switcher codex list | List all Codex profiles with status |
switcher codex switch <n|label> | Switch to a profile by index or label |
switcher codex next | Rotate to the next profile |
switcher codex change [n|label] | Slash-command parity: no arg → next, label/index → switch |
switcher codex menu | Launch interactive profile management menu |
switcher codex add [label] [--type apikey|chatgpt] | Create a new empty profile |
switcher codex remove <n|label> | Delete a profile |
switcher codex import <path> [label] | Import auth.json or API key file |
switcher codex export <n|label> [dest] | Export profile credentials to a file |
switcher codex pool | Alias for list |
switcher codex pool list | List rotation pool profiles |
switcher codex pool health | Run health checks across pool profiles |
switcher codex pool export [--dest DIR] | Export all pool profiles to a directory |
switcher codex pool status | Show rotation state and next-up profile |
switcher codex pool add [label] | Alias for add |
switcher codex pool remove <n|label> | Alias for remove |
switcher codex pool import <path> [label] | Alias for import |
~/.gemini/oauth_creds.json → profile directorygemini-cli-oauth, key: main-account)~/.gemini/mcp-oauth-tokens.json (token cache) to avoid stale sessionsGEMINI_API_KEY and GOOGLE_API_KEY to ~/.config/ai-account-switcher/env.shenv.sh before launching gemini~/.codex/auth.json → profile directoryOPENAI_API_KEY to env.shenv.sh before launching codex~/.codex/auth.json → profile directoryreload())ai-account-switcher/
├── main.py # Direct-run entry point
├── pyproject.toml # Project metadata and dependencies
├── docs/
│ └── spec.md # Full technical specification
├── switcher/
│ ├── __init__.py
│ ├── cli.py # Argument parsing, command routing
│ ├── config.py # TOML config management
│ ├── state.py # Active profile state + OAuth client cache + handoff flags
│ ├── ui.py # Terminal colors, tables, dashboard
│ ├── ui_menu.py # Interactive numbered TUI menu
│ ├── utils.py # XDG paths, logging, file locking, atomic symlinks
│ ├── errors.py # Custom exception hierarchy
│ ├── health.py # Token/key validation, quota fetch, OAuth client discovery
│ ├── installer.py # Shell RC injection, hooks, bin symlinks, env.sh generation
│ ├── profiles/
│ │ ├── base.py # Abstract ProfileManager
│ │ ├── gemini.py # Gemini profile operations
│ │ └── codex.py # Codex profile operations + isolation integration
│ ├── auth/
│ │ ├── keyring_backend.py # Keyring CRUD with file fallback
│ │ ├── gemini_auth.py # Gemini credential activation
│ │ ├── codex_auth.py # Codex credential activation
│ │ ├── codex_memory.py # Per-profile Codex memory snapshot/restore
│ │ ├── codex_plugins.py # Per-profile plugin list snapshot + divergence warning
│ │ └── codex_sandbox.py # Per-profile sandbox policy snapshot/restore
│ └── hooks/
│ ├── quota_patterns.py # Centralised quota-error regex patterns
│ ├── gemini_after_agent.py # Post-response quota error detection + rotation
│ └── gemini_before_agent.py # Pre-request proactive quota check
└── tests/ # Test suite (pytest, 471+ tests)
| Path | Purpose |
|---|---|
~/.config/ai-account-switcher/config.toml | User preferences |
~/.config/ai-account-switcher/state.json | Active profiles, rotation state |
~/.config/ai-account-switcher/state/quota_error_gemini.json | Short-lived handoff flag (AfterAgent → BeforeAgent) |
~/.config/ai-account-switcher/env.sh | Exported API key environment variables |
~/.config/ai-account-switcher/cache/oauth_client.json | Cached OAuth client credentials (24-hour TTL) |
~/.config/ai-account-switcher/profiles/ | Profile credential storage |
~/.config/ai-account-switcher/logs/switcher.log | Full application log |
~/.config/ai-account-switcher/logs/errors.log | Error-only log for quick triage |
~/.config/ai-account-switcher/logs/commands.log | Per-command timing and exit status |
~/.gemini/oauth_creds.json | Symlinked by switcher |
~/.gemini/settings.json | Gemini hooks registered here |
~/.codex/auth.json | Symlinked by switcher |
Contributions are welcome! Here's how to get started.
# Clone and enter the repository
git clone https://github.com/your-user/ai-account-switcher.git
cd ai-account-switcher
# Create a virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install in editable mode with dev dependencies
pip install -e ".[dev]"
# Run the full test suite
pytest
# Run a specific test file
pytest tests/test_config.py -v
# Run with coverage
pytest --cov=switcher --cov-report=term-missing
# Check for lint issues
ruff check switcher/ tests/
# Auto-fix lint issues
ruff check --fix switcher/ tests/
# Check formatting
ruff format --check switcher/ tests/
# Auto-format
ruff format switcher/ tests/
mypy switcher/
from __future__ import annotations for modern union syntaxpathlib.Path for all filesystem paths — never raw stringsdataclasses.dataclass(slots=True) for structured dataArgs:, Returns:, Raises:)argparse for CLI — no third-party CLI frameworks~/.gemini/, ~/.codex/, or the OS keyring in teststmp_path fixtures and mock_keyring for isolationswitcher.utils.get_config_dir() / get_gemini_dir() / get_codex_dir() to return temp pathsrequests.post / requests.get) for health checks and quota APIsgit checkout -b feature/my-featureruff check && ruff format --check && mypy switcher/ && pytestmainswitcher versionswitcher alerts or check ~/.config/ai-account-switcher/logs/errors.logThis project is licensed under the MIT License — see the LICENSE file for details.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.3.0 | 10 Mar 2026 | 9 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 09:50 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:c0aab1e270c5c10279bcea76acfba7c4f9ccbc5dddc6951e63efee3073958fa8