[codex] Support local marketplace sources (#17756)

## Summary

- Port marketplace source support into the shared core marketplace-add
flow
- Support local marketplace directory sources
- Support direct `marketplace.json` URL sources
- Persist the new source types in config/schema and cover them in CLI
and app-server tests

## Validation

- `cargo test -p codex-core marketplace_add`
- `cargo test -p codex-cli marketplace_add`
- `cargo test -p codex-app-server marketplace_add`
- `just write-config-schema`
- `just fmt`
- `just fix -p codex-core`
- `just fix -p codex-cli`

## Context

Current `main` moved marketplace-add behavior into shared core code and
still assumed only git-backed sources. This change keeps that structure
but restores support for local directories and direct manifest URLs in
the shared path.
This commit is contained in:
xli-oai
2026-04-14 15:58:14 -07:00
committed by GitHub
parent 96254a763a
commit 3cc689fb23
9 changed files with 408 additions and 33 deletions

View File

@@ -1,6 +1,8 @@
use anyhow::Result;
use codex_config::CONFIG_TOML_FILE;
use codex_core::plugins::marketplace_install_root;
use predicates::str::contains;
use pretty_assertions::assert_eq;
use std::path::Path;
use tempfile::TempDir;
@@ -37,7 +39,7 @@ fn write_marketplace_source(source: &Path, marker: &str) -> Result<()> {
}
#[tokio::test]
async fn marketplace_add_rejects_local_directory_source() -> Result<()> {
async fn marketplace_add_local_directory_source() -> Result<()> {
let codex_home = TempDir::new()?;
let source = TempDir::new()?;
write_marketplace_source(source.path(), "local ref")?;
@@ -48,16 +50,40 @@ async fn marketplace_add_rejects_local_directory_source() -> Result<()> {
.current_dir(source_parent)
.args(["marketplace", "add", source_arg.as_str()])
.assert()
.failure()
.stderr(contains(
"local marketplace sources are not supported yet; use an HTTP(S) Git URL, SSH Git URL, or GitHub owner/repo",
));
.success();
assert!(
!marketplace_install_root(codex_home.path())
.join("debug")
.exists()
let installed_root = marketplace_install_root(codex_home.path()).join("debug");
assert!(!installed_root.exists());
let config = std::fs::read_to_string(codex_home.path().join(CONFIG_TOML_FILE))?;
let config: toml::Value = toml::from_str(&config)?;
let expected_source = source.path().canonicalize()?.display().to_string();
assert_eq!(
config["marketplaces"]["debug"]["source_type"].as_str(),
Some("local")
);
assert_eq!(
config["marketplaces"]["debug"]["source"].as_str(),
Some(expected_source.as_str())
);
Ok(())
}
#[tokio::test]
async fn marketplace_add_rejects_local_manifest_file_source() -> Result<()> {
let codex_home = TempDir::new()?;
let source = TempDir::new()?;
write_marketplace_source(source.path(), "local ref")?;
let manifest_path = source.path().join(".agents/plugins/marketplace.json");
codex_command(codex_home.path())?
.args(["marketplace", "add", manifest_path.to_str().unwrap()])
.assert()
.failure()
.stderr(contains(
"local marketplace source must be a directory, not a file",
));
Ok(())
}