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.
76 lines
2.4 KiB
Rust
76 lines
2.4 KiB
Rust
#[cfg(target_os = "linux")]
|
||
mod landlock;
|
||
#[cfg(target_os = "linux")]
|
||
mod linux_run_main;
|
||
|
||
#[cfg(target_os = "linux")]
|
||
pub use linux_run_main::run_main;
|
||
|
||
use std::future::Future;
|
||
use std::path::PathBuf;
|
||
|
||
/// Helper that consolidates the common boilerplate found in several Codex
|
||
/// binaries (`codex`, `codex-exec`, `codex-tui`) around dispatching to the
|
||
/// `codex-linux-sandbox` sub-command.
|
||
///
|
||
/// When the current executable is invoked through the hard-link or alias
|
||
/// named `codex-linux-sandbox` we *directly* execute [`run_main`](crate::run_main)
|
||
/// (which never returns). Otherwise we:
|
||
/// 1. Construct a Tokio multi-thread runtime.
|
||
/// 2. Derive the path to the current executable (so children can re-invoke
|
||
/// the sandbox) when running on Linux.
|
||
/// 3. Execute the provided async `main_fn` inside that runtime, forwarding
|
||
/// any error.
|
||
///
|
||
/// This function eliminates duplicated code across the various `main.rs`
|
||
/// entry-points.
|
||
pub fn run_with_sandbox<F, Fut>(main_fn: F) -> anyhow::Result<()>
|
||
where
|
||
F: FnOnce(Option<PathBuf>) -> Fut,
|
||
Fut: Future<Output = anyhow::Result<()>>,
|
||
{
|
||
use std::path::Path;
|
||
|
||
// Determine if we were invoked via the special alias.
|
||
let argv0 = std::env::args().next().unwrap_or_default();
|
||
let exe_name = Path::new(&argv0)
|
||
.file_name()
|
||
.and_then(|s| s.to_str())
|
||
.unwrap_or("");
|
||
|
||
if exe_name == "codex-linux-sandbox" {
|
||
// Safety: [`run_main`] never returns.
|
||
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()?;
|
||
runtime.block_on(async move {
|
||
let codex_linux_sandbox_exe: Option<PathBuf> = if cfg!(target_os = "linux") {
|
||
std::env::current_exe().ok()
|
||
} else {
|
||
None
|
||
};
|
||
|
||
main_fn(codex_linux_sandbox_exe).await
|
||
})
|
||
}
|
||
|
||
#[cfg(not(target_os = "linux"))]
|
||
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();
|
||
}
|