"""插件管理器 - Phase 8.2""" import importlib.util import os import sys from typing import Any from app.agents.plugins.manifest import PluginManifest from app.agents.plugins.sandbox import PluginSandbox class PluginManager: """插件管理器 负责插件的安装、卸载、启用、禁用和生命周期管理。 """ def __init__(self, plugins_dir: str | None = None): """ Args: plugins_dir: 插件目录,None 则使用默认目录 """ if plugins_dir is None: plugins_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "plugins") self.plugins_dir = plugins_dir self._plugins: dict[str, PluginManifest] = {} self._enabled: dict[str, bool] = {} self._modules: dict[str, Any] = {} self._sandbox = PluginSandbox() def install(self, plugin_path: str) -> bool: """安装插件 Args: plugin_path: 插件目录路径或 manifest.json 所在目录 Returns: 是否安装成功 """ try: manifest_path = os.path.join(plugin_path, "manifest.json") if not os.path.exists(manifest_path): return False with open(manifest_path, "r", encoding="utf-8") as f: import json data = json.load(f) manifest = PluginManifest.from_dict(data) # 验证 manifest if not self._validate_manifest(manifest, plugin_path): return False # 复制插件到 plugins_dir target_dir = os.path.join(self.plugins_dir, manifest.id) os.makedirs(os.path.dirname(target_dir), exist_ok=True) # 保存 manifest with open(os.path.join(target_dir, "manifest.json"), "w", encoding="utf-8") as f: json.dump(manifest.to_dict(), f, indent=2, ensure_ascii=False) # 注册插件 self._plugins[manifest.id] = manifest self._enabled[manifest.id] = True return True except Exception: return False def uninstall(self, plugin_id: str) -> bool: """卸载插件 Args: plugin_id: 插件 ID Returns: 是否卸载成功 """ if plugin_id not in self._plugins: return False # 禁用插件 self.disable(plugin_id) # 移除模块 if plugin_id in self._modules: del self._modules[plugin_id] # 移除插件 del self._plugins[plugin_id] del self._enabled[plugin_id] # 删除目录 plugin_dir = os.path.join(self.plugins_dir, plugin_id) if os.path.exists(plugin_dir): import shutil shutil.rmtree(plugin_dir) return True def enable(self, plugin_id: str) -> bool: """启用插件 Args: plugin_id: 插件 ID Returns: 是否启用成功 """ if plugin_id not in self._plugins: return False self._enabled[plugin_id] = True return True def disable(self, plugin_id: str) -> bool: """禁用插件 Args: plugin_id: 插件 ID Returns: 是否禁用成功 """ if plugin_id not in self._plugins: return False self._enabled[plugin_id] = False return True def reload(self, plugin_id: str) -> bool: """重新加载插件 Args: plugin_id: 插件 ID Returns: 是否重新加载成功 """ if plugin_id not in self._plugins: return False # 卸载模块 if plugin_id in self._modules: del self._modules[plugin_id] # 重新加载 return self._load_plugin_module(plugin_id) def list_plugins(self) -> list[PluginManifest]: """列出所有插件""" return list(self._plugins.values()) def get_plugin(self, plugin_id: str) -> PluginManifest | None: """获取插件清单""" return self._plugins.get(plugin_id) def is_enabled(self, plugin_id: str) -> bool: """检查插件是否启用""" return self._enabled.get(plugin_id, False) def _validate_manifest(self, manifest: PluginManifest, plugin_path: str) -> bool: """验证 manifest""" # 检查主入口文件是否存在 main_path = os.path.join(plugin_path, manifest.main) if not os.path.exists(main_path): return False return True def _load_plugin_module(self, plugin_id: str) -> bool: """加载插件模块""" plugin_dir = os.path.join(self.plugins_dir, plugin_id) manifest = self._plugins.get(plugin_id) if not manifest: return False try: main_path = os.path.join(plugin_dir, manifest.main) spec = importlib.util.spec_from_file_location(plugin_id, main_path) if spec and spec.loader: module = importlib.util.module_from_spec(spec) sys.modules[plugin_id] = module spec.loader.exec_module(module) self._modules[plugin_id] = module return True except Exception: pass return False # 全局单例 _manager: PluginManager | None = None def get_plugin_manager() -> PluginManager: """获取全局插件管理器""" global _manager if _manager is None: _manager = PluginManager() return _manager