use super::manifest::{PluginManifest, SlotDefinition};
use super::shell_bridge::ShellBridge;
use anyhow::Result;
use serde_json::Value;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
#[derive(Debug)]
pub struct Plugin {
pub name: Arc<str>,
pub path: PathBuf,
pub manifest: PluginManifest,
pub enabled: bool,
}
#[derive(Debug)]
pub struct PluginSlot {
pub name: Arc<str>,
pub slot_type: Arc<str>,
pub plugin_name: Arc<str>,
}
pub struct PluginManager {
plugins_dir: PathBuf,
plugins: HashMap<Arc<str>, Plugin>,
slots: Vec<PluginSlot>,
}
impl PluginManager {
pub fn new(plugins_dir: PathBuf) -> Self {
Self {
plugins_dir,
plugins: HashMap::new(),
slots: Vec::new(),
}
}
pub fn plugins_dir(&self) -> &Path {
&self.plugins_dir
}
pub fn list(&self) -> Vec<&Plugin> {
self.plugins.values().collect()
}
pub fn get(&self, name: &str) -> Option<&Plugin> {
self.plugins.get(name)
}
pub fn load(&mut self, plugin_name: &str) -> Result<()> {
let plugin_dir = self.plugins_dir.join(plugin_name);
if !plugin_dir.exists() {
anyhow::bail!("Plugin directory not found: {:?}", plugin_dir);
}
let manifest_path = plugin_dir.join("openclaw.plugin.json");
if !manifest_path.exists() {
anyhow::bail!("Plugin manifest not found: {:?}", manifest_path);
}
let manifest_content = std::fs::read_to_string(&manifest_path)?;
let manifest: PluginManifest = serde_json::from_str(&manifest_content)?;
for slot in &manifest.slots {
self.slots.push(PluginSlot {
name: slot.name.clone(),
slot_type: slot.slot_type.clone(),
plugin_name: manifest.name.clone(),
});
}
let plugin = Plugin {
name: manifest.name.clone(),
path: plugin_dir,
manifest,
enabled: true,
};
self.plugins.insert(plugin.name.clone(), plugin);
Ok(())
}
pub async fn execute(&self, plugin_name: &str, method: &str, params: Option<Value>) -> Result<Value> {
let plugin = self.plugins.get(plugin_name)
.ok_or_else(|| anyhow::anyhow!("Plugin '{}' not found", plugin_name))?;
if !plugin.enabled {
anyhow::bail!("Plugin '{}' is disabled", plugin_name);
}
let mut bridge = ShellBridge::new(Some(plugin.manifest.runtime.clone()));
bridge.start(&plugin.path, &plugin.manifest.main).await?;
let result = bridge.call(method, params).await;
bridge.stop().await?;
result
}
pub fn list_slots(&self) -> &[PluginSlot] {
&self.slots
}
}