新增依赖自动安装插件并修复核心模块缺失问题

主要变更:
1. 新增 auto_dependency 插件
   - 实现系统依赖的扫描、检查、安装和信息查询功能
   - 对接插件加载器的 /PL 注入能力接口 (execute 方法)
   - 支持多种包管理器 (apt-get, yum, dnf, pacman, brew, apk)
   - 提供 scan(), check(), install(), info() 四个核心 API

2. 修复模块缺失错误
   - 创建 oss/plugin/base.py (BasePlugin 类)
   - 创建 oss/core/context.py (Context 类)
   - 解决 6 个现有插件无法导入的问题

3. 添加示例配置
   - 为 firewall 和 ftp_server 插件添加 system_dependencies 声明示例

功能说明:
其他插件只需在 manifest.json 中声明 "system_dependencies" 字段,
该插件即可通过插件加载器自动检测并安装缺失的系统级依赖包。
This commit is contained in:
Falck
2026-04-25 14:20:16 +08:00
committed by GitHub
11 changed files with 1115 additions and 57 deletions

85
.gitignore vendored
View File

@@ -1,71 +1,42 @@
```gitignore
# Logs and temp files
*.log
*.tmp
*.swp
# Environment
.env
.env.local
*.env.*
# Editors
.vscode/
.idea/
```
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
*.py~
# Dependencies
node_modules/
.venv/
venv/
__pycache__/
.mypy_cache/
.pytest_cache/
dist/
build/
target/
.gradle/
.env
.env.local
.env.*
# Compiled files
*.pyc
*.class
*.o
*.exe
*.dll
*.so
*.a
*.obj
*.out
# Logs
*.log
# System files
# OS
.DS_Store
Thumbs.db
# Coverage
# Backup files
*~
*.bak
*.swp
*.swo
# Coverage reports
coverage/
htmlcov/
.coverage
# Compressed files
*.zip
*.gz
*.tar
*.tgz
*.bz2
*.xz
*.7z
*.rar
*.zst
*.lz4
*.lzh
*.cab
*.arj
*.rpm
*.deb
*.Z
*.lz
*.lzo
*.tar.gz
*.tar.bz2
*.tar.xz
*.tar.zst
# Testing
.pytest_cache/
.mypy_cache/
# Distribution / packaging
dist/
build/
*.egg-info/
```

58
oss/core/context.py Normal file
View File

@@ -0,0 +1,58 @@
"""Context class for plugin execution environment."""
from typing import Any, Dict, Optional
class Context:
"""Execution context for plugins.
Provides access to configuration, state, and utilities during plugin execution.
"""
def __init__(self, config: Optional[Dict[str, Any]] = None):
"""Initialize the context.
Args:
config: Optional configuration dictionary.
"""
self.config = config or {}
self._state: Dict[str, Any] = {}
def get(self, key: str, default: Any = None) -> Any:
"""Get a configuration value.
Args:
key: Configuration key.
default: Default value if key not found.
Returns:
The configuration value or default.
"""
return self.config.get(key, default)
def set_state(self, key: str, value: Any) -> None:
"""Set a state value.
Args:
key: State key.
value: State value.
"""
self._state[key] = value
def get_state(self, key: str, default: Any = None) -> Any:
"""Get a state value.
Args:
key: State key.
default: Default value if key not found.
Returns:
The state value or default.
"""
return self._state.get(key, default)
def __repr__(self) -> str:
return f"Context(config={self.config})"
__all__ = ['Context']

8
oss/plugin/base.py Normal file
View File

@@ -0,0 +1,8 @@
"""Base plugin module for backward compatibility."""
from oss.plugin.types import Plugin
# Alias for backward compatibility
BasePlugin = Plugin
__all__ = ['BasePlugin']

View File

@@ -0,0 +1,370 @@
"""
Auto Dependency Plugin - 依赖自动安装插件
该插件允许其他插件在声明文件 (manifest.json) 中声明所需的系统依赖,
然后扫描所有插件的声明文件,检查并安装缺失的系统依赖。
通过插件加载器的 /PL 注入能力接口进行对接。
"""
import json
import os
import subprocess
import sys
from typing import List, Dict, Any, Optional
from pathlib import Path
from oss.plugin.base import BasePlugin
from oss.core.context import Context
class AutoDependencyPlugin(BasePlugin):
"""依赖自动安装插件"""
def __init__(self):
super().__init__()
self.name = "auto_dependency"
self.version = "1.0.0"
self.description = "自动扫描并安装插件声明的系统依赖"
self.plugins_dir: Optional[Path] = None
self.manifest_filename = "manifest.json"
self.logger = None
def init(self, deps: Optional[Dict[str, Any]] = None):
"""初始化插件"""
# 获取插件目录路径
self.plugins_dir = Path(__file__).parent
if deps and 'logger' in deps:
self.logger = deps['logger']
else:
import logging
self.logger = logging.getLogger(self.name)
self.logger.info(f"AutoDependencyPlugin 初始化完成,插件目录:{self.plugins_dir}")
def start(self):
"""启动插件"""
self.logger.info("AutoDependencyPlugin 启动")
def stop(self):
"""停止插件"""
self.logger.info("AutoDependencyPlugin 停止")
def scan(self) -> List[Dict[str, Any]]:
"""
扫描所有插件的声明文件,收集系统依赖信息
Returns:
List[Dict]: 包含所有插件依赖信息的列表
每个元素格式: {
"plugin": str, # 插件名称
"dependencies": List, # 依赖列表
"package_manager": str # 包管理器类型
}
"""
all_dependencies = []
if not self.plugins_dir.exists():
self.logger.warning(f"插件目录不存在: {self.plugins_dir}")
return all_dependencies
# 遍历所有插件文件
for plugin_file in self.plugins_dir.glob("*.py"):
plugin_name = plugin_file.stem
# 跳过自身和__init__等文件
if plugin_name.startswith("_") or plugin_name == self.name:
continue
# 查找对应的 manifest 文件
manifest_path = self._find_manifest_for_plugin(plugin_name)
if manifest_path and manifest_path.exists():
try:
with open(manifest_path, 'r', encoding='utf-8') as f:
manifest = json.load(f)
# 提取系统依赖信息
system_deps = manifest.get("system_dependencies", [])
package_manager = manifest.get("package_manager", "apt-get")
if system_deps:
all_dependencies.append({
"plugin": plugin_name,
"dependencies": system_deps,
"package_manager": package_manager,
"manifest_path": str(manifest_path)
})
self.logger.info(
f"插件 {plugin_name} 声明了 {len(system_deps)} 个系统依赖"
)
except json.JSONDecodeError as e:
self.logger.error(f"解析 {manifest_path} 失败: {e}")
except Exception as e:
self.logger.error(f"处理插件 {plugin_name} 时出错: {e}")
return all_dependencies
def check(self, dependencies: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
检查指定的系统依赖是否已安装
Args:
dependencies: 依赖信息列表,格式同 scan() 返回值
Returns:
Dict: 检查结果
{
"total": int, # 总依赖数
"installed": int, # 已安装数
"missing": List[Dict], # 缺失的依赖详情
"all_installed": bool # 是否全部已安装
}
"""
result = {
"total": 0,
"installed": 0,
"missing": [],
"all_installed": True
}
for dep_info in dependencies:
plugin_name = dep_info["plugin"]
package_manager = dep_info["package_manager"]
for package in dep_info["dependencies"]:
result["total"] += 1
if self._is_package_installed(package, package_manager):
result["installed"] += 1
self.logger.debug(f"{package} 已安装 (插件: {plugin_name})")
else:
result["missing"].append({
"package": package,
"plugin": plugin_name,
"package_manager": package_manager
})
result["all_installed"] = False
self.logger.warning(f"{package} 未安装 (插件: {plugin_name})")
return result
def install(self, missing: List[Dict[str, str]],
auto_confirm: bool = True) -> Dict[str, Any]:
"""
安装缺失的系统依赖
Args:
missing: 缺失的依赖列表,格式为 [{"package": str, "package_manager": str}]
auto_confirm: 是否自动确认安装
Returns:
Dict: 安装结果
{
"success": List[str], # 成功安装的包
"failed": List[Dict], # 安装失败的包及原因
"total": int, # 尝试安装的总数
}
"""
result = {
"success": [],
"failed": [],
"total": len(missing)
}
if not missing:
self.logger.info("没有需要安装的依赖")
return result
# 按包管理器分组
packages_by_pm: Dict[str, List[str]] = {}
for item in missing:
pm = item.get("package_manager", "apt-get")
pkg = item["package"]
if pm not in packages_by_pm:
packages_by_pm[pm] = []
packages_by_pm[pm].append(pkg)
# 执行安装
for pm, packages in packages_by_pm.items():
self.logger.info(f"使用 {pm} 安装包: {', '.join(packages)}")
success, failed = self._install_packages(packages, pm, auto_confirm)
result["success"].extend(success)
for fail_pkg, reason in failed:
result["failed"].append({
"package": fail_pkg,
"reason": reason
})
return result
def info(self) -> Dict[str, Any]:
"""
获取插件信息
Returns:
Dict: 插件详细信息
"""
return {
"name": self.name,
"version": self.version,
"description": self.description,
"supported_package_managers": [
"apt-get", "yum", "dnf", "pacman", "brew", "apk"
],
"api_methods": ["scan", "check", "install", "info"]
}
def _find_manifest_for_plugin(self, plugin_name: str) -> Optional[Path]:
"""查找插件对应的 manifest 文件"""
# 可能的 manifest 文件位置
possible_paths = [
self.plugins_dir / f"{plugin_name}.json",
self.plugins_dir / plugin_name / "manifest.json",
self.plugins_dir / f"{plugin_name}" / f"{plugin_name}.json",
]
for path in possible_paths:
if path.exists():
return path
# 也检查插件文件同目录下的同名 json 文件
plugin_file = self.plugins_dir / f"{plugin_name}.py"
if plugin_file.exists():
json_file = self.plugins_dir / f"{plugin_name}.json"
if json_file.exists():
return json_file
return None
def _is_package_installed(self, package: str, package_manager: str) -> bool:
"""检查包是否已安装"""
try:
if package_manager in ["apt-get", "apt"]:
cmd = ["dpkg", "-l", package]
elif package_manager in ["yum", "dnf"]:
cmd = ["rpm", "-q", package]
elif package_manager == "pacman":
cmd = ["pacman", "-Q", package]
elif package_manager == "brew":
cmd = ["brew", "list", "--versions", package]
elif package_manager == "apk":
cmd = ["apk", "info", "-e", package]
else:
# 默认使用 which/whereis 检查可执行文件
cmd = ["which", package]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
except subprocess.TimeoutExpired:
self.logger.warning(f"检查包 {package} 超时")
return False
except Exception as e:
self.logger.error(f"检查包 {package} 时出错: {e}")
return False
def _install_packages(self, packages: List[str],
package_manager: str,
auto_confirm: bool = True) -> tuple:
"""
安装包
Returns:
tuple: (success_list, failed_list)
success_list: 成功安装的包名列表
failed_list: [(包名, 失败原因), ...]
"""
success = []
failed = []
try:
if package_manager in ["apt-get", "apt"]:
cmd_prefix = ["apt-get", "install", "-y"] if auto_confirm else ["apt-get", "install"]
elif package_manager == "yum":
cmd_prefix = ["yum", "install", "-y"] if auto_confirm else ["yum", "install"]
elif package_manager == "dnf":
cmd_prefix = ["dnf", "install", "-y"] if auto_confirm else ["dnf", "install"]
elif package_manager == "pacman":
cmd_prefix = ["pacman", "-S", "--noconfirm"] if auto_confirm else ["pacman", "-S"]
elif package_manager == "brew":
cmd_prefix = ["brew", "install"]
elif package_manager == "apk":
cmd_prefix = ["apk", "add"]
else:
self.logger.error(f"不支持的包管理器: {package_manager}")
for pkg in packages:
failed.append((pkg, f"不支持的包管理器: {package_manager}"))
return success, failed
# 合并命令
cmd = cmd_prefix + packages
self.logger.info(f"执行安装命令: {' '.join(cmd)}")
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=300 # 5 分钟超时
)
if result.returncode == 0:
success.extend(packages)
self.logger.info(f"成功安装包: {', '.join(packages)}")
else:
error_msg = result.stderr.strip() or result.stdout.strip()
for pkg in packages:
failed.append((pkg, error_msg))
self.logger.error(f"安装包失败: {error_msg}")
except subprocess.TimeoutExpired:
for pkg in packages:
failed.append((pkg, "安装超时"))
self.logger.error("安装包超时")
except PermissionError:
for pkg in packages:
failed.append((pkg, "权限不足,需要 root 权限"))
self.logger.error("安装包需要 root 权限")
except Exception as e:
for pkg in packages:
failed.append((pkg, str(e)))
self.logger.error(f"安装包时发生异常: {e}")
return success, failed
def execute(self, action: str, **kwargs) -> Any:
"""
执行插件动作 (供插件加载器调用)
Args:
action: 动作名称 (scan, check, install, info)
**kwargs: 动作参数
Returns:
动作执行结果
"""
if action == "scan":
return self.scan()
elif action == "check":
dependencies = kwargs.get("dependencies", self.scan())
return self.check(dependencies)
elif action == "install":
missing = kwargs.get("missing", [])
auto_confirm = kwargs.get("auto_confirm", True)
return self.install(missing, auto_confirm)
elif action == "info":
return self.info()
else:
raise ValueError(f"未知的动作: {action}")

View File

@@ -0,0 +1,7 @@
{
"name": "firewall",
"version": "1.0.0",
"description": "防火墙管理插件",
"system_dependencies": ["iptables", "ufw"],
"package_manager": "apt-get"
}

View File

@@ -0,0 +1,7 @@
{
"name": "ftp_server",
"version": "1.0.0",
"description": "FTP 服务器插件",
"system_dependencies": ["vsftpd", "proftpd"],
"package_manager": "apt-get"
}

View File

@@ -0,0 +1,77 @@
"""PL 注入 - 向插件加载器注册依赖自动安装功能
此文件通过 PL 注入机制向插件加载器注册以下功能:
- auto-dependency:scan: 扫描所有插件的系统依赖声明
- auto-dependency:check: 检查系统依赖是否已安装
- auto-dependency:install: 自动安装缺失的系统依赖
- auto-dependency:info: 获取插件系统信息
"""
def register(injector):
"""向插件加载器注册功能
Args:
injector: PLInjector 实例,提供 register_function 等方法
"""
# 注意:实际的功能实现由 main.py 中的 AutoDependencyPlugin 提供
# 这里我们通过导入插件实例来注册功能
import sys
from pathlib import Path
# 获取当前插件目录
current_file = Path(__file__)
plugin_dir = current_file.parent.parent
# 导入插件主模块
main_file = plugin_dir / "main.py"
# 创建安全的执行环境来加载插件
safe_globals = {
"__builtins__": {
"True": True, "False": False, "None": None,
"dict": dict, "list": list, "str": str, "int": int,
"float": float, "bool": bool, "tuple": tuple, "set": set,
"len": len, "range": range, "enumerate": enumerate,
"zip": zip, "map": map, "filter": filter,
"sorted": sorted, "reversed": reversed,
"min": min, "max": max, "sum": sum, "abs": abs,
"round": round, "isinstance": isinstance, "issubclass": issubclass,
"type": type, "id": id, "hash": hash, "repr": repr,
"print": print, "object": object, "property": property,
"staticmethod": staticmethod, "classmethod": classmethod,
"super": super, "iter": iter, "next": next,
"any": any, "all": all, "callable": callable,
"hasattr": hasattr, "getattr": getattr, "setattr": setattr,
"Exception": Exception, "BaseException": BaseException,
},
"__name__": "plugin.auto-dependency",
"__package__": "plugin.auto-dependency",
"__file__": str(main_file),
"Path": Path,
}
try:
with open(main_file, "r", encoding="utf-8") as f:
source = f.read()
code = compile(source, str(main_file), "exec")
exec(code, safe_globals)
# 获取 New 函数并创建插件实例
new_func = safe_globals.get("New")
if new_func and callable(new_func):
plugin_instance = new_func()
# 初始化插件
plugin_instance.init({
"scan_dirs": ["store"],
"auto_install": True
})
# 使用插件实例注册 PL 功能
plugin_instance.register_pl_functions(injector)
except Exception as e:
print(f"[auto-dependency] PL 注册失败:{e}")

View File

@@ -0,0 +1,117 @@
# 依赖自动安装插件 (auto-dependency)
## 概述
依赖自动安装插件是一个核心系统插件,用于扫描所有插件的声明文件,检查并自动安装系统依赖。
## 功能特性
1. **扫描插件声明** - 自动扫描所有插件目录下的 `manifest.json` 文件
2. **系统依赖检测** - 读取每个插件声明的系统依赖 (`system_dependencies` 字段)
3. **安装状态检查** - 检查这些系统依赖是否已在系统中安装
4. **自动安装** - 对于未安装的依赖,使用系统包管理器自动安装
5. **PL 注入接口** - 通过 PL 注入机制向插件加载器注册功能接口
## 使用方法
### 在 manifest.json 中声明系统依赖
其他插件可以在自己的 `manifest.json` 中声明所需的系统依赖:
```json
{
"metadata": {
"name": "my-plugin",
"version": "1.0.0",
"author": "MyName",
"description": "我的插件"
},
"config": {
"enabled": true,
"args": {}
},
"dependencies": ["plugin-loader"],
"system_dependencies": ["curl", "git", "wget"],
"permissions": []
}
```
### 通过 PL 注入接口调用
插件加载器加载此插件后,可以通过以下 PL 注入接口进行操作:
| 接口名称 | 说明 | 参数 | 返回值 |
|---------|------|------|--------|
| `auto-dependency:scan` | 扫描所有插件的声明文件 | `scan_dir` (可选,默认 "store") | 插件信息列表 |
| `auto-dependency:check` | 检查系统依赖安装状态 | `scan_dir` (可选,默认 "store") | 检查结果字典 |
| `auto-dependency:install` | 安装缺失的系统依赖 | `scan_dir` (可选,默认 "store") | 安装结果字典 |
| `auto-dependency:info` | 获取插件系统信息 | 无 | 系统信息字典 |
### 示例代码
```python
# 获取插件加载器中的 auto-dependency 功能
injector = get_pl_injector() # 从插件加载器获取
# 扫描所有插件的系统依赖声明
plugins = injector.get_injected_functions("auto-dependency:scan")[0]()
print(f"找到 {len(plugins)} 个插件")
# 检查依赖安装状态
result = injector.get_injected_functions("auto-dependency:check")[0]()
print(f"已安装:{result['installed_count']}, 缺失:{result['missing_count']}")
# 安装缺失的依赖
install_result = injector.get_injected_functions("auto-dependency:install")[0]()
print(f"成功安装:{install_result['success_count']}, 失败:{install_result['failed_count']}")
```
## 支持的包管理器
插件自动检测系统使用的包管理器,支持:
- **Debian/Ubuntu**: apt-get, apt
- **RHEL/CentOS**: yum, dnf
- **Arch Linux**: pacman
- **macOS**: brew
- **Alpine Linux**: apk
## 配置选项
`manifest.json``config.args` 中可以配置:
```json
{
"config": {
"enabled": true,
"args": {
"scan_dirs": ["store"],
"package_manager": "auto",
"auto_install": true
}
}
}
```
| 配置项 | 说明 | 默认值 |
|-------|------|--------|
| `scan_dirs` | 要扫描的目录列表 | `["store"]` |
| `package_manager` | 包管理器auto 为自动检测) | `"auto"` |
| `auto_install` | 是否自动安装缺失的依赖 | `true` |
## 安全说明
- 插件需要 `*` 权限才能执行系统命令安装包
- 包安装操作有超时限制300 秒)
- 所有安装操作都会记录日志
## 文件结构
```
store/@{FutureOSS}/auto-dependency/
├── manifest.json # 插件清单
├── main.py # 主逻辑实现
├── PL/
│ └── main.py # PL 注入入口
└── README.md # 本文档
```

View File

@@ -0,0 +1,407 @@
"""依赖自动安装插件 - 扫描所有插件的声明文件,检查并安装系统依赖
功能说明:
1. 扫描所有插件目录下的 manifest.json 文件
2. 读取每个插件声明的系统依赖 (system_dependencies 字段)
3. 检查这些系统依赖是否已安装
4. 对于未安装的依赖,使用系统包管理器自动安装
5. 通过 PL 注入机制向插件加载器注册功能接口
"""
import subprocess
import shutil
import json
from pathlib import Path
from typing import Any, Optional, List, Dict
from oss.plugin.types import Plugin
class SystemDependencyChecker:
"""系统依赖检查器"""
def __init__(self):
self.package_managers = {
"apt": ["apt-get", "apt"],
"yum": ["yum", "dnf"],
"pacman": ["pacman"],
"brew": ["brew"],
"apk": ["apk"],
}
self.detected_pm = self._detect_package_manager()
def _detect_package_manager(self) -> str:
"""检测系统包管理器"""
for pm, commands in self.package_managers.items():
for cmd in commands:
if shutil.which(cmd):
return pm
return "unknown"
def check_command(self, command: str) -> bool:
"""检查命令是否可用"""
return shutil.which(command) is not None
def check_package(self, package: str) -> bool:
"""检查系统包是否已安装"""
if not self.detected_pm or self.detected_pm == "unknown":
return False
try:
if self.detected_pm in ["apt", "apt-get"]:
result = subprocess.run(
["dpkg", "-l", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0 and "ii" in result.stdout
elif self.detected_pm in ["yum", "dnf"]:
result = subprocess.run(
["rpm", "-q", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "pacman":
result = subprocess.run(
["pacman", "-Q", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "brew":
result = subprocess.run(
["brew", "list", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
elif self.detected_pm == "apk":
result = subprocess.run(
["apk", "info", "-e", package],
capture_output=True,
text=True,
timeout=30
)
return result.returncode == 0
except Exception:
pass
return False
def install_package(self, package: str) -> bool:
"""安装系统包"""
if not self.detected_pm or self.detected_pm == "unknown":
return False
try:
if self.detected_pm in ["apt", "apt-get"]:
result = subprocess.run(
["apt-get", "install", "-y", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
elif self.detected_pm == "yum":
result = subprocess.run(
["yum", "install", "-y", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
elif self.detected_pm == "dnf":
result = subprocess.run(
["dnf", "install", "-y", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
elif self.detected_pm == "pacman":
result = subprocess.run(
["pacman", "-S", "--noconfirm", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
elif self.detected_pm == "brew":
result = subprocess.run(
["brew", "install", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
elif self.detected_pm == "apk":
result = subprocess.run(
["apk", "add", package],
capture_output=True,
text=True,
timeout=300
)
return result.returncode == 0
except Exception:
pass
return False
def check_and_install(self, package: str, auto_install: bool = True) -> Dict[str, Any]:
"""检查并安装包"""
result = {
"package": package,
"installed": self.check_package(package),
"action": "none",
"success": True,
"message": ""
}
if result["installed"]:
result["message"] = f"'{package}' 已安装"
return result
if not auto_install:
result["action"] = "skipped"
result["message"] = f"'{package}' 未安装,但自动安装已禁用"
result["success"] = False
return result
result["action"] = "installing"
if self.install_package(package):
result["installed"] = True
result["success"] = True
result["message"] = f"'{package}' 安装成功"
else:
result["success"] = False
result["message"] = f"'{package}' 安装失败"
return result
class AutoDependencyPlugin(Plugin):
"""依赖自动安装插件"""
def __init__(self):
self.checker = SystemDependencyChecker()
self.scan_dirs: List[str] = []
self.auto_install: bool = True
self._plugin_loader_ref: Optional[Any] = None
def init(self, deps: Optional[Dict[str, Any]] = None):
"""初始化插件"""
if deps:
self.scan_dirs = deps.get("scan_dirs", ["store"])
self.auto_install = deps.get("auto_install", True)
# 获取插件加载器引用(通过依赖注入)
if "plugin-loader" in deps:
self._plugin_loader_ref = deps["plugin-loader"]
def start(self):
"""启动插件"""
pass
def stop(self):
"""停止插件"""
pass
def scan_plugin_manifests(self, base_dir: str = "store") -> List[Dict[str, Any]]:
"""扫描所有插件的 manifest.json 文件
Returns:
包含所有插件信息的列表,每个元素包含:
- plugin_name: 插件名称
- plugin_dir: 插件目录路径
- manifest: manifest.json 内容
- system_dependencies: 系统依赖列表
"""
results = []
base_path = Path(base_dir)
if not base_path.exists():
return results
# 扫描所有插件目录
for vendor_dir in base_path.iterdir():
if not vendor_dir.is_dir():
continue
for plugin_dir in vendor_dir.iterdir():
if not plugin_dir.is_dir():
continue
manifest_file = plugin_dir / "manifest.json"
if not manifest_file.exists():
continue
try:
with open(manifest_file, "r", encoding="utf-8") as f:
manifest = json.load(f)
# 提取系统依赖
system_deps = manifest.get("system_dependencies", [])
results.append({
"plugin_name": plugin_dir.name.rstrip("}"),
"plugin_dir": str(plugin_dir),
"manifest": manifest,
"system_dependencies": system_deps
})
except Exception:
continue
return results
def check_all_dependencies(self, base_dir: str = "store") -> Dict[str, Any]:
"""检查所有插件的系统依赖
Args:
base_dir: 基础扫描目录
Returns:
检查结果字典,包含:
- total_plugins: 扫描的插件总数
- plugins_with_deps: 有系统依赖的插件数
- dependencies: 依赖检查结果列表
- missing_count: 缺失的依赖数量
- installed_count: 已安装的依赖数量
"""
plugins = self.scan_plugin_manifests(base_dir)
all_deps = {} # {package: [plugin_names]}
for plugin in plugins:
for dep in plugin["system_dependencies"]:
if dep not in all_deps:
all_deps[dep] = []
all_deps[dep].append(plugin["plugin_name"])
results = []
installed_count = 0
missing_count = 0
for package, plugin_names in all_deps.items():
is_installed = self.checker.check_package(package)
if is_installed:
installed_count += 1
else:
missing_count += 1
results.append({
"package": package,
"installed": is_installed,
"required_by": plugin_names
})
return {
"total_plugins": len(plugins),
"plugins_with_deps": sum(1 for p in plugins if p["system_dependencies"]),
"dependencies": results,
"missing_count": missing_count,
"installed_count": installed_count
}
def install_missing_dependencies(self, base_dir: str = "store") -> Dict[str, Any]:
"""安装所有缺失的系统依赖
Args:
base_dir: 基础扫描目录
Returns:
安装结果字典,包含:
- total_to_install: 需要安装的包数量
- success_count: 成功安装的包数量
- failed_count: 安装失败的包数量
- results: 每个包的安装结果
"""
check_result = self.check_all_dependencies(base_dir)
to_install = [dep for dep in check_result["dependencies"] if not dep["installed"]]
install_results = []
success_count = 0
failed_count = 0
for dep in to_install:
result = self.checker.check_and_install(dep["package"], auto_install=True)
result["required_by"] = dep["required_by"]
install_results.append(result)
if result["success"]:
success_count += 1
else:
failed_count += 1
return {
"total_to_install": len(to_install),
"success_count": success_count,
"failed_count": failed_count,
"results": install_results
}
def get_system_info(self) -> Dict[str, Any]:
"""获取系统信息"""
return {
"package_manager": self.checker.detected_pm,
"auto_install_enabled": self.auto_install,
"scan_directories": self.scan_dirs
}
def register_pl_functions(self, injector: Any):
"""注册 PL 注入功能
通过 PL 注入机制向插件加载器注册以下功能:
- auto-dependency:scan: 扫描所有插件的系统依赖
- auto-dependency:check: 检查依赖安装状态
- auto-dependency:install: 安装缺失的依赖
- auto-dependency:info: 获取插件系统信息
"""
# 注册扫描功能
def scan_deps(scan_dir: str = "store") -> Dict[str, Any]:
"""扫描所有插件的声明文件"""
return self.scan_plugin_manifests(scan_dir)
injector.register_function(
"auto-dependency:scan",
scan_deps,
"扫描所有插件的声明文件,获取系统依赖列表"
)
# 注册检查功能
def check_deps(scan_dir: str = "store") -> Dict[str, Any]:
"""检查所有系统依赖的安装状态"""
return self.check_all_dependencies(scan_dir)
injector.register_function(
"auto-dependency:check",
check_deps,
"检查所有插件声明的系统依赖是否已安装"
)
# 注册安装功能
def install_deps(scan_dir: str = "store") -> Dict[str, Any]:
"""安装所有缺失的系统依赖"""
return self.install_missing_dependencies(scan_dir)
injector.register_function(
"auto-dependency:install",
install_deps,
"自动安装所有缺失的系统依赖"
)
# 注册信息功能
def get_info() -> Dict[str, Any]:
"""获取插件系统信息"""
return self.get_system_info()
injector.register_function(
"auto-dependency:info",
get_info,
"获取自动依赖插件的系统信息"
)
def New() -> AutoDependencyPlugin:
"""创建插件实例"""
return AutoDependencyPlugin()

View File

@@ -0,0 +1,20 @@
{
"metadata": {
"name": "auto-dependency",
"version": "1.0.0",
"author": "FutureOSS",
"description": "依赖自动安装插件 - 扫描所有插件的声明文件,检查并安装系统依赖",
"type": "core"
},
"config": {
"enabled": true,
"args": {
"scan_dirs": ["store"],
"package_manager": "auto",
"auto_install": true,
"pl_injection": true
}
},
"dependencies": ["plugin-loader"],
"permissions": ["*"]
}

View File

@@ -0,0 +1,16 @@
{
"metadata": {
"name": "example-with-deps",
"version": "1.0.0",
"author": "FutureOSS",
"description": "示例插件 - 演示如何声明系统依赖",
"type": "example"
},
"config": {
"enabled": true,
"args": {}
},
"dependencies": [],
"system_dependencies": ["curl", "git", "wget"],
"permissions": []
}