万字解析 OpenClaw 源码架构-插件开发指南

4 阅读27分钟

插件测试与调试

OpenClaw 采用多包/多模块组织方式,插件生态位于 extensions 目录,核心运行时位于 src,测试配置集中在根目录的 vitest 配置文件与 test 目录中。测试体系通过 Vitest 提供的并发执行、覆盖率统计、环境隔离与模拟能力,支撑从单模块单元测试到跨模块集成与端到端验证的全链路质量保障。

graph TB
subgraph "测试配置"
VC["vitest.config.ts"]
VUC["vitest.unit.config.ts"]
VEC["vitest.e2e.config.ts"]
end
subgraph "测试基础设施"
SETUP["test/setup.ts"]
GSETUP["test/global-setup.ts"]
end
subgraph "插件SDK测试工具"
PRM["extensions/test-utils/plugin-runtime-mock.ts"]
REM["extensions/test-utils/runtime-env.ts"]
end
VC --> VUC
VC --> VEC
SETUP --> PRM
SETUP --> REM

核心组件

  • 测试框架与配置
    • Vitest:提供测试运行、并发池、覆盖率、超时控制与环境隔离能力。
    • 多套配置文件:基础配置、单元测试专用配置、端到端测试专用配置,分别针对不同测试场景优化执行策略与资源占用。
  • 测试基础设施
    • 全局设置:安装测试环境、进程警告过滤、插件注册表初始化与清理。
    • 通道插件桩:为不同渠道(如 Discord、Slack、Telegram、WhatsApp、Signal、iMessage)提供可复用的出站适配器与配置解析桩,便于在无真实渠道连接的情况下进行插件行为验证。
  • 插件SDK测试工具
    • 运行时模拟:对 PluginRuntime 的各子域(config、system、media、tts、stt、tools、channel、events、logging、state、subagent)进行函数级模拟,支持深度合并覆盖,便于局部定制。
    • 运行时环境模拟:对日志、错误输出与进程退出进行模拟,便于在测试中捕获与断言副作用。

架构总览

下图展示了测试执行路径与关键组件之间的交互关系,体现从配置加载、环境初始化、测试执行到覆盖率统计的整体流程。

sequenceDiagram
participant CLI as "命令行"
participant VBase as "Vitest 基础配置"
participant VUnit as "Vitest 单元配置"
participant VE2E as "Vitest 端到端配置"
participant Setup as "全局测试设置"
participant Mock as "插件运行时模拟"
participant Test as "具体测试用例"
CLI->>VBase : 加载基础配置
VBase->>VUnit : 继承并覆盖单元测试规则
VBase->>VE2E : 继承并覆盖端到端测试规则
CLI->>Setup : 执行全局设置与环境隔离
Setup->>Mock : 初始化插件运行时与通道桩
CLI->>Test : 并发执行测试用例
Test->>Mock : 使用模拟对象进行断言
Test-->>CLI : 输出结果与覆盖率

详细组件分析

组件A:测试配置与执行策略

  • 基础配置(vitest.config.ts)
    • 别名映射:为 openclaw/plugin-sdk 子路径建立别名,确保插件开发与测试时的导入一致性。
    • 超时与钩子:根据平台调整测试超时与钩子超时,提升稳定性。
    • 并发与隔离:默认使用进程池(forks),避免 VM Forks 下的环境泄漏;支持按 CI/本地自动调节最大并发数。
    • 包含/排除:明确包含与排除范围,确保覆盖率锚定在核心源码,避免扩展、应用与 UI 对阈值的影响。
    • 覆盖率:启用 v8 提供商,设置行/函数/分支/语句阈值,仅统计实际被测试覆盖的源码。
  • 单元测试配置(vitest.unit.config.ts)
    • 在基础配置基础上进一步缩小测试范围,排除网关、浏览器、Web、频道等大型集成面,聚焦纯逻辑与小模块。
  • 端到端测试配置(vitest.e2e.config.ts)
    • 强制进程池以避免上下文泄漏;根据 CPU 数量与环境变量动态确定并发度;支持静默或详细输出模式。
flowchart TD
Start(["启动测试"]) --> LoadBase["加载基础 Vitest 配置"]
LoadBase --> UnitCfg{"是否为单元测试?"}
UnitCfg --> |是| ApplyUnit["应用单元测试配置<br/>缩小范围/排除集成面"]
UnitCfg --> |否| E2ECfg{"是否为端到端测试?"}
E2ECfg --> |是| ApplyE2E["应用端到端配置<br/>进程池/并发/输出模式"]
E2ECfg --> |否| Continue["继续使用基础配置"]
ApplyUnit --> EnvInit["执行全局环境初始化"]
ApplyE2E --> EnvInit
Continue --> EnvInit
EnvInit --> RunTests["并发执行测试用例"]
RunTests --> Coverage["生成覆盖率报告"]
Coverage --> End(["完成"])

组件B:测试基础设施与环境隔离

  • 全局设置(test/setup.ts)
    • 安装进程警告过滤,减少噪音。
    • 设置 HOME 与状态隔离,避免跨测试污染。
    • 初始化插件注册表,注入通道桩(Discord、Slack、Telegram、WhatsApp、Signal、iMessage),统一出站发送接口。
    • 恢复虚拟时钟,防止跨文件/跨进程泄漏。
  • 全局清理(test/global-setup.ts)
    • 作为 Vitest globalSetup 出口,集中安装与清理测试环境。
sequenceDiagram
participant Vitest as "Vitest"
participant Global as "global-setup.ts"
participant Setup as "setup.ts"
participant Registry as "插件注册表"
participant Channels as "通道桩"
Vitest->>Global : 执行全局设置
Global->>Setup : 安装测试环境
Setup->>Registry : 创建默认插件注册表
Setup->>Channels : 注入通道桩
Vitest-->>Setup : 测试执行期间保持隔离
Vitest->>Global : 测试结束后清理

组件C:插件SDK测试工具

  • 插件运行时模拟(plugin-runtime-mock.ts)
    • 深度合并覆盖:支持对任意层级进行部分覆盖,便于只修改关键行为。
    • 函数级模拟:对 config、system、media、tts、stt、tools、channel、events、logging、state、subagent 等域进行 vi.fn() 模拟。
    • 通道子域:对 text、reply、routing、pairing、media、session、mentions、reactions、groups、debounce、commands 等进行细粒度模拟。
  • 运行时环境模拟(runtime-env.ts)
    • 模拟日志、错误输出与进程退出,便于在测试中捕获副作用并断言。
classDiagram
class PluginRuntimeMock {
+version
+config.loadConfig()
+system.enqueueSystemEvent()
+media.loadWebMedia()
+tts.textToSpeechTelephony()
+stt.transcribeAudioFile()
+tools.createMemoryGetTool()
+channel.text.chunkText()
+channel.reply.dispatchReplyFromConfig()
+channel.routing.resolveAgentRoute()
+channel.pairing.buildPairingReply()
+channel.session.resolveStorePath()
+channel.mentions.matchesMentionPatterns()
+channel.reactions.shouldAckReaction()
+channel.debounce.createInboundDebouncer()
+channel.commands.isControlCommandMessage()
+events.onAgentEvent()
+logging.getChildLogger()
+state.resolveStateDir()
+subagent.run()
}
class RuntimeEnvMock {
+log()
+error()
+exit(code)
}
PluginRuntimeMock --> RuntimeEnvMock : "依赖"

