alookdlna投屏t
17.07MB · 2026-04-04
OpenClaw 是一个多平台 AI 网关,它协调消息渠道和 AI 编码代理之间的对话。它充当个人 AI 助手系统,您可以将其运行在自己的设备上。
OpenClaw 提供以下核心能力:
| 能力类型 | 描述 | 支持平台 |
|---|---|---|
| 即时通讯集成 | 连接主流即时通讯应用 | WhatsApp、T@elegrimm、Slack、Discord、Signal、iMessage 等 |
| 语音通话 | 通过原生客户端进行语音通话 | 支持双向实时语音 |
| 功能 | 和处理消息事件 | 全平台支持 |
| 设备控制 | 远程设备控制能力 | 支持多种设备类型 |
┌─────────────────────────────────────────────────────────────┐
│ 消息渠道层 │
│ WhatsApp │ T@elegrimm │ Slack │ Discord │ Signal │ iMessage │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ OpenClaw AI 网关 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 插件系统 │ │ 路由引擎 │ │ 会话管理 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ AI 编码代理层 │
│ Claude │ GPT │ Gemini │ 本地模型 │ 自定义代理 │
└─────────────────────────────────────────────────────────────┘
OpenClaw 支持三种互补的扩展机制,每种机制针对不同的使用场景:
| 扩展类型 | 格式 | 目的 | 分配位置 |
|---|---|---|---|
| 插件 | openclaw.plugin.json + 运行时模块 | 添加提供程序、工具、钩子、通道和其他运行时功能 | 捆绑包(extensions/*)、工作区包(.openclaw/extensions/)或 npm 包 |
| 插件包 | Codex/Claude/Cursor 兼容布局 | 跨平台能力映射 | .codex-plugin/、.claude-plugin/、.cursor-plugin/ |
| 技能 | Markdown 文件(SKILL.md) | 定义代理功能和指令 | ClawHub(社区)、工作区(.openclaw/workspace/skills/)或托管 |
插件是 OpenClaw 最强大的扩展机制,允许开发者:
插件分布方式:
# 捆绑插件(随 OpenClaw 发行)
extensions/
├── core-plugin/
├── voice-call/
└── memory-core/
# 工作区插件(项目级别)
.openclaw/extensions/
├── my-custom-plugin/
└── team-shared-plugin/
# npm 包(社区发布)
npm install @openclaw/plugin-example
插件包提供跨平台兼容性,允许 OpenClaw 使用其他 AI 工具的插件配置:
.codex-plugin/ 目录.claude-plugin/ 目录.cursor-plugin/ 目录技能是以 Markdown 格式定义的代理指令集,用于:
技能来源:
插件系统采用优先级发现机制,按照以下顺序查找插件(先匹配成功者优先):
┌─────────────────────────────────────────────────────────────┐
│ 插件发现优先级 │
├─────────────────────────────────────────────────────────────┤
│ 1. 配置路径 plugins.load.paths(显式定义) │
│ ↓ │
│ 2. 工作区扩展 .openclaw/extensions/*.ts(当前工作区) │
│ ↓ │
│ 3. 全局扩展 ~/.openclaw/extensions/*.ts(用户级别) │
│ ↓ │
│ 4. 捆绑插件 随网关发行的核心插件 │
└─────────────────────────────────────────────────────────────┘
发现流程说明:
plugins.load.paths 显式指定的插件路径,优先级最高.openclaw/extensions/ 目录下的 TypeScript 插件~/.openclaw/extensions/ 下的插件,对所有项目可用OpenClaw 为插件提供了一个专门的运行时环境,实现以下目标:
| 成分 | 角色 | 代码实体 |
|---|---|---|
| 装载机 | 协调发现和实例化 | loadOpenClawPlugins |
| 注册表 | 保存工具、钩子和提供程序的活动实例 | PluginRegistry |
| 显现 | 用于验证的静态元数据,无需加载代码 | PluginManifestRecord |
| 运行时 | 提供给插件模块的执行上下文 | PluginRuntime |
┌─────────────────────────────────────────────────────────────┐
│ PluginLoader │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 扫描插件目录 │ │
│ │ 2. 解析 openclaw.plugin.json │ │
│ │ 3. 创建 PluginManifestRecord │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PluginRegistry │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Providers │ │ Tools │ │ Hooks │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Channels │ │ Services │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ PluginRuntime │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 为每个插件创建独立的执行上下文 │ │
│ │ 提供对系统服务的受控访问 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
插件实现了一个 register 函数,接收 OpenClawPluginApi 参数。此 API 允许注册以下组件:
| 注册类型 | 描述 | 使用场景 |
|---|---|---|
| 提供商 | 模型、语音、图像和网络搜索提供商 | 集成新的 AI 服务 |
| 工具 | 创建 AnyAgentTool 实例的工厂函数 | 扩展代理功能 |
| 钩子 | 系统事件和提示注入的处理程序 | 拦截和处理事件 |
| 渠道 | 消息平台集成 | 连接新的消息平台 |
┌─────────────────┐
│ 插件模块加载 │
└────────┬────────┘
↓
┌─────────────────┐
│ 调用 register() │
└────────┬────────┘
↓
┌─────────────────────────────────────────────────┐
│ OpenClawPluginApi │
│ ┌────────────────┐ ┌─────────────┐ │
│ │registerProvider│ │ registerTool│ │
│ └────────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌────────────────┐ │
│ │registerHook │ │registerChannel │ │
│ └─────────────┘ └────────────────┘ │
│ ┌───────────────┐ │
│ │registerService│ │
│ └───────────────┘ │
└─────────────────────────────────────────────────┘
↓
┌─────────────────┐
│ PluginRegistry │
│ 存储注册信息 │
└─────────────────┘
某些插件类型是独占的,这意味着在特定角色下同一时间只能激活一个插件。这些插件类型通过 plugins.slots 配置进行管理。
| 插槽名称 | 目的 | 默认值 |
|---|---|---|
memory | 主动长期记忆实现 | memory-core |
contextEngine | 主动上下文管理逻辑 | legacy |
插槽配置示例:
{
"plugins": {
"slots": {
"memory": "memory-lancedb",
"contextEngine": "advanced-context"
}
}
}
OpenClaw 按照特定顺序扫描插件,第一个匹配到插件 ID 的插件将被选中:
| 优先级 | 来源 | 路径 | 说明 |
|---|---|---|---|
| 1(最高) | 配置路径 | plugins.load.paths 中定义 | 显式指定的插件路径 |
| 2 | 工作区扩展 | <workspaceDir>/.openclaw/extensions/ | 项目级别插件 |
| 3 | 全局扩展 | ~/.openclaw/extensions/ | 用户级别插件 |
| 4(最低) | 捆绑插件 | OPENCLAW_BUNDLED_PLUGINS_DIR | 随发行版提供的核心插件 |
discoverOpenClawPlugins 函数负责发现插件,其工作流程如下:
┌─────────────────────────────────────────────────────────────┐
│ 插件发现流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 扫描配置路径 │
│ └─ 读取 plugins.load.paths │
│ │
│ 2. 扫描工作区扩展 │
│ └─ 查找 .openclaw/extensions/ 目录 │
│ │
│ 3. 扫描全局扩展 │
│ └─ 查找 ~/.openclaw/extensions/ 目录 │
│ │
│ 4. 扫描捆绑插件 │
│ └─ 查找 OPENCLAW_BUNDLED_PLUGINS_DIR │
│ │
│ 5. 解析清单文件 │
│ ├─ openclaw.plugin.json │
│ └─ package.json(含 openclaw 字段) │
│ │
│ 6. 创建 PluginManifestRecord │
│ └─ 验证元数据 │
└─────────────────────────────────────────────────────────────┘
清单文件格式:
// openclaw.plugin.json
{
"id": "my-plugin",
"name": "My Custom Plugin",
"version": "1.0.0",
"description": "A custom plugin for OpenClaw",
"main": "dist/index.js",
"config": {
"schema": "./config.schema.json"
}
}
// package.json(替代方案)
{
"name": "@myorg/openclaw-plugin",
"version": "1.0.0",
"openclaw": {
"id": "my-plugin",
"main": "dist/index.js"
}
}
OpenClaw 使用 PluginLoader 动态导入插件模块,原生支持 TypeScript(通过 jiti):
// 插件加载流程
async function loadPlugin(modulePath: string) {
// 使用 jiti 动态导入 TypeScript 模块
const module = await jiti.import(modulePath);
// 创建独立的运行时上下文
const runtime = new PluginRuntime(pluginId);
// 调用插件的 register 函数
await module.register(runtime.api);
return runtime;
}
PluginRuntime 为每个插件创建独立的接口,提供对系统服务的隔离访问:
┌─────────────────────────────────────────────────────────────┐
│ 主进程 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PluginRegistry(全局) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↑
受控访问接口
│
┌─────────────────────────────────────────────────────────────┐
│ 插件运行时层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │Runtime (A) │ │Runtime (B) │ │Runtime (C) │ │
│ │ Plugin A │ │ Plugin B │ │ Plugin C │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
隔离优势:
插件 SDK 是扩展开发的主要接口,通过子路径导出进行分发:openclaw/plugin-sdk。
| 子路径 | 目的 |
|---|---|
openclaw/plugin-sdk/core | 核心类型和注册助手,例如 definePluginEntry |
openclaw/plugin-sdk/runtime | 访问 PluginRuntime 与 Gateway 核心交互的方法 |
openclaw/plugin-sdk/channel-setup | 用于配置和注册新消息通道的实用程序 |
openclaw/plugin-sdk/provider-setup | 用于注册 AI 模型提供商和身份验证流程的辅助工具 |
openclaw/plugin-sdk/sandbox | 用于与执行沙箱环境交互的工具 |
openclaw/plugin-sdk/webhook-ingress | 用于处理传入 webhook 的功能 |
register 函数接收一个包含 api 方法的对象,用于接入系统:
| 方法 | 目的 | 参数 |
|---|---|---|
registerProvider | 新增一个人工智能模型提供商 | ProviderDefinition |
registerTool | 注册一个 AnyAgentTool 工厂供代理使用 | ToolFactory |
registerHook | 附加到内部生命周期事件 | HookDefinition |
registerChannel | 新增消息平台集成 | ChannelDefinition |
registerService | 启动由插件生命周期管理的后台服务 | ServiceDefinition |
import { definePluginEntry } from 'openclaw/plugin-sdk/core';
import { PluginRuntime } from 'openclaw/plugin-sdk/runtime';
export default definePluginEntry({
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
async register(api: OpenClawPluginApi) {
// 注册模型提供商
await api.registerProvider({
id: 'my-provider',
name: 'My AI Provider',
authMethod: 'api-key',
// ... 其他配置
});
// 注册工具
await api.registerTool({
id: 'my-tool',
name: 'My Custom Tool',
factory: async (context) => {
return {
name: 'my_tool',
description: 'A custom tool',
execute: async (params) => { /* ... */ }
};
}
});
// 注册钩子
await api.registerHook({
event: 'onMessage',
handler: async (event) => {
console.log('Message received:', event);
}
});
// 注册通道
await api.registerChannel({
id: 'my-channel',
type: 'messaging',
// ... 其他配置
});
// 注册后台服务
await api.registerService({
id: 'my-service',
start: async () => { /* 启动服务 */ },
stop: async () => { /* 停止服务 */ }
});
}
});
提供商通过 registerProvider 注册。定义了如何进行身份验证、列出模型以及处理推理流。
interface ProviderDefinition {
// 唯一标识符
id: string;
// 显示名称
name: string;
// 身份验证方法
authMethod: 'oauth' | 'api-key' | 'token';
// 基础 URL
baseUrl?: string;
// 环境变量覆盖
envVar?: string;
// 模型列表
models: ModelDefinition[];
// 推理处理
handleRequest: (request: InferenceRequest) => Promise<InferenceResponse>;
}
插件(例如 google-gemini-cli)实现 ProviderAuthMethod 接口,使用 ProviderAuthContext 通过 prompter 与用户交互:
// OAuth 认证流程示例
const authMethod: ProviderAuthMethod = {
type: 'oauth',
async initiate(context: ProviderAuthContext) {
// 生成 PKCE
const { verifier, challenge } = await generatePKCE();
// 构建授权 URL
const authUrl = buildAuthUrl({
clientId: process.env.CLIENT_ID,
redirectUri: 'http://localhost:3000/callback',
scope: ['openid', 'profile'],
codeChallenge: challenge
});
// 打开浏览器让用户授权
await context.prompter.openUrl(authUrl);
// 等待回调
const code = await waitForCallback();
// 交换令牌
const tokens = await exchangeCodeForTokens(code, verifier);
return tokens;
},
async refresh(refreshToken: string) {
// 刷新访问令牌
return await refreshAccessToken(refreshToken);
}
};
工具通过 OpenClawPluginToolFactory 注册,这使得工具能够通过 OpenClawPluginToolContext 实例化:
interface OpenClawPluginToolContext {
// 会话 ID
sessionId: string;
// 代理 ID
agentId: string;
// 工作区目录
workspaceDir: string;
// 配置
config: Record<string, any>;
}
// 工具工厂示例
const toolFactory: OpenClawPluginToolFactory = async (context: OpenClawPluginToolContext) => {
return {
name: 'file_operations',
description: 'Perform file operations in the workspace',
parameters: {
type: 'object',
properties: {
operation: { type: 'string', enum: ['read', 'write', 'delete'] },
path: { type: 'string' }
}
},
execute: async (params) => {
const fullPath = path.join(context.workspaceDir, params.path);
// 执行文件操作
return { success: true };
}
};
};
插件通过 registerHook 订阅系统事件。支持的类别包括:
| 事件类别 | 事件名称 | 描述 |
|---|---|---|
| 代理生命周期 | onAgentStart | 代理启动时触发 |
onAgentEnd | 代理结束时触发 | |
onAgentError | 代理出错时触发 | |
| 消息事件 | onMessage | 收到消息时触发 |
onMessageSent | 发送消息后触发 | |
| 配置更改 | onConfigChange | 配置更改时触发 |
| 系统事件 | onStartup | 系统启动时触发 |
onShutdown | 系统关闭时触发 |
// 钩子注册示例
await api.registerHook({
event: 'onMessage',
priority: 10, // 优先级(可选)
handler: async (event: MessageEvent) => {
// 处理消息事件
console.log(`Received message from ${event.sender}: ${event.content}`);
// 可以修改消息
event.content = preprocessMessage(event.content);
// 或者阻止消息传递
// event.stopPropagation();
}
});
插件使用 OpenClawPluginConfigSchema 定义其配置要求:
import { z } from 'zod';
// 配置模式示例
const configSchema: OpenClawPluginConfigSchema = {
// UI 提示(用于表单渲染)
uiHints: {
apiKey: {
label: "API Key",
sensitive: true, // 敏感字段,显示为密码输入
help: "Your provider API key"
},
model: {
label: "Default Model",
help: "Select the default model to use",
options: ["gpt-4", "gpt-3.5-turbo", "claude-3"]
},
maxTokens: {
label: "Max Tokens",
help: "Maximum tokens per request",
defaultValue: 4096
}
},
// JSON Schema(用于验证)
jsonSchema: {
type: "object",
properties: {
apiKey: {
type: "string",
minLength: 1
},
model: {
type: "string",
enum: ["gpt-4", "gpt-3.5-turbo", "claude-3"]
},
maxTokens: {
type: "integer",
minimum: 1,
maximum: 32000
}
},
required: ["apiKey"]
}
};
网关使用 safeParse 或 validate 方法验证配置:
// 配置验证流程
async function validatePluginConfig(
pluginId: string,
config: unknown
): Promise<ValidationResult> {
const schema = await loadConfigSchema(pluginId);
// 使用 Zod 进行验证
const result = schema.safeParse(config);
if (!result.success) {
return {
valid: false,
errors: result.error.errors.map(e => ({
path: e.path.join('.'),
message: e.message
}))
};
}
return { valid: true, data: result.data };
}
plugins list 命令显示已发现插件的当前状态:
$ openclaw plugins list
┌────────────────────┬──────────┬────────────┬─────────────────────┐
│ Plugin ID │ Status │ Origin │ Path │
├────────────────────┼──────────┼────────────┼─────────────────────┤
│ memory-core │ loaded │ bundled │ extensions/memory │
│ google-gemini-cli │ loaded │ global │ ~/.openclaw/ext/... │
│ my-custom-plugin │ disabled │ workspace │ .openclaw/ext/... │
│ broken-plugin │ error │ config │ /custom/path/... │
└────────────────────┴──────────┴────────────┴─────────────────────┘
| 特征 | 描述 |
|---|---|
| 清单文件 | openclaw.plugin.json 包含元数据和配置模式 |
| 状态 | loaded(已加载)、disabled(已禁用)或 error(错误),可通过 CLI 查看 |
| 来源 | 跟踪插件是否为 bundled(捆绑)、workspace(工作区)或 global(全局) |
┌─────────────┐
│ Discovered │
└──────┬──────┘
│
验证清单文件
│
┌────────────┼────────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Valid │ │ Invalid │ │ Disabled│
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
加载模块 标记错误 保持禁用
│ │ │
┌────┴────┐ │ │
↓ ↓ │ │
┌────────┐ ┌────────┐ │ │
│ Loaded │ │ Error │ │ │
└────────┘ └────────┘ │ │
↓ │
┌─────────┐ │
│ Error │◄──────┘
└─────────┘
提供模型访问或扩展功能(如语音通话)的 OpenClaw 插件遵循通用结构:
extensions/<plugin-name>/
├── index.ts # 插件入口点,提供商注册
├── src/
│ ├── providers/ # 特定提供商实现(如 Twilio、Plivo)
│ │ ├── twilio.ts
│ │ └── plivo.ts
│ ├── webhook.ts # 异步事件的 Webhook 服务器
│ ├── config.ts # Zod 验证的插件配置
│ ├── types.ts # TypeScript 类型定义
│ └── utils/ # 工具函数
│ ├── auth.ts
│ └── validation.ts
├── tests/
│ ├── providers.test.ts
│ └── webhook.test.ts
├── openclaw.plugin.json # 插件清单
├── README.md # 用户文档
├── package.json # 插件元数据
└── tsconfig.json # TypeScript 配置
所有插件都会导出一个实现插件契约的对象。register() 钩子接收 OpenClawPluginApi:
// index.ts - 插件入口点
import { definePluginEntry } from 'openclaw/plugin-sdk/core';
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/core';
import { registerMyProvider } from './providers';
import { registerMyTools } from './tools';
import { registerMyHooks } from './hooks';
export default definePluginEntry({
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
description: 'A custom OpenClaw plugin',
// 配置模式
configSchema: {
uiHints: {
apiKey: { label: 'API Key', sensitive: true }
},
jsonSchema: {
type: 'object',
properties: {
apiKey: { type: 'string' }
},
required: ['apiKey']
}
},
// 注册钩子
async register(api: OpenClawPluginApi) {
// 注册模型提供商
await registerMyProvider(api);
// 注册自定义工具
await registerMyTools(api);
// 注册生命周期钩子
await registerMyHooks(api);
console.log('My Plugin registered successfully');
}
});
Google Gemini CLI 插件通过 OAuth 身份验证注册提供程序。注册过程会将插件特定的逻辑映射到全局 models.json 配置。
// providers/gemini.ts
import { ProviderDefinition } from 'openclaw/plugin-sdk/provider-setup';
export const geminiProvider: ProviderDefinition = {
id: 'google-gemini-cli',
name: 'Google Gemini (CLI)',
// 身份验证方法
authMethod: {
type: 'oauth',
// PKCE OAuth 流程
async initiate(context) {
const { verifier, challenge } = await generatePKCE();
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
authUrl.searchParams.set('client_id', process.env.GEMINI_CLIENT_ID!);
authUrl.searchParams.set('redirect_uri', 'http://localhost:3000/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('code_challenge', challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
// 打开浏览器
await context.prompter.openUrl(authUrl.toString());
// 等待回调
const { code } = await context.waitForCallback();
// 交换令牌
return await exchangeCodeForTokens(code, verifier);
},
async refresh(refreshToken: string) {
return await refreshGoogleToken(refreshToken);
}
},
// 模型列表
models: [
{ id: 'gemini-pro', name: 'Gemini Pro' },
{ id: 'gemini-pro-vision', name: 'Gemini Pro Vision' }
],
// 推理处理
async handleRequest(request) {
const response = await fetch('https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent', {
method: 'POST',
headers: {
'Authorization': `Bearer ${request.accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(request.body)
});
return await response.json();
}
};
典型的服务提供商定义包括:
| 字段 | 描述 | 示例 |
|---|---|---|
| 提供商 ID | 唯一标识符 | google-gemini-cli |
| 身份验证方法 | 在提供程序运行时中定义 | oauth、api-key 或 token |
| 环境变量 | 可选的凭据覆盖 | GOOGLE_API_KEY |
| 基础 URL | API 端点 | |
| 模型列表 | 支持的模型 | gemini-pro, gemini-pro-vision |
┌─────────────────────────────────────────────────────────────┐
│ PKCE OAuth 流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 生成 PKCE 挑战 │
│ ├─ 生成随机 verifier │
│ └─ 计算 challenge = SHA256(verifier) │
│ │
│ 2. 构建授权 URL │
│ ├─ 添加 client_id │
│ ├─ 添加 redirect_uri (localhost) │
│ ├─ 添加 code_challenge │
│ └─ 添加 scope │
│ │
│ 3. 用户授权 │
│ ├─ 打开浏览器 │
│ └─ 用户登录并授权 │
│ │
│ 4. 接收回调 │
│ ├─ 本地服务器接收授权码 │
│ └─ 验证 state 参数 │
│ │
│ 5. 交换令牌 │
│ ├─ 发送 code + verifier │
│ ├─ 服务器验证 PKCE │
│ └─ 返回 access_token + refresh_token │
│ │
│ 6. 存储凭据 │
│ └─ 安全存储令牌 │
│ │
└─────────────────────────────────────────────────────────────┘
凭证解析:系统使用 resolveOAuthToken 在提供程序运行时获取活动凭证。
async function resolveOAuthToken(providerId: string): Promise<string> {
const credentials = await credentialStore.get(providerId);
if (isExpired(credentials)) {
// 自动刷新令牌
const newTokens = await refreshOAuthToken(credentials.refreshToken);
await credentialStore.set(providerId, newTokens);
return newTokens.accessToken;
}
return credentials.accessToken;
}
身份验证选择集成:在 openclaw onboard 过程中,applyNonInteractivePluginProviderChoice 函数负责选择基于 OAuth 的提供商。
令牌刷新:提供商实现 refreshOpenAICodexToken 来处理静默凭证续期。
voice-call 扩展程序展示了一个功能强大的插件,可以管理外部 Webhook 和实时媒体流。
┌─────────────────────────────────────────────────────────────┐
│ 语音通话架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Twilio │◄───────►│ Webhook │ │
│ │ Provider │ HTTP │ Server │ │
│ └─────────────┘ └──────┬──────┘ │
│ │ │
│ WebSocket│ │
│ ↓ │
│ ┌─────────────┐ │
│ │ Media │ │
│ │ Stream │ │
│ │ Handler │ │
│ └──────┬──────┘ │
│ │ │
│ Audio Stream│ │
│ ↓ │
│ ┌─────────────┐ │
│ │ AI │ │
│ │ Provider │ │
│ │ (OpenAI) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Webhook 安全:使用 verifyTwilioProviderWebhook 验证来自 Twilio 的签名。
import { createHmac } from 'crypto';
async function verifyTwilioProviderWebhook(
request: Request,
authToken: string
): Promise<boolean> {
const signature = request.headers['x-twilio-signature'];
const url = request.url;
const params = await request.formData();
// 构建签名数据
const data = url + Object.entries(params).sort().join('');
// 计算 HMAC
const expectedSignature = createHmac('sha1', authToken)
.update(data)
.digest('base64');
return signature === expectedSignature;
}
媒体流:使用 OpenAI RealtimeVoiceCallWebhookServer 初始化 MediaStreamHandler 实现双向音频。
import { RealtimeVoiceCallWebhookServer, MediaStreamHandler } from 'openclaw/plugin-sdk/webhook-ingress';
const voiceServer = new RealtimeVoiceCallWebhookServer({
port: 8080,
path: '/voice',
async onCallStart(callSid: string, streamSid: string) {
// 初始化媒体流处理器
const mediaHandler = new MediaStreamHandler({
streamSid,
onAudioReceived: async (audioData) => {
// 发送到 AI 提供商处理
const response = await aiProvider.processAudio(audioData);
return response;
}
});
return mediaHandler;
}
});
过期通话清理器:后台任务 startStaleCallReaper 确保在超时后清理孤立的通话记录。
async function startStaleCallReaper() {
setInterval(async () => {
const staleCalls = await db.calls.findStale({
maxAge: 30 * 60 * 1000 // 30 分钟
});
for (const call of staleCalls) {
await cleanupCall(call.id);
console.log(`Cleaned up stale call: ${call.id}`);
}
}, 5 * 60 * 1000); // 每 5 分钟运行一次
}
插件集成到 openclaw onboard 向导中,引导用户完成配置。
┌─────────────────────────────────────────────────────────────┐
│ 注册流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 发现 │
│ └─ resolveProviderSetupFlowContributions │
│ 扫描已注册的插件以查找设置流程 │
│ │
│ 2. 选择 │
│ └─ buildAuthChoiceGroups │
│ 用户看到分组选项 │
│ │
│ 3. 应用 │
│ └─ applyNonInteractiveAuthChoice │
│ 将选择持久化到 openclaw.json 或 auth-profiles.json │
│ │
└─────────────────────────────────────────────────────────────┘
OpenClaw 支持将凭据存储为纯文本或引用。
| 模式 | 描述 | 示例 |
|---|---|---|
| 引用模式 | 存储环境变量名称,而不是字面值 | env:GOOGLE_API_KEY |
| 纯文本模式 | 将原始 API 密钥直接存储在配置中 | sk-xxxxx |
// openclaw.json - 引用模式(推荐)
{
"providers": {
"openai": {
"apiKey": "env:OPENAI_API_KEY"
},
"google": {
"credentials": "env:GOOGLE_CREDENTIALS_PATH"
}
}
}
// openclaw.json - 纯文本模式(不推荐)
{
"providers": {
"openai": {
"apiKey": "sk-xxxxxxxxxxxxxxxx"
}
}
}
.env 文件或系统环境中设置凭据openclaw.json 添加到 .gitignore# .env 文件示例
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx
GOOGLE_API_KEY=AIzaxxxxxxxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxx
| 成分 | 描述 |
|---|---|
| 供应商注册 | 定义模型、基本 URL 和身份验证方法 |
| 授权逻辑 | 实现 OAuth、API 密钥或自定义身份验证标记 |
| 配置模式 | 为插件设置定义 Zod 模式 |
| Webhook Ingress | (可选)使用 createWebhookInFlightLimiter 进行安全防护 |
| 入职流程 | 注册交互式 CLI 的设置流程 |
| 错误处理 | 实现适当的错误处理和日志记录 |
| 测试覆盖 | 编写单元测试和集成测试 |
| 文档 | 编写 README 和 API 文档 |
┌─────────────────────────────────────────────────────────────┐
│ 插件开发流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 规划 │
│ ├─ 确定插件类型(提供商/工具/钩子/通道) │
│ ├─ 设计配置模式 │
│ └─ 规划身份验证方式 │
│ │
│ 2. 开发 │
│ ├─ 创建插件目录结构 │
│ ├─ 实现核心功能 │
│ ├─ 编写配置模式 │
│ └─ 实现身份验证 │
│ │
│ 3. 测试 │
│ ├─ 编写单元测试 │
│ ├─ 编写集成测试 │
│ └─ 手动测试 │
│ │
│ 4. 部署 │
│ ├─ 本地安装测试 │
│ ├─ 发布到 npm(可选) │
│ └─ 编写文档 │
│ │
│ 5. 维护 │
│ ├─ 处理问题反馈 │
│ ├─ 发布更新 │
│ └─ 保持兼容性 │
│ │
└─────────────────────────────────────────────────────────────┘
# 查看插件状态
openclaw plugins list
# 启用调试日志
DEBUG=openclaw:plugin:* openclaw start
# 测试特定插件
openclaw plugins test my-plugin
# 验证配置模式
openclaw plugins validate my-plugin
A: 使用 DEBUG 环境变量启用详细日志:
DEBUG=openclaw:* openclaw start
A: 目前不支持插件间依赖。每个插件应该是独立的。
A: 使用插件插槽(slots)机制管理独占类型插件,或通过配置禁用冲突插件。