From 11fab432be5a873fea97688ef67e8af896f5ea8c Mon Sep 17 00:00:00 2001 From: "Adam Perry @ OpenAI" Date: Mon, 22 Jun 2026 17:02:33 -0700 Subject: [PATCH] path-uri: clarify host-native path conversion (#29501) ## Why Downstream refactors are producing confusing code with this functionality having a very generic name. Encoding the specific conversion approach in the method name makes it clearer. ## What Rename `PathUri::from_path` to `PathUri::from_host_native_path` and update its Rust call sites. --- codex-rs/apply-patch/src/invocation.rs | 28 +++---- codex-rs/apply-patch/src/lib.rs | 38 +++++----- codex-rs/apply-patch/src/parser.rs | 2 +- codex-rs/core/src/apply_patch_tests.rs | 2 +- codex-rs/core/src/tools/events.rs | 6 +- codex-rs/core/src/turn_diff_tracker_tests.rs | 2 +- codex-rs/core/tests/common/test_codex.rs | 8 +- codex-rs/core/tests/suite/agents_md.rs | 35 ++++----- codex-rs/core/tests/suite/apply_patch_cli.rs | 12 +-- codex-rs/core/tests/suite/network_approval.rs | 2 +- codex-rs/core/tests/suite/remote_env.rs | 47 ++++++------ codex-rs/core/tests/suite/rmcp_client.rs | 2 +- codex-rs/core/tests/suite/skills.rs | 4 +- codex-rs/core/tests/suite/unified_exec.rs | 8 +- codex-rs/core/tests/suite/view_image.rs | 10 +-- codex-rs/exec-server/src/environment.rs | 12 ++- codex-rs/exec-server/src/fs_helper.rs | 5 +- codex-rs/exec-server/src/local_process.rs | 3 +- codex-rs/exec-server/src/protocol.rs | 4 +- .../src/server/file_system_handler.rs | 7 +- .../exec-server/src/server/handler/tests.rs | 3 +- codex-rs/exec-server/src/server/processor.rs | 3 +- codex-rs/exec-server/tests/exec_process.rs | 24 +++--- codex-rs/exec-server/tests/file_stream.rs | 26 ++++--- .../exec-server/tests/file_system/shared.rs | 76 ++++++++++++------- .../exec-server/tests/file_system_unix.rs | 59 ++++++++------ .../exec-server/tests/file_system_windows.rs | 9 ++- codex-rs/exec-server/tests/relay.rs | 4 +- .../rmcp-client/src/stdio_server_launcher.rs | 2 +- codex-rs/utils/path-uri/src/lib.rs | 2 +- codex-rs/utils/path-uri/src/tests.rs | 3 +- 31 files changed, 250 insertions(+), 198 deletions(-) diff --git a/codex-rs/apply-patch/src/invocation.rs b/codex-rs/apply-patch/src/invocation.rs index de9c735c5..8ac2084e4 100644 --- a/codex-rs/apply-patch/src/invocation.rs +++ b/codex-rs/apply-patch/src/invocation.rs @@ -500,7 +500,7 @@ mod tests { assert_matches!( maybe_parse_apply_patch_verified( &args, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), LOCAL_FS.as_ref(), /*sandbox*/ None, ) @@ -517,7 +517,7 @@ mod tests { assert_matches!( maybe_parse_apply_patch_verified( &args, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), LOCAL_FS.as_ref(), /*sandbox*/ None, ) @@ -752,7 +752,7 @@ PATCH"#, _ => panic!("Expected a single UpdateFile hunk"), }; - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks(&path_uri, chunks, LOCAL_FS.as_ref(), /*sandbox*/ None) .await @@ -792,7 +792,7 @@ PATCH"#, _ => panic!("Expected a single UpdateFile hunk"), }; - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks(&path_uri, chunks, LOCAL_FS.as_ref(), /*sandbox*/ None) .await @@ -832,7 +832,7 @@ PATCH"#, let result = maybe_parse_apply_patch_verified( &argv, - &PathUri::from_path(session_dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(session_dir.path()).expect("absolute test path"), LOCAL_FS.as_ref(), /*sandbox*/ None, ) @@ -844,7 +844,7 @@ PATCH"#, result, MaybeApplyPatchVerified::Body(ApplyPatchAction { changes: HashMap::from([( - PathUri::from_path(session_dir.path().join(relative_path)) + PathUri::from_host_native_path(session_dir.path().join(relative_path)) .expect("absolute test path"), ApplyPatchFileChange::Update { unified_diff: r#"@@ -1 +1 @@ @@ -857,7 +857,8 @@ PATCH"#, }, )]), patch: argv[1].clone(), - cwd: PathUri::from_path(session_dir.path()).expect("absolute test path"), + cwd: PathUri::from_host_native_path(session_dir.path()) + .expect("absolute test path"), }) ); } @@ -887,7 +888,7 @@ PATCH"#, let result = maybe_parse_apply_patch_verified( &argv, - &PathUri::from_path(session_dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(session_dir.path()).expect("absolute test path"), LOCAL_FS.as_ref(), /*sandbox*/ None, ) @@ -902,8 +903,8 @@ PATCH"#, worktree_dir.as_path() ); - let source_path = - PathUri::from_path(worktree_dir.join(source_name)).expect("absolute test path"); + let source_path = PathUri::from_host_native_path(worktree_dir.join(source_name)) + .expect("absolute test path"); let change = action .changes() .get(&source_path) @@ -912,7 +913,8 @@ PATCH"#, match change { ApplyPatchFileChange::Update { move_path, .. } => { let expected_move_path = - PathUri::from_path(worktree_dir.join(dest_name)).expect("absolute test path"); + PathUri::from_host_native_path(worktree_dir.join(dest_name)) + .expect("absolute test path"); assert_eq!(move_path.as_ref(), Some(&expected_move_path)); } other => panic!("expected update change, got {other:?}"), @@ -923,7 +925,7 @@ PATCH"#, async fn test_unreadable_destinations_still_verify() { let session_dir = tempdir().unwrap(); fs::write(session_dir.path().join("binary.dat"), [0xff, 0xfe, 0xfd]).unwrap(); - let cwd = PathUri::from_path(session_dir.path()).expect("absolute test path"); + let cwd = PathUri::from_host_native_path(session_dir.path()).expect("absolute test path"); let add_argv = vec![ "apply_patch".to_string(), "*** Begin Patch\n*** Add File: binary.dat\n+text\n*** End Patch".to_string(), @@ -966,7 +968,7 @@ PATCH"#, let result = maybe_parse_apply_patch_verified( &argv, - &PathUri::from_path(session_dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(session_dir.path()).expect("absolute test path"), LOCAL_FS.as_ref(), /*sandbox*/ None, ) diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index f27610d6c..81ad1f078 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -913,7 +913,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -937,7 +937,7 @@ mod tests { #[tokio::test] async fn test_apply_patch_hunks_accept_relative_and_absolute_paths() { let dir = tempdir().unwrap(); - let cwd = PathUri::from_path(dir.path()).expect("absolute test path"); + let cwd = PathUri::from_host_native_path(dir.path()).expect("absolute test path"); let relative_add = dir.path().join("relative-add.txt"); let absolute_add = dir.path().join("absolute-add.txt"); let relative_delete = dir.path().join("relative-delete.txt"); @@ -1016,7 +1016,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1052,7 +1052,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1092,7 +1092,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1136,7 +1136,7 @@ mod tests { let mut stderr = Vec::new(); let failure = apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1196,7 +1196,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1254,7 +1254,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1298,7 +1298,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1341,7 +1341,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1391,7 +1391,7 @@ mod tests { [Hunk::UpdateFile { chunks, .. }] => chunks, _ => panic!("Expected a single UpdateFile hunk"), }; - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks( &path_uri, update_file_chunks, @@ -1439,7 +1439,7 @@ mod tests { _ => panic!("Expected a single UpdateFile hunk"), }; - let resolved_path = PathUri::from_path(&path).expect("absolute test path"); + let resolved_path = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks( &resolved_path, chunks, @@ -1485,7 +1485,7 @@ mod tests { _ => panic!("Expected a single UpdateFile hunk"), }; - let resolved_path = PathUri::from_path(&path).expect("absolute test path"); + let resolved_path = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks( &resolved_path, chunks, @@ -1529,7 +1529,7 @@ mod tests { _ => panic!("Expected a single UpdateFile hunk"), }; - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks(&path_uri, chunks, LOCAL_FS.as_ref(), /*sandbox*/ None) .await @@ -1580,7 +1580,7 @@ mod tests { _ => panic!("Expected a single UpdateFile hunk"), }; - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let diff = unified_diff_from_chunks(&path_uri, chunks, LOCAL_FS.as_ref(), /*sandbox*/ None) .await @@ -1610,7 +1610,7 @@ mod tests { let mut stderr = Vec::new(); apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1648,7 +1648,7 @@ g let mut stderr = Vec::new(); let result = apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), @@ -1667,7 +1667,7 @@ g let dir = tempdir().unwrap(); let path = dir.path().join("binary.dat"); fs::write(dir.path().join("source.txt"), "before\n").unwrap(); - let cwd = PathUri::from_path(dir.path()).expect("absolute test path"); + let cwd = PathUri::from_host_native_path(dir.path()).expect("absolute test path"); for patch in [ wrap_patch("*** Add File: binary.dat\n+text"), @@ -1705,7 +1705,7 @@ g let mut stderr = Vec::new(); let delta = apply_patch( &patch, - &PathUri::from_path(dir.path()).expect("absolute test path"), + &PathUri::from_host_native_path(dir.path()).expect("absolute test path"), &mut stdout, &mut stderr, LOCAL_FS.as_ref(), diff --git a/codex-rs/apply-patch/src/parser.rs b/codex-rs/apply-patch/src/parser.rs index 04e0053a3..ec2d97c47 100644 --- a/codex-rs/apply-patch/src/parser.rs +++ b/codex-rs/apply-patch/src/parser.rs @@ -480,7 +480,7 @@ fn test_parse_patch_accepts_relative_and_absolute_hunk_paths() { #[test] fn test_hunk_resolve_path_accepts_relative_and_absolute_paths() { let cwd_dir = tempfile::tempdir().unwrap(); - let cwd = PathUri::from_path(cwd_dir.path()).unwrap(); + let cwd = PathUri::from_host_native_path(cwd_dir.path()).unwrap(); let absolute_dir = tempfile::tempdir().unwrap(); let absolute_add = absolute_dir.path().join("absolute-add.py").abs(); let absolute_delete = absolute_dir.path().join("absolute-delete.py").abs(); diff --git a/codex-rs/core/src/apply_patch_tests.rs b/codex-rs/core/src/apply_patch_tests.rs index dfc48f8a6..d845ca92b 100644 --- a/codex-rs/core/src/apply_patch_tests.rs +++ b/codex-rs/core/src/apply_patch_tests.rs @@ -8,7 +8,7 @@ use tempfile::tempdir; fn convert_apply_patch_maps_add_variant() { let tmp = tempdir().expect("tmp"); let path = tmp.path().join("a.txt"); - let path_uri = PathUri::from_path(&path).expect("absolute test path"); + let path_uri = PathUri::from_host_native_path(&path).expect("absolute test path"); let action = ApplyPatchAction::new_add_for_test(&path_uri, "hello".to_string()); let got = convert_apply_patch_to_protocol(&action); diff --git a/codex-rs/core/src/tools/events.rs b/codex-rs/core/src/tools/events.rs index 34c41d835..757125a03 100644 --- a/codex-rs/core/src/tools/events.rs +++ b/codex-rs/core/src/tools/events.rs @@ -645,7 +645,7 @@ mod tests { make_session_and_context_with_dynamic_tools_and_rx(Vec::new()).await; let tracker = Arc::new(Mutex::new(TurnDiffTracker::new())); let dir = tempdir().expect("tempdir"); - let cwd = PathUri::from_path(dir.path()).expect("absolute cwd"); + let cwd = PathUri::from_host_native_path(dir.path()).expect("absolute cwd"); let mut stdout = Vec::new(); let mut stderr = Vec::new(); let delta = codex_apply_patch::apply_patch( @@ -729,7 +729,7 @@ mod tests { make_session_and_context_with_dynamic_tools_and_rx(Vec::new()).await; let tracker = Arc::new(Mutex::new(TurnDiffTracker::new())); let dir = tempdir().expect("tempdir"); - let cwd = PathUri::from_path(dir.path()).expect("absolute cwd"); + let cwd = PathUri::from_host_native_path(dir.path()).expect("absolute cwd"); for patch in [ "*** Begin Patch\n*** Add File: a.txt\n+one\n*** End Patch", @@ -782,7 +782,7 @@ mod tests { make_session_and_context_with_dynamic_tools_and_rx(Vec::new()).await; let tracker = Arc::new(Mutex::new(TurnDiffTracker::new())); let dir = tempdir().expect("tempdir"); - let cwd = PathUri::from_path(dir.path()).expect("absolute cwd"); + let cwd = PathUri::from_host_native_path(dir.path()).expect("absolute cwd"); let mut stdout = Vec::new(); let mut stderr = Vec::new(); let delta = codex_apply_patch::apply_patch( diff --git a/codex-rs/core/src/turn_diff_tracker_tests.rs b/codex-rs/core/src/turn_diff_tracker_tests.rs index 9c58de913..5f984a2c4 100644 --- a/codex-rs/core/src/turn_diff_tracker_tests.rs +++ b/codex-rs/core/src/turn_diff_tracker_tests.rs @@ -18,7 +18,7 @@ fn git_blob_sha1_hex(data: &str) -> String { } async fn apply_verified_patch(root: &Path, patch: &str) -> AppliedPatchDelta { - let cwd = PathUri::from_path(root).expect("absolute tempdir path"); + let cwd = PathUri::from_host_native_path(root).expect("absolute tempdir path"); let argv = vec!["apply_patch".to_string(), patch.to_string()]; match codex_apply_patch::maybe_parse_apply_patch_verified( &argv, diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index d843e0097..8784a4d4a 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -998,7 +998,7 @@ impl TestCodexHarness { ) -> Result<()> { let abs_path = self.path_abs(rel); if let Some(parent) = abs_path.parent() { - let parent_uri = PathUri::from_path(&parent)?; + let parent_uri = PathUri::from_host_native_path(&parent)?; self.test .fs() .create_directory( @@ -1008,7 +1008,7 @@ impl TestCodexHarness { ) .await?; } - let abs_path_uri = PathUri::from_path(&abs_path)?; + let abs_path_uri = PathUri::from_host_native_path(&abs_path)?; self.test .fs() .write_file( @@ -1022,7 +1022,7 @@ impl TestCodexHarness { pub async fn read_file_text(&self, rel: impl AsRef) -> Result { let path = self.path_abs(rel); - let path_uri = PathUri::from_path(&path)?; + let path_uri = PathUri::from_host_native_path(&path)?; Ok(self .test .fs() @@ -1032,7 +1032,7 @@ impl TestCodexHarness { pub async fn create_dir_all(&self, rel: impl AsRef) -> Result<()> { let path = self.path_abs(rel); - let path_uri = PathUri::from_path(&path)?; + let path_uri = PathUri::from_host_native_path(&path)?; self.test .fs() .create_directory( diff --git a/codex-rs/core/tests/suite/agents_md.rs b/codex-rs/core/tests/suite/agents_md.rs index 498a66607..9094461aa 100644 --- a/codex-rs/core/tests/suite/agents_md.rs +++ b/codex-rs/core/tests/suite/agents_md.rs @@ -141,8 +141,8 @@ async fn agents_override_is_preferred_over_agents_md() -> Result<()> { agents_instructions(test_codex().with_workspace_setup(|cwd, fs| async move { let agents_md = cwd.join("AGENTS.md"); let override_md = cwd.join("AGENTS.override.md"); - let agents_md_uri = PathUri::from_path(&agents_md)?; - let override_md_uri = PathUri::from_path(&override_md)?; + let agents_md_uri = PathUri::from_host_native_path(&agents_md)?; + let override_md_uri = PathUri::from_host_native_path(&override_md)?; fs.write_file(&agents_md_uri, b"base doc".to_vec(), /*sandbox*/ None) .await?; fs.write_file( @@ -177,8 +177,8 @@ async fn configured_fallback_is_used_when_agents_candidate_is_directory() -> Res .with_workspace_setup(|cwd, fs| async move { let agents_dir = cwd.join("AGENTS.md"); let fallback = cwd.join("WORKFLOW.md"); - let agents_dir_uri = PathUri::from_path(&agents_dir)?; - let fallback_uri = PathUri::from_path(&fallback)?; + let agents_dir_uri = PathUri::from_host_native_path(&agents_dir)?; + let fallback_uri = PathUri::from_host_native_path(&fallback)?; fs.create_directory( &agents_dir_uri, CreateDirectoryOptions { recursive: true }, @@ -220,10 +220,10 @@ async fn agents_docs_are_concatenated_from_project_root_to_cwd() -> Result<()> { let root_agents = root.join("AGENTS.md"); let git_marker = root.join(".git"); let nested_agents = nested.join("AGENTS.md"); - let nested_uri = PathUri::from_path(&nested)?; - let root_agents_uri = PathUri::from_path(&root_agents)?; - let git_marker_uri = PathUri::from_path(&git_marker)?; - let nested_agents_uri = PathUri::from_path(&nested_agents)?; + let nested_uri = PathUri::from_host_native_path(&nested)?; + let root_agents_uri = PathUri::from_host_native_path(&root_agents)?; + let git_marker_uri = PathUri::from_host_native_path(&git_marker)?; + let nested_agents_uri = PathUri::from_host_native_path(&nested_agents)?; fs.create_directory( &nested_uri, @@ -363,7 +363,7 @@ async fn selected_environment_sources_match_model_visible_instructions() -> Resu let mut builder = test_codex() .with_home(home) .with_workspace_setup(|cwd, fs| async move { - let agents_md_uri = PathUri::from_path(cwd.join("AGENTS.md"))?; + let agents_md_uri = PathUri::from_host_native_path(cwd.join("AGENTS.md"))?; fs.write_file( &agents_md_uri, b"project doc".to_vec(), @@ -420,7 +420,8 @@ async fn loads_user_instructions_without_a_primary_environment() -> Result<()> { .with_home(Arc::clone(&home)) .with_user_instructions_provider(provider.clone()) .with_workspace_setup(|cwd, fs| async move { - let project_agents_uri = PathUri::from_path(cwd.join(GLOBAL_AGENTS_FILENAME))?; + let project_agents_uri = + PathUri::from_host_native_path(cwd.join(GLOBAL_AGENTS_FILENAME))?; fs.write_file( &project_agents_uri, PROJECT_INSTRUCTIONS.as_bytes().to_vec(), @@ -505,7 +506,7 @@ async fn fresh_thread_composes_global_before_project_and_reports_sources() -> Re let mut builder = test_codex() .with_home(Arc::clone(&home)) .with_workspace_setup(|cwd, fs| async move { - let agents_md_uri = PathUri::from_path(cwd.join("AGENTS.md"))?; + let agents_md_uri = PathUri::from_host_native_path(cwd.join("AGENTS.md"))?; fs.write_file( &agents_md_uri, PROJECT_INSTRUCTIONS.as_bytes().to_vec(), @@ -534,7 +535,7 @@ async fn fresh_thread_composes_global_before_project_and_reports_sources() -> Re )?; test.fs() .write_file( - &PathUri::from_path(&project_source)?, + &PathUri::from_host_native_path(&project_source)?, NEW_PROJECT_INSTRUCTIONS.as_bytes().to_vec(), /*sandbox*/ None, ) @@ -631,7 +632,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho .with_user_instructions_provider(provider.clone()) .with_workspace_setup(|cwd, fs| async move { fs.write_file( - &PathUri::from_path(cwd.join(GLOBAL_AGENTS_FILENAME))?, + &PathUri::from_host_native_path(cwd.join(GLOBAL_AGENTS_FILENAME))?, b"remote project instructions".to_vec(), /*sandbox*/ None, ) @@ -658,7 +659,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho }, TurnEnvironmentSelection { environment_id: LOCAL_ENVIRONMENT_ID.to_string(), - cwd: PathUri::from_path(local_root.path())?, + cwd: PathUri::from_host_native_path(local_root.path())?, }, ], thread_extension_init: Default::default(), @@ -671,7 +672,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho vec![ PathUri::from_abs_path(&global_source), PathUri::from_abs_path(&remote_source), - PathUri::from_path(&local_source)?, + PathUri::from_host_native_path(&local_source)?, ] ); @@ -684,7 +685,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho )?; test.fs() .write_file( - &PathUri::from_path(test.config.cwd.join(GLOBAL_AGENTS_OVERRIDE_FILENAME))?, + &PathUri::from_host_native_path(test.config.cwd.join(GLOBAL_AGENTS_OVERRIDE_FILENAME))?, b"new remote project instructions".to_vec(), /*sandbox*/ None, ) @@ -712,7 +713,7 @@ async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapsho vec![ PathUri::from_abs_path(&global_source), PathUri::from_abs_path(&remote_source), - PathUri::from_path(&local_source)?, + PathUri::from_host_native_path(&local_source)?, ] ); diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index 2527e4cb9..c348dd6f9 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -1329,7 +1329,7 @@ async fn apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nest config.cwd = config.cwd.join("subdir"); }) .with_workspace_setup(|cwd, fs| async move { - let cwd_uri = PathUri::from_path(&cwd)?; + let cwd_uri = PathUri::from_host_native_path(&cwd)?; fs.create_directory( &cwd_uri, CreateDirectoryOptions { recursive: true }, @@ -1337,8 +1337,8 @@ async fn apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nest ) .await?; let repo_root = cwd.parent().expect("nested cwd should have parent"); - let git_uri = PathUri::from_path(repo_root.join(".git"))?; - let repo_file_uri = PathUri::from_path(repo_root.join("repo.txt"))?; + let git_uri = PathUri::from_host_native_path(repo_root.join(".git"))?; + let repo_file_uri = PathUri::from_host_native_path(repo_root.join("repo.txt"))?; fs.write_file( &git_uri, b"gitdir: /tmp/fake-worktree\n".to_vec(), @@ -1586,7 +1586,7 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let shared_cwd_uri = PathUri::from_path(&shared_cwd)?; + let shared_cwd_uri = PathUri::from_host_native_path(&shared_cwd)?; let _ = fs::remove_dir_all(shared_cwd.as_path()); test.fs() .remove( @@ -1689,7 +1689,7 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re assert_eq!( test.fs() .read_file_text( - &PathUri::from_path(shared_cwd.join(file_name))?, + &PathUri::from_host_native_path(shared_cwd.join(file_name))?, /*sandbox*/ None, ) .await?, @@ -1859,7 +1859,7 @@ async fn apply_patch_clears_aggregated_diff_after_inexact_delta() -> Result<()> let harness = apply_patch_harness_with(|builder| { builder.with_workspace_setup(|cwd, fs| async move { - let binary_path_uri = PathUri::from_path(cwd.join("binary.dat"))?; + let binary_path_uri = PathUri::from_host_native_path(cwd.join("binary.dat"))?; fs.write_file( &binary_path_uri, vec![0xff, 0xfe, 0xfd], diff --git a/codex-rs/core/tests/suite/network_approval.rs b/codex-rs/core/tests/suite/network_approval.rs index 14db16c60..f476fb728 100644 --- a/codex-rs/core/tests/suite/network_approval.rs +++ b/codex-rs/core/tests/suite/network_approval.rs @@ -74,7 +74,7 @@ async fn approved_network_host_for_one_environment_still_prompts_in_another() -> SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; test.fs() .create_directory( &remote_cwd_uri, diff --git a/codex-rs/core/tests/suite/remote_env.rs b/codex-rs/core/tests/suite/remote_env.rs index fa805fa44..2df03f92a 100644 --- a/codex-rs/core/tests/suite/remote_env.rs +++ b/codex-rs/core/tests/suite/remote_env.rs @@ -173,7 +173,7 @@ async fn remote_test_env_can_connect_and_use_filesystem() -> Result<()> { let file_system = test_env.environment().get_filesystem(); let file_path_abs = test_env.cwd().join("remote-test-env-ok"); - let file_path_uri = PathUri::from_path(&file_path_abs)?; + let file_path_uri = PathUri::from_host_native_path(&file_path_abs)?; let payload = b"remote-test-env-ok".to_vec(); file_system @@ -329,8 +329,8 @@ async fn remote_sandbox_denial_requests_approval_and_retries() -> Result<()> { let nonce = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis(); let remote_cwd = PathBuf::from(format!("/tmp/codex-remote-denial-cwd-{nonce}")).abs(); let target_path = PathBuf::from(format!("/tmp/codex-remote-denial-target-{nonce}")).abs(); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; - let target_uri = PathUri::from_path(&target_path)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; + let target_uri = PathUri::from_host_native_path(&target_path)?; test.fs() .create_directory( &remote_cwd_uri, @@ -801,8 +801,8 @@ async fn exec_command_routes_to_selected_remote_environment() -> Result<()> { )) .abs(); let remote_marker_name = "marker.txt"; - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; - let remote_marker_uri = PathUri::from_path(remote_cwd.join(remote_marker_name))?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; + let remote_marker_uri = PathUri::from_host_native_path(remote_cwd.join(remote_marker_name))?; test.fs() .create_directory( &remote_cwd_uri, @@ -900,7 +900,7 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result let local_write_root = local_cwd.path().join(relative_write_root); let local_target_path = local_cwd.path().join(relative_target_path); fs::create_dir(&local_write_root)?; - let remote_write_root_uri = PathUri::from_path(&remote_write_root)?; + let remote_write_root_uri = PathUri::from_host_native_path(&remote_write_root)?; test.fs() .create_directory( &remote_write_root_uri, @@ -1040,7 +1040,7 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result assert_eq!( test.fs() .read_file_text( - &PathUri::from_path(&remote_target_path)?, + &PathUri::from_host_native_path(&remote_target_path)?, /*sandbox*/ None, ) .await?, @@ -1084,7 +1084,7 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result< SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; test.fs() .create_directory( &remote_cwd_uri, @@ -1129,7 +1129,7 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result< let remote_contents = test .fs() .read_file_text( - &PathUri::from_path(remote_cwd.join(file_name))?, + &PathUri::from_host_native_path(remote_cwd.join(file_name))?, /*sandbox*/ None, ) .await?; @@ -1174,7 +1174,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> { SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; test.fs() .create_directory( &remote_cwd_uri, @@ -1188,7 +1188,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> { SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let target_path_uri = PathUri::from_path(&target_path)?; + let target_path_uri = PathUri::from_host_native_path(&target_path)?; let _ = fs::remove_file(&target_path); test.fs() .remove( @@ -1362,7 +1362,7 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis() )) .abs(); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; test.fs() .create_directory( &remote_cwd_uri, @@ -1417,7 +1417,7 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm let remote_contents = test .fs() .read_file_text( - &PathUri::from_path(remote_cwd.join(file_name))?, + &PathUri::from_host_native_path(remote_cwd.join(file_name))?, /*sandbox*/ None, ) .await?; @@ -1455,8 +1455,8 @@ async fn remote_test_env_sandboxed_read_allows_readable_root() -> Result<()> { let allowed_dir = PathBuf::from(format!("/tmp/codex-remote-readable-{}", std::process::id())); let file_path = allowed_dir.join("note.txt"); - let allowed_dir_uri = PathUri::from_path(&allowed_dir)?; - let file_path_uri = PathUri::from_path(&file_path)?; + let allowed_dir_uri = PathUri::from_host_native_path(&allowed_dir)?; + let file_path_uri = PathUri::from_host_native_path(&file_path)?; file_system .create_directory( &allowed_dir_uri, @@ -1516,7 +1516,7 @@ async fn remote_test_env_sandboxed_read_rejects_symlink_parent_dotdot_escape() - ))?; let requested_path = - PathUri::from_path(allowed_dir.join("link").join("..").join("secret.txt"))?; + PathUri::from_host_native_path(allowed_dir.join("link").join("..").join("secret.txt"))?; let sandbox = read_only_sandbox(allowed_dir.clone()); let error = match file_system.read_file(&requested_path, Some(&sandbox)).await { Ok(_) => anyhow::bail!("read should fail after path normalization"), @@ -1564,7 +1564,7 @@ async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> { let sandbox = workspace_write_sandbox(allowed_dir.clone()); file_system .remove( - &PathUri::from_path(&symlink_path)?, + &PathUri::from_host_native_path(&symlink_path)?, RemoveOptions { recursive: false, force: false, @@ -1582,13 +1582,16 @@ async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> { .is_ok(); assert!(!symlink_exists); let outside = file_system - .read_file_text(&PathUri::from_path(&outside_file)?, /*sandbox*/ None) + .read_file_text( + &PathUri::from_host_native_path(&outside_file)?, + /*sandbox*/ None, + ) .await?; assert_eq!(outside, "outside"); file_system .remove( - &PathUri::from_path(&root)?, + &PathUri::from_host_native_path(&root)?, RemoveOptions { recursive: true, force: true, @@ -1630,8 +1633,8 @@ async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> { let sandbox = workspace_write_sandbox(allowed_dir.clone()); file_system .copy( - &PathUri::from_path(&source_symlink)?, - &PathUri::from_path(&copied_symlink)?, + &PathUri::from_host_native_path(&source_symlink)?, + &PathUri::from_host_native_path(&copied_symlink)?, CopyOptions { recursive: false }, Some(&sandbox), ) @@ -1664,7 +1667,7 @@ async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> { file_system .remove( - &PathUri::from_path(&root)?, + &PathUri::from_host_native_path(&root)?, RemoveOptions { recursive: true, force: true, diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 236fbee4e..95f7fbb93 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -747,7 +747,7 @@ async fn stdio_server_uses_configured_cwd_before_runtime_fallback() -> anyhow::R let fixture = test_codex() .with_workspace_setup(|cwd, fs| async move { let configured_cwd = cwd.join("mcp-configured-cwd"); - let configured_cwd_uri = PathUri::from_path(&configured_cwd)?; + let configured_cwd_uri = PathUri::from_host_native_path(&configured_cwd)?; fs.create_directory( &configured_cwd_uri, CreateDirectoryOptions { recursive: true }, diff --git a/codex-rs/core/tests/suite/skills.rs b/codex-rs/core/tests/suite/skills.rs index 2de9bf26c..7beaa1710 100644 --- a/codex-rs/core/tests/suite/skills.rs +++ b/codex-rs/core/tests/suite/skills.rs @@ -31,7 +31,7 @@ async fn write_repo_skill( body: &str, ) -> Result<()> { let skill_dir = cwd.join(".agents").join("skills").join(name); - let skill_dir_uri = PathUri::from_path(&skill_dir)?; + let skill_dir_uri = PathUri::from_host_native_path(&skill_dir)?; fs.create_directory( &skill_dir_uri, CreateDirectoryOptions { recursive: true }, @@ -40,7 +40,7 @@ async fn write_repo_skill( .await?; let contents = format!("---\nname: {name}\ndescription: {description}\n---\n\n{body}\n"); let path = skill_dir.join("SKILL.md"); - let path_uri = PathUri::from_path(&path)?; + let path_uri = PathUri::from_host_native_path(&path)?; fs.write_file(&path_uri, contents.into_bytes(), /*sandbox*/ None) .await?; Ok(()) diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index 8187fc0cb..fe6ce7fbb 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -229,7 +229,7 @@ async fn create_workspace_directory( rel_path: impl AsRef, ) -> Result { let abs_path = test.config.cwd.join(rel_path.as_ref()); - let abs_path_uri = PathUri::from_path(&abs_path)?; + let abs_path_uri = PathUri::from_host_native_path(&abs_path)?; test.fs() .create_directory( &abs_path_uri, @@ -437,7 +437,7 @@ async fn unified_exec_emits_exec_command_begin_event() -> Result<()> { assert_command(&begin_event.command, "-lc", "/bin/echo hello unified exec"); - assert_eq!(begin_event.cwd, PathUri::from_path(&cwd)?); + assert_eq!(begin_event.cwd, PathUri::from_host_native_path(&cwd)?); wait_for_event(&test.codex, |event| { matches!(event, EventMsg::TurnComplete(_)) @@ -508,7 +508,7 @@ async fn unified_exec_resolves_relative_workdir() -> Result<()> { assert_eq!( begin_event.cwd, - PathUri::from_path(&workdir)?, + PathUri::from_host_native_path(&workdir)?, "exec_command cwd should resolve relative workdir against turn cwd", ); @@ -570,7 +570,7 @@ async fn unified_exec_respects_workdir_override() -> Result<()> { assert_eq!( begin_event.cwd, - PathUri::from_path(&workdir)?, + PathUri::from_host_native_path(&workdir)?, "exec_command cwd should reflect the requested workdir override" ); diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index c2c7b562d..a660ec4da 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -133,7 +133,7 @@ fn png_bytes(width: u32, height: u32, rgba: [u8; 4]) -> anyhow::Result> async fn create_workspace_directory(test: &TestCodex, rel_path: &str) -> anyhow::Result { let abs_path = test.config.cwd.join(rel_path); - let abs_path_uri = PathUri::from_path(&abs_path)?; + let abs_path_uri = PathUri::from_host_native_path(&abs_path)?; test.fs() .create_directory( &abs_path_uri, @@ -151,7 +151,7 @@ async fn write_workspace_file( ) -> anyhow::Result { let abs_path = test.config.cwd.join(rel_path); if let Some(parent) = abs_path.parent() { - let parent_uri = PathUri::from_path(&parent)?; + let parent_uri = PathUri::from_host_native_path(&parent)?; test.fs() .create_directory( &parent_uri, @@ -160,7 +160,7 @@ async fn write_workspace_file( ) .await?; } - let abs_path_uri = PathUri::from_path(&abs_path)?; + let abs_path_uri = PathUri::from_host_native_path(&abs_path)?; test.fs() .write_file(&abs_path_uri, contents, /*sandbox*/ None) .await?; @@ -589,7 +589,7 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<() )) .abs(); let image_path = remote_cwd.join("remote.png"); - let remote_cwd_uri = PathUri::from_path(&remote_cwd)?; + let remote_cwd_uri = PathUri::from_host_native_path(&remote_cwd)?; test.fs() .create_directory( &remote_cwd_uri, @@ -598,7 +598,7 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<() ) .await?; let png = png_bytes(/*width*/ 1, /*height*/ 1, [0, 255, 0, 255])?; - let image_path_uri = PathUri::from_path(&image_path)?; + let image_path_uri = PathUri::from_host_native_path(&image_path)?; test.fs() .write_file(&image_path_uri, png, /*sandbox*/ None) .await?; diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index 5990d8811..40d73cade 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -1151,8 +1151,10 @@ mod tests { .start(crate::ExecParams { process_id: ProcessId::from("default-env-proc"), argv: vec!["true".to_string()], - cwd: PathUri::from_path(std::env::current_dir().expect("read current dir")) - .expect("cwd URI"), + cwd: PathUri::from_host_native_path( + std::env::current_dir().expect("read current dir"), + ) + .expect("cwd URI"), env_policy: None, env: Default::default(), tty: false, @@ -1188,8 +1190,10 @@ mod tests { .start(crate::ExecParams { process_id: ProcessId::from("local-sandbox-proc"), argv: vec!["true".to_string()], - cwd: PathUri::from_path(std::env::current_dir().expect("read current dir")) - .expect("cwd URI"), + cwd: PathUri::from_host_native_path( + std::env::current_dir().expect("read current dir"), + ) + .expect("cwd URI"), env_policy: None, env: Default::default(), tty: false, diff --git a/codex-rs/exec-server/src/fs_helper.rs b/codex-rs/exec-server/src/fs_helper.rs index f4db844b5..9fa74bf3d 100644 --- a/codex-rs/exec-server/src/fs_helper.rs +++ b/codex-rs/exec-server/src/fs_helper.rs @@ -314,8 +314,9 @@ mod tests { #[test] fn helper_protocol_uses_path_uris() -> serde_json::Result<()> { - let local_path = PathUri::from_path(std::env::current_dir().expect("cwd").join("file")) - .expect("path URI"); + let local_path = + PathUri::from_host_native_path(std::env::current_dir().expect("cwd").join("file")) + .expect("path URI"); let paths = [ local_path, PathUri::parse("file://server/share/file").expect("path URI"), diff --git a/codex-rs/exec-server/src/local_process.rs b/codex-rs/exec-server/src/local_process.rs index c7e99de53..1f0dc5fec 100644 --- a/codex-rs/exec-server/src/local_process.rs +++ b/codex-rs/exec-server/src/local_process.rs @@ -956,7 +956,8 @@ mod tests { ExecParams { process_id: ProcessId::from("env-test"), argv: vec!["true".to_string()], - cwd: PathUri::from_path(std::env::current_dir().expect("cwd")).expect("cwd URI"), + cwd: PathUri::from_host_native_path(std::env::current_dir().expect("cwd")) + .expect("cwd URI"), env_policy: None, env, tty: false, diff --git a/codex-rs/exec-server/src/protocol.rs b/codex-rs/exec-server/src/protocol.rs index 99ee3431a..8771e869d 100644 --- a/codex-rs/exec-server/src/protocol.rs +++ b/codex-rs/exec-server/src/protocol.rs @@ -519,7 +519,7 @@ mod tests { let legacy_cwd = std::env::current_dir().expect("current directory"); let native_sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd( PermissionProfile::default(), - PathUri::from_path(&legacy_cwd).expect("cwd URI"), + PathUri::from_host_native_path(&legacy_cwd).expect("cwd URI"), ); let mut legacy_sandbox = serde_json::to_value(&native_sandbox).expect("sandbox should serialize"); @@ -531,7 +531,7 @@ mod tests { .expect("legacy absolute path should deserialize"); let expected_sandbox = native_sandbox; let expected = FsReadFileParams { - path: PathUri::from_path(legacy_path).expect("path URI"), + path: PathUri::from_host_native_path(legacy_path).expect("path URI"), sandbox: Some(expected_sandbox.clone()), }; diff --git a/codex-rs/exec-server/src/server/file_system_handler.rs b/codex-rs/exec-server/src/server/file_system_handler.rs index ddf17f21c..1a6e07393 100644 --- a/codex-rs/exec-server/src/server/file_system_handler.rs +++ b/codex-rs/exec-server/src/server/file_system_handler.rs @@ -274,7 +274,7 @@ mod tests { ) .expect("runtime paths"); let handler = FileSystemHandler::new(runtime_paths); - let sandbox_cwd = PathUri::from_path(temp_dir.path()).expect("tempdir URI"); + let sandbox_cwd = PathUri::from_host_native_path(temp_dir.path()).expect("tempdir URI"); let sandbox_context = |sandbox_policy| { FileSystemSandboxContext::from_legacy_sandbox_policy( sandbox_policy, @@ -292,7 +292,8 @@ mod tests { }, ), ] { - let path = PathUri::from_path(temp_dir.path().join(file_name)).expect("path URI"); + let path = + PathUri::from_host_native_path(temp_dir.path().join(file_name)).expect("path URI"); handler .write_file(FsWriteFileParams { @@ -312,7 +313,7 @@ mod tests { .expect("canonicalize file"); assert_eq!( canonicalized.path, - PathUri::from_path( + PathUri::from_host_native_path( std::fs::canonicalize(temp_dir.path().join(file_name)).expect("canonical path"), ) .expect("canonical path URI"), diff --git a/codex-rs/exec-server/src/server/handler/tests.rs b/codex-rs/exec-server/src/server/handler/tests.rs index 9cbbf78d0..ebb2b6e6e 100644 --- a/codex-rs/exec-server/src/server/handler/tests.rs +++ b/codex-rs/exec-server/src/server/handler/tests.rs @@ -27,7 +27,8 @@ fn exec_params_with_argv(process_id: &str, argv: Vec) -> ExecParams { ExecParams { process_id: ProcessId::from(process_id), argv, - cwd: PathUri::from_path(std::env::current_dir().expect("cwd")).expect("cwd URI"), + cwd: PathUri::from_host_native_path(std::env::current_dir().expect("cwd")) + .expect("cwd URI"), env_policy: None, env: inherited_path_env(), tty: false, diff --git a/codex-rs/exec-server/src/server/processor.rs b/codex-rs/exec-server/src/server/processor.rs index f5c2ba760..e9316e0d5 100644 --- a/codex-rs/exec-server/src/server/processor.rs +++ b/codex-rs/exec-server/src/server/processor.rs @@ -432,7 +432,8 @@ mod tests { ExecParams { process_id, argv: sleep_then_print_argv(), - cwd: PathUri::from_path(std::env::current_dir().expect("cwd")).expect("cwd URI"), + cwd: PathUri::from_host_native_path(std::env::current_dir().expect("cwd")) + .expect("cwd URI"), env_policy: None, env, tty: false, diff --git a/codex-rs/exec-server/tests/exec_process.rs b/codex-rs/exec-server/tests/exec_process.rs index f35923815..2f9b9c1db 100644 --- a/codex-rs/exec-server/tests/exec_process.rs +++ b/codex-rs/exec-server/tests/exec_process.rs @@ -75,7 +75,7 @@ async fn assert_exec_process_starts_and_exits(use_remote: bool) -> Result<()> { .start(ExecParams { process_id: ProcessId::from("proc-1"), argv: vec!["true".to_string()], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -218,7 +218,7 @@ async fn assert_exec_process_streams_output(use_remote: bool) -> Result<()> { "-c".to_string(), "sleep 0.05; printf 'session output\\n'".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -251,7 +251,7 @@ async fn assert_exec_process_pushes_events(use_remote: bool) -> Result<()> { "-c".to_string(), "printf 'event output\\n'; sleep 0.1; printf 'event err\\n' >&2; sleep 0.1; exit 7".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -300,7 +300,7 @@ async fn assert_exec_process_replays_events_after_close(use_remote: bool) -> Res "-c".to_string(), "printf 'late one\\n'; printf 'late two\\n'".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -350,7 +350,7 @@ async fn assert_exec_process_retains_output_after_exit_until_streams_close( DELAYED_OUTPUT_AFTER_EXIT_PARENT_ARG.to_string(), release_path.to_string_lossy().into_owned(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -425,7 +425,7 @@ async fn assert_exec_process_write_then_read(use_remote: bool) -> Result<()> { "-c".to_string(), "IFS= read line; printf 'from-stdin:%s\\n' \"$line\"".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: true, @@ -464,7 +464,7 @@ async fn assert_exec_process_write_then_read_without_tty(use_remote: bool) -> Re "-c".to_string(), "IFS= read line; printf 'from-stdin:%s\\n' \"$line\"".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -499,7 +499,7 @@ async fn assert_exec_process_rejects_write_without_pipe_stdin(use_remote: bool) "-c".to_string(), "sleep 0.3; if IFS= read -r line; then printf 'read:%s\\n' \"$line\"; else printf 'eof\\n'; fi".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -535,7 +535,7 @@ async fn assert_exec_process_signal_interrupts_process(use_remote: bool) -> Resu "-c".to_string(), "trap 'printf \"signal:2\\n\"; exit 7' INT; printf 'ready\\n'; while :; do :; done".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -590,7 +590,7 @@ async fn assert_exec_process_signal_reports_unsupported_on_windows(use_remote: b "/C".to_string(), "echo ready && ping -n 30 127.0.0.1 >NUL".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -632,7 +632,7 @@ async fn assert_exec_process_preserves_queued_events_before_subscribe( "-c".to_string(), "printf 'queued output\\n'".to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: Default::default(), tty: false, @@ -683,7 +683,7 @@ async fn remote_exec_process_recovers_after_transport_disconnect() -> Result<()> ) .to_string(), ], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: /*env_policy*/ None, env: HashMap::from([ ( diff --git a/codex-rs/exec-server/tests/file_stream.rs b/codex-rs/exec-server/tests/file_stream.rs index e30e8c468..89162daca 100644 --- a/codex-rs/exec-server/tests/file_stream.rs +++ b/codex-rs/exec-server/tests/file_stream.rs @@ -45,7 +45,10 @@ async fn stream_stops_after_an_exact_block_boundary() -> Result<()> { std::fs::write(&path, vec![b'x'; BLOCK_SIZE * 2])?; let chunks = file_system - .read_file_stream(&PathUri::from_path(path)?, /*sandbox*/ None) + .read_file_stream( + &PathUri::from_host_native_path(path)?, + /*sandbox*/ None, + ) .await? .try_collect::>() .await?; @@ -64,7 +67,7 @@ async fn completed_streams_release_handle_capacity() -> Result<()> { let tmp = TempDir::new()?; let path = tmp.path().join("repeated.txt"); std::fs::write(&path, b"repeated")?; - let path = PathUri::from_path(path)?; + let path = PathUri::from_host_native_path(path)?; for _ in 0..=OPEN_FILE_LIMIT { let chunks = file_system @@ -88,7 +91,7 @@ async fn stream_rejects_platform_sandbox() -> Result<()> { let result = file_system .read_file_stream( - &PathUri::from_path(&path)?, + &PathUri::from_host_native_path(&path)?, Some(&read_only_sandbox(tmp.path().to_path_buf())), ) .await; @@ -120,7 +123,7 @@ async fn file_reads_reject_fifo_without_waiting_for_a_writer() -> Result<()> { ); } - let path_uri = PathUri::from_path(&path)?; + let path_uri = PathUri::from_host_native_path(&path)?; let read_error = timeout( Duration::from_secs(1), file_system.read_file(&path_uri, /*sandbox*/ None), @@ -158,7 +161,7 @@ async fn file_reads_reject_named_pipes() -> Result<()> { let read_error = timeout( Duration::from_secs(1), file_system.read_file( - &PathUri::from_path(std::path::Path::new(&read_path))?, + &PathUri::from_host_native_path(std::path::Path::new(&read_path))?, /*sandbox*/ None, ), ) @@ -173,7 +176,7 @@ async fn file_reads_reject_named_pipes() -> Result<()> { let stream_result = timeout( Duration::from_secs(1), file_system.read_file_stream( - &PathUri::from_path(std::path::Path::new(&stream_path))?, + &PathUri::from_host_native_path(std::path::Path::new(&stream_path))?, /*sandbox*/ None, ), ) @@ -202,7 +205,10 @@ async fn stream_keeps_reading_the_open_file_after_path_replacement() -> Result<( let path = tmp.path().join("replaceable.bin"); std::fs::write(&path, vec![b'a'; BLOCK_SIZE + 1])?; let mut stream = file_system - .read_file_stream(&PathUri::from_path(&path)?, /*sandbox*/ None) + .read_file_stream( + &PathUri::from_host_native_path(&path)?, + /*sandbox*/ None, + ) .await?; assert_eq!( @@ -236,7 +242,7 @@ async fn read_block_supports_non_sequential_offsets_and_lengths() -> Result<()> let open = client .fs_open(FsOpenParams { handle_id: Uuid::new_v4().simple().to_string(), - path: PathUri::from_path(path)?, + path: PathUri::from_host_native_path(path)?, sandbox: None, }) .await?; @@ -295,7 +301,7 @@ async fn open_enforces_the_per_connection_limit_and_close_releases_capacity() -> let tmp = TempDir::new()?; let path = tmp.path().join("limited.bin"); std::fs::write(&path, b"limited")?; - let path = PathUri::from_path(path)?; + let path = PathUri::from_host_native_path(path)?; let mut handles = Vec::with_capacity(OPEN_FILE_LIMIT); for _ in 0..OPEN_FILE_LIMIT { let open = client @@ -359,7 +365,7 @@ async fn open_rejects_handle_ids_longer_than_32_bytes() -> Result<()> { let error = client .fs_open(FsOpenParams { handle_id: "x".repeat(33), - path: PathUri::from_path(path)?, + path: PathUri::from_host_native_path(path)?, sandbox: None, }) .await diff --git a/codex-rs/exec-server/tests/file_system/shared.rs b/codex-rs/exec-server/tests/file_system/shared.rs index 08ba2efdd..8eb17f39f 100644 --- a/codex-rs/exec-server/tests/file_system/shared.rs +++ b/codex-rs/exec-server/tests/file_system/shared.rs @@ -66,7 +66,10 @@ async fn file_system_get_metadata_reports_files_and_directories( std::fs::create_dir(&directory_path)?; let file_metadata = file_system - .get_metadata(&PathUri::from_path(&file_path)?, /*sandbox*/ None) + .get_metadata( + &PathUri::from_host_native_path(&file_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!( @@ -83,7 +86,10 @@ async fn file_system_get_metadata_reports_files_and_directories( assert!(file_metadata.modified_at_ms > 0); let directory_metadata = file_system - .get_metadata(&PathUri::from_path(&directory_path)?, /*sandbox*/ None) + .get_metadata( + &PathUri::from_host_native_path(&directory_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!( @@ -116,7 +122,7 @@ async fn file_system_create_directory_creates_nested_directories( file_system .create_directory( - &PathUri::from_path(&nested_dir)?, + &PathUri::from_host_native_path(&nested_dir)?, CreateDirectoryOptions { recursive: true }, /*sandbox*/ None, ) @@ -140,7 +146,7 @@ async fn file_system_write_file_writes_bytes( let file_path = tmp.path().join("note.txt"); file_system .write_file( - &PathUri::from_path(&file_path)?, + &PathUri::from_host_native_path(&file_path)?, b"hello from trait".to_vec(), /*sandbox*/ None, ) @@ -155,21 +161,21 @@ async fn file_system_write_file_writes_bytes( fn path_uri_join_and_parent_preserve_lexical_paths() -> Result<()> { let tmp = TempDir::new()?; let source_dir = tmp.path().join("source"); - let source_dir_uri = PathUri::from_path(&source_dir)?; + let source_dir_uri = PathUri::from_host_native_path(&source_dir)?; let joined_nested = source_dir_uri.join("nested/note.txt")?; assert_eq!( joined_nested, - PathUri::from_path(source_dir.join("nested").join("note.txt"))? + PathUri::from_host_native_path(source_dir.join("nested").join("note.txt"))? ); let joined_parent = joined_nested.parent(); assert_eq!( joined_parent, - Some(PathUri::from_path(source_dir.join("nested"))?) + Some(PathUri::from_host_native_path(source_dir.join("nested"))?) ); let joined_parent_traversal = source_dir_uri.join("../outside")?; assert_eq!( joined_parent_traversal, - PathUri::from_path(source_dir.join("../outside"))? + PathUri::from_host_native_path(source_dir.join("../outside"))? ); Ok(()) } @@ -188,7 +194,10 @@ async fn file_system_read_file_returns_bytes( std::fs::write(&file_path, "hello from trait")?; let contents = file_system - .read_file(&PathUri::from_path(&file_path)?, /*sandbox*/ None) + .read_file( + &PathUri::from_host_native_path(&file_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!(contents, b"hello from trait"); @@ -213,7 +222,10 @@ async fn file_system_read_file_stream_returns_bounded_chunks( std::fs::write(&file_path, &contents)?; let chunks = file_system - .read_file_stream(&PathUri::from_path(file_path)?, /*sandbox*/ None) + .read_file_stream( + &PathUri::from_host_native_path(file_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))? .try_collect::>() @@ -249,7 +261,10 @@ async fn file_system_read_file_text_returns_string( std::fs::write(&file_path, "hello from trait")?; let contents = file_system - .read_file_text(&PathUri::from_path(&file_path)?, /*sandbox*/ None) + .read_file_text( + &PathUri::from_host_native_path(&file_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!(contents, "hello from trait"); @@ -271,8 +286,8 @@ async fn file_system_copy_copies_file(implementation: FileSystemImplementation) file_system .copy( - &PathUri::from_path(&source_file)?, - &PathUri::from_path(&copied_file)?, + &PathUri::from_host_native_path(&source_file)?, + &PathUri::from_host_native_path(&copied_file)?, CopyOptions { recursive: false }, /*sandbox*/ None, ) @@ -302,8 +317,8 @@ async fn file_system_copy_copies_directory_recursively( file_system .copy( - &PathUri::from_path(&source_dir)?, - &PathUri::from_path(&copied_dir)?, + &PathUri::from_host_native_path(&source_dir)?, + &PathUri::from_host_native_path(&copied_dir)?, CopyOptions { recursive: true }, /*sandbox*/ None, ) @@ -332,7 +347,10 @@ async fn file_system_read_directory_lists_entries( std::fs::write(source_dir.join("root.txt"), "hello")?; let mut entries = file_system - .read_directory(&PathUri::from_path(&source_dir)?, /*sandbox*/ None) + .read_directory( + &PathUri::from_host_native_path(&source_dir)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; entries.sort_by(|left, right| left.file_name.cmp(&right.file_name)); @@ -370,7 +388,7 @@ async fn file_system_remove_removes_directory( file_system .remove( - &PathUri::from_path(&directory_path)?, + &PathUri::from_host_native_path(&directory_path)?, RemoveOptions { recursive: true, force: true, @@ -398,7 +416,7 @@ async fn file_system_write_file_reports_missing_parent( let error = match file_system .write_file( - &PathUri::from_path(&missing_parent_path)?, + &PathUri::from_host_native_path(&missing_parent_path)?, b"hello from trait".to_vec(), /*sandbox*/ None, ) @@ -432,8 +450,8 @@ async fn file_system_copy_rejects_directory_without_recursive( let error = file_system .copy( - &PathUri::from_path(&source_dir)?, - &PathUri::from_path(tmp.path().join("dest"))?, + &PathUri::from_host_native_path(&source_dir)?, + &PathUri::from_host_native_path(tmp.path().join("dest"))?, CopyOptions { recursive: false }, /*sandbox*/ None, ) @@ -465,7 +483,7 @@ async fn file_system_sandboxed_metadata_and_read_allow_readable_root( let sandbox = read_only_sandbox(allowed_dir); let metadata = file_system - .get_metadata(&PathUri::from_path(&file_path)?, Some(&sandbox)) + .get_metadata(&PathUri::from_host_native_path(&file_path)?, Some(&sandbox)) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!( @@ -481,7 +499,7 @@ async fn file_system_sandboxed_metadata_and_read_allow_readable_root( ); let contents = file_system - .read_file(&PathUri::from_path(&file_path)?, Some(&sandbox)) + .read_file(&PathUri::from_host_native_path(&file_path)?, Some(&sandbox)) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!(contents, b"sandboxed hello"); @@ -505,8 +523,8 @@ pub(crate) async fn assert_canonicalize_resolves_directory_alias( std::fs::write(&file_path, "canonical hello")?; create_directory_alias(&source_dir, &alias_dir)?; - let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?; - let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?; + let requested_path = PathUri::from_host_native_path(alias_dir.join("nested").join("note.txt"))?; + let expected_path = PathUri::from_host_native_path(std::fs::canonicalize(&file_path)?)?; assert_ne!(requested_path, expected_path); let canonical_path = file_system @@ -535,8 +553,8 @@ pub(crate) async fn assert_sandboxed_canonicalize_resolves_directory_alias( create_directory_alias(&source_dir, &alias_dir)?; let sandbox = read_only_sandbox(tmp.path().to_path_buf()); - let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?; - let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?; + let requested_path = PathUri::from_host_native_path(alias_dir.join("nested").join("note.txt"))?; + let expected_path = PathUri::from_host_native_path(std::fs::canonicalize(&file_path)?)?; assert_ne!(requested_path, expected_path); let canonical_path = file_system @@ -591,7 +609,7 @@ async fn file_system_sandboxed_write_allows_additional_write_root( file_system .write_file( - &PathUri::from_path(&file_path)?, + &PathUri::from_host_native_path(&file_path)?, b"created".to_vec(), Some(&sandbox), ) @@ -617,8 +635,8 @@ async fn file_system_copy_rejects_copying_directory_into_descendant( let error = file_system .copy( - &PathUri::from_path(&source_dir)?, - &PathUri::from_path(source_dir.join("nested").join("copy"))?, + &PathUri::from_host_native_path(&source_dir)?, + &PathUri::from_host_native_path(source_dir.join("nested").join("copy"))?, CopyOptions { recursive: true }, /*sandbox*/ None, ) diff --git a/codex-rs/exec-server/tests/file_system_unix.rs b/codex-rs/exec-server/tests/file_system_unix.rs index 57c3a82e9..0287844a7 100644 --- a/codex-rs/exec-server/tests/file_system_unix.rs +++ b/codex-rs/exec-server/tests/file_system_unix.rs @@ -187,7 +187,7 @@ async fn sandboxed_file_system_helper_finds_bwrap_on_preserved_path() -> Result< file_system .write_file( - &PathUri::from_path(&file_path)?, + &PathUri::from_host_native_path(&file_path)?, b"written through fs helper".to_vec(), Some(&sandbox), ) @@ -221,7 +221,10 @@ async fn file_system_get_metadata_reports_symlink_targets( let symlink_path = tmp.path().join("note-link.txt"); symlink(&file_path, &symlink_path)?; let symlink_metadata = file_system - .get_metadata(&PathUri::from_path(&symlink_path)?, /*sandbox*/ None) + .get_metadata( + &PathUri::from_host_native_path(&symlink_path)?, + /*sandbox*/ None, + ) .await .with_context(|| format!("mode={implementation}"))?; assert_eq!( @@ -243,7 +246,7 @@ async fn file_system_get_metadata_reports_symlink_targets( symlink(&dir_path, &dir_symlink_path)?; let dir_symlink_metadata = file_system .get_metadata( - &PathUri::from_path(&dir_symlink_path)?, + &PathUri::from_host_native_path(&dir_symlink_path)?, /*sandbox*/ None, ) .await @@ -278,7 +281,7 @@ async fn file_system_sandboxed_write_rejects_unwritable_path( let sandbox = read_only_sandbox(tmp.path().to_path_buf()); let error = match file_system .write_file( - &PathUri::from_path(&blocked_path)?, + &PathUri::from_host_native_path(&blocked_path)?, b"nope".to_vec(), Some(&sandbox), ) @@ -314,7 +317,7 @@ async fn file_system_sandboxed_write_allows_explicit_alias_roots( file_system .write_file( - &PathUri::from_path(&file_path)?, + &PathUri::from_host_native_path(&file_path)?, b"created".to_vec(), Some(&sandbox), ) @@ -345,7 +348,10 @@ async fn file_system_sandboxed_read_rejects_symlink_escape( let requested_path = allowed_dir.join("link").join("secret.txt"); let sandbox = read_only_sandbox(allowed_dir); let error = match file_system - .read_file(&PathUri::from_path(&requested_path)?, Some(&sandbox)) + .read_file( + &PathUri::from_host_native_path(&requested_path)?, + Some(&sandbox), + ) .await { Ok(_) => anyhow::bail!("read should be blocked"), @@ -375,7 +381,7 @@ async fn file_system_sandboxed_read_rejects_symlink_parent_dotdot_escape( symlink(&outside_dir, allowed_dir.join("link"))?; let requested_path = - PathUri::from_path(allowed_dir.join("link").join("..").join("secret.txt"))?; + PathUri::from_host_native_path(allowed_dir.join("link").join("..").join("secret.txt"))?; let sandbox = read_only_sandbox(allowed_dir); let error = match file_system.read_file(&requested_path, Some(&sandbox)).await { Ok(_) => anyhow::bail!("read should fail after path normalization"), @@ -411,7 +417,7 @@ async fn file_system_sandboxed_write_rejects_symlink_escape( let sandbox = workspace_write_sandbox(allowed_dir); let error = match file_system .write_file( - &PathUri::from_path(&requested_path)?, + &PathUri::from_host_native_path(&requested_path)?, b"nope".to_vec(), Some(&sandbox), ) @@ -449,7 +455,7 @@ async fn file_system_sandboxed_write_preserves_existing_hard_link( let sandbox = workspace_write_sandbox(allowed_dir); file_system .write_file( - &PathUri::from_path(&hard_link)?, + &PathUri::from_host_native_path(&hard_link)?, b"updated through existing hard link\n".to_vec(), Some(&sandbox), ) @@ -495,7 +501,7 @@ async fn file_system_create_directory_rejects_symlink_escape( let sandbox = workspace_write_sandbox(allowed_dir); let error = match file_system .create_directory( - &PathUri::from_path(&requested_path)?, + &PathUri::from_host_native_path(&requested_path)?, CreateDirectoryOptions { recursive: false }, Some(&sandbox), ) @@ -530,7 +536,10 @@ async fn file_system_read_directory_rejects_symlink_escape( let requested_path = allowed_dir.join("link"); let sandbox = read_only_sandbox(allowed_dir); let error = match file_system - .read_directory(&PathUri::from_path(&requested_path)?, Some(&sandbox)) + .read_directory( + &PathUri::from_host_native_path(&requested_path)?, + Some(&sandbox), + ) .await { Ok(_) => anyhow::bail!("read_directory should be blocked"), @@ -562,8 +571,8 @@ async fn file_system_copy_rejects_symlink_escape_destination( let sandbox = workspace_write_sandbox(allowed_dir.clone()); let error = match file_system .copy( - &PathUri::from_path(allowed_dir.join("source.txt"))?, - &PathUri::from_path(&requested_destination)?, + &PathUri::from_host_native_path(allowed_dir.join("source.txt"))?, + &PathUri::from_host_native_path(&requested_destination)?, CopyOptions { recursive: false }, Some(&sandbox), ) @@ -600,7 +609,7 @@ async fn file_system_remove_removes_symlink_not_target( let sandbox = workspace_write_sandbox(allowed_dir); file_system .remove( - &PathUri::from_path(&symlink_path)?, + &PathUri::from_host_native_path(&symlink_path)?, RemoveOptions { recursive: false, force: false, @@ -640,8 +649,8 @@ async fn file_system_copy_preserves_symlink_source( let sandbox = workspace_write_sandbox(allowed_dir.clone()); file_system .copy( - &PathUri::from_path(&source_symlink)?, - &PathUri::from_path(&copied_symlink)?, + &PathUri::from_host_native_path(&source_symlink)?, + &PathUri::from_host_native_path(&copied_symlink)?, CopyOptions { recursive: false }, Some(&sandbox), ) @@ -677,7 +686,7 @@ async fn file_system_remove_rejects_symlink_escape( let sandbox = workspace_write_sandbox(allowed_dir); let error = match file_system .remove( - &PathUri::from_path(&requested_path)?, + &PathUri::from_host_native_path(&requested_path)?, RemoveOptions { recursive: false, force: false, @@ -718,8 +727,8 @@ async fn file_system_copy_rejects_symlink_escape_source( let sandbox = workspace_write_sandbox(allowed_dir); let error = match file_system .copy( - &PathUri::from_path(&requested_source)?, - &PathUri::from_path(&requested_destination)?, + &PathUri::from_host_native_path(&requested_source)?, + &PathUri::from_host_native_path(&requested_destination)?, CopyOptions { recursive: false }, Some(&sandbox), ) @@ -752,8 +761,8 @@ async fn file_system_copy_preserves_symlinks_in_recursive_copy( file_system .copy( - &PathUri::from_path(&source_dir)?, - &PathUri::from_path(&copied_dir)?, + &PathUri::from_host_native_path(&source_dir)?, + &PathUri::from_host_native_path(&copied_dir)?, CopyOptions { recursive: true }, /*sandbox*/ None, ) @@ -798,8 +807,8 @@ async fn file_system_copy_ignores_unknown_special_files_in_recursive_copy( file_system .copy( - &PathUri::from_path(&source_dir)?, - &PathUri::from_path(&copied_dir)?, + &PathUri::from_host_native_path(&source_dir)?, + &PathUri::from_host_native_path(&copied_dir)?, CopyOptions { recursive: true }, /*sandbox*/ None, ) @@ -837,8 +846,8 @@ async fn file_system_copy_rejects_standalone_fifo_source( let error = file_system .copy( - &PathUri::from_path(&fifo_path)?, - &PathUri::from_path(tmp.path().join("copied"))?, + &PathUri::from_host_native_path(&fifo_path)?, + &PathUri::from_host_native_path(tmp.path().join("copied"))?, CopyOptions { recursive: false }, /*sandbox*/ None, ) diff --git a/codex-rs/exec-server/tests/file_system_windows.rs b/codex-rs/exec-server/tests/file_system_windows.rs index fbf8b0733..1d5b4a13f 100644 --- a/codex-rs/exec-server/tests/file_system_windows.rs +++ b/codex-rs/exec-server/tests/file_system_windows.rs @@ -74,7 +74,10 @@ async fn file_system_remote_fs_helper_respects_windows_sandbox_write_policy() -> let readable_file = readonly_dir.join("readable.txt"); std::fs::write(&readable_file, b"readable")?; let read_result = file_system - .read_file(&PathUri::from_path(&readable_file)?, Some(&sandbox)) + .read_file( + &PathUri::from_host_native_path(&readable_file)?, + Some(&sandbox), + ) .await; // Some local Windows hosts cannot create restricted tokens. Reaching that // error still proves the remote fs helper went through the Windows sandbox @@ -87,7 +90,7 @@ async fn file_system_remote_fs_helper_respects_windows_sandbox_write_policy() -> let blocked_file = readonly_dir.join("blocked.txt"); let error = file_system .write_file( - &PathUri::from_path(&blocked_file)?, + &PathUri::from_host_native_path(&blocked_file)?, b"blocked".to_vec(), Some(&sandbox), ) @@ -104,7 +107,7 @@ async fn file_system_remote_fs_helper_respects_windows_sandbox_write_policy() -> fn read_only_sandbox_for_cwd(cwd: std::path::PathBuf) -> Result { Ok(FileSystemSandboxContext::from_legacy_sandbox_policy( SandboxPolicy::new_read_only_policy(), - PathUri::from_path(cwd)?, + PathUri::from_host_native_path(cwd)?, )?) } diff --git a/codex-rs/exec-server/tests/relay.rs b/codex-rs/exec-server/tests/relay.rs index c9f873c15..30ce1459b 100644 --- a/codex-rs/exec-server/tests/relay.rs +++ b/codex-rs/exec-server/tests/relay.rs @@ -144,7 +144,7 @@ async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> { .exec(ExecParams { process_id: ProcessId::from("proc-1"), argv: vec!["true".to_string()], - cwd: PathUri::from_path(std::env::current_dir()?)?, + cwd: PathUri::from_host_native_path(std::env::current_dir()?)?, env_policy: None, env: HashMap::new(), tty: false, @@ -167,7 +167,7 @@ async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> { std::fs::write(&large_file_path, &large_file_contents)?; let read_response = client .fs_read_file(FsReadFileParams { - path: PathUri::from_path(large_file_path)?, + path: PathUri::from_host_native_path(large_file_path)?, sandbox: None, }) .await?; diff --git a/codex-rs/rmcp-client/src/stdio_server_launcher.rs b/codex-rs/rmcp-client/src/stdio_server_launcher.rs index 94aa45c1e..dfd2420db 100644 --- a/codex-rs/rmcp-client/src/stdio_server_launcher.rs +++ b/codex-rs/rmcp-client/src/stdio_server_launcher.rs @@ -488,7 +488,7 @@ impl ExecutorStdioServerLauncher { // before sending an executor request. let argv = Self::process_api_argv(&program, &args).map_err(io::Error::other)?; let env = Self::process_api_env(envs).map_err(io::Error::other)?; - let cwd = PathUri::from_path(cwd)?; + let cwd = PathUri::from_host_native_path(cwd)?; let process_id = ExecutorProcessTransport::next_process_id(); // Start the MCP server process on the executor with raw pipes. `tty=false` // keeps stdout as a clean protocol stream, while `pipe_stdin=true` lets diff --git a/codex-rs/utils/path-uri/src/lib.rs b/codex-rs/utils/path-uri/src/lib.rs index 5ef1df2fa..6c92c5e1d 100644 --- a/codex-rs/utils/path-uri/src/lib.rs +++ b/codex-rs/utils/path-uri/src/lib.rs @@ -121,7 +121,7 @@ impl PathUri { /// Relative paths are reported as invalid input. Absolute paths without a /// valid URI representation use the fallback documented on /// [`Self::from_abs_path`]. - pub fn from_path(path: impl AsRef) -> io::Result { + pub fn from_host_native_path(path: impl AsRef) -> io::Result { let path = AbsolutePathBuf::from_absolute_path_checked(path) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; Ok(Self::from_abs_path(&path)) diff --git a/codex-rs/utils/path-uri/src/tests.rs b/codex-rs/utils/path-uri/src/tests.rs index 075d343d0..0e45baaa1 100644 --- a/codex-rs/utils/path-uri/src/tests.rs +++ b/codex-rs/utils/path-uri/src/tests.rs @@ -423,7 +423,8 @@ fn path_uri_deserializes_legacy_absolute_paths() { #[test] fn path_uri_rejects_relative_native_paths() { - let error = PathUri::from_path("src/lib.rs").expect_err("relative path should be rejected"); + let error = + PathUri::from_host_native_path("src/lib.rs").expect_err("relative path should be rejected"); assert_eq!(error.kind(), io::ErrorKind::InvalidInput); }