Skip to content

Commit 2c5f343

Browse files
committed
Codex: resolve relative config roots and respect table overrides
Follow-up to 061e7c9 (regression from 95caac5). Relative writable_roots in config.toml resolve against CODEX_HOME, as Codex does. A whole-table -c sandbox_workspace_write={...} override is left alone instead of being replaced by the dotted roots override.
1 parent 061e7c9 commit 2c5f343

1 file changed

Lines changed: 51 additions & 9 deletions

File tree

‎src/tools/codex_preprocessing.rs‎

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use crate::paths;
1111
/// ignored outside workspace-write, whereas `--add-dir` is fatal at startup
1212
/// when the effective sandbox is read-only (e.g. workspace-write on Windows
1313
/// without the Windows sandbox). The override replaces the whole list, so it
14-
/// carries the user's roots from the CLI or `config.toml`.
14+
/// carries the user's roots from the CLI or `$CODEX_HOME/config.toml`; roots
15+
/// set only in project or system config are dropped. `[permissions]` profiles
16+
/// ignore these legacy roots, so there the hcom dir is not made writable.
1517
pub fn ensure_hcom_writable(tokens: &[String], codex_home: Option<&Path>) -> Vec<String> {
1618
const ROOTS_KEY: &str = "sandbox_workspace_write.writable_roots";
1719
let end = tokens
@@ -40,7 +42,12 @@ pub fn ensure_hcom_writable(tokens: &[String], codex_home: Option<&Path>) -> Vec
4042
.or_else(|| token.strip_prefix("-c="))
4143
.or_else(|| token.strip_prefix("-c"))
4244
};
43-
if let Some((key, value)) = raw.and_then(|raw| raw.split_once('='))
45+
let override_kv = raw.and_then(|raw| raw.split_once('='));
46+
// A whole-table override would be replaced by the dotted one below.
47+
if override_kv.is_some_and(|(key, _)| key.trim() == "sandbox_workspace_write") {
48+
return tokens.to_vec();
49+
}
50+
if let Some((key, value)) = override_kv
4451
&& key.trim() == ROOTS_KEY
4552
{
4653
match parse_roots(value) {
@@ -86,10 +93,17 @@ fn string_array(value: &toml::Value) -> Option<Vec<String>> {
8693
fn config_writable_roots(codex_home: &Path) -> Option<Vec<String>> {
8794
let text = std::fs::read_to_string(codex_home.join("config.toml")).ok()?;
8895
let table: toml::Table = toml::from_str(&text).ok()?;
89-
string_array(
96+
let roots = string_array(
9097
table
9198
.get("sandbox_workspace_write")?
9299
.get("writable_roots")?,
100+
)?;
101+
// Codex resolves these against the config file; a CLI override would not.
102+
Some(
103+
roots
104+
.into_iter()
105+
.map(|root| codex_home.join(root).to_string_lossy().into_owned())
106+
.collect(),
93107
)
94108
}
95109

@@ -390,17 +404,43 @@ mod tests {
390404
fn test_ensure_hcom_writable_keeps_config_roots() {
391405
init_config();
392406
let codex_home = tempfile::tempdir().unwrap();
407+
let root = codex_home
408+
.path()
409+
.join("work")
410+
.to_string_lossy()
411+
.into_owned();
412+
let roots = toml::Value::Array(vec![toml::Value::String(root.clone())]);
393413
std::fs::write(
394414
codex_home.path().join("config.toml"),
395-
"[sandbox_workspace_write]\nwritable_roots = [\"C:\\\\work\"]\n",
415+
format!("[sandbox_workspace_write]\nwritable_roots = {roots}\n"),
396416
)
397417
.unwrap();
398418
let result = ensure_hcom_writable(&[], Some(codex_home.path()));
399419
let hcom_dir = paths::hcom_dir().to_string_lossy().to_string();
400-
assert_eq!(
401-
injected_roots(&result),
402-
vec![r"C:\work".to_string(), hcom_dir]
403-
);
420+
assert_eq!(injected_roots(&result), vec![root, hcom_dir]);
421+
}
422+
423+
#[test]
424+
#[serial]
425+
fn test_ensure_hcom_writable_resolves_relative_config_roots() {
426+
init_config();
427+
let codex_home = tempfile::tempdir().unwrap();
428+
std::fs::write(
429+
codex_home.path().join("config.toml"),
430+
"[sandbox_workspace_write]\nwritable_roots = [\"rel\"]\n",
431+
)
432+
.unwrap();
433+
let result = ensure_hcom_writable(&[], Some(codex_home.path()));
434+
let expected = codex_home.path().join("rel").to_string_lossy().into_owned();
435+
assert_eq!(injected_roots(&result)[0], expected);
436+
}
437+
438+
#[test]
439+
#[serial]
440+
fn test_ensure_hcom_writable_leaves_table_override() {
441+
init_config();
442+
let tokens = s(&["-c", r#"sandbox_workspace_write={writable_roots=["/x"]}"#]);
443+
assert_eq!(ensure_hcom_writable(&tokens, None), tokens);
404444
}
405445

406446
#[test]
@@ -644,7 +684,9 @@ mod tests {
644684
assert!(
645685
!result.contains(&"sandbox_workspace_write.network_access=true".to_string())
646686
);
647-
assert!(has_hcom_writable_dir(&result));
687+
// A whole-table override owns writable_roots too.
688+
let table = args.iter().any(|arg| arg.contains("={"));
689+
assert_eq!(has_hcom_writable_dir(&result), !table);
648690
}
649691
}
650692
}

0 commit comments

Comments
 (0)