Skip to content

v0.32.0

Choose a tag to compare

@errhythm errhythm released this 12 Sep 14:50
· 18 commits to main since this release
4eaa481

A sync with upstream realiti4/claude-swap through 7187ce8, 33 commits. Most of it lands on session mode, where running an account as both the default login and a session could leave one copy of the token stale. That case is now handled instead of warned about.

Added

  • ccswap run 2 --require-session. Without it, asking to run an account that is already the default login quietly launches plain claude. With it, that fast path refuses instead, so a script cannot think it got an isolated session when it got the default one.
  • The macOS menu bar can run as a launchd LaunchAgent. ccswap menubar blocks the terminal that started it, so the status item died with the terminal. ccswap menubar --install-service registers it to start at login, with --uninstall-service and --service-status alongside. Logs go to ~/Library/Logs/com.ccswap.menubar.{log,err}.
  • loginExpiresAt on JSON rows, when the stored login records when its refresh token expires. That is the moment the slot needs a fresh /login and ccswap add --slot N, so a script can warn days ahead instead of discovering relogin_required the hard way.
  • usageError and usageRetryAt on rows whose usage is unavailable with nothing else to explain it. usageError names the last fetch failure by kind, such as http-429 or timeout, and usageRetryAt gives the next attempt while the cache is backing off.

Changed

  • An exited session's credential is adopted into the account's backup before a switch or usage check reads that backup. A session refreshes its own copy of the token, so the backup went stale the moment the session rotated it.
  • ccswap switch refuses to move the default login onto an account with a live session whose stored backup has already fallen behind. Activating a consumed generation could only fail with invalid_grant on its first refresh, so this is now an error rather than a warning you find out about later.
  • A live session's usage is read with the session's own credential and never refreshed. A read the server refuses shows as token expired and is not requested again until the session renews the credential itself.
  • The token-persist warning goes to stderr. It ran inside ccswap list --json and the other --json commands, whose stdout is meant to be one machine-readable object.

Fixed

  • ccswap add refuses a credential whose owner is not the account being added, instead of storing it under the wrong slot.
  • Session records whose PID has been recycled are ignored. A dead session's PID reassigned to an unrelated process read as a live session.
  • An unreadable backup no longer produces a dead-token verdict. Not being able to read the file is not evidence the token is dead.
  • A symlinked profile's Keychain entry is read under its target path.
  • The menu bar stops leaking callbacks. Its rumps callback registry was never purged on rebuild, so memory grew without bound.
  • The menu bar reports an interpreter that cannot draw the status item, gating on the interpreter build rather than its version, and requires macOS 26.

Fork-specific

  • The LaunchAgent resolves the ccswap console script. Upstream looks up its own cswap, which does not exist here, so both probes missed and the plist got a virtualenv-internal path that ccswap upgrade then breaks.
  • The launchd job is labelled com.ccswap.menubar, not upstream's com.cswap.menubar.