组件D:测试类型与策略

  • 单元测试(Unit Tests)
    • 目标:验证纯函数、工具函数、小型模块与边界条件。
    • 策略:使用基础配置或单元配置,优先排除大型集成面;利用模拟对象隔离外部依赖。
  • 集成测试(Integration Tests)
    • 目标:验证模块间协作、事件流与数据传递。
    • 策略:在基础配置下运行,结合通道桩与最小化外部依赖,关注路由、会话与回复分发等关键路径。
  • 端到端测试(E2E Tests)
    • 目标:验证完整用户场景与真实环境下的行为。
    • 策略:使用端到端配置,强制进程池与可控并发;通过脚本与容器化流程(如 Docker)搭建稳定环境。
flowchart TD
U["单元测试"] --> I["集成测试"]
I --> E["端到端测试"]
U --> |高内聚低耦合| U
I --> |模块间协作| I
E --> |真实环境| E

依赖关系分析

  • 配置继承
    • 单元配置与端到端配置均基于基础配置,通过 include/exclude 与 pool/maxWorkers 等参数差异化执行。
  • 测试执行依赖
    • 基础配置决定别名、超时、并发与覆盖率策略;全局设置负责环境隔离与注册表初始化;插件运行时模拟为测试提供稳定的外部依赖替代。
  • 脚本与CI
    • package.json 中的 test:* 脚本串联了静态检查、构建与各类测试执行,支持 CI 环境变量驱动的并发与模式切换。
graph TB
Base["vitest.config.ts"] --> Unit["vitest.unit.config.ts"]
Base --> E2E["vitest.e2e.config.ts"]
Base --> Setup["test/setup.ts"]
Setup --> Mock["plugin-runtime-mock.ts"]
Scripts["package.json 脚本"] --> Base
Scripts --> Unit
Scripts --> E2E

性能考虑

  • 并发与资源
    • 本地与 CI 环境下自动选择最大并发数,避免过度竞争磁盘/网络/进程资源。
    • 端到端测试强制进程池,降低上下文泄漏风险,但会增加内存占用。
  • 覆盖率锚定
    • 仅统计实际被测试覆盖的源码,避免扩展、应用与 UI 对阈值的影响,提高稳定性与可维护性。
  • 超时与稳定性
    • 根据平台调整测试与钩子超时,减少偶发失败;在 CI 中适当放宽超时以提升稳定性。

故障排查指南

  • 断点调试
    • 使用 Vitest 的调试模式运行测试,结合浏览器/Node 调试器设置断点,逐步定位问题。
  • 日志记录
    • 使用插件运行时模拟中的日志接口,输出关键路径与输入输出,便于回溯。
  • 环境隔离问题
    • 若出现跨文件/跨进程污染,检查是否使用了虚拟时钟未恢复、环境变量未还原或共享状态未清理。
  • 并发冲突
    • 将并发数调低或切换为串行执行,确认问题是否由竞态条件导致。
  • 覆盖率异常
    • 检查 include/exclude 规则与覆盖率阈值,确保目标源码被纳入统计。

结论

通过合理配置 Vitest、完善测试基础设施与模拟工具、严格区分测试类型与策略,OpenClaw 的插件测试体系能够在保证稳定性的同时,高效覆盖从单元到端到端的各个层面。建议在日常开发中遵循本文档的规范与最佳实践,配合 CI 自动化脚本,持续提升插件质量与交付效率。

附录

A. 测试用例编写规范

  • 命名约定
    • 使用 .test.ts 后缀,描述性命名,清晰表达被测功能与预期。
  • 断言风格
    • 优先使用明确的断言,避免模糊匹配;对异步操作使用 await 与超时控制。
  • 模拟与桩
    • 优先使用插件运行时模拟与通道桩,避免真实外部依赖;对必须的真实依赖,使用最小化、可预测的数据集。
  • 边界与异常
    • 显式覆盖空输入、异常路径与边界条件,确保健壮性。

B. 测试数据准备与模拟对象创建

  • 测试数据
    • 使用最小化、可重复的数据集;必要时通过 fixtures 或脚本生成临时数据。
  • 模拟对象
    • 使用插件运行时模拟与通道桩,按需深度合并覆盖关键行为。
  • 环境变量
    • 使用 vi.stubEnv 与 unstubEnvs 控制环境变量作用域,避免跨测试污染。

C. 测试环境隔离

  • HOME 与状态隔离
    • 使用 withIsolatedTestHome 确保每次测试的配置与状态独立。
  • 注册表隔离
    • 默认注册表在 beforeAll 注入,afterEach 恢复,避免跨文件污染。

D. 持续集成与自动化测试

  • 常用脚本
    • test:all、test:unit、test:e2e、test:live、test:coverage 等,覆盖静态检查、构建与各类测试。
  • Docker 化端到端
    • 提供多类 Docker 脚本用于端到端安装、网关网络、插件安装与清理,便于 CI 环境复现。
  • 并发与输出
    • 支持通过环境变量控制端到端并发度与输出详细程度,满足不同 CI 需求。

插件打包与发布

OpenClaw 采用多包工作区(pnpm workspace)组织,核心应用与插件位于不同目录,通过统一的构建与发布脚本进行协同。关键路径包括:

  • 根包与导出映射:定义主入口、子路径导出与 CLI 入口
  • 工作区配置:声明扩展包范围与仅构建依赖
  • 构建流水线:TypeScript 编译、插件 SDK 声明生成、产物复制与校验
  • 插件清单:每个插件必须提供 openclaw.plugin.json,用于严格配置校验
  • 发布检查:版本对齐、变更日志、安装烟雾测试与 npm 包内容核验
graph TB
subgraph "根包"
P["package.json<br/>导出映射/脚本/依赖"]
W["pnpm-workspace.yaml<br/>工作区/仅构建依赖"]
end
subgraph "构建脚本"
B1["scripts/tsdown-build.mjs<br/>编译入口"]
B2["scripts/write-plugin-sdk-entry-dts.ts<br/>生成SDK入口声明"]
B3["scripts/copy-plugin-sdk-root-alias.mjs<br/>复制运行时别名"]
B4["scripts/check-plugin-sdk-exports.mjs<br/>导出完整性校验"]
B5["scripts/sync-plugin-versions.ts<br/>插件版本同步"]
end
subgraph "插件清单"
M1["docs/plugins/manifest.md<br/>清单字段与校验规则"]
E1["extensions/voice-call/openclaw.plugin.json<br/>示例清单"]
E2["extensions/discord/openclaw.plugin.json<br/>最小清单"]
end
subgraph "发布与市场"
R["docs/reference/RELEASING.md<br/>发布检查清单"]
C["docs/plugins/community.md<br/>社区插件提交"]
end
P --> B1
W --> B1
B1 --> B2
B1 --> B3
B1 --> B4
B5 --> P
M1 --> E1
M1 --> E2
R --> P
C --> P

