Codex Handbook
ext/skills/tests/executor_file_system_authority.rs 294 lines
use std::io;use std::sync::Arc;use std::sync::atomic::AtomicUsize;use std::sync::atomic::Ordering;use codex_core_skills::HostLoadedSkills;use codex_core_skills::loader::SkillRoot;use codex_core_skills::loader::load_skills_from_roots;use codex_exec_server::CopyOptions;use codex_exec_server::CreateDirectoryOptions;use codex_exec_server::EnvironmentManager;use codex_exec_server::ExecutorFileSystem;use codex_exec_server::ExecutorFileSystemFuture;use codex_exec_server::FileMetadata;use codex_exec_server::FileSystemReadStream;use codex_exec_server::FileSystemSandboxContext;use codex_exec_server::ReadDirectoryEntry;use codex_exec_server::RemoveOptions;use codex_protocol::capabilities::CapabilityRootLocation;use codex_protocol::capabilities::SelectedCapabilityRoot;use codex_protocol::protocol::SkillScope;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 =    "---\nname: synthetic\ndescription: Synthetic executor skill.\n---\n\nEXECUTOR_ONLY_BODY\n";static NEXT_TEST_ROOT_ID: AtomicUsize = AtomicUsize::new(0);struct SyntheticFileSystem {    alias_root: AbsolutePathBuf,    canonical_root: AbsolutePathBuf,}impl SyntheticFileSystem {    async fn canonicalize(&self, path: &PathUri) -> io::Result<PathUri> {        let path = path.to_abs_path()?;        if path == self.alias_root {            return Ok(PathUri::from_abs_path(&self.canonical_root));        }        self.metadata(&path)?;        Ok(PathUri::from_abs_path(&path))    }    async fn read_file(&self, path: &PathUri) -> io::Result<Vec<u8>> {        if path.to_abs_path()? == self.canonical_root.join("skill/SKILL.md") {            Ok(SKILL_CONTENTS.as_bytes().to_vec())        } else {            Err(io::Error::new(io::ErrorKind::NotFound, "not found"))        }    }    async fn read_directory(&self, path: &PathUri) -> io::Result<Vec<ReadDirectoryEntry>> {        let path = path.to_abs_path()?;        if path == self.canonical_root {            Ok(vec![ReadDirectoryEntry {                file_name: "skill".to_string(),                is_directory: true,                is_file: false,            }])        } else if path == self.canonical_root.join("skill") {            Ok(vec![ReadDirectoryEntry {                file_name: "SKILL.md".to_string(),                is_directory: false,                is_file: true,            }])        } else {            Err(io::Error::new(io::ErrorKind::NotFound, "not found"))        }    }    fn metadata(&self, path: &AbsolutePathBuf) -> io::Result<FileMetadata> {        let skill_dir = self.canonical_root.join("skill");        let skill_path = skill_dir.join("SKILL.md");        let (is_directory, is_file) = if path == &self.canonical_root || path == &skill_dir {            (true, false)        } else if path == &skill_path {            (false, true)        } else {            return Err(io::Error::new(io::ErrorKind::NotFound, "not found"));        };        Ok(FileMetadata {            is_directory,            is_file,            is_symlink: false,            size: 0,            created_at_ms: 0,            modified_at_ms: 0,        })    }}impl ExecutorFileSystem for SyntheticFileSystem {    fn canonicalize<'a>(        &'a self,        path: &'a PathUri,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, PathUri> {        Box::pin(SyntheticFileSystem::canonicalize(self, path))    }    fn read_file<'a>(        &'a self,        path: &'a PathUri,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, Vec<u8>> {        Box::pin(SyntheticFileSystem::read_file(self, path))    }    fn read_file_stream<'a>(        &'a self,        _path: &'a PathUri,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, FileSystemReadStream> {        Box::pin(async {            Err(io::Error::new(                io::ErrorKind::Unsupported,                "synthetic filesystem does not support streaming reads",            ))        })    }    fn write_file<'a>(        &'a self,        _path: &'a PathUri,        _contents: Vec<u8>,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, ()> {        Box::pin(async move { Err(io::Error::new(io::ErrorKind::Unsupported, "read only")) })    }    fn create_directory<'a>(        &'a self,        _path: &'a PathUri,        _options: CreateDirectoryOptions,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, ()> {        Box::pin(async move { Err(io::Error::new(io::ErrorKind::Unsupported, "read only")) })    }    fn get_metadata<'a>(        &'a self,        path: &'a PathUri,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, FileMetadata> {        Box::pin(async move { self.metadata(&path.to_abs_path()?) })    }    fn read_directory<'a>(        &'a self,        path: &'a PathUri,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, Vec<ReadDirectoryEntry>> {        Box::pin(SyntheticFileSystem::read_directory(self, path))    }    fn remove<'a>(        &'a self,        _path: &'a PathUri,        _options: RemoveOptions,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, ()> {        Box::pin(async move { Err(io::Error::new(io::ErrorKind::Unsupported, "read only")) })    }    fn copy<'a>(        &'a self,        _source_path: &'a PathUri,        _destination_path: &'a PathUri,        _options: CopyOptions,        _sandbox: Option<&'a FileSystemSandboxContext>,    ) -> ExecutorFileSystemFuture<'a, ()> {        Box::pin(async move { Err(io::Error::new(io::ErrorKind::Unsupported, "read only")) })    }}#[tokio::test]async fn skill_loading_and_reads_use_the_supplied_executor_file_system() {    let test_root =        std::env::temp_dir().join(format!("codex-executor-skill-fs-{}", std::process::id()));    let alias_root = AbsolutePathBuf::from_absolute_path_checked(test_root.join("alias"))        .expect("absolute path");    let canonical_root = AbsolutePathBuf::from_absolute_path_checked(test_root.join("canonical"))        .expect("absolute path");    assert!(!alias_root.as_path().exists());    assert!(!canonical_root.as_path().exists());    let outcome = load_skills_from_roots([SkillRoot {        path: alias_root.clone(),        scope: SkillScope::User,        file_system: Arc::new(SyntheticFileSystem {            alias_root,            canonical_root: canonical_root.clone(),        }),        plugin_id: None,        plugin_root: None,    }])    .await;    assert_eq!(outcome.errors, Vec::new());    assert_eq!(outcome.skills.len(), 1);    let skill = outcome.skills[0].clone();    assert_eq!(skill.name, "synthetic");    assert_eq!(        skill.path_to_skills_md,        canonical_root.join("skill/SKILL.md")    );    let loaded = HostLoadedSkills::new(Arc::new(outcome));    assert_eq!(        loaded.read_skill_text(&skill).await.expect("skill body"),        SKILL_CONTENTS    );}#[tokio::test]async fn selected_root_id_distinguishes_identical_executor_paths() {    let test_root = create_local_skill_root("root-identity").expect("create local skill root");    let root_path = test_root.to_string_lossy().into_owned();    let canonical_root = AbsolutePathBuf::from_absolute_path_checked(&test_root)        .expect("absolute skill root")        .canonicalize()        .expect("canonicalize skill root")        .to_string_lossy()        .replace('\\', "/");    let provider = ExecutorSkillProvider::new_with_restriction_product(        Arc::new(EnvironmentManager::default_for_tests()),        /*restriction_product*/ None,    );    let catalog = provider        .list(SkillListQuery {            turn_id: "turn-1".to_string(),            executor_roots: ["root-a", "root-b"]                .into_iter()                .map(|id| SelectedCapabilityRoot {                    id: id.to_string(),                    location: CapabilityRootLocation::Environment {                        environment_id: "local".to_string(),                        path: root_path.clone(),                    },                })                .collect(),            host: None,            include_host_skills: false,            include_bundled_skills: true,            include_orchestrator_skills: false,            mcp_resources: None,        })        .await        .expect("list executor skills");    assert_eq!(        catalog            .entries            .iter()            .map(|entry| (                entry.authority.id.clone(),                entry.display_path.clone().expect("display path"),            ))            .collect::<Vec<_>>(),        vec![            (                "root-a".to_string(),                format!(                    "skill://root-a/{}/skill/SKILL.md",                    canonical_root.trim_start_matches('/')                ),            ),            (                "root-b".to_string(),                format!(                    "skill://root-b/{}/skill/SKILL.md",                    canonical_root.trim_start_matches('/')                ),            ),        ]    );    std::fs::remove_dir_all(test_root).expect("remove skill directory");}fn create_local_skill_root(label: &str) -> io::Result<std::path::PathBuf> {    let id = NEXT_TEST_ROOT_ID.fetch_add(1, Ordering::Relaxed);    let test_root = std::env::temp_dir().join(format!(        "codex-executor-skill-{label}-{}-{id}",        std::process::id()    ));    let skill_dir = test_root.join("skill");    std::fs::create_dir_all(&skill_dir)?;    std::fs::write(skill_dir.join("SKILL.md"), SKILL_CONTENTS)?;    Ok(test_root)}