Files
NebulaShell/oss/core/manager.py
Falck bce27db4ac 重大重构:引擎模块拆分 + P0插件实现 + 55个Bug修复
核心变更:
- engine.py(1781行)拆分为8个独立模块: lifecycle/security/deps/
  datastore/pl_injector/watcher/signature/manager
- 新增plugin-bridge: 事件总线 + 服务注册 + RPC通信
- 新增i18n: 国际化/多语言翻译支持
- 新增plugin-storage: 插件键值/文件存储
- 新增ws-api: WebSocket实时通信(pub/sub + 自定义处理器)
- nodejs-adapter统一为Plugin ABC模式

Bug修复:
- 修复load_all()中store_dir未定义崩溃
- 修复DependencyResolver入度计算(拓扑排序)
- 修复PermissionError隐藏内置异常
- 修复CORS中间件头部未附加到响应
- 修复IntegrityChecker跳过__pycache__目录
- 修复版本号不一致(v2.0.0→v1.2.0)
- 修复测试文件的Logger导入/路径/私有方法调用
- 修复context.py缺少typing导入
- 修复config.py STORE_DIR默认路径(./mods→./store)

测试覆盖: 14→91个测试, 全部通过
2026-05-12 11:40:06 +08:00

753 lines
29 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""NebulaShell Core Engine — 核心引擎
整合功能:
- 插件加载(目录结构)
- 生命周期管理
- 依赖解析
- 签名校验RSA-SHA256
- PL 注入(沙箱执行)
- 能力注册
- 文件监控与热重载
- HTTP 服务(子模块)
- REPL 终端(子模块)
- 全面防护:完整性检查、内存保护、行为审计、防篡改监控、降级恢复
- 数据存储接口(为 data-store 插件预留)
"""
import sys
import json
import re
import os
import time
import types
import hashlib
import threading
import traceback
import importlib.util
import functools
from pathlib import Path
from typing import Any, Optional, Callable
from oss.plugin.types import register_plugin_type
from oss.plugin.capabilities import scan_capabilities
from oss.logger.logger import Log
from oss.config import get_config
from oss.core.lifecycle import LifecycleManager, Lifecycle
from oss.core.security import IntegrityChecker, MemoryGuard, AuditLogger, TamperMonitor, FallbackManager, PluginPermissionError, PluginProxy
from oss.core.deps import DependencyError, DependencyResolver
from oss.core.datastore import DataStore
from oss.core.pl_injector import PLValidationError, PLInjector
from oss.core.watcher import HotReloadError, FileWatcher
from oss.core.signature import SignatureError, SignatureVerifier, PluginSigner
class PluginInfo:
"""插件信息"""
def __init__(self):
self.name: str = ""
self.version: str = ""
self.author: str = ""
self.description: str = ""
self.readme: str = ""
self.config: dict[str, Any] = {}
self.extensions: dict[str, Any] = {}
self.lifecycle: Any = None
self.capabilities: set[str] = set()
self.dependencies: list[str] = []
self.pl_injected: bool = False
self.file_hash: str = "" # 文件完整性 hash
class CapabilityRegistry:
"""能力注册表"""
def __init__(self, permission_check: bool = True):
self.providers: dict = {}
self.consumers: dict = {}
self.permission_check = permission_check
def register_provider(self, capability: str, plugin_name: str, instance: Any):
self.providers[capability] = {"plugin": plugin_name, "instance": instance}
if capability not in self.consumers:
self.consumers[capability] = []
def register_consumer(self, capability: str, plugin_name: str):
if capability not in self.consumers:
self.consumers[capability] = []
if plugin_name not in self.consumers[capability]:
self.consumers[capability].append(plugin_name)
def get_provider(self, capability: str, requester: str = "", allowed_plugins: list = None) -> Optional[Any]:
if capability not in self.providers:
return None
if self.permission_check and allowed_plugins is not None:
pn = self.providers[capability]["plugin"]
if pn != requester and pn not in allowed_plugins and "*" not in allowed_plugins:
raise PluginPermissionError(f"插件 '{requester}' 无权使用能力 '{capability}'")
return self.providers[capability]["instance"]
def has_capability(self, capability: str) -> bool:
return capability in self.providers
def get_consumers(self, capability: str) -> list:
return self.consumers.get(capability, [])
class PluginManager:
"""插件管理器 — Core 的核心"""
def __init__(self, permission_check: bool = True):
self.plugins: dict = {}
self.capability_registry = CapabilityRegistry(permission_check=permission_check)
self.permission_check = permission_check
self.enforce_signature = True
self.pl_injector = PLInjector(self)
self.lifecycle_manager = LifecycleManager()
self.dependency_resolver = DependencyResolver()
self.signature_verifier = SignatureVerifier()
self.hot_reload_watcher = None
# 全面防护
self.integrity_checker = IntegrityChecker()
self.memory_guard = MemoryGuard(self)
self.audit_logger = AuditLogger()
self.tamper_monitor = TamperMonitor(self)
self.fallback_manager = FallbackManager(self)
# 数据存储
self.data_store = DataStore()
# HTTP 服务 & REPL
self.http_server = None
self.repl_shell = None
# NBPF 组件
self.nbpf_loader = None
self._nbpf_initialized = False
# 插件目录映射
self._plugin_dirs: dict[str, Path] = {}
# ── NBPF 支持 ──
def _init_nbpf(self):
"""初始化 NBPF 加载器"""
if self._nbpf_initialized:
return
try:
from oss.core.nbpf import NBPFLoader, NBPCrypto, NIRCompiler
config = get_config()
self._trusted_keys_dir = Path(config.get("NBPF_TRUSTED_KEYS_DIR", "./data/nbpf-keys/trusted"))
rsa_keys_dir = Path(config.get("NBPF_RSA_KEYS_DIR", "./data/nbpf-keys/rsa"))
# 加载信任的 Ed25519 公钥
trusted_ed25519 = {}
if self._trusted_keys_dir.exists():
for kf in self._trusted_keys_dir.glob("*.pem"):
name = kf.stem
trusted_ed25519[name] = kf.read_bytes()
# 加载信任的 RSA 公钥
trusted_rsa = {}
if rsa_keys_dir.exists():
for kf in rsa_keys_dir.glob("*.pem"):
name = kf.stem
trusted_rsa[name] = kf.read_bytes()
# 加载 RSA 私钥
rsa_private = None
private_dir = Path(config.get("NBPF_KEYS_DIR", "./data/nbpf-keys")) / "private"
if private_dir.exists():
pk_files = list(private_dir.glob("*.pem"))
if pk_files:
rsa_private = pk_files[0].read_bytes()
self.nbpf_loader = NBPFLoader(
crypto=NBPCrypto(),
compiler=NIRCompiler(),
trusted_ed25519_keys=trusted_ed25519,
trusted_rsa_keys=trusted_rsa,
rsa_private_key=rsa_private,
)
self._nbpf_initialized = True
Log.info("Core", "NBPF 加载器已初始化")
except Exception as e:
Log.warn("Core", f"NBPF 加载器初始化失败: {e}")
def load_nbpf(self, nbpf_path: Path, plugin_name: str = None) -> Optional[Any]:
"""加载 .nbpf 插件文件
如果插件作者不在本地信任列表中,会通过 CLI 交互询问用户是否信任。
信任后自动将公钥加入信任列表,下次无需再次询问。
Args:
nbpf_path: .nbpf 文件路径
plugin_name: 可选,插件名称
Returns:
插件实例,失败或用户拒绝信任返回 None
"""
if not self._nbpf_initialized:
self._init_nbpf()
if self.nbpf_loader is None:
Log.error("Core", "NBPF 加载器未初始化,无法加载 .nbpf 文件")
return None
# 第一次尝试加载
result = self._do_load_nbpf(nbpf_path, plugin_name)
if result is not None:
return result
# 如果第一次失败(未信任且用户首次拒绝),不再重试
return None
def _do_load_nbpf(self, nbpf_path: Path, plugin_name: str = None) -> Optional[Any]:
"""执行 .nbpf 加载,含信任检查"""
import base64 as _b64
import hashlib as _hl
try:
instance, info = self.nbpf_loader.load(nbpf_path, plugin_name)
name = info["name"]
is_trusted = info.get("trusted", False)
# 如果作者未被信任,询问用户
if not is_trusted:
author = info.get("author", "unknown")
pub_key_b64 = info.get("signer_public_key", "")
pub_key_bytes = _b64.b64decode(pub_key_b64)
# 计算公钥指纹SHA256 前 16 位 hex
fingerprint = _hl.sha256(pub_key_bytes).hexdigest()[:16]
print("\n" + "=" * 54)
print(f" [NBPF] 检测到未知作者的插件")
print(f" {'' * 50}")
print(f" 插件名称: {name}")
print(f" 插件作者: {author}")
print(f" 插件版本: {info.get('version', '?')}")
print(f" 作者公钥指纹: {fingerprint}")
print(f" {'' * 50}")
answer = input(" 是否信任此作者? [y/N] > ").strip().lower()
if answer in ("y", "yes"):
# 用户信任 → 保存公钥到信任列表
self._trust_author(pub_key_bytes, name, author)
# 重新加载
return self._do_load_nbpf(nbpf_path, plugin_name)
else:
Log.warn("Core", f"用户已拒绝信任作者 '{author}',跳过插件 {name}")
return None
# 构建 PluginInfo
pinfo = PluginInfo()
pinfo.name = name
pinfo.version = info.get("version", "")
pinfo.author = info.get("author", "")
pinfo.description = info.get("description", "")
pinfo.dependencies = info.get("manifest", {}).get("dependencies", [])
# 注册到插件列表
self.plugins[name] = {
"instance": instance,
"module": None,
"info": pinfo,
"permissions": [],
"nbpf_path": str(nbpf_path),
}
self._plugin_dirs[name] = nbpf_path.parent
# 生命周期
pinfo.lifecycle = self.lifecycle_manager.create(name)
# 审计日志
self.audit_logger.log(name, "loaded", f".nbpf 版本 {pinfo.version}")
Log.ok("Core", f"NBPF 插件 '{name}' 加载成功")
return instance
except Exception as e:
Log.error("Core", f"NBPF 插件加载失败: {e}")
return None
def _trust_author(self, pub_key_bytes: bytes, plugin_name: str, author: str):
"""将作者公钥加入本地信任列表"""
import hashlib as _hl
fingerprint = _hl.sha256(pub_key_bytes).hexdigest()[:16]
key_name = f"author_{fingerprint}"
# 创建信任目录
if not hasattr(self, '_trusted_keys_dir') or self._trusted_keys_dir is None:
from oss.config import get_config
cfg = get_config()
self._trusted_keys_dir = Path(cfg.get("NBPF_TRUSTED_KEYS_DIR", "./data/nbpf-keys/trusted"))
self._trusted_keys_dir.mkdir(parents=True, exist_ok=True)
# 保存公钥文件
key_path = self._trusted_keys_dir / f"{key_name}.pem"
key_path.write_bytes(pub_key_bytes)
# 更新加载器的信任列表
self.nbpf_loader.trusted_ed25519_keys[key_name] = pub_key_bytes
Log.ok("NBPF", f"已将作者 '{author}' 加入信任列表 ({key_path})")
def _get_plugin_dir(self, plugin_name: str) -> Optional[Path]:
return self._plugin_dirs.get(plugin_name)
def _load_manifest(self, plugin_dir: Path) -> dict:
mf = plugin_dir / "manifest.json"
if not mf.exists():
return {}
with open(mf, "r", encoding="utf-8") as f:
return json.load(f)
def _load_readme(self, plugin_dir: Path) -> str:
rf = plugin_dir / "README.md"
if not rf.exists():
return ""
with open(rf, "r", encoding="utf-8") as f:
return f.read()
def _parse_config_file(self, file_path: Path, file_type: str) -> dict:
"""通用配置文件解析 - 使用 ast.literal_eval 安全解析"""
import ast
if not file_path.exists():
return {}
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
Log.warn("Core", f"{file_type}文件不存在:{file_path}")
return {}
except PermissionError as e:
Log.error("Core", f"{file_type}文件无权限读取:{file_path} - {e}")
return {}
except UnicodeDecodeError as e:
Log.error("Core", f"{file_type}文件编码错误:{file_path} - {e}")
return {}
try:
result = ast.literal_eval(content)
if isinstance(result, dict):
return {k: v for k, v in result.items() if not k.startswith("_")}
except (ValueError, SyntaxError):
pass
config = {}
for line in content.split('\n'):
line = line.strip()
if not line or line.startswith('#'):
continue
match = re.match(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', line)
if match:
key, value_str = match.groups()
if key.startswith('_'):
continue
try:
value = ast.literal_eval(value_str)
config[key] = value
except (ValueError, SyntaxError):
Log.warn("Core", f"{file_path} 跳过无效的值:{line}")
continue
return config
def _load_config(self, plugin_dir: Path) -> dict:
return self._parse_config_file(plugin_dir / "config.py", "配置")
def _load_extensions(self, plugin_dir: Path) -> dict:
return self._parse_config_file(plugin_dir / "extensions.py", "扩展")
def load(self, plugin_dir: Path, use_sandbox: bool = True) -> Optional[Any]:
"""加载单个插件
支持:
- 目录结构插件main.py
- .nbpf 文件(直接传入 .nbpf 路径)
"""
# 如果是 .nbpf 文件,使用 NBPF 加载器
if plugin_dir.suffix == ".nbpf":
return self.load_nbpf(plugin_dir)
main_file = plugin_dir / "main.py"
if not main_file.exists():
return None
manifest = self._load_manifest(plugin_dir)
readme = self._load_readme(plugin_dir)
config = self._load_config(plugin_dir)
extensions = self._load_extensions(plugin_dir)
capabilities = scan_capabilities(plugin_dir)
plugin_name = plugin_dir.name.rstrip("}")
# 完整性检查:加载前计算 hash
self.integrity_checker.register(plugin_name, plugin_dir)
# PL 注入检查
pl_injection = manifest.get("config", {}).get("args", {}).get("pl_injection", False)
if pl_injection:
Log.tip("Core", f"插件 '{plugin_name}' 声明了 pl_injection正在检查 PL/ 文件夹...")
if not self.pl_injector.check_and_load_pl(plugin_dir, plugin_name):
Log.error("Core", f"插件 '{plugin_name}' 因 PL 注入检查失败被拒绝加载")
return None
Log.ok("Core", f"插件 '{plugin_name}' PL 注入检查通过")
permissions = manifest.get("permissions", [])
spec = importlib.util.spec_from_file_location(f"plugin.{plugin_name}", str(main_file))
module = importlib.util.module_from_spec(spec)
module.__package__ = f"plugin.{plugin_name}"
module.__path__ = [str(plugin_dir)]
sys.modules[spec.name] = module
spec.loader.exec_module(module)
if not hasattr(module, "New"):
return None
instance = module.New()
if self.permission_check and 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", "")
info.author = meta.get("author", "")
info.description = meta.get("description", "")
info.readme = readme
info.config = manifest.get("config", {}).get("args", config)
info.extensions = extensions
info.capabilities = capabilities
info.dependencies = manifest.get("dependencies", [])
info.pl_injected = pl_injection
info.file_hash = self.integrity_checker.get_hash(plugin_name) or ""
for cap in capabilities:
self.capability_registry.register_provider(cap, plugin_name, instance)
info.lifecycle = self.lifecycle_manager.create(plugin_name)
self.plugins[plugin_name] = {"instance": instance, "module": module, "info": info, "permissions": permissions}
self._plugin_dirs[plugin_name] = plugin_dir
# 审计日志
self.audit_logger.log(plugin_name, "loaded", f"版本 {info.version}")
# 通过 bridge 通知其他插件
if plugin_name != "plugin-bridge":
bridge = self._get_bridge()
if bridge:
bridge.emit("plugin.loaded", name=plugin_name, version=info.version)
return instance
def _restart_plugin(self, plugin_name: str):
"""重启单个插件"""
if plugin_name not in self.plugins:
return
plugin_dir = self._plugin_dirs.get(plugin_name)
if not plugin_dir:
return
# 停止旧实例
try:
if hasattr(self.plugins[plugin_name]["instance"], "stop"):
self.plugins[plugin_name]["instance"].stop()
except Exception:
pass
# 从 sys.modules 中移除
module_name = f"plugin.{plugin_name}"
if module_name in sys.modules:
del sys.modules[module_name]
module_name = f"nbpf.{plugin_name}"
if module_name in sys.modules:
del sys.modules[module_name]
# 重新加载
del self.plugins[plugin_name]
self.load(plugin_dir)
def load_all(self, mods_dir: str = "mods"):
if 'plugin' not in sys.modules:
pkg = types.ModuleType('plugin')
pkg.__path__ = []
pkg.__package__ = 'plugin'
sys.modules['plugin'] = pkg
Log.tip("Core", "已创建 plugin 命名空间包")
from oss.config import get_config
config = get_config()
store_dir = str(config.get("STORE_DIR", "./store"))
if not self._check_any_plugins(store_dir):
Log.warn("Core", "未检测到任何插件")
self._bootstrap_installation()
self._load_plugins_from_dir(Path(store_dir))
self._sort_by_dependencies()
def _check_any_plugins(self, store_dir: str) -> bool:
sp = Path(store_dir)
if not sp.exists():
return False
for vendor_dir in sp.iterdir():
if vendor_dir.is_dir():
for plugin_dir in vendor_dir.iterdir():
if plugin_dir.is_dir() and (plugin_dir / "main.py").exists():
return True
return False
def _load_plugins_from_dir(self, store_dir: Path):
if not store_dir.exists():
Log.warn("Core", f"插件目录不存在: {store_dir}")
return
for vendor_dir in sorted(store_dir.iterdir()):
if not vendor_dir.is_dir():
continue
for plugin_dir in sorted(vendor_dir.iterdir()):
if not plugin_dir.is_dir():
continue
try:
self.load(plugin_dir)
except Exception as e:
Log.error("Core", f"加载插件失败 {plugin_dir.name}: {e}")
self._link_capabilities()
def _load_mods_from_dir(self, mods_dir: Path):
if not mods_dir.exists():
return
nbpf_files = []
for f in mods_dir.iterdir():
if f.is_file() and f.suffix == ".nbpf":
nbpf_files.append(f)
nbpf_files.sort(key=lambda x: x.name)
for f in nbpf_files:
Log.info("Core", f"加载模组: {f.name}")
self.load(f)
self._link_capabilities()
def _check_any_mods(self, mods_dir: str) -> bool:
sp = Path(mods_dir)
if sp.exists():
for f in sp.iterdir():
if f.is_file() and f.suffix == ".nbpf":
return True
return False
def _bootstrap_installation(self):
Log.info("Core", "跳过引导安装(无可用插件)")
def _sort_by_dependencies(self):
for n, i in self.plugins.items():
self.dependency_resolver.add_dependency(n, i["info"].dependencies)
try:
order = self.dependency_resolver.resolve()
sp = {}
for n in order:
if n in self.plugins:
sp[n] = self.plugins[n]
for n in set(self.plugins.keys()) - set(sp.keys()):
sp[n] = self.plugins[n]
self.plugins = sp
except Exception as e:
Log.error("Core", f"依赖解析失败: {e}")
def _link_capabilities(self):
for pn, info in self.plugins.items():
for cap in info["info"].capabilities:
if self.capability_registry.has_capability(cap):
for cn in self.capability_registry.get_consumers(cap):
if cn in self.plugins:
ci = self.plugins[cn]["info"]
ca = self.plugins[cn].get("permissions", [])
try:
p = self.capability_registry.get_provider(cap, requester=cn, allowed_plugins=ca)
if p and hasattr(ci, "extensions"):
ci.extensions[f"_{cap}_provider"] = p
except PluginPermissionError as e:
Log.error("Core", f"权限拒绝: {e}")
def start_all(self):
self._inject_dependencies()
for n, i in self.plugins.items():
try:
wrapped = self.fallback_manager.wrap_plugin_method(n, i["instance"].start)
wrapped()
except Exception as e:
Log.error("Core", f"启动失败 {n}: {e}")
def _get_bridge(self):
"""Get the plugin-bridge instance if loaded."""
if "plugin-bridge" in self.plugins:
bridge = self.plugins["plugin-bridge"]["instance"]
if hasattr(bridge, "emit"):
return bridge
return None
def init_and_start_all(self):
Log.info("Core", f"init_and_start_all 被调用plugins={len(self.plugins)}")
self._inject_dependencies()
ordered = self._get_ordered_plugins()
Log.tip("Core", f"插件启动顺序: {' -> '.join(ordered)}")
for name in ordered:
if "Core" in name:
continue
try:
Log.info("Core", f"初始化: {name}")
wrapped_init = self.fallback_manager.wrap_plugin_method(name, self.plugins[name]["instance"].init)
wrapped_init()
except Exception as e:
Log.error("Core", f"初始化失败 {name}: {e}")
for name in ordered:
if "Core" in name:
continue
try:
Log.info("Core", f"启动: {name}")
wrapped_start = self.fallback_manager.wrap_plugin_method(name, self.plugins[name]["instance"].start)
wrapped_start()
bridge = self._get_bridge()
if bridge and name != "plugin-bridge":
bridge.emit("plugin.started", name=name)
except Exception as e:
Log.error("Core", f"启动失败 {name}: {e}")
def _get_ordered_plugins(self) -> list[str]:
try:
ordered = [n for n in self.dependency_resolver.resolve() if n in self.plugins]
if ordered:
return ordered
except Exception as e:
Log.warn("Core", f"依赖解析失败,使用原始顺序: {e}")
return list(self.plugins.keys())
def _inject_dependencies(self):
Log.info("Core", f"开始注入依赖,共 {len(self.plugins)} 个插件")
nm = {}
for n in self.plugins:
c = n.rstrip("}")
nm[c] = n
nm[c + "}"] = n
for n, i in self.plugins.items():
inst = i["instance"]
io = i.get("info")
if not io or not io.dependencies:
continue
for dn in io.dependencies:
ad = nm.get(dn) or nm.get(dn + "}")
if ad and ad in self.plugins:
sn = f"set_{dn.replace('-', '_')}"
if hasattr(inst, sn):
try:
getattr(inst, sn)(self.plugins[ad]["instance"])
Log.ok("Core", f"注入成功: {n} <- {ad}")
except Exception as e:
Log.error("Core", f"注入依赖失败 {n}.{sn}: {e}")
else:
Log.warn("Core", f"{n} 没有 {sn} 方法")
def stop_all(self):
for n, i in reversed(list(self.plugins.items())):
try:
if hasattr(i["instance"], "stop"):
i["instance"].stop()
bridge = self._get_bridge()
if bridge and n != "plugin-bridge":
bridge.emit("plugin.stopped", name=n)
except Exception as e:
Log.error("Core", f"插件 {n} 停止失败:{type(e).__name__}: {e}")
self.lifecycle_manager.stop_all()
def get_info(self, name: str) -> Optional[PluginInfo]:
if name in self.plugins:
return self.plugins[name]["info"]
return None
def has_capability(self, capability: str) -> bool:
return self.capability_registry.has_capability(capability)
def get_capability_provider(self, capability: str) -> Optional[Any]:
return self.capability_registry.get_provider(capability)
# ── HTTP 服务 ──
def start_http_server(self):
"""启动 HTTP 服务(子模块)"""
try:
from oss.core.http_api.server import HttpServer
from oss.core.http_api.router import HttpRouter
from oss.core.http_api.middleware import MiddlewareChain
router = HttpRouter()
middleware = MiddlewareChain()
self.http_server = HttpServer(router=router, middleware=middleware)
self.http_server.start()
Log.ok("Core", "HTTP 服务已启动")
except Exception as e:
Log.error("Core", f"HTTP 服务启动失败: {e}")
def stop_http_server(self):
"""停止 HTTP 服务"""
if self.http_server:
try:
self.http_server.stop()
Log.info("Core", "HTTP 服务已停止")
except Exception as e:
Log.error("Core", f"HTTP 服务停止失败: {e}")
def get_http_router(self):
"""获取 HTTP 路由器"""
if self.http_server:
return self.http_server.router
return None
# ── REPL ──
def start_repl(self):
"""启动 REPL 终端(子模块)"""
try:
from oss.core.repl.main import NebulaShell
self.repl_shell = NebulaShell(self)
Log.ok("Core", "REPL 终端已启动")
self.repl_shell.cmdloop()
except Exception as e:
Log.error("Core", f"REPL 启动失败: {e}")
# ── 防护管理 ──
def start_tamper_monitor(self):
"""启动防篡改监控"""
self.tamper_monitor.start()
def stop_tamper_monitor(self):
"""停止防篡改监控"""
self.tamper_monitor.stop()
def get_audit_logs(self, plugin_name: str = None, limit: int = 50) -> list[dict]:
"""获取审计日志"""
return self.audit_logger.get_logs(plugin_name, limit)
def get_tamper_alerts(self) -> list[dict]:
"""获取防篡改告警"""
return self.tamper_monitor.get_alerts()
def get_degraded_plugins(self) -> list[str]:
"""获取降级插件列表"""
return self.fallback_manager.get_degraded_plugins()
def recover_plugin(self, plugin_name: str) -> bool:
"""手动恢复降级插件"""
return self.fallback_manager.recover(plugin_name)
def get_status(self) -> dict:
"""获取 Core 状态摘要"""
nbpf_count = sum(1 for i in self.plugins.values() if i.get("nbpf_path"))
return {
"plugins": {
"total": len(self.plugins),
"nbpf": nbpf_count,
"directory": len(self.plugins) - nbpf_count,
"degraded": self.fallback_manager.get_degraded_plugins(),
},
"nbpf_loader": self._nbpf_initialized,
"http_server": self.http_server is not None,
"tamper_monitor": self.tamper_monitor._running,
"audit_logs": len(self.audit_logger._logs),
"tamper_alerts": len(self.tamper_monitor._alerts),
"data_store": str(self.data_store._base_dir),
}