@youdie006/swapdex 0.159.0 → 0.162.0

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.
package/README.md CHANGED
@@ -38,21 +38,21 @@ account -- a work seat and a personal subscription, a client's org and your own
38
38
  -- switching means logging out and back in every time.
39
39
 
40
40
  swapdex gives each account its **own permanent space** -- its own
41
- `CLAUDE_CONFIG_DIR` slot -- and flips between them without ever copying a token.
41
+ `CLAUDE_CONFIG_DIR` or `CODEX_HOME` slot -- and switches the default pointer.
42
42
  `swapdex use work` points your default account there and a plain `claude`
43
43
  follows it; `swapdex run work` launches straight into that account (each terminal
44
- can be a different one). Because nothing is copied, **a switch can never log an
45
- account out** -- even if a session is still running when you switch.
44
+ can be a different one). Existing native sessions keep their own slot when the
45
+ default changes.
46
46
  `swapdex onboard` sets this up in a few prompts.
47
47
 
48
- It is a **switcher, not a rotator.** It manages accounts you already own for
49
- distinct purposes, with no feature for cycling them to get around a rate limit
50
- -- see [What it will not do](#what-it-will-not-do).
48
+ It manages accounts you already own, with separate launch defaults, proxy
49
+ selection and configurable failover. See [How it works](#how-it-works) for the
50
+ difference between permanent slots and legacy saved snapshots.
51
51
 
52
- Safety is the design center: in the slot model swapdex never writes a credential
53
- at all -- each account's own login creates and refreshes its token, in its own
54
- slot -- and it only ever hands the official CLI its own credentials: no wrapper,
55
- no proxy, no client spoofing.
52
+ Each account signs in within its own slot. The optional local proxy uses the
53
+ selected account's credential for requests and can renew idle slots in place.
54
+ When an actual native session owns the same login, Swapdex leaves renewal to
55
+ that application and uses a verified, read-only access snapshot when available.
56
56
 
57
57
  ## Concepts
58
58
 
@@ -224,10 +224,9 @@ personal you@personal.com
224
224
 
225
225
  It reads each account's remaining quota from Anthropic's official OAuth usage
226
226
  endpoint using that account's **own** token -- read-only, and it spends zero
227
- message quota. The active account is always live; a saved account whose token
228
- has expired reports so rather than showing a stale number (swapdex never
229
- refreshes tokens -- that is the line between a switcher and a rotator). It is
230
- also in `swapdex ui` under the `%` key.
227
+ message quota. It uses the slot or a verified current native login for that
228
+ account. An unavailable or expired credential reports its state rather than
229
+ inventing current quota. It is also in `swapdex ui` under the `%` key.
231
230
 
232
231
  ### The dashboard
233
232
 
@@ -240,12 +239,33 @@ setup is one keystroke and a name.
240
239
  <img src="https://raw.githubusercontent.com/youdie006/swapdex/main/docs/ui-demo.gif" alt="swapdex ui on a fresh machine: it finds the Claude Code and Codex logins already present, saves them as a profile named main, and shows the account with its 5h and 7d usage bars" width="760" />
241
240
  </div>
242
241
 
242
+ ## Resuming Codex conversations
243
+
244
+ Use `codex resume` normally, or `codex resume --all` to include other working
245
+ directories. Swapdex keeps one stable OpenAI provider across account changes.
246
+ The paying account is shown by `swapdex serve --tool codex --quiet`.
247
+ If the proxy cannot start, the launcher warns that Codex will use its own login
248
+ directly.
249
+
250
+ After updating from a version that created `swapdex` provider IDs, run
251
+ `swapdex shim` to refresh the launcher. It automatically repairs those legacy
252
+ session labels before launching Codex. To inspect or retry the repair directly:
253
+
254
+ ```sh
255
+ swapdex repair-codex-sessions --dry-run
256
+ swapdex repair-codex-sessions
257
+ ```
258
+
259
+ The repair preserves conversation contents and keeps a private recovery journal.
260
+ Open sessions are deferred until they close. Unsupported compressed rollouts
261
+ and unsuccessful repairs are reported rather than silently hidden.
262
+
243
263
  ## Keeping accounts from expiring
244
264
 
245
- An account nobody opens dies on its own. These refresh tokens go stale when
246
- they are not exercised -- measured across two machines, an idle Codex slot
247
- stops working about ten days after its last run -- and once the refresh token
248
- is gone only a browser sign-in brings the account back.
265
+ Access tokens expire by design. An idle slot can renew while its refresh token
266
+ remains valid, but provider expiry, revocation or renewal by another credential
267
+ holder can make a new browser sign-in necessary. Keep-alive reduces avoidable
268
+ idle expiry; it cannot guarantee that a login never expires.
249
269
 
250
270
  swapdex renews idle accounts for you, but **only while its proxy is running**,
251
271
  because that is the process holding the timer:
@@ -280,9 +300,9 @@ own slot*. swapdex never copies a token between slots: `swapdex run <name>`
280
300
  `exec`s `claude` with that slot's `CLAUDE_CONFIG_DIR`, and `swapdex use <name>`
281
301
  writes a one-line pointer that a small `claude` shim on your PATH reads. Shared
282
302
  config (`settings.json`, global `CLAUDE.md`) is symlinked into each new slot;
283
- the token and history stay per-slot. Because no credential is ever moved, a
284
- token refresh in one account can never revoke another -- **a switch cannot log
285
- you out**.
303
+ the token and history stay per-slot. Independently signed-in slots avoid sharing
304
+ a rotating refresh chain. Copies of one login remain coupled even if they live
305
+ in different directories; the warning below applies to those copies.
286
306
 
287
307
  **Classic snapshots (still supported).** Each CLI also keeps its login in a
288
308
  small on-disk file:
@@ -334,50 +354,54 @@ for other software.
334
354
 
335
355
  Codex renewal is checked before access expires: a running Codex proxy checks
336
356
  every 30 minutes and attempts renewal for idle slots within 48 hours of expiry.
357
+ `swapdex refresh [name]` exits with status 4 if any requested renewal fails or
358
+ is deferred by the running-session guard, even if another account renews.
359
+ Already-current accounts and successful or empty runs return 0. Missing or
360
+ unreadable logins produce a sign-in remedy without an OAuth request.
361
+
337
362
  `swapdex refresh --keep-alive` runs the same check without a proxy. If a local
338
- session holds a due account, renewal is deferred to avoid retiring the token
339
- that session holds. The account list and picker then show that renewal is
340
- deferred and its refresh validity is unverified; this does not mean the access
341
- token has expired or the account needs a new login. A confirmed refresh
342
- rejection is reported separately as requiring re-login.
363
+ session holds a due account, Swapdex leaves its refresh token alone. A verified
364
+ usable native login is shown as managed by Claude or Codex, including
365
+ `renewal_owner` in `ls --json`; this is not an OAuth renewal by Swapdex. When
366
+ that native login cannot be verified, renewal remains deferred and unverified.
367
+ Actual access expiry and recorded refresh rejection are separate warnings.
368
+ An inaccessible macOS Keychain is a read-access problem, not proof that the
369
+ login expired.
370
+
371
+ For an HTTP request rejected with 401, the managed proxy first attempts bounded
372
+ recovery of the same selected account: reread a changed usable native access
373
+ token, or await a coordinated Swapdex renewal for an idle login. It retries
374
+ only with a changed usable token, before any explicitly configured failover.
375
+ An unavailable selected login produces an error instead of silently using the
376
+ client's different account.
377
+
378
+ The launch default and proxy selection control different operations: the first
379
+ affects new native launches, and the second affects subsequent managed HTTP
380
+ requests. Changing a file or default does not switch every existing native
381
+ process, in-flight request or WebSocket conversation. Explicit account pins
382
+ retain their selected account.
343
383
 
344
384
  An external copy can renew without changing any local file. Neither the local
345
385
  access token's issue time nor `last_refresh` reveals that remote event. These
346
386
  checks therefore cannot certify refresh validity after unseen remote activity;
347
387
  external consumers need their own login instead of a copy of a managed slot.
348
388
 
349
- ### What it will not do
350
-
351
- These are structural properties, not promises -- the code is built so they
352
- cannot happen:
353
-
354
- - **No HTTP client, no background network.** The binary has no HTTP client in
355
- its dependency graph (CI asserts this on every commit), so it cannot phone
356
- home or exfiltrate a token. Switching, `ls`, `status`, `usage` -- all 100%
357
- local. The one exception is the opt-in `swapdex quota` command, which shells
358
- out to `curl` to read your *own* remaining balance from Anthropic's official
359
- usage endpoint (that account's own token, read-only, spends zero message
360
- quota). It runs only when you type it, sends no data anywhere, and touches no
361
- other endpoint.
362
- - **No auto-rotation.** There is no `--auto`, `--next`, or
363
- `--when-rate-limited` flag. `use` only ever switches to a name you type.
364
- - **No token export.** There is no command that prints a saved credential.
365
- - **No wrapper, no client spoofing.** swapdex swaps the credential file that the
366
- official `claude` / `codex` binary already reads, then gets out of the way. It
367
- never sits between the CLI and the API, never proxies requests, and never
368
- presents itself as the official client. Your traffic is the real CLI's traffic.
369
- (Launching the official tool once, on your explicit pick - `login`'s sign-in
370
- flow, `ui`'s session resume - is a hand-off, not a wrapper: swapdex `exec`s
371
- and is gone.)
372
-
373
- Anthropic and OpenAI both permit multiple accounts for genuinely different
374
- purposes but forbid using multiple accounts to get around a single workload's
375
- rate limit, and forbid routing subscription OAuth tokens through third-party
376
- tools or spoofing the official client. swapdex is built for the former and
377
- structurally cannot do the latter -- it only ever hands the real CLI its own
378
- credentials. See
379
- [Anthropic Usage Policy](https://www.anthropic.com/legal/usage-policy) and
380
- [OpenAI Usage Policies](https://openai.com/policies/usage-policies/).
389
+ ### Network and credential behavior
390
+
391
+ Account selection and listing read local state. Opt-in quota lookups contact
392
+ provider usage endpoints. The optional proxy relays API requests with the
393
+ selected credential, and its scheduled renewal work contacts OAuth endpoints
394
+ for idle logins. It uses `ureq` with rustls and bundled roots; CI excludes heavy
395
+ async runtimes and system-TLS dependencies.
396
+
397
+ Explicit account selection, launch defaults and configured proxy failover are
398
+ separate controls. A local file lock coordinates participating Swapdex callers;
399
+ it cannot lock an independent native CLI or another machine. Native renewal
400
+ ownership and access availability are therefore reported separately.
401
+
402
+ There is no command that prints a saved credential. OAuth request secrets are
403
+ passed to curl on stdin, and diagnostics redact credentials. Native launches
404
+ execute the installed official CLI with the chosen account's configuration.
381
405
 
382
406
  ## MCP (read-only)
383
407
 
@@ -407,18 +431,16 @@ that project's README, July 2026):
407
431
 
408
432
  - [claude-swap](https://github.com/realiti4/claude-swap) -- Claude Code only,
409
433
  a TUI with live usage bars, and *optional auto-switching* near your limit.
410
- If you want auto-rotation, use it -- swapdex deliberately refuses to have
411
- that feature.
434
+ This older comparison should be read alongside the current source review below.
412
435
  - [aisw](https://github.com/burakdede/aisw) -- cross-tool including Gemini,
413
436
  OS-keyring storage, Windows support. More features, bigger surface.
414
437
  - [caam](https://github.com/Dicklesworthstone/coding_agent_account_manager) --
415
- cross-tool with a shell wrapper and automatic rotation on rate limits; the
416
- philosophical opposite of swapdex.
438
+ cross-tool with a shell wrapper and automatic rotation on rate limits.
417
439
 
418
- Pick swapdex if you want the smallest thing that switches your AI CLIs
419
- together, can always undo (`restore`), diagnoses itself (`doctor`), shows
420
- your remaining balance (`quota`), and structurally cannot rotate, proxy, or
421
- spoof the official client.
440
+ For current Codex switching and renewal mechanisms, see the
441
+ [2026-09-15 source survey](docs/research/2026-09-15-codex-switcher-survey.md):
442
+ pinned implementations, regression-test coverage and the limits of file-based
443
+ switching while native sessions keep running.
422
444
 
423
445
  ## Roadmap
424
446
 
package/bin/exit.js CHANGED
File without changes
package/bin/swapdex.js CHANGED
File without changes
package/man/swapdex.1 CHANGED
@@ -1,6 +1,6 @@
1
1
  .ie \n(.g .ds Aq \(aq
2
2
  .el .ds Aq '
3
- .TH swapdex 1 "swapdex 0.159.0"
3
+ .TH swapdex 1 "swapdex 0.162.0"
4
4
  .SH NAME
5
5
  swapdex \- Switch Claude Code / Codex / Gemini / Antigravity login accounts, locally and safely.
6
6
  .SH SYNOPSIS
@@ -16,6 +16,9 @@ Print help
16
16
  Print version
17
17
  .SH SUBCOMMANDS
18
18
  .TP
19
+ swapdex\-repair\-codex\-sessions(1)
20
+ Repair provider metadata written by older Swapdex Codex shims
21
+ .TP
19
22
  swapdex\-add(1)
20
23
  Save the current live login as a named profile
21
24
  .TP
@@ -136,4 +139,4 @@ Print the man page (roff) to stdout
136
139
  swapdex\-help(1)
137
140
  Print this message or the help of the given subcommand(s)
138
141
  .SH VERSION
139
- v0.159.0
142
+ v0.162.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdie006/swapdex",
3
- "version": "0.159.0",
3
+ "version": "0.162.0",
4
4
  "description": "Switch between multiple Claude Code, Codex, Gemini, and Antigravity login accounts, locally and safely.",
5
5
  "bin": {
6
6
  "swapdex": "bin/swapdex.js"
@@ -12,10 +12,10 @@
12
12
  "man/swapdex.1"
13
13
  ],
14
14
  "optionalDependencies": {
15
- "@youdie006/swapdex-darwin-arm64": "0.159.0",
16
- "@youdie006/swapdex-darwin-x64": "0.159.0",
17
- "@youdie006/swapdex-linux-x64": "0.159.0",
18
- "@youdie006/swapdex-linux-arm64": "0.159.0"
15
+ "@youdie006/swapdex-darwin-arm64": "0.162.0",
16
+ "@youdie006/swapdex-darwin-x64": "0.162.0",
17
+ "@youdie006/swapdex-linux-x64": "0.162.0",
18
+ "@youdie006/swapdex-linux-arm64": "0.162.0"
19
19
  },
20
20
  "keywords": [
21
21
  "cli",