mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why Selected capability roots belong to the executor filesystem, not the app-server host. Converting their path strings into the host's native `Path` breaks whenever the two machines use different path conventions, such as a Windows executor behind a Unix app-server. This PR establishes `PathUri` as the selected-plugin boundary so the executor remains authoritative for its paths. ## What changed - Require `selectedCapabilityRoots[].location.path` to be a canonical `file:` URI and deserialize it directly as `PathUri`; native path strings are rejected. - Update the app-server schema, generated TypeScript, examples, and request coverage for the URI contract. - Keep selected roots, resolved plugin locations, manifest paths, and manifest resources as `PathUri`. - Inspect and read plugin roots and manifests only through the selected environment's `ExecutorFileSystem`. - Parse executor manifests with the shared URI-native parser from #29620 instead of projecting them onto the host filesystem. - Enforce resource containment lexically and preserve the root URI's POSIX or Windows path convention. - Cover foreign Windows plugin roots and URI-native manifest resources. ```text thread/start selectedCapabilityRoots[].location.path = "file:///C:/plugins/demo" | PathUri v ExecutorFileSystem | +--> plugin.json +--> manifest resources ``` This PR stops at the shared selected-plugin representation. The next two PRs remove the remaining host-path projections in the skill and MCP consumers. ## Stack 1. #29614 — add lexical `PathUri` containment. 2. #29620 — share URI-native manifest path resolution. 3. **This PR** — keep selected plugin roots and resources URI-native. 4. #29626 — load executor skills without host path conversion. 5. #29628 — resolve executor MCP working directories without host path conversion.
52 lines
1.7 KiB
Rust
52 lines
1.7 KiB
Rust
use codex_utils_path_uri::LegacyAppPathString;
|
|
use codex_utils_path_uri::PathUri;
|
|
use schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use ts_rs::TS;
|
|
|
|
/// A user-selected root that can expose one or more runtime capabilities.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SelectedCapabilityRoot {
|
|
/// Stable identifier supplied by the capability selection platform.
|
|
pub id: String,
|
|
/// Where the selected root can be resolved.
|
|
pub location: CapabilityRootLocation,
|
|
}
|
|
|
|
/// Location used to resolve a selected capability root.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CapabilityRootLocation {
|
|
/// A path owned by an execution environment.
|
|
Environment {
|
|
#[serde(rename = "environmentId")]
|
|
#[ts(rename = "environmentId")]
|
|
environment_id: String,
|
|
/// Absolute path for the root in the selected environment.
|
|
#[serde(deserialize_with = "deserialize_path_uri_from_api_path")]
|
|
#[schemars(with = "String")]
|
|
#[ts(type = "string")]
|
|
path: PathUri,
|
|
},
|
|
}
|
|
|
|
fn deserialize_path_uri_from_api_path<'de, D>(deserializer: D) -> Result<PathUri, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let path = LegacyAppPathString::deserialize(deserializer)?;
|
|
if let Ok(path_uri) = PathUri::parse(path.as_str()) {
|
|
return Ok(path_uri);
|
|
}
|
|
path.try_into().map_err(serde::de::Error::custom)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "capabilities_tests.rs"]
|
|
mod tests;
|