From d6c4083f98094dbd8d86f70992729924d93f73c4 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 22 Jul 2025 15:54:33 -0700 Subject: [PATCH] feat: support dotenv (including ~/.codex/.env) (#1653) This PR adds a `load_dotenv()` helper function to the `codex-common` crate that is available when the `cli` feature is enabled. The function uses [`dotenvy`](https://crates.io/crates/dotenvy) to update the environment from: - `$CODEX_HOME/.env` - `$(pwd)/.env` To test: - ran `printenv OPENAI_API_KEY` to verify the env var exists in my environment - ran `just codex exec hello` to verify the CLI uses my `OPENAI_API_KEY` - ran `unset OPENAI_API_KEY` - ran `just codex exec hello` again and got **ERROR: Missing environment variable: `OPENAI_API_KEY`**, as expected - created `~/.codex/.env` and added `OPENAI_API_KEY=sk-proj-...` (also ran `chmod 400 ~/.codex/.env` for good measure) - ran `just codex exec hello` again and it worked, verifying it picked up `OPENAI_API_KEY` from `~/.codex/.env` Note this functionality was available in the TypeScript CLI: https://github.com/openai/codex/pull/122 and was recently requested over on https://github.com/openai/codex/issues/1262#issuecomment-3093203551. --- codex-rs/Cargo.lock | 8 ++++++++ codex-rs/core/src/config.rs | 2 +- codex-rs/linux-sandbox/Cargo.toml | 2 ++ codex-rs/linux-sandbox/src/lib.rs | 12 ++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 9b4a4e32..3e4b84a4 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -756,7 +756,9 @@ version = "0.0.0" dependencies = [ "anyhow", "clap", + "codex-common", "codex-core", + "dotenvy", "landlock", "libc", "seccompiler", @@ -1272,6 +1274,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "dupe" version = "0.9.1" diff --git a/codex-rs/core/src/config.rs b/codex-rs/core/src/config.rs index 8ed06c45..2dfd3e55 100644 --- a/codex-rs/core/src/config.rs +++ b/codex-rs/core/src/config.rs @@ -561,7 +561,7 @@ fn default_model() -> String { /// function will Err if the path does not exist. /// - If `CODEX_HOME` is not set, this function does not verify that the /// directory exists. -fn find_codex_home() -> std::io::Result { +pub fn find_codex_home() -> std::io::Result { // Honor the `CODEX_HOME` environment variable when it is set to allow users // (and tests) to override the default location. if let Ok(val) = std::env::var("CODEX_HOME") { diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml index c8cd1078..5c2dea60 100644 --- a/codex-rs/linux-sandbox/Cargo.toml +++ b/codex-rs/linux-sandbox/Cargo.toml @@ -17,7 +17,9 @@ workspace = true [dependencies] anyhow = "1" clap = { version = "4", features = ["derive"] } +codex-common = { path = "../common", features = ["cli"] } codex-core = { path = "../core" } +dotenvy = "0.15.7" tokio = { version = "1", features = ["rt-multi-thread"] } [dev-dependencies] diff --git a/codex-rs/linux-sandbox/src/lib.rs b/codex-rs/linux-sandbox/src/lib.rs index 568f0158..96067846 100644 --- a/codex-rs/linux-sandbox/src/lib.rs +++ b/codex-rs/linux-sandbox/src/lib.rs @@ -43,6 +43,10 @@ where crate::run_main(); } + // This modifies the environment, which is not thread-safe, so do this + // before creating any threads/the Tokio runtime. + load_dotenv(); + // Regular invocation – create a Tokio runtime and execute the provided // async entry-point. let runtime = tokio::runtime::Runtime::new()?; @@ -61,3 +65,11 @@ where pub fn run_main() -> ! { panic!("codex-linux-sandbox is only supported on Linux"); } + +/// Load env vars from ~/.codex/.env and `$(pwd)/.env`. +fn load_dotenv() { + if let Ok(codex_home) = codex_core::config::find_codex_home() { + dotenvy::from_path(codex_home.join(".env")).ok(); + } + dotenvy::dotenv().ok(); +}