mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
Separate plugin catalog listing from package resolution (#46567)
## What changed - Move catalog types and the `PluginProvider` listing interface into `codex-core-plugins`, with a concrete `PluginListQuery` carrying thread, turn, and MCP resource context. - Remove root resolution from `PluginProvider`; keep executor package access in `ExecutorPluginProvider::resolve_bound`, which retains the filesystem used to resolve the package. - Add shared provider result and error types, and re-export the listing API from `codex-mcp-extension`. ## Testing Update executor and manifest tests to use `resolve_bound`, and assert that malformed manifest errors preserve the underlying `serde_json::Error` as their source. GitOrigin-RevId: c6cd5a0269ed3b8ac9b93f306a70821e63e3671e
This commit is contained in:
committed by
copyberry
parent
ef9f3d022a
commit
a7e069dc3a
@@ -1,76 +0,0 @@
|
||||
//! Source-neutral plugin discovery types.
|
||||
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// One source's complete discovery snapshot; no entries means no plugins were found.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct PluginCatalog {
|
||||
pub entries: Vec<PluginCatalogEntry>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
/// Manifest metadata and every location that can supply the same logical plugin.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct PluginCatalogEntry {
|
||||
pub id: PluginIdentity,
|
||||
pub display_name: String,
|
||||
pub version: Option<String>,
|
||||
/// Serialized `.mcp.json` server declarations keyed by their authored names.
|
||||
pub mcp_servers: BTreeMap<String, String>,
|
||||
pub connector_ids: Vec<String>,
|
||||
pub locations: Vec<PluginSourceLocation>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PluginCatalogEntry {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
formatter
|
||||
.debug_struct("PluginCatalogEntry")
|
||||
.field("id", &self.id)
|
||||
.field("display_name", &self.display_name)
|
||||
.field("version", &self.version)
|
||||
// Declaration values can contain secrets, so only log their keys.
|
||||
.field(
|
||||
"mcp_server_names",
|
||||
&self.mcp_servers.keys().collect::<Vec<_>>(),
|
||||
)
|
||||
.field("connector_ids", &self.connector_ids)
|
||||
.field("locations", &self.locations)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Stable key used to merge discoveries of the same logical plugin.
|
||||
/// Identity is independent of whether the plugin is supplied by the cloud or an executor.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum PluginIdentity {
|
||||
/// Stable ID assigned by plugin-service and shared by its materialized installations.
|
||||
Remote { remote_plugin_id: String },
|
||||
/// Local `name@marketplace` config key for a plugin without a remote ID.
|
||||
Local { plugin_id: String },
|
||||
}
|
||||
|
||||
impl PluginIdentity {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Remote { remote_plugin_id } => remote_plugin_id,
|
||||
Self::Local { plugin_id } => plugin_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A source that can supply a plugin; cloud URIs are opaque, not local paths.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PluginSourceLocation {
|
||||
Cloud {
|
||||
resource_uri: String,
|
||||
bundle_uri: Option<String>,
|
||||
},
|
||||
Executor {
|
||||
/// Executor environment that owns `root`.
|
||||
environment_id: String,
|
||||
/// Local `name@marketplace` key used for this installation in configuration.
|
||||
plugin_id: String,
|
||||
root: PathUri,
|
||||
},
|
||||
}
|
||||
@@ -1,21 +1,16 @@
|
||||
//! Shared plugin package models, source providers, identifiers, and telemetry summaries.
|
||||
//! Shared plugin package models, identifiers, and telemetry summaries.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub use codex_utils_plugins::mention_syntax;
|
||||
|
||||
mod bundled_hooks;
|
||||
mod catalog;
|
||||
mod load_outcome;
|
||||
pub mod manifest;
|
||||
mod plugin_id;
|
||||
mod provider;
|
||||
|
||||
pub use bundled_hooks::is_allowlisted_bundled_cleanup_hook;
|
||||
pub use catalog::PluginCatalog;
|
||||
pub use catalog::PluginCatalogEntry;
|
||||
pub use catalog::PluginIdentity;
|
||||
pub use catalog::PluginSourceLocation;
|
||||
use codex_config::HookEventsToml;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
@@ -25,9 +20,6 @@ pub use load_outcome::prompt_safe_plugin_description;
|
||||
pub use plugin_id::PluginId;
|
||||
pub use plugin_id::PluginIdError;
|
||||
pub use plugin_id::validate_plugin_segment;
|
||||
pub use provider::PluginProvider;
|
||||
pub use provider::PluginProviderError;
|
||||
pub use provider::PluginProviderFuture;
|
||||
pub use provider::PluginResourceLocator;
|
||||
pub use provider::ResolvedPlugin;
|
||||
pub use provider::ResolvedPluginError;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
//! Shared APIs for catalog discovery and root-at-a-time plugin resolution.
|
||||
//! Authority-bound plugin descriptors and resource locations.
|
||||
|
||||
use crate::PluginCatalog;
|
||||
use crate::manifest::PluginManifest;
|
||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::error::Error as StdError;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use thiserror::Error;
|
||||
|
||||
/// A plugin resource paired with the environment that owns its filesystem.
|
||||
@@ -108,36 +103,6 @@ fn environment_resource(
|
||||
})
|
||||
}
|
||||
|
||||
/// Error returned when a provider cannot produce a complete catalog snapshot.
|
||||
#[derive(Clone, Debug, Error, PartialEq, Eq)]
|
||||
#[error("{0}")]
|
||||
pub struct PluginProviderError(pub String);
|
||||
|
||||
pub type PluginProviderFuture<'a, T> =
|
||||
Pin<Box<dyn Future<Output = Result<T, PluginProviderError>> + Send + 'a>>;
|
||||
|
||||
/// Discovers plugin metadata through batch listing or single-root resolution.
|
||||
///
|
||||
/// Cloud providers implement `list`; executor providers continue implementing `resolve`.
|
||||
/// Each contributor invokes its provider's supported operation; the other is a no-op.
|
||||
pub trait PluginProvider<ListQuery = ()>: Send + Sync {
|
||||
/// Error returned by root-at-a-time resolution.
|
||||
type Error: StdError + Send + Sync + 'static;
|
||||
|
||||
/// Returns a complete snapshot.
|
||||
fn list(&self, _query: ListQuery) -> PluginProviderFuture<'_, PluginCatalog> {
|
||||
Box::pin(async { Ok(PluginCatalog::default()) })
|
||||
}
|
||||
|
||||
/// Resolves one selected root using the filesystem authority named by that root.
|
||||
fn resolve(
|
||||
&self,
|
||||
_root: &SelectedCapabilityRoot,
|
||||
) -> impl Future<Output = Result<Option<ResolvedPlugin>, Self::Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "provider_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
Reference in New Issue
Block a user