use async_trait::async_trait; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::SandboxPolicy; use codex_utils_absolute_path::AbsolutePathBuf; use tokio::io; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CreateDirectoryOptions { pub recursive: bool, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RemoveOptions { pub recursive: bool, pub force: bool, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct CopyOptions { pub recursive: bool, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct FileMetadata { pub is_directory: bool, pub is_file: bool, pub is_symlink: bool, pub created_at_ms: i64, pub modified_at_ms: i64, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct ReadDirectoryEntry { pub file_name: String, pub is_directory: bool, pub is_file: bool, } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct FileSystemSandboxContext { pub sandbox_policy: SandboxPolicy, pub windows_sandbox_level: WindowsSandboxLevel, #[serde(default)] pub windows_sandbox_private_desktop: bool, #[serde(default)] pub use_legacy_landlock: bool, pub additional_permissions: Option, } impl FileSystemSandboxContext { pub fn new(sandbox_policy: SandboxPolicy) -> Self { Self { sandbox_policy, windows_sandbox_level: WindowsSandboxLevel::Disabled, windows_sandbox_private_desktop: false, use_legacy_landlock: false, additional_permissions: None, } } pub fn should_run_in_sandbox(&self) -> bool { matches!( self.sandbox_policy, SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. } ) } } pub type FileSystemResult = io::Result; #[async_trait] pub trait ExecutorFileSystem: Send + Sync { async fn read_file( &self, path: &AbsolutePathBuf, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult>; /// Reads a file and decodes it as UTF-8 text. async fn read_file_text( &self, path: &AbsolutePathBuf, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult { let bytes = self.read_file(path, sandbox).await?; String::from_utf8(bytes).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err)) } async fn write_file( &self, path: &AbsolutePathBuf, contents: Vec, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult<()>; async fn create_directory( &self, path: &AbsolutePathBuf, create_directory_options: CreateDirectoryOptions, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult<()>; async fn get_metadata( &self, path: &AbsolutePathBuf, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult; async fn read_directory( &self, path: &AbsolutePathBuf, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult>; async fn remove( &self, path: &AbsolutePathBuf, remove_options: RemoveOptions, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult<()>; async fn copy( &self, source_path: &AbsolutePathBuf, destination_path: &AbsolutePathBuf, copy_options: CopyOptions, sandbox: Option<&FileSystemSandboxContext>, ) -> FileSystemResult<()>; }