[codex] Add marketplace remove command and shared logic (#17752)

## Summary

Move the marketplace remove implementation into shared core logic so
both the CLI command and follow-up app-server RPC can reuse the same
behavior.

This change:
- adds a shared `codex_core::plugins::remove_marketplace(...)` flow
- moves validation, config removal, and installed-root deletion out of
the CLI
- keeps the CLI as a thin wrapper over the shared implementation
- adds focused core coverage for the shared remove path

## Validation

- `just fmt`
- focused local coverage for the shared remove path
- heavier follow-up validation deferred to stacked PR CI
This commit is contained in:
xli-oai
2026-04-17 21:44:47 -07:00
committed by GitHub
parent 6b39d0c657
commit e9c70fff3f
7 changed files with 629 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
use anyhow::Result;
use codex_config::MarketplaceConfigUpdate;
use codex_config::record_user_marketplace;
use codex_core::plugins::marketplace_install_root;
use predicates::str::contains;
use std::path::Path;
use tempfile::TempDir;
fn codex_command(codex_home: &Path) -> Result<assert_cmd::Command> {
let mut cmd = assert_cmd::Command::new(codex_utils_cargo_bin::cargo_bin("codex")?);
cmd.env("CODEX_HOME", codex_home);
Ok(cmd)
}
fn configured_marketplace_update() -> MarketplaceConfigUpdate<'static> {
MarketplaceConfigUpdate {
last_updated: "2026-04-13T00:00:00Z",
last_revision: None,
source_type: "git",
source: "https://github.com/owner/repo.git",
ref_name: Some("main"),
sparse_paths: &[],
}
}
fn write_installed_marketplace(codex_home: &Path, marketplace_name: &str) -> Result<()> {
let root = marketplace_install_root(codex_home).join(marketplace_name);
std::fs::create_dir_all(root.join(".agents/plugins"))?;
std::fs::write(root.join(".agents/plugins/marketplace.json"), "{}")?;
std::fs::write(root.join("marker.txt"), "installed")?;
Ok(())
}
#[tokio::test]
async fn marketplace_remove_deletes_config_and_installed_root() -> Result<()> {
let codex_home = TempDir::new()?;
record_user_marketplace(codex_home.path(), "debug", &configured_marketplace_update())?;
write_installed_marketplace(codex_home.path(), "debug")?;
codex_command(codex_home.path())?
.args(["plugin", "marketplace", "remove", "debug"])
.assert()
.success()
.stdout(contains("Removed marketplace `debug`."));
let config_path = codex_home.path().join("config.toml");
let config = std::fs::read_to_string(config_path)?;
assert!(!config.contains("[marketplaces.debug]"));
assert!(
!marketplace_install_root(codex_home.path())
.join("debug")
.exists()
);
Ok(())
}
#[tokio::test]
async fn marketplace_remove_rejects_unknown_marketplace() -> Result<()> {
let codex_home = TempDir::new()?;
codex_command(codex_home.path())?
.args(["plugin", "marketplace", "remove", "debug"])
.assert()
.failure()
.stderr(contains(
"marketplace `debug` is not configured or installed",
));
Ok(())
}