Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ use codex_core::mcp::group_tools_by_server;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_core::parse_cursor;
use codex_core::plugins::MarketplaceError;
use codex_core::plugins::MarketplacePluginSourceSummary;
use codex_core::plugins::MarketplacePluginSource;
use codex_core::plugins::PluginInstallError as CorePluginInstallError;
use codex_core::plugins::PluginInstallRequest;
use codex_core::plugins::PluginReadRequest;
Expand Down Expand Up @@ -5429,8 +5429,8 @@ impl CodexMessageProcessor {
enabled: plugin.enabled,
name: plugin.name,
source: marketplace_plugin_source_to_info(plugin.source),
install_policy: plugin.install_policy.into(),
auth_policy: plugin.auth_policy.into(),
install_policy: plugin.policy.installation.into(),
auth_policy: plugin.policy.authentication.into(),
interface: plugin.interface.map(plugin_interface_to_info),
})
.collect(),
Expand Down Expand Up @@ -5519,8 +5519,8 @@ impl CodexMessageProcessor {
source: marketplace_plugin_source_to_info(outcome.plugin.source),
installed: outcome.plugin.installed,
enabled: outcome.plugin.enabled,
install_policy: outcome.plugin.install_policy.into(),
auth_policy: outcome.plugin.auth_policy.into(),
install_policy: outcome.plugin.policy.installation.into(),
auth_policy: outcome.plugin.policy.authentication.into(),
interface: outcome.plugin.interface.map(plugin_interface_to_info),
},
description: outcome.plugin.description,
Expand Down Expand Up @@ -7456,7 +7456,7 @@ fn plugin_skills_to_info(skills: &[codex_core::skills::SkillMetadata]) -> Vec<Sk
}

