{"_id":"@adamtash/ai-as","_rev":"5-0451950a9d3352bab1c8c489ea825d6e","name":"@adamtash/ai-as","dist-tags":{"latest":"0.1.4"},"versions":{"0.1.0":{"name":"@adamtash/ai-as","version":"0.1.0","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"license":"MIT","_id":"@adamtash/ai-as@0.1.0","maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"bin":{"codex-as":"bin/codex-as.js","claude-as":"bin/claude-as.js"},"dist":{"shasum":"f807606e4b353b7e3bb7a0ec8792c23c43e354fd","tarball":"https://registry.npmjs.org/@adamtash/ai-as/-/ai-as-0.1.0.tgz","fileCount":20,"integrity":"sha512-vHTn34HWnG32G7qVs+XJkEiag8+wg6HPiVUP1AIk2DXW67bdGXef2p6jqMCHs8z90waWkwlsMIUxbG4GLKeLrA==","signatures":[{"sig":"MEQCIGM24xxF4I68XlUQijFlNIfI+h5arQ+7ekkg92haWHLFAiBS0Duol4a8zJARgP01s/4Jo9v+kamlxOAzV79QeA+JzQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":119968},"type":"module","engines":{"node":">=20"},"gitHead":"78eafa0252d0c77836c1c5326c18701c2eee1fd1","scripts":{"test":"node ./test/claude.mjs && node ./test/codex.mjs"},"_npmUser":{"name":"adamtash","email":"adem@adamtash.com"},"_npmVersion":"11.6.2","description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","directories":{},"_nodeVersion":"25.2.1","dependencies":{"htm":"^3.1.1","ink":"^6.8.0","react":"^19.2.4"},"preferGlobal":true,"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-as_0.1.0_1773680196067_0.5099006918819002","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@adamtash/ai-as","version":"0.1.1","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"license":"MIT","_id":"@adamtash/ai-as@0.1.1","maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"bin":{"codex-as":"bin/codex-as.js","claude-as":"bin/claude-as.js"},"dist":{"shasum":"f4c67da9c37042692341ac33625f9ec60adf7c06","tarball":"https://registry.npmjs.org/@adamtash/ai-as/-/ai-as-0.1.1.tgz","fileCount":20,"integrity":"sha512-c4TP9wPE0kC5wJCMDmLk0CQhkaxxKVsWVDQQ+Z6ONcLBXyvV7RLpzQ0fOyJrcWQlBcR9OP0SqVFg/LtJS/MetA==","signatures":[{"sig":"MEQCIAJv7H34oAiz4CdutnpDrXMOKWxdUOhJ2VUzA8AqX3n3AiBoP6qojRKzQJyanqAslCwHFKRimKon2vGsgC7nAMgtsw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121230},"type":"module","engines":{"node":">=20"},"gitHead":"da9fde3382489ab88bb25a5c80dedb2c31ff47d8","scripts":{"test":"node ./test/claude.mjs && node ./test/codex.mjs"},"_npmUser":{"name":"adamtash","email":"adem@adamtash.com"},"_npmVersion":"11.6.2","description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","directories":{},"_nodeVersion":"25.2.1","dependencies":{"htm":"^3.1.1","ink":"^6.8.0","react":"^19.2.4"},"preferGlobal":true,"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-as_0.1.1_1773680487683_0.7289359974434098","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@adamtash/ai-as","version":"0.1.2","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"license":"MIT","_id":"@adamtash/ai-as@0.1.2","maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"bin":{"codex-as":"bin/codex-as.js","claude-as":"bin/claude-as.js"},"dist":{"shasum":"52a7ca48e35f1070cf4e212b2bc85b9529df34f0","tarball":"https://registry.npmjs.org/@adamtash/ai-as/-/ai-as-0.1.2.tgz","fileCount":20,"integrity":"sha512-9VJsSMtOplIdft7XzpYdmIM6E0qKLbAv1Y4R/01EiIsLZCh9IvfzLdv+6TX3ae/NpQ13Jiikn3Ru4AkgnFFpJg==","signatures":[{"sig":"MEUCIQCe8McaXFZmnuRIC5p1ROyg/bv+zz/rttUL1dqf8yBw+QIgMdsEUW2ho3/5Z3UWEqxnWlw8v77XTYqwW/zdJLxKuc0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121263},"type":"module","engines":{"node":">=20"},"gitHead":"3828cdbe5b6ff2a6791e51948818d827fb4ec5d9","scripts":{"test":"node ./test/claude.mjs && node ./test/codex.mjs"},"_npmUser":{"name":"adamtash","email":"adem@adamtash.com"},"_npmVersion":"11.6.2","description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","directories":{},"_nodeVersion":"25.2.1","dependencies":{"htm":"^3.1.1","ink":"^6.8.0","react":"^19.2.4"},"preferGlobal":true,"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-as_0.1.2_1773680951670_0.6095875612921835","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@adamtash/ai-as","version":"0.1.3","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"license":"MIT","_id":"@adamtash/ai-as@0.1.3","maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"bin":{"codex-as":"bin/codex-as.js","claude-as":"bin/claude-as.js"},"dist":{"shasum":"5c416c7a53b73a16cb4c993c06a1e7b9dc1db321","tarball":"https://registry.npmjs.org/@adamtash/ai-as/-/ai-as-0.1.3.tgz","fileCount":20,"integrity":"sha512-Ft8c3GyrYV3vpOo1UMvdmZe7JjJL4F9YO899Y8L+n9/t+bhv0b5ZIuy/lw3iJJCQpH2o3Ld2ttHXSwgl8SYLwg==","signatures":[{"sig":"MEQCIELswiKUnVD6RinUm+juB5gfGTHdLeVMin4rvVlJviEHAiAk1N/jtqqWYDxJ/pBqNRQrf4RW+EBMtloPMamxhUBnPA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":142108},"type":"module","engines":{"node":">=20"},"gitHead":"1a653a1ec50b273ba799547fdab84e5205032f2c","scripts":{"test":"node ./test/claude.mjs && node ./test/codex.mjs"},"_npmUser":{"name":"adamtash","email":"adem@adamtash.com"},"_npmVersion":"11.6.2","description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","directories":{},"_nodeVersion":"25.2.1","dependencies":{"htm":"^3.1.1","ink":"^6.8.0","react":"^19.2.4"},"preferGlobal":true,"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/ai-as_0.1.3_1773683543310_0.6689733788108401","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@adamtash/ai-as","version":"0.1.4","description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","license":"MIT","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"type":"module","engines":{"node":">=20"},"publishConfig":{"access":"public"},"preferGlobal":true,"bin":{"claude-as":"bin/claude-as.js","codex-as":"bin/codex-as.js"},"scripts":{"test":"node ./test/claude.mjs && node ./test/codex.mjs"},"dependencies":{"htm":"^3.1.1","ink":"^6.8.0","react":"^19.2.4"},"gitHead":"b50f6492f6b0f6b6d1861d0b23f53e1d2ea165f6","_id":"@adamtash/ai-as@0.1.4","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-iNvc2dTQdjyiId1T/EaElbApKLBlKUT9wofrCg8isimaoiG+Wo1g6/NlpnYF3gxJs3ubsXNBmraNcpfjnHUrxQ==","shasum":"a187e1b1c464c2b83501f39484a8daacec88ef84","tarball":"https://registry.npmjs.org/@adamtash/ai-as/-/ai-as-0.1.4.tgz","fileCount":21,"unpackedSize":148392,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDZQtp4PYaJeUKyxFWovaD6S6+PlfvLfuREwe2FPadjkgIgHtzxYFgbyTjCA/D6lnEGwEzFgyddzP6BEaq+35cR7lA="}]},"_npmUser":{"name":"adamtash","email":"adem@adamtash.com"},"directories":{},"maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-as_0.1.4_1773684536955_0.9807481687086359"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-16T16:56:35.980Z","modified":"2026-03-16T18:08:57.253Z","0.1.0":"2026-03-16T16:56:36.222Z","0.1.1":"2026-03-16T17:01:27.850Z","0.1.2":"2026-03-16T17:09:11.806Z","0.1.3":"2026-03-16T17:52:23.526Z","0.1.4":"2026-03-16T18:08:57.144Z"},"license":"MIT","keywords":["claude","codex","openai","anthropic","cli","tui","account-switcher"],"description":"Switch Claude Code and Codex accounts mid-session — no restarts, no interruptions.","maintainers":[{"name":"adamtash","email":"adem@adamtash.com"}],"readme":"# ai-as\n\nSwitch Claude Code and Codex accounts mid-session — no restarts, no interruptions.\n\n| CLI | Tool | Accounts stored at |\n|-----|------|-------------------|\n| `claude-as` | Claude Code (Anthropic) | `~/.claude-as/accounts.json` |\n| `codex-as` | Codex CLI (OpenAI) | `~/.codex-as/accounts.json` |\n\n---\n\n## Install\n\nRequirements: Node.js 20+, and the respective CLI installed (`claude` / `codex`).\n\n```bash\nnpm install -g @adamtash/ai-as\n```\n\nOr run without installing:\n\n```bash\nnpx @adamtash/ai-as@latest claude-as\nnpx @adamtash/ai-as@latest codex-as\n```\n\nFrom a local clone:\n\n```bash\ngit clone https://github.com/adamtash/ai-account-switch\ncd ai-account-switch\nnpm install\nnpm link\n```\n\n---\n\n## claude-as\n\nTerminal UI and CLI for switching Claude Code accounts.\n\n![claude-as TUI overview](./assets/claude-tui-overview.svg)\n![claude-as usage meters](./assets/claude-tui-usage.svg)\n\n### What it does\n\n- Patches the Claude Code binary so OAuth tokens are read live from disk instead of being memoized for the lifetime of the process.\n- Manages a local file of named Claude OAuth accounts so you can switch the active account in an already-running patched Claude session.\n\nClaude Code currently caches the result of its internal OAuth getter. If you log out and log in from another terminal, older running sessions keep using the cached token. The patch changes that getter from a memoized function to a plain function, so new requests in the already-running session re-read the latest persisted token.\n\nThe patch is intentionally narrow — it rewrites a single internal assignment to remove memoization while keeping the binary size unchanged. On macOS the patched binary is automatically ad-hoc re-signed.\n\n### Usage\n\n```bash\nclaude-as               # full-screen TUI\nclaude-as add           # add the current Claude account\nclaude-as list          # show saved accounts with live usage\nclaude-as pick          # switch to the best account for the current 5h window\nclaude-as use --name you@example.com\nclaude-as import-current --name work\nclaude-as status        # show active credential target\nclaude-as patch         # patch the Claude binary\nclaude-as restore       # restore from backup\n```\n\nKeys in the TUI: `↑/↓` move · `enter` use · `n` best · `r` refresh · `x` restore · `a` add · `d` remove · `q` quit\n\n**`pick`** skips accounts whose 5-hour window is full, then prefers the account whose 5h limit resets soonest. Ties are broken by highest current 5h utilization (burn expiring capacity first).\n\n`use` and `pick` both patch the Claude binary first. If this is the first patch, restart any already-running Claude process once.\n\n#### Safety\n\n- A backup is created as `<binary>.claude-as.bak`.\n- The patch is a no-op if already applied.\n- If the expected internal pattern is missing, the tool exits without writing.\n- On macOS, patched binaries are ad-hoc re-signed automatically.\n- Saved accounts are stored as plaintext JSON in `~/.claude-as/accounts.json`.\n- On macOS, the active Claude token is written to Keychain with plaintext fallback.\n\n#### Note on Claude updates\n\nThe patch works by locating a specific minified string inside the Claude binary and replacing it in place. This string is assigned by the bundler and **can change with any Claude Code update** — even ones that don't touch the OAuth logic.\n\nWhen a Claude update ships:\n- If the pattern is still present, the patch continues to work with no action needed.\n- If the pattern has changed, `claude-as patch` will print an error and exit without modifying the binary. Re-patching will do nothing until `patch.js` is updated with the new needle.\n\n**Symptom:** after updating Claude, `claude-as list` shows accounts fine but switching no longer takes effect in running sessions — that means the binary is now unpatched. Run `claude-as patch` to confirm; if it errors, the needle needs updating.\n\n#### Verify\n\n1. Start Claude Code in terminal A.\n2. In terminal B, run `claude auth logout` and `claude auth login`.\n3. Return to terminal A and make a new request — it should use the new account without restarting.\n\n---\n\n## codex-as\n\nTerminal UI and CLI for switching Codex accounts.\n\n![codex-as TUI overview](./assets/codex-tui-overview.svg)\n![codex-as usage meters](./assets/codex-tui-usage.svg)\n\n### What it does\n\nManages a local file of named Codex auth payloads in `~/.codex-as/accounts.json`, lets you switch the active auth written to `~/.codex/auth.json`, and shows live 5-hour and 7-day usage for each saved account by querying `codex app-server`.\n\nUnlike Claude Code, Codex does not need a binary patch — `codex-as` works by managing auth state cleanly around Codex's own auth file and app-server APIs.\n\n### Usage\n\n```bash\ncodex-as                # full-screen TUI\ncodex-as add            # log into a new Codex account and save it\ncodex-as list           # show saved accounts with live usage\ncodex-as pick           # switch to the best account for the current 5h window\ncodex-as use --name you@example.com\ncodex-as import-current # save whatever is currently in ~/.codex/auth.json\ncodex-as status         # show current auth target and live usage\n```\n\nKeys in the TUI: `↑/↓` move · `enter` use · `n` best · `r` refresh · `a` add · `d` remove · `q` quit\n\n**`add`** runs `codex login` inside an isolated temporary `CODEX_HOME`, reads the resulting `auth.json`, derives the account name from the authenticated email, saves it to `~/.codex-as/accounts.json`, and makes it active in your real `~/.codex/auth.json`.\n\n```bash\ncodex-as add --device-auth\ncodex-as add --with-api-key --name work-api\n```\n\n**`pick`** uses the same algorithm as `claude-as pick` — skips full accounts, prefers the soonest-resetting 5h window, breaks ties by highest utilization.\n\n**`list`** probes each saved account in an isolated temporary `CODEX_HOME` so it can show live rate-limit data without mutating your real active auth.\n\n#### Safety\n\n- Saved accounts are stored as plaintext JSON in `~/.codex-as/accounts.json`.\n- The active Codex auth is written to `~/.codex/auth.json`.\n- `list` and `pick` read live rate limits through `codex app-server`.\n- `add` runs `codex login` in an isolated temp directory.\n\n---\n\n## GSD agent integration\n\nBoth `claude-as` and `codex-as` automatically sync credentials into the [GSD](https://github.com/pHo9UBenaA/gsd) agent auth file whenever you switch accounts.\n\n- **claude-as** writes `anthropic.access / refresh / expires` into `~/.gsd/agent/auth.json`\n- **codex-as** writes `openai-codex.access / refresh / expires / accountId` into `~/.gsd/agent/auth.json`\n\nThe sync only runs if `~/.gsd/agent/auth.json` already exists and contains the matching key (`anthropic` for claude, `openai-codex` for codex). If the file is absent or the key is missing, it is silently skipped — no error, no side effects.\n\nWhen synced, the CLI output shows:\n```\nGSD        agent auth synced\n```\n\nNo configuration needed. If you use GSD, your agent picks up the new credentials automatically on every `use` or `pick`.\n\n---\n\n## Development\n\n```bash\nnpm install\nnpm test\n```\n\nTests run offline (the codex tests use a fake `codex` binary, the claude tests use local fixtures).\n\n```bash\n# Target a specific binary\nclaude-as patch --target ~/.local/share/claude/versions/2.1.76\n\n# Use custom paths\ncodex-as list --accounts-file ~/tmp/codex-accounts.json\ncodex-as status --codex-home ~/tmp/codex-home\n```\n\n---\n\n## Publishing\n\n```bash\nnpm publish\n```\n\nBoth `claude-as` and `codex-as` will be available after `npm install -g @adamtash/ai-as`.\n","readmeFilename":"README.md"}