Codex Handbook
core-plugins/src/provider.rs 250 lines
use crate::manifest::parse_plugin_manifest;use codex_exec_server::EnvironmentManager;use codex_exec_server::ExecutorFileSystem;use codex_plugin::PluginProvider;use codex_plugin::ResolvedPlugin;use codex_plugin::ResolvedPluginError;use codex_protocol::capabilities::CapabilityRootLocation;use codex_protocol::capabilities::SelectedCapabilityRoot;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_path_uri::PathUri;use codex_utils_plugins::DISCOVERABLE_PLUGIN_MANIFEST_PATHS;use std::io;use std::path::PathBuf;use std::sync::Arc;use thiserror::Error;/// Failure to resolve an environment-owned capability root as a plugin package.#[derive(Debug, Error)]pub enum ExecutorPluginProviderError {    #[error("selected capability root `{root_id}` has invalid path `{path}`: {message}")]    InvalidRootPath {        root_id: String,        path: String,        message: String,    },    #[error(        "selected capability root `{root_id}` references unavailable environment `{environment_id}`"    )]    UnavailableEnvironment {        root_id: String,        environment_id: String,    },    #[error("failed to inspect selected capability root `{root_id}` at {path}: {source}")]    InspectRoot {        root_id: String,        path: AbsolutePathBuf,        #[source]        source: io::Error,    },    #[error("selected capability root `{root_id}` path {path} is not a directory")]    RootNotDirectory {        root_id: String,        path: AbsolutePathBuf,    },    #[error("failed to inspect plugin manifest for `{root_id}` at {path}: {source}")]    InspectManifest {        root_id: String,        path: AbsolutePathBuf,        #[source]        source: io::Error,    },    #[error("failed to read plugin manifest for `{root_id}` at {path}: {source}")]    ReadManifest {        root_id: String,        path: AbsolutePathBuf,        #[source]        source: io::Error,    },    #[error("failed to parse plugin manifest for `{root_id}` at {path}: {source}")]    ParseManifest {        root_id: String,        path: AbsolutePathBuf,        #[source]        source: serde_json::Error,    },    #[error("failed to construct plugin descriptor for `{root_id}`: {source}")]    ConstructDescriptor {        root_id: String,        #[source]        source: ResolvedPluginError,    },}/// Resolves plugin packages through the filesystem owned by an execution environment.#[derive(Clone, Debug)]pub struct ExecutorPluginProvider {    environment_manager: Arc<EnvironmentManager>,}/// A resolved plugin paired with the concrete filesystem used to read it.#[derive(Clone)]pub struct ResolvedExecutorPlugin {    plugin: ResolvedPlugin,    file_system: Arc<dyn ExecutorFileSystem>,}impl ResolvedExecutorPlugin {    /// Returns the source-neutral plugin descriptor.    pub fn plugin(&self) -> &ResolvedPlugin {        &self.plugin    }    /// Returns the concrete filesystem that resolved the descriptor.    pub fn file_system(&self) -> &dyn ExecutorFileSystem {        self.file_system.as_ref()    }}impl ExecutorPluginProvider {    /// Creates a provider backed by the active execution environments.    pub fn new(environment_manager: Arc<EnvironmentManager>) -> Self {        Self {            environment_manager,        }    }    /// Resolves a plugin and retains the exact filesystem used for package access.    pub async fn resolve_bound(        &self,        selected_root: &SelectedCapabilityRoot,    ) -> Result<Option<ResolvedExecutorPlugin>, ExecutorPluginProviderError> {        let root_id = &selected_root.id;        let plugin_root = selected_plugin_root(selected_root)?;        let CapabilityRootLocation::Environment { environment_id, .. } = &selected_root.location;        let environment = self            .environment_manager            .get_environment(environment_id)            .ok_or_else(|| ExecutorPluginProviderError::UnavailableEnvironment {                root_id: root_id.clone(),                environment_id: environment_id.clone(),            })?;        let file_system = environment.get_filesystem();        let plugin = resolve_plugin_root(selected_root, plugin_root, file_system.as_ref()).await?;        Ok(plugin.map(|plugin| ResolvedExecutorPlugin {            plugin,            file_system,        }))    }}impl PluginProvider for ExecutorPluginProvider {    type Error = ExecutorPluginProviderError;    async fn resolve(        &self,        selected_root: &SelectedCapabilityRoot,    ) -> Result<Option<ResolvedPlugin>, Self::Error> {        self.resolve_bound(selected_root)            .await            .map(|plugin| plugin.map(|plugin| plugin.plugin))    }}fn selected_plugin_root(    selected_root: &SelectedCapabilityRoot,) -> Result<AbsolutePathBuf, ExecutorPluginProviderError> {    let root_id = &selected_root.id;    let CapabilityRootLocation::Environment { path, .. } = &selected_root.location;    let plugin_root = PathBuf::from(path);    if !plugin_root.is_absolute() {        return Err(ExecutorPluginProviderError::InvalidRootPath {            root_id: root_id.clone(),            path: path.clone(),            message: "executor path must be absolute".to_string(),        });    }    AbsolutePathBuf::from_absolute_path_checked(plugin_root).map_err(|err| {        ExecutorPluginProviderError::InvalidRootPath {            root_id: root_id.clone(),            path: path.clone(),            message: err.to_string(),        }    })}async fn resolve_plugin_root(    selected_root: &SelectedCapabilityRoot,    plugin_root: AbsolutePathBuf,    file_system: &dyn ExecutorFileSystem,) -> Result<Option<ResolvedPlugin>, ExecutorPluginProviderError> {    let root_id = &selected_root.id;    let CapabilityRootLocation::Environment { environment_id, .. } = &selected_root.location;    let root_uri = PathUri::from_abs_path(&plugin_root);    let root_metadata = file_system        .get_metadata(&root_uri, /*sandbox*/ None)        .await        .map_err(|source| ExecutorPluginProviderError::InspectRoot {            root_id: root_id.clone(),            path: plugin_root.clone(),            source,        })?;    if !root_metadata.is_directory {        return Err(ExecutorPluginProviderError::RootNotDirectory {            root_id: root_id.clone(),            path: plugin_root,        });    }    let mut manifest_path = None;    for relative_path in DISCOVERABLE_PLUGIN_MANIFEST_PATHS {        let candidate = plugin_root.join(relative_path);        let candidate_uri = PathUri::from_abs_path(&candidate);        match file_system            .get_metadata(&candidate_uri, /*sandbox*/ None)            .await        {            Ok(metadata) if metadata.is_file => {                manifest_path = Some((candidate, candidate_uri));                break;            }            Ok(_) => {}            Err(err) if err.kind() == io::ErrorKind::NotFound => {}            Err(source) => {                return Err(ExecutorPluginProviderError::InspectManifest {                    root_id: root_id.clone(),                    path: candidate,                    source,                });            }        }    }    let Some((manifest_path, manifest_uri)) = manifest_path else {        return Ok(None);    };    let contents = file_system        .read_file_text(&manifest_uri, /*sandbox*/ None)        .await        .map_err(|source| ExecutorPluginProviderError::ReadManifest {            root_id: root_id.clone(),            path: manifest_path.clone(),            source,        })?;    let manifest =        parse_plugin_manifest(&plugin_root, &manifest_path, &contents).map_err(|source| {            ExecutorPluginProviderError::ParseManifest {                root_id: root_id.clone(),                path: manifest_path.clone(),                source,            }        })?;    let plugin = ResolvedPlugin::from_environment(        root_id.clone(),        environment_id.clone(),        plugin_root,        manifest_path,        manifest,    )    .map_err(|source| ExecutorPluginProviderError::ConstructDescriptor {        root_id: root_id.clone(),        source,    })?;    Ok(Some(plugin))}#[cfg(test)]#[path = "provider_tests.rs"]mod tests;