Skip to content
Conduit

skill

LLMSwapper

Local dashboard to switch the active Claude Code account with one click, showing each account's 5-hour and weekly quota. Zero dependencies, host and WSL, Docker-ready.

monac-cc/LLMSwappercommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/monac-cc/LLMSwapper itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 21, 2026

LLMSwapper

Switch the active AI-subscription account with one click - and see how much quota each one has left before you do.

Today it swaps Claude Code and OpenAI Codex CLI accounts. The name is a plan, not a claim. What is coming.

test Node dependencies Docker platform license


Install

Node 18+ and Claude Code, Codex CLI or both. No npm install, no build step.

git clone https://github.com/monac-cc/LLMSwapper.git
cd LLMSwapper
node server.js          # opens http://127.0.0.1:7373
WindowsAlso detects your WSL distros and swaps inside them, from the same screen.
macOSCredentials usually live in the login Keychain - item Claude Code-credentials, account claude-code-user - and the app reads and writes that item through security; a plain ~/.claude/.credentials.json is used when that item does not exist. With CLAUDE_CONFIG_DIR set it addresses that directory's own item, as Claude Code does.
LinuxNothing special. This is the host the Docker image is really for.
docker compose up -d          # http://127.0.0.1:7373
git pull && docker compose up -d   # update: compose rebuilds the image on every up

By hand, on a port of your choice:

docker build -t llmswapper .
docker run -d --name llmswapper -p 127.0.0.1:7373:7373 \
  -v "$PWD/data:/app/data" \
  -v "$HOME:/home/node" \
  llmswapper
  • Publish on 127.0.0.1 only. Inside the container the server must listen on 0.0.0.0, so the loopback guarantee has to be imposed here.
  • Mount the data/ you already use. A named volume gives the container a separate, empty account store. On a Linux host create it first (mkdir -p data) if the panel has never run there: Docker creates a missing bind-mount source as root, and the server, which runs as uid 1000, cannot write to it.
  • Never run the container and node server.js together. The rate floor is per process; two instances rate-limit each other.
  • Windows: set $env:CLAUDE_HOME = $env:USERPROFILE first. macOS: swapping needs credentials in a file, not the Keychain. Linux: add --user "$(id -u):$(id -g)" if the mounts belong to another uid.
  • Update with git pull && docker compose up -d. The compose file rebuilds on every up; with docker run you have to docker build again yourself. The container banner shows the build stamp, so a stale container is visible at a glance.
  • Mount your home directory, not ~/.claude and ~/.claude.json separately. Claude Code rewrites ~/.claude.json by renaming a new file over it, and a single-file bind mount on a Linux host keeps the old inode: the container reads a frozen copy and its writes never reach the host. With the home directory mounted, the rename happens inside the mount and every write stays atomic. The container sees your whole home; it already held your Claude credentials, which is the valuable part.
  • Docker Desktop (Windows, macOS) resolves the share by path, so there the narrower pair -v "$HOME/.claude:/home/node/.claude" -v "$HOME/.claude.json:/home/node/.claude.json" also works. On a file mount the server writes in place (a mount point cannot be replaced by rename), and if the mount ever detaches it refuses to swap and says so in a banner, rather than pretending.
  • A container cannot see host processes or reach WSL. The panel says so on screen rather than reporting "not running" for what it cannot see.

Another port: PORT=7400 node server.js. It never hops to the next free one - the rate floor is per process, so a second instance would rate-limit both.

powershell -ExecutionPolicy Bypass -File scripts\install-autostart.ps1

Registers a per-user scheduled task (no admin) that launches the panel hidden at logon, restarts it 15 seconds after any crash and writes the time and exit code of each one to the log (a clean exit, such as the port being busy, is left alone), and never kills it on a time limit. To stop it, end the task and the server it leaves running (Task Scheduler does not stop that one): Stop-ScheduledTask LLMSwapper; Stop-Process -Id (Get-NetTCPConnection -LocalPort 7373 -State Listen).OwningProcess. It also starts it right away - unless something already listens on 7373, in which case stop that first and run Start-ScheduledTask LLMSwapper. Output goes to data\server.log. Remove with -Uninstall. Don't combine it with running node server.js by hand - same rule as Docker, one instance only.


Status: Claude Code and Codex CLI. A Claude | Codex switch at the top of the page picks which one you are looking at; each keeps its own accounts, environments and tab. The problem is not specific to either - any coding tool backed by a personal subscription holds one account at a time - and Other providers is a list of criteria, not of dates.

What it does

