Skip to main content

Harness Swap (hswap)

Switch between multiple accounts across AI coding harnesses, with automatic rate-limit rotation, a unified usage dashboard, and parallel sessions.

hswap grew out of claude-swap. Its Claude Code support is the claude-swap engine (unchanged apart from a small embedding hook); Codex CLI, Grok Build, OpenCode and Antigravity are added through the same adapter contract any other harness can implement.

Claude Code Codex CLI Grok Build OpenCode Antigravity (agy)
list / add / remove accounts yes yes yes yes (native) yes⁵
switch the default login yes yes¹ yes yes (native auth switch) yes⁵
running sessions pick up a switch yes² no, restart yes undocumented no, restart
usage 5h / 7d / per-model experimental³ experimental³ experimental³ (Go plan) experimental³
automatic rotation hswap claude auto experimental⁴ experimental⁴ experimental⁴ experimental⁴
parallel isolated sessions experimental yes experimental no experimental

¹ file credential store only (cli_auth_credentials_store = "file", Codex's default). ² on macOS once the Keychain cache expires. ³ opt-in: hswap config set usage.remote <ids>. hswap asks the harness itself (Codex app-server, Grok's ACP agent, OpenCode api experimental.usage.limits, agy -p /usage) and never calls a provider API or refreshes a token. Without the opt-in Codex still shows the last snapshot it logged locally. Each harness's answer for a real, active paid login was read once (2026-11-03, read-only) and mapped correctly; inactive-account reads, keep-alive and automatic switching are verified against fake harnesses only. OpenCode reports Go-plan limits only for OpenCode Console logins, not API keys. Turning the opt-in on or off takes effect on the next read. list --cached / usage --cached read the cache: no usage read, no token refresh (hswap still runs each harness's version probe, and OpenCode's auth list, to list accounts). ⁴ opt-in per harness: hswap config set auto.enabled <ids>, then hswap <harness> auto. Needs live usage, and every provider's terms are unclear or restrictive about switching accounts to get past limits (Google's are the strictest); hswap capabilities and config set print the warning. Claude keeps hswap claude auto, with its own threshold, JSON lines and exit codes (docs/claude.md). ⁵ file-backed login only (Linux; not the macOS/Windows keychain); add --login is unavailable. Cursor and Gemini CLI are not supported. See docs/harness-compatibility.md for the evidence and the reasons behind every cell. hswap capabilities prints the live answer for your install.

Install

hswap is not on PyPI yet. From a clone:

uv tool install .          # or: pipx install .
hswap harnesses            # what is installed, which versions

Python 3.12+. hswap does not install a cswap command, so it never collides with an installed upstream claude-swap; hswap claude ... takes every cswap command (and python -m claude_swap still runs the engine directly).

Quick start

hswap                       # dashboard (when run in a terminal)
hswap list                  # every harness, every account, usage where available
hswap status                # the active account per harness

Claude Code

Exactly the claude-swap workflow, spelled hswap claude:

hswap claude add            # add the account Claude Code is logged into
hswap claude switch 2       # or an email / alias
hswap claude auto           # rotate before rate limits (threshold, priority, cooldown)
hswap claude run 2          # this terminal only, other sessions unaffected
hswap claude help           # everything else: tui, map, alias, threshold, export, ...

Full guide: docs/claude.md. Existing claude-swap data is used in place — nothing to import.

Codex CLI

hswap codex add                       # store the current login
hswap codex add --login --alias work  # sign in another account in a throwaway CODEX_HOME;
                                      # the current login is never replaced or logged out
hswap codex switch work               # swaps $CODEX_HOME/auth.json; restart running Codex
hswap codex usage                     # last logged snapshot; live after `config set usage.remote codex`
hswap codex switch --strategy best    # one-off: the account with the most headroom
hswap codex run personal -- resume    # isolated per-account CODEX_HOME, this terminal only

run on the account that is already the default login runs in the default CODEX_HOME (a second copy of the token would mean two refreshers); hswap still holds a session lock there, so switch and remove wait until it exits.

Grok Build

hswap grok add --login       # isolated GROK_HOME sign-in
hswap grok switch 1          # leaves that login as the only scope; Grok hot-reloads it
hswap grok run 2             # experimental isolated GROK_HOME session

Antigravity (agy)

File-backed login only (~/.gemini/antigravity-cli/antigravity-oauth-token).

hswap agy add                # the current login; sign in the next account in agy, add again
hswap agy switch 2           # swaps the token file; restart running agy sessions
hswap agy run 2 -- --continue    # experimental: per-account --gemini_dir

Usage and automatic switching (opt-in)

hswap config set usage.remote codex,grok,opencode,agy   # ask each harness for live usage
hswap list --cached                                      # no network, no token refresh
hswap config set auto.enabled codex                      # allow automatic switching
hswap codex auto [--once | --interval S] [--dry-run] [--json]

Keep logins alive

hswap codex refresh [ACCOUNT] [--json]    # also: grok, agy
# crontab: keep stored logins fresh on a machine you rarely use
0 */6 * * * hswap codex refresh

refresh asks the harness (in each account's own home) so the harness refreshes a due token itself; hswap never calls a provider. It needs no opt-in, skips an account a running session owns and the active login while that harness runs (any process whose command line names the harness executable counts, e.g. a sandbox that binds it), and prints token refreshed / token still valid (agy: login used), or sign in again: ... with exit 1. OpenCode is unsupported: switch to an idle account (hswap opencode switch) before its token lapses. Claude: the engine refreshes tokens when it reads usage, so run hswap claude list or hswap claude auto --once from cron. Verified against fake harnesses only, never a live account. Why it is safe with several devices: docs/security.md.

auto switches only on fresh usage at or over rotation.threshold, at most once per auto.cooldown (default 600 s), and never on missing data. Read the terms warning in hswap capabilities first.

OpenCode

OpenCode 2.x keeps several saved accounts per provider itself; hswap drives its opencode auth commands and never edits its database.

hswap opencode list
hswap opencode add --provider openai     # runs `opencode auth login openai`
hswap opencode switch 3                  # runs `opencode auth switch <provider> <id>`
hswap opencode switch work --provider anthropic

OpenCode 1.x (legacy auth.json) is handled by swapping one provider's entry at a time (experimental, mock-verified).

Commands

hswap harnesses | list [H] | status [H] | capabilities [H] | doctor | migrate [--dry-run]
hswap config [list|get|set|unset|path]  plugins.trusted, rotation.threshold, usage.remote, auto.enabled, auto.cooldown
hswap tui

hswap <harness> list [--json] [--no-usage] [--cached]
hswap <harness> status | usage [ACCOUNT] [--json] [--cached]
hswap <harness> auto [--once|--interval S] [--dry-run] [--json]   # opt-in
hswap <harness> add [--alias A] [--provider P] [--login]
hswap <harness> switch ACCOUNT [--provider P] [--force] [--json]
hswap <harness> switch --strategy best|next-available
hswap <harness> run ACCOUNT [-- harness args...]
hswap <harness> alias ACCOUNT NAME | --unset
hswap <harness> remove ACCOUNT [--yes]

These generic verbs apply to every harness except Claude Code, whose hswap claude ... takes the full claude-swap command set instead (hswap claude help); the cross-harness commands (hswap list claude, hswap capabilities claude, ...) work for Claude too.

ACCOUNT is a slot number, an alias, or an email/label. Unsupported operations fail with the reason (hswap capabilities lists them all). Every --json payload carries schemaVersion: 1; Claude's own --json keeps claude-swap's schema. Cross-harness commands (list, status, capabilities, doctor, harnesses) still show every harness that works when one fails, report the failure (errors in JSON), and exit 1.

  • remove forgets the stored login. The account's isolated-session history stays in <hswap home>/<harness>/accounts/<slot>/; delete that directory to discard it.
  • switch --force saves the login it replaces to <hswap home>/<harness>/stash/<file>.<timestamp> (0600) and prints the path. To get it back, copy it over the harness's login file (e.g. $CODEX_HOME/auth.json) and hswap <harness> add it.
  • rotation.threshold (default 95%) is the cut-off for switch --strategy on the generic harnesses. Claude's auto-switch has its own settings (hswap claude config, default autoswitch.threshold 90%, per-account hswap claude threshold).

How it stays safe

  • Credentials never appear in output: core models carry no secrets, and hswap refuses to print JSON that looks like it contains a token. The one exception is the explicit, plaintext hswap claude export -.
  • Before overwriting a login hswap copies the harness's freshly rotated tokens back into the stored account, refuses to overwrite a login it does not manage (--force stashes it first, 0600), leaves other providers' entries alone (Grok: the target becomes the only login scope, the others are saved first), writes atomically at 0600 under hswap's lock and the harness's own lock, re-reads the result and restores the original on any failure.
  • For Codex and Grok, an account running in a run session cannot be activated globally (or removed) until that session exits — two processes would rotate one refresh token — and a run on the default login blocks switching away from it. Claude's run keeps the claude-swap rules (see docs/claude.md).
  • Plugins (third-party adapters) load only after you trust that id from that package: hswap config set plugins.trusted <id>@<distribution> (the command hswap harnesses prints). See docs/adapter-development.md.

Details: docs/security.md.

Documentation

License

MIT

Metadata

Release files for harness-swap 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for harness-swap 0.2.0
File Size Uploaded Provenance Actions
harness_swap-0.2.0.tar.gz 1.0 MB With provenance Details for harness_swap-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for harness-swap 0.2.0
File Interpreter ABI Platform Actions
harness_swap-0.2.0-py3-none-any.whl Python 3 none any Details for harness_swap-0.2.0-py3-none-any.whl

Total release size: 1.5 MB

Release files / harness_swap-0.2.0.tar.gz

Download URL harness_swap-0.2.0.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
c54c2483bb9be2db21eb837d7bf5ec9dfe0cd2b1bd662752228da63fc01ca91e
BLAKE2b-256 checksum
How to use checksums
38843c38f8aa59a35d7fbe73238d43b2319ad54350f73a6db3969c91fa0a4ece
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Release files / harness_swap-0.2.0-py3-none-any.whl

Download URL harness_swap-0.2.0-py3-none-any.whl
Size 476.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9ce9b8c992794e69d5074624b4572a468f7e961a00a4987bcec28326aabe9f5e
BLAKE2b-256 checksum
How to use checksums
ff24a8a2343a470fc9b6222ce69a5e4cb06693d33702b150ee98f2945daf4349
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

Security for harness-swap 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for harness_swap-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page