mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat: Add remote plugin fields to plugin API (#17277)
## Summary
Update the plugin API for the new remote plugin model.
The mental model is no longer “keep local plugin state in sync with
remote.” Instead, local and remote plugins are becoming separate
sources. Remote catalog entries can be shown directly from the remote
API before installation; after installation they are still downloaded
into the local cache for execution, but remote installed state will come
from the API and be held in memory rather than being read from config.
• ## API changes
- Remove `forceRemoteSync` from `plugin/list`, `plugin/install`, and
`plugin/uninstall`.
- Remove `remoteSyncError` from `plugin/list`.
- Add remote-capable metadata to `plugin/list` / `plugin/read`:
- nullable `marketplaces[].path`
- `source: { type: "remote", downloadUrl }`
- URL asset fields alongside local path fields:
`composerIconUrl`, `logoUrl`, `screenshotUrls`
- Make `plugin/read` and `plugin/install` source-compatible:
- `marketplacePath?: AbsolutePathBuf | null`
- `remoteMarketplaceName?: string | null`
- exactly one source is required at runtime
This commit is contained in:
committed by
GitHub
Unverified
parent
120bbf46c1
commit
26d9894a27
@@ -6428,55 +6428,19 @@ impl CodexMessageProcessor {
|
||||
|
||||
async fn plugin_list(&self, request_id: ConnectionRequestId, params: PluginListParams) {
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
let PluginListParams {
|
||||
cwds,
|
||||
force_remote_sync,
|
||||
} = params;
|
||||
let PluginListParams { cwds } = params;
|
||||
let roots = cwds.unwrap_or_default();
|
||||
plugins_manager.maybe_start_non_curated_plugin_cache_refresh(&roots);
|
||||
|
||||
let mut config = match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
self.outgoing.send_error(request_id, err).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut remote_sync_error = None;
|
||||
let auth = self.auth_manager.auth().await;
|
||||
|
||||
if force_remote_sync {
|
||||
match plugins_manager
|
||||
.sync_plugins_from_remote(&config, auth.as_ref(), /*additive_only*/ false)
|
||||
.await
|
||||
{
|
||||
Ok(sync_result) => {
|
||||
info!(
|
||||
installed_plugin_ids = ?sync_result.installed_plugin_ids,
|
||||
enabled_plugin_ids = ?sync_result.enabled_plugin_ids,
|
||||
disabled_plugin_ids = ?sync_result.disabled_plugin_ids,
|
||||
uninstalled_plugin_ids = ?sync_result.uninstalled_plugin_ids,
|
||||
"completed plugin/list remote sync"
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
error = %err,
|
||||
"plugin/list remote sync failed; returning local marketplace state"
|
||||
);
|
||||
remote_sync_error = Some(err.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
config = match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
self.outgoing.send_error(request_id, err).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let config_for_marketplace_listing = config.clone();
|
||||
let plugins_manager_for_marketplace_listing = plugins_manager.clone();
|
||||
let (data, marketplace_load_errors) = match tokio::task::spawn_blocking(move || {
|
||||
@@ -6494,7 +6458,7 @@ impl CodexMessageProcessor {
|
||||
.into_iter()
|
||||
.map(|marketplace| PluginMarketplaceEntry {
|
||||
name: marketplace.name,
|
||||
path: marketplace.path,
|
||||
path: Some(marketplace.path),
|
||||
interface: marketplace.interface.map(|interface| MarketplaceInterface {
|
||||
display_name: interface.display_name,
|
||||
}),
|
||||
@@ -6509,7 +6473,7 @@ impl CodexMessageProcessor {
|
||||
source: marketplace_plugin_source_to_info(plugin.source),
|
||||
install_policy: plugin.policy.installation.into(),
|
||||
auth_policy: plugin.policy.authentication.into(),
|
||||
interface: plugin.interface.map(plugin_interface_to_info),
|
||||
interface: plugin.interface.map(local_plugin_interface_to_info),
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
@@ -6569,7 +6533,6 @@ impl CodexMessageProcessor {
|
||||
PluginListResponse {
|
||||
marketplaces: data,
|
||||
marketplace_load_errors,
|
||||
remote_sync_error,
|
||||
featured_plugin_ids,
|
||||
},
|
||||
)
|
||||
@@ -6613,8 +6576,40 @@ impl CodexMessageProcessor {
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
let PluginReadParams {
|
||||
marketplace_path,
|
||||
remote_marketplace_name,
|
||||
plugin_name,
|
||||
} = params;
|
||||
let marketplace_path = match (marketplace_path, remote_marketplace_name) {
|
||||
(Some(marketplace_path), None) => marketplace_path,
|
||||
(None, Some(remote_marketplace_name)) => {
|
||||
self.outgoing
|
||||
.send_error(
|
||||
request_id,
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!(
|
||||
"remote plugin read is not supported yet for marketplace {remote_marketplace_name}"
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
self.outgoing
|
||||
.send_error(
|
||||
request_id,
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "plugin/read requires exactly one of marketplacePath or remoteMarketplaceName".to_string(),
|
||||
data: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let config_cwd = marketplace_path.as_path().parent().map(Path::to_path_buf);
|
||||
|
||||
let config = match self.load_latest_config(config_cwd).await {
|
||||
@@ -6664,7 +6659,7 @@ impl CodexMessageProcessor {
|
||||
enabled: outcome.plugin.enabled,
|
||||
install_policy: outcome.plugin.policy.installation.into(),
|
||||
auth_policy: outcome.plugin.policy.authentication.into(),
|
||||
interface: outcome.plugin.interface.map(plugin_interface_to_info),
|
||||
interface: outcome.plugin.interface.map(local_plugin_interface_to_info),
|
||||
},
|
||||
description: outcome.plugin.description,
|
||||
skills: plugin_skills_to_info(&visible_skills, &outcome.plugin.disabled_skill_paths),
|
||||
@@ -6738,9 +6733,40 @@ impl CodexMessageProcessor {
|
||||
async fn plugin_install(&self, request_id: ConnectionRequestId, params: PluginInstallParams) {
|
||||
let PluginInstallParams {
|
||||
marketplace_path,
|
||||
remote_marketplace_name,
|
||||
plugin_name,
|
||||
force_remote_sync,
|
||||
} = params;
|
||||
let marketplace_path = match (marketplace_path, remote_marketplace_name) {
|
||||
(Some(marketplace_path), None) => marketplace_path,
|
||||
(None, Some(remote_marketplace_name)) => {
|
||||
self.outgoing
|
||||
.send_error(
|
||||
request_id,
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: format!(
|
||||
"remote plugin install is not supported yet for marketplace {remote_marketplace_name}"
|
||||
),
|
||||
data: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
(Some(_), Some(_)) | (None, None) => {
|
||||
self.outgoing
|
||||
.send_error(
|
||||
request_id,
|
||||
JSONRPCErrorError {
|
||||
code: INVALID_REQUEST_ERROR_CODE,
|
||||
message: "plugin/install requires exactly one of marketplacePath or remoteMarketplaceName".to_string(),
|
||||
data: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let config_cwd = marketplace_path.as_path().parent().map(Path::to_path_buf);
|
||||
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
@@ -6749,21 +6775,7 @@ impl CodexMessageProcessor {
|
||||
marketplace_path,
|
||||
};
|
||||
|
||||
let install_result = if force_remote_sync {
|
||||
let config = match self.load_latest_config(config_cwd.clone()).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
self.outgoing.send_error(request_id, err).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let auth = self.auth_manager.auth().await;
|
||||
plugins_manager
|
||||
.install_plugin_with_remote_sync(&config, auth.as_ref(), request)
|
||||
.await
|
||||
} else {
|
||||
plugins_manager.install_plugin(request).await
|
||||
};
|
||||
let install_result = plugins_manager.install_plugin(request).await;
|
||||
|
||||
match install_result {
|
||||
Ok(result) => {
|
||||
@@ -6915,27 +6927,10 @@ impl CodexMessageProcessor {
|
||||
request_id: ConnectionRequestId,
|
||||
params: PluginUninstallParams,
|
||||
) {
|
||||
let PluginUninstallParams {
|
||||
plugin_id,
|
||||
force_remote_sync,
|
||||
} = params;
|
||||
let PluginUninstallParams { plugin_id } = params;
|
||||
let plugins_manager = self.thread_manager.plugins_manager();
|
||||
|
||||
let uninstall_result = if force_remote_sync {
|
||||
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
self.outgoing.send_error(request_id, err).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let auth = self.auth_manager.auth().await;
|
||||
plugins_manager
|
||||
.uninstall_plugin_with_remote_sync(&config, auth.as_ref(), plugin_id)
|
||||
.await
|
||||
} else {
|
||||
plugins_manager.uninstall_plugin(plugin_id).await
|
||||
};
|
||||
let uninstall_result = plugins_manager.uninstall_plugin(plugin_id).await;
|
||||
|
||||
match uninstall_result {
|
||||
Ok(()) => {
|
||||
@@ -9105,7 +9100,7 @@ fn plugin_skills_to_info(
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn plugin_interface_to_info(interface: PluginManifestInterface) -> PluginInterface {
|
||||
fn local_plugin_interface_to_info(interface: PluginManifestInterface) -> PluginInterface {
|
||||
PluginInterface {
|
||||
display_name: interface.display_name,
|
||||
short_description: interface.short_description,
|
||||
@@ -9119,8 +9114,11 @@ fn plugin_interface_to_info(interface: PluginManifestInterface) -> PluginInterfa
|
||||
default_prompt: interface.default_prompt,
|
||||
brand_color: interface.brand_color,
|
||||
composer_icon: interface.composer_icon,
|
||||
composer_icon_url: None,
|
||||
logo: interface.logo,
|
||||
logo_url: None,
|
||||
screenshots: interface.screenshots,
|
||||
screenshot_urls: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user