Claude Code holds one account at a time; using another means /login and a browser round trip, every time. LLMSwapper stores each account once and makes the change a click - on your machine and inside your WSL distros - and shows what each has left of its 5-hour and weekly windows, so it answers the real question: which account should I switch to right now.

The interface is bilingual (ES / EN) with light and dark themes, both remembered. Server-side error text is still Spanish only - a known gap, since translating it needs the API to return codes rather than sentences.

Adding accounts

Two ways in. The choice decides one thing: whether the account can identify itself.

Paste a long-lived token - recommended. Run claude setup-token, approve in the browser, paste what it prints. Lasts a year, no renewal. The form has an open a terminal for me link that runs the command for you.

  • Grants one scope, user:inference, so it has no email, plan or organisation - it shows the name you give it.
  • Quota meters still work, from a different source. See below.

Import a live session - claude then /login, then press import. Full scope, so it identifies itself and reads quota for free.

  • Costs one login per account, and each login lasts about 30 days, fixed when you log in: renewing rotates the tokens but never extends it. The row warns three days ahead; then log in again (Shift + click import keeps your current session) or turn it into a one-year token.
  • Shift + click imports from a CLAUDE_CONFIG_DIR=/tmp/other claude session without disturbing yours.

Adding the same account twice updates it in place.

Usage meters

The panel asks every minute and on demand; how fresh each reading really is depends on its source. Where the numbers come from, and what they cost:

AccountSourceCost per reading
Imported/api/oauth/usage0 tokens
setup-tokenanthropic-ratelimit-unified-* headers8 in + 1 out
  • An inference-only token is answered 403 by the usage endpoint forever, so the panel never asks.
  • The free endpoints carry no rate-limit headers, so the probe spends the floor: Haiku, max_tokens: 1, a one-character prompt. About 2,600 tokens a day per account, against a window measured in hundreds of thousands.
  • When it cannot refresh, it shows the last reading marked stale. Switching is unaffected.

Switching

Press swap:

  1. Warns if Claude Code is open - the change lands on new sessions.
  2. Backs up credentials and ~/.claude.json to data/backups/ (last 20).
  3. Refreshes the token if it is about to expire.
  4. Rewrites only claudeAiOauth and oauthAccount. Projects, history and mcpOAuth are untouched.
  5. Verifies against the API and rolls both files back if the token is rejected (401/403). A rate limit or a network failure keeps the swap and says it could not be confirmed.

Environments

The tabs are the places a swap can write: the host, named after its OS, and every WSL distro. Nothing to configure - it runs wsl.exe -l -q --running and takes what comes back.

  • Lists only distros that are running - a scan must not boot the stopped ones - and skips system distros (docker-desktop) and any distro where Claude Code has never run, checked by resolving $HOME and looking for ~/.claude.json over the share.
  • Detection is cached 30 s. The ↻ beside the tabs re-scans past it, and says when nothing changed.
  • A dot means Claude Code is running there. Each environment tracks its own active account.
  • WSL is a Windows feature; elsewhere there is one tab.

Codex

Press Codex in the switch at the top. The same page - rows, meters, tabs, swap - now works on OpenAI Codex CLI accounts (ChatGPT sign-in), kept in a store of their own: nothing on the Claude side ever sees an OpenAI token, or the reverse. The switch and each view's tab are remembered.

Adding an account. Three ways in, none of which touches the session you are using:

  • add account - opens a terminal running codex login with CODEX_HOME pointed at a panel-owned directory (data/codex/login/). Finish the login in the browser, then press import in the step that appears under the header. The panel stores it and deletes that directory's auth.json. If Windows has reserved the port the browser login answers on (1455 - os error 10013), the terminal runs codex login --device-auth instead: open the link it prints and type the code.
  • import - stores the account Codex is signed in with in the selected environment right now.
  • Shift + click import - reads a login from another directory, one you signed in to with CODEX_HOME=/tmp/other codex login. That directory and the panel then share one refresh token, and the panel says so: use the account through the panel from then on, not from there.

There is no Codex setup-token, so there is nothing to paste. API-key auth.json files are refused: only ChatGPT accounts have a quota to show. Adding the same account twice updates it in place, and the name you gave it stays.

Switching is the same audited swap as Claude's: backup to data/codex/backups/ (last 20), refresh if the token is about to expire, rewrite only tokens, auth_mode and last_refresh in auth.json (every other key survives), verify against the usage endpoint, and restore the file if OpenAI rejects the token (401/403) - or delete it again if there was none before. A Cloudflare bot challenge (an HTML 403) only warns: it says nothing about the token.

WSL. A distro gets a Codex tab once it is running and has a ~/.codex directory, i.e. once Codex is installed and has run there. Swapping writes that distro's auth.json, so an account you signed in to on Windows works inside WSL with no second login.

