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
2 changes: 2 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 41 additions & 5 deletions codex-rs/app-server/src/config/external_agent_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,16 @@ impl ExternalAgentConfigService {
"plugins migration item is missing details".to_string(),
));
};
let config = ConfigBuilder::default()
.codex_home(self.codex_home.clone())
.fallback_cwd(Some(
cwd.map(Path::to_path_buf)
.unwrap_or_else(|| self.codex_home.clone()),
))
.build()
.await
.map_err(|err| io::Error::other(format!("failed to load config: {err}")))?;
let requirements = config.config_layer_stack.requirements().clone();
let mut outcome = PluginImportOutcome::default();
let plugins_manager = PluginsManager::new(self.codex_home.clone());
for plugin_group in plugins {
Expand Down Expand Up @@ -916,7 +926,8 @@ impl ExternalAgentConfigService {
ref_name: import_source.ref_name,
sparse_paths: Vec::new(),
};
let add_marketplace_outcome = add_marketplace(self.codex_home.clone(), request).await;
let add_marketplace_outcome =
add_marketplace(self.codex_home.clone(), requirements.clone(), request).await;
let marketplace_path = match add_marketplace_outcome {
Ok(add_marketplace_outcome) => {
let Some(marketplace_path) = find_marketplace_manifest_path(
Expand Down Expand Up @@ -954,12 +965,37 @@ impl ExternalAgentConfigService {
continue;
}
};
let install_config = match ConfigBuilder::default()
.codex_home(self.codex_home.clone())
.fallback_cwd(Some(
cwd.map(Path::to_path_buf)
.unwrap_or_else(|| self.codex_home.clone()),
))
.build()
.await
{
Ok(config) => config,
Err(err) => {
record_plugin_import_errors(
&mut outcome,
cwd,
&plugin_ids,
"plugin_import",
format!("failed to reload config after adding marketplace: {err}"),
);
outcome.failed_plugin_ids.extend(plugin_ids);
continue;
}
};
for plugin_name in plugin_names {
match plugins_manager
.install_plugin(PluginInstallRequest {
plugin_name: plugin_name.clone(),
marketplace_path: marketplace_path.clone(),
})
.install_plugin(
&install_config.config_layer_stack,
PluginInstallRequest {
plugin_name: plugin_name.clone(),
marketplace_path: marketplace_path.clone(),
},
)
.await
{
Ok(_) => outcome
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ impl MarketplaceRequestProcessor {
&self,
params: MarketplaceAddParams,
) -> Result<MarketplaceAddResponse, JSONRPCErrorError> {
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
add_marketplace_to_codex_home(
self.config.codex_home.to_path_buf(),
config.config_layer_stack.requirements().clone(),
MarketplaceAddRequest {
source: params.source,
ref_name: params.ref_name,
Expand Down
5 changes: 4 additions & 1 deletion codex-rs/app-server/src/request_processors/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,10 @@ impl PluginRequestProcessor {
marketplace_path,
};

let result = match plugins_manager.install_plugin(request).await {
let result = match plugins_manager
.install_plugin(&config.config_layer_stack, request)
.await
{
Ok(result) => result,
Err(err) => {
warn!(
Expand Down
11 changes: 7 additions & 4 deletions codex-rs/cli/src/marketplace_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ impl MarketplaceCli {
.map_err(anyhow::Error::msg)?;

match subcommand {
MarketplaceSubcommand::Add(args) => run_add(args).await?,
MarketplaceSubcommand::Add(args) => run_add(overrides, args).await?,
MarketplaceSubcommand::List(args) => run_list(overrides, args).await?,
MarketplaceSubcommand::Upgrade(args) => run_upgrade(overrides, args).await?,
MarketplaceSubcommand::Remove(args) => run_remove(args).await?,
Expand All @@ -142,17 +142,20 @@ impl MarketplaceCli {
}
}

async fn run_add(args: AddMarketplaceArgs) -> Result<()> {
async fn run_add(overrides: Vec<(String, toml::Value)>, args: AddMarketplaceArgs) -> Result<()> {
let AddMarketplaceArgs {
source,
ref_name,
sparse_paths,
json,
} = args;

let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
let config = Config::load_with_cli_overrides(overrides)
.await
.context("failed to load configuration")?;
let outcome = add_marketplace(
codex_home.to_path_buf(),
config.codex_home.to_path_buf(),
config.config_layer_stack.requirements().clone(),
MarketplaceAddRequest {
source,
ref_name,
Expand Down
11 changes: 7 additions & 4 deletions codex-rs/cli/src/plugin_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,13 @@ pub async fn run_plugin_add(
&plugin_name,
)?;
let outcome = manager
.install_plugin(PluginInstallRequest {
plugin_name,
marketplace_path: marketplace.path,
})
.install_plugin(
&plugins_input.config_layer_stack,
PluginInstallRequest {
plugin_name,
marketplace_path: marketplace.path,
},
)
.await?;

if json {
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/core-plugins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ codex-plugin = { workspace = true }
codex-protocol = { workspace = true }
codex-tools = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-path = { workspace = true }
Comment thread
xl-openai marked this conversation as resolved.
codex-utils-path-uri = { workspace = true }
codex-utils-plugins = { workspace = true }
chrono = { workspace = true }
dirs = { workspace = true }
flate2 = { workspace = true }
indexmap = { workspace = true, features = ["serde"] }
reqwest = { workspace = true }
regex = { workspace = true }
semver = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
Expand Down
18 changes: 11 additions & 7 deletions codex-rs/core-plugins/src/discoverable_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,14 +932,18 @@ fn string_set(values: &[&str]) -> HashSet<String> {

async fn install_marketplace_plugin(codex_home: &Path, marketplace_root: &Path, plugin_name: &str) {
write_curated_plugin_sha_with(codex_home, TEST_CURATED_PLUGIN_SHA);
let config = load_plugins_config(codex_home, marketplace_root).await;
PluginsManager::new(codex_home.to_path_buf())
.install_plugin(PluginInstallRequest {
plugin_name: plugin_name.to_string(),
marketplace_path: AbsolutePathBuf::try_from(
marketplace_root.join(".agents/plugins/marketplace.json"),
)
.expect("marketplace path"),
})
.install_plugin(
&config.config_layer_stack,
PluginInstallRequest {
plugin_name: plugin_name.to_string(),
marketplace_path: AbsolutePathBuf::try_from(
marketplace_root.join(".agents/plugins/marketplace.json"),
)
.expect("marketplace path"),
},
)
.await
.expect("plugin should install");
}
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/core-plugins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod manager;
pub mod manifest;
pub mod marketplace;
pub mod marketplace_add;
mod marketplace_policy;
pub mod marketplace_remove;
pub mod marketplace_upgrade;
mod plugin_bundle_archive;
Expand All @@ -23,6 +24,8 @@ mod tool_suggest_metadata;
pub const OPENAI_CURATED_MARKETPLACE_NAME: &str = "openai-curated";
pub const OPENAI_API_CURATED_MARKETPLACE_NAME: &str = "openai-api-curated";
pub const OPENAI_BUNDLED_MARKETPLACE_NAME: &str = "openai-bundled";
pub(crate) const OPENAI_BUNDLED_ALPHA_MARKETPLACE_NAME: &str = "openai-bundled-alpha";
pub(crate) const OPENAI_PRIMARY_RUNTIME_MARKETPLACE_NAME: &str = "openai-primary-runtime";

pub fn is_openai_curated_marketplace_name(marketplace_name: &str) -> bool {
marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME
Expand Down
65 changes: 39 additions & 26 deletions codex-rs/core-plugins/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ use crate::marketplace::find_installable_marketplace_plugin;
use crate::marketplace::find_marketplace_plugin;
use crate::marketplace::list_marketplaces;
use crate::marketplace::plugin_interface_with_marketplace_category;
use crate::marketplace_policy::MarketplacePolicy;
use crate::marketplace_upgrade::ConfiguredMarketplaceUpgradeError;
use crate::marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome;
use crate::marketplace_upgrade::configured_git_marketplace_names;
use crate::marketplace_upgrade::upgrade_configured_git_marketplaces;
use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;
use crate::remote::RecommendedPluginsMode;
Expand Down Expand Up @@ -1247,19 +1247,10 @@ impl PluginsManager {

pub async fn install_plugin(
&self,
config_layer_stack: &ConfigLayerStack,
request: PluginInstallRequest,
) -> Result<PluginInstallOutcome, PluginInstallError> {
let resolved = match find_installable_marketplace_plugin(
&request.marketplace_path,
&request.plugin_name,
self.restriction_product,
) {
Ok(resolved) => resolved,
Err(err) => {
self.track_plugin_install_resolution_failed(&err);
return Err(err.into());
}
};
let resolved = self.resolve_installable_plugin(config_layer_stack, &request)?;
let plugin_id = resolved.plugin_id.clone();
match self.install_resolved_plugin(resolved).await {
Ok(outcome) => Ok(outcome),
Expand All @@ -1274,12 +1265,11 @@ impl PluginsManager {
}
}

pub async fn install_plugin_with_remote_sync(
fn resolve_installable_plugin(
&self,
config: &PluginsConfigInput,
auth: Option<&CodexAuth>,
request: PluginInstallRequest,
) -> Result<PluginInstallOutcome, PluginInstallError> {
config_layer_stack: &ConfigLayerStack,
request: &PluginInstallRequest,
) -> Result<ResolvedMarketplacePlugin, PluginInstallError> {
let resolved = match find_installable_marketplace_plugin(
&request.marketplace_path,
&request.plugin_name,
Expand All @@ -1291,6 +1281,32 @@ impl PluginsManager {
return Err(err.into());
}
};
if let Err(message) =
MarketplacePolicy::from_requirements(config_layer_stack.requirements())
.validate_install(
config_layer_stack,
self.codex_home.as_path(),
&request.marketplace_path,
&resolved.plugin_id.marketplace_name,
)
{
let err = MarketplaceError::InvalidMarketplaceFile {
path: request.marketplace_path.to_path_buf(),
message,
};
self.track_plugin_install_resolution_failed(&err);
return Err(err.into());
}
Ok(resolved)
}

pub async fn install_plugin_with_remote_sync(
&self,
config: &PluginsConfigInput,
auth: Option<&CodexAuth>,
request: PluginInstallRequest,
) -> Result<PluginInstallOutcome, PluginInstallError> {
let resolved = self.resolve_installable_plugin(&config.config_layer_stack, &request)?;
let plugin_id = resolved.plugin_id.as_key();
// This only forwards the backend mutation before the local install flow.
if let Err(err) = crate::remote_legacy::enable_remote_plugin(
Expand Down Expand Up @@ -1989,21 +2005,18 @@ impl PluginsManager {
config: &PluginsConfigInput,
marketplace_name: Option<&str>,
) -> Result<ConfiguredMarketplaceUpgradeOutcome, String> {
let mut outcome = upgrade_configured_git_marketplaces(
self.codex_home.as_path(),
&config.config_layer_stack,
marketplace_name,
);
if let Some(marketplace_name) = marketplace_name
&& !configured_git_marketplace_names(&config.config_layer_stack)
.iter()
.any(|name| name == marketplace_name)
&& outcome.selected_marketplaces.is_empty()
{
return Err(format!(
"marketplace `{marketplace_name}` is not configured as a Git marketplace"
));
}

let mut outcome = upgrade_configured_git_marketplaces(
self.codex_home.as_path(),
&config.config_layer_stack,
marketplace_name,
);
if !outcome.upgraded_roots.is_empty() {
match refresh_non_curated_plugin_cache_force_reinstall(
self.codex_home.as_path(),
Expand Down
Loading
Loading