核心组件

  • 插件清单与 JSON Schema
    • 每个插件必须在根目录提供 openclaw.plugin.json,包含 id 与 configSchema;未提供或无效将阻断配置校验
    • 清单字段支持 kind、channels、providers、skills、uiHints、version 等,用于发现、注册与 UI 渲染
  • 插件 SDK 导出与子路径
    • 根包导出映射为 openclaw/plugin-sdk/* 子路径,包含 core、compat 及各渠道/扩展子模块
    • 构建阶段生成稳定入口声明与运行时别名,确保外部插件可按需导入
  • 版本同步与变更日志
    • 发布前执行插件版本同步脚本,将扩展包版本与核心版本对齐,并在 CHANGELOG 中记录
  • 安装与发布
    • npm 发布遵循“仅发布已存在于 npm 的 @openclaw/* 插件”,并以发布检查清单核验包内容与安装路径

架构总览

下图展示从源码到发布产物的关键流程:构建脚本驱动编译与产物生成,清单规范保障运行期发现与校验,发布检查清单确保包内容与安装体验一致。

sequenceDiagram
participant Dev as "开发者"
participant Build as "构建脚本"
participant Dist as "dist 输出"
participant Manifest as "openclaw.plugin.json"
participant NPM as "npm 注册表"
participant Market as "社区插件市场"
Dev->>Build : 触发构建(pnpm build)
Build->>Dist : 编译 TypeScript/生成声明/复制别名
Build->>Manifest : 生成/校验插件清单
Dev->>NPM : 执行发布(npm publish)
NPM-->>Market : 更新列表(仅已存在 npm 的 @openclaw/*)
Market-->>Dev : 用户可通过 openclaw plugins install 安装

详细组件分析

组件A:插件清单与配置校验

  • 必备字段
    • id:插件标识符
    • configSchema:JSON Schema,用于严格配置校验
  • 可选字段
    • kind、channels、providers、skills、name、description、uiHints、version
  • 行为约束
    • 未知 channels.* 与 plugins.entries.* 需可发现 id,否则视为错误
    • 破损或缺失清单导致 Doctor 报错
    • 已禁用插件保留配置并告警
  • 示例参考
    • 复杂清单示例:extensions/voice-call/openclaw.plugin.json
    • 最小清单示例:extensions/discord/openclaw.plugin.json
flowchart TD
Start(["读取插件清单"]) --> CheckID["校验 id 是否存在"]
CheckID --> CheckSchema["校验 configSchema 是否有效"]
CheckSchema --> ValidateRefs["校验 channels.* 与 plugins.entries.* 引用是否可发现"]
ValidateRefs --> Enabled{"插件是否启用?"}
Enabled --> |是| Proceed["允许加载并执行配置校验"]
Enabled --> |否| Warn["保留配置并发出警告"]
Proceed --> End(["完成"])
Warn --> End

组件B:插件 SDK 导出与子路径

  • 导出映射
    • 根包导出 openclaw/plugin-sdk 与各子路径(core、compat、各渠道/扩展)
  • 构建产物
    • 生成稳定入口声明与运行时别名,确保外部插件可按需导入
  • 导出完整性校验
    • 构建后运行导出检查脚本,确保关键命名导出与子路径文件齐全
sequenceDiagram
participant Build as "构建脚本"
participant Dist as "dist/plugin-sdk"
participant Checker as "导出检查脚本"
Build->>Dist : 生成 index.js 与各子路径 .js/.d.ts
Build->>Dist : 复制 root-alias.cjs
Checker->>Dist : 读取最终导出集合
Checker->>Checker : 校验必需导出与子路径文件
Checker-->>Build : 通过/失败

组件C:版本同步与变更日志

  • 同步逻辑
    • 读取根包版本,遍历 extensions 目录,将扩展包版本对齐至根版本
    • 自动在 CHANGELOG.md 中插入版本条目
    • 移除 workspace:* 开发依赖(如存在)
  • 使用场景
    • 发布前统一版本,减少用户侧版本不一致问题
flowchart TD
A["读取根包版本"] --> B["扫描 extensions 目录"]
B --> C{"是否存在 package.json"}
C --> |否| Skip["跳过该目录"]
C --> |是| D["读取扩展包版本"]
D --> E{"版本是否需要更新"}
E --> |否| Skip
E --> |是| F["写入新版本/更新 CHANGELOG/移除 workspace:*"]
F --> G["汇总结果"]
Skip --> G

组件D:构建与产物生成

  • 编译入口
    • 通过 tsdown 构建,支持日志级别控制
  • 声明与别名
    • 生成插件 SDK 入口声明与运行时别名,确保类型与运行时一致性
  • 脚本职责
    • tsdown-build.mjs:调用 tsdown 并继承标准输出
    • write-plugin-sdk-entry-dts.ts:生成各子路径 .d.ts
    • copy-plugin-sdk-root-alias.mjs:复制运行时别名文件
flowchart TD
Start(["pnpm build"]) --> TSC["tsdown-build.mjs 启动编译"]
TSC --> DTS["write-plugin-sdk-entry-dts.ts 生成入口声明"]
TSC --> Alias["copy-plugin-sdk-root-alias.mjs 复制别名"]
DTS --> Dist["产出 dist/plugin-sdk/*"]
Alias --> Dist
Dist --> End(["完成"])

组件E:发布与社区市场提交

  • npm 发布范围
    • 仅发布已存在于 npm 的 @openclaw/* 插件,非 npm 的内置插件保持磁盘树形态
  • 发布检查清单
    • 版本与元数据、构建产物、变更日志、安装烟雾测试、包内容核验、GitHub Release 与 Appcast
  • 社区插件市场
    • 第三方插件需满足质量门槛,PR 提交格式明确,仓库需具备使用文档与问题跟踪
flowchart TD
Prep["准备版本/元数据/变更日志"] --> Build["pnpm build 生成 dist"]
Build --> Pack["npm pack 校验包内容"]
Pack --> Test["安装烟雾测试/端到端测试"]
Test --> Publish["npm publish --access public"]
Publish --> Release["创建 GitHub Release/更新 appcast"]
Release --> Market["社区插件市场收录"]

依赖关系分析

  • 工作区与仅构建依赖
    • pnpm-workspace.yaml 声明工作区范围与仅构建依赖,避免不必要的二进制安装
  • 根包导出映射
    • package.json 的 exports 映射为插件 SDK 子路径提供稳定入口
  • 插件清单与运行期发现
    • 插件清单用于严格配置校验与运行期发现,缺失或错误将阻断加载
graph LR
WS["pnpm-workspace.yaml"] --> Ext["extensions/*"]
WS --> Root["根包 package.json"]
Root --> Exports["exports 映射"]
Ext --> Manifest["openclaw.plugin.json"]
Exports --> Plugins["插件加载/导出校验"]
Manifest --> Plugins

性能考量

  • 构建缓存与并发
    • 构建脚本通过 tsdown 控制日志级别,避免冗余输出影响 CI 性能
  • 产物体积与发布范围
    • 发布前通过 npm pack 校验包内容,排除 macOS 应用等非必要文件,降低包体与下载时间
  • 插件发现缓存
    • 运行期对插件发现与清单元数据使用短时缓存,减少启动/重载抖动

插件开发指南

OpenClaw 的插件系统采用模块化设计,主要由以下部分组成:

  • 插件根目录:每个插件必须包含一个 openclaw.plugin.json 文件,用于声明插件的基本信息、配置模式和功能特性。
  • 入口文件:插件的主入口文件(通常为 index.tsindex.js)负责注册插件的功能,如网关方法、HTTP 路由、代理工具、CLI 命令等。
  • 扩展目录:官方扩展位于 extensions/ 目录下,包含各种频道插件、工具插件和集成插件。
  • 插件 SDK:OpenClaw 提供了丰富的插件 SDK,支持不同频道和功能的插件开发。
graph TB
subgraph "OpenClaw 核心"
Core["核心平台<br/>网关控制平面"]
Plugins["插件系统<br/>动态加载与验证"]
end
subgraph "插件类型"
Adapter["消息适配器插件<br/>频道集成"]
Tool["工具插件<br/>代理工具与服务"]
Integration["集成插件<br/>第三方服务桥接"]
end
subgraph "扩展目录"
Extensions["extensions/<插件名>"]
VoiceCall["voice-call"]
Discord["discord"]
Telegram["telegram"]
MemoryCore["memory-core"]
end
Core --> Plugins
Plugins --> Adapter
Plugins --> Tool
Plugins --> Integration
Adapter --> Extensions
Extensions --> VoiceCall
Extensions --> Discord
Extensions --> Telegram
Extensions --> MemoryCore

核心组件

插件清单文件(openclaw.plugin.json)

每个插件都必须包含一个 openclaw.plugin.json 文件,该文件定义了插件的基本信息、配置模式和功能特性。清单文件是插件系统验证配置的关键依据。

必需字段

  • id:插件的唯一标识符
  • configSchema:插件配置的 JSON Schema

可选字段

  • kind:插件类型(如 memory、context-engine)
  • channels:插件注册的频道列表
  • providers:插件注册的提供商列表
  • skills:要加载的技能目录列表
  • name:插件显示名称
  • description:插件简短描述
  • uiHints:UI 渲染的配置字段提示
  • version:插件版本信息

配置模式要求

  • 每个插件必须提供 JSON Schema,即使不接受任何配置
  • Schema 在配置读写时进行验证,而非运行时验证

插件入口文件

插件入口文件负责注册插件的功能,包括:

  • 注册网关方法(Gateway RPC)
  • 注册 HTTP 路由
  • 注册代理工具
  • 注册 CLI 命令
  • 注册后台服务
  • 注册上下文引擎

插件可以导出函数或对象形式的插件定义,并通过 api.register* 方法注册各种功能。

架构概览

OpenClaw 的插件系统采用在进程内运行的设计,插件被视为可信代码。插件通过插件 SDK 与核心平台交互,提供扩展功能。

sequenceDiagram
participant User as "用户"
participant Gateway as "网关"
participant Plugin as "插件"
participant Core as "核心平台"
User->>Gateway : 发送消息/触发命令
Gateway->>Plugin : 调用插件方法
Plugin->>Core : 访问核心助手
Core-->>Plugin : 返回数据/状态
Plugin-->>Gateway : 处理结果
Gateway-->>User : 响应输出
Note over Plugin,Core : 插件在进程内运行,被视为可信代码

详细组件分析

消息适配器插件

消息适配器插件用于集成新的消息渠道,如 Discord、Telegram 等。这类插件需要实现频道插件接口,提供配置解析、账户解析、消息发送等功能。

Discord 插件示例

Discord 插件是一个典型的频道插件实现:

classDiagram
class DiscordPlugin {
+string id
+string name
+string description
+Object configSchema
+register(api) void
}
class ChannelPlugin {
+string id
+Object meta
+Object capabilities
+Object config
+Object outbound
+Object setup
+Object security
+Object status
+Object gateway
+Object mentions
+Object threading
+Object streaming
+Object actions
+Object commands
}
DiscordPlugin --> ChannelPlugin : "注册频道"

Telegram 插件示例

Telegram 插件展示了如何实现一个简单的频道插件:

flowchart TD
Start([插件初始化]) --> SetRuntime["设置运行时"]
SetRuntime --> RegisterChannel["注册频道插件"]
RegisterChannel --> Ready([插件就绪])

工具插件

工具插件为代理提供额外的能力,如内存搜索、文件处理等。这些插件通过注册代理工具来扩展 AI 助手的功能。

内存核心插件示例

内存核心插件展示了如何注册代理工具:

classDiagram
class MemoryCorePlugin {
+string id
+string name
+string description
+string kind
+Object configSchema
+register(api) void
}
class MemoryTools {
+createMemorySearchTool(ctx) Tool
+createMemoryGetTool(ctx) Tool
+registerMemoryCli(program) void
}
MemoryCorePlugin --> MemoryTools : "注册工具"

集成插件

集成插件用于连接第三方服务,如语音通话、认证提供商等。这些插件通常提供更复杂的功能,需要处理网络通信、音频处理等。

语音通话插件示例

语音通话插件是一个复杂的集成插件,展示了多种功能的组合:

graph LR
subgraph "语音通话插件"
Config["配置解析<br/>resolveVoiceCallConfig"]
Validate["配置验证<br/>validateProviderConfig"]
Runtime["运行时创建<br/>createVoiceCallRuntime"]
subgraph "网关方法"
Initiate["voicecall.initiate"]
Continue["voicecall.continue"]
Speak["voicecall.speak"]
End["voicecall.end"]
Status["voicecall.status"]
Start["voicecall.start"]
end
subgraph "代理工具"
VoiceCallTool["voice_call 工具"]
end
subgraph "CLI 命令"
VoiceCallCLI["voicecall 命令"]
end
subgraph "服务"
VoiceCallService["voicecall 服务"]
end
end
Config --> Validate
Validate --> Runtime
Runtime --> Initiate
Runtime --> Continue
Runtime --> Speak
Runtime --> End
Runtime --> Status
Runtime --> Start
Runtime --> VoiceCallTool
Runtime --> VoiceCallCLI
Runtime --> VoiceCallService

依赖分析

OpenClaw 的插件系统依赖于多种技术栈和工具:

核心依赖

  • Node.js >= 22:运行时环境要求
  • TypeScript:类型安全的开发语言
  • Jiti:动态导入模块的运行时加载器
  • JSON Schema:配置验证的核心机制

插件 SDK 导入路径

OpenClaw 提供了详细的插件 SDK 导入路径,支持不同频道和功能的插件开发:

graph TB
subgraph "插件 SDK 路径"
Core["openclaw/plugin-sdk/core<br/>通用插件 API"]
Compat["openclaw/plugin-sdk/compat<br/>兼容性助手"]
ChannelSDK["频道特定 SDK"]
Providers["提供商 SDK"]
end
subgraph "频道 SDK"
TelegramSDK["openclaw/plugin-sdk/telegram"]
DiscordSDK["openclaw/plugin-sdk/discord"]
SlackSDK["openclaw/plugin-sdk/slack"]
SignalSDK["openclaw/plugin-sdk/signal"]
ImessageSDK["openclaw/plugin-sdk/imessage"]
WhatsappSDK["openclaw/plugin-sdk/whatsapp"]
LineSDK["openclaw/plugin-sdk/line"]
MsteamsSDK["openclaw/plugin-sdk/msteams"]
end
subgraph "提供商 SDK"
AcpxSDK["openclaw/plugin-sdk/acpx"]
BluebubblesSDK["openclaw/plugin-sdk/bluebubbles"]
CopilotProxySDK["openclaw/plugin-sdk/copilot-proxy"]
DevicePairSDK["openclaw/plugin-sdk/device-pair"]
DiagnosticsOtelSDK["openclaw/plugin-sdk/diagnostics-otel"]
DiffsSDK["openclaw/plugin-sdk/diffs"]
FeishuSDK["openclaw/plugin-sdk/feishu"]
GeminiSDK["openclaw/plugin-sdk/google-gemini-cli-auth"]
GooglechatSDK["openclaw/plugin-sdk/googlechat"]
IrcSDK["openclaw/plugin-sdk/irc"]
LlmTaskSDK["openclaw/plugin-sdk/llm-task"]
LobsterSDK["openclaw/plugin-sdk/lobster"]
MatrixSDK["openclaw/plugin-sdk/matrix"]
MattermostSDK["openclaw/plugin-sdk/mattermost"]
MemoryCoreSDK["openclaw/plugin-sdk/memory-core"]
MemoryLancedbSDK["openclaw/plugin-sdk/memory-lancedb"]
MinimaxSDK["openclaw/plugin-sdk/minimax-portal-auth"]
NextcloudTalkSDK["openclaw/plugin-sdk/nextcloud-talk"]
NostrSDK["openclaw/plugin-sdk/nostr"]
OpenProseSDK["openclaw/plugin-sdk/open-prose"]
PhoneControlSDK["openclaw/plugin-sdk/phone-control"]
QwenSDK["openclaw/plugin-sdk/qwen-portal-auth"]
SynologyChatSDK["openclaw/plugin-sdk/synology-chat"]
TalkVoiceSDK["openclaw/plugin-sdk/talk-voice"]
ThreadOwnershipSDK["openclaw/plugin-sdk/thread-ownership"]
TlonSDK["openclaw/plugin-sdk/tlon"]
TwitchSDK["openclaw/plugin-sdk/twitch"]
VoiceCallSDK["openclaw/plugin-sdk/voice-call"]
ZaloSDK["openclaw/plugin-sdk/zalo"]
ZalouserSDK["openclaw/plugin-sdk/zalouser"]
end
ChannelSDK --> TelegramSDK
ChannelSDK --> DiscordSDK
ChannelSDK --> SlackSDK
ChannelSDK --> SignalSDK
ChannelSDK --> ImessageSDK
ChannelSDK --> WhatsappSDK
ChannelSDK --> LineSDK
ChannelSDK --> MsteamsSDK

性能考虑

缓存机制

OpenClaw 为插件发现和清单元数据使用短期进程内缓存,以减少启动/重载时的突发工作量。可以通过环境变量禁用缓存或调整缓存窗口。

运行时助手

插件可以访问核心助手提供的运行时助手,如 TTS 和 STT 功能,但需要注意:

  • 使用核心 messages.tts 配置
  • 返回 PCM 音频缓冲区和采样率
  • 边缘 TTS 不支持电话

安全性

  • 插件在进程内运行,被视为可信代码
  • 需要适当的权限管理和安全检查
  • 对外部依赖的构建脚本需要特别注意

插件类型系统

OpenClaw 将“插件 SDK”与“插件运行时”分层组织:

  • 插件 SDK:对外暴露统一的类型与工具,如 ChannelPlugin、OpenClawPlugin、ProviderAuthContext 等
  • 插件运行时:提供子代理(subagent)与通道(channel)运行时能力
  • 具体渠道插件:在 SDK 类型之上实现具体适配器(adapters),例如 Discord、Telegram、Slack
graph TB
subgraph "插件SDK"
SDK_Index["src/plugin-sdk/index.ts<br/>统一导出"]
SDK_ChannelCommon["src/plugin-sdk/channel-plugin-common.ts<br/>通用类型与工具"]
SDK_PluginsTypes["src/plugins/types.ts<br/>OpenClawPlugin/ProviderAuthContext等"]
SDK_RuntimeTypes["src/plugins/runtime/types.ts<br/>子代理运行时类型"]
SDK_ChannelTypes["src/channels/plugins/types.plugin.ts<br/>ChannelPlugin泛型定义"]
end
subgraph "具体渠道插件"
Discord["extensions/discord/src/channel.ts"]
Telegram["extensions/telegram/src/channel.ts"]
Slack["extensions/slack/src/channel.ts"]
end
SDK_Index --> SDK_ChannelCommon
SDK_Index --> SDK_PluginsTypes
SDK_Index --> SDK_RuntimeTypes
SDK_Index --> SDK_ChannelTypes
SDK_ChannelTypes --> Discord
SDK_ChannelTypes --> Telegram
SDK_ChannelTypes --> Slack

核心组件

本节从类型系统角度拆解核心类型,并给出它们之间的关系与职责。

  • OpenClawPlugin(插件定义)

    • 职责:描述一个插件的基本信息、配置模式、生命周期钩子注册点、命令与服务注册点等
    • 关键字段:id、name、description、version、kind、configSchema、register、activate
    • 关联类型:OpenClawPluginApi、OpenClawPluginModule、OpenClawPluginDefinition、OpenClawPluginService、OpenClawPluginCommandDefinition 等
  • OpenClawPluginApi(插件API)

    • 职责:为插件提供注册工具(工具、钩子、HTTP路由、通道、网关方法、CLI、服务、提供商、命令、上下文引擎)
    • 关键方法:registerTool、registerHook、registerHttpRoute、registerChannel、registerGatewayMethod、registerCli、registerService、registerProvider、registerCommand、registerContextEngine、resolvePath、on
  • ChannelPlugin(渠道插件)

    • 职责:定义某一渠道(Discord、Telegram、Slack 等)的适配器集合与能力声明
    • 泛型参数:ChannelPlugin<ResolvedAccount, Probe, Audit>
      • ResolvedAccount:账户解析后的类型(如 Discord/Telegram/Slack 的账户类型)
      • Probe:探测结果类型(如 Telegram 的 Probe)
      • Audit:审计结果类型(由各渠道自行定义)
    • 关键字段:id、meta、capabilities、config、configSchema、setup、pairing、security、groups、mentions、outbound、status、gateway、auth、elevated、commands、streaming、threading、messaging、agentPrompt、directory、resolver、actions、heartbeat、agentTools
  • ProviderAuthContext(提供商认证上下文)

    • 职责:为 ProviderAuthMethod.run 提供统一的认证环境(配置、工作区、运行时、提示器、打开URL、OAuth处理器)
    • 关键属性:config、workspaceDir、agentDir、prompter、runtime、isRemote、openUrl、oauth.createVpsAwareHandlers
  • PluginRuntime(插件运行时)

    • 职责:提供子代理运行时能力(run/waitForRun/getSessionMessages/getSession/deleteSession)与通道运行时
    • 子代理运行时:SubagentRunParams/SubagentRunResult/SubagentWaitParams/SubagentWaitResult/SubagentGetSessionMessagesParams/SubagentGetSessionMessagesResult/SubagentDeleteSessionParams
  • ProviderAuthMethod/ProviderAuthResult(提供商认证)

    • ProviderAuthMethod:认证方法定义(id、label、hint、kind、run)
    • ProviderAuthResult:认证结果(profiles、configPatch、defaultModel、notes)
  • OpenClawPluginService(插件服务)

    • 职责:声明式服务(id、start、stop),用于长期运行的任务或后台服务
  • OpenClawPluginCommandDefinition(插件命令)

    • 职责:注册不经过 LLM 的自定义命令(name、nativeNames、description、acceptsArgs、requireAuth、handler)

架构总览

下图展示插件类型系统在运行时的交互关系:插件通过 OpenClawPluginApi 注册能力;ChannelPlugin 作为渠道适配器集合被注册到运行时;ProviderAuthContext 为提供商认证提供统一环境;PluginRuntime 提供子代理与通道运行时。

classDiagram
class OpenClawPluginApi {
+registerTool(tool, opts)
+registerHook(events, handler, opts)
+registerHttpRoute(params)
+registerChannel(registration)
+registerGatewayMethod(method, handler)
+registerCli(registrar, opts)
+registerService(service)
+registerProvider(provider)
+registerCommand(command)
+registerContextEngine(id, factory)
+resolvePath(input)
+on(hookName, handler, opts)
}
class OpenClawPluginDefinition {
+id
+name
+description
+version
+kind
+configSchema
+register(api)
+activate(api)
}
class ChannelPlugin~ResolvedAccount, Probe, Audit~ {
+id
+meta
+capabilities
+config
+configSchema
+setup
+pairing
+security
+groups
+mentions
+outbound
+status
+gateway
+auth
+elevated
+commands
+streaming
+threading
+messaging
+agentPrompt
+directory
+resolver
+actions
+heartbeat
+agentTools
}
class ProviderAuthContext {
+config
+workspaceDir
+agentDir
+prompter
+runtime
+isRemote
+openUrl(url)
+oauth.createVpsAwareHandlers
}
class PluginRuntime {
+subagent.run(params)
+subagent.waitForRun(params)
+subagent.getSessionMessages(params)
+subagent.getSession(params)
+subagent.deleteSession(params)
+channel
}
OpenClawPluginDefinition --> OpenClawPluginApi : "register/activate"
ChannelPlugin --> OpenClawPluginApi : "registerChannel"
ProviderAuthContext --> OpenClawPluginApi : "ProviderAuthMethod.run"
PluginRuntime --> OpenClawPluginApi : "resolvePath"

详细组件分析

ChannelPlugin 类型体系与泛型参数

  • ChannelPlugin<ResolvedAccount, Probe, Audit> 是渠道适配器的统一契约,不同渠道通过泛型参数表达自身特化:
    • ResolvedAccount:账户解析后的强类型,如 Discord 的 ResolvedDiscordAccount、Telegram 的 ResolvedTelegramAccount、Slack 的 ResolvedSlackAccount
    • Probe:探测阶段返回的结构化信息,如 Telegram 的 TelegramProbe
    • Audit:审计阶段返回的结构化信息,由各渠道自行定义
  • 在具体实现中:
    • Discord 插件:ChannelPlugin
    • Telegram 插件:ChannelPlugin<ResolvedTelegramAccount, TelegramProbe>
    • Slack 插件:ChannelPlugin
classDiagram
class ChannelPlugin~ResolvedAccount, Probe, Audit~ {
+id
+meta
+capabilities
+config
+configSchema
+setup
+pairing
+security
+groups
+mentions
+outbound
+status
+gateway
+auth
+elevated
+commands
+streaming
+threading
+messaging
+agentPrompt
+directory
+resolver
+actions
+heartbeat
+agentTools
}
class ResolvedDiscordAccount
class ResolvedTelegramAccount
class TelegramProbe
class ResolvedSlackAccount
ChannelPlugin <|.. DiscordPlugin : "Discord"
ChannelPlugin <|.. TelegramPlugin : "Telegram"
ChannelPlugin <|.. SlackPlugin : "Slack"
DiscordPlugin --> ResolvedDiscordAccount : "ResolvedAccount"
TelegramPlugin --> ResolvedTelegramAccount : "ResolvedAccount"
TelegramPlugin --> TelegramProbe : "Probe"
SlackPlugin --> ResolvedSlackAccount : "ResolvedAccount"

ProviderAuthContext 与 ProviderAuthMethod

  • ProviderAuthContext 为认证流程提供统一上下文,包括配置、工作区、运行时、提示器、打开 URL、OAuth 处理器等
  • ProviderAuthMethod 定义了认证方法的标识、标签、提示、认证方式(oauth、api_key、token、device_code、custom)以及执行函数
  • ProviderAuthResult 返回认证成功后的凭据、配置补丁、默认模型与备注
sequenceDiagram
participant Plugin as "插件"
participant AuthCtx as "ProviderAuthContext"
participant OAuth as "OAuth处理器"
participant Result as "ProviderAuthResult"
Plugin->>AuthCtx : "调用 ProviderAuthMethod.run(ctx)"
AuthCtx->>OAuth : "createVpsAwareHandlers()"
OAuth-->>AuthCtx : "返回处理函数"
AuthCtx-->>Plugin : "执行认证流程"
Plugin-->>Result : "返回 profiles/configPatch/defaultModel/notes"

OpenClawPluginApi 注册流程

  • 插件在 register/activate 阶段通过 OpenClawPluginApi 注册各类能力:
    • 工具:registerTool
    • 钩子:registerHook
    • HTTP 路由:registerHttpRoute
    • 渠道:registerChannel
    • 网关方法:registerGatewayMethod
    • CLI:registerCli
    • 服务:registerService
    • 提供商:registerProvider
    • 命令:registerCommand
    • 上下文引擎:registerContextEngine
    • 路径解析:resolvePath
    • 生命周期钩子:on
sequenceDiagram
participant Loader as "插件加载器"
participant PluginDef as "OpenClawPluginDefinition"
participant Api as "OpenClawPluginApi"
participant Runtime as "PluginRuntime"
Loader->>PluginDef : "调用 register(api)"
PluginDef->>Api : "registerTool/registerHook/..."
Api->>Runtime : "resolvePath/子代理运行时"
PluginDef-->>Loader : "完成注册"

插件命令与服务

  • 插件命令(OpenClawPluginCommandDefinition)绕过 LLM,适合状态切换或无需 AI 推理的简单命令
  • 插件服务(OpenClawPluginService)提供可选的长期运行任务或后台服务
flowchart TD
Start(["插件命令入口"]) --> Parse["解析命令参数"]
Parse --> Validate{"是否授权发送者?"}
Validate --> |否| Deny["拒绝并返回帮助信息"]
Validate --> |是| Exec["执行命令处理器"]
Exec --> Reply["生成回复负载并返回"]
Deny --> End(["结束"])
Reply --> End

依赖关系分析

  • 插件 SDK 通过统一导出文件集中暴露类型与工具,便于扩展生态复用
  • 具体渠道插件基于 ChannelPlugin 泛型契约实现适配器,形成“接口统一 + 实现多样”的模式
  • ProviderAuthContext 与 ProviderAuthMethod 解耦认证流程与渠道实现
  • OpenClawPluginApi 作为“胶水层”,将插件能力注入到运行时
graph LR
SDK_Index["src/plugin-sdk/index.ts"] --> SDK_ChannelTypes["src/channels/plugins/types.plugin.ts"]
SDK_Index --> SDK_PluginsTypes["src/plugins/types.ts"]
SDK_Index --> SDK_RuntimeTypes["src/plugins/runtime/types.ts"]
SDK_ChannelTypes --> Discord["extensions/discord/src/channel.ts"]
SDK_ChannelTypes --> Telegram["extensions/telegram/src/channel.ts"]
SDK_ChannelTypes --> Slack["extensions/slack/src/channel.ts"]

性能考虑

  • 使用泛型参数明确 ResolvedAccount/Probe/Audit,有助于编译期检查与运行时选择性处理
  • 将认证流程抽象为 ProviderAuthMethod,避免重复实现,提升可维护性
  • 子代理运行时(PluginRuntime.subagent)支持异步等待与会话消息查询,建议结合超时与幂等键控制资源占用
  • 渠道适配器按需启用(capabilities)与按前缀热重载(reload.configPrefixes),减少不必要的初始化成本

插件配置与清单

  • 清单文件位于每个扩展目录内,文件名固定为 openclaw.plugin.json。
  • 运行时通过 manifest 解析器加载清单,结合配置校验器对插件配置进行 schema 校验。
  • 网关层支持配置热重载与重启策略,按路径前缀判定是否可热重载或需重启。
graph TB
subgraph "扩展目录"
A["extensions/*/openclaw.plugin.json"]
end
subgraph "清单解析"
B["manifest.ts<br/>loadPluginManifest()"]
C["manifest-registry.ts<br/>缓存与校验"]
end
subgraph "配置校验"
D["validation.ts<br/>validateInSuite()"]
end
subgraph "插件加载"
E["loader.ts<br/>createApi()/register()"]
end
subgraph "网关热重载"
F["config-reload.ts<br/>startGatewayConfigReloader()"]
G["config-reload-plan.ts<br/>buildGatewayReloadPlan()"]
H["server-reload-handlers.ts<br/>setState()/restart()"]
end
A --> B --> C --> D --> E
D --> F --> G --> H

