[codex] migrate ExecutorFileSystem paths to PathUri (#27424)

## Why

We're moving exec-server to use PathUri for its internal path
representations.

## What

Move `ExecutorFileSystem` APIs to use `PathUri` instead of
`AbsolutePathBuf`. Future changes will convert higher-level parts of
exec-server.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-11 18:44:18 +00:00
committed by GitHub
parent 4a05d3b282
commit b2a4e3be27
52 changed files with 1126 additions and 495 deletions
+1
View File
@@ -70,6 +70,7 @@ codex-utils-image = { workspace = true }
codex-utils-home-dir = { workspace = true }
codex-utils-output-truncation = { workspace = true }
codex-utils-path = { workspace = true }
codex-utils-path-uri = { workspace = true }
codex-utils-plugins = { workspace = true }
codex-utils-pty = { workspace = true }
codex-utils-string = { workspace = true }
+26 -10
View File
@@ -26,6 +26,7 @@ use codex_exec_server::ExecutorFileSystem;
use codex_features::Feature;
use codex_prompts::HIERARCHICAL_AGENTS_MESSAGE;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use std::io;
use toml::Value as TomlValue;
use tracing::error;
@@ -58,7 +59,19 @@ impl<'a> AgentsMdManager<'a> {
let base = codex_dir?;
for candidate in [LOCAL_AGENTS_MD_FILENAME, DEFAULT_AGENTS_MD_FILENAME] {
let path = base.join(candidate);
let data = match fs.read_file(&path, /*sandbox*/ None).await {
// A missing global instructions file is normal, but an unrepresentable
// configured path means Codex cannot honor the workspace configuration.
let path_uri = match PathUri::from_abs_path(&path) {
Ok(path_uri) => path_uri,
Err(err) => {
startup_warnings.push(format!(
"Failed to read global AGENTS.md instructions from `{}`: {err}",
path.display()
));
continue;
}
};
let data = match fs.read_file(&path_uri, /*sandbox*/ None).await {
Ok(data) => data,
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
Err(err) if err.kind() == io::ErrorKind::IsADirectory => continue,
@@ -149,14 +162,15 @@ impl<'a> AgentsMdManager<'a> {
break;
}
match fs.get_metadata(&p, /*sandbox*/ None).await {
let path_uri = PathUri::from_abs_path(&p)?;
match fs.get_metadata(&path_uri, /*sandbox*/ None).await {
Ok(metadata) if !metadata.is_file => continue,
Ok(_) => {}
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
Err(err) => return Err(err),
}
let mut data = match fs.read_file(&p, /*sandbox*/ None).await {
let mut data = match fs.read_file(&path_uri, /*sandbox*/ None).await {
Ok(data) => data,
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
Err(err) => return Err(err),
@@ -231,12 +245,13 @@ impl<'a> AgentsMdManager<'a> {
for ancestor in dir.ancestors() {
for marker in &project_root_markers {
let marker_path = ancestor.join(marker);
let marker_exists = match fs.get_metadata(&marker_path, /*sandbox*/ None).await
{
Ok(_) => true,
Err(err) if err.kind() == io::ErrorKind::NotFound => false,
Err(err) => return Err(err),
};
let marker_path_uri = PathUri::from_abs_path(&marker_path)?;
let marker_exists =
match fs.get_metadata(&marker_path_uri, /*sandbox*/ None).await {
Ok(_) => true,
Err(err) if err.kind() == io::ErrorKind::NotFound => false,
Err(err) => return Err(err),
};
if marker_exists {
project_root = Some(ancestor.clone());
break;
@@ -272,7 +287,8 @@ impl<'a> AgentsMdManager<'a> {
for d in search_dirs {
for name in &candidate_filenames {
let candidate = d.join(name);
match fs.get_metadata(&candidate, /*sandbox*/ None).await {
let candidate_uri = PathUri::from_abs_path(&candidate)?;
match fs.get_metadata(&candidate_uri, /*sandbox*/ None).await {
Ok(md) if md.is_file => {
found.push(candidate);
break;
+7 -3
View File
@@ -7,6 +7,7 @@ use codex_config::config_toml::ConfigToml;
use codex_exec_server::ExecutorFileSystem;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_absolute_path::AbsolutePathBufGuard;
use codex_utils_path_uri::PathUri;
use serde::Deserialize;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
@@ -319,7 +320,8 @@ async fn read_resolved_agent_role_file(
path: &AbsolutePathBuf,
role_name_hint: Option<&str>,
) -> std::io::Result<ResolvedAgentRoleFile> {
let contents = fs.read_file_text(path, /*sandbox*/ None).await?;
let path_uri = PathUri::from_abs_path(path)?;
let contents = fs.read_file_text(&path_uri, /*sandbox*/ None).await?;
let config_base_dir = path.parent().unwrap_or_else(|| path.clone());
parse_agent_role_file_contents(
&contents,
@@ -391,8 +393,9 @@ async fn validate_agent_role_config_file(
return Ok(());
};
let config_file_uri = PathUri::from_abs_path(config_file)?;
let metadata = fs
.get_metadata(config_file, /*sandbox*/ None)
.get_metadata(&config_file_uri, /*sandbox*/ None)
.await
.map_err(|e| {
std::io::Error::new(
@@ -522,7 +525,8 @@ async fn collect_agent_role_files(
let mut files = Vec::new();
let mut dirs = vec![dir.clone()];
while let Some(dir) = dirs.pop() {
let entries = match fs.read_directory(&dir, /*sandbox*/ None).await {
let dir_uri = PathUri::from_abs_path(&dir)?;
let entries = match fs.read_directory(&dir_uri, /*sandbox*/ None).await {
Ok(entries) => entries,
Err(err) if err.kind() == ErrorKind::NotFound => continue,
Err(err) => return Err(err),
+3 -1
View File
@@ -105,6 +105,7 @@ use codex_protocol::protocol::SandboxPolicy;
pub use codex_thread_store::ExtraConfig;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_absolute_path::AbsolutePathBufGuard;
use codex_utils_path_uri::PathUri;
use rmcp::model::ElicitationCapability;
use rmcp::model::FormElicitationCapability;
use rmcp::model::UrlElicitationCapability;
@@ -3667,8 +3668,9 @@ impl Config {
return Ok(None);
};
let path_uri = PathUri::from_abs_path(path)?;
let contents = fs
.read_file_text(path, /*sandbox*/ None)
.read_file_text(&path_uri, /*sandbox*/ None)
.await
.map_err(|e| {
std::io::Error::new(
@@ -27,6 +27,7 @@ use crate::tools::registry::CoreToolRuntime;
use crate::tools::registry::ToolExecutor;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
use codex_utils_path_uri::PathUri;
pub struct ViewImageHandler {
options: ViewImageToolOptions,
@@ -146,9 +147,15 @@ impl ViewImageHandler {
let abs_path = cwd.join(path);
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, &cwd);
let fs = turn_environment.environment.get_filesystem();
let path_uri = PathUri::from_abs_path(&abs_path).map_err(|error| {
FunctionCallError::RespondToModel(format!(
"unable to locate image at `{}`: {error}",
abs_path.display()
))
})?;
let metadata = fs
.get_metadata(&abs_path, Some(&sandbox))
.get_metadata(&path_uri, Some(&sandbox))
.await
.map_err(|error| {
FunctionCallError::RespondToModel(format!(
@@ -164,7 +171,7 @@ impl ViewImageHandler {
)));
}
let file_bytes = fs
.read_file(&abs_path, Some(&sandbox))
.read_file(&path_uri, Some(&sandbox))
.await
.map_err(|error| {
FunctionCallError::RespondToModel(format!(
+1
View File
@@ -28,6 +28,7 @@ codex-models-manager = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
codex-utils-path-uri = { workspace = true }
ctor = { workspace = true }
futures = { workspace = true }
notify = { workspace = true }
+26 -7
View File
@@ -43,6 +43,7 @@ use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::protocol::TurnEnvironmentSelections;
use codex_protocol::user_input::UserInput;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use futures::future::BoxFuture;
use serde_json::Value;
use tempfile::TempDir;
@@ -135,10 +136,11 @@ pub async fn test_env() -> Result<TestEnv> {
let environment =
codex_exec_server::Environment::create_for_tests(Some(websocket_url.clone()))?;
let cwd = remote_aware_cwd_path();
let cwd_uri = PathUri::from_path(&cwd)?;
environment
.get_filesystem()
.create_directory(
&cwd,
&cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -907,35 +909,45 @@ impl TestCodexHarness {
) -> Result<()> {
let abs_path = self.path_abs(rel);
if let Some(parent) = abs_path.parent() {
let parent_uri = PathUri::from_path(&parent)?;
self.test
.fs()
.create_directory(
&parent,
&parent_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
}
let abs_path_uri = PathUri::from_path(&abs_path)?;
self.test
.fs()
.write_file(&abs_path, contents.as_ref().to_vec(), /*sandbox*/ None)
.write_file(
&abs_path_uri,
contents.as_ref().to_vec(),
/*sandbox*/ None,
)
.await?;
Ok(())
}
pub async fn read_file_text(&self, rel: impl AsRef<Path>) -> Result<String> {
let path = self.path_abs(rel);
let path_uri = PathUri::from_path(&path)?;
Ok(self
.test
.fs()
.read_file_text(&self.path_abs(rel), /*sandbox*/ None)
.read_file_text(&path_uri, /*sandbox*/ None)
.await?)
}
pub async fn create_dir_all(&self, rel: impl AsRef<Path>) -> Result<()> {
let path = self.path_abs(rel);
let path_uri = PathUri::from_path(&path)?;
self.test
.fs()
.create_directory(
&self.path_abs(rel),
&path_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -948,10 +960,11 @@ impl TestCodexHarness {
}
pub async fn remove_abs_path(&self, path: &AbsolutePathBuf) -> Result<()> {
let path_uri = PathUri::from_abs_path(path)?;
self.test
.fs()
.remove(
path,
&path_uri,
RemoveOptions {
recursive: false,
force: true,
@@ -963,7 +976,13 @@ impl TestCodexHarness {
}
pub async fn abs_path_exists(&self, path: &AbsolutePathBuf) -> Result<bool> {
match self.test.fs().get_metadata(path, /*sandbox*/ None).await {
let path_uri = PathUri::from_abs_path(path)?;
match self
.test
.fs()
.get_metadata(&path_uri, /*sandbox*/ None)
.await
{
Ok(_) => Ok(true),
Err(err) if err.kind() == ErrorKind::NotFound => Ok(false),
Err(err) => Err(err.into()),
+37 -14
View File
@@ -7,6 +7,7 @@ use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::Op;
use codex_protocol::user_input::UserInput;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use core_test_support::PathBufExt;
use core_test_support::create_directory_symlink;
use core_test_support::load_default_config_for_test;
@@ -112,10 +113,12 @@ 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");
fs.write_file(&agents_md, b"base doc".to_vec(), /*sandbox*/ None)
let agents_md_uri = PathUri::from_path(&agents_md)?;
let override_md_uri = PathUri::from_path(&override_md)?;
fs.write_file(&agents_md_uri, b"base doc".to_vec(), /*sandbox*/ None)
.await?;
fs.write_file(
&override_md,
&override_md_uri,
b"override doc".to_vec(),
/*sandbox*/ None,
)
@@ -146,14 +149,20 @@ 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)?;
fs.create_directory(
&agents_dir,
&agents_dir_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
fs.write_file(&fallback, b"fallback doc".to_vec(), /*sandbox*/ None)
.await?;
fs.write_file(
&fallback_uri,
b"fallback doc".to_vec(),
/*sandbox*/ None,
)
.await?;
Ok::<(), anyhow::Error>(())
}),
)
@@ -183,23 +192,35 @@ 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)?;
fs.create_directory(
&nested,
&nested_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
fs.write_file(&root_agents, b"root doc".to_vec(), /*sandbox*/ None)
.await?;
fs.write_file(
&git_marker,
&root_agents_uri,
b"root doc".to_vec(),
/*sandbox*/ None,
)
.await?;
fs.write_file(
&git_marker_uri,
b"gitdir: /tmp/mock-git-dir\n".to_vec(),
/*sandbox*/ None,
)
.await?;
fs.write_file(&nested_agents, b"child doc".to_vec(), /*sandbox*/ None)
.await?;
fs.write_file(
&nested_agents_uri,
b"child doc".to_vec(),
/*sandbox*/ None,
)
.await?;
Ok::<(), anyhow::Error>(())
}),
)
@@ -314,8 +335,9 @@ 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"))?;
fs.write_file(
&cwd.join("AGENTS.md"),
&agents_md_uri,
b"project doc".to_vec(),
/*sandbox*/ None,
)
@@ -367,8 +389,9 @@ 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"))?;
fs.write_file(
&cwd.join("AGENTS.md"),
&agents_md_uri,
PROJECT_INSTRUCTIONS.as_bytes().to_vec(),
/*sandbox*/ None,
)
@@ -392,7 +415,7 @@ async fn fresh_thread_composes_global_before_project_and_reports_sources() -> Re
)?;
test.fs()
.write_file(
&project_source,
&PathUri::from_path(&project_source)?,
NEW_PROJECT_INSTRUCTIONS.as_bytes().to_vec(),
/*sandbox*/ None,
)
+18 -13
View File
@@ -38,6 +38,7 @@ use codex_protocol::user_input::UserInput;
#[cfg(target_os = "linux")]
use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use core_test_support::PathBufExt;
use core_test_support::assert_regex_match;
use core_test_support::get_remote_test_env;
@@ -1329,25 +1330,24 @@ 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)?;
fs.create_directory(
&cwd,
&cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.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"))?;
fs.write_file(
&repo_root.join(".git"),
&git_uri,
b"gitdir: /tmp/fake-worktree\n".to_vec(),
/*sandbox*/ None,
)
.await?;
fs.write_file(
&repo_root.join("repo.txt"),
b"before\n".to_vec(),
/*sandbox*/ None,
)
.await?;
fs.write_file(&repo_file_uri, b"before\n".to_vec(), /*sandbox*/ None)
.await?;
Ok(())
})
})
@@ -1582,10 +1582,11 @@ 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 _ = fs::remove_dir_all(shared_cwd.as_path());
test.fs()
.remove(
&shared_cwd,
&shared_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -1596,7 +1597,7 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re
fs::create_dir_all(shared_cwd.as_path())?;
test.fs()
.create_directory(
&shared_cwd,
&shared_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -1683,7 +1684,10 @@ async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Re
assert_eq!(fs::read_to_string(shared_cwd.join(file_name))?, "local\n");
assert_eq!(
test.fs()
.read_file_text(&shared_cwd.join(file_name), /*sandbox*/ None)
.read_file_text(
&PathUri::from_path(shared_cwd.join(file_name))?,
/*sandbox*/ None,
)
.await?,
"remote\n"
);
@@ -1710,7 +1714,7 @@ index 0000000000000000000000000000000000000000..9c998f7b995a7327177b38a90d138517
let _ = fs::remove_dir_all(shared_cwd.as_path());
test.fs()
.remove(
&shared_cwd,
&shared_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -1851,8 +1855,9 @@ 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"))?;
fs.write_file(
&cwd.join("binary.dat"),
&binary_path_uri,
vec![0xff, 0xfe, 0xfd],
/*sandbox*/ None,
)
@@ -1,4 +1,5 @@
use codex_features::Feature;
use codex_utils_path_uri::PathUri;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
use core_test_support::responses::mount_sse_once;
@@ -27,7 +28,8 @@ async fn hierarchical_agents_appends_to_project_doc_in_user_instructions() {
})
.with_workspace_setup(|cwd, fs| async move {
let agents_md = cwd.join("AGENTS.md");
fs.write_file(&agents_md, b"be nice".to_vec(), /*sandbox*/ None)
let agents_md_uri = PathUri::from_path(&agents_md)?;
fs.write_file(&agents_md_uri, b"be nice".to_vec(), /*sandbox*/ None)
.await?;
Ok::<(), anyhow::Error>(())
});
+57 -33
View File
@@ -28,6 +28,7 @@ use codex_protocol::request_permissions::RequestPermissionProfile;
use codex_protocol::request_permissions::RequestPermissionsResponse;
use codex_protocol::user_input::UserInput;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use core_test_support::PathBufExt;
use core_test_support::PathExt;
use core_test_support::get_remote_test_env;
@@ -155,19 +156,20 @@ async fn remote_test_env_can_connect_and_use_filesystem() -> Result<()> {
let file_system = test_env.environment().get_filesystem();
let file_path_abs = remote_test_file_path().abs();
let file_path_uri = PathUri::from_path(&file_path_abs)?;
let payload = b"remote-test-env-ok".to_vec();
file_system
.write_file(&file_path_abs, payload.clone(), /*sandbox*/ None)
.write_file(&file_path_uri, payload.clone(), /*sandbox*/ None)
.await?;
let actual = file_system
.read_file(&file_path_abs, /*sandbox*/ None)
.read_file(&file_path_uri, /*sandbox*/ None)
.await?;
assert_eq!(actual, payload);
file_system
.remove(
&file_path_abs,
&file_path_uri,
RemoveOptions {
recursive: false,
force: true,
@@ -295,16 +297,18 @@ 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))?;
test.fs()
.create_directory(
&remote_cwd,
&remote_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
test.fs()
.write_file(
&remote_cwd.join(remote_marker_name),
&remote_marker_uri,
b"remote-routing".to_vec(),
/*sandbox*/ None,
)
@@ -338,7 +342,7 @@ async fn exec_command_routes_to_selected_remote_environment() -> Result<()> {
test.fs()
.remove(
&remote_cwd,
&remote_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -390,9 +394,10 @@ 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)?;
test.fs()
.create_directory(
&remote_write_root,
&remote_write_root_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -527,7 +532,10 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result
);
assert_eq!(
test.fs()
.read_file_text(&remote_target_path, /*sandbox*/ None)
.read_file_text(
&PathUri::from_path(&remote_target_path)?,
/*sandbox*/ None,
)
.await?,
"remote-request-permissions-ok"
);
@@ -538,7 +546,7 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result
test.fs()
.remove(
&remote_cwd,
&PathUri::from_abs_path(&remote_cwd)?,
RemoveOptions {
recursive: true,
force: true,
@@ -567,9 +575,10 @@ 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)?;
test.fs()
.create_directory(
&remote_cwd,
&remote_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -610,7 +619,10 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result<
let remote_contents = test
.fs()
.read_file_text(&remote_cwd.join(file_name), /*sandbox*/ None)
.read_file_text(
&PathUri::from_path(remote_cwd.join(file_name))?,
/*sandbox*/ None,
)
.await?;
assert_eq!(remote_contents, "patched remote freeform\n");
assert!(
@@ -620,7 +632,7 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result<
test.fs()
.remove(
&remote_cwd,
&remote_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -651,9 +663,10 @@ 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)?;
test.fs()
.create_directory(
&remote_cwd,
&remote_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -664,10 +677,11 @@ 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 _ = fs::remove_file(&target_path);
test.fs()
.remove(
&target_path,
&target_path_uri,
RemoveOptions {
recursive: false,
force: true,
@@ -771,7 +785,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
.await;
assert_eq!(
test.fs()
.read_file_text(&target_path, /*sandbox*/ None)
.read_file_text(&target_path_uri, /*sandbox*/ None)
.await?,
"remote\n"
);
@@ -785,7 +799,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
wait_for_completion_without_patch_approval(&test).await;
assert_eq!(
test.fs()
.read_file_text(&target_path, /*sandbox*/ None)
.read_file_text(&target_path_uri, /*sandbox*/ None)
.await?,
"remote updated\n"
);
@@ -793,7 +807,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
let _ = fs::remove_file(&target_path);
test.fs()
.remove(
&target_path,
&target_path_uri,
RemoveOptions {
recursive: false,
force: true,
@@ -803,7 +817,7 @@ async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> {
.await?;
test.fs()
.remove(
&remote_cwd,
&remote_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -832,9 +846,10 @@ 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)?;
test.fs()
.create_directory(
&remote_cwd,
&remote_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -885,7 +900,10 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm
let remote_contents = test
.fs()
.read_file_text(&remote_cwd.join(file_name), /*sandbox*/ None)
.read_file_text(
&PathUri::from_path(remote_cwd.join(file_name))?,
/*sandbox*/ None,
)
.await?;
assert_eq!(remote_contents, "patched remote exec\n");
assert!(
@@ -895,7 +913,7 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm
test.fs()
.remove(
&remote_cwd,
&remote_cwd_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -919,16 +937,18 @@ 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)?;
file_system
.create_directory(
&absolute_path(allowed_dir.clone()),
&allowed_dir_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
file_system
.write_file(
&absolute_path(file_path.clone()),
&file_path_uri,
b"sandboxed hello".to_vec(),
/*sandbox*/ None,
)
@@ -936,13 +956,13 @@ async fn remote_test_env_sandboxed_read_allows_readable_root() -> Result<()> {
let sandbox = read_only_sandbox(allowed_dir.clone());
let contents = file_system
.read_file(&absolute_path(file_path.clone()), Some(&sandbox))
.read_file(&file_path_uri, Some(&sandbox))
.await?;
assert_eq!(contents, b"sandboxed hello");
file_system
.remove(
&absolute_path(allowed_dir),
&allowed_dir_uri,
RemoveOptions {
recursive: true,
force: true,
@@ -976,7 +996,8 @@ async fn remote_test_env_sandboxed_read_rejects_symlink_parent_dotdot_escape() -
secret = secret_path.display(),
))?;
let requested_path = absolute_path(allowed_dir.join("link").join("..").join("secret.txt"));
let requested_path =
PathUri::from_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"),
@@ -1023,7 +1044,7 @@ async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> {
let sandbox = workspace_write_sandbox(allowed_dir.clone());
file_system
.remove(
&absolute_path(symlink_path.clone()),
&PathUri::from_path(&symlink_path)?,
RemoveOptions {
recursive: false,
force: false,
@@ -1033,18 +1054,21 @@ async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> {
.await?;
let symlink_exists = file_system
.get_metadata(&absolute_path(symlink_path), /*sandbox*/ None)
.get_metadata(
&PathUri::from_abs_path(&absolute_path(symlink_path))?,
/*sandbox*/ None,
)
.await
.is_ok();
assert!(!symlink_exists);
let outside = file_system
.read_file_text(&absolute_path(outside_file.clone()), /*sandbox*/ None)
.read_file_text(&PathUri::from_path(&outside_file)?, /*sandbox*/ None)
.await?;
assert_eq!(outside, "outside");
file_system
.remove(
&absolute_path(root),
&PathUri::from_path(&root)?,
RemoveOptions {
recursive: true,
force: true,
@@ -1085,8 +1109,8 @@ async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> {
let sandbox = workspace_write_sandbox(allowed_dir.clone());
file_system
.copy(
&absolute_path(source_symlink),
&absolute_path(copied_symlink.clone()),
&PathUri::from_path(&source_symlink)?,
&PathUri::from_path(&copied_symlink)?,
CopyOptions { recursive: false },
Some(&sandbox),
)
@@ -1117,7 +1141,7 @@ async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> {
file_system
.remove(
&absolute_path(root),
&PathUri::from_path(&root)?,
RemoveOptions {
recursive: true,
force: true,
+4 -1
View File
@@ -48,6 +48,7 @@ use codex_protocol::protocol::McpToolCallBeginEvent;
use codex_protocol::protocol::Op;
use codex_protocol::user_input::UserInput;
use codex_utils_cargo_bin::cargo_bin;
use codex_utils_path_uri::PathUri;
use core_test_support::assert_regex_match;
use core_test_support::remote_env_env_var;
use core_test_support::responses;
@@ -625,8 +626,10 @@ 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)?;
fs.create_directory(
&cwd.join("mcp-configured-cwd"),
&configured_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
+5 -2
View File
@@ -9,6 +9,7 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::Op;
use codex_protocol::user_input::UserInput;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use core_test_support::responses::ev_assistant_message;
use core_test_support::responses::ev_completed;
use core_test_support::responses::ev_response_created;
@@ -29,15 +30,17 @@ 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)?;
fs.create_directory(
&skill_dir,
&skill_dir_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
let contents = format!("---\nname: {name}\ndescription: {description}\n---\n\n{body}\n");
let path = skill_dir.join("SKILL.md");
fs.write_file(&path, contents.into_bytes(), /*sandbox*/ None)
let path_uri = PathUri::from_path(&path)?;
fs.write_file(&path_uri, contents.into_bytes(), /*sandbox*/ None)
.await?;
Ok(())
}
+3 -1
View File
@@ -17,6 +17,7 @@ use codex_protocol::protocol::ExecCommandSource;
use codex_protocol::protocol::ExecCommandStatus;
use codex_protocol::protocol::Op;
use codex_protocol::user_input::UserInput;
use codex_utils_path_uri::PathUri;
use core_test_support::TempDirExt;
use core_test_support::assert_regex_match;
use core_test_support::managed_network_requirements_loader;
@@ -228,9 +229,10 @@ async fn create_workspace_directory(
rel_path: impl AsRef<std::path::Path>,
) -> Result<std::path::PathBuf> {
let abs_path = test.config.cwd.join(rel_path.as_ref());
let abs_path_uri = PathUri::from_path(&abs_path)?;
test.fs()
.create_directory(
&abs_path,
&abs_path_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
+12 -6
View File
@@ -29,6 +29,7 @@ use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::Op;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::user_input::UserInput;
use codex_utils_path_uri::PathUri;
use core_test_support::PathBufExt;
use core_test_support::PathExt;
use core_test_support::get_remote_test_env;
@@ -134,9 +135,10 @@ fn png_bytes(width: u32, height: u32, rgba: [u8; 4]) -> anyhow::Result<Vec<u8>>
async fn create_workspace_directory(test: &TestCodex, rel_path: &str) -> anyhow::Result<PathBuf> {
let abs_path = test.config.cwd.join(rel_path);
let abs_path_uri = PathUri::from_path(&abs_path)?;
test.fs()
.create_directory(
&abs_path,
&abs_path_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
@@ -151,16 +153,18 @@ async fn write_workspace_file(
) -> anyhow::Result<PathBuf> {
let abs_path = test.config.cwd.join(rel_path);
if let Some(parent) = abs_path.parent() {
let parent_uri = PathUri::from_path(&parent)?;
test.fs()
.create_directory(
&parent,
&parent_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
}
let abs_path_uri = PathUri::from_path(&abs_path)?;
test.fs()
.write_file(&abs_path, contents, /*sandbox*/ None)
.write_file(&abs_path_uri, contents, /*sandbox*/ None)
.await?;
Ok(abs_path.into_path_buf())
}
@@ -607,16 +611,18 @@ 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)?;
test.fs()
.create_directory(
&remote_cwd,
&remote_cwd_uri,
CreateDirectoryOptions { recursive: true },
/*sandbox*/ None,
)
.await?;
let png = png_bytes(/*width*/ 1, /*height*/ 1, [0, 255, 0, 255])?;
let image_path_uri = PathUri::from_path(&image_path)?;
test.fs()
.write_file(&image_path, png, /*sandbox*/ None)
.write_file(&image_path_uri, png, /*sandbox*/ None)
.await?;
let remote_selection = TurnEnvironmentSelection {
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
@@ -675,7 +681,7 @@ async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<()
test.fs()
.remove(
&remote_cwd,
&remote_cwd_uri,
RemoveOptions {
recursive: true,
force: true,