Expose configured marketplace source in plugin list JSON (#26417)

## Summary
- Follow-up to #25330
- Add `marketplaceSource` to `codex plugin list --json` entries for
configured marketplaces
- Keep the existing per-plugin `source` field unchanged; this still
reports the local plugin source path
- Include only the configured marketplace `sourceType` and `source` from
`config.toml`
- Keep human-readable output unchanged
- Add CLI coverage for configured local and git marketplace sources

Example:

```json
{
  "source": {
    "source": "local",
    "path": "/path/to/.codex/.tmp/marketplaces/debug/plugins/sample"
  },
  "marketplaceSource": {
    "sourceType": "git",
    "source": "https://example.com/acme/agent-skills.git"
  }
}
```

## Validation
- `just fmt`
- `just fix -p codex-cli`
- `just test -p codex-cli plugin_list`
This commit is contained in:
mpc-oai
2026-06-04 12:20:32 -05:00
committed by GitHub
Unverified
parent cbf62f64cb
commit cdc1d592df
2 changed files with 126 additions and 2 deletions
+70
View File
@@ -2,6 +2,7 @@ use anyhow::Result;
use codex_config::CONFIG_TOML_FILE;
use codex_config::MarketplaceConfigUpdate;
use codex_config::record_user_marketplace;
use codex_utils_absolute_path::canonicalize_existing_preserving_symlinks;
use predicates::prelude::PredicateBooleanExt;
use predicates::str::contains;
use pretty_assertions::assert_eq;
@@ -495,6 +496,7 @@ async fn plugin_list_prints_plugins_in_a_table() -> Result<()> {
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 source_path = source.path().to_string_lossy().into_owned();
let assert = codex_command(codex_home.path())?
.args(["plugin", "list", "--available", "--json"])
@@ -519,6 +521,69 @@ async fn plugin_list_json_prints_available_plugins_when_requested() -> Result<()
"source": "local",
"path": plugin_path.display().to_string(),
},
"marketplaceSource": {
"sourceType": "local",
"source": source_path,
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
},
],
})
);
Ok(())
}
#[tokio::test]
async fn plugin_list_json_includes_configured_git_marketplace_source() -> Result<()> {
let codex_home = TempDir::new()?;
let marketplace_root = codex_home
.path()
.join(".tmp")
.join("marketplaces")
.join("debug");
write_plugins_enabled_config(codex_home.path())?;
write_marketplace_source(&marketplace_root)?;
let update = MarketplaceConfigUpdate {
last_updated: "2026-06-04T08:39:49Z",
last_revision: Some("abc123"),
source_type: "git",
source: "https://example.com/acme/agent-skills.git",
ref_name: None,
sparse_paths: &[],
};
record_user_marketplace(codex_home.path(), "debug", &update)?;
let plugin_path = marketplace_root.join("plugins").join("sample");
let normalized_plugin_path = canonicalize_existing_preserving_symlinks(&plugin_path)?;
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": normalized_plugin_path.display().to_string(),
},
"marketplaceSource": {
"sourceType": "git",
"source": "https://example.com/acme/agent-skills.git",
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
},
@@ -533,6 +598,7 @@ async fn plugin_list_json_prints_available_plugins_when_requested() -> Result<()
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");
let source_path = source.path().to_string_lossy().into_owned();
codex_command(codex_home.path())?
.args(["plugin", "add", "sample@debug"])
@@ -561,6 +627,10 @@ async fn plugin_list_json_prints_installed_plugins() -> Result<()> {
"source": "local",
"path": plugin_path.display().to_string(),
},
"marketplaceSource": {
"sourceType": "local",
"source": source_path,
},
"installPolicy": "AVAILABLE",
"authPolicy": "ON_INSTALL",
},