核心组件

  • 清单解析器:负责定位并解析 openclaw.plugin.json,提取 id、configSchema、kind、channels/providers/skills 等元数据。
  • 配置校验器:基于插件 manifest 中的 configSchema 对用户配置进行 JSON Schema 校验,并生成问题与警告。
  • 插件加载器:在配置有效后创建插件 API 并调用插件导出的 register/activate 生命周期钩子。
  • 热重载引擎:监听配置变更,根据路径前缀规则决定热重载、无操作或重启。

架构总览

下图展示从清单到运行时配置校验、插件注册、再到网关热重载的关键流程。

sequenceDiagram
participant Ext as "扩展清单<br/>openclaw.plugin.json"
participant Man as "清单解析<br/>loadPluginManifest()"
participant Reg as "清单注册表<br/>manifest-registry.ts"
participant Val as "配置校验<br/>validateInSuite()"
participant Load as "插件加载<br/>loader.ts"
participant GW as "网关热重载<br/>config-reload.ts"
Ext->>Man : 读取并解析清单
Man-->>Reg : 返回清单对象
Reg-->>Val : 提供各插件 configSchema
Val-->>Load : 验证通过后传递配置
Load-->>Load : createApi() + register()/activate()
Val-->>GW : 变更检测触发
GW-->>GW : buildGatewayReloadPlan()
GW-->>GW : 热重载/无操作/重启

