[codex] Add plugin list JSON output (#25330)

## Summary
- add `--json` output to `codex plugin list` with `installed` and
`available` arrays
- add `--available` for JSON output only; using it without `--json` is
rejected
- keep the existing non-JSON table output unchanged
- add CLI coverage for JSON installed/available output and the
`--available`/`--json` requirement

## Validation
- `just test -p codex-cli plugin_list`
- `just fix -p codex-cli`
- `git diff --check`

Note: `just fmt` ran Rust formatting first, then failed in the Python
ruff step because `openai-codex-cli-bin==0.132.0` has no wheel for this
Linux platform.
This commit is contained in:
xl-openai
2026-06-01 21:27:06 -07:00
committed by GitHub
Unverified
parent c8e5db16c9
commit cb63ee7f5d
2 changed files with 250 additions and 1 deletions
+99
View File
@@ -4,6 +4,8 @@ use codex_config::MarketplaceConfigUpdate;
use codex_config::record_user_marketplace;
use predicates::prelude::PredicateBooleanExt;
use predicates::str::contains;
use pretty_assertions::assert_eq;
use serde_json::json;
use std::path::Path;
use tempfile::TempDir;
@@ -489,6 +491,103 @@ async fn plugin_list_prints_plugins_in_a_table() -> Result<()> {
Ok(())
}
#[tokio::test]
async fn plugin_list_json_prints_available_plugins_when_requested() -> Result<()> {
let (codex_home, source) = setup_local_marketplace()?;
let plugin_path = source.path().join("plugins").join("sample");
let assert = codex_command(codex_home.path())?
.args(["plugin", "list", "--available", "--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!({
"installed": [],
"available": [
{
"pluginId": "sample@debug",
"name": "sample",
"marketplaceName": "debug",
"version": "1.2.3",
"installed": false,
"enabled": false,
"source": {
"source": "local",
"path": plugin_path.display().to_string(),
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
},
],
})
);
Ok(())
}
#[tokio::test]
async fn plugin_list_json_prints_installed_plugins() -> Result<()> {
let (codex_home, source) = setup_local_marketplace()?;
let plugin_path = source.path().join("plugins").join("sample");
codex_command(codex_home.path())?
.args(["plugin", "add", "sample@debug"])
.assert()
.success();
let assert = codex_command(codex_home.path())?
.args(["plugin", "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!({
"installed": [
{
"pluginId": "sample@debug",
"name": "sample",
"marketplaceName": "debug",
"version": "1.2.3",
"installed": true,
"enabled": true,
"source": {
"source": "local",
"path": plugin_path.display().to_string(),
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
},
],
"available": [],
})
);
Ok(())
}
#[tokio::test]
async fn plugin_list_available_requires_json() -> Result<()> {
let (codex_home, _source) = setup_local_marketplace()?;
codex_command(codex_home.path())?
.args(["plugin", "list", "--available"])
.assert()
.failure()
.stderr(contains(
"the following required arguments were not provided",
))
.stderr(contains("--json"));
Ok(())
}
#[tokio::test]
async fn plugin_list_shows_installed_version_when_plugin_is_installed() -> Result<()> {
let (codex_home, _source) = setup_local_marketplace()?;