mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Read connector declarations from executor plugins (#29852)
## Why Selected capability roots can live on a different executor and operating system from app-server. Their connector declarations must therefore be read through the executor that owns the package, without converting executor URIs into host paths. This PR adds that authority-bound reader without activating connectors or changing thread startup. ## What changed - Add a small `codex-connectors-extension` crate for executor-owned connector I/O. - Read only the app configuration explicitly declared by the resolved plugin manifest. - Read through the `ExecutorFileSystem` retained by `ResolvedExecutorPlugin`; there is no host-filesystem fallback or default-file probe. - Keep `PathUri` values intact so Windows, Unix, and remote executor paths work from any orchestrator OS. - Return full `AppDeclaration` values so the caller retains declaration names and categories for routing. - Preserve the selected plugin ID and exact executor URI in read and parse errors. The contract is intentionally narrow: selected packages are trusted, valid packages and packages that provide connectors explicitly declare their app configuration. ## Stack scope This PR is stacked on #29851. It only provides the executor-backed reader. #29856 resolves selected roots at thread start, freezes their connector snapshot, and contains the remote-capable end-to-end authority test for the complete path.
This commit is contained in:
Generated
+12
@@ -2596,6 +2596,18 @@ dependencies = [
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-connectors-extension"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"codex-connectors",
|
||||
"codex-core-plugins",
|
||||
"codex-plugin",
|
||||
"codex-utils-path-uri",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codex-context-fragments"
|
||||
version = "0.0.0"
|
||||
|
||||
@@ -48,6 +48,7 @@ members = [
|
||||
"exec-server",
|
||||
"execpolicy",
|
||||
"execpolicy-legacy",
|
||||
"ext/connectors",
|
||||
"ext/extension-api",
|
||||
"ext/goal",
|
||||
"ext/guardian",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
load("//:defs.bzl", "codex_rust_crate")
|
||||
|
||||
codex_rust_crate(
|
||||
name = "connectors",
|
||||
crate_name = "codex_connectors_extension",
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
name = "codex-connectors-extension"
|
||||
version.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "codex_connectors_extension"
|
||||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codex-connectors = { workspace = true }
|
||||
codex-core-plugins = { workspace = true }
|
||||
codex-plugin = { workspace = true }
|
||||
codex-utils-path-uri = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -0,0 +1,64 @@
|
||||
use codex_connectors::parse_plugin_app_config;
|
||||
use codex_core_plugins::ResolvedExecutorPlugin;
|
||||
use codex_plugin::AppDeclaration;
|
||||
use codex_plugin::PluginResourceLocator;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Loads connector declarations from a resolved plugin through its owning executor.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct ExecutorPluginConnectorProvider;
|
||||
|
||||
/// Failure to load connector declarations from an executor plugin.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExecutorPluginConnectorProviderError {
|
||||
#[error("failed to read app config for selected plugin `{plugin_id}` at `{path}`: {source}")]
|
||||
ReadConfig {
|
||||
plugin_id: String,
|
||||
path: PathUri,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
#[error("failed to parse app config for selected plugin `{plugin_id}` at `{path}`: {source}")]
|
||||
ParseConfig {
|
||||
plugin_id: String,
|
||||
path: PathUri,
|
||||
#[source]
|
||||
source: serde_json::Error,
|
||||
},
|
||||
}
|
||||
|
||||
impl ExecutorPluginConnectorProvider {
|
||||
/// Returns the connector declarations contributed by `plugin`.
|
||||
pub async fn load(
|
||||
&self,
|
||||
plugin: &ResolvedExecutorPlugin,
|
||||
) -> Result<Vec<AppDeclaration>, ExecutorPluginConnectorProviderError> {
|
||||
let resolved_plugin = plugin.plugin();
|
||||
let plugin_id = resolved_plugin.selected_root_id();
|
||||
let Some(PluginResourceLocator::Environment {
|
||||
path: config_path, ..
|
||||
}) = resolved_plugin.manifest().paths.apps.as_ref()
|
||||
else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let contents = plugin
|
||||
.file_system()
|
||||
.read_file_text(config_path, /*sandbox*/ None)
|
||||
.await
|
||||
.map_err(|source| ExecutorPluginConnectorProviderError::ReadConfig {
|
||||
plugin_id: plugin_id.to_string(),
|
||||
path: config_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
parse_plugin_app_config(&contents).map_err(|source| {
|
||||
ExecutorPluginConnectorProviderError::ParseConfig {
|
||||
plugin_id: plugin_id.to_string(),
|
||||
path: config_path.clone(),
|
||||
source,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
//! Executor-backed connector declaration loading.
|
||||
|
||||
mod executor_plugin;
|
||||
|
||||
pub use executor_plugin::ExecutorPluginConnectorProvider;
|
||||
pub use executor_plugin::ExecutorPluginConnectorProviderError;
|
||||
Reference in New Issue
Block a user