详细组件分析

清单文件结构与字段语义

  • 必填字段
    • id:插件唯一标识,字符串,去空白后必填。
    • configSchema:JSON Schema 对象,用于校验插件配置。
  • 可选字段
    • kind:插件类型,如 memory、context-engine。
    • channels/providers/skills:字符串数组,分别声明该插件提供的通道/模型提供商/技能目录。
    • name/description/version:元信息。
    • uiHints:键为“点式路径”,值为 UI 提示对象,含 label/help/tags/advanced/sensitive/placeholder 等。
  • 典型示例
    • 基础通道类插件(如 Discord/Telegram):仅包含 id 与空配置 schema。
    • 记忆类插件(如 memory-lancedb):包含敏感字段(如 API Key)与高级选项。
    • 复杂工具类插件(如 voice-call):包含大量嵌套配置与枚举约束。

配置校验规则与错误处理

  • 启用状态与配置存在性
    • 若插件被启用或存在配置,则必须进行校验;否则跳过。
  • 缺失 schema 的处理
    • 若插件未提供 configSchema,将产生问题项。
  • 错误与警告
    • 校验失败时,收集每条错误并映射到具体路径;若插件已禁用但仍有配置,发出警告。
  • 内存插件槽位决策
    • 当存在多个内存插件时,依据槽位选择与决策逻辑确定唯一可用者。

