Files
NebulaShell/store/@{FutureOSS}/plugin-loader/PL_EXAMPLE.md
Falck 395cda2e8b chore: add website directory to gitignore and update VSCode config
- Add `website/` to .gitignore to exclude website build artifacts
- Add Node.js debug configurations for FutureOSS website in launch.json
- Update VSCode color theme to "Default Dark Modern"
- Refactor plugin loader to simplify dependency and lifecycle plugin loading logic
2026-04-25 06:43:45 +08:00

5.8 KiB
Raw Blame History

PL 注入机制使用说明

概述

PL 注入机制允许插件通过 PL/ 文件夹向插件加载器注册自定义功能。插件加载器在启动时会自动扫描所有插件,检查其 manifest.json 中是否声明了 pl_injection 配置项。

使用步骤

1. 在 manifest.json 中声明 pl_injection

在插件的 manifest.jsonconfig.args 中添加 "pl_injection": true

{
  "metadata": {
    "name": "my-plugin",
    "version": "1.0.0",
    "author": "MyName",
    "description": "我的插件",
    "type": "utility"
  },
  "config": {
    "enabled": true,
    "args": {
      "pl_injection": true
    }
  },
  "dependencies": [],
  "permissions": []
}

2. 创建 PL/ 文件夹和 PL/main.py

在插件目录下创建 PL/ 文件夹,并在其中创建 main.py

store/@{MyName}/my-plugin/
├── manifest.json      # 声明 pl_injection: true
├── main.py            # 插件主逻辑
├── PL/                # PL 注入文件夹
│   └── main.py        # 注入逻辑(必须包含 register() 函数)
└── README.md

3. 实现 PL/main.py

PL/main.py 必须导出一个 register(injector) 函数,接收一个 PLInjector 实例:

# PL/main.py
"""PL 注入 - 向插件加载器注册功能"""

def register(injector):
    """向插件加载器注册功能
    
    Args:
        injector: PLInjector 实例,提供以下注册方法:
            - register_function(name, func, description="")
            - register_route(method, path, handler)
            - register_event_handler(event_name, handler)
    """
    
    # 示例 1: 注册一个普通功能
    def my_helper():
        print("这是从 PL 注入的功能")
    
    injector.register_function("my_helper", my_helper, "一个辅助功能")
    
    # 示例 2: 注册 HTTP 路由
    def hello_handler(request):
        return {"message": "Hello from PL injection!"}
    
    injector.register_route("GET", "/pl/hello", hello_handler)
    
    # 示例 3: 注册事件处理器
    def on_plugin_started(plugin_name):
        print(f"插件 {plugin_name} 已启动")
    
    injector.register_event_handler("plugin.started", on_plugin_started)

4. 引用其他文件

PL/main.py 可以引用 PL/ 文件夹下的其他 Python 文件:

store/@{MyName}/my-plugin/PL/
├── main.py            # 入口,包含 register() 函数
├── helpers.py         # 辅助函数(被 main.py 引用)
└── routes.py          # 路由定义(被 main.py 引用)
# PL/main.py
from .helpers import format_response
from .routes import register_routes

def register(injector):
    def my_handler():
        return format_response("Hello")
    injector.register_function("my_handler", my_handler)
    register_routes(injector)

行为说明

场景 结果
manifest.json 中 pl_injection: true + 存在 PL/main.py 正常加载,执行注入
manifest.json 中 pl_injection: true + 缺少 PL/ 文件夹 警告并拒绝加载该插件
manifest.json 中 pl_injection: true + 存在 PL/ 但缺少 main.py 警告并拒绝加载该插件
manifest.json 中未声明 pl_injection 正常加载,跳过 PL 检查
manifest.json 中 pl_injection: false 正常加载,跳过 PL 检查

安全限制

PL 注入机制实施了多层安全限制,防止恶意代码注入:

1. 文件类型限制

  • PL 文件夹中禁止包含 .sh.bat.exe.dll.so.dylib.bin 等可执行/二进制文件
  • 违反则拒绝加载该插件

2. 静态源码安全检查

PL/main.py 源码在编译前会进行静态扫描,禁止以下操作:

  • 导入系统级模块(ossyssubprocessshutilsocketctypescffimultiprocessingthreading
  • 使用 __import__execevalcompile
  • 直接操作文件(open
  • 访问 __builtins__

3. 沙箱执行环境

PL/main.py 在受限的沙箱中执行,仅提供安全的 builtins

  • 基础类型:dictliststrintfloatbooltupleset
  • 安全函数:lenrangeenumeratezipmapfiltersorted
  • 异常类型:ExceptionValueErrorTypeErrorKeyErrorIndexError

4. 参数校验

校验项 限制
功能名称 仅允许字母、数字、下划线、冒号、斜杠、连字符、点,最长 128 字符
路由路径 必须以 / 开头,禁止 ..///\.~%,最长 256 字符
HTTP 方法 仅允许 GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS
事件名称 字母开头,仅允许字母、数字、点、下划线,最长 128 字符
功能描述 最长 256 字符

5. 数量限制

限制项 上限
每个插件最多注册的功能数 50
每个功能名称最多被注册次数 10

6. 异常安全

  • 所有注册的函数会被自动包装,执行时抛出异常不会影响主流程
  • 异常会被记录到日志,函数返回 None

7. 调用者溯源

  • 通过栈帧回溯自动识别调用者插件名
  • 防止其他插件冒充注册

注入器 API

PLInjector 实例提供以下方法供 PL/main.py 调用:

方法 说明
register_function(name, func, description="") 注册一个注入功能
register_route(method, path, handler) 注册 HTTP 路由
register_event_handler(event_name, handler) 注册事件处理器
get_injected_functions(name=None) 获取已注册的注入功能
get_injection_info(plugin_name=None) 获取注入信息
has_injection(plugin_name) 检查插件是否有 PL 注入
get_registry_info() 获取注册表完整信息(用于监控)