乐于分享
好东西不私藏

OpenClaw Plugin 生命周期全流程 Pipeline 手册

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<{ oktruepluginIdstring } | { okfalseerrorstring }>

1.2 Spec 路由(分流决策)

安装系统根据输入格式决定走哪条路径:

typescript

// src/auto-reply/reply/commands-plugins.ts (约 L178)functionlooksLikeLocalPluginInstallSpec(rawstring): 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()
写入 managed npm root
src/infra/safe-package-install.ts createSafeNpmInstallArgs()
构建安全的 npm install 参数

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.lengththrowMISSING_EXTENSIONS_ERROR;// 4. 安全策略 preflightawaitpreflightPluginNpmInstallPolicy({...});// 5. 执行 npm install (到 managed root)awaitrunCommandWithTimeout(npmCmd, npmArgs, { timeout120_000 });// 6. 创建 openclaw peer linkawaitlinkOpenClawPeerDependencies(targetDir);// 7. 扫描已安装的依赖树awaitscanInstalledPackageDependencyTree({...});// 8. 返回结果return { oktrue, pluginId, targetDir, version };}

1.4 安全扫描管线

源码位置
函数
扫描对象
src/plugins/install-security-scan.ts:57 scanBundleInstallSource()
解压后的 bundle 源文件
src/plugins/install-security-scan.ts:75 scanPackageInstallSource()
package 源 + 入口点
src/plugins/install-security-scan.ts:97 scanInstalledPackageDependencyTree()
npm 依赖树
src/plugins/install-security-scan.ts:122 preflightPluginNpmInstallPolicy()
npm install 前策略检查

typescript

// src/plugins/install-security-scan.tsexporttypeInstallSecurityScanResult = {blocked?: {code?: "security_scan_blocked" | "security_scan_failed";reasonstring;  };};

所有扫描函数都通过惰性 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 {// 按优先级扫描多个根路径}

发现源的优先级顺序:

优先级
Origin
路径
1
bundled <install>/dist/extensions/
2
workspace <workspace>/extensions/
3
global ~/.openclaw/extensions/
4
package
managed npm root 安装的包
5
bundle
bundle 格式插件

2.2 候选者类型

typescript

// src/plugins/discovery.ts (约 L82)exporttypePluginCandidate = {idHintstring;               // 从目录名/包名推断的 IDsourcestring;               // 入口文件路径setupSource?: string;         // 可选的 setup 入口rootDirstring;              // 插件根目录originPluginOrigin;         // "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(paramsResolveManifestActivationPlanParams,): PluginActivationPlan {// 遍历所有 manifest records// 对每个 plugin 检查是否匹配当前 trigger// 返回需要激活的 plugin ID 列表 + 激活原因}

触发器类型(决定哪些 plugin 需要被激活):

typescript

exporttypePluginActivationPlannerTrigger =  | { kind"command"commandstring }      // 用户执行了某个命令  | { kind"provider"providerstring }     // 需要某个 model provider  | { kind"agentHarness"runtimestring }  // Agent harness 需要  | { kind"channel"channelstring }       // 消息通道需要  | { kind"route"routestring }           // 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
模块顶部(170+ imports)
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(candidatePluginCandidate): 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(apiOpenClawPluginApi) {// 注册 Tools  api.registerTool({name"analyze_data",description"Analyze tabular data files",parameters: { /* JSON Schema */ },executeasync (params) => { /* 实现 */ },  });// 注册 Hooks  api.registerHook("before_tool_call"async (event, ctx) => {// 在 tool 调用前注入逻辑  });// 注册 Provider  api.registerProvider({id"my-model-provider",models: [...],streamasync (model, context, options) => { /* LLM 调用 */ },  });// 注册 Channel  api.registerChannel({id"my-channel",// ...  });// 注册 Gateway Method (HTTP API)  api.registerGatewayMethod({method"myPlugin.customAction",scope"operator.admin",handlerasync (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(registryGlobalHookRunnerRegistry,optionsHookRunnerOptions = {},) {// 配置超时、失败策略// 提供 runVoidHook、runModifyingHook、runClaimingHook 等执行方法}

Hook 执行策略:

Hook 类型
执行模式
示例
Void Hook
并行、fire-and-forget
agent_end

message_received
Modifying Hook
顺序、结果累积合并
before_prompt_build

before_model_resolve
Claiming Hook
先到先得
inbound_claim
Gate Hook
阻止/放行
before_tool_call

超时默认值(src/plugins/hooks.ts L200+):

typescript

constDEFAULT_VOID_HOOK_TIMEOUT_MS_BY_HOOK = {agent_end30_000,before_compaction30_000,after_compaction30_000,};constDEFAULT_MODIFYING_HOOK_TIMEOUT_MS_BY_HOOK = {before_agent_run15_000,before_agent_start15_000,before_prompt_build15_000,resolve_exec_env15_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
Plugin tools 注册表

typescript

// src/tools/planner.ts (约 L40)exportfunctionbuildToolPlan(optionsBuildToolPlanOptions): 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 关键差异

维度
Bundled (extensions/)
External (npm 安装)
Origin
"bundled" "global"

 或 "package"
安全扫描
跳过(已审核代码)
必须通过 3 层扫描
Package Contract
不需要 compat 字段
必须有 pluginApi + openclawVersion
分发方式
随 OpenClaw dist 打包
npm registry / git / 本地路径
源码位置
extensions/<id>/ ~/.openclaw/extensions/<id>/
构建
在 tsdown.config.ts 统一编译
独立构建后安装
peer 依赖
直接引用 core 内部
通过 SDK alias 桥接

开发者实操:创建一个外部 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(apiOpenClawPluginApi) {    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"textJSON.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()

 ~L161
npm 安装细节?
src/plugins/install.ts
整文件(L1-120 为核心)
安全检查有哪些?
src/plugins/install-security-scan.ts
4 个 scan* 函数
Plugin 如何被发现?
src/plugins/discovery.ts discoverOpenClawPlugins()
Manifest 怎么解析?
src/plugins/manifest.ts loadPluginManifest()

, L38 常量
哪些 Plugin 该激活?
src/plugins/activation-planner.ts resolveManifestActivationPlan()

 L69
模块怎么加载?
src/plugins/loader.ts loadOpenClawPlugins()
Tools 如何注册?
src/plugins/registry.ts createPluginRegistry()
Hooks 如何运行?
src/plugins/hooks.ts createHookRunner()

 L157
全局 Hook Runner?
src/plugins/hook-runner-global.ts getGlobalHookRunner()
Agent 如何找到 Tool?
src/tools/planner.ts buildToolPlan()

 L40
Tool 如何被执行?
packages/agent-core/src/agent-loop.ts executeToolCalls()

 ~L301
外部包兼容性?
packages/plugin-package-contract/src/index.ts validateExternalCodePluginPackageJson()

 L103
配置如何影响启用?
src/plugins/config-state.ts resolveEffectiveEnableState()

基于 OpenClaw v2026.6.2 · src/plugins/ (625 files) + packages/plugin-package-contract/ 源码分析