生命周期钩子与 API

  • 插件可通过 register/activate 导出函数接收 OpenClawPluginApi,进行工具注册、HTTP 路由、命令、服务、提供方等能力注册。
  • 支持的钩子名称集合覆盖代理运行、消息收发、工具调用、会话生命周期、网关启停等多个阶段。
  • 钩子事件与结果类型定义清晰,便于插件在关键节点注入上下文或修改行为。

权限配置与安全

  • 清单中 uiHints 的 sensitive 字段用于标记敏感配置项(如密钥),便于 UI 层隐藏输入。
  • 网关层对插件路由访问控制可按路径上下文强制执行认证与速率限制。
  • 插件配置 schema 中可使用枚举、正则、最小最大值等约束,降低误配风险。

热重载机制与动态更新

  • 热重载策略
    • 支持 off/restart/hot/hybrid 四种模式,由 gateway.reload.mode 控制。
    • debounceMs 控制变更合并与延迟应用。
  • 变更评估
    • 通过 diffConfigPaths 比较前后配置差异,构建重载计划。
    • 基于路径前缀规则判断是否可热重载、无操作或需要重启。
  • 插件贡献
    • 插件可声明 reload.configPrefixes(热重载)与 noopPrefixes(无操作)前缀,影响重载决策。
  • 执行与回退
    • 在 hot 模式下若需重启则忽略热重载并记录告警。
    • 重启请求需存在 SIGUSR1 监听器,否则跳过。
