OpenClaw Plugin 生命周期全流程 Pipeline 手册
从 openclaw plugins install <spec>到 Plugin 的 Tool 出现在 Agent 可用列表中——每一步的代码函数、数据流向与决策逻辑

Pipeline 全景图
plaintext
┌─────────────────────────────────────────────────────────────────────────┐│ Phase 1: INSTALL ││ ││ CLI Command → Spec Routing → Security Preflight → Download/Extract ││ → Source Scan → Dependency Scan → Compat Validation → Persist Record │└───────────────────────────────────┬─────────────────────────────────────┘ │ Gateway Restart / Hot Reload┌───────────────────────────────────▼─────────────────────────────────────┐│ Phase 2: DISCOVERY ││ ││ Scan Roots → Collect Candidates → Validate Paths → Build Manifest ││ Registry → Provenance Index │└───────────────────────────────────┬─────────────────────────────────────┘ │┌───────────────────────────────────▼─────────────────────────────────────┐│ Phase 3: ACTIVATION PLANNING ││ ││ Resolve Config → Normalize Enable State → Activation Planner ││ → Compute Plugin ID Set → Create Activation Context │└───────────────────────────────────┬─────────────────────────────────────┘ │┌───────────────────────────────────▼─────────────────────────────────────┐│ Phase 4: MODULE LOADING ││ ││ Resolve Entry → SDK Alias → Import Module → Unwrap Default Export ││ → Call register() → Build Plugin API │└───────────────────────────────────┬─────────────────────────────────────┘ │┌───────────────────────────────────▼─────────────────────────────────────┐│ Phase 5: RUNTIME REGISTRATION ││ ││ Register Tools → Register Hooks → Register Providers → Register ││ Channels → Register Gateway Methods → Registry Active │└───────────────────────────────────┬─────────────────────────────────────┘ │┌───────────────────────────────────▼─────────────────────────────────────┐│ Phase 6: AGENT AVAILABILITY ││ ││ Tool Planner → System Prompt Build → Agent Loop → Tool Execution │└─────────────────────────────────────────────────────────────────────────┘
Phase 1: 安装(Install)
1.1 CLI 入口
|
|
|
|---|---|
src/plugins/cli.ts:113 |
registerPluginCliCommands() |
src/auto-reply/reply/commands-plugins.ts:161+ |
installPluginFromPluginsCommand() |
用户执行命令时的第一个代码入口:
typescript
// src/auto-reply/reply/commands-plugins.ts (约 L161)asyncfunctioninstallPluginFromPluginsCommand(params: { raw: string; // 用户输入的原始 spec snapshot: ConfigSnapshotForInstallPersist;}): Promise<{ ok: true; pluginId: string } | { ok: false; error: string }>
1.2 Spec 路由(分流决策)
安装系统根据输入格式决定走哪条路径:
typescript
// src/auto-reply/reply/commands-plugins.ts (约 L178)functionlooksLikeLocalPluginInstallSpec(raw: string): boolean {return ( raw.startsWith(".") || // 相对路径 raw.startsWith("~") || // Home 路径 raw.startsWith("/") || // 绝对路径 raw.endsWith(".ts") || // TypeScript 文件 raw.endsWith(".tgz") || // npm 打包文件 raw.endsWith(".zip") // zip 归档 );}
路由决策树:
plaintext
输入 spec ├── fs.existsSync(resolved) = true │ ├── isArchive? → installPluginFromPath() [archive mode] │ └── isDir? → installPluginFromPath() [path mode] │ ├── looksLikeLocalPluginInstallSpec() = true │ └── 报错: "Path not found" │ ├── startsWith("git:") │ └── installPluginFromGitSpec() │ ├── findTrustedCatalogPackageInstall(packageName) │ └── npm 安装 (带 integrity 校验) │ └── 默认: npm registry 安装 └── installPluginFromNpmSpec()
1.3 npm 安装流程
|
|
|
|
|---|---|---|
src/plugins/install.ts:1+ |
|
|
src/infra/npm-managed-root.ts |
upsertManagedNpmRootDependency() |
|
src/infra/safe-package-install.ts |
createSafeNpmInstallArgs() |
|
typescript
// src/plugins/install.ts (核心流程简化)asyncfunctioninstallPluginFromNpmSpec(params: { spec: string; // e.g. "@openclaw/plugin-brave@latest" logger: PluginInstallLogger;}): Promise<InstallResult> {// 1. 解析 npm spec → 获取包名、版本、registryconst resolution = awaitresolveNpmSpecMetadata(spec);// 2. 验证兼容性const compat = normalizeExternalPluginCompatibility(packageJson);// 检查 openclaw.compat.pluginApi 范围satisfiesPluginApiRange(compat.pluginApiRange);// 3. 验证 openclaw.extensions 字段存在const extensions = resolvePackageExtensionEntries(packageJson);if (!extensions.length) throwMISSING_EXTENSIONS_ERROR;// 4. 安全策略 preflightawaitpreflightPluginNpmInstallPolicy({...});// 5. 执行 npm install (到 managed root)awaitrunCommandWithTimeout(npmCmd, npmArgs, { timeout: 120_000 });// 6. 创建 openclaw peer linkawaitlinkOpenClawPeerDependencies(targetDir);// 7. 扫描已安装的依赖树awaitscanInstalledPackageDependencyTree({...});// 8. 返回结果return { ok: true, pluginId, targetDir, version };}
1.4 安全扫描管线
|
|
|
|
|---|---|---|
src/plugins/install-security-scan.ts:57 |
scanBundleInstallSource() |
|
src/plugins/install-security-scan.ts:75 |
scanPackageInstallSource() |
|
src/plugins/install-security-scan.ts:97 |
scanInstalledPackageDependencyTree() |
|
src/plugins/install-security-scan.ts:122 |
preflightPluginNpmInstallPolicy() |
|
typescript
// src/plugins/install-security-scan.tsexporttypeInstallSecurityScanResult = {blocked?: {code?: "security_scan_blocked" | "security_scan_failed";reason: string; };};
所有扫描函数都通过惰性 import 加载实际运行时:
typescript
asyncfunctionloadInstallSecurityScanRuntime() {returnawaitimport("./install-security-scan.runtime.js");}
这是 延迟加载 设计——正常的 Gateway 启动不需要加载安全扫描代码,只在 install 时按需引入。
1.5 安装记录持久化
安装完成后,写入 openclaw.json 的 plugins.entries + plugins.install 记录:
typescript
// src/auto-reply/reply/commands-plugins.tsawaitpersistPluginInstall({snapshot: params.snapshot,pluginId: result.pluginId,install: {source: "archive" | "path" | "npm" | "git",sourcePath: resolved,installPath: result.targetDir,version: result.version, },});
Phase 2: 发现(Discovery)
2.1 扫描根路径
|
|
|
|---|---|
src/plugins/discovery.ts:1 |
discoverOpenClawPlugins() |
src/plugins/roots.ts |
resolvePluginSourceRoots() |
typescript
// src/plugins/discovery.ts (约 L88)exportfunctiondiscoverOpenClawPlugins(params: { workspaceDir?: string; env?: NodeJS.ProcessEnv;}): PluginDiscoveryResult {// 按优先级扫描多个根路径}
发现源的优先级顺序:
|
|
|
|
|---|---|---|
|
|
bundled |
<install>/dist/extensions/ |
|
|
workspace |
<workspace>/extensions/ |
|
|
global |
~/.openclaw/extensions/ |
|
|
package |
|
|
|
bundle |
|
2.2 候选者类型
typescript
// src/plugins/discovery.ts (约 L82)exporttypePluginCandidate = {idHint: string; // 从目录名/包名推断的 IDsource: string; // 入口文件路径setupSource?: string; // 可选的 setup 入口rootDir: string; // 插件根目录origin: PluginOrigin; // "bundled" | "global" | "workspace" | "package"format?: PluginFormat; // "code" | "bundle" | "legacy"packageName?: string; // npm 包名packageVersion?: string; // 版本号packageManifest?: OpenClawPackageManifest;};
2.3 Manifest 加载
|
|
|
|---|---|
src/plugins/manifest.ts:38 |
PLUGIN_MANIFEST_FILENAME = "openclaw.plugin.json" |
src/plugins/manifest.ts:40 |
MAX_PLUGIN_MANIFEST_BYTES = 256 * 1024 |
src/plugins/manifest.ts:60 |
clearPluginManifestLoadCache() |
typescript
// openclaw.plugin.json 结构示例{"id": "my-data-plugin","name": "Data Analysis Plugin","version": "1.0.0","description": "Advanced data analysis tools",// 声明提供的能力"providers": ["my-custom-model"],"channels": [],"tools": ["analyze_data", "predict_model"],// 激活触发器"activation": {"commands": ["analyze"],"capabilities": ["data-analysis"] },// 配置 Schema"configSchema": {"type": "object","properties": {"dataDir": { "type": "string", "description": "数据存储目录" } } },// Hooks 声明"hooks": ["before_tool_call", "after_tool_call"]}
Manifest 加载带有 LRU 缓存(最多 512 条)和 mtime/ctime 校验:
typescript
// src/plugins/manifest.ts (约 L41)const pluginManifestLoadCache = newPluginLruCache<PluginManifestLoadCacheEntry>(MAX_PLUGIN_MANIFEST_LOAD_CACHE_ENTRIES,);
Phase 3: 激活规划(Activation Planning)
3.1 配置归一化
|
|
|
|---|---|
src/plugins/config-state.ts |
normalizePluginsConfig() |
src/plugins/config-state.ts |
resolveEffectiveEnableState() |
src/plugins/config-state.ts |
resolveEffectivePluginActivationState() |
typescript
// 决定一个 Plugin 是否启用的完整逻辑链:// 1. plugins.allow[] → 允许列表白名单// 2. plugins.entries[id].enabled → 显式开关// 3. isPluginEnabledByDefaultForPlatform() → 平台默认启用// 4. autoEnable → 自动启用(基于配置发现)
3.2 激活计划器
|
|
|
|---|---|
src/plugins/activation-planner.ts:69 |
resolveManifestActivationPlan() |
src/plugins/activation-planner.ts:122 |
resolveManifestActivationPluginIds() |
src/plugins/activation-planner.ts:128 |
listManifestActivationTriggerReasons() |
typescript
// src/plugins/activation-planner.ts (约 L69)exportfunctionresolveManifestActivationPlan(params: ResolveManifestActivationPlanParams,): PluginActivationPlan {// 遍历所有 manifest records// 对每个 plugin 检查是否匹配当前 trigger// 返回需要激活的 plugin ID 列表 + 激活原因}
触发器类型(决定哪些 plugin 需要被激活):
typescript
exporttypePluginActivationPlannerTrigger = | { kind: "command"; command: string } // 用户执行了某个命令 | { kind: "provider"; provider: string } // 需要某个 model provider | { kind: "agentHarness"; runtime: string } // Agent harness 需要 | { kind: "channel"; channel: string } // 消息通道需要 | { kind: "route"; route: string } // HTTP 路由需要 | { kind: "capability"; capability: ... } // 能力声明需要
3.3 激活上下文构建
|
|
|
|---|---|
src/plugins/activation-context.ts:73 |
withActivatedPluginIds() |
src/plugins/activation-context.ts:117 |
applyPluginCompatibilityOverrides() |
src/plugins/activation-context.ts:155 |
applyPluginAutoEnableForActivation() |
typescript
// src/plugins/activation-context.ts (约 L73)exportfunctionwithActivatedPluginIds(params: { config?: OpenClawConfig; pluginIds: readonlystring[]; overrideGlobalDisable?: boolean; overrideExplicitDisable?: boolean;}): OpenClawConfig | undefined {// 将需要激活的 plugin IDs 注入到 config 的 allow 列表// 设置 entries[id].enabled = true}
Phase 4: 模块加载(Module Loading)
4.1 Loader 主入口
|
|
|
|---|---|
src/plugins/loader.ts:1 |
|
src/plugins/loader.ts |
loadOpenClawPlugins()
|
Loader 是整个 Plugin 系统中最大的单个文件(170+ imports),它协调所有发现的候选者的加载。
4.2 SDK Alias 解析
|
|
|
|---|---|
src/plugins/sdk-alias.ts |
buildPluginLoaderAliasMap() |
src/plugins/sdk-alias.ts |
resolvePluginSdkAliasFile() |
src/plugins/plugin-sdk-native-resolver.ts |
installOpenClawPluginSdkNativeResolver() |
Plugin 代码中写 import { ... } from "openclaw/plugin-sdk/runtime" 时,Loader 需要将其解析到正确的 dist 路径:
typescript
// SDK 别名映射逻辑"openclaw/plugin-sdk" → "<install>/dist/plugin-sdk/index.js""openclaw/plugin-sdk/runtime" → "<install>/dist/plugin-sdk/runtime.js""openclaw/plugin-sdk/types" → "<install>/dist/plugin-sdk/types.js"// ... 120+ 子路径
4.3 模块导入与注册
typescript
// src/plugins/loader.ts (概念化)asyncfunctionloadSinglePlugin(candidate: PluginCandidate): Promise<PluginRecord> {// 1. 解析入口文件const entryPath = resolvePluginRuntimeModulePath(candidate);// 2. 动态 importconstmodule = awaitimport(toSafeImportPath(entryPath));// 3. 解包 default exportconst pluginExport = unwrapDefaultModuleExport(module);// 4. 构建 Plugin API(注册能力的接口)const api = buildPluginApi(pluginId, registry);// 5. 调用 register() pluginExport.register(api);// 6. 返回 PluginRecordreturncreatePluginRecord({ id: pluginId, status: "loaded", ... });}
4.4 Package Contract 验证
|
|
|
|---|---|
packages/plugin-package-contract/src/index.ts:57 |
normalizeExternalPluginCompatibility() |
packages/plugin-package-contract/src/index.ts:92 |
listMissingExternalCodePluginFieldPaths() |
packages/plugin-package-contract/src/index.ts:103 |
validateExternalCodePluginPackageJson() |
外部 Plugin 的 package.json 必须包含:
json
{"openclaw":{"compat":{"pluginApi":">=1.0.0"// ← 必须:Plugin API 兼容范围},"build":{"openclawVersion":"2026.6.2"// ← 必须:构建时的 OpenClaw 版本},"extensions":["./dist/index.js"]// ← 必须:运行时入口}}
Phase 5: 运行时注册(Runtime Registration)
5.1 Plugin Registry
|
|
|
|---|---|
src/plugins/registry.ts:1 |
createPluginRegistry() |
src/plugins/registry-empty.ts |
createEmptyPluginRegistry() |
src/plugins/active-runtime-registry.ts:14 |
getActiveRuntimePluginRegistry() |
Registry 是 Plugin 注册所有能力的目标容器:
typescript
// Plugin register() 函数的标准模式exportfunctionregister(api: OpenClawPluginApi) {// 注册 Tools api.registerTool({name: "analyze_data",description: "Analyze tabular data files",parameters: { /* JSON Schema */ },execute: async (params) => { /* 实现 */ }, });// 注册 Hooks api.registerHook("before_tool_call", async (event, ctx) => {// 在 tool 调用前注入逻辑 });// 注册 Provider api.registerProvider({id: "my-model-provider",models: [...],stream: async (model, context, options) => { /* LLM 调用 */ }, });// 注册 Channel api.registerChannel({id: "my-channel",// ... });// 注册 Gateway Method (HTTP API) api.registerGatewayMethod({method: "myPlugin.customAction",scope: "operator.admin",handler: async (params, respond) => { /* 处理 */ }, });}
5.2 Hook Runner
|
|
|
|---|---|
src/plugins/hooks.ts:157 |
createHookRunner() |
src/plugins/hooks.ts:268 |
getHooksForName()
|
src/plugins/hook-runner-global.ts |
getGlobalHookRunner() |
typescript
// src/plugins/hooks.ts (约 L157)exportfunctioncreateHookRunner(registry: GlobalHookRunnerRegistry,options: HookRunnerOptions = {},) {// 配置超时、失败策略// 提供 runVoidHook、runModifyingHook、runClaimingHook 等执行方法}
Hook 执行策略:
|
|
|
|
|---|---|---|
|
|
|
agent_end
message_received |
|
|
|
before_prompt_build
before_model_resolve |
|
|
|
inbound_claim |
|
|
|
before_tool_call |
超时默认值(src/plugins/hooks.ts L200+):
typescript
constDEFAULT_VOID_HOOK_TIMEOUT_MS_BY_HOOK = {agent_end: 30_000,before_compaction: 30_000,after_compaction: 30_000,};constDEFAULT_MODIFYING_HOOK_TIMEOUT_MS_BY_HOOK = {before_agent_run: 15_000,before_agent_start: 15_000,before_prompt_build: 15_000,resolve_exec_env: 15_000,};
Phase 6: Agent 可用性(Agent Availability)
6.1 Tool 进入 Agent 可用列表
|
|
|
|---|---|
src/tools/planner.ts:40 |
buildToolPlan() |
src/tools/availability.ts:37 |
hasConfiguredValue() |
src/plugins/tools.ts |
|
typescript
// src/tools/planner.ts (约 L40)exportfunctionbuildToolPlan(options: BuildToolPlanOptions): ToolPlan {// 1. 收集所有已注册的 tools(来自 core + plugins)// 2. 检查 availability 条件(config 开关、环境变量)// 3. 排序(compareDescriptors)// 4. 去重(assertUniqueNames)// 5. 返回 ToolPlan: { active: [...], hidden: [...] }}
6.2 Agent Loop 中的 Tool 调用
当 LLM 响应包含 tool call 时,Agent Loop 执行:
plaintext
LLM Response → toolCalls[{name: "analyze_data", arguments: {...}}] │ ▼ Agent Loop (packages/agent-core/src/agent-loop.ts:301) prepareToolCall() │ ▼ context.tools.find(t => t.name === "analyze_data") │ ← 这里找到 Plugin 注册的 tool ▼ validateToolArguments(tool, toolCall) │ ▼ config.beforeToolCall() ← Plugin hooks 可拦截 │ ▼ tool.execute(toolCallId, args, signal, updateCallback) │ ← 调用 Plugin 提供的实现 ▼ config.afterToolCall() ← Plugin hooks 可修改结果 │ ▼ ToolResultMessage → 回到 Agent 上下文
Bundled vs External Plugin 关键差异
|
|
extensions/) |
|
|---|---|---|
|
|
"bundled" |
"global"
"package" |
|
|
|
|
|
|
|
pluginApi + openclawVersion |
|
|
|
|
|
|
extensions/<id>/ |
~/.openclaw/extensions/<id>/ |
|
|
tsdown.config.ts 统一编译 |
|
|
|
|
|
开发者实操:创建一个外部 Plugin
目录结构
plaintext
my-plugin/├── package.json ← 必须有 openclaw.* 字段├── openclaw.plugin.json ← Plugin manifest├── src/│ └── index.ts ← register() 入口├── dist/│ └── index.js ← 编译产物└── tsconfig.json
package.json
json
{"name":"@myorg/openclaw-plugin-data-analysis","version":"1.0.0","type":"module","main":"./dist/index.js","openclaw":{"compat":{"pluginApi":">=1.0.0"},"build":{"openclawVersion":"2026.6.2"},"extensions":["./dist/index.js"]},"peerDependencies":{"openclaw":"*"}}
openclaw.plugin.json
json
{"id":"data-analysis","name":"Data Analysis Plugin","version":"1.0.0","description":"Excel/CSV analysis and ML prediction tools","tools":["analyze_excel","predict_model","clean_data"],"activation":{"capabilities":["data-analysis"]},"configSchema":{"type":"object","properties":{"pythonPath":{"type":"string","default":"python3"},"maxFileSize":{"type":"number","default":104857600}}}}
src/index.ts
typescript
importtype { OpenClawPluginApi } from"openclaw/plugin-sdk";exportdefault {register(api: OpenClawPluginApi) { api.registerTool({name: "analyze_excel",description: "Inspect, clean, and analyze Excel/CSV files",parameters: {type: "object",properties: {file: { type: "string", description: "Path to data file" },action: { type: "string", enum: ["inspect", "clean", "stats", "trend"],description: "Analysis action" }, },required: ["file", "action"], },asyncexecute(toolCallId, args, signal) {const { file, action } = args;// 调用 Python 脚本或直接处理const result = awaitrunAnalysis(file, action);return {content: [{ type: "text", text: JSON.stringify(result) }],details: { file, action }, }; }, }); api.registerHook("before_prompt_build", async (event) => {return {appendSystemContext: "When analyzing data, always inspect first.", }; }); },};
安装与验证
bash
# 构建npm run build# 本地安装测试openclaw plugins install ./my-plugin# 或发布到 npm 后openclaw plugins install @myorg/openclaw-plugin-data-analysis# 验证已加载openclaw plugins listopenclaw tools list | grep analyze_excel
关键函数索引(快速定位)
|
|
|
|
|---|---|---|
|
|
src/auto-reply/reply/commands-plugins.ts |
installPluginFromPluginsCommand()
|
|
|
src/plugins/install.ts |
|
|
|
src/plugins/install-security-scan.ts |
scan* 函数 |
|
|
src/plugins/discovery.ts |
discoverOpenClawPlugins() |
|
|
src/plugins/manifest.ts |
loadPluginManifest()
|
|
|
src/plugins/activation-planner.ts |
resolveManifestActivationPlan()
|
|
|
src/plugins/loader.ts |
loadOpenClawPlugins() |
|
|
src/plugins/registry.ts |
createPluginRegistry() |
|
|
src/plugins/hooks.ts |
createHookRunner()
|
|
|
src/plugins/hook-runner-global.ts |
getGlobalHookRunner() |
|
|
src/tools/planner.ts |
buildToolPlan()
|
|
|
packages/agent-core/src/agent-loop.ts |
executeToolCalls()
|
|
|
packages/plugin-package-contract/src/index.ts |
validateExternalCodePluginPackageJson()
|
|
|
src/plugins/config-state.ts |
resolveEffectiveEnableState() |
基于 OpenClaw v2026.6.2 · src/plugins/ (625 files) + packages/plugin-package-contract/ 源码分析
夜雨聆风