Meters read chatgpt.com/backend-api/wham/usage, the endpoint Codex's own /status reads: the 5-hour and weekly windows, 0 tokens per reading, at most a minute old. When OpenAI reports only the weekly window, the 5-hour meter says there is no 5-hour limit rather than showing a blank. A 429 backs off for 10 minutes and keeps the last reading, marked stale. An account whose sign-in OpenAI has revoked shows "sign in again": add account, log in with it, import.

Codex's own limits, and the panel's:

  • Never log out of Codex to switch accounts. Codex's logout revokes that account's tokens at OpenAI, so the copy the panel holds dies with it - the row then says "revoked" and the account has to be added again. Add accounts with add account and switch with swap; neither logs anything out.
  • A swap is a codex login, and open Paseo sessions follow it. It writes the session to auth.json, so every Codex you start afterwards runs on the new account. If Paseo is installed, its open Codex agents are reloaded with paseo agent reload, which resumes the same conversation on the new account: idle ones at once (the one that ran out of quota included). One still working is never stopped: it goes on with its account and switches the moment it stops by itself, so your next message runs on the new one.
  • Terminal codex sessions switch at once, mid-answer included. They run against Codex's shared background server (/status says "Local background server"), which never re-reads auth.json. The panel logs that server in with the new account itself, through codex app-server proxy, the way Codex's host apps do (account/login/start with the access token, kept in the server's memory): the request in flight finishes on the old account and the next one goes out on the new. Nothing is restarted. Codex does not renew a login handed over like that, so the panel feeds the server every newer token that lands in auth.json, and renews it itself a day before it expires. A server that does not answer is tried again in 30 seconds. A WSL distro linked to the Windows auth.json (below) gets the same for its own background server, while the distro is running.
  • What runs outside that server keeps the old account. The desktop app, codex exec or a Codex in an unlinked WSL distro read auth.json when they started; the swap reaches the next one you start. The panel says so after the swap.
  • Want one account in Windows and WSL at once? Link the distro's file to the Windows one, once, inside the distro: ln -sf /mnt/c/Users/<you>/.codex/auth.json ~/.codex/auth.json. Then there is a single file, Codex coordinates its refreshes through it (verified: it writes through the link), the WSL tab disappears from the Codex view and every swap on Windows applies to both.
  • One environment per account at a time. Codex refresh tokens rotate, and the old one dies on use: two auth.json files holding the same one kill each other on the first refresh. Swapping an account into a second environment is refused until the first one is switched to another account.
  • Keyring storage is refused. With cli_auth_credentials_store = "keyring" (or auto) in config.toml, the session is not in auth.json, so a swap would do nothing. The panel refuses and says how to go back to "file".
  • No automatic rotation and no /swapper* skills for Codex yet. Those drive Claude only.
  • CODEX_ACCESS_TOKEN outranks auth.json. While it is set, a swap is a silent no-op; the panel shows a banner. CODEX_API_KEY only reaches codex exec and is not treated as an override.

Slash commands (Claude Code skills)

Three skills drive the panel from inside any Claude Code session, so you can check quota or switch account without leaving the terminal. They talk to the running panel over 127.0.0.1:7373; nothing works unless node server.js is up (PORT if you moved it).

CommandWhat you typeWhat it does
/swapper-usage/swapper-usageOne line per account with its 5-hour and weekly quota; marks the one in use (and when its session resets) and the freest one to switch to. Read-only.
/swapper <name>/swapper Manuel · /swapper devs@…Switches the host's active account to the one named (label or email), then shows what it has left.
/swapper-auto/swapper-auto on · off · statusTurns automatic rotation on or off, and shows the whole rotation queue: the account in use, which is next, and any excluded one with the reason.

/swapper-usage prints one aligned line per account — ● in use, → the freest to switch to:

  Swapper · host                   5h     7d

  ● Cyberxia                      31%    53%   en uso · reset 4h 12m
  → Castillo · carloscastilloazr   0%    48%   más libre
    Manuel Cordero                 0%     0%
    Castillo · ccastillo          81%    71%   casi
    CTO                          100%    68%   tope

Two accounts sharing a label are told apart by the email's local part. /swapper-auto uses the same shape: ● the account in use, → siguiente the one it would rotate to, and excluida · why on any it will skip.

/swapper Manuel matches by name or email — if it is ambiguous (two accounts labelled the same) it lists them and asks for the email rather than guessing. The change lands on new Claude Code sessions; one already open keeps its token.

Automatic rotation (/swapper-auto on). The server then watches the active account and, the moment its 5-hour session reaches 90%, swaps to the freshest account that still has room — so your next claude launch lands on capacity you have not spent. It only rotates into an account below the threshold in both windows; if none qualifies it stays put. State is on disk, so it survives a restart, and it is the same audited swap (backup, verify, roll back) as a manual one. Turn it off with /swapper-auto off. An account whose token is rejected is left out of the queue, and a rotation that fails waits out the same cooldown as one that succeeds, so a dead credential is never retried every three minutes.

Install

Copy the three skill folders into your user skills folder, once:

# macOS / Linux / Git Bash
cp -r skills/swapper skills/swapper-usage skills/swapper-auto ~/.claude/skills/
# Windows PowerShell
Copy-Item skills\swapper,skills\swapper-usage,skills\swapper-auto $env:USERPROFILE\.claude\skills\ -Recurse

Each skill is self-contained — a SKILL.md and a small swapper.mjs — and needs only Node and the running panel. They work from any Claude Code session, not just one opened in this repo.

Paseo plugin

paseo-plugin/ shows the LLMswapper of every paired Paseo device in one compact panel, one row per account with its 5-hour and 7-day bars, and switches the account on any of them. Install it with paseo plugin add git:monac-cc/LLMSwapper:paseo-plugin.

It also makes a switch reach the current session. A running Claude Code keeps its token in memory, so after a switch the plugin has Paseo reload that machine's Claude agents, which resume the same conversation on the new account: idle ones at once, busy ones when their turn ends. Switches made here, by /swapper or by automatic rotation count too.

Other providers

Claude Code and Codex CLI ship today. The rest are candidates, in rough order of fit.

ProviderToolStatus
AnthropicClaude CodeShipping - host and WSL, token or import
OpenAICodex CLIShipping - host and WSL, ChatGPT sign-in, see Codex
GitHubCopilotConsidering - ~/.config/github-copilot/
GoogleGemini CLIConsidering - ~/.gemini/
Cursor / Windsurfeditor sign-inInvestigating - held by the editor, not a file this tool can rewrite

A provider fits when its credentials live in a file this app can rewrite, the account is identifiable enough to label, and swapping does not require killing a running session. One that keeps its session in an OS keychain, or pinned to a process, needs a different mechanism. If a tool you use fits, an issue naming it and where it stores credentials is the most useful thing to send.

Security

  • Loopback only by default, plus a required header on every API call. DNS rebinding is rejected by validating the Host hostname.
  • Reaching it from another device is opt-in and unauthenticated. SWAPPER_BIND=0.0.0.0 widens the socket and SWAPPER_ALLOWED_HOSTS=192.168.1.10,my-pc widens the Host allowlist - both, or it stays closed. Only the hosts you name are let in; every other one, rebinding included, is still refused. There is no login, so anyone who can route to that address can swap, rename and delete your accounts. A tunnel or a private mesh is the better answer; this is for a network you trust.
  • Tokens live in data/ (real NTFS ACL on Windows) and never leave the process. Anything matching sk-ant-*, an OpenAI JWT or an OpenAI refresh token is scrubbed from logs and responses; a test fails the build if a token literal appears in any source file.
  • No OAuth flow in the panel. You mint tokens in your own terminal and paste them, or sign in with the CLI's own login and import the result.
  • A pasted token is a year-long secret. The field is a password input, emptied when the form closes.
  • Every swap is preceded by a backup and rolls back on failure.

Limitations

  • ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_OAUTH_TOKEN outrank the credentials file. While one is set, swapping is a silent no-op. The panel detects this and says so.
  • Token accounts have no identity - no email, plan or organisation. Nothing to read, so nothing is invented. Name one with its real email and /status reads as before.
  • Remote Control does not work with setup-token accounts. Anthropic documents it. The Remote Control disconnected - /login line is not a broken session; silence it with "disableRemoteControl": true.
  • The usage endpoint allows ~5 requests per 5 minutes for the whole app, hence the hard floor and the persisted, escalating cooldown.

Troubleshooting

SymptomCause
Swapping "works" but Claude Code keeps the old accountAn overriding environment variable.
Remote Control disconnected - /loginExpected with setup-token accounts.
Not logged in - Please run /loginThe credentials blob has no scopes.
Meters empty and marked staleRate-limited. Recovers on its own.
Ya hay algo escuchando en… at start-upAnother instance. The port never hops.

Development

node test.js        # 103 checks, ~13 s, no external network

Every fetch is stubbed, so a run never spends the usage endpoint's budget. Each module has its own self-check (node lib/swap.js). Internals are in ARCHITECTURE.md.

License

MIT

↑↓ navigate openesc close