diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 51a6cd08cc6c..46dccdeae720 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2515,6 +2515,7 @@ dependencies = [ "codex-protocol", "codex-utils-absolute-path", "codex-utils-path", + "codex-utils-path-uri", "core-foundation 0.9.4", "dns-lookup", "dunce", @@ -2768,6 +2769,7 @@ dependencies = [ "codex-skills", "codex-utils-absolute-path", "codex-utils-output-truncation", + "codex-utils-path-uri", "codex-utils-plugins", "dirs", "dunce", @@ -2848,6 +2850,7 @@ dependencies = [ "codex-shell-command", "codex-test-binary-support", "codex-utils-absolute-path", + "codex-utils-path-uri", "codex-utils-pty", "codex-utils-rustls-provider", "ctor 0.6.3", @@ -3004,6 +3007,7 @@ dependencies = [ "async-trait", "codex-protocol", "codex-utils-absolute-path", + "codex-utils-path-uri", "serde", ] @@ -3820,6 +3824,7 @@ dependencies = [ "codex-extension-api", "codex-protocol", "codex-utils-absolute-path", + "codex-utils-path-uri", "codex-utils-string", "pretty_assertions", "tokio", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index b87b0cf39f1f..ad4ebf7a792f 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -237,6 +237,7 @@ codex-utils-json-to-toml = { path = "utils/json-to-toml" } codex-utils-oss = { path = "utils/oss" } codex-utils-output-truncation = { path = "utils/output-truncation" } codex-utils-path = { path = "utils/path-utils" } +codex-utils-path-uri = { path = "utils/path-uri" } codex-utils-plugins = { path = "utils/plugins" } codex-utils-pty = { path = "utils/pty" } codex-utils-rustls-provider = { path = "utils/rustls-provider" } diff --git a/codex-rs/config/Cargo.toml b/codex-rs/config/Cargo.toml index f77a76b764cb..31e6be005a84 100644 --- a/codex-rs/config/Cargo.toml +++ b/codex-rs/config/Cargo.toml @@ -62,6 +62,7 @@ windows-sys = { version = "0.52", features = [ ] } [dev-dependencies] +codex-utils-path-uri = { workspace = true } pretty_assertions = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true, features = ["full"] } diff --git a/codex-rs/config/src/loader/tests.rs b/codex-rs/config/src/loader/tests.rs index 6492f9a0c08e..7dc9b13bd031 100644 --- a/codex-rs/config/src/loader/tests.rs +++ b/codex-rs/config/src/loader/tests.rs @@ -7,8 +7,8 @@ use codex_file_system::FileSystemResult; use codex_file_system::FileSystemSandboxContext; use codex_file_system::ReadDirectoryEntry; use codex_file_system::RemoveOptions; +use codex_utils_path_uri::PathUri; use pretty_assertions::assert_eq; -use std::path::Path; use tempfile::tempdir; struct TestFileSystem; @@ -17,22 +17,12 @@ struct TestFileSystem; impl ExecutorFileSystem for TestFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, _sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { - path.canonicalize() - } - - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - Ok(base_path.join(path)) - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - Ok(path.parent()) + ) -> FileSystemResult { + let path = path.to_abs_path()?; + let canonicalized = path.canonicalize()?; + PathUri::from_abs_path(&canonicalized) } async fn read_file( diff --git a/codex-rs/core-skills/Cargo.toml b/codex-rs/core-skills/Cargo.toml index 3c18bee60368..9473fa8e46cb 100644 --- a/codex-rs/core-skills/Cargo.toml +++ b/codex-rs/core-skills/Cargo.toml @@ -26,6 +26,7 @@ codex-protocol = { workspace = true } codex-skills = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-output-truncation = { workspace = true } +codex-utils-path-uri = { workspace = true } codex-utils-plugins = { workspace = true } dirs = { workspace = true } dunce = { workspace = true } diff --git a/codex-rs/core-skills/src/loader.rs b/codex-rs/core-skills/src/loader.rs index e2c3896cbab9..d4dc8b3b73b0 100644 --- a/codex-rs/core-skills/src/loader.rs +++ b/codex-rs/core-skills/src/loader.rs @@ -19,6 +19,7 @@ use codex_protocol::protocol::Product; use codex_protocol::protocol::SkillScope; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBufGuard; +use codex_utils_path_uri::PathUri; use codex_utils_plugins::PluginSkillRoot; use codex_utils_plugins::plugin_namespace_for_skill_path; use dirs::home_dir; @@ -475,8 +476,12 @@ async fn canonicalize_for_skill_identity( fs: &dyn ExecutorFileSystem, path: &AbsolutePathBuf, ) -> AbsolutePathBuf { - fs.canonicalize(path, /*sandbox*/ None) + let Ok(path_uri) = PathUri::from_abs_path(path) else { + return path.clone(); + }; + fs.canonicalize(&path_uri, /*sandbox*/ None) .await + .and_then(|path| path.to_abs_path()) .unwrap_or_else(|_| path.clone()) } diff --git a/codex-rs/exec-server/Cargo.toml b/codex-rs/exec-server/Cargo.toml index eadab83fe703..3173ad1bdda7 100644 --- a/codex-rs/exec-server/Cargo.toml +++ b/codex-rs/exec-server/Cargo.toml @@ -24,6 +24,7 @@ codex-protocol = { workspace = true } codex-sandboxing = { workspace = true } codex-shell-command = { workspace = true } codex-utils-absolute-path = { workspace = true } +codex-utils-path-uri = { workspace = true } codex-utils-pty = { workspace = true } codex-utils-rustls-provider = { workspace = true } futures = { workspace = true } diff --git a/codex-rs/exec-server/src/fs_helper.rs b/codex-rs/exec-server/src/fs_helper.rs index 0a210175b271..ebda194265f5 100644 --- a/codex-rs/exec-server/src/fs_helper.rs +++ b/codex-rs/exec-server/src/fs_helper.rs @@ -238,10 +238,13 @@ pub(crate) async fn run_direct_request( })) } FsHelperRequest::Canonicalize(params) => { + let path = + codex_utils_path_uri::PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?; let path = file_system - .canonicalize(¶ms.path, /*sandbox*/ None) + .canonicalize(&path, /*sandbox*/ None) .await .map_err(map_fs_error)?; + let path = path.to_abs_path().map_err(map_fs_error)?; Ok(FsHelperPayload::Canonicalize(FsCanonicalizeResponse { path, })) @@ -305,23 +308,72 @@ fn map_fs_error(err: io::Error) -> JSONRPCErrorError { #[cfg(test)] mod tests { + use codex_utils_absolute_path::AbsolutePathBuf; + use pretty_assertions::assert_eq; + use serde_json::json; + use super::*; #[test] - fn helper_requests_use_fs_method_names() -> serde_json::Result<()> { - assert_eq!( - serde_json::to_value(FsHelperRequest::WriteFile(FsWriteFileParams { - path: std::env::current_dir() - .expect("cwd") - .join("file") - .as_path() - .try_into() - .expect("absolute path"), + fn helper_protocol_keeps_native_absolute_paths() -> serde_json::Result<()> { + let local_path = + AbsolutePathBuf::from_absolute_path(std::env::current_dir().expect("cwd").join("file")) + .expect("absolute path"); + #[cfg(not(windows))] + let paths = [local_path]; + #[cfg(windows)] + let paths = [ + local_path, + AbsolutePathBuf::from_absolute_path(r"\\server\share\file").expect("absolute UNC path"), + ]; + + for path in paths { + let expected_path = path.to_string_lossy().into_owned(); + + let request = serde_json::to_value(FsHelperRequest::WriteFile(FsWriteFileParams { + path: path.clone(), data_base64: String::new(), sandbox: None, - }))?["operation"], - FS_WRITE_FILE_METHOD, - ); + }))?; + assert_eq!( + request, + json!({ + "operation": FS_WRITE_FILE_METHOD, + "params": { + "path": expected_path.as_str(), + "dataBase64": "", + "sandbox": null, + }, + }), + ); + let request_path = request["params"]["path"] + .as_str() + .expect("request path should be a string"); + assert_eq!(request_path, expected_path); + assert!(!request_path.starts_with("file:")); + + let response = serde_json::to_value(FsHelperResponse::Ok( + FsHelperPayload::Canonicalize(FsCanonicalizeResponse { path }), + ))?; + assert_eq!( + response, + json!({ + "status": "ok", + "payload": { + "operation": FS_CANONICALIZE_METHOD, + "response": { + "path": expected_path.as_str(), + }, + }, + }), + ); + let response_path = response["payload"]["response"]["path"] + .as_str() + .expect("canonicalize response path should be a string"); + assert_eq!(response_path, expected_path); + assert!(!response_path.starts_with("file:")); + } + Ok(()) } } diff --git a/codex-rs/exec-server/src/local_file_system.rs b/codex-rs/exec-server/src/local_file_system.rs index 3f410f926dea..5a5f6d0595db 100644 --- a/codex-rs/exec-server/src/local_file_system.rs +++ b/codex-rs/exec-server/src/local_file_system.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_path_uri::PathUri; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -81,25 +82,13 @@ impl LocalFileSystem { impl ExecutorFileSystem for LocalFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { + ) -> FileSystemResult { let (file_system, sandbox) = self.file_system_for(sandbox)?; file_system.canonicalize(path, sandbox).await } - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - self.unsandboxed.join(base_path, path).await - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - self.unsandboxed.parent(path).await - } - async fn read_file( &self, path: &AbsolutePathBuf, @@ -175,25 +164,13 @@ impl ExecutorFileSystem for LocalFileSystem { impl ExecutorFileSystem for UnsandboxedFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { + ) -> FileSystemResult { reject_platform_sandbox_context(sandbox)?; self.file_system.canonicalize(path, /*sandbox*/ None).await } - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - self.file_system.join(base_path, path).await - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - self.file_system.parent(path).await - } - async fn read_file( &self, path: &AbsolutePathBuf, @@ -282,23 +259,14 @@ impl ExecutorFileSystem for UnsandboxedFileSystem { impl ExecutorFileSystem for DirectFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { + ) -> FileSystemResult { reject_sandbox_context(sandbox)?; - AbsolutePathBuf::from_absolute_path(tokio::fs::canonicalize(path.as_path()).await?) - } - - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - Ok(base_path.join(path)) - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - Ok(path.parent()) + let path = path.to_abs_path()?; + let canonicalized = + AbsolutePathBuf::from_absolute_path(tokio::fs::canonicalize(path.as_path()).await?)?; + PathUri::from_abs_path(&canonicalized) } async fn read_file( diff --git a/codex-rs/exec-server/src/remote_file_system.rs b/codex-rs/exec-server/src/remote_file_system.rs index 54198c93bac0..f5b04e39dfee 100644 --- a/codex-rs/exec-server/src/remote_file_system.rs +++ b/codex-rs/exec-server/src/remote_file_system.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use base64::Engine as _; use base64::engine::general_purpose::STANDARD; use codex_utils_absolute_path::AbsolutePathBuf; -use std::path::Path; +use codex_utils_path_uri::PathUri; use tokio::io; use tracing::trace; @@ -20,8 +20,6 @@ use crate::protocol::FsCanonicalizeParams; use crate::protocol::FsCopyParams; use crate::protocol::FsCreateDirectoryParams; use crate::protocol::FsGetMetadataParams; -use crate::protocol::FsJoinParams; -use crate::protocol::FsParentParams; use crate::protocol::FsReadDirectoryParams; use crate::protocol::FsReadFileParams; use crate::protocol::FsRemoveParams; @@ -45,46 +43,20 @@ impl RemoteFileSystem { impl ExecutorFileSystem for RemoteFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { + ) -> FileSystemResult { trace!("remote fs canonicalize"); + let path = path.to_abs_path()?; let client = self.client.get().await.map_err(map_remote_error)?; let response = client .fs_canonicalize(FsCanonicalizeParams { - path: path.clone(), + path, sandbox: remote_sandbox_context(sandbox), }) .await .map_err(map_remote_error)?; - Ok(response.path) - } - - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - trace!("remote fs join"); - let client = self.client.get().await.map_err(map_remote_error)?; - let response = client - .fs_join(FsJoinParams { - base_path: base_path.clone(), - path: path.to_path_buf(), - }) - .await - .map_err(map_remote_error)?; - Ok(response.path) - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - trace!("remote fs parent"); - let client = self.client.get().await.map_err(map_remote_error)?; - let response = client - .fs_parent(FsParentParams { path: path.clone() }) - .await - .map_err(map_remote_error)?; - Ok(response.path) + PathUri::from_abs_path(&response.path) } async fn read_file( diff --git a/codex-rs/exec-server/src/sandboxed_file_system.rs b/codex-rs/exec-server/src/sandboxed_file_system.rs index 00b6966c3bd1..1460b2eb4ea5 100644 --- a/codex-rs/exec-server/src/sandboxed_file_system.rs +++ b/codex-rs/exec-server/src/sandboxed_file_system.rs @@ -3,7 +3,7 @@ use base64::Engine as _; use base64::engine::general_purpose::STANDARD; use codex_app_server_protocol::JSONRPCErrorError; use codex_utils_absolute_path::AbsolutePathBuf; -use std::path::Path; +use codex_utils_path_uri::PathUri; use tokio::io; use crate::CopyOptions; @@ -55,34 +55,22 @@ impl SandboxedFileSystem { impl ExecutorFileSystem for SandboxedFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { + ) -> FileSystemResult { let sandbox = require_platform_sandbox(sandbox)?; let response = self .run_sandboxed( sandbox, FsHelperRequest::Canonicalize(FsCanonicalizeParams { - path: path.clone(), + path: path.to_abs_path()?, sandbox: None, }), ) .await? .expect_canonicalize() .map_err(map_sandbox_error)?; - Ok(response.path) - } - - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - Ok(base_path.join(path)) - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - Ok(path.parent()) + PathUri::from_abs_path(&response.path) } async fn read_file( 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 b60589d02004..18bc8bbfc633 100644 --- a/codex-rs/exec-server/src/server/file_system_handler.rs +++ b/codex-rs/exec-server/src/server/file_system_handler.rs @@ -3,6 +3,7 @@ use std::io; use base64::Engine as _; use base64::engine::general_purpose::STANDARD; use codex_app_server_protocol::JSONRPCErrorError; +use codex_utils_path_uri::PathUri; use crate::CopyOptions; use crate::CreateDirectoryOptions; @@ -116,11 +117,13 @@ impl FileSystemHandler { &self, params: FsCanonicalizeParams, ) -> Result { + let requested_path = PathUri::from_abs_path(¶ms.path).map_err(map_fs_error)?; let path = self .file_system - .canonicalize(¶ms.path, params.sandbox.as_ref()) + .canonicalize(&requested_path, params.sandbox.as_ref()) .await .map_err(map_fs_error)?; + let path = path.to_abs_path().map_err(map_fs_error)?; Ok(FsCanonicalizeResponse { path }) } @@ -128,11 +131,8 @@ impl FileSystemHandler { &self, params: FsJoinParams, ) -> Result { - let path = self - .file_system - .join(¶ms.base_path, ¶ms.path) - .await - .map_err(map_fs_error)?; + // TODO(anp): remove and migrate callers to PathUri. + let path = params.base_path.join(params.path); Ok(FsJoinResponse { path }) } @@ -140,11 +140,8 @@ impl FileSystemHandler { &self, params: FsParentParams, ) -> Result { - let path = self - .file_system - .parent(¶ms.path) - .await - .map_err(map_fs_error)?; + // TODO(anp): remove and migrate callers to PathUri. + let path = params.path.parent(); Ok(FsParentResponse { path }) } @@ -262,6 +259,24 @@ mod tests { .await .expect("write file"); + let canonicalized = handler + .canonicalize(FsCanonicalizeParams { + path: path.clone(), + sandbox: Some(FileSystemSandboxContext::from_legacy_sandbox_policy( + sandbox_policy.clone(), + sandbox_cwd.clone(), + )), + }) + .await + .expect("canonicalize file"); + assert_eq!( + canonicalized.path, + AbsolutePathBuf::from_absolute_path( + std::fs::canonicalize(path.as_path()).expect("canonical path"), + ) + .expect("absolute canonical path"), + ); + let response = handler .read_file(FsReadFileParams { path, @@ -276,4 +291,34 @@ mod tests { assert_eq!(response.data_base64, STANDARD.encode("ok")); } } + + #[tokio::test] + async fn protocol_join_and_parent_remain_native_path_operations() { + let temp_dir = tempfile::tempdir().expect("tempdir"); + let runtime_paths = ExecServerRuntimePaths::new( + std::env::current_exe().expect("current exe"), + /*codex_linux_sandbox_exe*/ None, + ) + .expect("runtime paths"); + let handler = FileSystemHandler::new(runtime_paths); + let base_path = + AbsolutePathBuf::from_absolute_path(temp_dir.path()).expect("absolute tempdir"); + + let joined = handler + .join(FsJoinParams { + base_path: base_path.clone(), + path: "nested/file.txt".into(), + }) + .await + .expect("join path"); + assert_eq!(joined.path, base_path.join("nested/file.txt")); + + let parent = handler + .parent(FsParentParams { + path: joined.path.clone(), + }) + .await + .expect("parent path"); + assert_eq!(parent.path, joined.path.parent()); + } } diff --git a/codex-rs/exec-server/tests/file_system/shared.rs b/codex-rs/exec-server/tests/file_system/shared.rs index 34e3111d55cf..7ae8b407342a 100644 --- a/codex-rs/exec-server/tests/file_system/shared.rs +++ b/codex-rs/exec-server/tests/file_system/shared.rs @@ -9,6 +9,7 @@ use codex_protocol::models::FileSystemPermissions; use codex_protocol::models::PermissionProfile; use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy; use codex_sandboxing::policy_transforms::effective_network_sandbox_policy; +use codex_utils_path_uri::PathUri; use pretty_assertions::assert_eq; use std::path::Path; use tempfile::TempDir; @@ -130,40 +131,25 @@ async fn file_system_write_file_writes_bytes( Ok(()) } -#[test_case(FileSystemImplementation::Local ; "local")] -#[test_case(FileSystemImplementation::Remote ; "remote")] -#[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn file_system_join_and_parent_preserve_lexical_paths( - implementation: FileSystemImplementation, -) -> Result<()> { - let context = create_file_system_context(implementation).await?; - let file_system = context.file_system; - +#[test] +fn path_uri_join_and_parent_preserve_lexical_paths() -> Result<()> { let tmp = TempDir::new()?; let source_dir = tmp.path().join("source"); - let joined_nested = file_system - .join(&absolute_path(&source_dir), Path::new("nested/note.txt")) - .await - .with_context(|| format!("mode={implementation}"))?; + let source_dir_uri = PathUri::from_path(&source_dir)?; + let joined_nested = source_dir_uri.join("nested/note.txt")?; assert_eq!( joined_nested, - absolute_path(source_dir.join("nested").join("note.txt")) + PathUri::from_path(source_dir.join("nested").join("note.txt"))? ); - let joined_parent = file_system - .parent(&joined_nested) - .await - .with_context(|| format!("mode={implementation}"))?; + let joined_parent = joined_nested.parent(); assert_eq!( joined_parent, - Some(absolute_path(source_dir.join("nested"))) + Some(PathUri::from_path(source_dir.join("nested"))?) ); - let joined_parent_traversal = file_system - .join(&absolute_path(&source_dir), Path::new("../outside")) - .await - .with_context(|| format!("mode={implementation}"))?; + let joined_parent_traversal = source_dir_uri.join("../outside")?; assert_eq!( joined_parent_traversal, - absolute_path(source_dir.join("../outside")) + PathUri::from_path(source_dir.join("../outside"))? ); Ok(()) @@ -448,8 +434,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 = absolute_path(alias_dir.join("nested").join("note.txt")); - let expected_path = absolute_path(std::fs::canonicalize(&file_path)?); + let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?; + let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?; assert_ne!(requested_path, expected_path); let canonical_path = file_system @@ -478,8 +464,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 = absolute_path(alias_dir.join("nested").join("note.txt")); - let expected_path = absolute_path(std::fs::canonicalize(&file_path)?); + let requested_path = PathUri::from_path(alias_dir.join("nested").join("note.txt"))?; + let expected_path = PathUri::from_path(std::fs::canonicalize(&file_path)?)?; assert_ne!(requested_path, expected_path); let canonical_path = file_system diff --git a/codex-rs/ext/skills/Cargo.toml b/codex-rs/ext/skills/Cargo.toml index c67d4b0e1bc2..41a1711cc07f 100644 --- a/codex-rs/ext/skills/Cargo.toml +++ b/codex-rs/ext/skills/Cargo.toml @@ -24,5 +24,6 @@ codex-utils-string = { workspace = true } [dev-dependencies] async-trait = { workspace = true } +codex-utils-path-uri = { workspace = true } pretty_assertions = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/codex-rs/ext/skills/tests/executor_file_system_authority.rs b/codex-rs/ext/skills/tests/executor_file_system_authority.rs index a8bcf60273b0..1df1f155e648 100644 --- a/codex-rs/ext/skills/tests/executor_file_system_authority.rs +++ b/codex-rs/ext/skills/tests/executor_file_system_authority.rs @@ -1,5 +1,4 @@ use std::io; -use std::path::Path; use std::sync::Arc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -24,6 +23,7 @@ use codex_skills_extension::ExecutorSkillProvider; use codex_skills_extension::provider::SkillListQuery; use codex_skills_extension::provider::SkillProvider; use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_path_uri::PathUri; use pretty_assertions::assert_eq; const SKILL_CONTENTS: &str = @@ -60,26 +60,15 @@ impl SyntheticFileSystem { impl ExecutorFileSystem for SyntheticFileSystem { async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, _sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult { - if path == &self.alias_root { - return Ok(self.canonical_root.clone()); + ) -> FileSystemResult { + let path = path.to_abs_path()?; + if path == self.alias_root { + return PathUri::from_abs_path(&self.canonical_root); } - self.metadata(path)?; - Ok(path.clone()) - } - - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult { - Ok(base_path.join(path)) - } - - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult> { - Ok(path.parent()) + self.metadata(&path)?; + PathUri::from_abs_path(&path) } async fn read_file( diff --git a/codex-rs/file-system/Cargo.toml b/codex-rs/file-system/Cargo.toml index 85e083567b70..bae75275763c 100644 --- a/codex-rs/file-system/Cargo.toml +++ b/codex-rs/file-system/Cargo.toml @@ -11,6 +11,7 @@ workspace = true async-trait = { workspace = true } codex-protocol = { workspace = true } codex-utils-absolute-path = { workspace = true } +codex-utils-path-uri = { workspace = true } serde = { workspace = true, features = ["derive"] } [lib] diff --git a/codex-rs/file-system/src/lib.rs b/codex-rs/file-system/src/lib.rs index 8fad1f5b62b1..b9f9cd7173ca 100644 --- a/codex-rs/file-system/src/lib.rs +++ b/codex-rs/file-system/src/lib.rs @@ -9,6 +9,7 @@ use codex_protocol::permissions::FileSystemSpecialPath; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::SandboxPolicy; use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_path_uri::PathUri; use std::io; use std::path::Path; @@ -136,19 +137,9 @@ pub trait ExecutorFileSystem: Send + Sync { /// Resolves a path within this filesystem. async fn canonicalize( &self, - path: &AbsolutePathBuf, + path: &PathUri, sandbox: Option<&FileSystemSandboxContext>, - ) -> FileSystemResult; - - /// Lexically joins a path onto an existing bound path. - async fn join( - &self, - base_path: &AbsolutePathBuf, - path: &Path, - ) -> FileSystemResult; - - /// Returns the parent directory of a bound path. - async fn parent(&self, path: &AbsolutePathBuf) -> FileSystemResult>; + ) -> FileSystemResult; async fn read_file( &self,