Files
codex/codex-rs/ext/connectors/src/executor_plugin.rs
T
jif 9ff8068880 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.
2026-06-24 23:56:50 +01:00

65 lines
2.2 KiB
Rust

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,
}
})
}
}