Skip to content

Commit 971faa2

Browse files
committed
2 parents a824974 + bce892a commit 971faa2

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

codex-rs/cli/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use codex_exec::Cli as ExecCli;
2121
use codex_exec::Command as ExecCommand;
2222
use codex_exec::ReviewArgs;
2323
use codex_execpolicy::ExecPolicyCheckCommand;
24+
use codex_login::bootstrap_anthropic_api_key_from_claude_credentials;
2425
use codex_responses_api_proxy::Args as ResponsesApiProxyArgs;
2526
use codex_state::StateRuntime;
2627
use codex_state::state_db_path;
@@ -588,6 +589,10 @@ fn main() -> anyhow::Result<()> {
588589
}
589590

590591
async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
592+
// If ANTHROPIC_API_KEY is not set, auto-load from Claude Code credentials
593+
// (~/.claude/.credentials.json) so Anthropic-configured providers work.
594+
bootstrap_anthropic_api_key_from_claude_credentials();
595+
591596
let MultitoolCli {
592597
config_overrides: mut root_config_overrides,
593598
feature_toggles,

codex-rs/login/src/auth/manager.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,46 @@ pub fn read_codex_api_key_from_env() -> Option<String> {
363363
.filter(|value| !value.is_empty())
364364
}
365365

366+
pub const ANTHROPIC_API_KEY_ENV_VAR: &str = "ANTHROPIC_API_KEY";
367+
368+
/// Read the Anthropic API key from the environment variable `ANTHROPIC_API_KEY`.
369+
pub fn read_anthropic_api_key_from_env() -> Option<String> {
370+
env::var(ANTHROPIC_API_KEY_ENV_VAR)
371+
.ok()
372+
.map(|value| value.trim().to_string())
373+
.filter(|value| !value.is_empty())
374+
}
375+
376+
/// Read the Claude Code OAuth access token from `~/.claude/.credentials.json`.
377+
///
378+
/// Claude Code stores OAuth credentials (from `claude.ai` login) in this file.
379+
/// The access token can be used as an `ANTHROPIC_API_KEY` bearer token for
380+
/// Anthropic API calls when a proper Anthropic provider is configured.
381+
pub fn read_claude_code_credentials() -> Option<String> {
382+
let home = env::var("HOME").ok().map(PathBuf::from)?;
383+
let creds_path = home.join(".claude").join(".credentials.json");
384+
let content = std::fs::read_to_string(creds_path).ok()?;
385+
let json: serde_json::Value = serde_json::from_str(&content).ok()?;
386+
json["claudeAiOauth"]["accessToken"]
387+
.as_str()
388+
.map(String::from)
389+
.filter(|s| !s.is_empty())
390+
}
391+
392+
/// If `ANTHROPIC_API_KEY` is not set in the environment, attempt to load the
393+
/// Claude Code OAuth token from `~/.claude/.credentials.json` and set it.
394+
///
395+
/// Call this early in the process lifecycle so downstream provider lookups see it.
396+
pub fn bootstrap_anthropic_api_key_from_claude_credentials() {
397+
if read_anthropic_api_key_from_env().is_some() {
398+
return;
399+
}
400+
if let Some(token) = read_claude_code_credentials() {
401+
// SAFETY: single-threaded startup context.
402+
unsafe { env::set_var(ANTHROPIC_API_KEY_ENV_VAR, token) };
403+
}
404+
}
405+
366406
/// Delete the auth.json file inside `codex_home` if it exists. Returns `Ok(true)`
367407
/// if a file was removed, `Ok(false)` if no auth file was present.
368408
pub fn logout(

codex-rs/login/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ pub use auth::load_auth_dot_json;
3232
pub use auth::login_with_api_key;
3333
pub use auth::logout;
3434
pub use auth::read_openai_api_key_from_env;
35+
pub use auth::read_anthropic_api_key_from_env;
36+
pub use auth::read_claude_code_credentials;
37+
pub use auth::bootstrap_anthropic_api_key_from_claude_credentials;
38+
pub use auth::ANTHROPIC_API_KEY_ENV_VAR;
3539
pub use auth::save_auth;
3640
pub use codex_app_server_protocol::AuthMode;
3741
pub use token_data::TokenData;

0 commit comments

Comments
 (0)