Skip to content

[codex] Add cross-repo plugin sources to marketplace manifests#18017

Merged
xli-oai merged 13 commits into
mainfrom
xli-codex/cross-repo-marketplace-support
Apr 17, 2026
Merged

[codex] Add cross-repo plugin sources to marketplace manifests#18017
xli-oai merged 13 commits into
mainfrom
xli-codex/cross-repo-marketplace-support

Conversation

@xli-oai
Copy link
Copy Markdown
Contributor

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

Summary

  • add first-class marketplace support for git-backed plugin sources
  • keep the newer marketplace parsing behavior from main, including alternate manifest locations and string local sources
  • materialize remote plugin sources during install, detail reads, and non-curated cache refresh
  • expose git plugin source metadata through the app-server protocol

Details

This teaches the marketplace parser to accept all of the following:

  • local string sources such as "source": "./plugins/foo"
  • local object sources such as {"source":"local","path":"./plugins/foo"}
  • remote repo-root sources such as {"source":"url","url":"https://github.com/org/repo.git"}
  • remote subdir sources such as {"source":"git-subdir","url":"owner/repo","path":"plugins/foo","ref":"main","sha":"..."}

It also preserves the newer tolerant behavior from main: invalid or unsupported plugin entries are skipped instead of breaking the whole marketplace.

Validation

  • cargo test -p codex-core plugins::marketplace::tests
  • just fix -p codex-core
  • just fmt

Notes

  • A full cargo test -p codex-core run still hit unrelated existing failures in agent and multi-agent tests during this session; the marketplace-focused suite passed after the rebase resolution.
@xli-oai xli-oai added the oai PRs contributed by OpenAI employees label Apr 15, 2026 — with ChatGPT Codex Connector
@xli-oai xli-oai marked this pull request as ready for review April 15, 2026 23:50
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: 03cb3e141f

ℹ️ 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 thread codex-rs/core/src/plugins/manager.rs Outdated
Comment on lines +2209 to +2212
run_git(
&["clone", url, destination.to_string_lossy().as_ref()],
None,
)?;
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 Resolve relative git URLs from marketplace root

normalize_git_plugin_source_url allows ./ and ../ git URLs, but clone_git_plugin_source executes git clone with cwd set to None. That makes relative URLs resolve from the process working directory instead of the marketplace file's root, so installs/detail reads can clone the wrong repo or fail whenever cwd differs.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed. Relative git source URLs are now normalized from the marketplace root during parsing, so later materialization no longer depends on the process cwd.

@xl-openai
Copy link
Copy Markdown
Collaborator

Could be a follow up PR but we need to deal with the actual loading of those plugins.

@xli-oai
Copy link
Copy Markdown
Contributor Author

xli-oai commented Apr 16, 2026

Could be a follow up PR but we need to deal with the actual loading of those plugins.

Yes! 👍

@xli-oai xli-oai force-pushed the xli-codex/cross-repo-marketplace-support branch from 21c13ec to 2cb73c8 Compare April 16, 2026 07:53
@viyatb-oai
Copy link
Copy Markdown
Collaborator

@xli-oai this seem to regress in marketplace plugin read behavior - with this change, it means that means an untrusted marketplace manifest can trigger git clone side effects during metadata reads, before the user explicitly approves installation. does that sound correct?

IMO plugin/read should stay read-only. We should only fetch or clone plugin sources during the actual install flow, where we can apply the right approval and source checks.

@xli-oai xli-oai force-pushed the xli-codex/cross-repo-marketplace-support branch from 47c4626 to cb711d8 Compare April 17, 2026 03:18
@xli-oai
Copy link
Copy Markdown
Contributor Author

xli-oai commented Apr 17, 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

description: outcome.plugin.description,
skills: plugin_skills_to_info(&visible_skills, &outcome.plugin.disabled_skill_paths),
apps: app_summaries,
mcp_servers: outcome.plugin.mcp_server_names,
};

P2 Badge Expose install-required reason in plugin/read responses

Core now sets details_unavailable_reason for uninstalled git sources, but plugin/read drops that signal and returns only empty description/skills/apps. Clients cannot distinguish “install required for remote source” from genuinely empty plugin metadata.

ℹ️ 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 thread codex-rs/core/src/plugins/manager.rs Outdated
Comment thread codex-rs/core-plugins/src/marketplace.rs Outdated
Comment thread codex-rs/core/src/plugins/manager.rs Outdated
Comment thread codex-rs/core-plugins/src/marketplace.rs Outdated
Comment thread codex-rs/core-plugins/src/loader.rs
Comment thread codex-rs/core-plugins/src/marketplace.rs Outdated
Comment thread codex-rs/core-plugins/src/loader.rs
Comment thread codex-rs/core/src/plugins/manager.rs
Comment thread codex-rs/core-plugins/src/loader.rs
Comment thread codex-rs/core-plugins/src/marketplace.rs Outdated
@xli-oai
Copy link
Copy Markdown
Contributor Author

xli-oai commented Apr 17, 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

pub struct PluginDetail {
pub marketplace_name: String,
pub marketplace_path: AbsolutePathBuf,
pub summary: PluginSummary,
pub description: Option<String>,
pub skills: Vec<SkillSummary>,
pub apps: Vec<AppSummary>,
pub mcp_servers: Vec<String>,
}

P2 Badge Expose remote-detail unavailability reason over app-server API

Core now sets details_unavailable_reason for uninstalled git-sourced plugins, but the v2 PluginDetail response type has no field for that signal. Clients receive empty details without a machine-readable reason, making the new core behavior effectively unusable from the API.

ℹ️ 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 +155 to +162
MarketplacePluginSource::Git { .. } => {
warn!(
plugin = plugin_name,
marketplace = OPENAI_CURATED_MARKETPLACE_NAME,
"skipping remote curated plugin source during cache refresh"
);
continue;
}
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 Refresh curated git plugin sources instead of skipping

refresh_curated_plugin_cache drops every curated plugin whose source is Git. A configured curated plugin backed by a git source will never be installed/updated during curated sync, so users can stay on missing/stale code even after marketplace updates.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

current limitation

Comment on lines +768 to +775
MarketplacePluginSource::Git { .. } => {
warn!(
plugin = plugin_name,
marketplace = %marketplace_name,
"skipping remote plugin source during remote sync"
);
continue;
}
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 Include git-sourced curated plugins in remote sync

sync_plugins_from_remote skips curated entries with MarketplacePluginSource::Git, so they never enter local_plugins. Remote-enabled plugins with those names are then not installed/uninstalled locally, causing persistent divergence between backend plugin state and local cache/config.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

current limitation

Comment on lines +284 to 293
let materialized =
materialize_marketplace_plugin_source(codex_home, &source).map_err(|err| {
format!("failed to materialize plugin source for {plugin_key}: {err}")
})?;
let source_path = materialized.path.clone();
let plugin_version = plugin_version_for_source(source_path.as_path())
.map_err(|err| format!("failed to read plugin version for {plugin_key}: {err}"))?;

if mode == NonCuratedCacheRefreshMode::IfVersionChanged
&& store.active_plugin_version(&plugin_id).as_deref() == Some(plugin_version.as_str())
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 Avoid cloning before version short-circuit in cache refresh

In IfVersionChanged mode, non-curated refresh materializes git sources and reads manifest versions before checking whether the installed version already matches. This forces a full clone per configured git plugin even when nothing changed, adding avoidable network/disk cost to periodic refresh.

Useful? React with 👍 / 👎.

Comment on lines 292 to 293
if mode == NonCuratedCacheRefreshMode::IfVersionChanged
&& store.active_plugin_version(&plugin_id).as_deref() == Some(plugin_version.as_str())
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 Reinstall git plugins when version is unchanged but content moved

Cache refresh gates reinstall on plugin version equality. For git plugins without an explicit manifest version, plugin_version_for_source resolves to "local", so upstream commits on the tracked ref are ignored and refresh wrongly skips reinstall.

Useful? React with 👍 / 👎.

Comment on lines +606 to +608
".." => {
normalized.pop();
}
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 Block parent traversal in relative git source URLs

normalize_relative_git_plugin_source_url accepts .. segments and repeatedly pop()s the marketplace root, allowing manifests to resolve relative git URLs outside the marketplace tree. Third-party marketplace files can therefore point installs at arbitrary local repositories/paths.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 48562472d. Relative git URLs now reject any .. path segment directly, including slash and backslash variants, so they cannot escape the marketplace root.

Copy link
Copy Markdown
Contributor Author

xli-oai commented Apr 17, 2026

@viyatb-oai Fixed. plugin/read now stays read-only for uninstalled git-sourced plugins: it returns marketplace metadata plus InstallRequiredForRemoteSource instead of materializing/cloning the remote source. Installed git plugins read details from the already-installed cache root.

@xli-oai
Copy link
Copy Markdown
Contributor Author

xli-oai commented Apr 17, 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: 20417c883c

ℹ️ 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 +768 to +775
MarketplacePluginSource::Git { .. } => {
warn!(
plugin = plugin_name,
marketplace = %marketplace_name,
"skipping remote plugin source during remote sync"
);
continue;
}
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 Sync git-sourced curated plugins during remote reconciliation

sync_plugins_from_remote records git-sourced plugins in local_plugin_names but then continues, so they never enter local_plugins. The later install/enable reconciliation iterates only local_plugins, which means remotely enabled curated git plugins are silently ignored and local state cannot converge to backend state.

Useful? React with 👍 / 👎.

Comment on lines 153 to +162
let source_path = match plugin.source {
MarketplacePluginSource::Local { path } => path,
MarketplacePluginSource::Git { .. } => {
warn!(
plugin = plugin_name,
marketplace = OPENAI_CURATED_MARKETPLACE_NAME,
"skipping remote curated plugin source during cache refresh"
);
continue;
}
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 Refresh curated git plugin cache instead of skipping entries

refresh_curated_plugin_cache drops every curated plugin whose source is Git. For configured curated plugins that moved to git-backed sources, startup cache refresh cannot reinstall/update them when curated version changes, leaving stale or missing local installs despite valid configuration.

Useful? React with 👍 / 👎.

@xli-oai xli-oai merged commit 0e111e0 into main Apr 17, 2026
60 of 66 checks passed
@xli-oai xli-oai deleted the xli-codex/cross-repo-marketplace-support branch April 17, 2026 22:11
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

oai PRs contributed by OpenAI employees

3 participants