ccswap 0.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. ccswap-0.20.0/.github/workflows/ci.yml +69 -0
  2. ccswap-0.20.0/.github/workflows/publish.yml +28 -0
  3. ccswap-0.20.0/.gitignore +13 -0
  4. ccswap-0.20.0/.python-version +1 -0
  5. ccswap-0.20.0/.vscode/launch.json +60 -0
  6. ccswap-0.20.0/LICENSE +21 -0
  7. ccswap-0.20.0/PKG-INFO +379 -0
  8. ccswap-0.20.0/README.md +347 -0
  9. ccswap-0.20.0/assets/tui-watch.png +0 -0
  10. ccswap-0.20.0/codex-manual-markdown +15843 -0
  11. ccswap-0.20.0/pyproject.toml +61 -0
  12. ccswap-0.20.0/src/claude_swap/__init__.py +9 -0
  13. ccswap-0.20.0/src/claude_swap/__main__.py +6 -0
  14. ccswap-0.20.0/src/claude_swap/autoswitch.py +1156 -0
  15. ccswap-0.20.0/src/claude_swap/cache.py +44 -0
  16. ccswap-0.20.0/src/claude_swap/claude_locks.py +147 -0
  17. ccswap-0.20.0/src/claude_swap/cli.py +999 -0
  18. ccswap-0.20.0/src/claude_swap/codex.py +574 -0
  19. ccswap-0.20.0/src/claude_swap/codex_autoswitch.py +249 -0
  20. ccswap-0.20.0/src/claude_swap/codex_usage.py +223 -0
  21. ccswap-0.20.0/src/claude_swap/credentials.py +1026 -0
  22. ccswap-0.20.0/src/claude_swap/exceptions.py +96 -0
  23. ccswap-0.20.0/src/claude_swap/json_output.py +172 -0
  24. ccswap-0.20.0/src/claude_swap/locking.py +83 -0
  25. ccswap-0.20.0/src/claude_swap/logging_config.py +64 -0
  26. ccswap-0.20.0/src/claude_swap/macos_keychain.py +226 -0
  27. ccswap-0.20.0/src/claude_swap/menubar.py +785 -0
  28. ccswap-0.20.0/src/claude_swap/migrations.py +536 -0
  29. ccswap-0.20.0/src/claude_swap/models.py +179 -0
  30. ccswap-0.20.0/src/claude_swap/oauth.py +627 -0
  31. ccswap-0.20.0/src/claude_swap/paths.py +187 -0
  32. ccswap-0.20.0/src/claude_swap/printer.py +205 -0
  33. ccswap-0.20.0/src/claude_swap/process_detection.py +146 -0
  34. ccswap-0.20.0/src/claude_swap/session.py +839 -0
  35. ccswap-0.20.0/src/claude_swap/settings.py +383 -0
  36. ccswap-0.20.0/src/claude_swap/snapshot_source.py +85 -0
  37. ccswap-0.20.0/src/claude_swap/switcher.py +3731 -0
  38. ccswap-0.20.0/src/claude_swap/transfer.py +491 -0
  39. ccswap-0.20.0/src/claude_swap/tui/__init__.py +27 -0
  40. ccswap-0.20.0/src/claude_swap/tui/app.py +317 -0
  41. ccswap-0.20.0/src/claude_swap/tui/autoview.py +228 -0
  42. ccswap-0.20.0/src/claude_swap/tui/cswap.tcss +202 -0
  43. ccswap-0.20.0/src/claude_swap/tui/dashboard.py +398 -0
  44. ccswap-0.20.0/src/claude_swap/tui/data.py +172 -0
  45. ccswap-0.20.0/src/claude_swap/tui/modals.py +159 -0
  46. ccswap-0.20.0/src/claude_swap/tui/theme.py +67 -0
  47. ccswap-0.20.0/src/claude_swap/tui/widgets.py +354 -0
  48. ccswap-0.20.0/src/claude_swap/update_check.py +135 -0
  49. ccswap-0.20.0/src/claude_swap/usage_store.py +438 -0
  50. ccswap-0.20.0/tests/__init__.py +1 -0
  51. ccswap-0.20.0/tests/conftest.py +277 -0
  52. ccswap-0.20.0/tests/fixtures/sample_config.json +10 -0
  53. ccswap-0.20.0/tests/test_api_key_accounts.py +370 -0
  54. ccswap-0.20.0/tests/test_autoswitch.py +1357 -0
  55. ccswap-0.20.0/tests/test_cache.py +72 -0
  56. ccswap-0.20.0/tests/test_claude_locks.py +99 -0
  57. ccswap-0.20.0/tests/test_cli.py +939 -0
  58. ccswap-0.20.0/tests/test_codex.py +228 -0
  59. ccswap-0.20.0/tests/test_codex_autoswitch.py +104 -0
  60. ccswap-0.20.0/tests/test_codex_usage.py +158 -0
  61. ccswap-0.20.0/tests/test_config_cli.py +268 -0
  62. ccswap-0.20.0/tests/test_json_output.py +524 -0
  63. ccswap-0.20.0/tests/test_locking.py +162 -0
  64. ccswap-0.20.0/tests/test_logging_config.py +59 -0
  65. ccswap-0.20.0/tests/test_macos_keychain.py +212 -0
  66. ccswap-0.20.0/tests/test_macos_keychain_contract.py +283 -0
  67. ccswap-0.20.0/tests/test_menubar.py +398 -0
  68. ccswap-0.20.0/tests/test_migrations.py +596 -0
  69. ccswap-0.20.0/tests/test_oauth.py +1291 -0
  70. ccswap-0.20.0/tests/test_paths.py +316 -0
  71. ccswap-0.20.0/tests/test_printer.py +181 -0
  72. ccswap-0.20.0/tests/test_process_detection.py +356 -0
  73. ccswap-0.20.0/tests/test_session.py +1276 -0
  74. ccswap-0.20.0/tests/test_settings.py +211 -0
  75. ccswap-0.20.0/tests/test_switcher.py +5436 -0
  76. ccswap-0.20.0/tests/test_transfer.py +1265 -0
  77. ccswap-0.20.0/tests/test_tui.py +1033 -0
  78. ccswap-0.20.0/tests/test_update_check.py +297 -0
  79. ccswap-0.20.0/tests/test_usage_store.py +405 -0
  80. ccswap-0.20.0/uv.lock +451 -0
@@ -0,0 +1,69 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ # uv (not pip) so the PEP 735 dev dependency-group — pytest,
17
+ # pytest-asyncio — installs from the committed lockfile, same as local
18
+ # development.
19
+ - name: Install uv
20
+ uses: astral-sh/setup-uv@v6
21
+ with:
22
+ python-version: '3.12'
23
+
24
+ - name: Install dependencies
25
+ run: uv sync
26
+
27
+ - name: Run tests
28
+ run: uv run pytest
29
+
30
+ test-windows:
31
+ runs-on: windows-latest
32
+
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - name: Install uv
37
+ uses: astral-sh/setup-uv@v6
38
+ with:
39
+ python-version: '3.12'
40
+
41
+ - name: Install dependencies
42
+ run: uv sync
43
+
44
+ - name: Run tests
45
+ # Includes the TUI tests: the Textual TUI (unlike the old curses one)
46
+ # runs on stock Windows, and this job is the proof.
47
+ run: uv run pytest
48
+
49
+ macos-keychain:
50
+ runs-on: macos-latest
51
+
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+
55
+ - name: Install uv
56
+ uses: astral-sh/setup-uv@v6
57
+ with:
58
+ python-version: '3.12'
59
+
60
+ - name: Install dependencies
61
+ run: uv sync
62
+
63
+ - name: Run macOS Keychain tests (contract + real security wrapper)
64
+ # faulthandler dumps every thread's stack if a test exceeds 60s — a
65
+ # `security` call blocking on an invisible SecurityAgent dialog would
66
+ # otherwise hang the job with no clue where. The step timeout is the
67
+ # backstop that actually kills it.
68
+ timeout-minutes: 5
69
+ run: uv run pytest tests/test_macos_keychain_contract.py tests/test_macos_keychain.py -v -o faulthandler_timeout=60
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version: '3.12'
20
+
21
+ - name: Install build dependencies
22
+ run: pip install build
23
+
24
+ - name: Build package
25
+ run: python -m build
26
+
27
+ - name: Publish to PyPI
28
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # superpowers session planning artifacts (not part of the project)
13
+ docs/superpowers/
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,60 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "cswap --list",
6
+ "type": "debugpy",
7
+ "request": "launch",
8
+ "module": "claude_swap.cli",
9
+ "args": ["--list"],
10
+ "cwd": "${workspaceFolder}",
11
+ "env": {
12
+ "PYTHONPATH": "${workspaceFolder}/src"
13
+ },
14
+ "console": "integratedTerminal"
15
+ },
16
+ {
17
+ "name": "cswap --switch",
18
+ "type": "debugpy",
19
+ "request": "launch",
20
+ "module": "claude_swap.cli",
21
+ "args": ["--switch"],
22
+ "cwd": "${workspaceFolder}",
23
+ "env": {
24
+ "PYTHONPATH": "${workspaceFolder}/src"
25
+ },
26
+ "console": "integratedTerminal"
27
+ },
28
+ {
29
+ "name": "cswap --purge",
30
+ "type": "debugpy",
31
+ "request": "launch",
32
+ "module": "claude_swap.cli",
33
+ "args": ["--purge"],
34
+ "cwd": "${workspaceFolder}",
35
+ "env": {
36
+ "PYTHONPATH": "${workspaceFolder}/src"
37
+ },
38
+ "console": "integratedTerminal"
39
+ },
40
+ {
41
+ "name": "cswap (custom args)",
42
+ "type": "debugpy",
43
+ "request": "launch",
44
+ "module": "claude_swap.cli",
45
+ "args": ["${input:cliArgs}"],
46
+ "cwd": "${workspaceFolder}",
47
+ "env": {
48
+ "PYTHONPATH": "${workspaceFolder}/src"
49
+ },
50
+ "console": "integratedTerminal"
51
+ }
52
+ ],
53
+ "inputs": [
54
+ {
55
+ "id": "cliArgs",
56
+ "type": "promptString",
57
+ "description": "CLI arguments (e.g., --list, --status, --switch)"
58
+ }
59
+ ]
60
+ }
ccswap-0.20.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onur Cetinkol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ccswap-0.20.0/PKG-INFO ADDED
@@ -0,0 +1,379 @@
1
+ Metadata-Version: 2.4
2
+ Name: ccswap
3
+ Version: 0.20.0
4
+ Summary: Claude Codex Swap — multi-account and usage manager for Claude Code and OpenAI Codex
5
+ Project-URL: Homepage, https://github.com/errhythm/cc-swap
6
+ Project-URL: Repository, https://github.com/errhythm/cc-swap
7
+ Project-URL: Issues, https://github.com/errhythm/cc-swap/issues
8
+ Project-URL: Upstream, https://github.com/realiti4/claude-swap
9
+ Author-email: Onur Cetinkol <onurcetinkol@gmail.com>, Ehsanur Rahman Rhythm <errhythm.me@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: account-switcher,claude,claude-code,cli,codex,openai
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: Microsoft :: Windows
19
+ Classifier: Operating System :: POSIX :: Linux
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: keyring>=25.0.0
27
+ Requires-Dist: textual<9,>=8.2.8
28
+ Requires-Dist: truststore>=0.10.4
29
+ Provides-Extra: menubar
30
+ Requires-Dist: rumps>=0.4.0; extra == 'menubar'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ccswap (Claude Codex Swap)
34
+
35
+ Multi-account and usage manager for Claude Code and OpenAI Codex. Save multiple logins, check their quota windows, switch manually or automatically before you hit a rate limit, and manage both providers from one dashboard.
36
+
37
+ `ccswap` began as a fork of [claude-swap (`cswap`)](https://github.com/realiti4/claude-swap) by Onur Cetinkol, and still carries the original MIT license and credit for that. Since then it's grown into its own project: no more tracking upstream, no `cswap` compatibility, its own package and release line, and adds Codex support. It's MIT-licensed too, so fork it, file issues, send PRs — whatever's useful to you.
38
+
39
+ ## Installation
40
+
41
+ ### Using uv (recommended)
42
+
43
+ ```bash
44
+ uv tool install ccswap
45
+ ```
46
+
47
+ ### Using pipx
48
+
49
+ ```bash
50
+ pipx install ccswap
51
+ ```
52
+
53
+ ### From source
54
+
55
+ ```bash
56
+ git clone https://github.com/errhythm/cc-swap.git
57
+ cd cc-swap
58
+ uv sync
59
+ uv run ccswap help
60
+ ```
61
+
62
+ ### Updating
63
+
64
+ ```bash
65
+ ccswap upgrade # uv/pipx installs on macOS/Linux: auto-detects and upgrades
66
+ # or run your installer directly:
67
+ uv tool upgrade ccswap
68
+ pipx upgrade ccswap
69
+ ```
70
+
71
+ ## Usage
72
+
73
+ ### Add your first account
74
+
75
+ Log into Claude Code with your first account, then:
76
+
77
+ ```bash
78
+ ccswap add
79
+ ```
80
+
81
+ ### Add more accounts
82
+
83
+ Log in with another account, then:
84
+
85
+ ```bash
86
+ ccswap add
87
+ ```
88
+
89
+ ### Switch accounts
90
+
91
+ Rotate to the next account:
92
+
93
+ ```bash
94
+ ccswap switch
95
+ ```
96
+
97
+ Or switch to a specific account:
98
+
99
+ ```bash
100
+ ccswap switch 2
101
+ ccswap switch user@example.com
102
+ ```
103
+
104
+ Not sure which one? `ccswap list` is the dashboard — every account's 5-hour and 7-day usage and reset times at a glance:
105
+
106
+ ```bash
107
+ ccswap list
108
+ ```
109
+
110
+ Or ccswap auto-picks by remaining quota — `ccswap switch --strategy best` (most quota left) or `--strategy next-available` (skip rate-limited accounts).
111
+
112
+ **Note:** You usually don't need to restart — on Linux/Windows the new account is picked up automatically, and on macOS after the Keychain cache expires. To apply it instantly, restart Claude Code or reopen the VS Code extension tab. See [Tips](#tips) for the per-platform details.
113
+
114
+ ### Automatic switching
115
+
116
+ Let ccswap watch your usage and switch for you. When the active account's 5-hour or 7-day window reaches the threshold (default 90%), it switches to the account with the most quota left — before you hit the limit, and safe to run while Claude Code is working:
117
+
118
+ ```bash
119
+ ccswap auto # foreground loop, polls every 60s
120
+ ccswap auto --threshold 80 # switch earlier
121
+ ccswap auto --once # single check-and-switch, for cron/scripts
122
+ ccswap auto --dry-run # log what it would do, never switch
123
+ ```
124
+
125
+ <details>
126
+ <summary>How it behaves & advanced usage</summary>
127
+
128
+ - Runs safely alongside Claude Code: switches take the same credential locks Claude Code uses, so a swap never collides with a token refresh.
129
+ - A cooldown (default 5 min) and a hysteresis margin stop it flip-flopping near the threshold; when every account is exhausted it sleeps until the earliest reset.
130
+ - Usage polling is adaptive — a couple of accounts per check, busy alternates watched more closely, exhausted ones left alone until they reset — so API traffic stays flat no matter how many accounts you manage.
131
+ - It fails safe: if a usage check errors it keeps trusting the last-known numbers while retries back off, and an expired token on an idle machine makes it hold rather than fail over (Claude Code refreshes the token on your next message).
132
+ - An account whose refresh token has died is quarantined and reported until you log in with it and re-run `ccswap add --slot N`. API-key accounts are never rotated onto unless you pass `--include-api-key-accounts`.
133
+
134
+ For cron/systemd timers, `--once` reports the outcome in its exit code (`0` switched, `1` error, `2` nothing to do, `3` blocked — no viable target), and `--json` emits one JSON event per line:
135
+
136
+ ```bash
137
+ */5 * * * * ccswap auto --once --json >> ~/.ccswap-auto.log 2>&1
138
+ ```
139
+
140
+ Defaults like the threshold and cooldown are configurable with `ccswap config set autoswitch.threshold 80` — flags override them (see [Configuration](#configuration)).
141
+
142
+ </details>
143
+
144
+ ### Run multiple accounts at the same time (session mode)
145
+
146
+ Launch Claude Code as a specific account in the current terminal only — every other terminal and the VS Code extension stay on your default account, so two accounts can work in parallel.
147
+
148
+ ```bash
149
+ ccswap run 2 # launch Claude Code as account 2, here only
150
+ ccswap run user@example.com # by email
151
+ ccswap run 2 -- --resume # everything after '--' is forwarded to claude
152
+ ccswap run 2 --share-history # share your chat history with this account too
153
+ ```
154
+
155
+ Sessions use your normal `~/.claude` setup (settings, CLAUDE.md, skills, etc.), but each account keeps its own chat history. Pass `--share-history` if you want your accounts to continue the same conversations — a session started under one account shows up in `--resume` under the others, and nothing already saved is lost. Not supported on Windows yet.
156
+
157
+ ### Interactive dashboard (TUI)
158
+
159
+ Run `ccswap` on its own (or `ccswap tui`) for the full-screen dashboard: live usage, switching, and auto-switching for Claude Code and Codex, all keyboard-driven. Use **Provider: Claude Code…** in the menu to change providers. Arrow-key and Vim-style menu navigation wraps at both ends. `ccswap watch` opens straight into the live monitor. Works on macOS, Linux, and Windows.
160
+
161
+ <img src="assets/tui-watch.png" width="760" alt="ccswap watch — live 5h/7d usage bars for every account, with reset times and the active account marked">
162
+
163
+ ### Codex accounts
164
+
165
+ `ccswap` can also save and switch Codex CLI logins. Log into each account with `codex login`, then save it before logging into the next one:
166
+
167
+ ```bash
168
+ codex login
169
+ ccswap codex add
170
+
171
+ # Log in to the next Codex account, then save it too.
172
+ codex login
173
+ ccswap codex add
174
+
175
+ ccswap codex list
176
+ ccswap codex usage # fetch 5h/7d windows; prints diagnostic errors
177
+ ccswap codex switch 1
178
+ ccswap codex switch # rotate to the next saved account
179
+ ccswap codex auto --once # switch when active quota reaches the threshold
180
+ ccswap codex remove 2
181
+ ```
182
+
183
+ Codex switching preserves the rest of `CODEX_HOME` (configuration, skills, sessions, and history) and replaces only `auth.json`. Restart Codex after switching so its running process loads the selected login. For ChatGPT-backed file logins, the dashboard reads the same read-only Codex rate-limit endpoint used by Codex and shows its primary (5h) and secondary (7d) windows. API-key accounts have no ChatGPT subscription quota, so they remain status-only. `ccswap codex auto` uses the same threshold, cooldown, `--once`, `--dry-run`, and JSON event controls as Claude auto-switching; it prepares the account for the next Codex launch and reports when a restart is needed.
184
+
185
+ Codex must use its documented file credential store. If your `~/.codex/config.toml` says `cli_auth_credentials_store = "keyring"`, change it to `"file"`, run `codex login`, then add the account. This deliberate restriction avoids writing a guessed OS-keyring entry.
186
+
187
+
188
+ ### Refresh expired tokens
189
+
190
+ If an account's token expires, log back into Claude Code with that account and re-run:
191
+
192
+ ```bash
193
+ ccswap add
194
+ ```
195
+
196
+ This will update the stored credentials without creating a duplicate.
197
+
198
+ ### Other commands
199
+
200
+ ```bash
201
+ ccswap run 2 # Run an account in this terminal only (session mode)
202
+ ccswap auto # Auto-switch when nearing rate limits (see above)
203
+ ccswap codex list # List saved Codex CLI accounts
204
+ ccswap codex switch 2 # Switch the file-backed Codex login
205
+ ccswap config # Show or edit settings (see Configuration below)
206
+ ccswap list # Show all accounts with 5h/7d usage and reset times
207
+ ccswap status # Show current account
208
+ ccswap add --slot 3 # Add account to a specific slot (prompts before overwrite)
209
+ ccswap remove 2 # Remove an account
210
+ ccswap tui # Interactive dashboard (also: bare `ccswap`)
211
+ ccswap watch # Dashboard, opened on the live watch page
212
+ ccswap upgrade # Upgrade ccswap to the latest version
213
+ ccswap purge # Remove all ccswap data
214
+ ```
215
+
216
+ The legacy `cswap` command remains available as a compatibility alias; use `ccswap` for new scripts.
217
+
218
+ ## Tips
219
+
220
+ - **Do you need to restart after switching?** Usually not. On **Linux and Windows**, credentials are stored in a file and Claude Code re-reads them whenever that file changes, so the new account takes effect on your next message — no restart needed. On **macOS**, credentials live in the Keychain, which Claude Code caches for about 30 seconds; a running session picks up the switch once that cache expires. Restart Claude Code (or close and reopen the VS Code extension tab) only if you want the change to apply instantly.
221
+ - **Continuing sessions after switching:** You can keep using the same Claude Code session after switching — run `ccswap switch` in any terminal and carry on. If you'd prefer a clean start, close and reopen Claude Code (or the VS Code extension tab) and use `--resume` to pick your previous session. Either way, the first message on the new account may use extra usage as its conversation cache rebuilds.
222
+
223
+ ## How it works
224
+
225
+ - Backs up provider credentials when you add an account
226
+ - Swaps Claude Code credentials or Codex's file-backed `auth.json` when you switch
227
+ - Account credentials stored securely using platform-appropriate methods
228
+ - Switches (manual and automatic) hold Claude Code's own credential locks while writing, so a swap never interleaves with a token refresh
229
+ - Auto-switch freshens a target's token before activating it, and quarantines accounts whose refresh token has died (recover with `ccswap add --slot N`)
230
+ - Codex usage checks refresh inactive saved logins when possible; running Codex sessions must be restarted after a Codex account switch
231
+
232
+ ## Data locations
233
+
234
+ | Platform | Credentials | Config backups |
235
+ |----------|-------------|----------------|
236
+ | Windows | File-based (inside the backup directory, under `credentials/`) | `~/.claude-swap-backup/` |
237
+ | macOS | macOS Keychain | `~/.claude-swap-backup/` |
238
+ | Linux / WSL | File-based (inside the backup directory, under `credentials/`) | `${XDG_DATA_HOME:-~/.local/share}/claude-swap/` |
239
+
240
+ Session-mode profiles (`ccswap run`) live under the backup directory in `sessions/`. Tool preferences (`settings.json`) and auto-switch state (`autoswitch_state.json` — cooldown and quarantined accounts; delete it to reset) live in the backup directory root.
241
+
242
+ On Linux/WSL, set `XDG_DATA_HOME` to override the default location.
243
+
244
+ ## Menu bar (macOS)
245
+
246
+ <details>
247
+ <summary>Optional macOS menu bar app — usage at a glance, click to switch</summary>
248
+
249
+ Needs the `menubar` extra (macOS only):
250
+
251
+ ```bash
252
+ uv tool install 'ccswap[menubar]' # or: pipx install 'ccswap[menubar]'
253
+ ccswap menubar
254
+ ```
255
+
256
+ Shows every account's 5h / 7d / spend usage and switches with a click (specific / rotate / best / next-available), plus the TUI's add / remove / refresh actions. Enable *Settings → Auto-switch accounts* to run the same engine as [`ccswap auto`](#automatic-switching) in the background; it shares the `autoswitch.*` settings, so the menu bar and CLI stay in sync. Off until you turn it on.
257
+
258
+ </details>
259
+
260
+ ## Advanced
261
+
262
+ ### Configuration
263
+
264
+ Tool preferences live in `settings.json` in the backup root; `ccswap config` reads and edits it with validation, so you never have to find the file or guess valid ranges.
265
+
266
+ <details>
267
+ <summary>Commands & usage</summary>
268
+
269
+ ```bash
270
+ ccswap config # list effective settings ("(default)" = not set)
271
+ ccswap config get autoswitch.threshold
272
+ ccswap config set autoswitch.threshold 80 # validated: rejects out-of-range values loudly
273
+ ccswap config unset autoswitch.threshold # back to the default
274
+ ccswap config path # where settings.json lives
275
+ ```
276
+
277
+ `ccswap config --help` lists every key with its valid range and default. Hand-editing the file still works — `ccswap config` is just a safer front door. `list` and `get` take `--json` for scripting.
278
+
279
+ </details>
280
+
281
+ ### Backup and migration
282
+
283
+ Move account data between machines or back it up:
284
+
285
+ ```bash
286
+ ccswap export backup.cswap # All accounts to a file
287
+ ccswap export backup.cswap --account 2 # One account
288
+ ccswap export backup.cswap --full # Include full local ~/.claude.json (same-PC backup)
289
+ ccswap import backup.cswap # Skips accounts that already exist
290
+ ccswap import backup.cswap --force # Overwrite existing
291
+ ```
292
+
293
+ The export file is plaintext JSON. If you need encryption, pipe through your tool of choice (e.g. `ccswap export - | gpg -c > backup.gpg`).
294
+
295
+ If an imported account is the one you're currently logged in as, activate the imported credentials with `ccswap switch N --force` (a plain `switch` to the current account is a safe no-op and won't touch the import).
296
+
297
+ ### JSON output for scripting
298
+
299
+ Add `--json` to `list`, `status`, or `switch` to emit a single machine-readable JSON object on stdout (human-readable notices go to stderr). Useful for scripting auto-swap and quota tracking.
300
+
301
+ ```bash
302
+ ccswap list --json # all accounts with usage/quota
303
+ ccswap status --json # current active account
304
+ ccswap switch --strategy best --json # switch, then report the result
305
+ ccswap switch 2 --json
306
+ ```
307
+
308
+ <details>
309
+ <summary>Example output & schema notes</summary>
310
+
311
+ ```json
312
+ {
313
+ "schemaVersion": 1,
314
+ "activeAccountNumber": 2,
315
+ "accounts": [
316
+ { "number": 2, "email": "you@example.com", "active": true, "usageStatus": "ok",
317
+ "usage": { "fiveHour": { "pct": 25.0, "resetsAt": "2026-06-22T23:29:59Z" },
318
+ "sevenDay": { "pct": 16.0, "resetsAt": "2026-06-26T17:59:59Z" } } }
319
+ ]
320
+ }
321
+ ```
322
+
323
+ Every payload carries a `schemaVersion` (currently `1`); on a handled error stdout is `{"schemaVersion":1,"error":{...}}` with a non-zero exit code. `--switch`/`--switch-to` report `{"switched": true|false, "from": …, "to": …, "reason": …}`.
324
+
325
+ Usage is served from a per-account cache: when the usage API is briefly unreachable, the last-known numbers are shown instead of nothing (the human view marks them with their age, e.g. `· 2m ago`). Rows with usage carry additive `usageFetchedAt`/`usageAgeSeconds` fields telling you how old the measurement is.
326
+
327
+ </details>
328
+
329
+ `ccswap auto --json` emits an event *stream* instead — one JSON object per line (`{"schemaVersion":1,"event":"switch","ts":…, …}` with kinds like `poll`, `switch`, `no-switch`, `account-quarantined`, `all-exhausted`, `error`). The contract is additive: new kinds and fields may appear, so scripts should ignore unknown ones.
330
+
331
+ ### Add an account from a raw token or API key
332
+
333
+ If you only have a long-lived setup-token (e.g., produced by `claude setup-token`)
334
+ or a managed API key (`sk-ant-api...`) and you don't want to log in via the browser
335
+ flow first — useful on headless servers or when receiving a token from another
336
+ machine — register it directly. The token type is auto-detected:
337
+
338
+ ```bash
339
+ ccswap add-token sk-ant-oat01-... # OAuth setup-token
340
+ ccswap add-token sk-ant-api03-... # managed API key
341
+ ccswap add-token sk-ant-oat01-... --slot 3
342
+ ccswap add-token - --slot 3 # read token from stdin
343
+ ccswap add-token --email user@example.com # optional label override
344
+ ```
345
+
346
+ `--email` is optional; omitted values use `setup-token-{slot}@token.local`
347
+ (or `api-key-{slot}@token.local` for API keys). No Anthropic API calls are made.
348
+
349
+ **API-key accounts.** An `sk-ant-api...` value registers a managed API-key account
350
+ (the kind Claude Code uses after `/login` with a key) rather than an OAuth
351
+ setup-token. It switches like any other account; since API keys have no subscription
352
+ quota, they show no usage and the usage-aware `switch` strategies never skip them as
353
+ rate-limited.
354
+
355
+ ## Uninstall
356
+
357
+ Remove all data:
358
+
359
+ ```bash
360
+ ccswap purge
361
+ ```
362
+
363
+ Then uninstall the tool:
364
+
365
+ ```bash
366
+ uv tool uninstall ccswap
367
+ # or
368
+ pipx uninstall ccswap
369
+ ```
370
+
371
+ ## Requirements
372
+
373
+ - Python 3.12+
374
+ - Claude Code and/or Codex CLI
375
+ - A file-backed login for managed Codex accounts
376
+
377
+ ## License
378
+
379
+ MIT. This fork retains the original project's copyright and license notice; see [LICENSE](LICENSE). Upstream: [realiti4/claude-swap](https://github.com/realiti4/claude-swap).