Skip to content

fix(platform): read the credential of the session's CLAUDE_CONFIG_DIR - #211

Open
cavalcanticampos wants to merge 1 commit into
stephenleo:mainfrom
cavalcanticampos:fix/account-follows-claude-config-dir
Open

cavalcanticampos wants to merge 1 commit into
stephenleo:mainfrom
cavalcanticampos:fix/account-follows-claude-config-dir

Conversation

@cavalcanticampos

Copy link
Copy Markdown

Fixes #194. Also covers the multi-account point in #210 (the usage_limits OAuth fetch used the first account's token in a second-account session).

What a user gets

With several accounts, each in its own CLAUDE_CONFIG_DIR, cship.account and the usage_limits OAuth fallback now follow the account the session actually runs as, instead of always reading the default ~/.claude credential.

The change

get_oauth_token now resolves the credential the way Claude Code stores it:

CLAUDE_CONFIG_DIR unset (or empty) CLAUDE_CONFIG_DIR=/path
Linux / WSL2 file ~/.claude/.credentials.json /path/.credentials.json
Linux secret-tool fallback, macOS Keychain Claude Code-credentials Claude Code-credentials-<hash>
Windows file ~/.claude/.credentials.json /path/.credentials.json

<hash> is the first 8 hex chars of SHA-256 of the CLAUDE_CONFIG_DIR value as given. I took this from Claude Code itself (2.1.322), which builds the service name as `Claude Code${OAUTH_FILE_SUFFIX}-credentials${configDirSet ? "-" + sha256(configDir).hex().substring(0, 8) : ""}` and stores the plaintext file at join(configDir, ".credentials.json"). An empty CLAUDE_CONFIG_DIR is treated as unset, as Claude Code does.

  • src/platform.rs: claude_config_dir() (env → home_dir()/.claude) and keychain_service_name(), used by the macOS, Linux and Windows paths. CLAUDE_HOME keeps working for the default dir.
  • Cargo.toml: adds sha2 = "0.10" (pure Rust) for the hash. It is a normal dependency rather than macOS-only so the naming is unit-tested on the Linux CI.
  • docs/faq.md: a short note on multi-account lookup.

How I verified it

  • cargo fmt --check, cargo clippy -- -D warnings, cargo test (533 + 77 pass), cargo build --release: all pass on Linux (WSL2).
  • New unit tests: unsuffixed name for unset/empty dir; suffixed names 1e91dd84 / d20442b7 for two paths, values computed with Node's crypto.createHash("sha256") exactly as Claude Code does; config-dir resolution prefers CLAUDE_CONFIG_DIR, falls back to ~/.claude, treats empty as unset. They are pure functions, so no test mutates process env.
  • The project can't be cross-compiled here (ring needs a C toolchain per target), so I ran cargo clippy --all-targets -D warnings on platform.rs alone in a scratch crate for aarch64-apple-darwin, x86_64-pc-windows-gnu and x86_64-unknown-linux-gnu: clean on all three.
  • End to end on Linux with cship explain: with CLAUDE_CONFIG_DIR pointing at an empty dir, main still reports "credential present" (it reads ~/.claude), this branch reports "no Claude Code credential found"; with a dir holding its own .credentials.json, this branch uses that one.

Not exercised: a real macOS Keychain with two accounts (I'm on Linux). The service name matches Claude Code's derivation and is covered by the tests above.

🤖 Generated with Claude Code

Claude Code keeps one credential per config dir: on Linux/Windows at
$CLAUDE_CONFIG_DIR/.credentials.json, on macOS in the Keychain item
"Claude Code-credentials-<first 8 hex of sha256(CLAUDE_CONFIG_DIR)>"
(unsuffixed when CLAUDE_CONFIG_DIR is unset). cship always read the
default one, so in a second-account session `account` showed the first
account and `usage_limits` fetched the first account's usage.

get_oauth_token now resolves the config dir and keychain service name the
same way Claude Code does. Fixes stephenleo#194.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

account module ignores CLAUDE_CONFIG_DIR — shows the default account in multi-account setups

1 participant