From 996945223cbf26d02b95094169c6fedbdbadc359 Mon Sep 17 00:00:00 2001 From: xintaofei Date: Sun, 12 Apr 2026 10:21:04 +0800 Subject: [PATCH] feat(acp): add opencode plugin list/install/uninstall commands and routes Co-Authored-By: Claude Sonnet 4.6 --- src-tauri/src/commands/acp.rs | 47 +++++++++++++++++++++++++++++++ src-tauri/src/lib.rs | 3 ++ src-tauri/src/web/handlers/acp.rs | 41 +++++++++++++++++++++++++++ src-tauri/src/web/router.rs | 3 ++ 4 files changed, 94 insertions(+) diff --git a/src-tauri/src/commands/acp.rs b/src-tauri/src/commands/acp.rs index a6bf4f3..96503a4 100644 --- a/src-tauri/src/commands/acp.rs +++ b/src-tauri/src/commands/acp.rs @@ -8,6 +8,7 @@ use tauri::State; use crate::acp::binary_cache; use crate::acp::error::AcpError; +use crate::acp::opencode_plugins::{self, PluginCheckSummary}; #[cfg(feature = "tauri-runtime")] use crate::acp::manager::ConnectionManager; use crate::acp::preflight::{self, PreflightResult}; @@ -2826,3 +2827,49 @@ pub async fn acp_delete_agent_skill( .map_err(|e| AcpError::protocol(format!("failed to delete skill entry: {e}")))?; Ok(()) } + +pub(crate) async fn opencode_list_plugins_core() -> Result { + opencode_plugins::check_opencode_plugins(None) + .map_err(|e| AcpError::Protocol(e)) +} + +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn opencode_list_plugins() -> Result { + opencode_list_plugins_core().await +} + +pub(crate) async fn opencode_install_plugins_core( + names: Option>, + task_id: String, + emitter: &EventEmitter, +) -> Result<(), AcpError> { + opencode_plugins::install_missing_plugins(names, task_id, emitter) + .await + .map_err(|e| AcpError::Protocol(e)) +} + +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn opencode_install_plugins( + names: Option>, + task_id: String, + app: tauri::AppHandle, +) -> Result<(), AcpError> { + let emitter = EventEmitter::Tauri(app); + opencode_install_plugins_core(names, task_id, &emitter).await +} + +pub(crate) async fn opencode_uninstall_plugin_core( + name: String, +) -> Result { + opencode_plugins::uninstall_plugin(name) + .await + .map_err(|e| AcpError::Protocol(e)) +} + +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn opencode_uninstall_plugin( + name: String, +) -> Result { + opencode_uninstall_plugin_core(name).await +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0f3c2da..e580e0a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -375,6 +375,9 @@ mod tauri_app { acp_commands::acp_read_agent_skill, acp_commands::acp_save_agent_skill, acp_commands::acp_delete_agent_skill, + acp_commands::opencode_list_plugins, + acp_commands::opencode_install_plugins, + acp_commands::opencode_uninstall_plugin, experts_commands::experts_list, experts_commands::experts_list_for_agent, experts_commands::experts_get_install_status, diff --git a/src-tauri/src/web/handlers/acp.rs b/src-tauri/src/web/handlers/acp.rs index a85f597..05999ae 100644 --- a/src-tauri/src/web/handlers/acp.rs +++ b/src-tauri/src/web/handlers/acp.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use axum::{extract::Extension, Json}; use serde::Deserialize; +use crate::acp::opencode_plugins::PluginCheckSummary; use crate::acp::preflight::PreflightResult; use crate::acp::types::{ AcpAgentInfo, AcpAgentStatus, AgentSkillContent, AgentSkillLayout, AgentSkillScope, @@ -534,3 +535,43 @@ pub async fn acp_reorder_agents( .map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?; Ok(Json(())) } + +pub async fn opencode_list_plugins() -> Result, AppCommandError> { + let result = acp_commands::opencode_list_plugins_core() + .await + .map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?; + Ok(Json(result)) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OpencodeInstallPluginsParams { + pub names: Option>, + pub task_id: String, +} + +pub async fn opencode_install_plugins( + Extension(state): Extension>, + Json(params): Json, +) -> Result, AppCommandError> { + let emitter = crate::web::event_bridge::EventEmitter::WebOnly(state.event_broadcaster.clone()); + acp_commands::opencode_install_plugins_core(params.names, params.task_id, &emitter) + .await + .map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?; + Ok(Json(())) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct OpencodeUninstallPluginParams { + pub name: String, +} + +pub async fn opencode_uninstall_plugin( + Json(params): Json, +) -> Result, AppCommandError> { + let result = acp_commands::opencode_uninstall_plugin_core(params.name) + .await + .map_err(|e| AppCommandError::task_execution_failed(e.to_string()))?; + Ok(Json(result)) +} diff --git a/src-tauri/src/web/router.rs b/src-tauri/src/web/router.rs index 0e5bac9..a07552f 100644 --- a/src-tauri/src/web/router.rs +++ b/src-tauri/src/web/router.rs @@ -175,6 +175,9 @@ pub fn build_router(state: Arc, token: String, static_dir: std::path:: .route("/acp_read_agent_skill", post(handlers::acp::acp_read_agent_skill)) .route("/acp_save_agent_skill", post(handlers::acp::acp_save_agent_skill)) .route("/acp_delete_agent_skill", post(handlers::acp::acp_delete_agent_skill)) + .route("/opencode_list_plugins", post(handlers::acp::opencode_list_plugins)) + .route("/opencode_install_plugins", post(handlers::acp::opencode_install_plugins)) + .route("/opencode_uninstall_plugin", post(handlers::acp::opencode_uninstall_plugin)) // ─── Experts ─── .route("/experts_list", post(handlers::experts::experts_list)) .route("/experts_list_for_agent", post(handlers::experts::experts_list_for_agent))