fn plugin_interface_to_info(
interface: codex_core::plugins::PluginManifestInterfaceSummary,
interface: codex_core::plugins::PluginManifestInterface,
) -> PluginInterface {
PluginInterface {
display_name: interface.display_name,
Expand All @@ -7476,9 +7476,9 @@ fn plugin_interface_to_info(
}
}

fn marketplace_plugin_source_to_info(source: MarketplacePluginSourceSummary) -> PluginSource {
fn marketplace_plugin_source_to_info(source: MarketplacePluginSource) -> PluginSource {
match source {
MarketplacePluginSourceSummary::Local { path } => PluginSource::Local { path },
MarketplacePluginSource::Local { path } => PluginSource::Local { path },
}
}

Expand Down
26 changes: 19 additions & 7 deletions codex-rs/app-server/tests/suite/v2/plugin_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,12 +655,24 @@ fn write_plugin_marketplace(
install_policy: Option<&str>,
auth_policy: Option<&str>,
) -> std::io::Result<()> {
let install_policy = install_policy
.map(|install_policy| format!(",\n \"installPolicy\": \"{install_policy}\""))
.unwrap_or_default();
let auth_policy = auth_policy
.map(|auth_policy| format!(",\n \"authPolicy\": \"{auth_policy}\""))
.unwrap_or_default();
let policy = if install_policy.is_some() || auth_policy.is_some() {
let installation = install_policy
.map(|installation| format!("\n \"installation\": \"{installation}\""))
.unwrap_or_default();
let separator = if install_policy.is_some() && auth_policy.is_some() {
","
} else {
""
};
let authentication = auth_policy
.map(|authentication| {
format!("{separator}\n \"authentication\": \"{authentication}\"")
})
.unwrap_or_default();
format!(",\n \"policy\": {{{installation}{authentication}\n }}")
} else {
String::new()
};
std::fs::create_dir_all(repo_root.join(".git"))?;
std::fs::create_dir_all(repo_root.join(".agents/plugins"))?;
std::fs::write(
Expand All @@ -674,7 +686,7 @@ fn write_plugin_marketplace(
"source": {{
"source": "local",
"path": "{source_path}"
}}{install_policy}{auth_policy}
}}{policy}
}}
]
}}"#
Expand Down
6 changes: 4 additions & 2 deletions codex-rs/app-server/tests/suite/v2/plugin_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,10 @@ async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Res
"source": "local",
"path": "./plugins/demo-plugin"
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Design"
}
]
Expand Down
6 changes: 4 additions & 2 deletions codex-rs/app-server/tests/suite/v2/plugin_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ async fn plugin_read_returns_plugin_details_with_bundle_contents() -> Result<()>
"source": "local",
"path": "./plugins/demo-plugin"
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Design"
}
]
Expand Down
1 change: 1 addition & 0 deletions codex-rs/core/src/auth_env_telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mod tests {
request_max_retries: None,
stream_max_retries: None,
stream_idle_timeout_ms: None,
websocket_connect_timeout_ms: None,
requires_openai_auth: false,
supports_websockets: false,
};
Expand Down
95 changes: 44 additions & 51 deletions codex-rs/core/src/plugins/manager.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
use super::PluginManifestPaths;
use super::curated_plugins_repo_path;
use super::load_plugin_manifest;
use super::manifest::PluginManifestInterfaceSummary;
use super::manifest::PluginManifestInterface;
use super::marketplace::MarketplaceError;
use super::marketplace::MarketplaceInterfaceSummary;
use super::marketplace::MarketplaceInterface;
use super::marketplace::MarketplacePluginAuthPolicy;
use super::marketplace::MarketplacePluginInstallPolicy;
use super::marketplace::MarketplacePluginSourceSummary;
use super::marketplace::MarketplacePluginPolicy;
use super::marketplace::MarketplacePluginSource;
use super::marketplace::ResolvedMarketplacePlugin;
use super::marketplace::list_marketplaces;
use super::marketplace::load_marketplace_summary;
use super::marketplace::load_marketplace;
use super::marketplace::resolve_marketplace_plugin;
use super::plugin_manifest_name;
use super::plugin_manifest_paths;
use super::read_curated_plugins_sha;
use super::remote::RemotePluginFetchError;
use super::remote::RemotePluginMutationError;
Expand Down Expand Up @@ -99,18 +97,17 @@ pub struct PluginInstallOutcome {
pub struct PluginReadOutcome {
pub marketplace_name: String,
pub marketplace_path: AbsolutePathBuf,
pub plugin: PluginDetailSummary,
pub plugin: PluginDetail,
}

#[derive(Debug, Clone, PartialEq)]
pub struct PluginDetailSummary {
pub struct PluginDetail {
pub id: String,
pub name: String,
pub description: Option<String>,
pub source: MarketplacePluginSourceSummary,
pub install_policy: MarketplacePluginInstallPolicy,
pub auth_policy: MarketplacePluginAuthPolicy,
pub interface: Option<PluginManifestInterfaceSummary>,
pub source: MarketplacePluginSource,
pub policy: MarketplacePluginPolicy,
pub interface: Option<PluginManifestInterface>,
pub installed: bool,
pub enabled: bool,
pub skills: Vec<SkillMetadata>,
Expand All @@ -119,21 +116,20 @@ pub struct PluginDetailSummary {
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConfiguredMarketplaceSummary {
pub struct ConfiguredMarketplace {
pub name: String,
pub path: AbsolutePathBuf,
pub interface: Option<MarketplaceInterfaceSummary>,
pub plugins: Vec<ConfiguredMarketplacePluginSummary>,
pub interface: Option<MarketplaceInterface>,
pub plugins: Vec<ConfiguredMarketplacePlugin>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConfiguredMarketplacePluginSummary {
pub struct ConfiguredMarketplacePlugin {
pub id: String,
pub name: String,
pub source: MarketplacePluginSourceSummary,
pub install_policy: MarketplacePluginInstallPolicy,
pub auth_policy: MarketplacePluginAuthPolicy,
pub interface: Option<PluginManifestInterfaceSummary>,
pub source: MarketplacePluginSource,
pub policy: MarketplacePluginPolicy,
pub interface: Option<PluginManifestInterface>,
pub installed: bool,
pub enabled: bool,
}
Expand Down Expand Up @@ -219,8 +215,8 @@ impl PluginCapabilitySummary {
}
}

impl From<PluginDetailSummary> for PluginCapabilitySummary {
fn from(value: PluginDetailSummary) -> Self {
impl From<PluginDetail> for PluginCapabilitySummary {
fn from(value: PluginDetail) -> Self {
Self {
config_name: value.id,
display_name: value.name,
Expand Down Expand Up @@ -648,7 +644,7 @@ impl PluginsManager {
curated_marketplace_root.join(".agents/plugins/marketplace.json"),
)
.map_err(|_| PluginRemoteSyncError::LocalMarketplaceNotFound)?;
let curated_marketplace = match load_marketplace_summary(&curated_marketplace_path) {
let curated_marketplace = match load_marketplace(&curated_marketplace_path) {
Ok(marketplace) => marketplace,
Err(MarketplaceError::MarketplaceNotFound { .. }) => {
return Err(PluginRemoteSyncError::LocalMarketplaceNotFound);
Expand Down Expand Up @@ -685,7 +681,7 @@ impl PluginsManager {
let plugin_id = PluginId::new(plugin_name.clone(), marketplace_name.clone())?;
let plugin_key = plugin_id.as_key();
let source_path = match plugin.source {
MarketplacePluginSourceSummary::Local { path } => path,
MarketplacePluginSource::Local { path } => path,
};
let current_enabled = configured_plugins
.get(&plugin_key)
Expand Down Expand Up @@ -820,7 +816,7 @@ impl PluginsManager {
&self,
config: &Config,
additional_roots: &[AbsolutePathBuf],
) -> Result<Vec<ConfiguredMarketplaceSummary>, MarketplaceError> {
) -> Result<Vec<ConfiguredMarketplace>, MarketplaceError> {
let (installed_plugins, configured_plugins) = self.configured_plugin_states(config);
let marketplaces = list_marketplaces(&self.marketplace_roots(additional_roots))?;
let mut seen_plugin_keys = HashSet::new();
Expand All @@ -838,7 +834,7 @@ impl PluginsManager {
return None;
}

Some(ConfiguredMarketplacePluginSummary {
Some(ConfiguredMarketplacePlugin {
// Enabled state is keyed by `<plugin>@<marketplace>`, so duplicate
// plugin entries from duplicate marketplace files intentionally
// resolve to the first discovered source.
Expand All @@ -850,14 +846,13 @@ impl PluginsManager {
.unwrap_or(false),
name: plugin.name,
source: plugin.source,
install_policy: plugin.install_policy,
auth_policy: plugin.auth_policy,
policy: plugin.policy,
interface: plugin.interface,
})
})
.collect::<Vec<_>>();

(!plugins.is_empty()).then_some(ConfiguredMarketplaceSummary {
(!plugins.is_empty()).then_some(ConfiguredMarketplace {
name: marketplace.name,
path: marketplace.path,
interface: marketplace.interface,
Expand All @@ -872,7 +867,7 @@ impl PluginsManager {
config: &Config,
request: &PluginReadRequest,
) -> Result<PluginReadOutcome, MarketplaceError> {
let marketplace = load_marketplace_summary(&request.marketplace_path)?;
let marketplace = load_marketplace(&request.marketplace_path)?;
let marketplace_name = marketplace.name.clone();
let plugin = marketplace
.plugins
Expand All @@ -893,23 +888,23 @@ impl PluginsManager {
let plugin_key = plugin_id.as_key();
let (installed_plugins, configured_plugins) = self.configured_plugin_states(config);
let source_path = match &plugin.source {
MarketplacePluginSourceSummary::Local { path } => path.clone(),
MarketplacePluginSource::Local { path } => path.clone(),
};
let manifest = load_plugin_manifest(source_path.as_path()).ok_or_else(|| {
MarketplaceError::InvalidPlugin(
"missing or invalid .codex-plugin/plugin.json".to_string(),
)
})?;
let description = manifest.description.clone();
let manifest_paths = plugin_manifest_paths(&manifest, source_path.as_path());
let skill_roots = plugin_skill_roots(source_path.as_path(), &manifest_paths);
let manifest_paths = &manifest.paths;
let skill_roots = plugin_skill_roots(source_path.as_path(), manifest_paths);
let skills = load_skills_from_roots(skill_roots.into_iter().map(|path| SkillRoot {
path,
scope: SkillScope::User,
}))
.skills;
let apps = load_plugin_apps(source_path.as_path());
let mcp_config_paths = plugin_mcp_config_paths(source_path.as_path(), &manifest_paths);
let mcp_config_paths = plugin_mcp_config_paths(source_path.as_path(), manifest_paths);
let mut mcp_server_names = Vec::new();
for mcp_config_path in mcp_config_paths {
mcp_server_names.extend(
Expand All @@ -924,13 +919,12 @@ impl PluginsManager {
Ok(PluginReadOutcome {
marketplace_name: marketplace.name,
marketplace_path: marketplace.path,
plugin: PluginDetailSummary {
plugin: PluginDetail {
id: plugin_key.clone(),
name: plugin.name,
description,
source: plugin.source,
install_policy: plugin.install_policy,
auth_policy: plugin.auth_policy,
policy: plugin.policy,
interface: plugin.interface,
installed: installed_plugins.contains(&plugin_key),
enabled: configured_plugins
Expand Down Expand Up @@ -1200,7 +1194,7 @@ pub(crate) fn load_plugins_from_layer_stack(
pub(crate) fn plugin_namespace_for_skill_path(path: &Path) -> Option<String> {
for ancestor in path.ancestors() {
if let Some(manifest) = load_plugin_manifest(ancestor) {
return Some(plugin_manifest_name(&manifest, ancestor));
return Some(manifest.name);
}
}

Expand All @@ -1217,7 +1211,7 @@ fn refresh_curated_plugin_cache(
curated_plugins_repo_path(codex_home).join(".agents/plugins/marketplace.json"),
)
.map_err(|_| "local curated marketplace is not available".to_string())?;
let curated_marketplace = load_marketplace_summary(&curated_marketplace_path)
let curated_marketplace = load_marketplace(&curated_marketplace_path)
.map_err(|err| format!("failed to load curated marketplace for cache refresh: {err}"))?;

let mut plugin_sources = HashMap::<String, AbsolutePathBuf>::new();
Expand All @@ -1232,7 +1226,7 @@ fn refresh_curated_plugin_cache(
continue;
}
let source_path = match plugin.source {
MarketplacePluginSourceSummary::Local { path } => path,
MarketplacePluginSource::Local { path } => path,
};
plugin_sources.insert(plugin_name, source_path);
}
Expand Down Expand Up @@ -1329,12 +1323,12 @@ fn load_plugin(config_name: String, plugin: &PluginConfig, store: &PluginStore)
return loaded_plugin;
};

let manifest_paths = plugin_manifest_paths(&manifest, plugin_root.as_path());
loaded_plugin.manifest_name = Some(plugin_manifest_name(&manifest, plugin_root.as_path()));
loaded_plugin.manifest_description = manifest.description;
loaded_plugin.skill_roots = plugin_skill_roots(plugin_root.as_path(), &manifest_paths);
let manifest_paths = &manifest.paths;
loaded_plugin.manifest_name = Some(manifest.name.clone());
loaded_plugin.manifest_description = manifest.description.clone();
loaded_plugin.skill_roots = plugin_skill_roots(plugin_root.as_path(), manifest_paths);
let mut mcp_servers = HashMap::new();
for mcp_config_path in plugin_mcp_config_paths(plugin_root.as_path(), &manifest_paths) {
for mcp_config_path in plugin_mcp_config_paths(plugin_root.as_path(), manifest_paths) {
let plugin_mcp = load_mcp_servers_from_file(plugin_root.as_path(), &mcp_config_path);
for (name, config) in plugin_mcp.mcp_servers {
if mcp_servers.insert(name.clone(), config).is_some() {
Expand Down Expand Up @@ -1396,10 +1390,9 @@ fn default_mcp_config_paths(plugin_root: &Path) -> Vec<AbsolutePathBuf> {

pub fn load_plugin_apps(plugin_root: &Path) -> Vec<AppConnectorId> {
if let Some(manifest) = load_plugin_manifest(plugin_root) {
let manifest_paths = plugin_manifest_paths(&manifest, plugin_root);
return load_apps_from_paths(
plugin_root,
plugin_app_config_paths(plugin_root, &manifest_paths),
plugin_app_config_paths(plugin_root, &manifest.paths),
);
}
load_apps_from_paths(plugin_root, default_app_config_paths(plugin_root))
Expand Down Expand Up @@ -1475,10 +1468,10 @@ pub fn plugin_telemetry_metadata_from_root(
return PluginTelemetryMetadata::from_plugin_id(plugin_id);
};

let manifest_paths = plugin_manifest_paths(&manifest, plugin_root);
let has_skills = !plugin_skill_roots(plugin_root, &manifest_paths).is_empty();
let manifest_paths = &manifest.paths;
let has_skills = !plugin_skill_roots(plugin_root, manifest_paths).is_empty();
let mut mcp_server_names = Vec::new();
for path in plugin_mcp_config_paths(plugin_root, &manifest_paths) {
for path in plugin_mcp_config_paths(plugin_root, manifest_paths) {
mcp_server_names.extend(
load_mcp_servers_from_file(plugin_root, &path)
.mcp_servers
Expand Down
Loading
Loading