Add JSON output for plugin subcommands (#26631)

## Summary
- Follow-up to #25330 and #26417
- Add `--json` output for `codex plugin add` and `codex plugin remove`
- Add `--json` output for `codex plugin marketplace
add/list/upgrade/remove`
- Keep existing human-readable output unchanged
- Keep existing error handling/stderr behavior unchanged; `--json`
changes successful stdout output only
- Align marketplace add/remove JSON field names with the existing
app-server protocol shape
- Add CLI coverage for plugin and marketplace JSON outputs

## Validation
- `just fmt`
- `just fix -p codex-cli`
- `just test -p codex-cli`
This commit is contained in:
mpc-oai
2026-06-05 14:40:31 -05:00
committed by GitHub
parent 055c7a7c53
commit bb7d19bc24
6 changed files with 438 additions and 9 deletions
+89
View File
@@ -338,6 +338,32 @@ async fn marketplace_list_shows_configured_marketplace_names() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn marketplace_list_json_prints_configured_marketplaces() -> Result<()> {
let (codex_home, source) = setup_local_marketplace()?;
let assert = codex_command(codex_home.path())?
.args(["plugin", "marketplace", "list", "--json"])
.assert()
.success();
let stdout = assert.get_output().stdout.as_slice();
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
assert_eq!(
actual,
json!({
"marketplaces": [
{
"name": "debug",
"root": source.path().display().to_string(),
},
],
})
);
Ok(())
}
#[tokio::test]
async fn marketplace_list_includes_home_marketplace_when_present() -> Result<()> {
let codex_home = TempDir::new()?;
@@ -802,6 +828,69 @@ async fn plugin_add_and_remove_updates_installed_plugin_config() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn plugin_add_json_prints_install_outcome() -> Result<()> {
let (codex_home, _source) = setup_local_marketplace()?;
let assert = codex_command(codex_home.path())?
.args(["plugin", "add", "sample@debug", "--json"])
.assert()
.success();
let stdout = assert.get_output().stdout.as_slice();
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
let installed_path = codex_home.path().join("plugins/cache/debug/sample/1.2.3");
let normalized_installed_path = canonicalize_existing_preserving_symlinks(&installed_path)?;
assert_eq!(
actual,
json!({
"pluginId": "sample@debug",
"name": "sample",
"marketplaceName": "debug",
"version": "1.2.3",
"installedPath": normalized_installed_path.display().to_string(),
"authPolicy": "ON_INSTALL",
})
);
Ok(())
}
#[tokio::test]
async fn plugin_remove_json_prints_remove_outcome() -> Result<()> {
let (codex_home, _source) = setup_local_marketplace()?;
codex_command(codex_home.path())?
.args(["plugin", "add", "sample@debug"])
.assert()
.success();
let assert = codex_command(codex_home.path())?
.args([
"plugin",
"remove",
"sample",
"--marketplace",
"debug",
"--json",
])
.assert()
.success();
let stdout = assert.get_output().stdout.as_slice();
let actual: serde_json::Value = serde_json::from_slice(stdout)?;
assert_eq!(
actual,
json!({
"pluginId": "sample@debug",
"name": "sample",
"marketplaceName": "debug",
})
);
Ok(())
}
#[tokio::test]
async fn plugin_add_rejects_unconfigured_repo_local_marketplaces() -> Result<()> {
let (codex_home, source) = setup_unconfigured_local_marketplace()?;