Skip to content

Add marketplace command#17087

Open
xli-oai wants to merge 5 commits intomainfrom
xli-codex/marketplace-add-command
Open

Add marketplace command#17087
xli-oai wants to merge 5 commits intomainfrom
xli-codex/marketplace-add-command

Conversation

@xli-oai
Copy link
Copy Markdown

@xli-oai xli-oai commented Apr 8, 2026

Added a new top-level codex marketplace add command for installing plugin marketplaces into Codex’s local marketplace cache.

This change adds source parsing for local directories, GitHub shorthand, and git URLs, supports optional --ref and git-only --sparse checkout paths, stages the source in a temp directory, validates the marketplace manifest, and installs it under $CODEX_HOME/marketplaces/<marketplace-name>

Included tests cover local install behavior in the CLI and marketplace discovery from installed roots in core. Scoped formatting and fix passes were run, and targeted CLI/core tests passed.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@xli-oai
Copy link
Copy Markdown
Author

xli-oai commented Apr 8, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Apr 8, 2026
Copy link
Copy Markdown
Author

xli-oai commented Apr 8, 2026

Design / storage summary for this PR:

Filesystem layout

  • $CODEX_HOME/.tmp/plugins/ remains the existing OpenAI-curated marketplace checkout. This PR does not move or rename it.
  • $CODEX_HOME/.tmp/marketplaces/<marketplace-name>/ is where marketplaces added by codex marketplace add are materialized.
  • $CODEX_HOME/.tmp/marketplaces/.staging/ is temporary staging space used while cloning/copying before install.
  • $CODEX_HOME/.tmp/known_marketplaces.json records the user-added marketplace roots that discovery should include.
  • $CODEX_HOME/plugins/cache/<marketplace>/<plugin>/<version>/ remains the existing plugin artifact/install cache. Marketplace add does not install plugin artifacts into this cache.

Flow

codex marketplace add <source> accepts a local marketplace directory or a git-like source. It stages the source first, by copying a local directory or cloning a git repo, validates the staged root as a Codex marketplace, rejects the reserved openai-curated marketplace name, then atomically replaces the add-managed destination for that marketplace name and records it in the local known-marketplaces registry.

After that, normal marketplace discovery combines the registered user-added marketplace roots with the existing curated checkout. Plugin listing can see plugins from the newly added marketplace, but already-installed plugin artifacts are left untouched.

@xli-oai xli-oai marked this pull request as ready for review April 8, 2026 21:32
@xli-oai xli-oai force-pushed the xli-codex/marketplace-add-command branch 6 times, most recently from faaff79 to f4909a7 Compare April 8, 2026 23:24
}
}

pub fn marketplace_install_root(codex_home: &Path) -> PathBuf {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe have a marketplace.rs or something as this file becomes too big.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

@xl-openai
Copy link
Copy Markdown
Collaborator

@codex review

@xli-oai xli-oai force-pushed the xli-codex/marketplace-add-command branch from f4909a7 to f9b23d5 Compare April 8, 2026 23:32
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9b23d58b9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1272 to +1273
if registry_path.is_file() {
return installed_marketplace_roots_from_registry(&registry_path);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fall back to disk scan when registry parsing fails

installed_marketplace_roots immediately trusts known_marketplaces.json when it exists. If that file is malformed/truncated, installed_marketplace_roots_from_registry returns an empty list and no filesystem fallback runs, so valid marketplaces under .tmp/marketplaces vanish from discovery. A single bad registry file effectively disables all installed marketplaces.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, fixed

Comment on lines +452 to +463
let backup = if destination.exists() {
let parent = destination
.parent()
.context("marketplace destination has no parent")?;
let staging_root = marketplace_staging_root(parent);
fs::create_dir_all(&staging_root)?;
let backup = tempfile::Builder::new()
.prefix("marketplace-backup-")
.tempdir_in(&staging_root)?;
let backup_root = backup.path().join("previous");
fs::rename(destination, &backup_root)?;
Some((backup, backup_root))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent concurrent add from overwriting existing marketplace

run_add rejects existing destinations, but replace_marketplace_root still replaces any destination that appears later. If another marketplace add creates the same directory between the check and this rename, this process moves the existing install aside and overwrites it. That TOCTOU race causes lost updates instead of the expected conflict error.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +1295 to +1297
fn marketplace_registry_path(codex_home: &Path) -> PathBuf {
codex_home.join(".tmp").join(KNOWN_MARKETPLACES_FILE)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need known_marketplaces.json? if marketplaces added via this CLI command are installed to a static location, can't we just walk that dir to find installed marketplaces?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chatted offline, its to track metadata about plugin source.

Comment on lines +42 to +45
/// Sparse-checkout paths to use while cloning git sources.
#[arg(long = "sparse", value_name = "PATH", num_args = 1..)]
sparse_paths: Vec<String>,
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont think this works (--sparse swallows source):
codex marketplace add --sparse plugins/foo owner/repo

what about something like:
codex marketplace add owner/repo --sparse plugins/foo?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, nice catch!

Comment on lines +153 to +154
let marketplace_name = validate_marketplace_root(&staged_root)
.with_context(|| format!("failed to validate marketplace from {}", source.display()))?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think plugins require marketplace names to be only ASCII alnum / _ / -, should we enforce that here as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes 👍

@xli-oai
Copy link
Copy Markdown
Author

xli-oai commented Apr 9, 2026

@codex review

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 972d3a5495

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

source.display()
);
println!("Installed marketplace root: {}", existing_root.display());
return Ok(());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-record registry on idempotent source adds

When marketplace add finds an already-installed source, it returns immediately without calling record_installed_marketplace_root. If known_marketplaces.json exists but is missing that entry, discovery trusts the registry and skips disk scan, so the marketplace stays invisible even though the CLI reports it is already added.

Useful? React with 👍 / 👎.

if !metadata_path.is_file() {
continue;
}
let metadata = read_marketplace_source_metadata(&root)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip bad metadata entries while scanning installs

A single malformed .codex-marketplace-source file aborts installed_marketplace_root_for_source because parse errors are propagated. That prevents adding unrelated marketplaces whenever one existing install has corrupted metadata, turning one bad entry into a global failure.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

@sayan-oai sayan-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for cleaning up! one nit

source.starts_with("git@") && source.contains(':')
}

fn is_http_git_url(source: &str) -> bool {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit-ish: feels like theres a lot of local FS operations/git operations alongside the main CLI command parsing.

maybe worth breaking up those ops into another file, or moving stuff to plugins module?

will leave it up to you

@xli-oai xli-oai force-pushed the xli-codex/marketplace-add-command branch from e8fa02e to 972d3a5 Compare April 9, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants