/plugins: add marketplace install flow (#18704)

This PR adds a new feature to the `/plugins` menu that gives users the
ability to add new plugin marketplaces. It introduces an Add Marketplace
tab to the right of installed marketplaces, a source prompt, loading and
error states, and the app-server request flow needed to perform the
install. After a successful `marketplace/add`, the popup refreshes back
into the newly added marketplace tab so the new plugins are immediately
visible.

- Add an Add Marketplace tab to the `/plugins` menu
- Prompt for marketplace source input from git repo, URL, or local path
- Show loading and error states during `marketplace/add`
- Refresh plugin data after success and switch into the newly added
marketplace tab
- Add tests and snapshot updates
This commit is contained in:
canvrno-oai
2026-04-28 14:22:39 -07:00
committed by GitHub
parent c6e7d564c3
commit 66b0781502
13 changed files with 606 additions and 24 deletions
+4 -8
View File
@@ -123,14 +123,12 @@ where
let marketplace_name = validate_marketplace_source_root(path)?;
if marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME {
return Err(MarketplaceAddError::InvalidRequest(format!(
"marketplace '{OPENAI_CURATED_MARKETPLACE_NAME}' is reserved and cannot be added from {}",
source.display()
"marketplace '{OPENAI_CURATED_MARKETPLACE_NAME}' is reserved and cannot be added from this source"
)));
}
if find_marketplace_root_by_name(codex_home, &install_root, &marketplace_name)?.is_some() {
return Err(MarketplaceAddError::InvalidRequest(format!(
"marketplace '{marketplace_name}' is already added from a different source; remove it before adding {}",
source.display()
"marketplace '{marketplace_name}' is already added from a different source; remove it before adding this source"
)));
}
record_added_marketplace_entry(codex_home, &marketplace_name, &install_metadata)?;
@@ -169,8 +167,7 @@ where
let marketplace_name = validate_marketplace_source_root(&staged_root)?;
if marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME {
return Err(MarketplaceAddError::InvalidRequest(format!(
"marketplace '{OPENAI_CURATED_MARKETPLACE_NAME}' is reserved and cannot be added from {}",
source.display()
"marketplace '{OPENAI_CURATED_MARKETPLACE_NAME}' is reserved and cannot be added from this source"
)));
}
@@ -178,8 +175,7 @@ where
ensure_marketplace_destination_is_inside_install_root(&install_root, &destination)?;
if destination.exists() {
return Err(MarketplaceAddError::InvalidRequest(format!(
"marketplace '{marketplace_name}' is already added from a different source; remove it before adding {}",
source.display()
"marketplace '{marketplace_name}' is already added from a different source; remove it before adding this source"
)));
}
@@ -58,9 +58,10 @@ pub(crate) fn parse_marketplace_source(
});
}
Err(MarketplaceAddError::InvalidRequest(format!(
"invalid marketplace source format: {source}"
)))
Err(MarketplaceAddError::InvalidRequest(
"invalid marketplace source format; expected owner/repo, a git URL, or a local marketplace path"
.to_string(),
))
}
pub(super) fn stage_marketplace_source<F>(
@@ -160,8 +161,7 @@ fn resolve_local_source_path(source: &str) -> Result<PathBuf, MarketplaceAddErro
path.canonicalize().map_err(|err| {
MarketplaceAddError::InvalidRequest(format!(
"failed to resolve local marketplace source {}: {err}",
path.display()
"failed to resolve local marketplace source path: {err}"
))
})
}