use std::sync::Arc; use anyhow::Result; use serde_json::{Value, json}; use crate::plugin::PluginManager; use crate::tools::{Tool, ToolDescriptionLength}; pub struct ConfigurePlugin(pub Arc); impl Tool for ConfigurePlugin { fn name(&self) -> &str { "configure_plugin" } fn category(&self) -> crate::tools::ToolCategory { crate::tools::ToolCategory::Config } fn description(&self) -> &str { "Update the configuration of a plugin and restart it immediately. \ Use `list_items` (type=plugins) to see available plugin ids and their config schemas. \ The config object must match the plugin's schema — extra keys are ignored." } fn parameters_schema(&self) -> Value { json!({ "type": "object", "properties": { "id": { "type": "string", "description": "Plugin id (e.g. \"remote_connectivity\", \"telegram\")." }, "config": { "type": "object", "description": "Config object matching the plugin's schema. Existing keys not provided here are cleared.", "additionalProperties": true }, "enabled": { "type": "boolean", "description": "Whether to enable the plugin. Defaults to true.", "default": true } }, "required": ["id", "config"] }) } fn describe(&self, args: &Value, _length: ToolDescriptionLength) -> String { let id = args["id"].as_str().unwrap_or("?"); let enabled = args["enabled"].as_bool().unwrap_or(true); let action = if enabled { "configure" } else { "disable" }; format!("{action} plugin `{id}`") } fn execute(&self, args: Value) -> Result { let id = args["id"] .as_str() .ok_or_else(|| anyhow::anyhow!("configure_plugin: missing required argument `id`"))?; let config = args["config"].clone(); if !config.is_object() { anyhow::bail!("configure_plugin: `config` must be an object"); } let enabled = args["enabled"].as_bool().unwrap_or(true); tokio::task::block_in_place(|| { tokio::runtime::Handle::current() .block_on(self.0.update_config(id, enabled, config)) })?; Ok(format!( "Plugin '{}' configured and {}.", id, if enabled { "started" } else { "stopped" } )) } }