chore(i18n): refresh zh-CN translations
This commit is contained in:
parent
4cdfe83ae5
commit
e5b62e3a42
@ -5,10 +5,10 @@ read_when:
|
||||
summary: Feishu 机器人概览、功能和配置
|
||||
title: Feishu
|
||||
x-i18n:
|
||||
generated_at: "2026-04-23T06:40:05Z"
|
||||
generated_at: "2026-04-23T20:12:19Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 11bf136cecb26dc939c5e78e020c0e6aa3312d9f143af0cab7568743c728cf13
|
||||
source_hash: 76565e89007211ba194ea9ab47ba926f5c9b5d12782d046bc94995a075671f02
|
||||
source_path: channels/feishu.md
|
||||
workflow: 15
|
||||
---
|
||||
@ -23,7 +23,7 @@ Feishu/Lark 是一个一体化协作平台,团队可以在其中聊天、共
|
||||
|
||||
## 快速开始
|
||||
|
||||
> **需要 OpenClaw 2026.4.10 或更高版本。** 运行 `openclaw --version` 进行检查。使用 `openclaw update` 升级。
|
||||
> **需要 OpenClaw 2026.4.23 或更高版本。** 运行 `openclaw --version` 进行检查。使用 `openclaw update` 升级。
|
||||
|
||||
<Steps>
|
||||
<Step title="运行渠道设置向导">
|
||||
@ -48,12 +48,12 @@ Feishu/Lark 是一个一体化协作平台,团队可以在其中聊天、共
|
||||
|
||||
配置 `dmPolicy` 以控制谁可以向机器人发送私信:
|
||||
|
||||
- `"pairing"` — 未知用户会收到一个配对码;通过 CLI 批准
|
||||
- `"pairing"` — 未知用户会收到配对码;通过 CLI 批准
|
||||
- `"allowlist"` — 只有列在 `allowFrom` 中的用户可以聊天(默认:仅机器人所有者)
|
||||
- `"open"` — 允许所有用户
|
||||
- `"disabled"` — 禁用所有私信
|
||||
|
||||
**批准配对请求:**
|
||||
**批准一个配对请求:**
|
||||
|
||||
```bash
|
||||
openclaw pairing list feishu
|
||||
@ -64,9 +64,9 @@ openclaw pairing approve feishu <CODE>
|
||||
|
||||
**群组策略**(`channels.feishu.groupPolicy`):
|
||||
|
||||
| Value | 行为 |
|
||||
| ------------- | ------------------------------------------ |
|
||||
| `"open"` | 响应群组中的所有消息 |
|
||||
| Value | Behavior |
|
||||
| ------------- | -------- |
|
||||
| `"open"` | 在群组中响应所有消息 |
|
||||
| `"allowlist"` | 仅响应 `groupAllowFrom` 中的群组 |
|
||||
| `"disabled"` | 禁用所有群组消息 |
|
||||
|
||||
@ -114,7 +114,7 @@ openclaw pairing approve feishu <CODE>
|
||||
channels: {
|
||||
feishu: {
|
||||
groupPolicy: "allowlist",
|
||||
// 群组 ID 形如:oc_xxx
|
||||
// 群组 ID 类似:oc_xxx
|
||||
groupAllowFrom: ["oc_xxx", "oc_yyy"],
|
||||
},
|
||||
},
|
||||
@ -131,7 +131,7 @@ openclaw pairing approve feishu <CODE>
|
||||
groupAllowFrom: ["oc_xxx"],
|
||||
groups: {
|
||||
oc_xxx: {
|
||||
// 用户 open_id 形如:ou_xxx
|
||||
// 用户 open_id 类似:ou_xxx
|
||||
allowFrom: ["ou_user1", "ou_user2"],
|
||||
},
|
||||
},
|
||||
@ -148,13 +148,13 @@ openclaw pairing approve feishu <CODE>
|
||||
|
||||
### 群组 ID(`chat_id`,格式:`oc_xxx`)
|
||||
|
||||
在 Feishu/Lark 中打开群组,点击右上角的菜单图标,然后进入 **设置**。群组 ID(`chat_id`)会显示在设置页面中。
|
||||
在 Feishu/Lark 中打开群组,点击右上角的菜单图标,然后进入**设置**。群组 ID(`chat_id`)会显示在设置页面中。
|
||||
|
||||

|
||||
|
||||
### 用户 ID(`open_id`,格式:`ou_xxx`)
|
||||
|
||||
启动 Gateway 网关,向机器人发送一条私信,然后查看日志:
|
||||
启动 Gateway 网关,向机器人发送一条私信,然后检查日志:
|
||||
|
||||
```bash
|
||||
openclaw logs --follow
|
||||
@ -170,8 +170,8 @@ openclaw pairing list feishu
|
||||
|
||||
## 常用命令
|
||||
|
||||
| Command | 说明 |
|
||||
| --------- | --------------------------- |
|
||||
| Command | Description |
|
||||
| --------- | ----------- |
|
||||
| `/status` | 显示机器人状态 |
|
||||
| `/reset` | 重置当前会话 |
|
||||
| `/model` | 显示或切换 AI 模型 |
|
||||
@ -184,16 +184,16 @@ openclaw pairing list feishu
|
||||
|
||||
### 机器人在群聊中没有响应
|
||||
|
||||
1. 确保机器人已被添加到群组中
|
||||
1. 确保机器人已加入群组
|
||||
2. 确保你已 @mention 机器人(默认要求)
|
||||
3. 验证 `groupPolicy` 不是 `"disabled"`
|
||||
4. 检查日志:`openclaw logs --follow`
|
||||
|
||||
### 机器人没有收到消息
|
||||
|
||||
1. 确保机器人已在 Feishu Open Platform / Lark Developer 中发布并通过审核
|
||||
1. 确保机器人已在 Feishu Open Platform / Lark Developer 中发布并获得批准
|
||||
2. 确保事件订阅包含 `im.message.receive_v1`
|
||||
3. 确保已选择 **persistent connection**(WebSocket)
|
||||
3. 确保已选择**持久连接**(WebSocket)
|
||||
4. 确保已授予所有必需的权限范围
|
||||
5. 确保 Gateway 网关正在运行:`openclaw gateway status`
|
||||
6. 检查日志:`openclaw logs --follow`
|
||||
@ -201,7 +201,7 @@ openclaw pairing list feishu
|
||||
### App Secret 泄露
|
||||
|
||||
1. 在 Feishu Open Platform / Lark Developer 中重置 App Secret
|
||||
2. 更新你配置中的值
|
||||
2. 更新你的配置中的值
|
||||
3. 重启 Gateway 网关:`openclaw gateway restart`
|
||||
|
||||
---
|
||||
@ -249,20 +249,20 @@ Feishu/Lark 支持通过交互式卡片进行流式回复。启用后,机器
|
||||
channels: {
|
||||
feishu: {
|
||||
streaming: true, // 启用流式卡片输出(默认:true)
|
||||
blockStreaming: true, // 启用块级流式传输(默认:true)
|
||||
blockStreaming: true, // 启用分块流式传输(默认:true)
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
将 `streaming: false` 设为关闭后,会以一条完整消息发送完整回复。
|
||||
将 `streaming: false` 设为关闭后,会在一条消息中发送完整回复。
|
||||
|
||||
### 配额优化
|
||||
|
||||
使用两个可选标志来减少 Feishu/Lark API 调用次数:
|
||||
|
||||
- `typingIndicator`(默认 `true`):设为 `false` 以跳过正在输入反应调用
|
||||
- `resolveSenderNames`(默认 `true`):设为 `false` 以跳过发送者资料查询
|
||||
- `typingIndicator`(默认 `true`):设为 `false` 可跳过“正在输入”反应调用
|
||||
- `resolveSenderNames`(默认 `true`):设为 `false` 可跳过发送者资料查询
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -277,7 +277,7 @@ Feishu/Lark 支持通过交互式卡片进行流式回复。启用后,机器
|
||||
|
||||
### ACP 会话
|
||||
|
||||
Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由文本命令驱动——没有原生斜杠命令菜单,因此请直接在会话中使用 `/acp ...` 消息。
|
||||
Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由文本命令驱动——没有原生斜杠命令菜单,因此请直接在对话中使用 `/acp ...` 消息。
|
||||
|
||||
#### 持久 ACP 绑定
|
||||
|
||||
@ -335,7 +335,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
|
||||
### 多智能体路由
|
||||
|
||||
使用 `bindings` 将 Feishu/Lark 私信或群组路由到不同的智能体。
|
||||
使用 `bindings` 将 Feishu/Lark 私信或群组路由到不同智能体。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -371,7 +371,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
- `match.peer.kind`:`"direct"`(私信)或 `"group"`(群聊)
|
||||
- `match.peer.id`:用户 Open ID(`ou_xxx`)或群组 ID(`oc_xxx`)
|
||||
|
||||
有关查找提示,请参见[获取群组/用户 ID](#get-groupuser-ids)。
|
||||
查找技巧请参见[获取群组/用户 ID](#get-groupuser-ids)。
|
||||
|
||||
---
|
||||
|
||||
@ -379,33 +379,33 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
|
||||
完整配置:[Gateway 网关配置](/zh-CN/gateway/configuration)
|
||||
|
||||
| Setting | 说明 | 默认值 |
|
||||
| Setting | Description | Default |
|
||||
| ------------------------------------------------- | ------------------------------------------ | ---------------- |
|
||||
| `channels.feishu.enabled` | 启用/禁用该渠道 | `true` |
|
||||
| `channels.feishu.domain` | API 域(`feishu` 或 `lark`) | `feishu` |
|
||||
| `channels.feishu.connectionMode` | 事件传输方式(`websocket` 或 `webhook`) | `websocket` |
|
||||
| `channels.feishu.defaultAccount` | 用于出站路由的默认账户 | `default` |
|
||||
| `channels.feishu.verificationToken` | webhook 模式必需 | — |
|
||||
| `channels.feishu.encryptKey` | webhook 模式必需 | — |
|
||||
| `channels.feishu.webhookPath` | Webhook 路由路径 | `/feishu/events` |
|
||||
| `channels.feishu.webhookHost` | Webhook 绑定主机 | `127.0.0.1` |
|
||||
| `channels.feishu.webhookPort` | Webhook 绑定端口 | `3000` |
|
||||
| `channels.feishu.accounts.<id>.appId` | App ID | — |
|
||||
| `channels.feishu.accounts.<id>.appSecret` | App Secret | — |
|
||||
| `channels.feishu.accounts.<id>.domain` | 按账户覆盖域设置 | `feishu` |
|
||||
| `channels.feishu.dmPolicy` | 私信策略 | `allowlist` |
|
||||
| `channels.feishu.allowFrom` | 私信允许列表(`open_id` 列表) | [BotOwnerId] |
|
||||
| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` |
|
||||
| `channels.feishu.groupAllowFrom` | 群组允许列表 | — |
|
||||
| `channels.feishu.requireMention` | 在群组中要求 @mention | `true` |
|
||||
| `channels.feishu.groups.<chat_id>.requireMention` | 按群组覆盖 @mention 要求 | inherited |
|
||||
| `channels.feishu.groups.<chat_id>.enabled` | 启用/禁用特定群组 | `true` |
|
||||
| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` |
|
||||
| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` |
|
||||
| `channels.feishu.streaming` | 流式卡片输出 | `true` |
|
||||
| `channels.feishu.blockStreaming` | 分块流式传输 | `true` |
|
||||
| `channels.feishu.typingIndicator` | 发送正在输入反应 | `true` |
|
||||
| `channels.feishu.resolveSenderNames` | 解析发送者显示名称 | `true` |
|
||||
| `channels.feishu.enabled` | 启用/禁用该渠道 | `true` |
|
||||
| `channels.feishu.domain` | API 域(`feishu` 或 `lark`) | `feishu` |
|
||||
| `channels.feishu.connectionMode` | 事件传输方式(`websocket` 或 `webhook`) | `websocket` |
|
||||
| `channels.feishu.defaultAccount` | 用于出站路由的默认账户 | `default` |
|
||||
| `channels.feishu.verificationToken` | webhook 模式必需 | — |
|
||||
| `channels.feishu.encryptKey` | webhook 模式必需 | — |
|
||||
| `channels.feishu.webhookPath` | webhook 路由路径 | `/feishu/events` |
|
||||
| `channels.feishu.webhookHost` | webhook 绑定主机 | `127.0.0.1` |
|
||||
| `channels.feishu.webhookPort` | webhook 绑定端口 | `3000` |
|
||||
| `channels.feishu.accounts.<id>.appId` | App ID | — |
|
||||
| `channels.feishu.accounts.<id>.appSecret` | App Secret | — |
|
||||
| `channels.feishu.accounts.<id>.domain` | 按账户覆盖域设置 | `feishu` |
|
||||
| `channels.feishu.dmPolicy` | 私信策略 | `allowlist` |
|
||||
| `channels.feishu.allowFrom` | 私信允许列表(`open_id` 列表) | [BotOwnerId] |
|
||||
| `channels.feishu.groupPolicy` | 群组策略 | `allowlist` |
|
||||
| `channels.feishu.groupAllowFrom` | 群组允许列表 | — |
|
||||
| `channels.feishu.requireMention` | 在群组中要求 @mention | `true` |
|
||||
| `channels.feishu.groups.<chat_id>.requireMention` | 按群组覆盖 @mention 要求 | 继承 |
|
||||
| `channels.feishu.groups.<chat_id>.enabled` | 启用/禁用特定群组 | `true` |
|
||||
| `channels.feishu.textChunkLimit` | 消息分块大小 | `2000` |
|
||||
| `channels.feishu.mediaMaxMb` | 媒体大小限制 | `30` |
|
||||
| `channels.feishu.streaming` | 流式卡片输出 | `true` |
|
||||
| `channels.feishu.blockStreaming` | 分块流式传输 | `true` |
|
||||
| `channels.feishu.typingIndicator` | 发送“正在输入”反应 | `true` |
|
||||
| `channels.feishu.resolveSenderNames` | 解析发送者显示名称 | `true` |
|
||||
|
||||
---
|
||||
|
||||
@ -419,7 +419,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
- ✅ 文件
|
||||
- ✅ 音频
|
||||
- ✅ 视频/媒体
|
||||
- ✅ 贴纸
|
||||
- ✅ 表情贴纸
|
||||
|
||||
### 发送
|
||||
|
||||
@ -429,11 +429,11 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
- ✅ 音频
|
||||
- ✅ 视频/媒体
|
||||
- ✅ 交互式卡片(包括流式更新)
|
||||
- ⚠️ 富文本(post 样式格式;不支持完整的 Feishu/Lark 编辑能力)
|
||||
- ⚠️ 富文本(post 风格格式;不支持完整的 Feishu/Lark 编辑能力)
|
||||
|
||||
### 话题和回复
|
||||
|
||||
- ✅ 内联回复
|
||||
- ✅ 行内回复
|
||||
- ✅ 话题回复
|
||||
- ✅ 回复话题消息时,媒体回复会保持话题感知
|
||||
|
||||
@ -442,7 +442,7 @@ Feishu/Lark 支持用于私信和群组话题消息的 ACP。Feishu/Lark ACP 由
|
||||
## 相关内容
|
||||
|
||||
- [渠道概览](/zh-CN/channels) — 所有受支持的渠道
|
||||
- [配对](/zh-CN/channels/pairing) — 私信身份验证与配对流程
|
||||
- [群组](/zh-CN/channels/groups) — 群聊行为与提及门控
|
||||
- [配对](/zh-CN/channels/pairing) — 私信身份验证和配对流程
|
||||
- [群组](/zh-CN/channels/groups) — 群聊行为和提及门控
|
||||
- [渠道路由](/zh-CN/channels/channel-routing) — 消息的会话路由
|
||||
- [安全](/zh-CN/gateway/security) — 访问模型与加固措施
|
||||
- [安全](/zh-CN/gateway/security) — 访问模型与加固
|
||||
|
||||
@ -1,37 +1,37 @@
|
||||
---
|
||||
read_when:
|
||||
- 开发 Google Chat 渠道功能
|
||||
summary: Google Chat 应用支持状态、能力和配置
|
||||
- 正在开发 Google Chat 渠道功能
|
||||
summary: Google Chat 应用支持状态、功能和配置
|
||||
title: Google Chat
|
||||
x-i18n:
|
||||
generated_at: "2026-04-05T08:14:34Z"
|
||||
generated_at: "2026-04-23T20:12:13Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 570894ed798dd0b9ba42806b050927216379a1228fcd2f96de565bc8a4ac7c2c
|
||||
source_hash: ff546be5a3e882445f78ee739ca9f55bf4337a3e533ea1e6be0cd588bf40fb19
|
||||
source_path: channels/googlechat.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Google Chat(Chat API)
|
||||
|
||||
状态:通过 Google Chat API webhook 支持私信和 spaces(仅 HTTP)。
|
||||
状态:已可用于通过 Google Chat API webhook 支持私信和空间(仅限 HTTP)。
|
||||
|
||||
## 快速设置(新手)
|
||||
## 快速开始(新手)
|
||||
|
||||
1. 创建一个 Google Cloud 项目并启用 **Google Chat API**。
|
||||
- 前往:[Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)
|
||||
- 如果 API 尚未启用,请启用它。
|
||||
- 如果 API 尚未启用,请先启用它。
|
||||
2. 创建一个 **Service Account**:
|
||||
- 点击 **Create Credentials** > **Service Account**。
|
||||
- 随意命名(例如 `openclaw-chat`)。
|
||||
- 随意命名(例如:`openclaw-chat`)。
|
||||
- 权限留空(点击 **Continue**)。
|
||||
- 可访问的主体留空(点击 **Done**)。
|
||||
3. 创建并下载 **JSON Key**:
|
||||
- 在服务账号列表中,点击你刚创建的那个账号。
|
||||
- 在 Service Account 列表中,点击你刚创建的那个账户。
|
||||
- 前往 **Keys** 标签页。
|
||||
- 点击 **Add Key** > **Create new key**。
|
||||
- 选择 **JSON** 并点击 **Create**。
|
||||
4. 将下载的 JSON 文件存储在你的 gateway 主机上(例如 `~/.openclaw/googlechat-service-account.json`)。
|
||||
- 选择 **JSON**,然后点击 **Create**。
|
||||
4. 将下载的 JSON 文件存放到你的 Gateway 网关主机上(例如:`~/.openclaw/googlechat-service-account.json`)。
|
||||
5. 在 [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat) 中创建一个 Google Chat 应用:
|
||||
- 填写 **Application info**:
|
||||
- **App name**:(例如 `OpenClaw`)
|
||||
@ -40,49 +40,49 @@ x-i18n:
|
||||
- 启用 **Interactive features**。
|
||||
- 在 **Functionality** 下,勾选 **Join spaces and group conversations**。
|
||||
- 在 **Connection settings** 下,选择 **HTTP endpoint URL**。
|
||||
- 在 **Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers**,并将其设置为你的 gateway 公网 URL 后跟 `/googlechat`。
|
||||
- _提示:运行 `openclaw status` 可找到你的 gateway 公网 URL。_
|
||||
- 在 **Visibility** 下,勾选 **Make this Chat app available to specific people and groups in <Your Domain>**。
|
||||
- 在文本框中输入你的邮箱地址(例如 `user@example.com`)。
|
||||
- 在 **Triggers** 下,选择 **Use a common HTTP endpoint URL for all triggers**,并将其设置为你的 Gateway 网关公网 URL 后接 `/googlechat`。
|
||||
- _提示:运行 `openclaw status` 以找到你的 Gateway 网关公网 URL。_
|
||||
- 在 **Visibility** 下,勾选 **Make this Chat app available to specific people and groups in `<Your Domain>`**。
|
||||
- 在文本框中输入你的电子邮件地址(例如 `user@example.com`)。
|
||||
- 点击底部的 **Save**。
|
||||
6. **启用应用状态**:
|
||||
- 保存后,**刷新页面**。
|
||||
- 找到 **App status** 部分(通常在保存后页面顶部或底部附近)。
|
||||
- 找到 **App status** 部分(通常在保存后出现在页面顶部或底部附近)。
|
||||
- 将状态改为 **Live - available to users**。
|
||||
- 再次点击 **Save**。
|
||||
7. 使用服务账号路径和 webhook audience 配置 OpenClaw:
|
||||
7. 使用 Service Account 路径和 webhook audience 配置 OpenClaw:
|
||||
- 环境变量:`GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json`
|
||||
- 或配置:`channels.googlechat.serviceAccountFile: "/path/to/service-account.json"`。
|
||||
8. 设置 webhook audience 类型和值(需与你的 Chat 应用配置匹配)。
|
||||
9. 启动 gateway。Google Chat 将向你的 webhook 路径发送 POST 请求。
|
||||
9. 启动 Gateway 网关。Google Chat 将向你的 webhook 路径发送 POST 请求。
|
||||
|
||||
## 添加到 Google Chat
|
||||
|
||||
在 gateway 运行且你的邮箱已加入可见性列表后:
|
||||
一旦 Gateway 网关正在运行,且你的邮箱已添加到可见性列表中:
|
||||
|
||||
1. 前往 [Google Chat](https://chat.google.com/)。
|
||||
2. 点击 **Direct Messages** 旁边的 **+**(加号)图标。
|
||||
3. 在搜索栏中(通常用于添加联系人),输入你在 Google Cloud Console 中配置的 **App name**。
|
||||
- **注意**:由于这是私有应用,机器人 _不会_ 出现在 “Marketplace” 浏览列表中。你必须按名称搜索它。
|
||||
4. 从结果中选择你的机器人。
|
||||
5. 点击 **Add** 或 **Chat** 开始一对一对话。
|
||||
5. 点击 **Add** 或 **Chat** 开始一对一会话。
|
||||
6. 发送 “Hello” 来触发助手!
|
||||
|
||||
## 公网 URL(仅 webhook)
|
||||
## 公网 URL(仅限 Webhook)
|
||||
|
||||
Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只将 `/googlechat` 路径暴露到互联网**。请将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络中。
|
||||
Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只应将 `/googlechat` 路径暴露到互联网**。请将 OpenClaw 仪表板和其他敏感端点保留在你的私有网络中。
|
||||
|
||||
### 方案 A:Tailscale Funnel(推荐)
|
||||
### 选项 A:Tailscale Funnel(推荐)
|
||||
|
||||
对私有仪表板使用 Tailscale Serve,对公开 webhook 路径使用 Funnel。这样可以保持 `/` 为私有,同时只暴露 `/googlechat`。
|
||||
将 Tailscale Serve 用于私有仪表板,将 Funnel 用于公网 webhook 路径。这样可以保持 `/` 为私有,同时只暴露 `/googlechat`。
|
||||
|
||||
1. **检查你的 gateway 绑定到了哪个地址:**
|
||||
1. **检查你的 Gateway 网关绑定到了哪个地址:**
|
||||
|
||||
```bash
|
||||
ss -tlnp | grep 18789
|
||||
```
|
||||
|
||||
记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0`,或你的 Tailscale IP,如 `100.x.x.x`)。
|
||||
记下 IP 地址(例如 `127.0.0.1`、`0.0.0.0`,或你的 Tailscale IP,例如 `100.x.x.x`)。
|
||||
|
||||
2. **仅向 tailnet 暴露仪表板(端口 8443):**
|
||||
|
||||
@ -94,7 +94,7 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
|
||||
tailscale serve --bg --https 8443 http://100.106.161.80:18789
|
||||
```
|
||||
|
||||
3. **仅公开暴露 webhook 路径:**
|
||||
3. **仅将 webhook 路径公开暴露:**
|
||||
|
||||
```bash
|
||||
# 如果绑定到 localhost(127.0.0.1 或 0.0.0.0):
|
||||
@ -105,7 +105,7 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
|
||||
```
|
||||
|
||||
4. **为该节点授权 Funnel 访问:**
|
||||
如果出现提示,请访问输出中显示的授权 URL,以在你的 tailnet 策略中为该节点启用 Funnel。
|
||||
如果系统提示,请访问输出中显示的授权 URL,以便在你的 tailnet 策略中为该节点启用 Funnel。
|
||||
|
||||
5. **验证配置:**
|
||||
|
||||
@ -114,19 +114,19 @@ Google Chat webhook 需要一个公网 HTTPS 端点。出于安全考虑,**只
|
||||
tailscale funnel status
|
||||
```
|
||||
|
||||
你的公网 webhook URL 将是:
|
||||
你的公网 webhook URL 将为:
|
||||
`https://<node-name>.<tailnet>.ts.net/googlechat`
|
||||
|
||||
你的私有仪表板将保持仅 tailnet 可访问:
|
||||
你的私有仪表板将继续仅限 tailnet:
|
||||
`https://<node-name>.<tailnet>.ts.net:8443/`
|
||||
|
||||
在 Google Chat 应用配置中,请使用公网 URL(不带 `:8443`)。
|
||||
在 Google Chat 应用配置中使用公网 URL(不带 `:8443`)。
|
||||
|
||||
> 注意:此配置在重启后仍会保留。若之后要移除,请运行 `tailscale funnel reset` 和 `tailscale serve reset`。
|
||||
> 注意:此配置会在重启后保留。若要稍后移除,请运行 `tailscale funnel reset` 和 `tailscale serve reset`。
|
||||
|
||||
### 方案 B:反向代理(Caddy)
|
||||
### 选项 B:反向代理(Caddy)
|
||||
|
||||
如果你使用类似 Caddy 的反向代理,只代理特定路径:
|
||||
如果你使用像 Caddy 这样的反向代理,请只代理特定路径:
|
||||
|
||||
```caddy
|
||||
your-domain.com {
|
||||
@ -134,38 +134,38 @@ your-domain.com {
|
||||
}
|
||||
```
|
||||
|
||||
使用此配置后,对 `your-domain.com/` 的任何请求都会被忽略或返回 404,而 `your-domain.com/googlechat` 会被安全地路由到 OpenClaw。
|
||||
使用此配置时,任何对 `your-domain.com/` 的请求都会被忽略或返回 404,而 `your-domain.com/googlechat` 会被安全地路由到 OpenClaw。
|
||||
|
||||
### 方案 C:Cloudflare Tunnel
|
||||
### 选项 C:Cloudflare Tunnel
|
||||
|
||||
将你的 tunnel ingress 规则配置为仅路由 webhook 路径:
|
||||
将你的 Tunnel ingress 规则配置为只路由 webhook 路径:
|
||||
|
||||
- **路径**:`/googlechat` -> `http://localhost:18789/googlechat`
|
||||
- **默认规则**:HTTP 404(Not Found)
|
||||
- **Path**:`/googlechat` -> `http://localhost:18789/googlechat`
|
||||
- **Default Rule**:HTTP 404(未找到)
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. Google Chat 会向 gateway 发送 webhook POST 请求。每个请求都包含 `Authorization: Bearer <token>` 请求头。
|
||||
1. Google Chat 向 Gateway 网关发送 webhook POST 请求。每个请求都包含一个 `Authorization: Bearer <token>` 请求头。
|
||||
- 当该请求头存在时,OpenClaw 会在读取/解析完整 webhook 请求体之前先验证 bearer 身份验证。
|
||||
- 对于在请求体中携带 `authorizationEventObject.systemIdToken` 的 Google Workspace Add-on 请求,也支持通过更严格的预身份验证请求体预算来处理。
|
||||
2. OpenClaw 会根据配置的 `audienceType` 和 `audience` 验证 token:
|
||||
- 对于在请求体中携带 `authorizationEventObject.systemIdToken` 的 Google Workspace Add-on 请求,系统会通过更严格的预认证请求体预算来支持。
|
||||
2. OpenClaw 会根据已配置的 `audienceType` 和 `audience` 验证该令牌:
|
||||
- `audienceType: "app-url"` → audience 是你的 HTTPS webhook URL。
|
||||
- `audienceType: "project-number"` → audience 是 Cloud 项目编号。
|
||||
3. 消息按 space 路由:
|
||||
- 私信使用会话键名 `agent:<agentId>:googlechat:direct:<spaceId>`。
|
||||
- spaces 使用会话键名 `agent:<agentId>:googlechat:group:<spaceId>`。
|
||||
4. 私信访问默认采用配对。未知发送方会收到一个配对码;使用以下命令批准:
|
||||
3. 消息按空间路由:
|
||||
- 私信使用会话键 `agent:<agentId>:googlechat:direct:<spaceId>`。
|
||||
- 空间使用会话键 `agent:<agentId>:googlechat:group:<spaceId>`。
|
||||
4. 私信访问默认采用配对。未知发送者会收到一个配对码;使用以下命令批准:
|
||||
- `openclaw pairing approve googlechat <code>`
|
||||
5. 群组 space 默认要求 @ 提及。若提及检测需要应用的用户名称,请使用 `botUser`。
|
||||
5. 群组空间默认需要 @ 提及。若提及检测需要应用的用户名,可使用 `botUser`。
|
||||
|
||||
## 目标
|
||||
|
||||
对交付和 allowlist 使用以下标识符:
|
||||
对投递和 allowlist 使用以下标识符:
|
||||
|
||||
- 私信:`users/<userId>`(推荐)。
|
||||
- 原始邮箱 `name@example.com` 是可变的,且仅在 `channels.googlechat.dangerouslyAllowNameMatching: true` 时用于直接 allowlist 匹配。
|
||||
- 原始邮箱 `name@example.com` 是可变的,且仅在 `channels.googlechat.dangerouslyAllowNameMatching: true` 时用于私信 allowlist 匹配。
|
||||
- 已弃用:`users/<email>` 会被视为用户 ID,而不是邮箱 allowlist。
|
||||
- Spaces:`spaces/<spaceId>`。
|
||||
- 空间:`spaces/<spaceId>`。
|
||||
|
||||
## 配置要点
|
||||
|
||||
@ -175,11 +175,11 @@ your-domain.com {
|
||||
googlechat: {
|
||||
enabled: true,
|
||||
serviceAccountFile: "/path/to/service-account.json",
|
||||
// 或 serviceAccountRef: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
|
||||
// or serviceAccountRef: { source: "file", provider: "filemain", id: "/channels/googlechat/serviceAccount" }
|
||||
audienceType: "app-url",
|
||||
audience: "https://gateway.example.com/googlechat",
|
||||
webhookPath: "/googlechat",
|
||||
botUser: "users/1234567890", // 可选;有助于提及检测
|
||||
botUser: "users/1234567890", // optional; helps mention detection
|
||||
dm: {
|
||||
policy: "pairing",
|
||||
allowFrom: ["users/1234567890"],
|
||||
@ -203,16 +203,16 @@ your-domain.com {
|
||||
|
||||
说明:
|
||||
|
||||
- 服务账号凭证也可以通过 `serviceAccount` 内联传入(JSON 字符串)。
|
||||
- 也支持 `serviceAccountRef`(env/file SecretRef),包括 `channels.googlechat.accounts.<id>.serviceAccountRef` 下的逐账号引用。
|
||||
- Service Account 凭证也可以通过 `serviceAccount`(JSON 字符串)内联传入。
|
||||
- 也支持 `serviceAccountRef`(env/file SecretRef),包括 `channels.googlechat.accounts.<id>.serviceAccountRef` 下的按账户配置引用。
|
||||
- 如果未设置 `webhookPath`,默认 webhook 路径为 `/googlechat`。
|
||||
- `dangerouslyAllowNameMatching` 会重新启用基于可变邮箱主体的 allowlist 匹配(紧急兼容模式)。
|
||||
- 启用 `actions.reactions` 后,可通过 `reactions` 工具和 `channels action` 使用表情回应。
|
||||
- 消息操作会公开 `send` 用于发送文本,公开 `upload-file` 用于显式发送附件。`upload-file` 接受 `media` / `filePath` / `path`,并支持可选的 `message`、`filename` 和线程目标。
|
||||
- `dangerouslyAllowNameMatching` 会重新启用基于可变邮箱主体的 allowlist 匹配(破窗兼容模式)。
|
||||
- 当启用 `actions.reactions` 时,可通过 `reactions` 工具和 `channels action` 使用表情回应。
|
||||
- 消息操作会暴露用于文本发送的 `send`,以及用于显式发送附件的 `upload-file`。`upload-file` 接受 `media` / `filePath` / `path`,并支持可选的 `message`、`filename` 和线程定向参数。
|
||||
- `typingIndicator` 支持 `none`、`message`(默认)和 `reaction`(reaction 需要用户 OAuth)。
|
||||
- 附件通过 Chat API 下载并存储到媒体管道中(大小受 `mediaMaxMb` 限制)。
|
||||
- 附件会通过 Chat API 下载,并存储到媒体管道中(大小受 `mediaMaxMb` 限制)。
|
||||
|
||||
Secrets 参考详情:[Secrets Management](/gateway/secrets)。
|
||||
Secrets 引用详情:[Secrets Management](/zh-CN/gateway/secrets)。
|
||||
|
||||
## 故障排除
|
||||
|
||||
@ -224,15 +224,15 @@ Secrets 参考详情:[Secrets Management](/gateway/secrets)。
|
||||
status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed
|
||||
```
|
||||
|
||||
这表示 webhook 处理器未注册。常见原因有:
|
||||
这意味着 webhook 处理器尚未注册。常见原因包括:
|
||||
|
||||
1. **渠道未配置**:你的配置中缺少 `channels.googlechat` 部分。请通过以下命令验证:
|
||||
1. **渠道未配置**:你的配置中缺少 `channels.googlechat` 部分。请使用以下命令验证:
|
||||
|
||||
```bash
|
||||
openclaw config get channels.googlechat
|
||||
```
|
||||
|
||||
如果返回 “Config path not found”,请添加配置(参见[配置要点](#config-highlights))。
|
||||
如果返回 “Config path not found”,请添加配置(参见[配置要点](#配置要点))。
|
||||
|
||||
2. **插件未启用**:检查插件状态:
|
||||
|
||||
@ -242,36 +242,36 @@ status code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Al
|
||||
|
||||
如果显示 “disabled”,请在配置中添加 `plugins.entries.googlechat.enabled: true`。
|
||||
|
||||
3. **Gateway 网关未重启**:添加配置后,请重启 gateway:
|
||||
3. **Gateway 网关未重启**:添加配置后,重启 Gateway 网关:
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
验证渠道是否正在运行:
|
||||
验证该渠道是否正在运行:
|
||||
|
||||
```bash
|
||||
openclaw channels status
|
||||
# 应显示:Google Chat default: enabled, configured, ...
|
||||
# Should show: Google Chat default: enabled, configured, ...
|
||||
```
|
||||
|
||||
### 其他问题
|
||||
|
||||
- 检查 `openclaw channels status --probe` 是否有认证错误或缺失的 audience 配置。
|
||||
- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅。
|
||||
- 如果提及门控阻止了回复,请将 `botUser` 设置为该应用的用户资源名称,并验证 `requireMention`。
|
||||
- 发送测试消息时使用 `openclaw logs --follow`,查看请求是否到达 gateway。
|
||||
- 检查 `openclaw channels status --probe`,查看是否存在认证错误或缺失的 audience 配置。
|
||||
- 如果没有收到消息,请确认 Chat 应用的 webhook URL 和事件订阅配置正确。
|
||||
- 如果提及门控阻止了回复,请将 `botUser` 设置为应用的用户资源名,并验证 `requireMention`。
|
||||
- 发送测试消息时使用 `openclaw logs --follow`,查看请求是否到达 Gateway 网关。
|
||||
|
||||
相关文档:
|
||||
|
||||
- [Gateway 网关配置](/gateway/configuration)
|
||||
- [安全性](/gateway/security)
|
||||
- [表情回应](/tools/reactions)
|
||||
- [Gateway 网关配置](/zh-CN/gateway/configuration)
|
||||
- [安全性](/zh-CN/gateway/security)
|
||||
- [Reactions](/zh-CN/tools/reactions)
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [渠道概览](/channels) — 所有支持的渠道
|
||||
- [配对](/channels/pairing) — 私信身份验证和配对流程
|
||||
- [群组](/channels/groups) — 群聊行为和提及门控
|
||||
- [渠道路由](/channels/channel-routing) — 消息的会话路由
|
||||
- [安全性](/gateway/security) — 访问模型和加固
|
||||
- [渠道概览](/zh-CN/channels) — 所有受支持的渠道
|
||||
- [配对](/zh-CN/channels/pairing) — 私信认证和配对流程
|
||||
- [群组](/zh-CN/channels/groups) — 群聊行为和提及门控
|
||||
- [渠道路由](/zh-CN/channels/channel-routing) — 消息的会话路由
|
||||
- [安全性](/zh-CN/gateway/security) — 访问模型和加固措施
|
||||
|
||||
@ -1,49 +1,47 @@
|
||||
---
|
||||
read_when:
|
||||
- 正在开发 Microsoft Teams 渠道功能
|
||||
- 在 Microsoft Teams 渠道上开发功能
|
||||
summary: Microsoft Teams 机器人支持状态、功能和配置
|
||||
title: Microsoft Teams
|
||||
x-i18n:
|
||||
generated_at: "2026-04-23T15:10:14Z"
|
||||
generated_at: "2026-04-23T20:12:13Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 872318a0f4ec42efef3c13bc90b4af1b3875ade431bd314583ea2cdc038cdc7c
|
||||
source_hash: 014fddd2de77a150113bf0f09bebe49ccda5847087929e01d9c0c85e4cd1219e
|
||||
source_path: channels/msteams.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Microsoft Teams
|
||||
|
||||
> “进入此处者,放弃一切希望。”
|
||||
|
||||
状态:支持文本和私信附件;频道/群组文件发送需要 `sharePointSiteId` + Graph 权限(参见[在群聊中发送文件](#sending-files-in-group-chats))。投票通过 Adaptive Cards 发送。消息操作会公开明确的 `upload-file`,用于优先发送文件的场景。
|
||||
支持文本和私信附件;渠道和群组文件发送需要 `sharePointSiteId` + Graph 权限(参见 [在群组聊天中发送文件](#sending-files-in-group-chats))。投票通过 Adaptive Cards 发送。消息操作公开了显式的 `upload-file`,用于文件优先发送。
|
||||
|
||||
## 内置插件
|
||||
|
||||
Microsoft Teams 在当前的 OpenClaw 版本中作为内置插件提供,因此在常规打包构建中不需要单独安装。
|
||||
Microsoft Teams 作为内置插件随当前的 OpenClaw 版本提供,因此在常规打包构建中
|
||||
无需单独安装。
|
||||
|
||||
如果你使用的是较旧的构建版本,或是不包含内置 Teams 的自定义安装,请手动安装它:
|
||||
如果你使用的是较旧版本,或是不包含内置 Teams 的自定义安装,
|
||||
请手动安装:
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/msteams
|
||||
```
|
||||
|
||||
本地检出版本(当你从 git 仓库运行时):
|
||||
本地检出(从 git 仓库运行时):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./path/to/local/msteams-plugin
|
||||
```
|
||||
|
||||
详情:[Plugins](/zh-CN/tools/plugin)
|
||||
详情: [Plugins](/zh-CN/tools/plugin)
|
||||
|
||||
## 快速设置(新手)
|
||||
## 快速设置(初学者)
|
||||
|
||||
1. 确保 Microsoft Teams 插件可用。
|
||||
- 当前打包发布的 OpenClaw 版本已默认内置该插件。
|
||||
- 较旧/自定义安装可使用上面的命令手动添加。
|
||||
- 当前打包的 OpenClaw 版本已默认内置它。
|
||||
- 较旧版本/自定义安装可使用上述命令手动添加。
|
||||
2. 创建一个 **Azure Bot**(App ID + 客户端密钥 + 租户 ID)。
|
||||
3. 使用这些凭证配置 OpenClaw。
|
||||
4. 通过公共 URL 或隧道暴露 `/api/messages`(默认端口为 3978)。
|
||||
4. 通过公共 URL 或隧道公开 `/api/messages`(默认端口为 3978)。
|
||||
5. 安装 Teams 应用包并启动 Gateway 网关。
|
||||
|
||||
最小配置(客户端密钥):
|
||||
@ -62,15 +60,9 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
}
|
||||
```
|
||||
|
||||
对于生产部署,建议使用[联合身份验证](#federated-authentication-certificate--managed-identity)(证书或托管身份),而不是客户端密钥。
|
||||
对于生产部署,建议考虑使用 [联合身份验证](#federated-authentication-certificate--managed-identity)(证书或托管身份)来替代客户端密钥。
|
||||
|
||||
注意:群聊默认被阻止(`channels.msteams.groupPolicy: "allowlist"`)。若要允许群组回复,请设置 `channels.msteams.groupAllowFrom`(或使用 `groupPolicy: "open"` 以允许任何成员,默认仍需提及)。
|
||||
|
||||
## 目标
|
||||
|
||||
- 通过 Teams 私信、群聊或频道与 OpenClaw 交流。
|
||||
- 保持路由可预测:回复始终返回到消息到达时所在的渠道。
|
||||
- 默认使用安全的渠道行为(除非另有配置,否则必须提及)。
|
||||
注意:默认会阻止群组聊天(`channels.msteams.groupPolicy: "allowlist"`)。要允许群组回复,请设置 `channels.msteams.groupAllowFrom`(或者使用 `groupPolicy: "open"` 来允许任何成员,默认仍需提及)。
|
||||
|
||||
## 配置写入
|
||||
|
||||
@ -88,17 +80,17 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
|
||||
**私信访问**
|
||||
|
||||
- 默认:`channels.msteams.dmPolicy = "pairing"`。未知发送者在获得批准前会被忽略。
|
||||
- 默认值: `channels.msteams.dmPolicy = "pairing"`。未知发送者在获批前会被忽略。
|
||||
- `channels.msteams.allowFrom` 应使用稳定的 AAD 对象 ID。
|
||||
- UPN/显示名称可能变化;默认禁用直接匹配,仅在 `channels.msteams.dangerouslyAllowNameMatching: true` 时启用。
|
||||
- 当凭证权限允许时,向导可通过 Microsoft Graph 将名称解析为 ID。
|
||||
- UPN/显示名称是可变的;默认禁用直接匹配,只有在设置 `channels.msteams.dangerouslyAllowNameMatching: true` 时才会启用。
|
||||
- 当凭证允许时,向导可以通过 Microsoft Graph 将名称解析为 ID。
|
||||
|
||||
**群组访问**
|
||||
|
||||
- 默认:`channels.msteams.groupPolicy = "allowlist"`(除非你添加 `groupAllowFrom`,否则会被阻止)。当未设置时,可使用 `channels.defaults.groupPolicy` 覆盖默认值。
|
||||
- `channels.msteams.groupAllowFrom` 控制哪些发送者可以在群聊/频道中触发(回退到 `channels.msteams.allowFrom`)。
|
||||
- 默认值: `channels.msteams.groupPolicy = "allowlist"`(除非你添加 `groupAllowFrom`,否则会被阻止)。当未设置时,可使用 `channels.defaults.groupPolicy` 覆盖默认值。
|
||||
- `channels.msteams.groupAllowFrom` 控制哪些发送者可以在群组聊天/渠道中触发(回退到 `channels.msteams.allowFrom`)。
|
||||
- 设置 `groupPolicy: "open"` 可允许任何成员(默认仍需提及)。
|
||||
- 若要**不允许任何频道**,请设置 `channels.msteams.groupPolicy: "disabled"`。
|
||||
- 若要**不允许任何渠道**,请设置 `channels.msteams.groupPolicy: "disabled"`。
|
||||
|
||||
示例:
|
||||
|
||||
@ -113,13 +105,14 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
}
|
||||
```
|
||||
|
||||
**Teams + 频道允许列表**
|
||||
**Teams + 渠道允许列表**
|
||||
|
||||
- 通过在 `channels.msteams.teams` 下列出 teams 和 channels,限制群组/频道回复范围。
|
||||
- 键应使用稳定的 team ID 和 channel 会话 ID。
|
||||
- 当 `groupPolicy="allowlist"` 且存在 teams 允许列表时,仅接受列出的 teams/channels(且需要提及)。
|
||||
- 配置向导接受 `Team/Channel` 条目,并会帮你保存。
|
||||
- 启动时,OpenClaw 会将 team/channel 和用户允许列表中的名称解析为 ID(当 Graph 权限允许时),并记录映射;未解析的 team/channel 名称会按输入原样保留,但默认会在路由中被忽略,除非启用了 `channels.msteams.dangerouslyAllowNameMatching: true`。
|
||||
- 通过在 `channels.msteams.teams` 下列出团队和渠道,限定群组/渠道回复范围。
|
||||
- 键应使用稳定的团队 ID 和渠道会话 ID。
|
||||
- 当 `groupPolicy="allowlist"` 且存在 teams 允许列表时,仅接受列出的团队/渠道(仍需提及)。
|
||||
- 配置向导接受 `Team/Channel` 条目,并会帮你存储它们。
|
||||
- 启动时,OpenClaw 会将团队/渠道和用户允许列表中的名称解析为 ID(当 Graph 权限允许时)
|
||||
并记录映射;默认情况下,无法解析的团队/渠道名称会按原样保留,但会在路由中被忽略,除非启用 `channels.msteams.dangerouslyAllowNameMatching: true`。
|
||||
|
||||
示例:
|
||||
|
||||
@ -142,43 +135,43 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
|
||||
## Azure Bot 设置
|
||||
|
||||
在配置 OpenClaw 之前,请先创建一个 Azure Bot 资源并保存其凭证。
|
||||
在配置 OpenClaw 之前,请先创建 Azure Bot 资源并记录其凭证。
|
||||
|
||||
<Steps>
|
||||
<Step title="创建 Azure Bot">
|
||||
前往 [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot),填写 **Basics** 选项卡:
|
||||
前往 [创建 Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot),并填写 **Basics** 选项卡:
|
||||
|
||||
| Field | Value |
|
||||
| ------------------ | ------------------------------------------------- |
|
||||
| **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) |
|
||||
| **Subscription** | 你的 Azure 订阅 |
|
||||
| **Resource group** | 新建或使用现有资源组 |
|
||||
| **Pricing tier** | 开发/测试请选择 **Free** |
|
||||
| **Type of App** | 推荐使用 **Single Tenant** |
|
||||
| **Creation type** | **Create new Microsoft App ID** |
|
||||
| Field | Value |
|
||||
| ------------------ | -------------------------------------------------------- |
|
||||
| **Bot handle** | 你的机器人名称,例如 `openclaw-msteams`(必须唯一) |
|
||||
| **Subscription** | 你的 Azure 订阅 |
|
||||
| **Resource group** | 新建或使用现有资源组 |
|
||||
| **Pricing tier** | 开发/测试请选择 **Free** |
|
||||
| **Type of App** | **Single Tenant**(推荐) |
|
||||
| **Creation type** | **Create new Microsoft App ID** |
|
||||
|
||||
<Note>
|
||||
新的多租户 bot 已在 2025-07-31 之后弃用。新建 bot 请使用 **Single Tenant**。
|
||||
2025-07-31 之后不再支持新的多租户机器人。新机器人请使用 **Single Tenant**。
|
||||
</Note>
|
||||
|
||||
点击 **Review + create** → **Create**(等待约 1–2 分钟)。
|
||||
点击 **Review + create** → **Create**(等待约 1-2 分钟)。
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="获取凭证">
|
||||
<Step title="记录凭证">
|
||||
在 Azure Bot 资源中,进入 **Configuration**:
|
||||
|
||||
- 复制 **Microsoft App ID** → `appId`
|
||||
- 点击 **Manage Password** → **Certificates & secrets** → **New client secret** → 复制其值 → `appPassword`
|
||||
- 进入 **Overview** → **Directory (tenant) ID** → `tenantId`
|
||||
- 点击 **Overview** → **Directory (tenant) ID** → `tenantId`
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="配置消息端点">
|
||||
<Step title="配置消息终结点">
|
||||
Azure Bot → **Configuration** → 设置 **Messaging endpoint**:
|
||||
|
||||
- 生产环境:`https://your-domain.com/api/messages`
|
||||
- 本地开发:使用隧道(参见[本地开发](#local-development-tunneling))
|
||||
- 生产环境: `https://your-domain.com/api/messages`
|
||||
- 本地开发:使用隧道(参见 [本地开发](#local-development-tunneling))
|
||||
|
||||
</Step>
|
||||
|
||||
@ -187,22 +180,20 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<a id="federated-authentication-certificate--managed-identity"></a>
|
||||
|
||||
## 联合身份验证
|
||||
|
||||
> 添加于 2026.3.24
|
||||
|
||||
对于生产部署,OpenClaw 支持将**联合身份验证**作为比客户端密钥更安全的替代方案。提供两种方式:
|
||||
对于生产部署,OpenClaw 支持 **联合身份验证**,作为比客户端密钥更安全的替代方案。支持两种方式:
|
||||
|
||||
### 选项 A:基于证书的身份验证
|
||||
|
||||
使用在你的 Entra ID 应用注册中登记的 PEM 证书。
|
||||
使用已在你的 Entra ID 应用注册中登记的 PEM 证书。
|
||||
|
||||
**设置:**
|
||||
|
||||
1. 生成或获取证书(带私钥的 PEM 格式)。
|
||||
2. 在 Entra ID → App Registration → **Certificates & secrets** → **Certificates** 中上传公开证书。
|
||||
1. 生成或获取一个证书(包含私钥的 PEM 格式)。
|
||||
2. 在 Entra ID → 应用注册 → **Certificates & secrets** → **Certificates** 中上传公钥证书。
|
||||
|
||||
**配置:**
|
||||
|
||||
@ -226,24 +217,24 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
- `MSTEAMS_AUTH_TYPE=federated`
|
||||
- `MSTEAMS_CERTIFICATE_PATH=/path/to/cert.pem`
|
||||
|
||||
### 选项 B:Azure Managed Identity
|
||||
### 选项 B:Azure 托管身份
|
||||
|
||||
使用 Azure Managed Identity 实现无密码身份验证。这非常适合部署在 Azure 基础设施上的场景(AKS、App Service、Azure VM),因为这些环境可提供托管身份。
|
||||
使用 Azure 托管身份实现无密码身份验证。这非常适合部署在 Azure 基础设施(AKS、App Service、Azure VM)上的场景,此时托管身份可用。
|
||||
|
||||
**工作原理:**
|
||||
|
||||
1. bot 所在的 pod/VM 拥有托管身份(系统分配或用户分配)。
|
||||
2. 一个**联合身份凭证**将该托管身份链接到 Entra ID 应用注册。
|
||||
1. 机器人 pod/VM 拥有一个托管身份(系统分配或用户分配)。
|
||||
2. 一个 **federated identity credential** 将该托管身份链接到 Entra ID 应用注册。
|
||||
3. 在运行时,OpenClaw 使用 `@azure/identity` 从 Azure IMDS 端点(`169.254.169.254`)获取令牌。
|
||||
4. 该令牌会传递给 Teams SDK,用于 bot 身份验证。
|
||||
4. 该令牌会传递给 Teams SDK,用于机器人身份验证。
|
||||
|
||||
**前提条件:**
|
||||
|
||||
- 已启用托管身份的 Azure 基础设施(AKS workload identity、App Service、VM)
|
||||
- 已在 Entra ID 应用注册上创建联合身份凭证
|
||||
- pod/VM 可访问 IMDS(`169.254.169.254:80`)
|
||||
- 已在 Entra ID 应用注册上创建 federated identity credential
|
||||
- pod/VM 可以通过网络访问 IMDS(`169.254.169.254:80`)
|
||||
|
||||
**配置(系统分配的托管身份):**
|
||||
**配置(系统分配托管身份):**
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -260,7 +251,7 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
}
|
||||
```
|
||||
|
||||
**配置(用户分配的托管身份):**
|
||||
**配置(用户分配托管身份):**
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -282,14 +273,14 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
|
||||
- `MSTEAMS_AUTH_TYPE=federated`
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY=true`
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>`(仅用户分配场景需要)
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID=<client-id>`(仅适用于用户分配)
|
||||
|
||||
### AKS workload identity 设置
|
||||
|
||||
对于使用 workload identity 的 AKS 部署:
|
||||
|
||||
1. 在 AKS 集群上**启用 workload identity**。
|
||||
2. 在 Entra ID 应用注册上**创建联合身份凭证**:
|
||||
1. 在你的 AKS 集群上**启用 workload identity**。
|
||||
2. 在 Entra ID 应用注册上**创建 federated identity credential**:
|
||||
|
||||
```bash
|
||||
az ad app federated-credential create --id <APP_OBJECT_ID> --parameters '{
|
||||
@ -319,147 +310,103 @@ openclaw plugins install ./path/to/local/msteams-plugin
|
||||
azure.workload.identity/use: "true"
|
||||
```
|
||||
|
||||
5. **确保可以访问 IMDS 网络地址**(`169.254.169.254`)——如果你使用 NetworkPolicy,请添加一条出口规则,允许访问 `169.254.169.254/32` 的 80 端口流量。
|
||||
5. **确保网络可访问** IMDS(`169.254.169.254`)—— 如果你使用了 NetworkPolicy,请添加一条出口规则,允许访问 `169.254.169.254/32` 的 80 端口。
|
||||
|
||||
### 身份验证类型对比
|
||||
|
||||
| Method | Config | Pros | Cons |
|
||||
| -------------------- | ---------------------------------------------- | -------------------- | ------------------------------ |
|
||||
| **Client secret** | `appPassword` | 设置简单 | 需要轮换密钥,安全性较低 |
|
||||
| **Certificate** | `authType: "federated"` + `certificatePath` | 无需通过网络共享密钥 | 证书管理有额外开销 |
|
||||
| **Managed Identity** | `authType: "federated"` + `useManagedIdentity` | 无密码,无需管理密钥 | 需要 Azure 基础设施 |
|
||||
| Method | Config | Pros | Cons |
|
||||
| -------------------- | ---------------------------------------------- | ---------------------------------- | ------------------------------------- |
|
||||
| **Client secret** | `appPassword` | 设置简单 | 需要轮换密钥,安全性较低 |
|
||||
| **Certificate** | `authType: "federated"` + `certificatePath` | 无需通过网络共享密钥 | 证书管理有额外开销 |
|
||||
| **Managed Identity** | `authType: "federated"` + `useManagedIdentity` | 无密码,无需管理密钥 | 需要 Azure 基础设施 |
|
||||
|
||||
**默认行为:** 当未设置 `authType` 时,OpenClaw 默认使用客户端密钥身份验证。现有配置无需修改即可继续工作。
|
||||
|
||||
## 本地开发(隧道)
|
||||
|
||||
Teams 无法访问 `localhost`。进行本地开发时请使用隧道:
|
||||
Teams 无法访问 `localhost`。本地开发时请使用隧道:
|
||||
|
||||
**选项 A:ngrok**
|
||||
|
||||
```bash
|
||||
ngrok http 3978
|
||||
# 复制 https URL,例如 https://abc123.ngrok.io
|
||||
# 将消息端点设置为:https://abc123.ngrok.io/api/messages
|
||||
# 将消息终结点设置为: https://abc123.ngrok.io/api/messages
|
||||
```
|
||||
|
||||
**选项 B:Tailscale Funnel**
|
||||
|
||||
```bash
|
||||
tailscale funnel 3978
|
||||
# 使用你的 Tailscale funnel URL 作为消息端点
|
||||
# 使用你的 Tailscale funnel URL 作为消息终结点
|
||||
```
|
||||
|
||||
## Teams Developer Portal(替代方式)
|
||||
|
||||
你也可以使用 [Teams Developer Portal](https://dev.teams.microsoft.com/apps),而不是手动创建 manifest ZIP:
|
||||
除了手动创建 manifest ZIP 之外,你也可以使用 [Teams Developer Portal](https://dev.teams.microsoft.com/apps):
|
||||
|
||||
1. 点击 **+ New app**
|
||||
2. 填写基本信息(名称、描述、开发者信息)
|
||||
3. 进入 **App features** → **Bot**
|
||||
3. 前往 **App features** → **Bot**
|
||||
4. 选择 **Enter a bot ID manually** 并粘贴你的 Azure Bot App ID
|
||||
5. 勾选作用域:**Personal**、**Team**、**Group Chat**
|
||||
5. 勾选作用域: **Personal**、**Team**、**Group Chat**
|
||||
6. 点击 **Distribute** → **Download app package**
|
||||
7. 在 Teams 中:**Apps** → **Manage your apps** → **Upload a custom app** → 选择该 ZIP
|
||||
7. 在 Teams 中: **Apps** → **Manage your apps** → **Upload a custom app** → 选择该 ZIP
|
||||
|
||||
这通常比手动编辑 JSON manifest 更容易。
|
||||
这通常比手动编辑 JSON manifest 更简单。
|
||||
|
||||
## 测试机器人
|
||||
|
||||
**选项 A:Azure Web Chat(先验证 webhook)**
|
||||
|
||||
1. 在 Azure Portal 中,进入你的 Azure Bot 资源 → **Test in Web Chat**
|
||||
2. 发送一条消息 —— 你应该能看到回复
|
||||
3. 这可以在设置 Teams 之前确认你的 webhook 端点可正常工作
|
||||
2. 发送一条消息 —— 你应该能看到响应
|
||||
3. 这可以在设置 Teams 之前确认你的 webhook 终结点工作正常
|
||||
|
||||
**选项 B:Teams(安装应用后)**
|
||||
**选项 B:Teams(安装应用之后)**
|
||||
|
||||
1. 安装 Teams 应用(侧载或组织目录)
|
||||
1. 安装 Teams 应用(旁加载或组织目录)
|
||||
2. 在 Teams 中找到该机器人并发送一条私信
|
||||
3. 检查 Gateway 网关日志中的传入 activity
|
||||
|
||||
## 设置(最小纯文本配置)
|
||||
<Accordion title="环境变量覆盖">
|
||||
|
||||
1. **确保 Microsoft Teams 插件可用**
|
||||
- 当前打包发布的 OpenClaw 版本已默认内置该插件。
|
||||
- 较旧/自定义安装可手动添加:
|
||||
- 从 npm:`openclaw plugins install @openclaw/msteams`
|
||||
- 从本地检出:`openclaw plugins install ./path/to/local/msteams-plugin`
|
||||
任意机器人/身份验证配置键也都可以通过环境变量设置:
|
||||
|
||||
2. **机器人注册**
|
||||
- 创建一个 Azure Bot(见上文),并记录:
|
||||
- App ID
|
||||
- 客户端密钥(App password)
|
||||
- Tenant ID(单租户)
|
||||
- `MSTEAMS_APP_ID`、`MSTEAMS_APP_PASSWORD`、`MSTEAMS_TENANT_ID`
|
||||
- `MSTEAMS_AUTH_TYPE`(`"secret"` 或 `"federated"`)
|
||||
- `MSTEAMS_CERTIFICATE_PATH`、`MSTEAMS_CERTIFICATE_THUMBPRINT`(联合身份验证 + 证书)
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY`、`MSTEAMS_MANAGED_IDENTITY_CLIENT_ID`(联合身份验证 + 托管身份;客户端 ID 仅适用于用户分配)
|
||||
|
||||
3. **Teams 应用 manifest**
|
||||
- 包含一个 `bot` 条目,其中 `botId = <App ID>`。
|
||||
- 作用域:`personal`、`team`、`groupChat`。
|
||||
- `supportsFiles: true`(个人作用域文件处理所必需)。
|
||||
- 添加 RSC 权限(见下文)。
|
||||
- 创建图标:`outline.png`(32x32)和 `color.png`(192x192)。
|
||||
- 将这三个文件一起打包为 zip:`manifest.json`、`outline.png`、`color.png`。
|
||||
|
||||
4. **配置 OpenClaw**
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
msteams: {
|
||||
enabled: true,
|
||||
appId: "<APP_ID>",
|
||||
appPassword: "<APP_PASSWORD>",
|
||||
tenantId: "<TENANT_ID>",
|
||||
webhook: { port: 3978, path: "/api/messages" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
你也可以使用环境变量代替配置键:
|
||||
- `MSTEAMS_APP_ID`
|
||||
- `MSTEAMS_APP_PASSWORD`
|
||||
- `MSTEAMS_TENANT_ID`
|
||||
- `MSTEAMS_AUTH_TYPE`(可选:`"secret"` 或 `"federated"`)
|
||||
- `MSTEAMS_CERTIFICATE_PATH`(联合身份验证 + 证书)
|
||||
- `MSTEAMS_CERTIFICATE_THUMBPRINT`(可选,身份验证并不要求)
|
||||
- `MSTEAMS_USE_MANAGED_IDENTITY`(联合身份验证 + 托管身份)
|
||||
- `MSTEAMS_MANAGED_IDENTITY_CLIENT_ID`(仅用户分配的 MI 需要)
|
||||
|
||||
5. **机器人端点**
|
||||
- 将 Azure Bot Messaging Endpoint 设置为:
|
||||
- `https://<host>:3978/api/messages`(或你选择的路径/端口)。
|
||||
|
||||
6. **运行 Gateway 网关**
|
||||
- 当内置或手动安装的插件可用,且存在带有凭证的 `msteams` 配置时,Teams 渠道会自动启动。
|
||||
</Accordion>
|
||||
|
||||
## 成员信息操作
|
||||
|
||||
OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,因此智能体和自动化流程可以直接通过 Microsoft Graph 解析渠道成员详情(显示名称、邮箱、角色)。
|
||||
OpenClaw 为 Microsoft Teams 提供了一个由 Graph 支持的 `member-info` 操作,因此智能体和自动化流程可以直接从 Microsoft Graph 解析渠道成员详细信息(显示名称、邮箱、角色)。
|
||||
|
||||
要求:
|
||||
|
||||
- `Member.Read.Group` RSC 权限(已包含在推荐的 manifest 中)
|
||||
- 对于跨 team 查询:需要带管理员同意的 `User.Read.All` Graph Application 权限
|
||||
- 对于跨团队查询:具有管理员同意的 `User.Read.All` Graph 应用程序权限
|
||||
|
||||
该操作受 `channels.msteams.actions.memberInfo` 控制(默认:当 Graph 凭证可用时启用)。
|
||||
该操作由 `channels.msteams.actions.memberInfo` 控制(默认:当 Graph 凭证可用时启用)。
|
||||
|
||||
## 历史上下文
|
||||
|
||||
- `channels.msteams.historyLimit` 控制会包装进提示词中的最近频道/群组消息数量。
|
||||
- 回退到 `messages.groupChat.historyLimit`。设置为 `0` 可禁用(默认 50)。
|
||||
- 获取到的线程历史会按发送者允许列表(`allowFrom` / `groupAllowFrom`)进行过滤,因此线程上下文注入目前只包含来自允许发送者的消息。
|
||||
- 引用的附件上下文(由 Teams 回复 HTML 派生的 `ReplyTo*`)目前会按收到时的原样传递。
|
||||
- 换句话说,允许列表控制谁可以触发智能体;而当前只有特定的补充上下文路径会被过滤。
|
||||
- 私信历史可通过 `channels.msteams.dmHistoryLimit`(用户轮次)进行限制。按用户覆盖:`channels.msteams.dms["<user_id>"].historyLimit`。
|
||||
- `channels.msteams.historyLimit` 控制包装进提示词中的最近渠道/群组消息数量。
|
||||
- 回退到 `messages.groupChat.historyLimit`。设置为 `0` 可禁用(默认值为 50)。
|
||||
- 获取到的线程历史会经过发送者允许列表过滤(`allowFrom` / `groupAllowFrom`),因此线程上下文预填充目前只包含来自允许发送者的消息。
|
||||
- 引用的附件上下文(由 Teams 回复 HTML 派生的 `ReplyTo*`)当前会按接收到的原样传递。
|
||||
- 换句话说,允许列表控制谁可以触发智能体;目前只有特定的补充上下文路径会被过滤。
|
||||
- 私信历史可以通过 `channels.msteams.dmHistoryLimit`(用户轮次)限制。按用户覆盖:`channels.msteams.dms["<user_id>"].historyLimit`。
|
||||
|
||||
## 当前 Teams RSC 权限
|
||||
|
||||
这些是我们 Teams 应用 manifest 中**现有的 resourceSpecific 权限**。它们仅在安装了该应用的 team/chat 内生效。
|
||||
以下是我们 Teams 应用 manifest 中**现有的 resourceSpecific 权限**。它们只在安装了该应用的团队/聊天内部生效。
|
||||
|
||||
**用于频道(team 作用域):**
|
||||
**用于渠道(团队范围):**
|
||||
|
||||
- `ChannelMessage.Read.Group`(Application)- 无需 @mention 即可接收所有频道消息
|
||||
- `ChannelMessage.Read.Group`(Application)- 无需 @mention 即可接收所有渠道消息
|
||||
- `ChannelMessage.Send.Group`(Application)
|
||||
- `Member.Read.Group`(Application)
|
||||
- `Owner.Read.Group`(Application)
|
||||
@ -467,13 +414,13 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
|
||||
- `TeamMember.Read.Group`(Application)
|
||||
- `TeamSettings.Read.Group`(Application)
|
||||
|
||||
**用于群聊:**
|
||||
**用于群组聊天:**
|
||||
|
||||
- `ChatMessage.Read.Chat`(Application)- 无需 @mention 即可接收所有群聊消息
|
||||
- `ChatMessage.Read.Chat`(Application)- 无需 @mention 即可接收所有群组聊天消息
|
||||
|
||||
## Teams manifest 示例
|
||||
|
||||
包含所需字段的最小有效示例。请替换 ID 和 URL。
|
||||
包含所需字段的最小有效示例。请替换其中的 ID 和 URL。
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -525,9 +472,9 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
|
||||
|
||||
- `bots[].botId` **必须**与 Azure Bot App ID 匹配。
|
||||
- `webApplicationInfo.id` **必须**与 Azure Bot App ID 匹配。
|
||||
- `bots[].scopes` 必须包含你计划使用的界面(`personal`、`team`、`groupChat`)。
|
||||
- `bots[].supportsFiles: true` 是个人作用域文件处理所必需的。
|
||||
- 如果你希望处理频道流量,`authorization.permissions.resourceSpecific` 必须包含频道读取/发送权限。
|
||||
- `bots[].scopes` 必须包含你计划使用的界面范围(`personal`、`team`、`groupChat`)。
|
||||
- `bots[].supportsFiles: true` 是个人范围内文件处理所必需的。
|
||||
- 如果你希望处理渠道流量,`authorization.permissions.resourceSpecific` 必须包含渠道读取/发送权限。
|
||||
|
||||
### 更新现有应用
|
||||
|
||||
@ -535,12 +482,12 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
|
||||
|
||||
1. 使用新设置更新你的 `manifest.json`
|
||||
2. **递增 `version` 字段**(例如 `1.0.0` → `1.1.0`)
|
||||
3. **重新打包为 zip**,包含 manifest 和图标(`manifest.json`、`outline.png`、`color.png`)
|
||||
3. 使用图标**重新打包 zip**(`manifest.json`、`outline.png`、`color.png`)
|
||||
4. 上传新的 zip:
|
||||
- **选项 A(Teams Admin Center):** Teams Admin Center → Teams apps → Manage apps → 找到你的应用 → Upload new version
|
||||
- **选项 B(侧载):** 在 Teams 中 → Apps → Manage your apps → Upload a custom app
|
||||
5. **对于 team 频道:** 在每个 team 中重新安装应用,新的权限才会生效
|
||||
6. **完全退出并重新启动 Teams**(而不只是关闭窗口),以清除缓存的应用元数据
|
||||
- **选项 A(Teams 管理中心):** Teams 管理中心 → Teams 应用 → 管理应用 → 找到你的应用 → 上传新版本
|
||||
- **选项 B(旁加载):** 在 Teams 中 → Apps → Manage your apps → Upload a custom app
|
||||
5. **对于团队渠道:** 在每个团队中重新安装该应用,使新权限生效
|
||||
6. **完全退出并重新启动 Teams**(不仅仅是关闭窗口),以清除缓存的应用元数据
|
||||
|
||||
## 功能:仅 RSC 与 Graph
|
||||
|
||||
@ -548,63 +495,63 @@ OpenClaw 为 Microsoft Teams 提供了基于 Graph 的 `member-info` 操作,
|
||||
|
||||
可用:
|
||||
|
||||
- 读取频道消息的**文本**内容。
|
||||
- 发送频道消息的**文本**内容。
|
||||
- 读取渠道消息**文本**内容。
|
||||
- 发送渠道消息**文本**内容。
|
||||
- 接收**个人(私信)**文件附件。
|
||||
|
||||
不可用:
|
||||
|
||||
- 频道/群组中的**图片或文件内容**(payload 仅包含 HTML 占位内容)。
|
||||
- 渠道/群组中的**图片或文件内容**(负载仅包含 HTML 占位内容)。
|
||||
- 下载存储在 SharePoint/OneDrive 中的附件。
|
||||
- 读取消息历史(超出实时 webhook 事件范围)。
|
||||
- 读取消息历史(超出实时 webhook 事件之外)。
|
||||
|
||||
### Teams RSC 加 Microsoft Graph Application 权限
|
||||
### Teams RSC 加 Microsoft Graph 应用程序权限
|
||||
|
||||
新增能力:
|
||||
新增:
|
||||
|
||||
- 下载托管内容(粘贴到消息中的图片)。
|
||||
- 下载存储在 SharePoint/OneDrive 中的文件附件。
|
||||
- 通过 Graph 读取频道/chat 消息历史。
|
||||
- 通过 Graph 读取渠道/聊天消息历史。
|
||||
|
||||
### RSC 与 Graph API 对比
|
||||
### RSC 与 Graph API
|
||||
|
||||
| Capability | RSC Permissions | Graph API |
|
||||
| ----------------------- | ----------------- | --------------------------------- |
|
||||
| **Real-time messages** | 是(通过 webhook) | 否(仅轮询) |
|
||||
| **Historical messages** | 否 | 是(可查询历史) |
|
||||
| **Setup complexity** | 仅应用 manifest | 需要管理员同意 + 令牌流程 |
|
||||
| **Works offline** | 否(必须正在运行) | 是(可随时查询) |
|
||||
| Capability | RSC Permissions | Graph API |
|
||||
| ----------------------- | -------------------- | ----------------------------------- |
|
||||
| **实时消息** | 是(通过 webhook) | 否(仅轮询) |
|
||||
| **历史消息** | 否 | 是(可查询历史) |
|
||||
| **设置复杂度** | 仅应用 manifest | 需要管理员同意 + 令牌流程 |
|
||||
| **离线可用** | 否(必须运行中) | 是(可随时查询) |
|
||||
|
||||
**结论:** RSC 用于实时监听;Graph API 用于历史访问。如果你希望在离线期间补回错过的消息,则需要带 `ChannelMessage.Read.All` 的 Graph API(需要管理员同意)。
|
||||
**结论:** RSC 用于实时监听;Graph API 用于历史访问。如果你想在离线期间补上错过的消息,就需要启用带有 `ChannelMessage.Read.All` 的 Graph API(需要管理员同意)。
|
||||
|
||||
## 启用 Graph 的媒体 + 历史记录(频道必需)
|
||||
## 启用 Graph 的媒体 + 历史(渠道必需)
|
||||
|
||||
如果你在**频道**中需要图片/文件,或希望获取**消息历史记录**,则必须启用 Microsoft Graph 权限并授予管理员同意。
|
||||
如果你需要在**渠道**中处理图片/文件,或想获取**消息历史**,则必须启用 Microsoft Graph 权限并授予管理员同意。
|
||||
|
||||
1. 在 Entra ID(Azure AD)**App Registration** 中,添加 Microsoft Graph **Application permissions**:
|
||||
- `ChannelMessage.Read.All`(频道附件 + 历史记录)
|
||||
- `Chat.Read.All` 或 `ChatMessage.Read.All`(群聊)
|
||||
1. 在 Entra ID(Azure AD)**应用注册**中,添加 Microsoft Graph **应用程序权限**:
|
||||
- `ChannelMessage.Read.All`(渠道附件 + 历史)
|
||||
- `Chat.Read.All` 或 `ChatMessage.Read.All`(群组聊天)
|
||||
2. 为租户**授予管理员同意**。
|
||||
3. 提升 Teams 应用 **manifest version**,重新上传,并且**在 Teams 中重新安装应用**。
|
||||
3. 提升 Teams 应用 **manifest 版本**,重新上传,并在 **Teams 中重新安装应用**。
|
||||
4. **完全退出并重新启动 Teams**,以清除缓存的应用元数据。
|
||||
|
||||
**用户提及的附加权限:** 对于当前对话中的用户,用户 @mention 开箱即用。不过,如果你想动态搜索并提及**当前对话之外**的用户,请添加 `User.Read.All`(Application)权限并授予管理员同意。
|
||||
**用户提及的附加权限:** 对于会话中的用户,用户 @mentions 可直接使用。但是,如果你想动态搜索并提及**当前会话之外**的用户,请添加 `User.Read.All`(Application)权限并授予管理员同意。
|
||||
|
||||
## 已知限制
|
||||
|
||||
### Webhook 超时
|
||||
|
||||
Teams 通过 HTTP webhook 投递消息。如果处理耗时过长(例如 LLM 响应较慢),你可能会看到:
|
||||
Teams 通过 HTTP webhook 传递消息。如果处理耗时过长(例如 LLM 响应较慢),你可能会看到:
|
||||
|
||||
- Gateway 网关超时
|
||||
- Teams 重试该消息(导致重复)
|
||||
- Teams 重试发送消息(导致重复)
|
||||
- 回复丢失
|
||||
|
||||
OpenClaw 会通过快速返回并主动发送回复来处理这种情况,但极慢的响应仍可能引发问题。
|
||||
OpenClaw 通过快速返回并主动发送回复来处理这一问题,但响应非常慢时仍可能出现问题。
|
||||
|
||||
### 格式化
|
||||
|
||||
Teams 的 markdown 能力比 Slack 或 Discord 更有限:
|
||||
Teams 的 markdown 比 Slack 或 Discord 更受限制:
|
||||
|
||||
- 基本格式可用:**粗体**、_斜体_、`code`、链接
|
||||
- 复杂 markdown(表格、嵌套列表)可能无法正确渲染
|
||||
@ -612,14 +559,14 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
|
||||
|
||||
## 配置
|
||||
|
||||
分组设置(共享渠道模式参见 `/gateway/configuration`)。
|
||||
按组分类的设置(共享渠道模式参见 `/gateway/configuration`)。
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="核心与 webhook">
|
||||
- `channels.msteams.enabled`
|
||||
- `channels.msteams.appId`、`appPassword`、`tenantId`:机器人凭证
|
||||
- `channels.msteams.webhook.port`(默认 `3978`)
|
||||
- `channels.msteams.webhook.path`(默认 `/api/messages`)
|
||||
- `channels.msteams.webhook.port`(默认值 `3978`)
|
||||
- `channels.msteams.webhook.path`(默认值 `/api/messages`)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="身份验证">
|
||||
@ -629,17 +576,17 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="访问控制">
|
||||
- `dmPolicy`:`pairing | allowlist | open | disabled`(默认:pairing)
|
||||
- `allowFrom`:私信允许列表,推荐使用 AAD 对象 ID;当 Graph 访问可用时,向导会解析名称
|
||||
- `dangerouslyAllowNameMatching`:用于可变 UPN/显示名称及 team/channel 名称路由的紧急放行开关
|
||||
- `requireMention`:在频道/群组中要求 @mention(默认 `true`)
|
||||
- `dmPolicy`:`pairing | allowlist | open | disabled`(默认值:pairing)
|
||||
- `allowFrom`:私信允许列表,优先使用 AAD 对象 ID;当 Graph 访问可用时,向导会解析名称
|
||||
- `dangerouslyAllowNameMatching`:用于可变 UPN/显示名称以及团队/渠道名称路由的紧急开关
|
||||
- `requireMention`:在渠道/群组中要求 @mention(默认值 `true`)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Team 和频道覆盖">
|
||||
<Accordion title="团队和渠道覆盖">
|
||||
所有这些设置都会覆盖顶层默认值:
|
||||
|
||||
- `teams.<teamId>.replyStyle`、`.requireMention`
|
||||
- `teams.<teamId>.tools`、`.toolsBySender`:按 team 设置的工具策略默认值
|
||||
- `teams.<teamId>.tools`、`.toolsBySender`:按团队划分的工具策略默认值
|
||||
- `teams.<teamId>.channels.<conversationId>.replyStyle`、`.requireMention`
|
||||
- `teams.<teamId>.channels.<conversationId>.tools`、`.toolsBySender`
|
||||
|
||||
@ -649,38 +596,38 @@ Teams 的 markdown 能力比 Slack 或 Discord 更有限:
|
||||
|
||||
<Accordion title="投递、媒体和操作">
|
||||
- `textChunkLimit`:出站文本分块大小
|
||||
- `chunkMode`:`length`(默认)或 `newline`(先按段落边界拆分,再按长度拆分)
|
||||
- `chunkMode`:`length`(默认)或 `newline`(在按长度分割前先按段落边界分割)
|
||||
- `mediaAllowHosts`:入站附件主机允许列表(默认包含 Microsoft/Teams 域名)
|
||||
- `mediaAuthAllowHosts`:重试时可接收 Authorization 头的主机(默认包含 Graph + Bot Framework)
|
||||
- `replyStyle`:`thread | top-level`(参见[回复样式](#reply-style-threads-vs-posts))
|
||||
- `actions.memberInfo`:切换基于 Graph 的成员信息操作(默认在 Graph 可用时开启)
|
||||
- `sharePointSiteId`:群聊/频道中文件上传所必需(参见[在群聊中发送文件](#sending-files-in-group-chats))
|
||||
- `mediaAuthAllowHosts`:重试时允许接收 Authorization 头的主机(默认包含 Graph + Bot Framework)
|
||||
- `replyStyle`:`thread | top-level`(参见 [回复样式](#reply-style-threads-vs-posts))
|
||||
- `actions.memberInfo`:切换由 Graph 支持的成员信息操作(默认在 Graph 可用时开启)
|
||||
- `sharePointSiteId`:群组聊天/渠道中文件上传所必需(参见 [在群组聊天中发送文件](#sending-files-in-group-chats))
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 路由和会话
|
||||
## 路由与会话
|
||||
|
||||
- 会话键遵循标准智能体格式(参见 [/concepts/session](/zh-CN/concepts/session)):
|
||||
- 私信共享主会话(`agent:<agentId>:<mainKey>`)。
|
||||
- 频道/群组消息使用会话 ID:
|
||||
- 渠道/群组消息使用会话 ID:
|
||||
- `agent:<agentId>:msteams:channel:<conversationId>`
|
||||
- `agent:<agentId>:msteams:group:<conversationId>`
|
||||
|
||||
## 回复样式:线程 vs 发帖
|
||||
## 回复样式:线程与帖子
|
||||
|
||||
Teams 最近在相同的底层数据模型之上引入了两种频道 UI 样式:
|
||||
Teams 最近在相同的底层数据模型之上引入了两种渠道 UI 样式:
|
||||
|
||||
| Style | Description | Recommended `replyStyle` |
|
||||
| ------------------------ | -------------------------------------- | ------------------------ |
|
||||
| **Posts**(经典) | 消息显示为卡片,下方带线程回复 | `thread`(默认) |
|
||||
| **Threads**(类似 Slack) | 消息线性流动,更像 Slack | `top-level` |
|
||||
| Style | Description | Recommended `replyStyle` |
|
||||
| ------------------------ | --------------------------------------------------------- | ------------------------ |
|
||||
| **Posts**(经典) | 消息显示为卡片,下方带有线程回复 | `thread`(默认) |
|
||||
| **Threads**(类似 Slack) | 消息线性流动,更像 Slack | `top-level` |
|
||||
|
||||
**问题:** Teams API 不会公开频道使用的是哪种 UI 样式。如果你使用了错误的 `replyStyle`:
|
||||
**问题在于:** Teams API 不会暴露渠道使用的是哪种 UI 样式。如果你使用了错误的 `replyStyle`:
|
||||
|
||||
- 在 Threads 样式的频道中使用 `thread` → 回复会以别扭的嵌套方式显示
|
||||
- 在 Posts 样式的频道中使用 `top-level` → 回复会显示为单独的顶层发帖,而不是在线程内
|
||||
- 在 Threads 风格的渠道中使用 `thread` → 回复会以不自然的嵌套方式显示
|
||||
- 在 Posts 风格的渠道中使用 `top-level` → 回复会显示为独立的顶层帖子,而不是在线程中
|
||||
|
||||
**解决方案:** 根据频道的实际设置,按频道配置 `replyStyle`:
|
||||
**解决方案:** 根据渠道的设置,为每个渠道配置 `replyStyle`:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -705,40 +652,40 @@ Teams 最近在相同的底层数据模型之上引入了两种频道 UI 样式
|
||||
|
||||
**当前限制:**
|
||||
|
||||
- **私信:** 图片和文件附件可通过 Teams bot 文件 API 正常工作。
|
||||
- **频道/群组:** 附件保存在 M365 存储(SharePoint/OneDrive)中。webhook payload 仅包含一个 HTML 占位内容,而不包含实际文件字节。**下载频道附件需要 Graph API 权限**。
|
||||
- 对于显式的优先发送文件场景,请使用 `action=upload-file`,并配合 `media` / `filePath` / `path`;可选的 `message` 会作为附带文本/评论,`filename` 会覆盖上传名称。
|
||||
- **私信:** 图片和文件附件可通过 Teams 机器人文件 API 正常工作。
|
||||
- **渠道/群组:** 附件存储在 M365 存储中(SharePoint/OneDrive)。webhook 负载只包含一个 HTML 占位内容,不包含实际文件字节。**下载渠道附件需要 Graph API 权限**。
|
||||
- 对于显式的文件优先发送,请使用 `action=upload-file`,配合 `media` / `filePath` / `path`;可选的 `message` 会作为附带文本/评论,`filename` 会覆盖上传后的名称。
|
||||
|
||||
如果没有 Graph 权限,带图片的频道消息会以纯文本形式接收(机器人无法访问图片内容)。
|
||||
默认情况下,OpenClaw 仅从 Microsoft/Teams 主机名下载媒体。可通过 `channels.msteams.mediaAllowHosts` 覆盖(使用 `["*"]` 允许任意主机)。
|
||||
Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的主机(默认包含 Graph + Bot Framework 主机)。请保持该列表严格受限(避免多租户后缀)。
|
||||
如果没有 Graph 权限,带图片的渠道消息会被作为纯文本接收(机器人无法访问图片内容)。
|
||||
默认情况下,OpenClaw 只会从 Microsoft/Teams 主机名下载媒体。可通过 `channels.msteams.mediaAllowHosts` 覆盖(使用 `["*"]` 可允许任意主机)。
|
||||
Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的主机(默认包括 Graph + Bot Framework 主机)。请保持此列表严格受限(避免多租户后缀)。
|
||||
|
||||
## 在群聊中发送文件
|
||||
## 在群组聊天中发送文件
|
||||
|
||||
机器人可以在私信中使用 FileConsentCard 流程发送文件(内置支持)。但是,**在群聊/频道中发送文件**需要额外设置:
|
||||
机器人可以使用 FileConsentCard 流程(内置)在私信中发送文件。不过,**在群组聊天/渠道中发送文件**需要额外设置:
|
||||
|
||||
| Context | How files are sent | Setup needed |
|
||||
| ------------------------ | ------------------------------------- | ----------------------------------------------- |
|
||||
| **私信** | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 |
|
||||
| **群聊/频道** | 上传到 SharePoint → 分享链接 | 需要 `sharePointSiteId` + Graph 权限 |
|
||||
| **图片(任意上下文)** | Base64 编码内联 | 开箱即用 |
|
||||
| Context | How files are sent | Setup needed |
|
||||
| ------------------------ | -------------------------------------------- | ----------------------------------------------- |
|
||||
| **私信** | FileConsentCard → 用户接受 → 机器人上传 | 开箱即用 |
|
||||
| **群组聊天/渠道** | 上传到 SharePoint → 分享链接 | 需要 `sharePointSiteId` + Graph 权限 |
|
||||
| **图片(任意上下文)** | Base64 编码内联 | 开箱即用 |
|
||||
|
||||
### 为什么群聊需要 SharePoint
|
||||
### 为什么群组聊天需要 SharePoint
|
||||
|
||||
机器人没有个人 OneDrive drive(`/me/drive` Graph API 端点不适用于应用身份)。要在群聊/频道中发送文件,机器人会上传到一个 **SharePoint site** 并创建共享链接。
|
||||
机器人没有个人 OneDrive 驱动器(`/me/drive` Graph API 端点不适用于应用身份)。要在群组聊天/渠道中发送文件,机器人需要上传到一个 **SharePoint 站点** 并创建共享链接。
|
||||
|
||||
### 设置
|
||||
|
||||
1. 在 Entra ID(Azure AD)→ App Registration 中**添加 Graph API 权限**:
|
||||
1. 在 Entra ID(Azure AD)→ 应用注册中**添加 Graph API 权限**:
|
||||
- `Sites.ReadWrite.All`(Application)- 上传文件到 SharePoint
|
||||
- `Chat.Read.All`(Application)- 可选,用于启用按用户的共享链接
|
||||
- `Chat.Read.All`(Application)- 可选,启用按用户的共享链接
|
||||
|
||||
2. 为租户**授予管理员同意**。
|
||||
|
||||
3. **获取你的 SharePoint site ID:**
|
||||
|
||||
```bash
|
||||
# 通过 Graph Explorer 或使用有效令牌配合 curl:
|
||||
# 通过 Graph Explorer 或使用有效令牌的 curl:
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}"
|
||||
|
||||
@ -746,7 +693,7 @@ Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles"
|
||||
|
||||
# 响应中包含:"id": "contoso.sharepoint.com,guid1,guid2"
|
||||
# 响应中包含: "id": "contoso.sharepoint.com,guid1,guid2"
|
||||
```
|
||||
|
||||
4. **配置 OpenClaw:**
|
||||
@ -764,40 +711,40 @@ Authorization 头只会附加到 `channels.msteams.mediaAuthAllowHosts` 中的
|
||||
|
||||
### 共享行为
|
||||
|
||||
| Permission | Sharing behavior |
|
||||
| --------------------------------------- | ---------------------------------------------- |
|
||||
| `Sites.ReadWrite.All` only | 组织范围共享链接(组织内任何人都可访问) |
|
||||
| `Sites.ReadWrite.All` + `Chat.Read.All` | 按用户共享链接(仅聊天成员可访问) |
|
||||
| Permission | Sharing behavior |
|
||||
| --------------------------------------- | --------------------------------------------------------- |
|
||||
| `Sites.ReadWrite.All` only | 全组织共享链接(组织内任何人都可访问) |
|
||||
| `Sites.ReadWrite.All` + `Chat.Read.All` | 按用户共享链接(只有聊天成员可访问) |
|
||||
|
||||
按用户共享更安全,因为只有聊天参与者可以访问该文件。如果缺少 `Chat.Read.All` 权限,机器人会回退到组织范围共享。
|
||||
按用户共享更安全,因为只有聊天参与者可以访问文件。如果缺少 `Chat.Read.All` 权限,机器人会回退为全组织共享。
|
||||
|
||||
### 回退行为
|
||||
|
||||
| Scenario | Result |
|
||||
| ------------------------------------------------- | -------------------------------------------- |
|
||||
| 群聊 + 文件 + 已配置 `sharePointSiteId` | 上传到 SharePoint,发送共享链接 |
|
||||
| 群聊 + 文件 + 无 `sharePointSiteId` | 尝试上传到 OneDrive(可能失败),仅发送文本 |
|
||||
| 个人聊天 + 文件 | FileConsentCard 流程(无需 SharePoint 即可工作) |
|
||||
| 任意上下文 + 图片 | Base64 编码内联(无需 SharePoint 即可工作) |
|
||||
| Scenario | Result |
|
||||
| ------------------------------------------------- | -------------------------------------------------- |
|
||||
| 群组聊天 + 文件 + 已配置 `sharePointSiteId` | 上传到 SharePoint,发送共享链接 |
|
||||
| 群组聊天 + 文件 + 无 `sharePointSiteId` | 尝试上传到 OneDrive(可能失败),仅发送文本 |
|
||||
| 个人聊天 + 文件 | FileConsentCard 流程(无需 SharePoint 即可工作) |
|
||||
| 任意上下文 + 图片 | Base64 编码内联(无需 SharePoint 即可工作) |
|
||||
|
||||
### 文件存储位置
|
||||
|
||||
上传的文件存储在已配置 SharePoint site 默认文档库中的 `/OpenClawShared/` 文件夹下。
|
||||
上传的文件会存储在已配置 SharePoint 站点默认文档库中的 `/OpenClawShared/` 文件夹下。
|
||||
|
||||
## 投票(Adaptive Cards)
|
||||
|
||||
OpenClaw 通过 Adaptive Cards 发送 Teams 投票(Teams 没有原生投票 API)。
|
||||
OpenClaw 将 Teams 投票作为 Adaptive Cards 发送(Teams 没有原生投票 API)。
|
||||
|
||||
- CLI:`openclaw message poll --channel msteams --target conversation:<id> ...`
|
||||
- CLI: `openclaw message poll --channel msteams --target conversation:<id> ...`
|
||||
- 投票由 Gateway 网关记录在 `~/.openclaw/msteams-polls.json` 中。
|
||||
- Gateway 网关必须保持在线才能记录投票。
|
||||
- 投票目前还不会自动发布结果摘要(如有需要,请检查存储文件)。
|
||||
|
||||
## 展示卡片
|
||||
|
||||
使用 `message` 工具或 CLI 向 Teams 用户或会话发送语义化展示 payload。OpenClaw 会根据通用展示契约将其渲染为 Teams Adaptive Cards。
|
||||
可使用 `message` 工具或 CLI 向 Teams 用户或会话发送语义化展示负载。OpenClaw 会根据通用展示契约将其渲染为 Teams Adaptive Cards。
|
||||
|
||||
`presentation` 参数接受语义块。提供 `presentation` 时,消息文本为可选项。
|
||||
`presentation` 参数接受语义块。当提供 `presentation` 时,消息文本为可选项。
|
||||
|
||||
**智能体工具:**
|
||||
|
||||
@ -821,7 +768,7 @@ openclaw message send --channel msteams \
|
||||
--presentation '{"title":"Hello","blocks":[{"type":"text","text":"Hello!"}]}'
|
||||
```
|
||||
|
||||
关于目标格式的详细信息,参见下方的[目标格式](#target-formats)。
|
||||
有关目标格式的详细信息,参见下文的 [目标格式](#target-formats)。
|
||||
|
||||
## 目标格式
|
||||
|
||||
@ -831,8 +778,8 @@ MSTeams 目标使用前缀来区分用户和会话:
|
||||
| ------------------- | -------------------------------- | --------------------------------------------------- |
|
||||
| 用户(按 ID) | `user:<aad-object-id>` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |
|
||||
| 用户(按名称) | `user:<display-name>` | `user:John Smith`(需要 Graph API) |
|
||||
| 群组/频道 | `conversation:<conversation-id>` | `conversation:19:abc123...@thread.tacv2` |
|
||||
| 群组/频道(原始) | `<conversation-id>` | `19:abc123...@thread.tacv2`(如果包含 `@thread`) |
|
||||
| 群组/渠道 | `conversation:<conversation-id>` | `conversation:19:abc123...@thread.tacv2` |
|
||||
| 群组/渠道(原始) | `<conversation-id>` | `19:abc123...@thread.tacv2`(如果包含 `@thread`) |
|
||||
|
||||
**CLI 示例:**
|
||||
|
||||
@ -840,10 +787,10 @@ MSTeams 目标使用前缀来区分用户和会话:
|
||||
# 按 ID 向用户发送
|
||||
openclaw message send --channel msteams --target "user:40a1a0ed-..." --message "Hello"
|
||||
|
||||
# 按显示名称向用户发送(会触发 Graph API 查找)
|
||||
# 按显示名称向用户发送(触发 Graph API 查询)
|
||||
openclaw message send --channel msteams --target "user:John Smith" --message "Hello"
|
||||
|
||||
# 发送到群聊或频道
|
||||
# 发送到群组聊天或渠道
|
||||
openclaw message send --channel msteams --target "conversation:19:abc...@thread.tacv2" --message "Hello"
|
||||
|
||||
# 向会话发送展示卡片
|
||||
@ -874,31 +821,31 @@ openclaw message send --channel msteams --target "conversation:19:abc...@thread.
|
||||
}
|
||||
```
|
||||
|
||||
注意:如果没有 `user:` 前缀,名称默认按群组/team 解析。按显示名称指定个人时,请始终使用 `user:`。
|
||||
注意:如果没有 `user:` 前缀,名称默认会按群组/团队解析。按显示名称定位个人时,请始终使用 `user:`。
|
||||
|
||||
## 主动消息
|
||||
|
||||
- 主动消息只有在用户已经交互**之后**才可发送,因为我们会在那时保存会话引用。
|
||||
- 关于 `dmPolicy` 和允许列表限制,参见 `/gateway/configuration`。
|
||||
- 只有在用户发生交互**之后**,才可以发送主动消息,因为我们会在那时存储会话引用。
|
||||
- 有关 `dmPolicy` 和允许列表控制,请参见 `/gateway/configuration`。
|
||||
|
||||
## Team 和频道 ID
|
||||
## 团队和渠道 ID
|
||||
|
||||
Teams URL 中的 `groupId` 查询参数**不是**配置中使用的 team ID。请从 URL 路径中提取 ID:
|
||||
Teams URL 中的 `groupId` 查询参数**不是**配置中使用的 team ID。应从 URL 路径中提取 ID:
|
||||
|
||||
**Team URL:**
|
||||
**团队 URL:**
|
||||
|
||||
```
|
||||
https://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...
|
||||
└────────────────────────────┘
|
||||
Team ID(对其进行 URL 解码)
|
||||
Team ID(对此进行 URL 解码)
|
||||
```
|
||||
|
||||
**频道 URL:**
|
||||
**渠道 URL:**
|
||||
|
||||
```
|
||||
https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...
|
||||
└─────────────────────────┘
|
||||
Channel ID(对其进行 URL 解码)
|
||||
Channel ID(对此进行 URL 解码)
|
||||
```
|
||||
|
||||
**用于配置:**
|
||||
@ -907,61 +854,73 @@ https://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?gr
|
||||
- Channel ID = `/channel/` 之后的路径段(URL 解码后)
|
||||
- **忽略** `groupId` 查询参数
|
||||
|
||||
## 私有频道
|
||||
## 私有渠道
|
||||
|
||||
机器人在私有频道中的支持有限:
|
||||
机器人在私有渠道中的支持有限:
|
||||
|
||||
| Feature | Standard Channels | Private Channels |
|
||||
| ---------------------------- | ----------------- | ---------------- |
|
||||
| 机器人安装 | 是 | 有限 |
|
||||
| 实时消息(webhook) | 是 | 可能无法工作 |
|
||||
| RSC 权限 | 是 | 行为可能不同 |
|
||||
| @mentions | 是 | 如果机器人可访问 |
|
||||
| Graph API 历史记录 | 是 | 是(有权限时) |
|
||||
| Feature | Standard Channels | Private Channels |
|
||||
| ---------------------------- | ----------------- | ------------------ |
|
||||
| 机器人安装 | 是 | 有限 |
|
||||
| 实时消息(webhook) | 是 | 可能无法工作 |
|
||||
| RSC 权限 | 是 | 行为可能不同 |
|
||||
| @mentions | 是 | 如果机器人可访问 |
|
||||
| Graph API 历史 | 是 | 是(有权限时) |
|
||||
|
||||
**如果私有频道无法工作,可使用以下变通方案:**
|
||||
**如果私有渠道无法工作,可采用以下变通方法:**
|
||||
|
||||
1. 对于机器人交互,使用标准频道
|
||||
1. 使用标准渠道进行机器人交互
|
||||
2. 使用私信 —— 用户始终可以直接给机器人发消息
|
||||
3. 使用 Graph API 访问历史记录(需要 `ChannelMessage.Read.All`)
|
||||
3. 使用 Graph API 获取历史访问(需要 `ChannelMessage.Read.All`)
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
- **频道中图片不显示:** 缺少 Graph 权限或管理员同意。请重新安装 Teams 应用,并完全退出后重新打开 Teams。
|
||||
- **频道中没有响应:** 默认要求提及;请设置 `channels.msteams.requireMention=false`,或按 team/频道进行配置。
|
||||
- **版本不匹配(Teams 仍显示旧 manifest):** 移除并重新添加应用,然后完全退出 Teams 以刷新。
|
||||
- **webhook 返回 401 Unauthorized:** 如果你在没有 Azure JWT 的情况下手动测试,这是预期行为 —— 说明端点可达,但身份验证失败。请使用 Azure Web Chat 正确测试。
|
||||
- **图片未显示在渠道中:** 缺少 Graph 权限或管理员同意。重新安装 Teams 应用,并完全退出/重新打开 Teams。
|
||||
- **渠道中没有响应:** 默认要求提及;设置 `channels.msteams.requireMention=false` 或按团队/渠道分别配置。
|
||||
- **版本不匹配(Teams 仍显示旧 manifest):** 删除并重新添加该应用,然后完全退出 Teams 以刷新。
|
||||
- **webhook 返回 401 Unauthorized:** 在未携带 Azure JWT 的情况下手动测试时这是预期行为 —— 说明终结点可达,但身份验证失败。请使用 Azure Web Chat 正确测试。
|
||||
|
||||
### Manifest 上传错误
|
||||
|
||||
- **“Icon file cannot be empty”:** manifest 引用了大小为 0 字节的图标文件。请创建有效的 PNG 图标(`outline.png` 为 32x32,`color.png` 为 192x192)。
|
||||
- **“webApplicationInfo.Id already in use”:** 该应用仍安装在另一个 team/chat 中。请先找到并卸载它,或等待 5–10 分钟让更改传播。
|
||||
- **上传时出现 “Something went wrong”:** 请改用 [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com) 上传,打开浏览器 DevTools(F12)→ Network 选项卡,并检查响应体中的实际错误信息。
|
||||
- **侧载失败:** 请尝试使用 “Upload an app to your org's app catalog”,而不是 “Upload a custom app” —— 这通常可以绕过侧载限制。
|
||||
- **“Icon file cannot be empty”:** manifest 引用的图标文件为 0 字节。请创建有效的 PNG 图标(`outline.png` 为 32x32,`color.png` 为 192x192)。
|
||||
- **“webApplicationInfo.Id already in use”:** 该应用仍安装在另一个团队/聊天中。先找到并卸载,或等待 5-10 分钟让变更传播。
|
||||
- **上传时出现 “Something went wrong”:** 请改用 [https://admin.teams.microsoft.com](https://admin.teams.microsoft.com) 上传,打开浏览器 DevTools(F12)→ Network 选项卡,并检查响应体中的实际错误。
|
||||
- **旁加载失败:** 尝试使用 “Upload an app to your org's app catalog” 而不是 “Upload a custom app” —— 这通常可以绕过旁加载限制。
|
||||
|
||||
### RSC 权限不起作用
|
||||
|
||||
1. 验证 `webApplicationInfo.id` 是否与你机器人的 App ID 完全一致
|
||||
2. 重新上传应用,并在 team/chat 中重新安装
|
||||
2. 重新上传应用,并在团队/聊天中重新安装
|
||||
3. 检查你的组织管理员是否阻止了 RSC 权限
|
||||
4. 确认你使用了正确的作用域:team 使用 `ChannelMessage.Read.Group`,群聊使用 `ChatMessage.Read.Chat`
|
||||
4. 确认你使用了正确的范围:团队用 `ChannelMessage.Read.Group`,群组聊天用 `ChatMessage.Read.Chat`
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Create Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot 设置指南
|
||||
- [创建 Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot 设置指南
|
||||
- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - 创建/管理 Teams 应用
|
||||
- [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema) - Teams 应用 manifest schema
|
||||
- [Receive channel messages with RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc) - 使用 RSC 接收频道消息
|
||||
- [RSC permissions reference](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent) - RSC 权限参考
|
||||
- [Teams bot file handling](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4)(频道/群组需要 Graph)
|
||||
- [Proactive messaging](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages) - 主动消息
|
||||
- [Teams 应用 manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)
|
||||
- [使用 RSC 接收渠道消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)
|
||||
- [RSC 权限参考](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent)
|
||||
- [Teams 机器人文件处理](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4)(渠道/群组需要 Graph)
|
||||
- [主动消息](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [Channels Overview](/zh-CN/channels) — 所有支持的渠道
|
||||
- [Pairing](/zh-CN/channels/pairing) — 私信身份验证和配对流程
|
||||
- [Groups](/zh-CN/channels/groups) — 群聊行为和提及限制
|
||||
- [Channel Routing](/zh-CN/channels/channel-routing) — 消息的会话路由
|
||||
- [Security](/zh-CN/gateway/security) — 访问模型和加固
|
||||
<CardGroup cols={2}>
|
||||
<Card title="渠道概览" icon="list" href="/zh-CN/channels">
|
||||
所有受支持的渠道。
|
||||
</Card>
|
||||
<Card title="配对" icon="link" href="/zh-CN/channels/pairing">
|
||||
私信身份验证与配对流程。
|
||||
</Card>
|
||||
<Card title="群组" icon="users" href="/zh-CN/channels/groups">
|
||||
群组聊天行为与提及控制。
|
||||
</Card>
|
||||
<Card title="渠道路由" icon="route" href="/zh-CN/channels/channel-routing">
|
||||
消息的会话路由。
|
||||
</Card>
|
||||
<Card title="安全" icon="shield" href="/zh-CN/gateway/security">
|
||||
访问模型与加固。
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
126
docs/zh-CN/ci.md
126
docs/zh-CN/ci.md
@ -1,27 +1,27 @@
|
||||
---
|
||||
read_when:
|
||||
- 你需要了解某个 CI 作业为什么运行了,或者为什么没有运行
|
||||
- 你需要了解某个 CI 作业为什么会运行或没有运行
|
||||
- 你正在调试失败的 GitHub Actions 检查
|
||||
summary: CI 作业图、作用域门禁,以及本地等效命令
|
||||
summary: CI 作业图、范围门控,以及本地命令对应项
|
||||
title: CI 流水线
|
||||
x-i18n:
|
||||
generated_at: "2026-04-23T19:45:54Z"
|
||||
generated_at: "2026-04-23T20:12:13Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 3e250c90d9be13dc25a0b028de5d72cf821387e33c0965cac7b935579e3c6ae7
|
||||
source_hash: 601bd170afc217e7e9841c3cc358b941c9706b2cec57f568c0b583888ac0b4e7
|
||||
source_path: ci.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# CI 流水线
|
||||
|
||||
CI 会在每次推送到 `main` 以及每个拉取请求时运行。它使用智能作用域划分,在仅有无关区域变更时跳过高开销作业。
|
||||
CI 会在每次推送到 `main` 以及每个拉取请求上运行。它使用智能范围判定,以便在只改动了无关区域时跳过昂贵作业。
|
||||
|
||||
QA Lab 在主智能作用域工作流之外有专门的 CI 通道。`Parity gate` 工作流会在匹配的 PR 变更以及手动触发时运行;它会构建私有 QA 运行时,并比较模拟的 GPT-5.4 和 Opus 4.6 智能体包。`QA-Lab - All Lanes` 工作流会在 `main` 上每晚运行,并支持手动触发;它会将模拟 parity gate、实时 Matrix 通道和实时 Telegram 通道作为并行作业扇出运行。实时作业使用 `qa-live-shared` 环境,而 Telegram 通道使用 Convex 租约。`OpenClaw Release Checks` 也会在发布批准前运行相同的 QA Lab 通道。
|
||||
QA Lab 在主智能范围工作流之外有专用的 CI 通道。`Parity gate` 工作流会在匹配的 PR 变更和手动触发时运行;它会构建私有 QA 运行时,并比较 mock GPT-5.4 和 Opus 4.6 智能体包。`QA-Lab - All Lanes` 工作流会在 `main` 上每晚运行,也支持手动触发;它会将 mock parity gate、实时 Matrix 通道和实时 Telegram 通道作为并行作业扇出运行。实时作业使用 `qa-live-shared` environment,Telegram 通道使用 Convex leases。`OpenClaw Release Checks` 也会在发布批准前运行相同的 QA Lab 通道。
|
||||
|
||||
`Duplicate PRs After Merge` 工作流是一个供维护者使用的手动工作流,用于合并后的重复项清理。它默认使用 dry-run,只有在 `apply=true` 时才会关闭显式列出的 PR。在修改 GitHub 之前,它会验证已合并的 PR 确实已合并,并验证每个重复 PR 要么引用了相同的 issue,要么存在重叠的变更 hunk。
|
||||
`Duplicate PRs After Merge` 工作流是一个供维护者在合并后进行重复 PR 清理的手动工作流。它默认是 dry-run,只有在 `apply=true` 时才会关闭明确列出的 PR。在修改 GitHub 之前,它会验证已落地的 PR 确实已合并,并检查每个重复 PR 是否具有共享的引用 issue,或是否存在重叠的改动 hunk。
|
||||
|
||||
`Test Performance Agent` 工作流是一个事件驱动的 Codex 维护通道,用于处理慢测试。它没有纯定时调度:`main` 上一次由非机器人触发且成功的 push CI 运行可以触发它,但如果当天 UTC 时间内已有另一次 workflow-run 调用已经运行或正在运行,它就会跳过。手动触发会绕过这一每日活动门禁。该通道会构建完整测试套件的分组 Vitest 性能报告,允许 Codex 仅进行小范围且不降低覆盖率的测试性能修复,然后重新运行完整测试套件报告,并拒绝任何会降低通过基线测试数量的变更。如果基线中已有失败测试,Codex 只能修复明显的失败项,并且在提交任何内容之前,agent 运行后的完整测试套件报告必须通过。
|
||||
`Test Performance Agent` 工作流是一个由事件驱动的 Codex 维护通道,用于处理慢测试。它没有纯定时调度:`main` 上一次成功的、非机器人触发的 push CI 运行可以触发它,但如果当天 UTC 已经有另一个 workflow-run 调用已经运行或正在运行,它就会跳过。手动触发会绕过这个每日活动门控。该通道会构建一份完整测试套件的分组 Vitest 性能报告,让 Codex 只能进行小范围且不降低覆盖率的测试性能修复,然后重新运行完整测试套件报告,并拒绝任何会降低通过基线测试数量的更改。如果基线本身存在失败测试,Codex 只能修复明显的失败,并且只有在 agent 处理后的完整测试套件报告通过时,才会提交任何内容。它使用 GitHub 托管的 Ubuntu,这样 Codex action 就能与 docs agent 保持相同的 drop-sudo 安全策略。
|
||||
|
||||
```bash
|
||||
gh workflow run duplicate-after-merge.yml \
|
||||
@ -32,86 +32,86 @@ gh workflow run duplicate-after-merge.yml \
|
||||
|
||||
## 作业概览
|
||||
|
||||
| 作业 | 目的 | 运行时机 |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------- |
|
||||
| `preflight` | 检测是否仅为文档变更、变更的作用域、变更的扩展,并构建 CI 清单 | 所有非草稿 push 和 PR |
|
||||
| `security-scm-fast` | 通过 `zizmor` 进行私钥检测和工作流审计 | 所有非草稿 push 和 PR |
|
||||
| `security-dependency-audit` | 针对 npm advisories 进行无依赖的生产 lockfile 审计 | 所有非草稿 push 和 PR |
|
||||
| `security-fast` | 快速安全作业的必需聚合作业 | 所有非草稿 push 和 PR |
|
||||
| `build-artifacts` | 构建 `dist/`、Control UI、已构建产物检查,以及可复用的下游产物 | 与 Node 相关的变更 |
|
||||
| `checks-fast-core` | 快速 Linux 正确性通道,例如 bundled/plugin-contract/protocol 检查 | 与 Node 相关的变更 |
|
||||
| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | 与 Node 相关的变更 |
|
||||
| `checks-node-extensions` | 覆盖整个扩展套件的完整 bundled-plugin 测试分片 | 与 Node 相关的变更 |
|
||||
| `checks-node-core-test` | Core Node 测试分片,不包括渠道、bundled、contract 和扩展通道 | 与 Node 相关的变更 |
|
||||
| `extension-fast` | 仅针对已变更 bundled plugin 的定向测试 | 带有扩展变更的拉取请求 |
|
||||
| `check` | 分片后的主本地门禁等效项:生产类型、lint、guard、测试类型和严格 smoke | 与 Node 相关的变更 |
|
||||
| `check-additional` | 架构、边界、扩展表面 guard、包边界以及 gateway-watch 分片 | 与 Node 相关的变更 |
|
||||
| `build-smoke` | 已构建 CLI 的 smoke 测试和启动内存 smoke | 与 Node 相关的变更 |
|
||||
| `checks` | 用于已构建产物渠道测试的验证器,以及仅在 push 时运行的 Node 22 兼容性检查 | 与 Node 相关的变更 |
|
||||
| `check-docs` | 文档格式、lint 和损坏链接检查 | 文档有变更时 |
|
||||
| `skills-python` | 针对 Python 支持的 Skills 运行 Ruff + pytest | 与 Python Skills 相关的变更 |
|
||||
| `checks-windows` | Windows 专用测试通道 | 与 Windows 相关的变更 |
|
||||
| `macos-node` | 使用共享已构建产物的 macOS TypeScript 测试通道 | 与 macOS 相关的变更 |
|
||||
| `macos-swift` | macOS 应用的 Swift lint、构建和测试 | 与 macOS 相关的变更 |
|
||||
| `android` | 两种 flavor 的 Android 单元测试,以及一个 debug APK 构建 | 与 Android 相关的变更 |
|
||||
| `test-performance-agent` | 在可信活动之后每日运行的 Codex 慢测试优化 | 主 CI 成功后或手动触发 |
|
||||
| 作业 | 目的 | 运行时机 |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------ |
|
||||
| `preflight` | 检测是否仅有文档变更、已变更范围、已变更扩展,并构建 CI 清单 | 在所有非草稿 push 和 PR 上始终运行 |
|
||||
| `security-scm-fast` | 通过 `zizmor` 进行私钥检测和工作流审计 | 在所有非草稿 push 和 PR 上始终运行 |
|
||||
| `security-dependency-audit` | 针对 npm advisories 进行无依赖的生产 lockfile 审计 | 在所有非草稿 push 和 PR 上始终运行 |
|
||||
| `security-fast` | 快速安全作业的必需聚合作业 | 在所有非草稿 push 和 PR 上始终运行 |
|
||||
| `build-artifacts` | 构建 `dist/`、Control UI、构建产物检查以及可复用的下游产物 | 与 Node 相关的变更 |
|
||||
| `checks-fast-core` | 快速 Linux 正确性通道,例如 bundled/plugin-contract/protocol 检查 | 与 Node 相关的变更 |
|
||||
| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | 与 Node 相关的变更 |
|
||||
| `checks-node-extensions` | 针对扩展套件的完整内置插件测试分片 | 与 Node 相关的变更 |
|
||||
| `checks-node-core-test` | Core Node 测试分片,不包括渠道、内置、契约和扩展通道 | 与 Node 相关的变更 |
|
||||
| `extension-fast` | 仅针对已变更内置插件的聚焦测试 | 带有扩展变更的拉取请求 |
|
||||
| `check` | 分片的主本地门控等价项:生产类型、lint、守卫、测试类型以及严格 smoke | 与 Node 相关的变更 |
|
||||
| `check-additional` | 架构、边界、扩展表面守卫、包边界以及 gateway-watch 分片 | 与 Node 相关的变更 |
|
||||
| `build-smoke` | 已构建 CLI 的 smoke 测试和启动内存 smoke | 与 Node 相关的变更 |
|
||||
| `checks` | 已构建产物渠道测试的校验器,以及仅在 push 上运行的 Node 22 兼容性检查 | 与 Node 相关的变更 |
|
||||
| `check-docs` | 文档格式、lint 和断链检查 | 文档有变更时 |
|
||||
| `skills-python` | 面向 Python 支持的 Skills 的 Ruff + pytest | 与 Python Skills 相关的变更 |
|
||||
| `checks-windows` | Windows 专用测试通道 | 与 Windows 相关的变更 |
|
||||
| `macos-node` | 使用共享构建产物的 macOS TypeScript 测试通道 | 与 macOS 相关的变更 |
|
||||
| `macos-swift` | macOS app 的 Swift lint、构建和测试 | 与 macOS 相关的变更 |
|
||||
| `android` | 两种 flavor 的 Android 单元测试,以及一次 debug APK 构建 | 与 Android 相关的变更 |
|
||||
| `test-performance-agent` | 在可信活动之后每日运行的 Codex 慢测试优化 | `main` CI 成功后或手动触发 |
|
||||
|
||||
## 快速失败顺序
|
||||
|
||||
作业按顺序排列,以便低成本检查先失败,避免高成本作业继续运行:
|
||||
作业的排列顺序经过设计,使便宜的检查能在昂贵作业开始前先失败:
|
||||
|
||||
1. `preflight` 决定哪些通道会存在。`docs-scope` 和 `changed-scope` 逻辑是该作业中的步骤,而不是独立作业。
|
||||
2. `security-scm-fast`、`security-dependency-audit`、`security-fast`、`check`、`check-additional`、`check-docs` 和 `skills-python` 会快速失败,而无需等待更重的产物和平台矩阵作业。
|
||||
3. `build-artifacts` 与快速 Linux 通道并行运行,这样下游消费者可以在共享构建准备好后立即开始。
|
||||
4. 之后扇出更重的平台和运行时通道:`checks-fast-core`、`checks-fast-contracts-channels`、`checks-node-extensions`、`checks-node-core-test`、仅限 PR 的 `extension-fast`、`checks`、`checks-windows`、`macos-node`、`macos-swift` 和 `android`。
|
||||
1. `preflight` 决定哪些通道实际存在。`docs-scope` 和 `changed-scope` 逻辑是这个作业中的步骤,而不是独立作业。
|
||||
2. `security-scm-fast`、`security-dependency-audit`、`security-fast`、`check`、`check-additional`、`check-docs` 和 `skills-python` 会快速失败,而无需等待更重的构建产物和平台矩阵作业。
|
||||
3. `build-artifacts` 与快速 Linux 通道并行运行,这样下游消费者就能在共享构建完成后立即开始。
|
||||
4. 更重的平台和运行时通道会在此之后扇出:`checks-fast-core`、`checks-fast-contracts-channels`、`checks-node-extensions`、`checks-node-core-test`、仅 PR 的 `extension-fast`、`checks`、`checks-windows`、`macos-node`、`macos-swift` 和 `android`。
|
||||
|
||||
作用域逻辑位于 `scripts/ci-changed-scope.mjs`,并由 `src/scripts/ci-changed-scope.test.ts` 中的单元测试覆盖。
|
||||
CI 工作流编辑会验证 Node CI 图以及工作流 lint,但不会仅因自身变更而强制运行 Windows、Android 或 macOS 原生构建;这些平台通道仍然只对平台源码变更生效。
|
||||
Windows Node 检查的作用域仅限于 Windows 专用的进程/路径包装器、npm/pnpm/UI 运行器辅助工具、包管理器配置,以及执行该通道的 CI 工作流表面;无关的源码、plugin、安装 smoke 和纯测试变更仍留在 Linux Node 通道中,这样它们就不会为了已由常规测试分片覆盖的内容而占用一个 16 vCPU 的 Windows worker。
|
||||
单独的 `install-smoke` 工作流会通过它自己的 `preflight` 作业复用相同的作用域脚本。它根据更窄的 changed-smoke 信号计算 `run_install_smoke`,因此 Docker/安装 smoke 会针对安装、打包、与容器相关的变更、bundled extension 生产变更,以及 Docker smoke 作业所覆盖的 core plugin/channel/Gateway 网关/插件 SDK 表面运行。纯测试和纯文档编辑不会占用 Docker worker。其 QR package smoke 会强制 Docker 的 `pnpm install` 层重新运行,同时保留 BuildKit 的 pnpm store 缓存,因此它仍能覆盖安装过程,而无需每次运行都重新下载依赖。它的 gateway-network e2e 会复用该作业前面构建的运行时镜像,因此它增加了真实的 container-to-container WebSocket 覆盖,而无需新增一次 Docker 构建。本地 `test:docker:all` 会预先构建一个共享的 live-test 镜像和一个共享的 `scripts/e2e/Dockerfile` built-app 镜像,然后在 `OPENCLAW_SKIP_DOCKER_BUILD=1` 下并行运行 live/E2E smoke 通道;默认并发数为 4,可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整。本地聚合作业默认会在首次失败后停止调度新的池化通道,并且每个通道都有一个 120 分钟的超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的通道会在并行池之后独占运行。可复用的 live/E2E 工作流也遵循共享镜像模式:它会在 Docker 矩阵之前构建并推送一个带 SHA 标签的 GHCR Docker E2E 镜像,然后在 `OPENCLAW_SKIP_DOCKER_BUILD=1` 下运行矩阵。定时的 live/E2E 工作流每天运行完整的发布路径 Docker 套件。QR 和 installer Docker 测试保持各自以安装为重点的 Dockerfile。另有一个独立的 `docker-e2e-fast` 作业,会在 120 秒命令超时内运行有界的 bundled-plugin Docker 配置:setup-entry 依赖修复以及合成的 bundled-loader 故障隔离。完整的 bundled 更新/渠道矩阵仍然是手动/完整套件,因为它会反复执行真实的 npm update 和 doctor repair 流程。
|
||||
范围逻辑位于 `scripts/ci-changed-scope.mjs`,并由 `src/scripts/ci-changed-scope.test.ts` 中的单元测试覆盖。
|
||||
CI 工作流编辑会验证 Node CI 作业图以及工作流 lint,但仅凭这些编辑本身不会强制运行 Windows、Android 或 macOS 原生构建;这些平台通道仍然只针对对应平台源码变更而运行。
|
||||
Windows Node 检查的范围限定在 Windows 专用的进程/路径包装器、npm/pnpm/UI runner 辅助、包管理器配置,以及执行该通道的 CI 工作流表面;无关的源码、插件、install-smoke 和仅测试改动仍会留在 Linux Node 通道中,因此不会为已被常规测试分片覆盖的内容占用一个 16-vCPU 的 Windows worker。
|
||||
独立的 `install-smoke` 工作流通过其自己的 `preflight` 作业复用同一个范围脚本。它根据更窄的 changed-smoke 信号计算 `run_install_smoke`,因此 Docker/install smoke 会在安装、打包、与容器相关的变更、内置扩展生产变更,以及 Docker smoke 作业所覆盖的核心 plugin/channel/Gateway 网关/Plugin SDK 表面发生变更时运行。仅测试和仅文档的编辑不会占用 Docker workers。它的 QR 包 smoke 会强制 Docker `pnpm install` 层重新运行,同时保留 BuildKit pnpm store cache,因此仍然能够验证安装流程,而不必在每次运行时重新下载依赖。它的 gateway-network e2e 会复用该作业前面构建的运行时镜像,因此它在不增加额外 Docker 构建的情况下,增加了真实的 container-to-container WebSocket 覆盖。本地 `test:docker:all` 会预先构建一个共享的 live-test 镜像和一个共享的 `scripts/e2e/Dockerfile` built-app 镜像,然后在设置 `OPENCLAW_SKIP_DOCKER_BUILD=1` 的情况下并行运行 live/E2E smoke 通道;默认并发数为 4,可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整。本地聚合作业默认会在第一次失败后停止调度新的池化通道,并且每个通道都有一个 120 分钟的超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的通道会在并行池之后以独占方式运行。可复用的 live/E2E 工作流也采用共享镜像模式:在 Docker 矩阵之前先构建并推送一个带 SHA 标签的 GHCR Docker E2E 镜像,然后在矩阵中设置 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行。定时的 live/E2E 工作流每天都会运行完整的发布路径 Docker 套件。QR 和 installer Docker 测试保留各自以安装为重点的 Dockerfile。另有一个独立的 `docker-e2e-fast` 作业,会在 120 秒命令超时限制下运行受限的内置插件 Docker 配置:setup-entry 依赖修复,以及 synthetic bundled-loader failure isolation。完整的 bundled update/channel 矩阵仍然保持为手动/完整套件,因为它会执行重复的真实 npm update 和 doctor 修复流程。
|
||||
|
||||
本地 changed-lane 逻辑位于 `scripts/changed-lanes.mjs`,由 `scripts/check-changed.mjs` 执行。这个本地门禁在架构边界方面比宽泛的 CI 平台作用域更严格:core 生产变更会运行 core 生产类型检查以及 core 测试,core 纯测试变更只运行 core 测试类型检查/测试,extension 生产变更会运行 extension 生产类型检查以及 extension 测试,而 extension 纯测试变更只运行 extension 测试类型检查/测试。公共插件 SDK 或 plugin-contract 变更会扩展为 extension 验证,因为扩展依赖这些 core 契约。仅发布元数据的版本变更会运行定向的版本/配置/root-dependency 检查。未知的根目录/配置变更会以安全优先方式落到所有通道。
|
||||
本地 changed-lane 逻辑位于 `scripts/changed-lanes.mjs`,并由 `scripts/check-changed.mjs` 执行。这个本地门控在架构边界方面比宽泛的 CI 平台范围更严格:核心生产改动会运行核心生产 typecheck 加核心测试,核心仅测试改动只运行核心测试 typecheck/测试,扩展生产改动会运行扩展生产 typecheck 加扩展测试,而扩展仅测试改动只运行扩展测试 typecheck/测试。公共 Plugin SDK 或 plugin-contract 改动会扩展到扩展校验,因为扩展依赖这些核心契约。仅有发布元数据的版本号变更会运行定向的版本/配置/根依赖检查。未知的根目录/配置改动会以安全优先方式落到所有通道。
|
||||
|
||||
在 push 上,`checks` 矩阵会增加一个仅在 push 时运行的 `compat-node22` 通道。在拉取请求上,该通道会被跳过,矩阵会保持聚焦于常规测试/渠道通道。
|
||||
在 push 上,`checks` 矩阵会添加仅限 push 的 `compat-node22` 通道。在拉取请求上,这个通道会被跳过,矩阵将聚焦于常规测试/渠道通道。
|
||||
|
||||
最慢的 Node 测试族会被拆分或平衡,以便每个作业都保持较小规模,同时避免过度预留 runner:渠道契约按权重拆成三个分片,bundled plugin 测试会在六个扩展 worker 间做负载均衡,小型 core 单元通道会成对组合,auto-reply 以三个平衡 worker 运行,而不是六个过小的 worker,agentic Gateway 网关/plugin 配置则分布到现有的仅源码 agentic Node 作业中,而不是等待已构建产物。大范围的浏览器、QA、媒体和杂项 plugin 测试使用各自专用的 Vitest 配置,而不是共享的 plugin 通用兜底配置。扩展分片作业会以单个 Vitest worker 和更大的 Node 堆大小串行运行 plugin 配置组,这样导入开销较大的 plugin 批次就不会让小型 CI runner 过载。宽泛的 agents 通道使用共享的 Vitest 文件级并行调度器,因为它的瓶颈主要在导入/调度,而不是由某个单独的慢测试文件主导。`runtime-config` 会和 infra core-runtime 分片一起运行,以避免共享 runtime 分片独自拖尾。`check-additional` 会把 package-boundary 的编译/canary 工作保持在一起,并将 runtime topology architecture 与 gateway watch 覆盖拆开;boundary guard 分片会在一个作业内部并发运行其小型且相互独立的 guard。Gateway watch、渠道测试以及 core support-boundary 分片会在 `dist/` 和 `dist-runtime/` 已构建完成后,于 `build-artifacts` 内部并发运行;这样既保留了它们原有的检查名称作为轻量验证作业,又避免额外占用两个 Blacksmith worker 和第二条产物消费者队列。
|
||||
最慢的 Node 测试家族会被拆分或均衡,以便每个作业都保持较小规模,同时避免过度占用 runner:渠道契约以三个加权分片运行,内置插件测试在六个扩展 worker 之间均衡分配,小型核心单元通道会成对组合,auto-reply 改为三个均衡 worker 而不是六个零碎 worker,agentic Gateway 网关/plugin 配置则分散到现有的仅源码 agentic Node 作业中,而不是等待构建产物。范围较广的 browser、QA、media 和杂项插件测试使用各自专用的 Vitest 配置,而不是共享的插件兜底配置。扩展分片作业会以单个 Vitest worker 和更大的 Node heap 串行运行插件配置组,这样导入开销大的插件批次就不会让小型 CI runner 过载。广泛的 agents 通道使用共享的 Vitest 文件级并行调度器,因为它的瓶颈主要在导入/调度,而不是由某个单独的慢测试文件主导。`runtime-config` 会与 infra core-runtime 分片一起运行,以避免共享 runtime 分片独自拖尾。`check-additional` 会将 package-boundary compile/canary 工作保持在一起,并将 runtime topology architecture 与 gateway watch 覆盖拆开;boundary guard 分片会在单个作业内并发运行其小型且相互独立的守卫检查。Gateway 网关 watch、channel 测试以及核心 support-boundary 分片会在 `dist/` 和 `dist-runtime/` 已经构建完成后,于 `build-artifacts` 中并发运行;这样既保留了它们原有的检查名称作为轻量校验作业,又避免了额外两个 Blacksmith worker 和第二个 artifact-consumer 队列。
|
||||
|
||||
Android CI 会同时运行 `testPlayDebugUnitTest` 和 `testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的 source set 或 manifest;它的单元测试通道仍会使用 SMS/call-log BuildConfig 标志编译该 flavor,同时避免在每次与 Android 相关的 push 上重复执行一个 debug APK 打包作业。
|
||||
Android CI 会同时运行 `testPlayDebugUnitTest` 和 `testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的 source set 或 manifest;它的单元测试通道仍会使用 SMS/call-log BuildConfig 标志编译该 flavor,同时避免在每次与 Android 相关的 push 上重复运行一个 debug APK 打包作业。
|
||||
|
||||
`extension-fast` 仅在 PR 上运行,因为 push 运行已经会执行完整的 bundled plugin 分片。这样既能为评审提供已变更 plugin 的反馈,又不会在 `main` 上为 `checks-node-extensions` 已经覆盖的内容额外占用一个 Blacksmith worker。
|
||||
`extension-fast` 仅在 PR 上运行,因为 push 运行已经会执行完整的内置插件分片。这样既能为评审提供已变更插件的反馈,又不会在 `main` 上为 `checks-node-extensions` 已经覆盖的内容额外占用一个 Blacksmith worker。
|
||||
|
||||
当同一 PR 或 `main` ref 上有较新的 push 到达时,GitHub 可能会把被替代的作业标记为 `cancelled`。除非同一 ref 上最新的一次运行也失败,否则应将这视为 CI 噪声。聚合分片检查使用 `!cancelled() && always()`,这样它们仍会正常报告分片失败,但不会在整个工作流已被替代后继续排队。
|
||||
当同一个 PR 或 `main` ref 上有更新的 push 到达时,GitHub 可能会将被替代的作业标记为 `cancelled`。除非同一 ref 的最新运行也失败,否则应将这视为 CI 噪音。聚合分片检查使用 `!cancelled() && always()`,因此它们仍会正常报告分片失败,但在整个工作流已经被替代后不会再排队运行。
|
||||
|
||||
CI 并发 key 采用版本化形式(`CI-v7-*`),这样 GitHub 端旧队列组中的 zombie 就不会无限期阻塞较新的 main 运行。
|
||||
CI 并发键是带版本号的(`CI-v7-*`),因此 GitHub 端旧队列组中的僵尸任务不会无限期阻塞较新的 `main` 运行。
|
||||
|
||||
## Runner
|
||||
|
||||
| Runner | 作业 |
|
||||
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ubuntu-24.04` | `preflight`、快速安全作业及其聚合(`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速 protocol/contract/bundled 检查、分片的渠道契约检查、除 lint 以外的 `check` 分片、`check-additional` 分片及聚合、Node 测试聚合验证器、文档检查、Python Skills、workflow-sanity、labeler、auto-response;install-smoke 的 preflight 也使用 GitHub 托管的 Ubuntu,这样 Blacksmith 矩阵可以更早进入排队 |
|
||||
| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`、build-smoke、Linux Node 测试分片、bundled plugin 测试分片、`android` |
|
||||
| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`,它对 CPU 仍然足够敏感,以至于 8 vCPU 节省下来的成本不如它带来的损失;install-smoke Docker 构建也是如此,32 vCPU 的排队时间成本高于其收益 |
|
||||
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
|
||||
| `blacksmith-6vcpu-macos-latest` | 在 `openclaw/openclaw` 上运行的 `macos-node`;fork 会回退到 `macos-latest` |
|
||||
| `blacksmith-12vcpu-macos-latest` | 在 `openclaw/openclaw` 上运行的 `macos-swift`;fork 会回退到 `macos-latest` |
|
||||
| Runner | 作业 |
|
||||
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ubuntu-24.04` | `preflight`、快速安全作业及其聚合作业(`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速 protocol/contract/bundled 检查、分片的渠道契约检查、除 lint 外的 `check` 分片、`check-additional` 分片及其聚合、Node 测试聚合校验器、文档检查、Python Skills、workflow-sanity、labeler、auto-response;install-smoke preflight 也使用 GitHub 托管的 Ubuntu,这样 Blacksmith 矩阵可以更早进入队列 |
|
||||
| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`、build-smoke、Linux Node 测试分片、内置插件测试分片、`android` |
|
||||
| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`,它仍然对 CPU 足够敏感,以至于 8 vCPU 的成本高于节省;install-smoke Docker 构建,在这里 32 vCPU 的排队时间成本高于节省 |
|
||||
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
|
||||
| `blacksmith-6vcpu-macos-latest` | `openclaw/openclaw` 上的 `macos-node`;fork 会回退到 `macos-latest` |
|
||||
| `blacksmith-12vcpu-macos-latest` | `openclaw/openclaw` 上的 `macos-swift`;fork 会回退到 `macos-latest` |
|
||||
|
||||
## 本地等效命令
|
||||
## 本地对应项
|
||||
|
||||
```bash
|
||||
pnpm changed:lanes # 查看 origin/main...HEAD 的本地 changed-lane 分类器
|
||||
pnpm check:changed # 智能本地门禁:按边界通道运行变更相关的类型检查/lint/测试
|
||||
pnpm check # 快速本地门禁:生产 tsgo + 分片 lint + 并行快速 guard
|
||||
pnpm changed:lanes # 检查 origin/main...HEAD 的本地 changed-lane 分类器
|
||||
pnpm check:changed # 智能本地门控:按边界通道运行已变更的 typecheck/lint/测试
|
||||
pnpm check # 快速本地门控:生产 tsgo + 分片 lint + 并行快速守卫
|
||||
pnpm check:test-types
|
||||
pnpm check:timed # 同一套门禁,并输出各阶段耗时
|
||||
pnpm check:timed # 相同门控,但带每个阶段的耗时统计
|
||||
pnpm build:strict-smoke
|
||||
pnpm check:architecture
|
||||
pnpm test:gateway:watch-regression
|
||||
pnpm test # vitest 测试
|
||||
pnpm test:channels
|
||||
pnpm test:contracts:channels
|
||||
pnpm check:docs # 文档格式 + lint + 损坏链接检查
|
||||
pnpm build # 当 CI 产物/build-smoke 通道相关时,构建 dist
|
||||
node scripts/ci-run-timings.mjs <run-id> # 汇总总耗时、排队耗时和最慢作业
|
||||
pnpm check:docs # 文档 format + lint + 断链检查
|
||||
pnpm build # 当 CI artifact/build-smoke 通道相关时,构建 dist
|
||||
node scripts/ci-run-timings.mjs <run-id> # 汇总总耗时、排队时间和最慢作业
|
||||
node scripts/ci-run-timings.mjs --recent 10 # 对比最近成功的 main CI 运行
|
||||
pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json
|
||||
pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json
|
||||
|
||||
@ -1,30 +1,30 @@
|
||||
---
|
||||
read_when:
|
||||
- 运行或调试 Gateway 网关进程
|
||||
summary: Gateway 网关服务、生命周期与运维的操作手册
|
||||
title: Gateway 网关操作手册
|
||||
summary: Gateway 网关服务、生命周期与运维手册
|
||||
title: Gateway 网关运行手册
|
||||
x-i18n:
|
||||
generated_at: "2026-04-20T06:30:59Z"
|
||||
generated_at: "2026-04-23T20:12:15Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: e1004cdd43b1db6794f3ca83da38dbdb231a1976329d9d6d851e2b02405278d8
|
||||
source_hash: a024b1250c9e6badd1c72cccbd6d568ea510db829be79ced19a12f293dbe6f2a
|
||||
source_path: gateway/index.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Gateway 网关操作手册
|
||||
# Gateway 网关运行手册
|
||||
|
||||
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维操作。
|
||||
将此页面用于 Gateway 网关服务的第 1 天启动和第 2 天运维。
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="深度故障排除" icon="siren" href="/zh-CN/gateway/troubleshooting">
|
||||
按症状优先的诊断,提供精确的命令步骤和日志特征。
|
||||
按症状优先的诊断指南,包含精确的命令步骤和日志特征。
|
||||
</Card>
|
||||
<Card title="配置" icon="sliders" href="/zh-CN/gateway/configuration">
|
||||
面向任务的设置指南 + 完整配置参考。
|
||||
</Card>
|
||||
<Card title="密钥管理" icon="key-round" href="/zh-CN/gateway/secrets">
|
||||
`SecretRef` 合约、运行时快照行为,以及迁移/重新加载操作。
|
||||
SecretRef 合约、运行时快照行为,以及迁移/重载操作。
|
||||
</Card>
|
||||
<Card title="密钥计划合约" icon="shield-check" href="/zh-CN/gateway/secrets-plan-contract">
|
||||
精确的 `secrets apply` 目标/路径规则,以及仅引用的 auth-profile 行为。
|
||||
@ -38,9 +38,9 @@ x-i18n:
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789
|
||||
# debug/trace 镜像输出到 stdio
|
||||
# 将 debug/trace 镜像输出到 stdio
|
||||
openclaw gateway --port 18789 --verbose
|
||||
# 强制终止所选端口上的监听器,然后启动
|
||||
# 强制终止所选端口上的监听进程,然后启动
|
||||
openclaw gateway --force
|
||||
```
|
||||
|
||||
@ -54,7 +54,7 @@ openclaw status
|
||||
openclaw logs --follow
|
||||
```
|
||||
|
||||
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期匹配的 `Capability: ...`。当你需要读取范围的 RPC 证明,而不仅仅是可达性时,请使用 `openclaw gateway status --require-rpc`。
|
||||
健康基线:`Runtime: running`、`Connectivity probe: ok`,以及与你预期一致的 `Capability: ...`。当你需要读取范围的 RPC 证明,而不仅仅是可达性时,使用 `openclaw gateway status --require-rpc`。
|
||||
|
||||
</Step>
|
||||
|
||||
@ -64,35 +64,35 @@ openclaw logs --follow
|
||||
openclaw channels status --probe
|
||||
```
|
||||
|
||||
当 Gateway 网关可达时,这会按账户运行实时的渠道探测和可选审计。
|
||||
如果 Gateway 网关不可达,CLI 会回退到仅基于配置的渠道摘要,
|
||||
当 Gateway 网关可达时,这会运行按账户划分的实时渠道探测和可选审计。
|
||||
如果 Gateway 网关不可达,CLI 会回退为仅配置的渠道摘要,
|
||||
而不是实时探测输出。
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Note>
|
||||
Gateway 网关配置重载会监视当前激活的配置文件路径(从 profile/state 默认值解析,或在设置时使用 `OPENCLAW_CONFIG_PATH`)。
|
||||
默认模式为 `gateway.reload.mode="hybrid"`。
|
||||
首次成功加载后,运行中的进程会提供当前激活的内存配置快照;成功重载后会原子性地替换该快照。
|
||||
Gateway 网关配置重载会监视活动配置文件路径(从 profile/state 默认值解析,或者在设置了 `OPENCLAW_CONFIG_PATH` 时使用该路径)。
|
||||
默认模式是 `gateway.reload.mode="hybrid"`。
|
||||
首次成功加载后,运行中的进程会提供活动的内存中配置快照;成功重载后会以原子方式替换该快照。
|
||||
</Note>
|
||||
|
||||
## 运行时模型
|
||||
|
||||
- 一个始终运行的进程,用于路由、控制平面和渠道连接。
|
||||
- 一个用于以下内容的单一复用端口:
|
||||
- 单个复用端口用于:
|
||||
- WebSocket 控制/RPC
|
||||
- HTTP API、OpenAI 兼容接口(`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`)
|
||||
- Control UI 和 hooks
|
||||
- HTTP API,兼容 OpenAI(`/v1/models`、`/v1/embeddings`、`/v1/chat/completions`、`/v1/responses`、`/tools/invoke`)
|
||||
- 控制 UI 和 hooks
|
||||
- 默认绑定模式:`loopback`。
|
||||
- 默认需要认证。共享密钥设置使用
|
||||
- 默认要求身份验证。共享密钥设置使用
|
||||
`gateway.auth.token` / `gateway.auth.password`(或
|
||||
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),而非 `loopback`
|
||||
的反向代理设置可以使用 `gateway.auth.mode: "trusted-proxy"`。
|
||||
`OPENCLAW_GATEWAY_TOKEN` / `OPENCLAW_GATEWAY_PASSWORD`),而非 loopback 的
|
||||
反向代理设置可以使用 `gateway.auth.mode: "trusted-proxy"`。
|
||||
|
||||
## OpenAI 兼容端点
|
||||
## 兼容 OpenAI 的端点
|
||||
|
||||
OpenClaw 现在最有价值的兼容性接口为:
|
||||
OpenClaw 当前最具杠杆效应的兼容性接口是:
|
||||
|
||||
- `GET /v1/models`
|
||||
- `GET /v1/models/{id}`
|
||||
@ -102,17 +102,17 @@ OpenClaw 现在最有价值的兼容性接口为:
|
||||
|
||||
为什么这一组很重要:
|
||||
|
||||
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成会先探测 `/v1/models`。
|
||||
- 许多 RAG 和 memory 流水线依赖 `/v1/embeddings`。
|
||||
- 原生面向智能体的客户端越来越倾向于使用 `/v1/responses`。
|
||||
- 大多数 Open WebUI、LobeChat 和 LibreChat 集成都会先探测 `/v1/models`。
|
||||
- 许多 RAG 和记忆流水线都期望 `/v1/embeddings`。
|
||||
- 原生支持智能体的客户端越来越倾向于使用 `/v1/responses`。
|
||||
|
||||
规划说明:
|
||||
|
||||
- `/v1/models` 以智能体优先:它会返回 `openclaw`、`openclaw/default` 和 `openclaw/<agentId>`。
|
||||
- `openclaw/default` 是稳定别名,始终映射到已配置的默认智能体。
|
||||
- 如果你想覆盖后端 provider/model,请使用 `x-openclaw-model`;否则将继续由所选智能体的常规模型和 embedding 设置进行控制。
|
||||
- 当你想要后端提供商/模型覆盖时,使用 `x-openclaw-model`;否则,所选智能体的常规模型和嵌入设置仍会生效。
|
||||
|
||||
所有这些都运行在 Gateway 网关主端口上,并使用与 Gateway 网关其余 HTTP API 相同的可信操作员认证边界。
|
||||
所有这些都运行在主 Gateway 网关端口上,并使用与 Gateway 网关其余 HTTP API 相同的可信操作员身份验证边界。
|
||||
|
||||
### 端口和绑定优先级
|
||||
|
||||
@ -126,15 +126,15 @@ OpenClaw 现在最有价值的兼容性接口为:
|
||||
| `gateway.reload.mode` | 行为 |
|
||||
| --------------------- | ------------------------------------------ |
|
||||
| `off` | 不进行配置重载 |
|
||||
| `hot` | 仅应用热更新安全的变更 |
|
||||
| `restart` | 遇到需要重启的变更时重启 |
|
||||
| `hybrid` (默认) | 安全时热应用,需要时重启 |
|
||||
| `hot` | 仅应用可安全热更新的更改 |
|
||||
| `restart` | 在需要重载的更改上重启 |
|
||||
| `hybrid`(默认) | 安全时热应用,需要时重启 |
|
||||
|
||||
## 操作员命令集
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
openclaw gateway status --deep # 添加系统级服务扫描
|
||||
openclaw gateway status --deep # 增加系统级服务扫描
|
||||
openclaw gateway status --json
|
||||
openclaw gateway install
|
||||
openclaw gateway restart
|
||||
@ -145,14 +145,14 @@ openclaw doctor
|
||||
```
|
||||
|
||||
`gateway status --deep` 用于额外的服务发现(LaunchDaemons/systemd 系统
|
||||
units/schtasks),而不是更深入的 RPC 健康探测。
|
||||
units/schtasks),而不是更深层的 RPC 健康探测。
|
||||
|
||||
## 多个 Gateway 网关(同一主机)
|
||||
|
||||
大多数安装应在每台机器上运行一个 Gateway 网关。单个 Gateway 网关可以承载多个
|
||||
大多数安装应在每台机器上运行一个 Gateway 网关。单个 Gateway 网关可以托管多个
|
||||
智能体和渠道。
|
||||
|
||||
只有当你明确需要隔离或一个救援 bot 时,才需要多个 Gateway 网关。
|
||||
只有当你有意需要隔离或一个救援机器人时,才需要多个 Gateway 网关。
|
||||
|
||||
有用的检查:
|
||||
|
||||
@ -161,12 +161,26 @@ openclaw gateway status --deep
|
||||
openclaw gateway probe
|
||||
```
|
||||
|
||||
预期表现:
|
||||
预期行为:
|
||||
|
||||
- `gateway status --deep` 可能会报告 `Other gateway-like services detected (best effort)`
|
||||
并在仍存在陈旧的 launchd/systemd/schtasks 安装时打印清理提示。
|
||||
- 当多个目标都有响应时,`gateway probe` 可能会警告 `multiple reachable gateways`。
|
||||
- 如果这是有意为之,请为每个 Gateway 网关隔离端口、配置/state 和 workspace 根目录。
|
||||
- `gateway status --deep` 可以报告 `Other gateway-like services detected (best effort)`
|
||||
并在仍存在过期的 launchd/systemd/schtasks 安装时打印清理提示。
|
||||
- 当多个目标有响应时,`gateway probe` 可以警告 `multiple reachable gateways`。
|
||||
- 如果这是有意为之,请为每个 Gateway 网关分别隔离端口、配置/state 和工作区根目录。
|
||||
|
||||
每个实例的检查清单:
|
||||
|
||||
- 唯一的 `gateway.port`
|
||||
- 唯一的 `OPENCLAW_CONFIG_PATH`
|
||||
- 唯一的 `OPENCLAW_STATE_DIR`
|
||||
- 唯一的 `agents.defaults.workspace`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json OPENCLAW_STATE_DIR=~/.openclaw-a openclaw gateway --port 19001
|
||||
OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b openclaw gateway --port 19002
|
||||
```
|
||||
|
||||
详细设置:[/gateway/multiple-gateways](/zh-CN/gateway/multiple-gateways)。
|
||||
|
||||
@ -182,16 +196,16 @@ ssh -N -L 18789:127.0.0.1:18789 user@host
|
||||
然后让客户端在本地连接到 `ws://127.0.0.1:18789`。
|
||||
|
||||
<Warning>
|
||||
SSH 隧道不会绕过 Gateway 网关认证。对于共享密钥认证,即使通过隧道,客户端
|
||||
仍然必须发送 `token`/`password`。对于带身份的模式,
|
||||
请求仍然必须满足该认证路径。
|
||||
SSH 隧道不会绕过 Gateway 网关身份验证。对于共享密钥身份验证,客户端即使
|
||||
通过隧道连接,仍然必须发送 `token`/`password`。对于携带身份的模式,
|
||||
请求仍然必须满足该身份验证路径。
|
||||
</Warning>
|
||||
|
||||
参见:[远程 Gateway 网关](/zh-CN/gateway/remote)、[认证](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
|
||||
参见:[Remote Gateway](/zh-CN/gateway/remote)、[Authentication](/zh-CN/gateway/authentication)、[Tailscale](/zh-CN/gateway/tailscale)。
|
||||
|
||||
## 监管和服务生命周期
|
||||
## 监督与服务生命周期
|
||||
|
||||
为了获得接近生产环境的可靠性,请使用受监管的运行方式。
|
||||
对于类生产环境的可靠性,请使用受监督的运行方式。
|
||||
|
||||
<Tabs>
|
||||
<Tab title="macOS(launchd)">
|
||||
@ -215,7 +229,7 @@ systemctl --user enable --now openclaw-gateway[-<profile>].service
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
要在登出后保持持久运行,请启用 lingering:
|
||||
如需在注销后仍保持运行,请启用 lingering:
|
||||
|
||||
```bash
|
||||
sudo loginctl enable-linger <user>
|
||||
@ -262,7 +276,7 @@ openclaw gateway stop
|
||||
|
||||
<Tab title="Linux(系统服务)">
|
||||
|
||||
对多用户/始终在线主机,请使用系统 unit。
|
||||
对于多用户/始终运行的主机,请使用系统 unit。
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
@ -276,28 +290,7 @@ sudo systemctl enable --now openclaw-gateway[-<profile>].service
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 一台主机上的多个 Gateway 网关
|
||||
|
||||
大多数设置应只运行**一个** Gateway 网关。
|
||||
只有在需要严格隔离/冗余时才使用多个(例如救援 profile)。
|
||||
|
||||
每个实例的检查清单:
|
||||
|
||||
- 唯一的 `gateway.port`
|
||||
- 唯一的 `OPENCLAW_CONFIG_PATH`
|
||||
- 唯一的 `OPENCLAW_STATE_DIR`
|
||||
- 唯一的 `agents.defaults.workspace`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json OPENCLAW_STATE_DIR=~/.openclaw-a openclaw gateway --port 19001
|
||||
OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b openclaw gateway --port 19002
|
||||
```
|
||||
|
||||
参见:[多个 Gateway 网关](/zh-CN/gateway/multiple-gateways)。
|
||||
|
||||
### 开发 profile 快速路径
|
||||
## 开发 profile 快速路径
|
||||
|
||||
```bash
|
||||
openclaw --dev setup
|
||||
@ -305,34 +298,34 @@ openclaw --dev gateway --allow-unconfigured
|
||||
openclaw --dev status
|
||||
```
|
||||
|
||||
默认值包括隔离的 state/config,以及基础 Gateway 网关端口 `19001`。
|
||||
默认值包括隔离的 state/config 和基础 Gateway 网关端口 `19001`。
|
||||
|
||||
## 协议快速参考(操作员视角)
|
||||
|
||||
- 第一个客户端帧必须是 `connect`。
|
||||
- 客户端的第一帧必须是 `connect`。
|
||||
- Gateway 网关返回 `hello-ok` 快照(`presence`、`health`、`stateVersion`、`uptimeMs`、limits/policy)。
|
||||
- `hello-ok.features.methods` / `events` 是保守的发现列表,而不是
|
||||
每个可调用辅助路由的自动生成清单。
|
||||
每个可调用 helper 路由的自动生成清单。
|
||||
- 请求:`req(method, params)` → `res(ok/payload|error)`。
|
||||
- 常见事件包括 `connect.challenge`、`agent`、`chat`、
|
||||
`session.message`、`session.tool`、`sessions.changed`、`presence`、`tick`、
|
||||
`health`、`heartbeat`、pairing/approval 生命周期事件以及 `shutdown`。
|
||||
`health`、`heartbeat`、配对/批准生命周期事件,以及 `shutdown`。
|
||||
|
||||
智能体运行分为两个阶段:
|
||||
|
||||
1. 立即接受确认(`status:"accepted"`)
|
||||
2. 最终完成响应(`status:"ok"|"error"`),其间会流式发送 `agent` 事件。
|
||||
|
||||
查看完整协议文档:[Gateway 协议](/zh-CN/gateway/protocol)。
|
||||
完整协议文档参见:[Gateway Protocol](/zh-CN/gateway/protocol)。
|
||||
|
||||
## 运维检查
|
||||
|
||||
### 存活性
|
||||
|
||||
- 打开 WS 并发送 `connect`。
|
||||
- 预期收到带快照的 `hello-ok` 响应。
|
||||
- 预期收到带有快照的 `hello-ok` 响应。
|
||||
|
||||
### 就绪状态
|
||||
### 就绪性
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
@ -340,26 +333,26 @@ openclaw channels status --probe
|
||||
openclaw health
|
||||
```
|
||||
|
||||
### 缺口恢复
|
||||
### 间隙恢复
|
||||
|
||||
事件不会重放。出现序列缺口时,在继续之前刷新状态(`health`、`system-presence`)。
|
||||
事件不会重放。遇到序列间隙时,请先刷新状态(`health`、`system-presence`)再继续。
|
||||
|
||||
## 常见故障特征
|
||||
## 常见失败特征
|
||||
|
||||
| 特征 | 可能问题 |
|
||||
| 特征 | 可能的问题 |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `refusing to bind gateway ... without auth` | 非 `loopback` 绑定,但没有有效的 Gateway 网关认证路径 |
|
||||
| `refusing to bind gateway ... without auth` | 非 loopback 绑定但没有有效的 Gateway 网关身份验证路径 |
|
||||
| `another gateway instance is already listening` / `EADDRINUSE` | 端口冲突 |
|
||||
| `Gateway start blocked: set gateway.mode=local` | 配置被设为远程模式,或损坏配置中缺少本地模式标记 |
|
||||
| `unauthorized` during connect | 客户端与 Gateway 网关之间的认证不匹配 |
|
||||
| `Gateway start blocked: set gateway.mode=local` | 配置被设置为远程模式,或受损配置中缺少 local-mode 标记 |
|
||||
| `unauthorized` during connect | 客户端与 Gateway 网关之间的身份验证不匹配 |
|
||||
|
||||
如需完整的诊断步骤,请使用 [Gateway 故障排除](/zh-CN/gateway/troubleshooting)。
|
||||
完整诊断步骤请使用 [Gateway Troubleshooting](/zh-CN/gateway/troubleshooting)。
|
||||
|
||||
## 安全保证
|
||||
|
||||
- 当 Gateway 网关不可用时,Gateway 协议客户端会快速失败(不会隐式回退到直接渠道)。
|
||||
- 当 Gateway 网关不可用时,Gateway 网关协议客户端会快速失败(不会隐式回退到直接渠道)。
|
||||
- 无效的/非 `connect` 的首帧会被拒绝并关闭连接。
|
||||
- 优雅关闭会在 socket 关闭前发出 `shutdown` 事件。
|
||||
- 优雅关闭会在 socket 关闭前发送 `shutdown` 事件。
|
||||
|
||||
---
|
||||
|
||||
@ -368,6 +361,6 @@ openclaw health
|
||||
- [故障排除](/zh-CN/gateway/troubleshooting)
|
||||
- [后台进程](/zh-CN/gateway/background-process)
|
||||
- [配置](/zh-CN/gateway/configuration)
|
||||
- [健康](/zh-CN/gateway/health)
|
||||
- [健康状态](/zh-CN/gateway/health)
|
||||
- [Doctor](/zh-CN/gateway/doctor)
|
||||
- [认证](/zh-CN/gateway/authentication)
|
||||
- [身份验证](/zh-CN/gateway/authentication)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,26 @@
|
||||
---
|
||||
read_when:
|
||||
- 你需要从插件中调用核心辅助函数(TTS、STT、图像生成、Web 搜索、子智能体)
|
||||
- 你需要从插件调用核心辅助工具(TTS、STT、图像生成、网络搜索、子智能体)
|
||||
- 你想了解 `api.runtime` 暴露了什么
|
||||
- 你正在从插件代码中访问配置、智能体或媒体辅助函数
|
||||
- 你正在从插件代码中访问配置、智能体或媒体辅助工具
|
||||
sidebarTitle: Runtime Helpers
|
||||
summary: '`api.runtime` —— 可供插件使用的注入式运行时辅助函数'
|
||||
title: 插件运行时辅助函数
|
||||
summary: api.runtime —— 可供插件使用的注入式运行时辅助工具
|
||||
title: 插件运行时辅助工具
|
||||
x-i18n:
|
||||
generated_at: "2026-04-15T16:36:52Z"
|
||||
generated_at: "2026-04-23T20:12:16Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: c77a6e9cd48c84affa17dce684bbd0e072c8b63485e4a5d569f3793a4ea4f9c8
|
||||
source_hash: 78672fff3f850854a68c9fa2494e595cec9177c49dc426c96e11f169a44fa76e
|
||||
source_path: plugins/sdk-runtime.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 插件运行时辅助函数
|
||||
# 插件运行时辅助工具
|
||||
|
||||
这是对在注册期间注入到每个插件中的 `api.runtime` 对象的参考说明。使用这些辅助函数,而不是直接导入宿主内部实现。
|
||||
这是对在注册期间注入到每个插件中的 `api.runtime` 对象的参考说明。请使用这些辅助工具,而不是直接导入宿主内部实现。
|
||||
|
||||
<Tip>
|
||||
**想看使用演示?** 请参阅 [渠道插件](/zh-CN/plugins/sdk-channel-plugins) 或 [提供商插件](/zh-CN/plugins/sdk-provider-plugins) 中的分步指南,它们会在具体上下文中展示这些辅助函数的用法。
|
||||
**想看操作演示?** 请参阅 [渠道插件](/zh-CN/plugins/sdk-channel-plugins) 或 [提供商插件](/zh-CN/plugins/sdk-provider-plugins) 中的分步指南,这些指南会结合上下文展示这些辅助工具的用法。
|
||||
</Tip>
|
||||
|
||||
```typescript
|
||||
@ -36,25 +36,25 @@ register(api) {
|
||||
智能体身份、目录和会话管理。
|
||||
|
||||
```typescript
|
||||
// 解析智能体的工作目录
|
||||
// Resolve the agent's working directory
|
||||
const agentDir = api.runtime.agent.resolveAgentDir(cfg);
|
||||
|
||||
// 解析智能体工作区
|
||||
// Resolve agent workspace
|
||||
const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg);
|
||||
|
||||
// 获取智能体身份
|
||||
// Get agent identity
|
||||
const identity = api.runtime.agent.resolveAgentIdentity(cfg);
|
||||
|
||||
// 获取默认思考级别
|
||||
// Get default thinking level
|
||||
const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model);
|
||||
|
||||
// 获取智能体超时时间
|
||||
// Get agent timeout
|
||||
const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg);
|
||||
|
||||
// 确保工作区存在
|
||||
// Ensure workspace exists
|
||||
await api.runtime.agent.ensureAgentWorkspace(cfg);
|
||||
|
||||
// 运行一个嵌入式智能体轮次
|
||||
// Run an embedded agent turn
|
||||
const agentDir = api.runtime.agent.resolveAgentDir(cfg);
|
||||
const result = await api.runtime.agent.runEmbeddedAgent({
|
||||
sessionId: "my-plugin:task-1",
|
||||
@ -66,11 +66,11 @@ const result = await api.runtime.agent.runEmbeddedAgent({
|
||||
});
|
||||
```
|
||||
|
||||
`runEmbeddedAgent(...)` 是一个中立的辅助函数,用于从插件代码启动一个普通的 OpenClaw 智能体轮次。它使用与渠道触发回复相同的 provider / model 解析逻辑和智能体 harness 选择方式。
|
||||
`runEmbeddedAgent(...)` 是用于从插件代码启动常规 OpenClaw 智能体轮次的中立辅助工具。它使用与渠道触发回复相同的提供商 / 模型解析和智能体 harness 选择逻辑。
|
||||
|
||||
`runEmbeddedPiAgent(...)` 仍保留作为兼容性别名。
|
||||
`runEmbeddedPiAgent(...)` 仍然作为兼容性别名保留。
|
||||
|
||||
**会话存储辅助函数** 位于 `api.runtime.agent.session` 下:
|
||||
**会话存储辅助工具** 位于 `api.runtime.agent.session` 下:
|
||||
|
||||
```typescript
|
||||
const storePath = api.runtime.agent.session.resolveStorePath(cfg);
|
||||
@ -81,50 +81,49 @@ const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId
|
||||
|
||||
### `api.runtime.agent.defaults`
|
||||
|
||||
默认 model 和 provider 常量:
|
||||
默认模型和提供商常量:
|
||||
|
||||
```typescript
|
||||
const model = api.runtime.agent.defaults.model; // 例如 "anthropic/claude-sonnet-4-6"
|
||||
const provider = api.runtime.agent.defaults.provider; // 例如 "anthropic"
|
||||
const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6"
|
||||
const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic"
|
||||
```
|
||||
|
||||
### `api.runtime.subagent`
|
||||
|
||||
启动并管理后台子智能体运行。
|
||||
启动和管理后台子智能体运行。
|
||||
|
||||
```typescript
|
||||
// 启动一次子智能体运行
|
||||
// Start a subagent run
|
||||
const { runId } = await api.runtime.subagent.run({
|
||||
sessionKey: "agent:main:subagent:search-helper",
|
||||
message: "Expand this query into focused follow-up searches.",
|
||||
provider: "openai", // 可选覆盖
|
||||
model: "gpt-4.1-mini", // 可选覆盖
|
||||
provider: "openai", // optional override
|
||||
model: "gpt-4.1-mini", // optional override
|
||||
deliver: false,
|
||||
});
|
||||
|
||||
// 等待完成
|
||||
// Wait for completion
|
||||
const result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 });
|
||||
|
||||
// 读取会话消息
|
||||
// Read session messages
|
||||
const { messages } = await api.runtime.subagent.getSessionMessages({
|
||||
sessionKey: "agent:main:subagent:search-helper",
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
// 删除一个会话
|
||||
// Delete a session
|
||||
await api.runtime.subagent.deleteSession({
|
||||
sessionKey: "agent:main:subagent:search-helper",
|
||||
});
|
||||
```
|
||||
|
||||
<Warning>
|
||||
model 覆盖(`provider` / `model`)需要操作员在配置中显式启用 `plugins.entries.<id>.subagent.allowModelOverride: true`。
|
||||
不受信任的插件仍然可以运行子智能体,但其覆盖请求会被拒绝。
|
||||
模型覆盖(`provider` / `model`)需要操作员在配置中通过 `plugins.entries.<id>.subagent.allowModelOverride: true` 显式启用。不受信任的插件仍然可以运行子智能体,但其覆盖请求会被拒绝。
|
||||
</Warning>
|
||||
|
||||
### `api.runtime.taskFlow`
|
||||
|
||||
将一个 Task Flow 运行时绑定到现有的 OpenClaw 会话键或受信任的工具上下文,然后在不必每次调用都传入 owner 的情况下创建和管理 Task Flows。
|
||||
将 Task Flow 运行时绑定到现有的 OpenClaw 会话键或受信任的工具上下文,然后在每次调用时无需传入所有者即可创建和管理 Task Flows。
|
||||
|
||||
```typescript
|
||||
const taskFlow = api.runtime.taskFlow.fromToolContext(ctx);
|
||||
@ -151,70 +150,70 @@ const waiting = taskFlow.setWaiting({
|
||||
});
|
||||
```
|
||||
|
||||
当你已经从自己的绑定层拿到了一个受信任的 OpenClaw 会话键时,请使用 `bindSession({ sessionKey, requesterOrigin })`。不要从原始用户输入中进行绑定。
|
||||
当你已经从自己的绑定层获得受信任的 OpenClaw 会话键时,请使用 `bindSession({ sessionKey, requesterOrigin })`。不要基于原始用户输入进行绑定。
|
||||
|
||||
### `api.runtime.tts`
|
||||
|
||||
文本转语音合成。
|
||||
|
||||
```typescript
|
||||
// 标准 TTS
|
||||
// Standard TTS
|
||||
const clip = await api.runtime.tts.textToSpeech({
|
||||
text: "Hello from OpenClaw",
|
||||
cfg: api.config,
|
||||
});
|
||||
|
||||
// 面向电话场景优化的 TTS
|
||||
// Telephony-optimized TTS
|
||||
const telephonyClip = await api.runtime.tts.textToSpeechTelephony({
|
||||
text: "Hello from OpenClaw",
|
||||
cfg: api.config,
|
||||
});
|
||||
|
||||
// 列出可用音色
|
||||
// List available voices
|
||||
const voices = await api.runtime.tts.listVoices({
|
||||
provider: "elevenlabs",
|
||||
cfg: api.config,
|
||||
});
|
||||
```
|
||||
|
||||
使用核心 `messages.tts` 配置和 provider 选择逻辑。返回 PCM 音频缓冲区和采样率。
|
||||
使用核心 `messages.tts` 配置和提供商选择。返回 PCM 音频缓冲区 + 采样率。
|
||||
|
||||
### `api.runtime.mediaUnderstanding`
|
||||
|
||||
图像、音频和视频分析。
|
||||
|
||||
```typescript
|
||||
// 描述一张图片
|
||||
// Describe an image
|
||||
const image = await api.runtime.mediaUnderstanding.describeImageFile({
|
||||
filePath: "/tmp/inbound-photo.jpg",
|
||||
cfg: api.config,
|
||||
agentDir: "/tmp/agent",
|
||||
});
|
||||
|
||||
// 转录音频
|
||||
// Transcribe audio
|
||||
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
|
||||
filePath: "/tmp/inbound-audio.ogg",
|
||||
cfg: api.config,
|
||||
mime: "audio/ogg", // 可选,用于无法推断 MIME 的情况
|
||||
mime: "audio/ogg", // optional, for when MIME cannot be inferred
|
||||
});
|
||||
|
||||
// 描述一个视频
|
||||
// Describe a video
|
||||
const video = await api.runtime.mediaUnderstanding.describeVideoFile({
|
||||
filePath: "/tmp/inbound-video.mp4",
|
||||
cfg: api.config,
|
||||
});
|
||||
|
||||
// 通用文件分析
|
||||
// Generic file analysis
|
||||
const result = await api.runtime.mediaUnderstanding.runFile({
|
||||
filePath: "/tmp/inbound-file.pdf",
|
||||
cfg: api.config,
|
||||
});
|
||||
```
|
||||
|
||||
当没有生成输出时(例如输入被跳过),返回 `{ text: undefined }`。
|
||||
当未产生输出时(例如输入被跳过),会返回 `{ text: undefined }`。
|
||||
|
||||
<Info>
|
||||
`api.runtime.stt.transcribeAudioFile(...)` 仍保留作为 `api.runtime.mediaUnderstanding.transcribeAudioFile(...)` 的兼容性别名。
|
||||
`api.runtime.stt.transcribeAudioFile(...)` 仍然作为 `api.runtime.mediaUnderstanding.transcribeAudioFile(...)` 的兼容性别名保留。
|
||||
</Info>
|
||||
|
||||
### `api.runtime.imageGeneration`
|
||||
@ -232,7 +231,7 @@ const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config })
|
||||
|
||||
### `api.runtime.webSearch`
|
||||
|
||||
Web 搜索。
|
||||
网络搜索。
|
||||
|
||||
```typescript
|
||||
const providers = api.runtime.webSearch.listProviders({ config: api.config });
|
||||
@ -254,11 +253,16 @@ const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image"
|
||||
const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath);
|
||||
const metadata = await api.runtime.media.getImageMetadata(filePath);
|
||||
const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 });
|
||||
const terminalQr = await api.runtime.media.renderQrTerminal("https://openclaw.ai");
|
||||
const pngQr = await api.runtime.media.renderQrPngBase64("https://openclaw.ai", {
|
||||
scale: 6, // 1-12
|
||||
marginModules: 4, // 0-16
|
||||
});
|
||||
```
|
||||
|
||||
### `api.runtime.config`
|
||||
|
||||
配置加载与写入。
|
||||
加载和写入配置。
|
||||
|
||||
```typescript
|
||||
const cfg = await api.runtime.config.loadConfig();
|
||||
@ -300,7 +304,7 @@ const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" },
|
||||
|
||||
### `api.runtime.modelAuth`
|
||||
|
||||
model 和 provider 的凭证解析。
|
||||
模型和提供商凭证解析。
|
||||
|
||||
```typescript
|
||||
const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });
|
||||
@ -330,7 +334,7 @@ api.runtime.tools.registerMemoryCli(/* ... */);
|
||||
|
||||
### `api.runtime.channel`
|
||||
|
||||
渠道特定的运行时辅助函数(在加载渠道插件时可用)。
|
||||
渠道专用运行时辅助工具(在加载渠道插件时可用)。
|
||||
|
||||
`api.runtime.channel.mentions` 是供使用运行时注入的内置渠道插件共享的入站提及策略接口:
|
||||
|
||||
@ -359,7 +363,7 @@ const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({
|
||||
});
|
||||
```
|
||||
|
||||
可用的提及辅助函数:
|
||||
可用的提及辅助工具包括:
|
||||
|
||||
- `buildMentionRegexes`
|
||||
- `matchesMentionPatterns`
|
||||
@ -367,7 +371,7 @@ const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({
|
||||
- `implicitMentionKindWhen`
|
||||
- `resolveInboundMentionDecision`
|
||||
|
||||
`api.runtime.channel.mentions` 有意不暴露较旧的 `resolveMentionGating*` 兼容性辅助函数。请优先使用规范化的 `{ facts, policy }` 路径。
|
||||
`api.runtime.channel.mentions` 有意不暴露较旧的 `resolveMentionGating*` 兼容性辅助工具。请优先使用标准化的 `{ facts, policy }` 路径。
|
||||
|
||||
## 存储运行时引用
|
||||
|
||||
@ -382,7 +386,7 @@ const store = createPluginRuntimeStore<PluginRuntime>({
|
||||
errorMessage: "my-plugin runtime not initialized",
|
||||
});
|
||||
|
||||
// 在你的入口点中
|
||||
// In your entry point
|
||||
export default defineChannelPluginEntry({
|
||||
id: "my-plugin",
|
||||
name: "My Plugin",
|
||||
@ -391,17 +395,17 @@ export default defineChannelPluginEntry({
|
||||
setRuntime: store.setRuntime,
|
||||
});
|
||||
|
||||
// 在其他文件中
|
||||
// In other files
|
||||
export function getRuntime() {
|
||||
return store.getRuntime(); // 如果尚未初始化则抛出异常
|
||||
return store.getRuntime(); // throws if not initialized
|
||||
}
|
||||
|
||||
export function tryGetRuntime() {
|
||||
return store.tryGetRuntime(); // 如果尚未初始化则返回 null
|
||||
return store.tryGetRuntime(); // returns null if not initialized
|
||||
}
|
||||
```
|
||||
|
||||
对于运行时存储标识,优先使用 `pluginId`。更底层的 `key` 形式适用于少见场景,即某个插件有意需要多个运行时槽位。
|
||||
对于 runtime-store 标识,优先使用 `pluginId`。较底层的 `key` 形式适用于不常见的场景,即某个插件有意需要多个运行时槽位。
|
||||
|
||||
## 其他顶层 `api` 字段
|
||||
|
||||
@ -411,14 +415,14 @@ export function tryGetRuntime() {
|
||||
| ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `api.id` | `string` | 插件 id |
|
||||
| `api.name` | `string` | 插件显示名称 |
|
||||
| `api.config` | `OpenClawConfig` | 当前配置快照(可用时为活动的内存中运行时快照) |
|
||||
| `api.config` | `OpenClawConfig` | 当前配置快照(可用时为处于活动状态的内存中运行时快照) |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | 来自 `plugins.entries.<id>.config` 的插件专用配置 |
|
||||
| `api.logger` | `PluginLogger` | 作用域日志记录器(`debug`、`info`、`warn`、`error`) |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | 当前加载模式;`"setup-runtime"` 是完整入口启动 / 设置前的轻量级窗口 |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | 当前加载模式;`"setup-runtime"` 是完整入口启动 / 设置之前的轻量级窗口 |
|
||||
| `api.resolvePath(input)` | `(string) => string` | 解析相对于插件根目录的路径 |
|
||||
|
||||
## 相关内容
|
||||
|
||||
- [SDK 概览](/zh-CN/plugins/sdk-overview) -- 子路径参考
|
||||
- [插件入口点](/zh-CN/plugins/sdk-entrypoints) -- `definePluginEntry` 选项
|
||||
- [插件内部机制](/zh-CN/plugins/architecture) -- 能力模型和注册表
|
||||
- [SDK 概览](/zh-CN/plugins/sdk-overview) —— 子路径参考
|
||||
- [插件入口点](/zh-CN/plugins/sdk-entrypoints) —— `definePluginEntry` 选项
|
||||
- [插件内部机制](/zh-CN/plugins/architecture) —— 能力模型和注册表
|
||||
|
||||
@ -1,53 +1,53 @@
|
||||
---
|
||||
read_when:
|
||||
- 运行或修复测试
|
||||
summary: 如何在本地运行测试(`vitest`),以及何时使用 `force`/`coverage` 模式
|
||||
summary: 如何在本地运行测试(vitest),以及何时使用 force/coverage 模式
|
||||
title: 测试
|
||||
x-i18n:
|
||||
generated_at: "2026-04-23T19:45:54Z"
|
||||
generated_at: "2026-04-23T20:12:12Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 078ba2d49ffc117b5069304113ab4b08d734ced76f8e0ac491e79375d6f3fde4
|
||||
source_hash: b00be94a548ca75ab0f2531186a2bf7928655454352ac6a38fd8a6ffa47e8451
|
||||
source_path: reference/test.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 测试
|
||||
|
||||
- 完整测试工具包(测试套件、实时测试、Docker):[测试](/zh-CN/help/testing)
|
||||
- 完整测试工具包(测试套件、live、Docker):[测试](/zh-CN/help/testing)
|
||||
|
||||
- `pnpm test:force`:终止任何仍在占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整的 Vitest 测试套件,以避免服务端测试与正在运行的实例发生冲突。当之前的 Gateway 网关运行遗留并占用了端口 `18789` 时,请使用此命令。
|
||||
- `pnpm test:coverage`:通过 `vitest.unit.config.ts` 运行带 V8 覆盖率的单元测试套件。这是一个基于已加载文件的单元覆盖率门禁,不是整个仓库所有文件的覆盖率。阈值为:行数/函数/语句 70%,分支 55%。由于 `coverage.all` 为 false,该门禁只衡量单元覆盖率测试套件中已加载的文件,而不会将拆分测试分组中的所有源码文件都视为未覆盖。
|
||||
- `pnpm test:coverage:changed`:仅对相对于 `origin/main` 有变更的文件运行单元覆盖率。
|
||||
- `pnpm test:changed`:当 diff 只涉及可路由的源码/测试文件时,将变更的 git 路径展开为有作用域的 Vitest 分组。配置或 setup 变更仍会回退到原生根项目运行,以便在需要时对 wiring 改动进行更广泛的重跑。
|
||||
- `pnpm changed:lanes`:显示相对于 `origin/main` 的 diff 所触发的架构分组。
|
||||
- `pnpm check:changed`:对相对于 `origin/main` 的 diff 运行智能变更门禁。它会将 core 变更与 core 测试分组一起运行,将扩展变更与扩展测试分组一起运行,将仅测试变更限制为测试 typecheck/测试,针对公共插件 SDK 或 plugin-contract 的变更扩展到扩展验证,并让仅含发布元数据的版本变更保持在定向的版本/配置/根依赖检查中。
|
||||
- `pnpm test`:通过有作用域的 Vitest 分组来路由显式的文件/目录目标。未指定目标的运行会使用固定的分片组,并展开为叶子配置以进行本地并行执行;扩展组始终会展开到每个扩展/插件的分片配置,而不是单个巨大的根项目进程。
|
||||
- 完整测试和扩展分片运行会更新 `.artifacts/vitest-shard-timings.json` 中的本地计时数据;后续运行会使用这些计时数据来平衡较慢和较快的分片。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地计时产物。
|
||||
- 部分 `plugin-sdk` 和 `commands` 测试文件现在会通过专用的轻量分组进行路由,这些分组仅保留 `test/setup.ts`,而将运行时较重的用例留在它们原有的分组中。
|
||||
- 部分 `plugin-sdk` 和 `commands` 辅助源码文件也会将 `pnpm test:changed` 映射到这些轻量分组中的显式同级测试,因此小型辅助函数改动无需重跑依赖重型运行时的测试套件。
|
||||
- `auto-reply` 现在也被拆分为三个专用配置(`core`、`top-level`、`reply`),这样 reply harness 就不会主导更轻量的顶层状态/token/helper 测试。
|
||||
- 基础 Vitest 配置现在默认使用 `pool: "threads"` 和 `isolate: false`,并在整个仓库配置中启用了共享的非隔离运行器。
|
||||
- `pnpm test:channels`:运行 `vitest.channels.config.ts`。
|
||||
- `pnpm test:extensions` 和 `pnpm test extensions`:运行所有扩展/插件分片。较重的渠道扩展和 OpenAI 会作为专用分片运行;其他扩展组保持批量执行。对单个内置插件分组使用 `pnpm test extensions/<id>`。
|
||||
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入明细报告,同时仍对显式文件/目录目标使用有作用域的分组路由。
|
||||
- `pnpm test:perf:imports:changed`:与上面相同的导入性能分析,但仅针对相对于 `origin/main` 有变更的文件。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>`:对同一份已提交 git diff 的变更模式路由路径与原生根项目运行进行基准对比。
|
||||
- `pnpm test:perf:changed:bench -- --worktree`:无需先提交,直接对当前工作树的变更集进行基准测试。
|
||||
- `pnpm test:force`:终止任何仍在占用默认控制端口的残留 Gateway 网关进程,然后使用隔离的 Gateway 网关端口运行完整的 Vitest 测试套件,避免服务端测试与正在运行的实例发生冲突。当先前的 Gateway 网关运行导致端口 `18789` 仍被占用时,请使用此命令。
|
||||
- `pnpm test:coverage`:通过 `vitest.unit.config.ts` 使用 V8 覆盖率运行单元测试套件。这是针对已加载文件的单元覆盖率门禁,不是整个仓库所有文件的覆盖率。阈值为:行数 / 函数 / 语句覆盖率 70%,分支覆盖率 55%。由于 `coverage.all` 为 false,此门禁只统计被单元覆盖率测试套件加载的文件,而不会把拆分测试通道中的每个源文件都视为未覆盖。
|
||||
- `pnpm test:coverage:changed`:仅针对自 `origin/main` 以来发生变更的文件运行单元覆盖率。
|
||||
- `pnpm test:changed`:当 diff 仅涉及可路由的源文件 / 测试文件时,将变更的 Git 路径展开为有范围限制的 Vitest 测试通道。配置 / 设置变更仍会回退到原生根项目运行,以便在需要时更广泛地重新运行布线相关改动。
|
||||
- `pnpm changed:lanes`:显示针对 `origin/main` 的 diff 所触发的架构测试通道。
|
||||
- `pnpm check:changed`:对比 `origin/main` 的 diff 运行智能变更门禁。它会将核心代码工作与核心测试通道一起运行,将扩展工作与扩展测试通道一起运行,将仅测试改动限制为仅测试类型检查 / 测试,针对公开的插件 SDK 或插件契约变更扩展到扩展验证,并让仅发布元数据的版本号变更保持在有针对性的版本 / 配置 / 根依赖检查范围内。
|
||||
- `pnpm test`:将显式指定的文件 / 目录目标路由到有范围限制的 Vitest 测试通道。未指定目标的运行会使用固定的分片组,并展开到叶子配置以便在本地并行执行;扩展组始终会展开为按扩展划分的分片配置,而不是一个巨大的根项目进程。
|
||||
- 完整测试和扩展分片运行会更新 `.artifacts/vitest-shard-timings.json` 中的本地时序数据;后续运行会使用这些时序数据来平衡慢分片和快分片。设置 `OPENCLAW_TEST_PROJECTS_TIMINGS=0` 可忽略本地时序工件。
|
||||
- 部分 `plugin-sdk` 和 `commands` 测试文件现在会通过专用轻量测试通道路由,这些通道仅保留 `test/setup.ts`,而运行时负载较重的用例仍保留在现有测试通道中。
|
||||
- 部分 `plugin-sdk` 和 `commands` 辅助源文件也会把 `pnpm test:changed` 映射到这些轻量测试通道中的显式同级测试,因此小型辅助函数改动无需重新运行重量级、依赖运行时的测试套件。
|
||||
- `auto-reply` 现在也被拆分为三个专用配置(`core`、`top-level`、`reply`),这样回复测试框架就不会主导较轻量的顶层状态 / token / helper 测试。
|
||||
- 基础 Vitest 配置现在默认使用 `pool: "threads"` 和 `isolate: false`,并在整个仓库配置中启用共享的非隔离运行器。
|
||||
- `pnpm test:channels` 运行 `vitest.channels.config.ts`。
|
||||
- `pnpm test:extensions` 和 `pnpm test extensions` 运行所有扩展 / 插件分片。重量级渠道扩展和 OpenAI 会作为专用分片运行;其他扩展组保持批量运行。对某一个内置插件测试通道,请使用 `pnpm test extensions/<id>`。
|
||||
- `pnpm test:perf:imports`:启用 Vitest 导入耗时 + 导入明细报告,同时仍对显式文件 / 目录目标使用有范围限制的测试通道路由。
|
||||
- `pnpm test:perf:imports:changed`:与上面相同的导入性能分析,但仅针对自 `origin/main` 以来发生变更的文件。
|
||||
- `pnpm test:perf:changed:bench -- --ref <git-ref>`:针对同一份已提交的 Git diff,对比变更模式路由路径与原生根项目运行的基准表现。
|
||||
- `pnpm test:perf:changed:bench -- --worktree`:无需先提交,即可对当前工作区改动集进行基准测试。
|
||||
- `pnpm test:perf:profile:main`:为 Vitest 主线程写入 CPU profile(`.artifacts/vitest-main-profile`)。
|
||||
- `pnpm test:perf:profile:runner`:为单元测试运行器写入 CPU + heap profile(`.artifacts/vitest-runner-profile`)。
|
||||
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行完整测试套件中的每个 Vitest 叶子配置,并写入分组时长数据以及每个配置的 JSON/日志产物。测试性能智能体会在尝试修复慢测试之前,将它作为基线。
|
||||
- `pnpm test:perf:profile:runner`:为单元测试运行器写入 CPU + 堆 profile(`.artifacts/vitest-runner-profile`)。
|
||||
- `pnpm test:perf:groups --full-suite --allow-failures --output .artifacts/test-perf/baseline-before.json`:串行运行完整测试套件中的每个 Vitest 叶子配置,并写入分组耗时数据以及每个配置对应的 JSON / 日志工件。测试性能智能体会将其用作尝试修复慢测试之前的基线。
|
||||
- `pnpm test:perf:groups:compare .artifacts/test-perf/baseline-before.json .artifacts/test-perf/after-agent.json`:比较性能优化改动前后的分组报告。
|
||||
- Gateway 网关集成:通过 `OPENCLAW_TEST_INCLUDE_GATEWAY=1 pnpm test` 或 `pnpm test:gateway` 选择启用。
|
||||
- `pnpm test:e2e`:运行 Gateway 网关端到端冒烟测试(多实例 WS/HTTP/node 配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 和自适应 worker;可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 获取详细日志。
|
||||
- `pnpm test:live`:运行 provider 实时测试(minimax/zai)。需要 API key,并且需要 `LIVE=1`(或 provider 专属的 `*_LIVE_TEST=1`)才能取消 skip。
|
||||
- `pnpm test:docker:all`:先构建共享的实时测试镜像和 Docker E2E 镜像一次,然后默认以并发数 4 使用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行 Docker 冒烟分组。可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 调整。除非设置了 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,否则运行器会在首次失败后停止调度新的池化分组;每个分组默认有 120 分钟超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或 provider 敏感的分组会在并行池之后以独占方式运行。每个分组的日志会写入 `.artifacts/docker-tests/<run-id>/`。
|
||||
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI,通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的实时模型 key(例如 `~/.profile` 中的 OpenAI),会拉取外部 Open WebUI 镜像,并不像常规单元/e2e 测试套件那样要求具备 CI 稳定性。
|
||||
- `pnpm test:docker:mcp-channels`:启动一个预置数据的 Gateway 网关容器和第二个客户端容器,后者会启动 `openclaw mcp serve`,然后验证路由会话发现、转录读取、附件元数据、实时事件队列行为、出站发送路由,以及通过真实 stdio bridge 传输的 Claude 风格渠道和权限通知。Claude 通知断言会直接读取原始 stdio MCP 帧,因此该冒烟测试能反映 bridge 实际发出的内容。
|
||||
- `pnpm test:e2e`:运行 Gateway 网关端到端冒烟测试(多实例 WS/HTTP/节点配对)。在 `vitest.e2e.config.ts` 中默认使用 `threads` + `isolate: false` 以及自适应 worker;可通过 `OPENCLAW_E2E_WORKERS=<n>` 调整,并设置 `OPENCLAW_E2E_VERBOSE=1` 以输出详细日志。
|
||||
- `pnpm test:live`:运行提供商 live 测试(minimax/zai)。需要 API key,并设置 `LIVE=1`(或提供商专用的 `*_LIVE_TEST=1`)才能取消跳过。
|
||||
- `pnpm test:docker:all`:先构建一次共享的 live-test 镜像和 Docker E2E 镜像,然后默认以并发度 4 运行 Docker 冒烟测试通道,并设置 `OPENCLAW_SKIP_DOCKER_BUILD=1`。可通过 `OPENCLAW_DOCKER_ALL_PARALLELISM=<n>` 调整。除非设置了 `OPENCLAW_DOCKER_ALL_FAIL_FAST=0`,否则运行器会在首次失败后停止调度新的池化测试通道;每个测试通道默认有 120 分钟超时,可通过 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖。对启动或提供商敏感的测试通道会在并行池之后以独占方式运行。每个测试通道的日志都会写入 `.artifacts/docker-tests/<run-id>/`。
|
||||
- `pnpm test:docker:openwebui`:启动 Docker 化的 OpenClaw + Open WebUI,通过 Open WebUI 登录,检查 `/api/models`,然后通过 `/api/chat/completions` 运行一次真实的代理聊天。需要可用的 live 模型 key(例如 `~/.profile` 中的 OpenAI),会拉取外部 Open WebUI 镜像,并不像普通 unit/e2e 测试套件那样被期望具有 CI 稳定性。
|
||||
- `pnpm test:docker:mcp-channels`:启动一个带种子数据的 Gateway 网关容器,以及第二个会启动 `openclaw mcp serve` 的客户端容器,然后通过真实的 stdio bridge 验证路由会话发现、转录读取、附件元数据、live 事件队列行为、出站发送路由,以及类似 Claude 的渠道与权限通知。Claude 通知断言会直接读取原始 stdio MCP 帧,因此该冒烟测试能反映 bridge 实际发出的内容。
|
||||
|
||||
## 本地 PR 门禁
|
||||
|
||||
对于本地 PR 合入/门禁检查,请运行:
|
||||
在本地执行 PR 合入 / 门禁检查时,运行:
|
||||
|
||||
- `pnpm check:changed`
|
||||
- `pnpm check`
|
||||
@ -56,12 +56,12 @@ x-i18n:
|
||||
- `pnpm test`
|
||||
- `pnpm check:docs`
|
||||
|
||||
如果 `pnpm test` 在负载较高的主机上出现偶发失败,在将其视为回归之前先重跑一次,然后使用 `pnpm test <path/to/test>` 进行定位。对于内存受限的主机,可使用:
|
||||
如果 `pnpm test` 在负载较高的主机上出现偶发失败,在视其为回归之前先重跑一次,然后用 `pnpm test <path/to/test>` 进行定位。对于内存受限的主机,请使用:
|
||||
|
||||
- `OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test`
|
||||
- `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache pnpm test:changed`
|
||||
|
||||
## 模型延迟基准测试(本地 keys)
|
||||
## 模型延迟基准测试(本地 key)
|
||||
|
||||
脚本:[`scripts/bench-model.ts`](https://github.com/openclaw/openclaw/blob/main/scripts/bench-model.ts)
|
||||
|
||||
@ -103,15 +103,15 @@ x-i18n:
|
||||
- `real`:`health`、`status`、`status --json`、`sessions`、`sessions --json`、`agents list --json`、`gateway status`、`gateway status --json`、`gateway health --json`、`config get gateway.port`
|
||||
- `all`:同时包含两个预设
|
||||
|
||||
输出会包含每个命令的 `sampleCount`、平均值、p50、p95、最小/最大值、退出码/信号分布,以及最大 RSS 汇总。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile,这样计时与 profile 捕获会使用同一套 harness。
|
||||
输出会包含每个命令的 `sampleCount`、平均值、p50、p95、最小值 / 最大值、exit-code / signal 分布,以及最大 RSS 摘要。可选的 `--cpu-prof-dir` / `--heap-prof-dir` 会为每次运行写入 V8 profile,使计时与 profile 捕获使用同一套测试框架。
|
||||
|
||||
保存输出约定:
|
||||
|
||||
- `pnpm test:startup:bench:smoke` 会将定向冒烟产物写入 `.artifacts/cli-startup-bench-smoke.json`
|
||||
- `pnpm test:startup:bench:save` 会使用 `runs=5` 和 `warmup=1` 将完整测试套件产物写入 `.artifacts/cli-startup-bench-all.json`
|
||||
- `pnpm test:startup:bench:update` 会使用 `runs=5` 和 `warmup=1` 刷新已检入的基线 fixture:`test/fixtures/cli-startup-bench.json`
|
||||
- `pnpm test:startup:bench:smoke` 会将目标冒烟工件写入 `.artifacts/cli-startup-bench-smoke.json`
|
||||
- `pnpm test:startup:bench:save` 会使用 `runs=5` 和 `warmup=1` 将完整测试套件工件写入 `.artifacts/cli-startup-bench-all.json`
|
||||
- `pnpm test:startup:bench:update` 会使用 `runs=5` 和 `warmup=1` 刷新已纳入版本控制的基线 fixture:`test/fixtures/cli-startup-bench.json`
|
||||
|
||||
已检入的 fixture:
|
||||
已纳入版本控制的 fixture:
|
||||
|
||||
- `test/fixtures/cli-startup-bench.json`
|
||||
- 使用 `pnpm test:startup:bench:update` 刷新
|
||||
@ -119,7 +119,7 @@ x-i18n:
|
||||
|
||||
## 新手引导 E2E(Docker)
|
||||
|
||||
Docker 是可选的;只有在需要容器化新手引导冒烟测试时才需要它。
|
||||
Docker 是可选的;只有在进行容器化新手引导冒烟测试时才需要。
|
||||
|
||||
在干净的 Linux 容器中运行完整冷启动流程:
|
||||
|
||||
@ -127,11 +127,11 @@ Docker 是可选的;只有在需要容器化新手引导冒烟测试时才需
|
||||
scripts/e2e/onboard-docker.sh
|
||||
```
|
||||
|
||||
该脚本会通过 pseudo-tty 驱动交互式向导,验证配置/工作区/会话文件,然后启动 Gateway 网关并运行 `openclaw health`。
|
||||
该脚本会通过 pseudo-tty 驱动交互式向导,验证 config/workspace/session 文件,然后启动 Gateway 网关并运行 `openclaw health`。
|
||||
|
||||
## 二维码导入冒烟测试(Docker)
|
||||
## QR 导入冒烟测试(Docker)
|
||||
|
||||
确保 `qrcode-terminal` 能在受支持的 Docker Node 运行时中加载(默认 Node 24,兼容 Node 22):
|
||||
确保维护中的 QR 运行时 helper 能在受支持的 Docker Node 运行时下加载(默认 Node 24,兼容 Node 22):
|
||||
|
||||
```bash
|
||||
pnpm test:docker:qr
|
||||
|
||||
Loading…
Reference in New Issue
Block a user