Title: Implement minimal core framework with PL injection and update build config

Key features implemented:
- Updated package metadata and dependencies in PKG-INFO, setup files
- Added main.py entry point for backward compatibility with README launch method
- Enhanced CLI with config options, system info command, and proper signal handling
- Implemented minimal PluginManager loading only plugin-loader core plugin
- Refactored PluginLoader to follow minimal core design, removed sandbox/isolation complexity
- Updated auto-dependency plugin with safer PL injection mechanism and disabled pl_injection
- Removed legacy plugin files (firewall, frp_proxy, ftp_server, multi_lang_deploy, ops_toolbox, security_gateway) as functionality moved to core plugin system
- Improved gitignore with comprehensive ignore patterns

The changes implement a minimal core framework design where only the plugin-loader is directly loaded by the core, with all other plugins managed through the PL injection mechanism, significantly simplifying the architecture.
This commit is contained in:
qwen.ai[bot]
2026-04-25 10:47:26 +00:00
parent a9bc12596e
commit 97ced1b5e6
181 changed files with 667 additions and 1647 deletions

View File

@@ -0,0 +1,278 @@
"""插件加载 Pro - 核心管理器"""
import sys
import json
import importlib.util
from pathlib import Path
from typing import Any, Optional
from oss.plugin.types import Plugin
from .config import ProConfig
from .registry import CapabilityRegistry
from .proxy import PluginProxy, PermissionError
from ..models.plugin_info import PluginInfo
from ..circuit.breaker import CircuitBreaker
from ..retry.handler import RetryHandler
from ..fallback.handler import FallbackHandler
from ..recovery.health import HealthChecker
from ..recovery.auto_fix import AutoRecovery
from ..isolation.timeout import TimeoutController, TimeoutError
from ..utils.logger import ProLogger
from oss.plugin.capabilities import scan_capabilities
class ProPluginManager:
"""Pro 插件管理器"""
def __init__(self, config: ProConfig):
self.config = config
self.plugins: dict[str, dict[str, Any]] = {}
self.capability_registry = CapabilityRegistry()
self._breakers: dict[str, CircuitBreaker] = {}
self._health_checker = HealthChecker(
config.health_check.interval,
config.health_check.timeout,
config.health_check.max_failures
)
self._auto_recovery = AutoRecovery(
config.auto_recovery.max_attempts,
config.auto_recovery.delay
)
def load_all(self, store_dir: str = "store"):
"""加载所有插件"""
ProLogger.info("loader", "开始扫描插件...")
self._load_from_dir(Path(store_dir))
ProLogger.info("loader", f"共加载 {len(self.plugins)} 个插件")
def _load_from_dir(self, store_dir: Path):
"""从目录加载插件"""
if not store_dir.exists():
return
for author_dir in store_dir.iterdir():
if not author_dir.is_dir():
continue
for plugin_dir in author_dir.iterdir():
if not plugin_dir.is_dir():
continue
main_file = plugin_dir / "main.py"
if not main_file.exists():
continue
self._load_single_plugin(plugin_dir)
def _load_single_plugin(self, plugin_dir: Path) -> Optional[Any]:
"""加载单个插件"""
main_file = plugin_dir / "main.py"
manifest_file = plugin_dir / "manifest.json"
try:
manifest = {}
if manifest_file.exists():
with open(manifest_file, "r", encoding="utf-8") as f:
manifest = json.load(f)
spec = importlib.util.spec_from_file_location(
f"pro_plugin.{plugin_dir.name}", str(main_file)
)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
if not hasattr(module, "New"):
return None
instance = module.New()
plugin_name = plugin_dir.name.rstrip("}")
permissions = manifest.get("permissions", [])
if permissions:
instance = PluginProxy(
plugin_name, instance, permissions, self.plugins
)
info = PluginInfo()
meta = manifest.get("metadata", {})
info.name = meta.get("name", plugin_name)
info.version = meta.get("version", "1.0.0")
info.author = meta.get("author", "")
info.description = meta.get("description", "")
info.dependencies = manifest.get("dependencies", [])
info.capabilities = scan_capabilities(plugin_dir)
for cap in info.capabilities:
self.capability_registry.register_provider(
cap, plugin_name, instance
)
self._breakers[plugin_name] = CircuitBreaker(
self.config.circuit_breaker.failure_threshold,
self.config.circuit_breaker.recovery_timeout,
self.config.circuit_breaker.half_open_requests
)
self.plugins[plugin_name] = {
"instance": instance,
"module": module,
"info": info,
"permissions": permissions,
"dir": plugin_dir
}
ProLogger.info("loader", f"已加载: {plugin_name} v{info.version}")
return instance
except Exception as e:
ProLogger.error("loader", f"加载失败 {plugin_dir.name}: {e}")
return None
def init_and_start_all(self):
"""初始化并启动所有插件"""
ProLogger.info("manager", "开始初始化所有插件...")
self._inject_dependencies()
ordered = self._get_ordered_plugins()
for name in ordered:
self._safe_init(name)
ProLogger.info("manager", "开始启动所有插件...")
for name in ordered:
self._safe_start(name)
self._health_checker.start(
on_failure_callback=self._on_plugin_failure
)
def _safe_init(self, name: str):
"""安全初始化插件"""
info = self.plugins[name]
instance = info["instance"]
breaker = self._breakers[name]
try:
breaker.call(instance.init)
info["info"].status = "initialized"
ProLogger.info("manager", f"已初始化: {name}")
except Exception as e:
ProLogger.error("manager", f"初始化失败 {name}: {e}")
info["info"].status = "error"
info["info"].error_count += 1
info["info"].last_error = str(e)
def _safe_start(self, name: str):
"""安全启动插件"""
info = self.plugins[name]
instance = info["instance"]
breaker = self._breakers[name]
try:
breaker.call(instance.start)
info["info"].status = "running"
self._health_checker.add_plugin(name, instance)
ProLogger.info("manager", f"已启动: {name}")
except Exception as e:
ProLogger.error("manager", f"启动失败 {name}: {e}")
info["info"].status = "error"
info["info"].error_count += 1
info["info"].last_error = str(e)
def stop_all(self):
"""停止所有插件"""
self._health_checker.stop()
for name in reversed(list(self.plugins.keys())):
self._safe_stop(name)
def _safe_stop(self, name: str):
"""安全停止插件"""
info = self.plugins[name]
instance = info["instance"]
try:
instance.stop()
info["info"].status = "stopped"
ProLogger.info("manager", f"已停止: {name}")
except Exception as e:
ProLogger.warn("manager", f"停止异常 {name}: {e}")
def _on_plugin_failure(self, name: str):
"""插件失败回调"""
ProLogger.error("recovery", f"插件 {name} 健康检查失败")
if not self.config.auto_recovery.enabled:
return
info = self.plugins.get(name)
if not info:
return
plugin_dir = info.get("dir")
module = info.get("module")
instance = info.get("instance")
if plugin_dir:
result = self._auto_recovery.attempt_recovery(
name, plugin_dir, module, instance
)
if result:
info["instance"] = result
info["info"].status = "running"
self._health_checker.reset_failure_count(name)
def _inject_dependencies(self):
"""注入依赖"""
name_map = {}
for name in self.plugins:
clean = name.rstrip("}")
name_map[clean] = name
name_map[clean + "}"] = name
for name, info in self.plugins.items():
deps = info["info"].dependencies
if not deps:
continue
for dep_name in deps:
actual_dep = name_map.get(dep_name) or name_map.get(dep_name + "}")
if actual_dep and actual_dep in self.plugins:
dep_instance = self.plugins[actual_dep]["instance"]
setter = f"set_{dep_name.replace('-', '_')}"
if hasattr(info["instance"], setter):
try:
getattr(info["instance"], setter)(dep_instance)
ProLogger.info("inject", f"{name} <- {actual_dep}")
except Exception as e:
ProLogger.error("inject", f"注入失败 {name}.{setter}: {e}")
def _get_ordered_plugins(self) -> list[str]:
"""获取插件顺序"""
ordered = []
visited = set()
def visit(name):
if name in visited:
return
visited.add(name)
info = self.plugins.get(name)
if not info:
return
for dep in info["info"].dependencies:
clean_dep = dep.rstrip("}")
if clean_dep in self.plugins:
visit(clean_dep)
ordered.append(name)
for name in self.plugins:
visit(name)
return ordered