flowchart TD
Start(["开始"]) --> Read["读取新配置快照"]
Read --> Diff["计算变更路径"]
Diff --> Mode{"模式: off/restart/hot/hybrid"}
Mode --> |off| End(["结束"])
Mode --> |restart| QueueRestart["排队重启"]
Mode --> Plan{"是否需要重启?"}
Plan --> |是且hot| Warn["记录告警并忽略热重载"] --> End
Plan --> |否| Hot["执行热重载回调"]
QueueRestart --> End
Hot --> End

依赖关系分析

  • 清单解析依赖边界文件读取与 JSON 解析,确保路径安全与内容合法性。
  • 配置校验依赖插件 manifest 中的 configSchema,形成“清单驱动”的强约束。
  • 插件加载依赖 OpenClawPluginApi,统一注册入口与生命周期钩子。
  • 热重载依赖路径前缀规则与插件贡献的 reload 前缀列表,形成“可热重载即安全”的设计。
graph LR
M["manifest.ts"] --> V["validation.ts"]
V --> L["loader.ts"]
V --> R["config-reload.ts"]
R --> P["config-reload-plan.ts"]
P --> S["server-reload-handlers.ts"]

性能考量

  • 清单缓存:manifest 注册表支持短缓存窗口,减少启动期突发重载带来的重复解析成本。
  • 配置校验缓存:schema 校验使用缓存键(含 mtime),避免重复校验。
  • 热重载去抖:变更监听采用 awaitWriteFinish 与 debounceMs,合并频繁写入。
  • 路由鉴权:插件路由可按路径上下文选择性强制鉴权,避免不必要的鉴权开销。

故障排查指南

  • 清单缺失或不合法
    • 现象:提示“清单未找到/路径不安全/非对象/缺少 id/缺少 configSchema”。
    • 排查:确认清单路径、权限与 JSON 合法性。
  • 配置无效
    • 现象:出现“invalid config: ...”或“plugin schema missing for ...”。
    • 排查:对照 configSchema 修正字段类型、枚举值、范围约束;检查 uiHints 是否正确标注敏感字段。
  • 插件未加载
    • 现象:日志显示“缺少 register/activate 导出”或注册异常。
    • 排查:确保插件模块导出 register 或 activate 函数,并返回同步或 Promise。
  • 热重载未生效
    • 现象:变更未触发热重载或被忽略。
    • 排查:检查 gateway.reload.mode 与路径前缀规则;确认插件是否声明了 reload.configPrefixes;确认 SIGUSR1 监听器是否存在。

结论

openclaw 的插件体系以“清单驱动 + 强约束校验 + 生命周期钩子 + 精细化热重载”为核心,既保证了配置的安全与一致性,又提供了灵活的扩展能力与可观测的运行时行为。遵循本文档的结构、规则与最佳实践,可显著降低插件开发与运维成本。

附录:配置示例与最佳实践

示例一:基础通道插件(Discord)

  • 特点:仅声明 id 与空配置 schema,适合零配置通道。
  • 建议:保持 schema 最小化,必要时通过 uiHints 提供简要帮助。

示例二:记忆插件(memory-lancedb)

  • 特点:包含敏感字段(如 API Key)、嵌套对象与数值范围约束,提供丰富的 uiHints。
  • 建议:对敏感字段标注 sensitive;为高级选项添加 advanced 标记;设置合理默认值与占位符。

