codex-python 0.2.14__tar.gz → 0.2.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-python
3
- Version: 0.2.14
3
+ Version: 0.2.15
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Python :: 3.13
@@ -31,4 +31,4 @@ __all__ = [
31
31
  ]
32
32
 
33
33
  # Package version. Kept in sync with Cargo.toml via CI before builds.
34
- __version__ = "0.2.14"
34
+ __version__ = "0.2.15"
@@ -422,12 +422,13 @@ dependencies = [
422
422
 
423
423
  [[package]]
424
424
  name = "codex_native"
425
- version = "0.2.14"
425
+ version = "0.2.15"
426
426
  dependencies = [
427
427
  "anyhow",
428
428
  "clap",
429
429
  "codex-core",
430
430
  "codex-protocol",
431
+ "dotenvy",
431
432
  "openssl-sys",
432
433
  "pathdiff",
433
434
  "pyo3",
@@ -601,6 +602,12 @@ dependencies = [
601
602
  "syn",
602
603
  ]
603
604
 
605
+ [[package]]
606
+ name = "dotenvy"
607
+ version = "0.15.7"
608
+ source = "registry+https://github.com/rust-lang/crates.io-index"
609
+ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
610
+
604
611
  [[package]]
605
612
  name = "downcast-rs"
606
613
  version = "1.2.1"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "codex_native"
3
- version = "0.2.14"
3
+ version = "0.2.15"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -22,6 +22,7 @@ tracing = "0.1"
22
22
  clap = { version = "4", features = ["derive"] }
23
23
  which = "6"
24
24
  pathdiff = "0.2"
25
+ dotenvy = "0.15.7"
25
26
 
26
27
  # Upstream Codex crates from the monorepo (use git deps; pin to main for now)
27
28
  # Pin to a specific commit of the upstream Codex monorepo to avoid breaking API changes
@@ -1,7 +1,7 @@
1
1
  use anyhow::{Context, Result};
2
2
  use codex_core::config::{find_codex_home, Config, ConfigOverrides, ConfigToml};
3
3
  use codex_core::protocol::{EventMsg, InputItem};
4
- use codex_core::{AuthManager, ConversationManager};
4
+ use codex_core::{AuthManager, CodexAuth, ConversationManager};
5
5
  // use of SandboxMode is handled within core::config; not needed here
6
6
  use pyo3::prelude::*;
7
7
  use pyo3::types::{PyDict, PyFloat, PyList, PyModule, PyString};
@@ -38,9 +38,14 @@ fn run_exec_collect(
38
38
  }
39
39
 
40
40
  async fn run_exec_impl(prompt: String, config: Config) -> Result<Vec<JsonValue>> {
41
- let conversation_manager = ConversationManager::new(AuthManager::shared(
42
- config.codex_home.clone(),
43
- ));
41
+ let conversation_manager = match std::env::var("OPENAI_API_KEY") {
42
+ Ok(val) if !val.trim().is_empty() => {
43
+ ConversationManager::with_auth(CodexAuth::from_api_key(&val))
44
+ }
45
+ _ => ConversationManager::new(AuthManager::shared(
46
+ config.codex_home.clone(),
47
+ )),
48
+ };
44
49
  let new_conv = conversation_manager.new_conversation(config).await?;
45
50
  let conversation = new_conv.conversation.clone();
46
51
 
@@ -173,6 +178,10 @@ fn build_config(
173
178
  overrides: Option<Bound<'_, PyDict>>,
174
179
  load_default_config: bool,
175
180
  ) -> Result<Config> {
181
+ // Match CLI behavior: import env vars from ~/.codex/.env (if present)
182
+ // before reading config/auth so OPENAI_API_KEY and friends are visible.
183
+ // Security: filter out CODEX_* variables just like the CLI does.
184
+ load_dotenv();
176
185
  let mut overrides_struct = ConfigOverrides::default();
177
186
  let mut cli_overrides: Vec<(String, TomlValue)> = Vec::new();
178
187
 
@@ -291,6 +300,33 @@ fn build_config(
291
300
  }
292
301
  }
293
302
 
303
+ const ILLEGAL_ENV_VAR_PREFIX: &str = "CODEX_";
304
+
305
+ /// Load env vars from ~/.codex/.env, filtering out any keys that start with
306
+ /// CODEX_ (reserved for internal use). This mirrors the behavior in the
307
+ /// `codex-arg0` crate used by the CLI so python users get the same DX.
308
+ fn load_dotenv() {
309
+ if let Ok(codex_home) = find_codex_home() {
310
+ let env_path = codex_home.join(".env");
311
+ if let Ok(iter) = dotenvy::from_path_iter(env_path) {
312
+ set_filtered(iter);
313
+ }
314
+ }
315
+ }
316
+
317
+ /// Helper to set vars from a dotenvy iterator while filtering out `CODEX_` keys.
318
+ fn set_filtered<I>(iter: I)
319
+ where
320
+ I: IntoIterator<Item = Result<(String, String), dotenvy::Error>>,
321
+ {
322
+ for (key, value) in iter.into_iter().flatten() {
323
+ if !key.to_ascii_uppercase().starts_with(ILLEGAL_ENV_VAR_PREFIX) {
324
+ // Safe to modify env here – we do it up front before we spawn runtimes/threads.
325
+ unsafe { std::env::set_var(&key, &value) };
326
+ }
327
+ }
328
+ }
329
+
294
330
  /// Convert a Python object into a TOML value. Returns Ok(None) for `None`.
295
331
  fn py_to_toml_value(obj: Bound<'_, PyAny>) -> Result<Option<TomlValue>> {
296
332
  use pyo3::types::{PyBool, PyDict, PyFloat, PyInt, PyList, PyString};
@@ -397,9 +433,14 @@ fn insert_parts(current: &mut TomlTable, parts: &[&str], val: TomlValue) {
397
433
  fn run_exec_stream_impl(prompt: String, config: Config, tx: mpsc::Sender<JsonValue>) -> Result<()> {
398
434
  let rt = tokio::runtime::Runtime::new()?;
399
435
  rt.block_on(async move {
400
- let conversation_manager = ConversationManager::new(AuthManager::shared(
401
- config.codex_home.clone(),
402
- ));
436
+ let conversation_manager = match std::env::var("OPENAI_API_KEY") {
437
+ Ok(val) if !val.trim().is_empty() => {
438
+ ConversationManager::with_auth(CodexAuth::from_api_key(&val))
439
+ }
440
+ _ => ConversationManager::new(AuthManager::shared(
441
+ config.codex_home.clone(),
442
+ )),
443
+ };
403
444
  let new_conv = conversation_manager.new_conversation(config).await?;
404
445
  let conversation = new_conv.conversation.clone();
405
446
 
File without changes
File without changes