示例三:复杂工具插件(voice-call)

  • 特点:多层级嵌套配置、大量枚举与正则约束、丰富的 uiHints。
  • 建议:分组组织 uiHints;为关键字段提供 pattern/minimum/maximum 约束;对电话号码等字段使用正则校验。

示例四:带技能的插件(ACPX)

  • 特点:声明 skills 目录,配合 configSchema 定义运行参数与 MCP 服务器配置。
  • 建议:将技能目录与清单同目录管理;为外部进程提供明确的命令与环境变量配置。

示例五:通用工具插件(Lobster)

  • 特点:简单配置 schema,适合轻量工具。
  • 建议:保持 schema 简洁,必要时逐步扩展。

最佳实践清单

  • 清单规范
    • id 唯一且稳定;configSchema 必须提供;uiHints 对敏感字段标注 sensitive。
  • 配置约束
    • 使用 enum/minimum/maximum/pattern 等 JSON Schema 能力;为复杂字段提供 nested schema。
  • 生命周期
    • 在 register/activate 中完成一次性初始化;利用钩子在关键节点注入逻辑。
  • 热重载
    • 明确声明 reload.configPrefixes 与 noopPrefixes;优先使用热重载,避免不必要的重启。
  • 安全
    • 将密钥类配置标记为 sensitive;在网关层对插件路由进行必要的鉴权与速率限制。

开发环境搭建

OpenClaw 采用 Monorepo 结构,根目录包含核心应用与多个工作区。插件开发主要涉及以下区域:

  • 根工作区:核心应用与构建脚本
  • 扩展工作区:官方插件示例(如 voice-call)
  • 插件 SDK:统一的类型与工具集,供所有插件使用
graph TB
Root["根工作区<br/>package.json, tsconfig.json"] --> Core["核心应用<br/>src/**"]
Root --> Extensions["扩展工作区<br/>extensions/*"]
Extensions --> VoiceCall["voice-call 插件<br/>index.ts, openclaw.plugin.json"]
Root --> SDK["插件 SDK<br/>src/plugin-sdk/index.ts"]
SDK --> Exports["导出路径<br/>openclaw/plugin-sdk/*"]

核心组件

  • Node.js 运行时:要求版本 ≥ 22.12.0
  • 包管理器:pnpm(版本 10.23.0),启用 pnpm 工作区
  • TypeScript 编译:严格模式,目标 ES2023,模块解析 NodeNext
  • 插件 SDK:通过根工作区导出,支持子路径导入(如 core、discord、telegram 等)

架构概览

OpenClaw 插件体系由三部分组成:

  • 插件 SDK:稳定、可发布、编译期可用的类型与工具集
  • 插件运行时:注入到插件中的运行表面,仅通过 api.runtime 访问核心行为
  • 插件实现:遵循 Manifest 规范的 TypeScript 模块,注册 RPC 方法、HTTP 路由、工具、CLI 命令等
graph TB
subgraph "插件实现"
Pkg["package.json<br/>openclaw.extensions"]
Manifest["openclaw.plugin.json<br/>configSchema, uiHints"]
Entry["index.ts<br/>register(api)"]
end
subgraph "插件 SDK"
SDKIndex["src/plugin-sdk/index.ts"]
SDKExports["package.json 导出<br/>openclaw/plugin-sdk/*"]
end
subgraph "运行时"
Runtime["api.runtime<br/>受控访问核心行为"]
Gateway["网关 RPC/HTTP 注册"]
end
Pkg --> Entry
Manifest --> Entry
Entry --> SDKExports
SDKExports --> SDKIndex
Entry --> Runtime
Runtime --> Gateway

详细组件分析

Node.js 与包管理器配置

  • Node.js 版本:≥ 22.12.0
  • 包管理器:pnpm 10.23.0,启用工作区(pnpm-workspace.yaml)
  • 依赖安装:推荐使用 pnpm 安装,确保 onlyBuiltDependencies 与 overrides 正常生效

TypeScript 配置与编译设置

  • 编译选项:严格模式、ES2023 目标、NodeNext 模块与解析
  • 路径映射:为 openclaw/plugin-sdk 提供本地路径别名,便于开发时引用
  • 插件 SDK 类型声明:独立的 tsconfig.plugin-sdk.dts.json,用于生成 dist/plugin-sdk 下的 d.ts 文件

插件 SDK 导入与使用

  • 入口导出:package.json 中定义了大量 openclaw/plugin-sdk/* 子路径导出
  • 使用建议:
    • 新插件优先使用具体子路径(如 core、discord、telegram)以减少打包体积
    • 保持与现有外部插件兼容,仍可使用主路径 openclaw/plugin-sdk
  • SDK 内容:类型、适配器、配置工具、分组策略、SSRF 策略、Webhook 守卫等

插件项目目录结构与文件组织

  • 必备文件
    • openclaw.plugin.json:插件清单,包含 id、configSchema、uiHints 等
    • package.json:声明 openclaw.extensions 数组,指向插件入口
    • index.ts:插件注册逻辑,调用 api.register* 系列方法
  • 推荐实践
    • 将配置校验与 UI 提示分离到 openclaw.plugin.json
    • 将 CLI、RPC、HTTP 路由、工具等注册集中在 index.ts
    • 使用子路径导入 SDK,避免引入不必要的类型或运行时代码

插件开发工具链配置

  • IDE 设置
    • 启用 TypeScript 严格模式与路径映射
    • 使用 pnpm 工作区,确保跨包引用正确解析
  • 调试配置
    • 使用 Node.js 调试器附加到 openclaw 进程
    • 在插件入口处设置断点,观察 api.runtime 行为
  • 代码格式化与 Lint
    • 使用 oxfmt(格式化)与 oxlint(Lint)
    • 通过脚本统一执行格式化与检查(如 format、lint、check)

插件生命周期与运行时交互

sequenceDiagram
participant Dev as "开发者"
participant Plugin as "插件(index.ts)"
participant SDK as "插件 SDK"
participant Runtime as "api.runtime"
participant Gateway as "网关"
Dev->>Plugin : 加载插件
Plugin->>SDK : 导入所需子路径
Plugin->>Runtime : 初始化运行时依赖
Plugin->>Gateway : 注册 RPC/HTTP/工具/CLI
Gateway-->>Plugin : 调用已注册接口
Plugin->>Runtime : 访问受控核心能力
Runtime-->>Plugin : 返回结果/状态

依赖分析

  • 根工作区依赖
    • Node.js 版本约束与包管理器版本
    • TypeScript 与 Vitest 等开发依赖
  • 插件 SDK 依赖
    • 通过 package.json 的 exports 字段对外暴露
    • 支持按需子路径导入,降低打包体积
  • 扩展工作区依赖
    • voice-call 示例展示了如何声明 openclaw.extensions 并在 package.json 中引用
graph LR
RootDeps["根依赖<br/>Node.js, pnpm, TypeScript"] --> SDKDeps["SDK 导出<br/>openclaw/plugin-sdk/*"]
SDKDeps --> ExtDeps["扩展依赖<br/>openclaw.extensions"]
ExtDeps --> VoiceCall["voice-call 示例"]

性能考虑

  • 使用 SDK 子路径导入,避免加载未使用的类型与运行时代码
  • 通过 tsconfig.plugin-sdk.dts.json 生成独立的类型声明,减少编译时间
  • 利用缓存机制(插件发现与清单缓存)提升启动与重载性能