diff --git a/docs/zh-CN/channels/telegram.md b/docs/zh-CN/channels/telegram.md index 0a1ad65c3..1d876a468 100644 --- a/docs/zh-CN/channels/telegram.md +++ b/docs/zh-CN/channels/telegram.md @@ -4,15 +4,15 @@ read_when: summary: Telegram 机器人支持状态、能力和配置 title: Telegram x-i18n: - generated_at: "2026-04-29T12:15:24Z" + generated_at: "2026-04-29T14:46:33Z" model: gpt-5.5 provider: openai - source_hash: 500f1282c7d6f7350b781a2413737b0e34b00ea8b042703341fd9ddb700ecfb4 + source_hash: 1ffc0c1a6bb94fbab81ede0f08b0e3a165f06c599d4d06d4b9e70c8ba41121f7 source_path: channels/telegram.md workflow: 16 --- -生产级支持通过 grammY 实现 bot 私信和群组。默认模式是长轮询;webhook 模式是可选的。 +可通过 grammY 用于生产环境中的 bot 私信和群组。默认模式是长轮询;webhook 模式为可选。 @@ -30,9 +30,9 @@ x-i18n: - 打开 Telegram,并与 **@BotFather** 聊天(确认账号名正好是 `@BotFather`)。 + 打开 Telegram 并与 **@BotFather** 对话(确认账号名完全是 `@BotFather`)。 - 运行 `/newbot`,按照提示操作,并保存 token。 + 运行 `/newbot`,按提示操作,并保存 token。 @@ -52,7 +52,7 @@ x-i18n: ``` 环境变量回退:`TELEGRAM_BOT_TOKEN=...`(仅默认账户)。 - Telegram **不**使用 `openclaw channels login telegram`;请在配置/环境变量中配置 token,然后启动 Gateway 网关。 + Telegram **不**使用 `openclaw channels login telegram`;请在配置或环境变量中配置 token,然后启动 Gateway 网关。 @@ -74,34 +74,34 @@ openclaw pairing approve telegram -token 解析顺序支持账户感知。实际使用中,配置值优先于环境变量回退,并且 `TELEGRAM_BOT_TOKEN` 仅适用于默认账户。 +token 解析顺序是账户感知的。实际上,配置值优先于环境变量回退,并且 `TELEGRAM_BOT_TOKEN` 仅适用于默认账户。 ## Telegram 端设置 - Telegram bot 默认启用**隐私模式**,这会限制它们能收到的群组消息。 + Telegram bot 默认启用**隐私模式**,这会限制它们能接收的群组消息。 如果 bot 必须看到所有群组消息,可以: - 通过 `/setprivacy` 禁用隐私模式,或 - 将 bot 设为群组管理员。 - 切换隐私模式时,请在每个群组中移除并重新添加 bot,以便 Telegram 应用该变更。 + 切换隐私模式时,请在每个群组中移除并重新添加 bot,以便 Telegram 应用更改。 管理员状态在 Telegram 群组设置中控制。 - 管理员 bot 会收到所有群组消息,这对常驻群组行为很有用。 + 管理员 bot 会接收所有群组消息,这对始终开启的群组行为很有用。 - - `/setjoingroups` 用于允许/拒绝添加到群组 + - `/setjoingroups` 用于允许或拒绝添加到群组 - `/setprivacy` 用于群组可见性行为 @@ -118,21 +118,21 @@ token 解析顺序支持账户感知。实际使用中,配置值优先于环 - `open`(要求 `allowFrom` 包含 `"*"`) - `disabled` - `dmPolicy: "open"` 搭配 `allowFrom: ["*"]` 会让任何找到或猜到 bot 用户名的 Telegram 账户都能向 bot 发送命令。仅在有意公开且工具受到严格限制的 bot 上使用它;单所有者 bot 应使用 `allowlist` 并搭配数字用户 ID。 + `dmPolicy: "open"` 搭配 `allowFrom: ["*"]` 会让任何找到或猜到 bot 用户名的 Telegram 账户都能向 bot 发送命令。仅应将它用于有意公开且工具严格受限的 bot;单所有者 bot 应使用带有数字用户 ID 的 `allowlist`。 - `channels.telegram.allowFrom` 接受数字 Telegram 用户 ID。`telegram:` / `tg:` 前缀会被接受并标准化。 - 在多账户配置中,限制性的顶层 `channels.telegram.allowFrom` 会被视为安全边界:账户级 `allowFrom: ["*"]` 条目不会让该账户公开,除非合并后的有效账户 allowlist 仍包含显式通配符。 - `dmPolicy: "allowlist"` 搭配空 `allowFrom` 会阻止所有私信,并会被配置校验拒绝。 - 设置只会要求输入数字用户 ID。 - 如果你已升级且配置包含 `@username` allowlist 条目,请运行 `openclaw doctor --fix` 解析它们(尽力而为;需要 Telegram bot token)。 - 如果你之前依赖配对存储 allowlist 文件,`openclaw doctor --fix` 可以在 allowlist 流程中将条目恢复到 `channels.telegram.allowFrom`(例如当 `dmPolicy: "allowlist"` 还没有显式 ID 时)。 + `channels.telegram.allowFrom` 接受数字 Telegram 用户 ID。`telegram:` / `tg:` 前缀会被接受并规范化。 + 在多账户配置中,限制性的顶层 `channels.telegram.allowFrom` 会被视为安全边界:除非合并后的有效账户 allowlist 仍包含显式通配符,否则账户级 `allowFrom: ["*"]` 条目不会让该账户公开。 + `dmPolicy: "allowlist"` 搭配空的 `allowFrom` 会阻止所有私信,并会被配置验证拒绝。 + 设置只会询问数字用户 ID。 + 如果你已升级且配置中包含 `@username` allowlist 条目,请运行 `openclaw doctor --fix` 来解析它们(尽力而为;需要 Telegram bot token)。 + 如果你之前依赖配对存储的 allowlist 文件,`openclaw doctor --fix` 可以在 allowlist 流程中将条目恢复到 `channels.telegram.allowFrom`(例如当 `dmPolicy: "allowlist"` 尚无显式 ID 时)。 - 对于单所有者 bot,优先使用 `dmPolicy: "allowlist"` 并搭配显式数字 `allowFrom` ID,以便将访问策略持久保存在配置中(而不是依赖之前的配对批准)。 + 对于单所有者 bot,优先使用带有显式数字 `allowFrom` ID 的 `dmPolicy: "allowlist"`,以便在配置中保持访问策略持久(而不是依赖之前的配对批准)。 - 常见困惑:私信配对批准并不表示“这个发送者在所有地方都已授权”。 - 配对授予私信访问权限。如果还没有命令所有者,第一次获批的配对还会设置 `commands.ownerAllowFrom`,让仅所有者命令和执行批准拥有显式操作员账户。 + 常见混淆:私信配对批准并不表示“此发送者在所有地方都已授权”。 + 配对授予私信访问权限。如果还没有命令所有者,第一次批准的配对也会设置 `commands.ownerAllowFrom`,以便仅所有者命令和 exec 批准拥有显式操作员账户。 群组发送者授权仍来自显式配置 allowlist。 - 如果你想要“我授权一次后,私信和群组命令都能工作”,请把你的数字 Telegram 用户 ID 放入 `channels.telegram.allowFrom`;对于仅所有者命令,请确保 `commands.ownerAllowFrom` 包含 `telegram:`。 + 如果你希望“我授权一次后,私信和群组命令都能工作”,请将你的数字 Telegram 用户 ID 放入 `channels.telegram.allowFrom`;对于仅所有者命令,请确保 `commands.ownerAllowFrom` 包含 `telegram:`。 ### 查找你的 Telegram 用户 ID @@ -148,18 +148,18 @@ token 解析顺序支持账户感知。实际使用中,配置值优先于环 curl "https://api.telegram.org/bot/getUpdates" ``` - 第三方方法(隐私性较低):`@userinfobot` 或 `@getidsbot`。 + 第三方方法(隐私较低):`@userinfobot` 或 `@getidsbot`。 - 两个控制项会共同生效: + 两个控制项会一起生效: 1. **允许哪些群组**(`channels.telegram.groups`) - 没有 `groups` 配置: - - 搭配 `groupPolicy: "open"`:任何群组都可以通过群组 ID 检查 - - 搭配 `groupPolicy: "allowlist"`(默认):群组会被阻止,直到你添加 `groups` 条目(或 `"*"`) - - 已配置 `groups`:作为 allowlist 生效(显式 ID 或 `"*"`) + - 使用 `groupPolicy: "open"`:任何群组都能通过群组 ID 检查 + - 使用 `groupPolicy: "allowlist"`(默认):群组会被阻止,直到你添加 `groups` 条目(或 `"*"`) + - 已配置 `groups`:作为 allowlist(显式 ID 或 `"*"`) 2. **群组中允许哪些发送者**(`channels.telegram.groupPolicy`) - `open` @@ -167,16 +167,16 @@ curl "https://api.telegram.org/bot/getUpdates" - `disabled` `groupAllowFrom` 用于群组发送者过滤。如果未设置,Telegram 会回退到 `allowFrom`。 - `groupAllowFrom` 条目应为数字 Telegram 用户 ID(`telegram:` / `tg:` 前缀会被标准化)。 - 不要把 Telegram 群组或超级群组聊天 ID 放入 `groupAllowFrom`。负数聊天 ID 应放在 `channels.telegram.groups` 下。 - 非数字条目会在发送者授权中被忽略。 - 安全边界(`2026.2.25+`):群组发送者认证**不会**继承私信配对存储批准。 - 配对保持仅限私信。对于群组,请设置 `groupAllowFrom` 或按群组/主题设置 `allowFrom`。 - 如果 `groupAllowFrom` 未设置,Telegram 会回退到配置 `allowFrom`,而不是配对存储。 - 单所有者 bot 的实用模式:在 `channels.telegram.allowFrom` 中设置你的用户 ID,不设置 `groupAllowFrom`,并在 `channels.telegram.groups` 下允许目标群组。 - 运行时说明:如果完全缺少 `channels.telegram`,运行时默认采用故障关闭的 `groupPolicy="allowlist"`,除非显式设置了 `channels.defaults.groupPolicy`。 + `groupAllowFrom` 条目应为数字 Telegram 用户 ID(`telegram:` / `tg:` 前缀会被规范化)。 + 不要将 Telegram 群组或超级群组 chat ID 放入 `groupAllowFrom`。负数 chat ID 属于 `channels.telegram.groups`。 + 非数字条目会被发送者授权忽略。 + 安全边界(`2026.2.25+`):群组发送者鉴权**不会**继承私信配对存储批准。 + 配对仅适用于私信。对于群组,请设置 `groupAllowFrom` 或每群组/每话题的 `allowFrom`。 + 如果未设置 `groupAllowFrom`,Telegram 会回退到配置中的 `allowFrom`,而不是配对存储。 + 单所有者 bot 的实用模式:在 `channels.telegram.allowFrom` 中设置你的用户 ID,保持 `groupAllowFrom` 未设置,并在 `channels.telegram.groups` 下允许目标群组。 + 运行时说明:如果完全缺少 `channels.telegram`,除非显式设置了 `channels.defaults.groupPolicy`,否则运行时默认采用故障关闭的 `groupPolicy="allowlist"`。 - 示例:允许一个特定群组中的任何成员: + 示例:允许一个特定群组中的任意成员: ```json5 { @@ -193,7 +193,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 示例:只允许一个特定群组中的特定用户: + 示例:只允许一个特定群组内的特定用户: ```json5 { @@ -213,21 +213,21 @@ curl "https://api.telegram.org/bot/getUpdates" 常见错误:`groupAllowFrom` 不是 Telegram 群组 allowlist。 - - 将类似 `-1001234567890` 的负数 Telegram 群组或超级群组聊天 ID 放在 `channels.telegram.groups` 下。 - - 当你想限制允许群组中哪些人可以触发 bot 时,将类似 `8734062810` 的 Telegram 用户 ID 放在 `groupAllowFrom` 下。 - - 仅当你希望允许群组中的任何成员都能与 bot 对话时,才使用 `groupAllowFrom: ["*"]`。 + - 将像 `-1001234567890` 这样的负数 Telegram 群组或超级群组 chat ID 放在 `channels.telegram.groups` 下。 + - 当你想限制已允许群组内哪些人可以触发 bot 时,将像 `8734062810` 这样的 Telegram 用户 ID 放在 `groupAllowFrom` 下。 + - 仅当你希望已允许群组中的任何成员都能与 bot 对话时,才使用 `groupAllowFrom: ["*"]`。 - 群组回复默认要求提及。 + 群组回复默认需要提及。 提及可以来自: - 原生 `@botusername` 提及,或 - - 以下位置中的提及模式: + - 以下位置的提及模式: - `agents.list[].groupChat.mentionPatterns` - `messages.groupChat.mentionPatterns` @@ -236,9 +236,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `/activation always` - `/activation mention` - 这些只会更新会话状态。使用配置实现持久化。 + 这些只更新会话状态。使用配置实现持久化。 - 持久化配置示例: + 持久配置示例: ```json5 { @@ -252,10 +252,10 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 获取群组聊天 ID: + 获取群组 chat ID: - 将群组消息转发给 `@userinfobot` / `@getidsbot` - - 或从 `openclaw logs --follow` 读取 `chat.id` + - 或从 `openclaw logs --follow` 中读取 `chat.id` - 或检查 Bot API `getUpdates` @@ -265,12 +265,12 @@ curl "https://api.telegram.org/bot/getUpdates" - Telegram 由 Gateway 网关进程拥有。 - 路由是确定性的:Telegram 入站会回复到 Telegram(模型不会选择渠道)。 -- 入站消息会标准化为共享渠道信封,并带有回复元数据和媒体占位符。 -- 群组会话按群组 ID 隔离。论坛主题会追加 `:topic:` 以保持主题隔离。 -- 私信消息可以携带 `message_thread_id`;OpenClaw 会用线程感知的会话键路由它们,并保留线程 ID 用于回复。 -- 长轮询使用 grammY runner,并按聊天/线程排序。整体 runner sink 并发使用 `agents.defaults.maxConcurrent`。 -- 每个 Gateway 网关进程内部都会保护长轮询,因此同一时间只有一个活跃轮询器可以使用一个 bot token。如果你仍然看到 `getUpdates` 409 冲突,可能有另一个 OpenClaw Gateway 网关、脚本或外部轮询器正在使用同一个 token。 -- 默认情况下,长轮询 watchdog 会在 120 秒内没有完成 `getUpdates` 活性检测后触发重启。仅当你的部署在长时间运行任务期间仍看到误判的轮询停滞重启时,才增加 `channels.telegram.pollingStallThresholdMs`。该值以毫秒为单位,允许范围是 `30000` 到 `600000`;支持按账户覆盖。 +- 入站消息会规范化为共享渠道信封,并带有回复元数据和媒体占位符。 +- 群组会话按群组 ID 隔离。论坛话题会追加 `:topic:` 以保持话题隔离。 +- 私信消息可以携带 `message_thread_id`;OpenClaw 使用线程感知的会话键进行路由,并保留线程 ID 用于回复。 +- 长轮询使用 grammY runner,并按 chat/线程排序。整体 runner sink 并发使用 `agents.defaults.maxConcurrent`。 +- 长轮询在每个 Gateway 网关进程内受到保护,因此同一时间只有一个活动 poller 可以使用一个 bot token。如果你仍然看到 `getUpdates` 409 冲突,可能是另一个 OpenClaw Gateway 网关、脚本或外部 poller 正在使用同一个 token。 +- 默认情况下,长轮询 watchdog 会在 120 秒内没有完成 `getUpdates` 活性检查后触发重启。仅当你的部署在长时间运行工作期间仍出现误判的轮询停滞重启时,才增加 `channels.telegram.pollingStallThresholdMs`。该值以毫秒为单位,允许范围为 `30000` 到 `600000`;支持每账户覆盖。 - Telegram Bot API 不支持已读回执(`sendReadReceipts` 不适用)。 ## 功能参考 @@ -280,16 +280,16 @@ curl "https://api.telegram.org/bot/getUpdates" OpenClaw 可以实时流式传输部分回复: - 直接聊天:预览消息 + `editMessageText` - - 群组/主题:预览消息 + `editMessageText` + - 群组/话题:预览消息 + `editMessageText` 要求: - `channels.telegram.streaming` 为 `off | partial | block | progress`(默认:`partial`) - - `progress` 在 Telegram 上映射到 `partial`(兼容跨渠道命名) - - `streaming.preview.toolProgress` 控制工具/进度更新是否复用同一个已编辑的预览消息(默认:预览流式传输启用时为 `true`) + - 在 Telegram 上,`progress` 映射为 `partial`(兼容跨渠道命名) + - `streaming.preview.toolProgress` 控制工具/进度更新是否复用同一条已编辑的预览消息(默认:预览流式传输启用时为 `true`) - 会检测旧版 `channels.telegram.streamMode` 和布尔值 `streaming`;运行 `openclaw doctor --fix` 将它们迁移到 `channels.telegram.streaming.mode` - 工具进度预览更新是在工具运行时显示的简短“Working...”行,例如命令执行、文件读取、规划更新或补丁摘要。Telegram 默认启用这些更新,以匹配 `v2026.4.22` 及更高版本中已发布的 OpenClaw 行为。若要为回答文本保留已编辑预览,但隐藏工具进度行,请设置: + 工具进度预览更新是在工具运行时显示的简短“Working...”行,例如命令执行、文件读取、规划更新或补丁摘要。Telegram 默认保持启用,以匹配 `v2026.4.22` 及更高版本中已发布的 OpenClaw 行为。若要保留答案文本的编辑预览但隐藏工具进度行,请设置: ```json { @@ -306,22 +306,22 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 仅当你希望只交付最终结果时,才使用 `streaming.mode: "off"`:Telegram 预览编辑会被禁用,通用工具/进度闲聊会被抑制,而不是作为独立的“Working...”消息发送。批准提示、媒体载荷和错误仍会通过正常最终交付路由。仅当你只想保留回答预览编辑,同时隐藏工具进度 Status 行时,才使用 `streaming.preview.toolProgress: false`。 + 仅当你需要只交付最终内容时,才使用 `streaming.mode: "off"`:Telegram 预览编辑会被禁用,并且通用工具/进度杂音会被抑制,而不是作为独立的“Working...”消息发送。批准提示、媒体载荷和错误仍通过正常最终交付路由。仅当你想保留答案预览编辑,同时隐藏工具进度状态行时,才使用 `streaming.preview.toolProgress: false`。 对于纯文本回复: - - 短私信/群组/话题预览:OpenClaw 保持同一条预览消息,并在原处执行最终编辑 - - 超过约一分钟的预览:OpenClaw 将完成的回复作为新的最终消息发送,然后清理预览,因此 Telegram 的可见时间戳反映的是完成时间,而不是预览创建时间 + - 简短的私信/群组/主题预览:OpenClaw 保留同一条预览消息,并在原位执行最终编辑 + - 早于约一分钟的预览:OpenClaw 会将完成的回复作为新的最终消息发送,然后清理预览,因此 Telegram 的可见时间戳会反映完成时间,而不是预览创建时间 对于复杂回复(例如媒体载荷),OpenClaw 会回退到正常的最终投递,然后清理预览消息。 - 预览流式传输独立于分块流式传输。当 Telegram 显式启用分块流式传输时,OpenClaw 会跳过预览流,以避免双重流式传输。 + 预览流式传输独立于分块流式传输。当为 Telegram 明确启用分块流式传输时,OpenClaw 会跳过预览流,以避免双重流式传输。 - 如果原生草稿传输不可用/被拒绝,OpenClaw 会自动回退到 `sendMessage` + `editMessageText`。 + 如果原生草稿传输不可用或被拒绝,OpenClaw 会自动回退到 `sendMessage` + `editMessageText`。 - 仅 Telegram 的推理流: + 仅限 Telegram 的推理流: - - `/reasoning stream` 在生成时将推理发送到实时预览 + - `/reasoning stream` 会在生成期间将推理发送到实时预览 - 最终答案发送时不包含推理文本 @@ -342,9 +342,9 @@ curl "https://api.telegram.org/bot/getUpdates" 原生命令默认值: - - `commands.native: "auto"` 为 Telegram 启用原生命令 + - `commands.native: "auto"` 会为 Telegram 启用原生命令 - 添加自定义命令菜单条目: + 添加自定义命令菜单项: ```json5 { @@ -361,47 +361,47 @@ curl "https://api.telegram.org/bot/getUpdates" 规则: - - 名称会规范化(去掉前导 `/`,转为小写) + - 名称会被规范化(去掉开头的 `/`,转换为小写) - 有效模式:`a-z`、`0-9`、`_`,长度 `1..32` - 自定义命令不能覆盖原生命令 - 冲突/重复项会被跳过并记录日志 - 注意事项: + 注意: - - 自定义命令只是菜单条目;它们不会自动实现行为 - - 即使插件/skill 命令未显示在 Telegram 菜单中,输入时仍可工作 + - 自定义命令只是菜单项;它们不会自动实现行为 + - 插件/Skills 命令即使未显示在 Telegram 菜单中,输入时仍然可以工作 - 如果禁用原生命令,内置项会被移除。自定义/插件命令在配置后仍可能注册。 + 如果禁用原生命令,内置命令会被移除。自定义/插件命令在配置后仍可能注册。 常见设置失败: - - `setMyCommands failed` 携带 `BOT_COMMANDS_TOO_MUCH` 表示 Telegram 菜单在裁剪后仍然溢出;减少插件/skill/自定义命令,或禁用 `channels.telegram.commands.native`。 - - 当直接 Bot API curl 命令可以工作,但 `deleteWebhook`、`deleteMyCommands` 或 `setMyCommands` 失败并显示 `404: Not Found`,可能表示 `channels.telegram.apiRoot` 被设置为完整的 `/bot` 端点。`apiRoot` 必须只是 Bot API 根地址,且 `openclaw doctor --fix` 会移除意外尾随的 `/bot`。 - - `getMe returned 401` 表示 Telegram 拒绝了配置的机器人令牌。请用当前 BotFather 令牌更新 `botToken`、`tokenFile` 或 `TELEGRAM_BOT_TOKEN`;OpenClaw 会在轮询前停止,因此这不会被报告为 webhook 清理失败。 - - `setMyCommands failed` 携带网络/fetch 错误通常表示到 `api.telegram.org` 的出站 DNS/HTTPS 被阻止。 + - `setMyCommands failed` 并带有 `BOT_COMMANDS_TOO_MUCH` 表示 Telegram 菜单在裁剪后仍然溢出;减少插件/Skills/自定义命令,或禁用 `channels.telegram.commands.native`。 + - 当直接的 Bot API curl 命令可用,但 `deleteWebhook`、`deleteMyCommands` 或 `setMyCommands` 因 `404: Not Found` 失败时,可能表示 `channels.telegram.apiRoot` 被设置成了完整的 `/bot` 端点。`apiRoot` 必须只是 Bot API 根地址,并且 `openclaw doctor --fix` 会移除意外追加的 `/bot`。 + - `getMe returned 401` 表示 Telegram 拒绝了已配置的机器人令牌。用当前 BotFather 令牌更新 `botToken`、`tokenFile` 或 `TELEGRAM_BOT_TOKEN`;OpenClaw 会在轮询前停止,因此这不会被报告为 webhook 清理失败。 + - `setMyCommands failed` 并带有网络/fetch 错误,通常表示到 `api.telegram.org` 的出站 DNS/HTTPS 被阻止。 ### 设备配对命令(`device-pair` 插件) 安装 `device-pair` 插件后: 1. `/pair` 生成设置代码 - 2. 在 iOS 应用中粘贴代码 - 3. `/pair pending` 列出待处理请求(包括角色/作用域) + 2. 在 iOS app 中粘贴代码 + 3. `/pair pending` 列出待处理请求(包括角色/范围) 4. 批准请求: - `/pair approve ` 用于显式批准 - - `/pair approve` 用于只有一个待处理请求的情况 + - 只有一个待处理请求时使用 `/pair approve` - `/pair approve latest` 用于最近的请求 - 设置代码携带一个短期有效的引导令牌。内置引导交接会将主节点令牌保持在 `scopes: []`;任何交接出去的操作员令牌都限制在 `operator.approvals`、`operator.read`、`operator.talk.secrets` 和 `operator.write`。引导作用域检查带有角色前缀,因此该操作员允许列表只满足操作员请求;非操作员角色仍需要其自身角色前缀下的作用域。 + 设置代码携带一个短期有效的 bootstrap 令牌。内置 bootstrap 交接会将主节点令牌保持在 `scopes: []`;任何被交接的操作员令牌仍限制在 `operator.approvals`、`operator.read`、`operator.talk.secrets` 和 `operator.write` 内。Bootstrap 范围检查带有角色前缀,因此该操作员 allowlist 只满足操作员请求;非操作员角色仍需要其自身角色前缀下的范围。 - 如果设备使用已变更的认证详情重试(例如角色/作用域/公钥),之前的待处理请求会被取代,新请求会使用不同的 `requestId`。批准前请重新运行 `/pair pending`。 + 如果设备用已更改的认证详细信息重试(例如角色/范围/公钥),先前的待处理请求会被取代,新请求会使用不同的 `requestId`。批准前请重新运行 `/pair pending`。 更多详情:[配对](/zh-CN/channels/pairing#pair-via-telegram-recommended-for-ios)。 - 配置内联键盘作用域: + 配置内联键盘范围: ```json5 { @@ -433,7 +433,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 作用域: + 范围: - `off` - `dm` @@ -441,9 +441,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `all` - `allowlist`(默认) - 旧版 `capabilities: ["inlineButtons"]` 映射到 `inlineButtons: "all"`。 + 旧版 `capabilities: ["inlineButtons"]` 会映射到 `inlineButtons: "all"`。 - 消息操作示例: + 消息动作示例: ```json5 { @@ -461,33 +461,33 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 回调点击会作为文本传递给智能体: + 回调点击会作为文本传给智能体: `callback_data: ` - - Telegram 工具操作包括: + + Telegram 工具动作包括: - - `sendMessage`(`to`、`content`、可选 `mediaUrl`、`replyToMessageId`、`messageThreadId`) + - `sendMessage`(`to`、`content`,可选 `mediaUrl`、`replyToMessageId`、`messageThreadId`) - `react`(`chatId`、`messageId`、`emoji`) - `deleteMessage`(`chatId`、`messageId`) - `editMessage`(`chatId`、`messageId`、`content`) - - `createForumTopic`(`chatId`、`name`、可选 `iconColor`、`iconCustomEmojiId`) + - `createForumTopic`(`chatId`、`name`,可选 `iconColor`、`iconCustomEmojiId`) - 渠道消息操作公开便捷别名(`send`、`react`、`delete`、`edit`、`sticker`、`sticker-search`、`topic-create`)。 + 渠道消息动作公开符合人体工学的别名(`send`、`react`、`delete`、`edit`、`sticker`、`sticker-search`、`topic-create`)。 - 门控控制项: + 门控控制: - `channels.telegram.actions.sendMessage` - `channels.telegram.actions.deleteMessage` - `channels.telegram.actions.reactions` - - `channels.telegram.actions.sticker`(默认:禁用) + - `channels.telegram.actions.sticker`(默认:已禁用) - 注意:`edit` 和 `topic-create` 当前默认启用,且没有单独的 `channels.telegram.actions.*` 开关。 - 运行时发送使用活动的配置/密钥快照(启动/重载),因此操作路径不会在每次发送时执行临时 SecretRef 重新解析。 + 注意:`edit` 和 `topic-create` 目前默认启用,并且没有单独的 `channels.telegram.actions.*` 开关。 + 运行时发送使用活动的配置/密钥快照(启动/重载),因此动作路径不会在每次发送时执行临时 SecretRef 重新解析。 - 反应移除语义:[/tools/reactions](/zh-CN/tools/reactions) + 移除回应的语义:[/tools/reactions](/zh-CN/tools/reactions) @@ -503,29 +503,29 @@ curl "https://api.telegram.org/bot/getUpdates" - `first` - `all` - 启用回复线程,并且原始 Telegram 文本或标题可用时,OpenClaw 会自动包含一段原生 Telegram 引用摘录。Telegram 将原生引用文本限制为 1024 个 UTF-16 代码单元,因此更长的消息会从开头开始引用,并在 Telegram 拒绝引用时回退为普通回复。 + 当启用回复线程并且原始 Telegram 文本或说明可用时,OpenClaw 会自动包含一段原生 Telegram 引用摘录。Telegram 将原生引用文本限制为 1024 个 UTF-16 码元,因此较长消息会从开头开始引用,如果 Telegram 拒绝该引用,则回退为普通回复。 注意:`off` 会禁用隐式回复线程。显式 `[[reply_to_*]]` 标签仍会被遵循。 - + 论坛超级群组: - - 话题会话键追加 `:topic:` - - 回复和正在输入目标为话题线程 - - 话题配置路径: + - 主题会话键追加 `:topic:` + - 回复和输入状态会定向到主题线程 + - 主题配置路径: `channels.telegram.groups..topics.` - 常规话题(`threadId=1`)特殊情况: + General 主题(`threadId=1`)特例: - - 消息发送省略 `message_thread_id`(Telegram 会拒绝 `sendMessage(...thread_id=1)`) - - 正在输入操作仍包含 `message_thread_id` + - 发送消息会省略 `message_thread_id`(Telegram 会拒绝 `sendMessage(...thread_id=1)`) + - 输入状态动作仍包含 `message_thread_id` - 话题继承:话题条目会继承群组设置,除非被覆盖(`requireMention`、`allowFrom`、`skills`、`systemPrompt`、`enabled`、`groupPolicy`)。 - `agentId` 仅限话题,不会从群组默认值继承。 + 主题继承:主题条目会继承群组设置,除非被覆盖(`requireMention`、`allowFrom`、`skills`、`systemPrompt`、`enabled`、`groupPolicy`)。 + `agentId` 仅限主题使用,不会从群组默认值继承。 - **按话题的智能体路由**:每个话题都可以通过在话题配置中设置 `agentId` 路由到不同智能体。这让每个话题拥有自己隔离的工作区、记忆和会话。示例: + **按主题进行智能体路由**:每个主题都可以通过在主题配置中设置 `agentId` 路由到不同智能体。这会让每个主题拥有自己的隔离工作区、记忆和会话。示例: ```json5 { @@ -545,13 +545,13 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 随后每个话题都有自己的会话键:`agent:zu:telegram:group:-1001234567890:topic:3` + 然后每个主题都有自己的会话键:`agent:zu:telegram:group:-1001234567890:topic:3` - **持久 ACP 话题绑定**:论坛话题可以通过顶层类型化 ACP 绑定固定 ACP harness 会话(`bindings[]`,带有 `type: "acp"`、`match.channel: "telegram"`、`peer.kind: "group"`,以及类似 `-1001234567890:topic:42` 的话题限定 ID)。当前限定在群组/超级群组中的论坛话题。请参阅 [ACP 智能体](/zh-CN/tools/acp-agents)。 + **持久 ACP 主题绑定**:论坛主题可以通过顶层类型化 ACP 绑定(`bindings[]`,包含 `type: "acp"` 和 `match.channel: "telegram"`、`peer.kind: "group"`,以及类似 `-1001234567890:topic:42` 的主题限定 ID)固定 ACP harness 会话。目前范围限定为群组/超级群组中的论坛主题。参见 [ACP 智能体](/zh-CN/tools/acp-agents)。 - **从聊天生成线程绑定的 ACP**:`/acp spawn --thread here|auto` 将当前话题绑定到新的 ACP 会话;后续消息会直接路由到那里。OpenClaw 会在话题内固定生成确认。需要 `channels.telegram.threadBindings.spawnAcpSessions=true`。 + **从聊天生成线程绑定的 ACP**:`/acp spawn --thread here|auto` 会将当前主题绑定到新的 ACP 会话;后续消息会直接路由到那里。OpenClaw 会在主题内固定生成确认消息。需要 `channels.telegram.threadBindings.spawnAcpSessions=true`。 - 模板上下文公开 `MessageThreadId` 和 `IsForum`。带有 `message_thread_id` 的私信聊天会保持私信路由,但使用线程感知的会话键。 + 模板上下文公开 `MessageThreadId` 和 `IsForum`。带有 `message_thread_id` 的私信聊天保持私信路由,但使用感知线程的会话键。 @@ -561,10 +561,10 @@ curl "https://api.telegram.org/bot/getUpdates" Telegram 区分语音便笺和音频文件。 - 默认:音频文件行为 - - 在智能体回复中使用标签 `[[audio_as_voice]]` 强制发送语音便笺 - - 入站语音便笺转录会在智能体上下文中被框定为机器生成的、不受信任的文本;提及检测仍使用原始转录,因此受提及门控的语音消息继续可用。 + - 在智能体回复中使用标签 `[[audio_as_voice]]` 可强制按语音便笺发送 + - 入站语音便笺转录会在智能体上下文中被框定为机器生成的、不受信任的文本;提及检测仍使用原始转录,因此由提及门控的语音消息会继续工作。 - 消息操作示例: + 消息动作示例: ```json5 { @@ -580,7 +580,7 @@ curl "https://api.telegram.org/bot/getUpdates" Telegram 区分视频文件和视频便笺。 - 消息操作示例: + 消息动作示例: ```json5 { @@ -592,7 +592,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 视频便笺不支持标题;提供的消息文本会单独发送。 + 视频便笺不支持说明;提供的消息文本会单独发送。 ### 贴纸 @@ -614,9 +614,9 @@ curl "https://api.telegram.org/bot/getUpdates" - `~/.openclaw/telegram/sticker-cache.json` - 贴纸会被描述一次(如果可能)并缓存,以减少重复的视觉调用。 + 贴纸会被描述一次(在可行时),并被缓存以减少重复视觉调用。 - 启用贴纸操作: + 启用贴纸动作: ```json5 { @@ -630,7 +630,7 @@ curl "https://api.telegram.org/bot/getUpdates" } ``` - 发送贴纸操作: + 发送贴纸动作: ```json5 { @@ -654,31 +654,31 @@ curl "https://api.telegram.org/bot/getUpdates" - - Telegram 反应会作为 `message_reaction` 更新到达(独立于消息载荷)。 + + Telegram 回应会以 `message_reaction` 更新到达(与消息载荷分开)。 - 启用后,OpenClaw 会将如下系统事件加入队列: + 启用后,OpenClaw 会将类似下面的系统事件加入队列: - `Telegram reaction added: 👍 by Alice (@alice) on msg 42` 配置: - - `channels.telegram.reactionNotifications`:`off | own | all`(默认:`own`) - - `channels.telegram.reactionLevel`:`off | ack | minimal | extensive`(默认:`minimal`) + - `channels.telegram.reactionNotifications`: `off | own | all`(默认:`own`) + - `channels.telegram.reactionLevel`: `off | ack | minimal | extensive`(默认:`minimal`) 注意事项: - - `own` 表示仅针对机器人发送消息的用户反应(通过已发送消息缓存尽力而为)。 - - 反应事件仍会遵守 Telegram 访问控制(`dmPolicy`、`allowFrom`、`groupPolicy`、`groupAllowFrom`);未授权发送者会被丢弃。 - - Telegram 不会在反应更新中提供话题 ID。 - - 非论坛群组路由到群组聊天会话 - - 论坛群组路由到群组常规话题会话(`:topic:1`),而不是确切的原始话题 + - `own` 表示仅用户对机器人发送消息的回应(通过已发送消息缓存尽力而为)。 + - 回应事件仍遵守 Telegram 访问控制(`dmPolicy`、`allowFrom`、`groupPolicy`、`groupAllowFrom`);未授权发送者会被丢弃。 + - Telegram 不会在回应更新中提供话题 ID。 + - 非论坛群组路由到群聊会话 + - 论坛群组路由到群组通用话题会话(`:topic:1`),而不是确切的原始话题 轮询/webhook 的 `allowed_updates` 会自动包含 `message_reaction`。 - + `ackReaction` 会在 OpenClaw 处理入站消息时发送一个确认表情符号。 解析顺序: @@ -686,17 +686,17 @@ curl "https://api.telegram.org/bot/getUpdates" - `channels.telegram.accounts..ackReaction` - `channels.telegram.ackReaction` - `messages.ackReaction` - - 智能体身份表情符号回退(`agents.list[].identity.emoji`,否则为 "👀") + - 智能体身份表情符号兜底(`agents.list[].identity.emoji`,否则为 "👀") 注意事项: - - Telegram 需要 unicode 表情符号(例如 "👀")。 - - 使用 `""` 可为某个渠道或账号禁用该反应。 + - Telegram 期望 unicode 表情符号(例如 "👀")。 + - 使用 `""` 可为某个渠道或账号禁用回应。 - 渠道配置写入默认启用(`configWrites !== false`)。 + 默认启用渠道配置写入(`configWrites !== false`)。 Telegram 触发的写入包括: @@ -718,28 +718,28 @@ curl "https://api.telegram.org/bot/getUpdates" - 默认使用长轮询。对于 webhook 模式,请设置 `channels.telegram.webhookUrl` 和 `channels.telegram.webhookSecret`;可选设置 `webhookPath`、`webhookHost`、`webhookPort`(默认值为 `/telegram-webhook`、`127.0.0.1`、`8787`)。 + 默认使用长轮询。对于 webhook 模式,设置 `channels.telegram.webhookUrl` 和 `channels.telegram.webhookSecret`;可选设置 `webhookPath`、`webhookHost`、`webhookPort`(默认值为 `/telegram-webhook`、`127.0.0.1`、`8787`)。 - 本地监听器绑定到 `127.0.0.1:8787`。对于公网入口,请在本地端口前放置反向代理,或有意设置 `webhookHost: "0.0.0.0"`。 + 本地监听器绑定到 `127.0.0.1:8787`。对于公开入口,可以在本地端口前放置反向代理,或有意设置 `webhookHost: "0.0.0.0"`。 - Webhook 模式会先验证请求防护、Telegram 密钥令牌和 JSON 正文,然后再向 Telegram 返回 `200`。 - 随后 OpenClaw 会通过长轮询所用的同一套按聊天/按话题机器人通道异步处理该更新,因此缓慢的智能体轮次不会占用 Telegram 的投递 ACK。 + Webhook 模式会先验证请求保护、Telegram secret token 和 JSON 正文,然后再向 Telegram 返回 `200`。 + OpenClaw 随后会通过长轮询使用的同一套按聊天/按话题机器人通道异步处理更新,因此缓慢的智能体轮次不会阻塞 Telegram 的投递 ACK。 - `channels.telegram.textChunkLimit` 默认值为 4000。 - - `channels.telegram.chunkMode="newline"` 会在按长度拆分前优先使用段落边界(空行)。 + - `channels.telegram.chunkMode="newline"` 在按长度拆分前优先使用段落边界(空行)。 - `channels.telegram.mediaMaxMb`(默认 100)限制入站和出站 Telegram 媒体大小。 - - `channels.telegram.timeoutSeconds` 覆盖 Telegram API 客户端超时时间(如果未设置,则使用 grammY 默认值)。 - - `channels.telegram.pollingStallThresholdMs` 默认为 `120000`;仅在误报轮询停滞重启时,在 `30000` 到 `600000` 之间调整。 + - `channels.telegram.timeoutSeconds` 覆盖 Telegram API 客户端超时(如果未设置,则应用 grammY 默认值)。 + - `channels.telegram.pollingStallThresholdMs` 默认值为 `120000`;仅针对误报的轮询停滞重启,在 `30000` 到 `600000` 之间调整。 - 群组上下文历史使用 `channels.telegram.historyLimit` 或 `messages.groupChat.historyLimit`(默认 50);`0` 表示禁用。 - - 回复/引用/转发补充上下文目前会按接收内容传递。 - - Telegram 允许列表主要用于限制谁可以触发智能体,而不是完整的补充上下文脱敏边界。 + - 回复/引用/转发补充上下文目前会按接收时原样传递。 + - Telegram allowlist 主要控制谁可以触发智能体,而不是完整的补充上下文编辑边界。 - 私信历史控制: - `channels.telegram.dmHistoryLimit` - `channels.telegram.dms[""].historyLimit` - - `channels.telegram.retry` 配置适用于 Telegram 发送助手(CLI/工具/操作)中可恢复的出站 API 错误。入站最终回复投递也会对 Telegram 预连接失败使用有界安全发送重试,但不会重试可能导致可见消息重复的模糊发送后网络信封。 + - `channels.telegram.retry` 配置应用于 Telegram 发送辅助工具(CLI/工具/操作),用于可恢复的出站 API 错误。入站最终回复投递也会对 Telegram 预连接失败使用有界的安全发送重试,但不会重试可能重复可见消息的、语义不明确的发送后网络信封。 CLI 发送目标可以是数字聊天 ID 或用户名: @@ -758,55 +758,55 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ --poll-duration-seconds 300 --poll-public ``` - Telegram 专用投票标志: + 仅限 Telegram 的投票标志: - `--poll-duration-seconds`(5-600) - `--poll-anonymous` - `--poll-public` - - `--thread-id` 用于论坛话题(或使用 `:topic:` 目标) + - 用于论坛话题的 `--thread-id`(或使用 `:topic:` 目标) Telegram 发送还支持: - - 当 `channels.telegram.capabilities.inlineButtons` 允许时,使用带 `buttons` 块的 `--presentation` 创建内联键盘 + - 当 `channels.telegram.capabilities.inlineButtons` 允许时,使用带 `buttons` 块的 `--presentation` 来创建内联键盘 - 当机器人可以在该聊天中置顶时,使用 `--pin` 或 `--delivery '{"pin":true}'` 请求置顶投递 - 使用 `--force-document` 将出站图片和 GIF 作为文档发送,而不是压缩照片或动画媒体上传 操作门控: - `channels.telegram.actions.sendMessage=false` 禁用出站 Telegram 消息,包括投票 - - `channels.telegram.actions.poll=false` 禁用 Telegram 投票创建,同时保留常规发送启用 + - `channels.telegram.actions.poll=false` 禁用 Telegram 投票创建,同时保留常规发送启用状态 - - Telegram 支持在审批者私信中进行执行审批,也可以选择在原始聊天或话题中发布提示。审批者必须是数字 Telegram 用户 ID。 + + Telegram 支持在审批人私信中进行 exec 审批,也可以选择在原始聊天或话题中发布提示。审批人必须是数字 Telegram 用户 ID。 配置路径: - - `channels.telegram.execApprovals.enabled`(至少一个审批者可解析时自动启用) - - `channels.telegram.execApprovals.approvers`(回退到 `commands.ownerAllowFrom` 中的数字所有者 ID) - - `channels.telegram.execApprovals.target`:`dm`(默认)| `channel` | `both` + - `channels.telegram.execApprovals.enabled`(至少解析到一个审批人时自动启用) + - `channels.telegram.execApprovals.approvers`(回退到来自 `commands.ownerAllowFrom` 的数字所有者 ID) + - `channels.telegram.execApprovals.target`: `dm`(默认)| `channel` | `both` - `agentFilter`、`sessionFilter` - `channels.telegram.allowFrom`、`groupAllowFrom` 和 `defaultTo` 控制谁可以与机器人对话,以及机器人向哪里发送普通回复。它们不会让某人成为执行审批者。当还没有命令所有者时,第一个已批准的私信配对会引导创建 `commands.ownerAllowFrom`,因此单所有者设置仍可工作,无需在 `execApprovals.approvers` 下重复 ID。 + `channels.telegram.allowFrom`、`groupAllowFrom` 和 `defaultTo` 控制谁可以和机器人对话,以及机器人将普通回复发送到哪里。它们不会让某人成为 exec 审批人。当还没有命令所有者时,首次获批的私信配对会引导生成 `commands.ownerAllowFrom`,因此单所有者设置仍可工作,而无需在 `execApprovals.approvers` 下重复 ID。 - 渠道投递会在聊天中显示命令文本;仅在受信任的群组/话题中启用 `channel` 或 `both`。当提示落在论坛话题中时,OpenClaw 会为审批提示和后续消息保留该话题。执行审批默认在 30 分钟后过期。 + 渠道投递会在聊天中显示命令文本;仅在可信群组/话题中启用 `channel` 或 `both`。当提示落在论坛话题中时,OpenClaw 会为审批提示和后续消息保留该话题。Exec 审批默认在 30 分钟后过期。 - 内联审批按钮还需要 `channels.telegram.capabilities.inlineButtons` 允许目标表面(`dm`、`group` 或 `all`)。带 `plugin:` 前缀的审批 ID 会通过插件审批解析;其他 ID 会先通过执行审批解析。 + 内联审批按钮还要求 `channels.telegram.capabilities.inlineButtons` 允许目标表面(`dm`、`group` 或 `all`)。带有 `plugin:` 前缀的审批 ID 会通过插件审批解析;其他 ID 会先通过 exec 审批解析。 - 参见[执行审批](/zh-CN/tools/exec-approvals)。 + 参见 [Exec 审批](/zh-CN/tools/exec-approvals)。 ## 错误回复控制 -当智能体遇到投递或提供商错误时,Telegram 可以回复错误文本,也可以将其抑制。两个配置键控制此行为: +当智能体遇到投递或提供商错误时,Telegram 可以回复错误文本,也可以抑制它。两个配置键控制此行为: -| 键 | 值 | 默认值 | 描述 | -| ----------------------------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------------- | -| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply` 会向聊天发送友好的错误消息。`silent` 会完全抑制错误回复。 | -| `channels.telegram.errorCooldownMs` | number (ms) | `60000` | 向同一聊天发送错误回复之间的最短时间。可防止中断期间出现错误刷屏。 | +| 键 | 值 | 默认值 | 说明 | +| ----------------------------------- | ----------------- | ------- | ------------------------------------------------------------------------------------------------ | +| `channels.telegram.errorPolicy` | `reply`, `silent` | `reply` | `reply` 会向聊天发送友好的错误消息。`silent` 会完全抑制错误回复。 | +| `channels.telegram.errorCooldownMs` | 数字(ms) | `60000` | 向同一聊天发送错误回复之间的最短时间。在故障期间防止错误刷屏。 | 支持按账号、按群组和按话题覆盖(继承方式与其他 Telegram 配置键相同)。 @@ -829,13 +829,13 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ ## 故障排除 - + - 如果 `requireMention=false`,Telegram 隐私模式必须允许完整可见性。 - BotFather:`/setprivacy` -> Disable - - 然后将机器人从群组移除并重新添加 + - 然后将机器人从群组中移除并重新添加 - 当配置期望未提及的群组消息时,`openclaw channels status` 会发出警告。 - - `openclaw channels status --probe` 可以检查明确的数字群组 ID;通配符 `"*"` 无法进行成员资格探测。 + - `openclaw channels status --probe` 可以检查显式数字群组 ID;通配符 `"*"` 无法探测成员身份。 - 快速会话测试:`/activation always`。 @@ -843,40 +843,40 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \ - 当 `channels.telegram.groups` 存在时,群组必须列出(或包含 `"*"`) - - 验证机器人是否为群组成员 - - 查看日志:`openclaw logs --follow` 查看跳过原因 + - 验证机器人在群组中的成员身份 + - 查看日志:`openclaw logs --follow`,了解跳过原因 - 授权你的发送者身份(配对和/或数字 `allowFrom`) - - 即使群组策略为 `open`,命令授权仍然适用 - - `setMyCommands failed` 加上 `BOT_COMMANDS_TOO_MUCH` 表示原生命令菜单条目过多;减少插件/skill/自定义命令,或禁用原生菜单 - - 启动时的 `deleteMyCommands` / `setMyCommands` 调用是有界的,并会在请求超时时通过 Telegram 的传输回退重试一次。持续的网络/fetch 错误通常表示到 `api.telegram.org` 的 DNS/HTTPS 可达性问题 + - 即使群组策略是 `open`,命令授权仍然适用 + - `setMyCommands failed` 并带有 `BOT_COMMANDS_TOO_MUCH` 表示原生命令菜单条目太多;减少插件/skill/自定义命令,或禁用原生菜单 + - 启动时的 `deleteMyCommands` / `setMyCommands` 调用是有界的,并会在请求超时时通过 Telegram 的传输回退重试一次。持续的网络/fetch 错误通常表示到 `api.telegram.org` 的 DNS/HTTPS 可达性存在问题 - + - - `getMe returned 401` 是已配置机器人令牌的 Telegram 身份验证失败。 - - 在 BotFather 中重新复制或重新生成机器人令牌,然后更新默认账号的 `channels.telegram.botToken`、`channels.telegram.tokenFile`、`channels.telegram.accounts..botToken` 或 `TELEGRAM_BOT_TOKEN`。 - - 启动期间的 `deleteWebhook 401 Unauthorized` 也是身份验证失败;将其视为“没有 webhook 存在”只会把同一个错误令牌失败推迟到后续 API 调用。 + - `getMe returned 401` 是配置的机器人 token 的 Telegram 身份验证失败。 + - 在 BotFather 中重新复制或重新生成机器人 token,然后更新默认账号的 `channels.telegram.botToken`、`channels.telegram.tokenFile`、`channels.telegram.accounts..botToken` 或 `TELEGRAM_BOT_TOKEN`。 + - 启动期间的 `deleteWebhook 401 Unauthorized` 也是身份验证失败;将其当作“没有 webhook 存在”只会把同一个错误 token 失败推迟到后续 API 调用。 - 如果 `deleteWebhook` 在轮询启动期间因瞬时网络错误失败,OpenClaw 会检查 `getWebhookInfo`;当 Telegram 报告空 webhook URL 时,轮询会继续,因为清理已经满足。 - - Node 22+ 与自定义 fetch/代理在 AbortSignal 类型不匹配时可能触发立即中止行为。 - - 一些主机会优先将 `api.telegram.org` 解析为 IPv6;损坏的 IPv6 出站连接可能导致间歇性 Telegram API 失败。 - - 如果日志包含 `TypeError: fetch failed` 或 `Network request for 'getUpdates' failed!`,OpenClaw 现在会将这些错误作为可恢复的网络错误重试。 - - 如果日志包含 `Polling stall detected`,OpenClaw 默认会在 120 秒内没有完成的长轮询活性后重启轮询并重建 Telegram 传输。 - - 当正在运行的轮询账号在启动宽限期后尚未完成 `getUpdates`,或其最后一次成功的轮询传输活动已过期时,`openclaw channels status --probe` 和 `openclaw doctor` 会发出警告。 - - 只有当长时间运行的 `getUpdates` 调用健康,但你的主机仍报告误报的轮询停滞重启时,才增大 `channels.telegram.pollingStallThresholdMs`。持续停滞通常指向主机与 `api.telegram.org` 之间的代理、DNS、IPv6 或 TLS 出站问题。 - - Telegram 还会遵循进程代理环境变量用于 Bot API 传输,包括 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 及其小写变体。`NO_PROXY` / `no_proxy` 仍然可以绕过 `api.telegram.org`。 - - 如果在服务环境中通过 `OPENCLAW_PROXY_URL` 配置了 OpenClaw 托管代理,且不存在标准代理环境变量,Telegram 也会将该 URL 用于 Bot API 传输。 - - 在直连出站/TLS 不稳定的 VPS 主机上,通过 `channels.telegram.proxy` 路由 Telegram API 调用: + - Node 22+ + 自定义 fetch/proxy 可能会在 AbortSignal 类型不匹配时触发立即中止行为。 + - 一些主机会先把 `api.telegram.org` 解析为 IPv6;损坏的 IPv6 出站可能导致 Telegram API 间歇性失败。 + - 如果日志包含 `TypeError: fetch failed` 或 `Network request for 'getUpdates' failed!`,OpenClaw 现在会将这些作为可恢复的网络错误重试。 + - 如果日志包含 `Polling stall detected`,OpenClaw 默认会在 120 秒内没有完成长轮询活性检查后重启轮询,并重建 Telegram 传输。 + - 当正在运行的轮询账号在启动宽限期后没有完成 `getUpdates`、正在运行的 webhook 账号在启动宽限期后没有完成 `setWebhook`,或上一次成功的轮询传输活动已过期时,`openclaw channels status --probe` 和 `openclaw doctor` 会发出警告。 + - 只有在长时间运行的 `getUpdates` 调用健康,但你的主机仍报告误报的轮询停滞重启时,才增大 `channels.telegram.pollingStallThresholdMs`。持续停滞通常指向主机与 `api.telegram.org` 之间的代理、DNS、IPv6 或 TLS 出站问题。 + - Telegram 的 Bot API 传输也遵循进程代理环境变量,包括 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 及其小写变体。`NO_PROXY` / `no_proxy` 仍可绕过 `api.telegram.org`。 + - 如果服务环境通过 `OPENCLAW_PROXY_URL` 配置了 OpenClaw 托管代理,且不存在标准代理环境变量,Telegram 也会将该 URL 用于 Bot API 传输。 + - 在直接出站/TLS 不稳定的 VPS 主机上,通过 `channels.telegram.proxy` 路由 Telegram API 调用: ```yaml channels: @@ -894,7 +894,7 @@ channels: autoSelectFamily: false ``` - - 默认已允许 Telegram 媒体下载使用 RFC 2544 基准范围响应(`198.18.0.0/15`)。如果受信任的 fake-IP 或透明代理在媒体下载期间将 `api.telegram.org` 重写为其他私有/内部/特殊用途地址,你可以选择启用仅限 Telegram 的绕过: + - RFC 2544 基准范围答案(`198.18.0.0/15`)默认已经允许用于 Telegram 媒体下载。如果受信任的 fake-IP 或透明代理在媒体下载期间将 `api.telegram.org` 重写为其他私有/内部/特殊用途地址,你可以选择启用仅限 Telegram 的绕过: ```yaml channels: @@ -903,20 +903,18 @@ channels: dangerouslyAllowPrivateNetwork: true ``` - - 同一选择启用项也可在每个账号级别使用: - `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork`。 - - 如果你的代理将 Telegram 媒体主机解析到 `198.18.x.x`,请先保持危险标志关闭。Telegram 媒体默认已允许 RFC 2544 基准范围。 + - 同一选择性启用也可按账号设置在 `channels.telegram.accounts..network.dangerouslyAllowPrivateNetwork`。 + - 如果你的代理将 Telegram 媒体主机解析为 `198.18.x.x`,请先关闭危险标志。Telegram 媒体默认已允许 RFC 2544 基准范围。 - `channels.telegram.network.dangerouslyAllowPrivateNetwork` 会削弱 Telegram - 媒体 SSRF 保护。仅在受信任、由操作员控制的代理环境中使用它,例如 Clash、Mihomo 或 Surge fake-IP 路由,且它们会合成 RFC 2544 基准范围之外的私有或特殊用途响应。正常公共互联网 Telegram 访问应保持关闭。 + `channels.telegram.network.dangerouslyAllowPrivateNetwork` 会削弱 Telegram 媒体 SSRF 防护。仅在受信任、由操作员控制的代理环境中使用,例如 Clash、Mihomo 或 Surge fake-IP 路由,并且它们会合成 RFC 2544 基准范围之外的私有或特殊用途答案。正常公共互联网 Telegram 访问请保持关闭。 - - 环境变量覆盖(临时): + - 环境覆盖(临时): - `OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY=1` - `OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY=1` - `OPENCLAW_TELEGRAM_DNS_RESULT_ORDER=ipv4first` - - 验证 DNS 响应: + - 验证 DNS 答案: ```bash dig +short api.telegram.org A @@ -932,27 +930,27 @@ dig +short api.telegram.org AAAA 主要参考:[配置参考 - Telegram](/zh-CN/gateway/config-channels#telegram)。 - + - 启动/认证:`enabled`、`botToken`、`tokenFile`、`accounts.*`(`tokenFile` 必须指向常规文件;符号链接会被拒绝) - 访问控制:`dmPolicy`、`allowFrom`、`groupPolicy`、`groupAllowFrom`、`groups`、`groups.*.topics.*`、顶层 `bindings[]`(`type: "acp"`) -- 执行审批:`execApprovals`、`accounts.*.execApprovals` +- exec 审批:`execApprovals`、`accounts.*.execApprovals` - 命令/菜单:`commands.native`、`commands.nativeSkills`、`customCommands` - 线程/回复:`replyToMode` - 流式传输:`streaming`(预览)、`streaming.preview.toolProgress`、`blockStreaming` -- 格式化/投递:`textChunkLimit`、`chunkMode`、`linkPreview`、`responsePrefix` +- 格式化/递送:`textChunkLimit`、`chunkMode`、`linkPreview`、`responsePrefix` - 媒体/网络:`mediaMaxMb`、`timeoutSeconds`、`pollingStallThresholdMs`、`retry`、`network.autoSelectFamily`、`network.dangerouslyAllowPrivateNetwork`、`proxy` -- 自定义 API 根路径:`apiRoot`(仅 Bot API 根路径;不要包含 `/bot`) +- 自定义 API 根地址:`apiRoot`(仅 Bot API 根地址;不要包含 `/bot`) - webhook:`webhookUrl`、`webhookSecret`、`webhookPath`、`webhookHost` - 操作/能力:`capabilities.inlineButtons`、`actions.sendMessage|editMessage|deleteMessage|reactions|sticker` -- 反应:`reactionNotifications`、`reactionLevel` +- reactions:`reactionNotifications`、`reactionLevel` - 错误:`errorPolicy`、`errorCooldownMs` - 写入/历史:`configWrites`、`historyLimit`、`dmHistoryLimit`、`dms.*.historyLimit` -多账号优先级:配置两个或更多账号 ID 时,设置 `channels.telegram.defaultAccount`(或包含 `channels.telegram.accounts.default`)以显式指定默认路由。否则 OpenClaw 会回退到第一个规范化账号 ID,并且 `openclaw doctor` 会发出警告。命名账号会继承 `channels.telegram.allowFrom` / `groupAllowFrom`,但不会继承 `accounts.default.*` 值。 +多账号优先级:配置两个或更多账号 ID 时,请设置 `channels.telegram.defaultAccount`(或包含 `channels.telegram.accounts.default`),以显式指定默认路由。否则,OpenClaw 会回退到第一个规范化账号 ID,并且 `openclaw doctor` 会发出警告。命名账号会继承 `channels.telegram.allowFrom` / `groupAllowFrom`,但不会继承 `accounts.default.*` 值。 ## 相关内容 @@ -962,7 +960,7 @@ dig +short api.telegram.org AAAA 将 Telegram 用户配对到 Gateway 网关。 - 群组和主题允许列表行为。 + 群组和 topic 允许列表行为。 将入站消息路由到智能体。 @@ -971,7 +969,7 @@ dig +short api.telegram.org AAAA 威胁模型与加固。 - 将群组和主题映射到智能体。 + 将群组和 topic 映射到智能体。 跨渠道诊断。 diff --git a/docs/zh-CN/ci.md b/docs/zh-CN/ci.md index ba126b372..c1335c143 100644 --- a/docs/zh-CN/ci.md +++ b/docs/zh-CN/ci.md @@ -1,67 +1,67 @@ --- read_when: - - 你需要了解为什么某个 CI 作业运行了或未运行 + - 你需要了解某个 CI 作业为什么运行或没有运行 - 你正在调试失败的 GitHub Actions 检查 -summary: CI 作业图、范围门控和本地命令等效项 +summary: CI 作业图、范围门禁和本地等效命令 title: CI 流水线 x-i18n: - generated_at: "2026-04-29T13:37:11Z" + generated_at: "2026-04-29T14:46:37Z" model: gpt-5.5 provider: openai - source_hash: 706dfe3a1b92a4e561ec76d8a6f192ad5d821f4c21ab546d28a9a1f6d4b962cb + source_hash: b3c51dff5db84e2d11f98b363a55d0d21309eeb9fce00fe90a8a9013c9c80385 source_path: ci.md workflow: 16 --- -CI 会在每次推送到 `main` 以及每个拉取请求时运行。它使用智能作用域,在只有无关区域发生变化时跳过昂贵的作业。手动 `workflow_dispatch` 运行会有意绕过智能作用域,并展开完整的常规 CI 图,用于发布候选版本或广泛验证;对于独立手动运行,Android 通道需通过 `include_android` 选择启用。仅发布用的插件预发布通道位于单独的 `Plugin Prerelease` 工作流中,并且只会从 `Full Release Validation` 或显式手动调度运行。 +CI 会在每次推送到 `main` 和每个 pull request 时运行。它使用智能作用域,在只有不相关区域发生变更时跳过高开销任务。手动 `workflow_dispatch` 运行会有意绕过智能作用域,并为候选发布或广泛验证展开完整的常规 CI 图;对于独立手动运行,Android lane 通过 `include_android` 选择启用。仅发布用的插件预发布 lane 位于单独的 `Plugin Prerelease` 工作流中,只会从 `Full Release Validation` 或显式手动派发运行。 -`check-dependencies` 分片会运行 `pnpm deadcode:dependencies`,这是一个仅针对生产 Knip 依赖项的检查过程,固定使用该脚本所用的最新 Knip 版本,并在 `dlx` 安装时禁用 pnpm 的最小发布时间限制。它还会运行 `pnpm deadcode:unused-files`,将 Knip 的生产未使用文件发现结果与 `scripts/deadcode-unused-files.allowlist.mjs` 进行比较。当 PR 添加新的未经审查的未使用文件,或在清理后留下过期的允许列表条目时,该防护会失败,同时保留 Knip 无法静态解析的有意动态插件、生成文件、构建、实时测试和软件包桥接表面。 +`check-dependencies` 分片运行 `pnpm deadcode:dependencies`,这是一次仅针对生产 Knip 依赖项的检查,固定使用该脚本所用的最新 Knip 版本,并在 `dlx` 安装时禁用 pnpm 的最低发布时长。它还运行 `pnpm deadcode:unused-files`,将 Knip 的生产未使用文件发现结果与 `scripts/deadcode-unused-files.allowlist.mjs` 进行比较。当 PR 新增未经审核的未使用文件,或清理后留下过时的 allowlist 条目时,该防护会失败,同时保留 Knip 无法静态解析的有意动态插件、生成文件、构建、live-test 和 package bridge 表面。 -`Full Release Validation` 是用于“发布前运行所有内容”的手动总括工作流。它接受分支、标签或完整提交 SHA,使用该目标调度手动 `CI` 工作流,调度 `Plugin Prerelease` 以提供仅发布用的插件/软件包/静态/Docker 证明,并调度 `OpenClaw Release Checks` 以执行安装冒烟、软件包验收、Docker 发布路径套件、实时/E2E、OpenWebUI、QA Lab parity、Matrix 和 Telegram 通道。当提供已发布的软件包规范时,它还可以运行发布后的 `NPM Telegram Beta E2E` 工作流。`release_profile=minimum|stable|full` 控制传递给发布检查的实时/提供商覆盖广度:`minimum` 保留最快的 OpenAI/core 发布关键通道,`stable` 添加稳定的提供商/后端集合,而 `full` 运行广泛的建议提供商/媒体矩阵。该总括工作流会记录已调度的子运行 ID,最终的 `Verify full validation` 作业会重新检查当前子运行结论,并为每个子运行追加最慢作业表。如果某个子工作流重新运行后变为绿色,只需重新运行父级验证器作业,以刷新总括结果和耗时摘要。 +`Full Release Validation` 是用于“发布前运行所有内容”的手动总控工作流。它接受分支、标签或完整 commit SHA,使用该目标派发手动 `CI` 工作流,派发 `Plugin Prerelease` 以提供仅发布用的插件、package、静态和 Docker 证明,并派发 `OpenClaw Release Checks` 以执行安装 smoke、package acceptance、Docker 发布路径套件、live/E2E、OpenWebUI、QA Lab parity、Matrix 和 Telegram lane。当提供已发布 package 规格时,它还可以运行发布后的 `NPM Telegram Beta E2E` 工作流。`release_profile=minimum|stable|full` 控制传入 release checks 的 live/provider 覆盖广度:`minimum` 保留最快的 OpenAI/core 发布关键 lane,`stable` 添加稳定的 provider/backend 集合,`full` 运行广泛的 advisory provider/media 矩阵。该总控会记录已派发的子运行 ID,最终的 `Verify full validation` 作业会重新检查当前子运行结论,并为每个子运行追加最慢作业表。如果重新运行某个子工作流后变绿,只需重新运行父级 verifier 作业,以刷新总控结果和耗时摘要。 -对于恢复,`Full Release Validation` 和 `OpenClaw Release Checks` 都接受 `rerun_group`。对发布候选版本使用 `all`,只针对常规完整 CI 子项使用 `ci`,针对每个发布子项使用 `release-checks`,或在总括工作流中使用更窄的发布分组:`install-smoke`、`cross-os`、`live-e2e`、`package`、`qa`、`qa-parity`、`qa-live` 或 `npm-telegram`。这样在完成针对性修复后,可以将失败发布环境的重新运行范围控制住。 +对于恢复,`Full Release Validation` 和 `OpenClaw Release Checks` 都接受 `rerun_group`。对候选发布使用 `all`,仅对常规完整 CI 子项使用 `ci`,对每个发布子项使用 `release-checks`,或在总控上使用更窄的发布组:`install-smoke`、`cross-os`、`live-e2e`、`package`、`qa`、`qa-parity`、`qa-live` 或 `npm-telegram`。这能让失败的发布箱在聚焦修复后保持有界重跑。 -发布实时/E2E 子项保留了广泛的原生 `pnpm test:live` 覆盖范围,但它会通过 `scripts/test-live-shard.mjs` 以命名分片运行(`native-live-src-agents`、`native-live-src-gateway-core`、按提供商过滤的 `native-live-src-gateway-profiles` 作业、`native-live-src-gateway-backends`、`native-live-test`、`native-live-extensions-a-k`、`native-live-extensions-l-n`、`native-live-extensions-openai`、`native-live-extensions-o-z-other`、`native-live-extensions-xai`、拆分后的媒体音频/视频分片,以及按提供商过滤的音乐分片),而不是一个串行作业。这样在保持相同文件覆盖范围的同时,让较慢的实时提供商失败更容易重新运行和诊断。聚合的 `native-live-extensions-o-z`、`native-live-extensions-media` 和 `native-live-extensions-media-music` 分片名称仍然可用于手动一次性重新运行。 +release live/E2E 子项保留广泛的原生 `pnpm test:live` 覆盖,但会通过 `scripts/test-live-shard.mjs` 将其作为命名分片运行(`native-live-src-agents`、`native-live-src-gateway-core`、经过 provider 过滤的 `native-live-src-gateway-profiles` 作业、`native-live-src-gateway-backends`、`native-live-test`、`native-live-extensions-a-k`、`native-live-extensions-l-n`、`native-live-extensions-openai`、`native-live-extensions-o-z-other`、`native-live-extensions-xai`、拆分的 media audio/video 分片,以及经过 provider 过滤的 music 分片),而不是一个串行作业。这样保持相同的文件覆盖,同时让缓慢的 live provider 失败更易重跑和诊断。聚合的 `native-live-extensions-o-z`、`native-live-extensions-media` 和 `native-live-extensions-media-music` 分片名称仍可用于手动一次性重跑。 -原生实时媒体分片在 `ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04` 中运行,该镜像由 `Live Media Runner Image` 工作流构建。该镜像预安装了 `ffmpeg` 和 `ffprobe`;媒体作业只会在设置前验证这些二进制文件。将 Docker 支持的实时套件保留在普通 Blacksmith 运行器上,因为容器作业并不适合启动嵌套 Docker 测试。 +原生 live media 分片运行在 `ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04` 中,该镜像由 `Live Media Runner Image` 工作流构建。该镜像预装 `ffmpeg` 和 `ffprobe`;media 作业只会在设置前验证二进制文件。将 Docker 支撑的 live 套件保留在普通 Blacksmith runner 上,因为 container job 不适合启动嵌套 Docker 测试。 -Docker 支持的实时模型/后端分片会针对每个选定提交使用单独共享的 `ghcr.io/openclaw/openclaw-live-test:` 镜像。实时发布工作流会构建并推送该镜像一次,然后 Docker 实时模型、Gateway 网关、CLI 后端、ACP 绑定和 Codex harness 分片会使用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行。如果这些分片独立重建完整源 Docker 目标,则说明发布运行配置错误,并会把耗时浪费在重复镜像构建上。 +Docker 支撑的 live model/backend 分片会为每个选定 commit 使用单独共享的 `ghcr.io/openclaw/openclaw-live-test:` 镜像。live release 工作流会构建并推送该镜像一次,然后 Docker live 模型、Gateway 网关、CLI backend、ACP bind 和 Codex harness 分片以 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行。如果这些分片独立重建完整源 Docker target,则说明发布运行配置错误,并会把耗时浪费在重复镜像构建上。 -`OpenClaw Release Checks` 使用受信任的工作流 ref 将选定 ref 解析一次为 `release-package-under-test` tarball,然后将该构件传递给实时/E2E 发布路径 Docker 工作流和软件包验收分片。这样可以让软件包字节在各发布环境中保持一致,并避免在多个子作业中重新打包同一个候选版本。 +`OpenClaw Release Checks` 使用受信任的 workflow ref 将所选 ref 一次性解析为 `release-package-under-test` tarball,然后将该 artifact 传给 live/E2E 发布路径 Docker 工作流和 package acceptance 分片。这能让 package 字节在各个发布箱中保持一致,并避免在多个子作业中重新打包同一个候选项。 -`Package Acceptance` 是用于验证软件包构件且不阻塞发布工作流的旁路运行工作流。它会从已发布的 npm 规范、使用选定 `workflow_ref` harness 构建的受信任 `package_ref`、带 SHA-256 的 HTTPS tarball URL,或另一个 GitHub Actions 运行中的 tarball 构件解析一个候选包,将其作为 `package-under-test` 上传,然后复用 Docker 发布/E2E 调度器,并使用该 tarball,而不是重新打包工作流检出内容。配置档覆盖冒烟、软件包、产品、完整和自定义 Docker 通道选择。`package` 配置档使用离线插件覆盖范围,因此已发布软件包验证不会受实时 ClawHub 可用性限制。可选 Telegram 通道会在 `NPM Telegram Beta E2E` 工作流中复用 `package-under-test` 构件,而已发布 npm 规范路径则保留用于独立调度。 +`Package Acceptance` 是用于验证 package artifact 且不阻塞发布工作流的旁路运行工作流。它会从已发布 npm 规格、使用所选 `workflow_ref` harness 构建的受信任 `package_ref`、带 SHA-256 的 HTTPS tarball URL,或来自另一个 GitHub Actions 运行的 tarball artifact 中解析一个候选项,将其上传为 `package-under-test`,然后使用该 tarball 复用 Docker release/E2E 调度器,而不是重新打包 workflow checkout。profile 覆盖 smoke、package、product、full 和自定义 Docker lane 选择。`package` profile 使用离线插件覆盖,因此已发布 package 验证不会受 live ClawHub 可用性约束。可选 Telegram lane 在 `NPM Telegram Beta E2E` 工作流中复用 `package-under-test` artifact,同时保留已发布 npm 规格路径以供独立派发使用。 -## 软件包验收 +## Package acceptance -当问题是“这个可安装的 OpenClaw 软件包作为产品是否可用?”时,使用 `Package Acceptance`。它不同于常规 CI:常规 CI 验证源代码树,而软件包验收通过用户在安装或更新后实际使用的同一个 Docker E2E harness 来验证单个 tarball。 +当问题是“这个可安装的 OpenClaw package 作为产品能否正常工作?”时,使用 `Package Acceptance`。它不同于常规 CI:常规 CI 验证源代码树,而 package acceptance 会通过用户安装或更新后使用的同一 Docker E2E harness 来验证单个 tarball。 -该工作流有四个作业: +该工作流包含四个作业: -1. `resolve_package` 检出 `workflow_ref`,解析一个软件包候选项,写入 `.artifacts/docker-e2e-package/openclaw-current.tgz`,写入 `.artifacts/docker-e2e-package/package-candidate.json`,将二者作为 `package-under-test` 构件上传,并在 GitHub 步骤摘要中打印来源、工作流 ref、软件包 ref、版本、SHA-256 和配置档。 -2. `docker_acceptance` 使用 `ref=workflow_ref` 和 `package_artifact_name=package-under-test` 调用 `openclaw-live-and-e2e-checks-reusable.yml`。可复用工作流会下载该构件,验证 tarball 清单,在需要时准备 package-digest Docker 镜像,并针对该软件包运行选定的 Docker 通道,而不是打包工作流检出内容。当某个配置档选择多个目标 `docker_lanes` 时,可复用工作流会准备一次软件包和共享镜像,然后将这些通道展开为带有唯一构件的并行目标 Docker 作业。 -3. `package_telegram` 可选择调用 `NPM Telegram Beta E2E`。当 `telegram_mode` 不是 `none` 时运行,并且在软件包验收已解析出候选包时安装同一个 `package-under-test` 构件;独立 Telegram 调度仍然可以安装已发布的 npm 规范。 -4. 如果软件包解析、Docker 验收或可选 Telegram 通道失败,`summary` 会使工作流失败。 +1. `resolve_package` checkout `workflow_ref`,解析一个 package 候选项,写入 `.artifacts/docker-e2e-package/openclaw-current.tgz`,写入 `.artifacts/docker-e2e-package/package-candidate.json`,将两者作为 `package-under-test` artifact 上传,并在 GitHub 步骤摘要中打印来源、workflow ref、package ref、版本、SHA-256 和 profile。 +2. `docker_acceptance` 调用 `openclaw-live-and-e2e-checks-reusable.yml`,并传入 `ref=workflow_ref` 和 `package_artifact_name=package-under-test`。可复用工作流会下载该 artifact,验证 tarball inventory,在需要时准备 package-digest Docker 镜像,并针对该 package 运行所选 Docker lane,而不是打包 workflow checkout。当某个 profile 选择多个目标 `docker_lanes` 时,可复用工作流会准备 package 和共享镜像一次,然后将这些 lane 展开为并行的目标 Docker 作业,并使用唯一 artifact。 +3. `package_telegram` 可选调用 `NPM Telegram Beta E2E`。当 `telegram_mode` 不是 `none` 时运行,并在 Package Acceptance 解析出候选项时安装同一个 `package-under-test` artifact;独立 Telegram 派发仍可安装已发布 npm 规格。 +4. `summary` 会在 package 解析、Docker acceptance 或可选 Telegram lane 失败时使工作流失败。 -候选来源: +候选项来源: -- `source=npm`:只接受 `openclaw@beta`、`openclaw@latest`,或精确的 OpenClaw 发布版本,例如 `openclaw@2026.4.27-beta.2`。用于已发布 beta/稳定版验收。 -- `source=ref`:打包受信任的 `package_ref` 分支、标签或完整提交 SHA。解析器会获取 OpenClaw 分支/标签,验证选定提交可从仓库分支历史或发布标签到达,在分离工作树中安装依赖,并使用 `scripts/package-openclaw-for-docker.mjs` 打包。 +- `source=npm`:仅接受 `openclaw@beta`、`openclaw@latest`,或精确的 OpenClaw 发布版本,例如 `openclaw@2026.4.27-beta.2`。将其用于已发布 beta/stable acceptance。 +- `source=ref`:打包受信任的 `package_ref` 分支、标签或完整 commit SHA。解析器会 fetch OpenClaw 分支/标签,验证所选 commit 可从仓库分支历史或发布标签到达,在 detached worktree 中安装依赖,并用 `scripts/package-openclaw-for-docker.mjs` 打包。 - `source=url`:下载 HTTPS `.tgz`;必须提供 `package_sha256`。 -- `source=artifact`:从 `artifact_run_id` 和 `artifact_name` 下载一个 `.tgz`;`package_sha256` 可选,但对于外部共享构件应提供。 +- `source=artifact`:从 `artifact_run_id` 和 `artifact_name` 下载一个 `.tgz`;`package_sha256` 可选,但外部共享 artifact 应提供。 -保持 `workflow_ref` 和 `package_ref` 分离。`workflow_ref` 是运行测试的受信任工作流/harness 代码。`package_ref` 是 `source=ref` 时被打包的源提交。这允许当前测试 harness 验证较旧的受信任源提交,而无需运行旧工作流逻辑。 +保持 `workflow_ref` 和 `package_ref` 分离。`workflow_ref` 是运行测试的受信任 workflow/harness 代码。`package_ref` 是当 `source=ref` 时被打包的源 commit。这允许当前测试 harness 验证较旧的受信任源 commit,而无需运行旧的工作流逻辑。 -配置档映射到 Docker 覆盖范围: +profile 映射到 Docker 覆盖: - `smoke`:`npm-onboard-channel-agent`、`gateway-network`、`config-reload` - `package`:`npm-onboard-channel-agent`、`doctor-switch`、`update-channel-switch`、`bundled-channel-deps-compat`、`plugins-offline`、`plugin-update` - `product`:`package` 加上 `mcp-channels`、`cron-mcp-cleanup`、`openai-web-search-minimal`、`openwebui` -- `full`:包含 OpenWebUI 的完整 Docker 发布路径分块 +- `full`:带 OpenWebUI 的完整 Docker 发布路径 chunk - `custom`:精确的 `docker_lanes`;当 `suite_profile=custom` 时必需 -发布检查会调用软件包验收,并使用 `source=ref`、`package_ref=`、`workflow_ref=`、`suite_profile=custom`、`docker_lanes='bundled-channel-deps-compat plugins-offline'` 和 `telegram_mode=mock-openai`。发布路径 Docker 分块覆盖重叠的软件包/更新/插件通道,而软件包验收会针对同一个已解析软件包 tarball 保留构件原生的内置渠道兼容性、离线插件和 Telegram 证明。跨操作系统发布检查仍覆盖操作系统特定的新手引导、安装器和平台行为;软件包/更新产品验证应从软件包验收开始。Windows 打包和安装器全新安装通道还会验证已安装的软件包可以从原始绝对 Windows 路径导入浏览器控制覆盖。OpenAI 跨操作系统智能体回合冒烟默认使用已设置的 `OPENCLAW_CROSS_OS_OPENAI_MODEL`,否则使用 `openai/gpt-5.4-mini`,因此安装和 Gateway 网关证明会保持快速且确定。专用实时提供商/模型通道仍覆盖更广泛的模型路由,包括较慢的前沿默认值。 +Release checks 调用 Package Acceptance 时使用 `source=ref`、`package_ref=`、`workflow_ref=`、`suite_profile=custom`、`docker_lanes='bundled-channel-deps-compat plugins-offline'` 和 `telegram_mode=mock-openai`。发布路径 Docker chunk 覆盖重叠的 package/update/plugin lane,而 Package Acceptance 会针对同一个已解析 package tarball 保留 artifact-native bundled-channel compat、offline plugin 和 Telegram 证明。Cross-OS release checks 仍覆盖特定 OS 的新手引导、安装器和平台行为;package/update 产品验证应从 Package Acceptance 开始。Windows packaged 和 installer fresh lane 还会验证已安装 package 能够从原始绝对 Windows 路径导入 browser-control override。OpenAI cross-OS agent-turn smoke 在设置了 `OPENCLAW_CROSS_OS_OPENAI_MODEL` 时默认使用它,否则使用 `openai/gpt-5.4-mini`,因此安装和 Gateway 网关证明保持快速且确定。专用 live provider/model lane 仍覆盖更广泛的模型路由,包括较慢的 frontier 默认值。 -软件包验收为已经发布的软件包设置了有限的旧版兼容窗口。直到 `2026.4.25` 的软件包(包括 `2026.4.25-beta.*`)可以对 `dist/postinstall-inventory.json` 中指向 tarball 省略文件的已知私有 QA 条目使用兼容路径;当软件包未暴露 `gateway install --wrapper` 标志时,`doctor-switch` 可以跳过其持久化子用例;`update-channel-switch` 可以从 tarball 派生的伪 git fixture 中删去缺失的 `pnpm.patchedDependencies`,并可以记录缺失的持久化 `update.channel`;插件冒烟可以读取旧版安装记录位置,或接受缺失的 marketplace 安装记录持久化;`plugin-update` 可以允许配置元数据迁移,同时仍要求安装记录和不重新安装行为保持不变。已发布的 `2026.4.26` 软件包也可以对已经随包发布的本地构建元数据戳文件发出警告。后续软件包必须满足现代合约;相同条件会失败,而不是警告或跳过。 +Package Acceptance 为已发布 package 设置了有界的旧版兼容窗口。直到 `2026.4.25` 的 package,包括 `2026.4.25-beta.*`,可以对 `dist/postinstall-inventory.json` 中指向 tarball 省略文件的已知私有 QA 条目使用兼容路径;当 package 未暴露该 flag 时,`doctor-switch` 可以跳过 `gateway install --wrapper` 持久化子用例;`update-channel-switch` 可以从 tarball 派生的 fake git fixture 中裁剪缺失的 `pnpm.patchedDependencies`,并可以记录缺失的已持久化 `update.channel`;plugin smoke 可以读取旧版 install-record 位置,或接受缺失的 marketplace install-record 持久化;`plugin-update` 可以允许配置元数据迁移,同时仍要求 install record 和 no-reinstall 行为保持不变。已发布的 `2026.4.26` package 也可以对已经发布的本地构建元数据 stamp 文件发出警告。之后的 package 必须满足现代合约;相同条件会失败,而不是警告或跳过。 示例: @@ -104,124 +104,23 @@ gh workflow run package-acceptance.yml \ -f docker_lanes='install-e2e plugin-update' ``` -调试失败的包验收运行时,请从 `resolve_package` -摘要开始,确认包来源、版本和 SHA-256。然后检查 -`docker_acceptance` 子运行及其 Docker 构件: -`.artifacts/docker-tests/**/summary.json`、`failures.json`、lane 日志、阶段 -计时和重新运行命令。优先重新运行失败的包 profile 或 -精确的 Docker lane,而不是重新运行完整发布验证。 +调试失败的包验收运行时,先从 `resolve_package` 摘要开始,确认包来源、版本和 SHA-256。然后检查 `docker_acceptance` 子运行及其 Docker 工件:`.artifacts/docker-tests/**/summary.json`、`failures.json`、通道日志、阶段耗时和重新运行命令。优先重新运行失败的包配置文件或精确的 Docker 通道,而不是重新运行完整发布验证。 -QA Lab 在主智能范围化 workflow 之外有专用 CI lane。 -`Parity gate` workflow 会在匹配的 PR 变更和手动分派时运行;它会 -构建私有 QA 运行时,并比较 mock GPT-5.5 和 Opus 4.6 -agentic 包。`QA-Lab - All Lanes` workflow 每晚在 `main` 上运行,也可 -手动分派;它会将 mock parity gate、live Matrix lane,以及 live -Telegram 和 Discord lane 扇出为并行 job。live job 使用 -`qa-live-shared` environment,Telegram/Discord 使用 Convex lease。发布 -检查会使用确定性的 mock 提供商和 mock 限定模型(`mock-openai/gpt-5.5` 和 -`mock-openai/gpt-5.5-alt`)运行 Matrix 和 Telegram live 传输 lane,因此渠道契约 -会与 live 模型延迟和正常提供商插件启动隔离。live 传输 Gateway 网关还会 -禁用内存搜索,因为 QA parity 会单独覆盖内存行为; -提供商连通性由单独的 live 模型、原生提供商和 Docker 提供商套件覆盖。Matrix 在计划和发布 gate 中使用 `--profile fast`, -仅当检出的 CLI 支持时才添加 `--fail-fast`。CLI 默认值 -和手动 workflow 输入仍为 `all`;手动 `matrix_profile=all` -分派始终会将完整 Matrix 覆盖分片为 `transport`、`media`、 -`e2ee-smoke`、`e2ee-deep` 和 `e2ee-cli` job。`OpenClaw Release Checks` 还会 -在发布批准前运行发布关键的 QA Lab lane;其 QA parity -gate 会将候选包和基线包作为并行 lane job 运行,然后将 -两个构件下载到一个小型报告 job 中,用于最终 parity 比较。 -不要把 PR 合并路径放在 `Parity gate` 后面,除非该变更确实 -触及 QA 运行时、模型包 parity,或 parity workflow 拥有的表面。 -对于普通渠道、配置、文档或单元测试修复,应将其视为可选 -信号,并遵循范围化的 CI/检查证据。 +QA Lab 在主智能范围工作流之外有专用 CI 通道。`Parity gate` 工作流会在匹配的 PR 变更和手动分发时运行;它会构建私有 QA 运行时,并比较模拟 GPT-5.5 和 Opus 4.6 智能体包。`QA-Lab - All Lanes` 工作流会在 `main` 上每晚运行,也可手动分发;它会将模拟一致性门禁、实时 Matrix 通道,以及实时 Telegram 和 Discord 通道作为并行作业展开。实时作业使用 `qa-live-shared` 环境,Telegram/Discord 使用 Convex 租约。发布检查会使用确定性模拟提供商和模拟限定模型(`mock-openai/gpt-5.5` 和 `mock-openai/gpt-5.5-alt`)运行 Matrix 和 Telegram 实时传输通道,因此渠道契约会与实时模型延迟和正常提供商插件启动隔离。实时传输 Gateway 网关还会禁用内存搜索,因为 QA 一致性会单独覆盖内存行为;提供商连通性由单独的实时模型、原生提供商和 Docker 提供商套件覆盖。Matrix 在计划门禁和发布门禁中使用 `--profile fast`,只有在检出的 CLI 支持时才会添加 `--fail-fast`。CLI 默认值和手动工作流输入保持为 `all`;手动 `matrix_profile=all` 分发始终会将完整 Matrix 覆盖分片为 `transport`、`media`、`e2ee-smoke`、`e2ee-deep` 和 `e2ee-cli` 作业。`OpenClaw Release Checks` 也会在发布批准前运行发布关键的 QA Lab 通道;其 QA 一致性门禁会将候选包和基线包作为并行通道作业运行,然后把两个工件下载到一个小型报告作业中,用于最终一致性比较。除非变更确实触及 QA 运行时、模型包一致性或一致性工作流拥有的表面,否则不要把 PR 落地路径放在 `Parity gate` 后面。对于普通渠道、配置、文档或单元测试修复,请将其视为可选信号,并改为遵循范围化的 CI/检查证据。 -`Duplicate PRs After Merge` workflow 是用于 -合并后重复项清理的手动维护者 workflow。它默认 dry-run,并且只有在 -`apply=true` 时才会关闭明确列出的 PR。在修改 GitHub 之前,它会验证 -已合并的 PR 已被 merge,并验证每个重复项要么有共享的引用 issue, -要么有重叠的变更 hunk。 +`Duplicate PRs After Merge` 工作流是一个用于落地后重复项清理的手动维护者工作流。它默认以 dry-run 运行,并且只有在 `apply=true` 时才会关闭显式列出的 PR。在修改 GitHub 之前,它会验证已落地 PR 已合并,并验证每个重复项要么有共享的引用问题,要么有重叠的变更代码块。 -`CodeQL` workflow 有意作为窄范围第一轮安全扫描器, -而不是完整仓库扫描。每日和手动运行会扫描 Actions workflow 代码, -以及最高风险的 JavaScript/TypeScript 凭证、密钥、沙箱、cron 和 -Gateway 网关表面,并使用高精度安全查询。 -channel-runtime-boundary job 会单独扫描核心渠道实现 -契约以及渠道插件运行时、Gateway 网关、插件 SDK、密钥和 -审计触点,并归入 `/codeql-critical-security/channel-runtime-boundary` -类别,使渠道安全信号无需扩大基线 -JS/TS 类别即可扩展。 +`CodeQL` 工作流有意作为窄范围的第一轮安全扫描器,而不是完整仓库扫描。每日和手动运行会使用高精度安全查询,扫描 Actions 工作流代码以及风险最高的 JavaScript/TypeScript 凭证、机密、沙箱、cron 和 Gateway 网关表面。channel-runtime-boundary 作业会单独扫描核心渠道实现契约,以及渠道插件运行时、Gateway 网关、插件 SDK、机密和审计触点,并归入 `/codeql-critical-security/channel-runtime-boundary` 类别,以便渠道安全信号可以在不扩展基线 JS/TS 类别的情况下扩展。 -`CodeQL Android Critical Security` workflow 是计划运行的 Android -安全分片。它会在 workflow sanity 接受的最小 -Blacksmith Linux runner 标签上为 CodeQL 手动构建 Android 应用,并将结果 -上传到 `/codeql-critical-security/android` 类别。 +`CodeQL Android Critical Security` 工作流是计划运行的 Android 安全分片。它会在 workflow sanity 接受的最小 Blacksmith Linux runner 标签上为 CodeQL 手动构建 Android 应用,并将结果上传到 `/codeql-critical-security/android` 类别下。 -`CodeQL macOS Critical Security` workflow 是每周/手动 macOS -安全分片。它会在 Blacksmith macOS 上为 CodeQL 手动构建 macOS 应用, -从上传的 SARIF 中过滤掉依赖构建结果,并将结果 -上传到 `/codeql-critical-security/macos` 类别。请将其置于每日 -默认 workflow 之外,因为即使结果干净,macOS 构建也会主导运行时间。 +`CodeQL macOS Critical Security` 工作流是每周/手动运行的 macOS 安全分片。它会在 Blacksmith macOS 上为 CodeQL 手动构建 macOS 应用,从上传的 SARIF 中过滤掉依赖构建结果,并将结果上传到 `/codeql-critical-security/macos` 类别下。请将它保持在每日默认工作流之外,因为即使结果干净,macOS 构建也会主导运行时间。 -`CodeQL Critical Quality` workflow 是对应的非安全分片。它 -仅在较小的 Blacksmith Linux runner 上,对窄范围高价值表面运行错误严重级别、 -非安全 JavaScript/TypeScript 质量查询。其 -core-auth-secrets job 会扫描凭证、密钥、沙箱、cron 和 Gateway 网关安全 -边界代码,并归入单独的 `/codeql-critical-quality/core-auth-secrets` -类别。config-boundary -job 会扫描配置 schema、迁移、规范化和 IO 契约,并归入 -单独的 `/codeql-critical-quality/config-boundary` 类别。 -gateway-runtime-boundary job 会扫描 Gateway 网关协议 schema 和服务器方法 -契约,并归入单独的 -`/codeql-critical-quality/gateway-runtime-boundary` 类别。 -channel-runtime-boundary job 会扫描核心渠道实现契约,并归入 -单独的 `/codeql-critical-quality/channel-runtime-boundary` 类别。 -agent-runtime-boundary job 会扫描命令执行、模型/提供商分派、 -自动回复分派和队列,以及 ACP 控制平面运行时契约,并归入 -单独的 `/codeql-critical-quality/agent-runtime-boundary` 类别。 -mcp-process-runtime-boundary job 会扫描 MCP server 和工具桥、进程 -监督 helper,以及出站交付契约,并归入单独的 -`/codeql-critical-quality/mcp-process-runtime-boundary` 类别。 -memory-runtime-boundary job 会扫描内存 host SDK、内存运行时 facade、 -内存插件 SDK alias、内存运行时激活 glue,以及内存 Doctor -命令,并归入单独的 `/codeql-critical-quality/memory-runtime-boundary` -类别。 -ui-control-plane job 会扫描 Control UI bootstrap、本地持久化、Gateway 网关 -控制流和任务控制平面运行时契约,并归入单独的 -`/codeql-critical-quality/ui-control-plane` 类别。 -web-media-runtime-boundary job 会扫描核心 web fetch/search、media IO、media -understanding、image-generation 和 media-generation 运行时契约,并归入 -单独的 `/codeql-critical-quality/web-media-runtime-boundary` 类别。 -plugin-boundary job 会扫描 loader、registry、public-surface 和插件 SDK -入口点契约,并归入单独的 `/codeql-critical-quality/plugin-boundary` -类别。plugin-sdk-package-contract job 会扫描已发布包侧的 -插件 SDK 源码和插件包契约 helper,并归入单独的 -`/codeql-critical-quality/plugin-sdk-package-contract` 类别。请保持该 -workflow 与 security 分离,使质量发现可以被 -计划运行、度量、禁用或扩展,而不会掩盖安全信号。 -Swift、Python 和内置插件的 CodeQL 扩展应仅在窄 profile 拥有稳定 -运行时间和信号后,作为范围化或分片的后续工作重新加入。 +`CodeQL Critical Quality` 工作流是匹配的非安全分片。它只会在较小的 Blacksmith Linux runner 上,对窄范围的高价值表面运行错误级别的非安全 JavaScript/TypeScript 质量查询。它的手动分发接受 `profile=all|plugin-sdk-package-contract`;窄配置文件是用于隔离运行一个质量分片的首个教学/迭代钩子,不会分发工作流的其余部分。它的 core-auth-secrets 作业会扫描凭证、机密、沙箱、cron 和 Gateway 网关安全边界代码,并归入单独的 `/codeql-critical-quality/core-auth-secrets` 类别。config-boundary 作业会扫描配置 schema、迁移、规范化和 IO 契约,并归入单独的 `/codeql-critical-quality/config-boundary` 类别。gateway-runtime-boundary 作业会扫描 Gateway 网关协议 schema 和服务器方法契约,并归入单独的 `/codeql-critical-quality/gateway-runtime-boundary` 类别。channel-runtime-boundary 作业会扫描核心渠道实现契约,并归入单独的 `/codeql-critical-quality/channel-runtime-boundary` 类别。agent-runtime-boundary 作业会扫描命令执行、模型/提供商分发、自动回复分发与队列,以及 ACP 控制平面运行时契约,并归入单独的 `/codeql-critical-quality/agent-runtime-boundary` 类别。mcp-process-runtime-boundary 作业会扫描 MCP 服务器和工具桥、进程监督辅助工具,以及出站交付契约,并归入单独的 `/codeql-critical-quality/mcp-process-runtime-boundary` 类别。memory-runtime-boundary 作业会扫描内存主机 SDK、内存运行时门面、内存插件 SDK 别名、内存运行时激活粘合代码,以及内存 Doctor 命令,并归入单独的 `/codeql-critical-quality/memory-runtime-boundary` 类别。ui-control-plane 作业会扫描 Control UI 引导、本地持久化、Gateway 网关控制流,以及任务控制平面运行时契约,并归入单独的 `/codeql-critical-quality/ui-control-plane` 类别。web-media-runtime-boundary 作业会扫描核心网页获取/搜索、媒体 IO、媒体理解、图像生成和媒体生成运行时契约,并归入单独的 `/codeql-critical-quality/web-media-runtime-boundary` 类别。plugin-boundary 作业会扫描加载器、注册表、公开表面和插件 SDK 入口点契约,并归入单独的 `/codeql-critical-quality/plugin-boundary` 类别。plugin-sdk-package-contract 作业会扫描已发布包侧的插件 SDK 源代码和插件包契约辅助工具,并归入单独的 `/codeql-critical-quality/plugin-sdk-package-contract` 类别。请将该工作流与安全工作流分开,以便质量发现可以在不遮蔽安全信号的情况下进行计划、衡量、禁用或扩展。Swift、Python 和内置插件的 CodeQL 扩展应只在这些窄配置文件具备稳定运行时间和信号之后,作为范围化或分片化的后续工作加回。 -`Docs Agent` workflow 是一个事件驱动的 Codex 维护 lane,用于保持 -现有文档与最近合并的变更一致。它没有纯计划任务:在 `main` 上 -成功的非 bot push CI 运行可以触发它,手动分派也可以 -直接运行它。当 `main` 已经前进,或过去一小时内已创建另一个 -非跳过的 Docs Agent 运行时,workflow-run 调用会跳过。运行时,它会 -审查从上一个非跳过 Docs Agent source SHA 到当前 `main` 的 commit 范围, -因此一次每小时运行可以覆盖自上一次文档 pass 以来累积的所有 main 变更。 +`Docs Agent` 工作流是一个事件驱动的 Codex 维护通道,用于让现有文档与最近落地的变更保持一致。它没有纯计划运行:`main` 上成功的非 bot push CI 运行可以触发它,手动分发也可以直接运行它。工作流运行调用会在 `main` 已向前推进,或最近一小时内已创建另一个未跳过的 Docs Agent 运行时跳过。运行时,它会审查从上一个未跳过的 Docs Agent 源 SHA 到当前 `main` 的提交范围,因此一次每小时运行可以覆盖自上次文档轮次以来累积的所有 main 变更。 -`Test Performance Agent` workflow 是一个事件驱动的 Codex 维护 lane, -用于慢测试。它没有纯计划任务:在 `main` 上成功的非 bot push CI 运行 -可以触发它,但如果同一 UTC 日已有另一个 workflow-run 调用 -已经运行或正在运行,它会跳过。手动分派会绕过该每日活动 -gate。该 lane 会构建完整套件分组 Vitest 性能报告,让 Codex -只进行小型且保留覆盖率的测试性能修复,而不是大规模 -重构,然后重新运行完整套件报告,并拒绝会降低 -通过基线测试数量的变更。如果基线存在失败测试,Codex 只能修复 -明显失败,并且 agent 后的完整套件报告必须通过,之后 -才能提交任何内容。当 `main` 在 bot push 落地前前进时,该 lane -会 rebase 已验证的 patch,重新运行 `pnpm check:changed`,并重试 push; -有冲突的陈旧 patch 会被跳过。它使用 GitHub 托管的 Ubuntu,因此 Codex -action 可以保持与 docs agent 相同的 drop-sudo 安全姿态。 +`Test Performance Agent` 工作流是一个事件驱动的 Codex 维护通道,用于处理慢测试。它没有纯计划运行:`main` 上成功的非 bot push CI 运行可以触发它,但如果当天 UTC 已经有另一个工作流运行调用运行过或正在运行,它会跳过。手动分发会绕过该每日活动门禁。该通道会构建完整套件分组 Vitest 性能报告,让 Codex 只进行小型、保持覆盖率的测试性能修复,而不是大范围重构,然后重新运行完整套件报告,并拒绝会降低通过基线测试数量的变更。如果基线有失败测试,Codex 只能修复明显失败,并且 after-agent 完整套件报告必须在提交任何内容前通过。当 `main` 在 bot push 落地前推进时,该通道会 rebase 已验证的补丁,重新运行 `pnpm check:changed`,并重试 push;有冲突的陈旧补丁会被跳过。它使用 GitHub 托管的 Ubuntu,因此 Codex action 可以保持与 docs agent 相同的 drop-sudo 安全姿态。 ```bash gh workflow run duplicate-after-merge.yml \ @@ -230,32 +129,45 @@ gh workflow run duplicate-after-merge.yml \ -f apply=true ``` -## Job 概览 +## 作业概览 -| 作业 | 用途 | 运行时机 | -| -------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------- | -| `preflight` | 检测仅文档变更、变更范围、变更的插件,并构建 CI 清单 | 始终在非草稿推送和 PR 上运行 | -| `security-scm-fast` | 通过 `zizmor` 进行私钥检测和工作流审计 | 始终在非草稿推送和 PR 上运行 | -| `security-dependency-audit` | 针对 npm 公告进行无需依赖项安装的生产 lockfile 审计 | 始终在非草稿推送和 PR 上运行 | -| `security-fast` | 快速安全作业所需的聚合结果 | 始终在非草稿推送和 PR 上运行 | -| `build-artifacts` | 构建 `dist/`、Control UI、构建产物检查,以及可复用的下游产物 | Node 相关变更 | -| `checks-fast-core` | 快速 Linux 正确性通道,例如内置/插件契约/协议检查 | Node 相关变更 | -| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | Node 相关变更 | -| `checks-node-core-test` | 核心 Node 测试分片,不包括渠道、内置、契约和插件通道 | Node 相关变更 | -| `check` | 分片的主本地门禁等价项:生产类型、lint、守卫、测试类型和严格冒烟测试 | Node 相关变更 | -| `check-additional` | 架构、边界、插件表面守卫、包边界和 gateway-watch 分片 | Node 相关变更 | -| `build-smoke` | 已构建 CLI 冒烟测试和启动内存冒烟测试 | Node 相关变更 | -| `checks` | 已构建产物渠道测试的验证器 | Node 相关变更 | -| `checks-node-compat-node22` | Node 22 兼容性构建和冒烟通道 | 发布时手动 CI 分发 | -| `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 清单 | 非草稿推送和 PR 始终运行 | +| `security-scm-fast` | 通过 `zizmor` 检测私钥并审计工作流 | 非草稿推送和 PR 始终运行 | +| `security-dependency-audit` | 针对 npm advisories 执行无依赖的生产锁文件审计 | 非草稿推送和 PR 始终运行 | +| `security-fast` | 快速安全作业的必需聚合检查 | 非草稿推送和 PR 始终运行 | +| `build-artifacts` | 构建 `dist/`、Control UI、构建产物检查,以及可复用的下游产物 | Node 相关变更 | +| `checks-fast-core` | 快速 Linux 正确性 lanes,例如内置/插件契约/协议检查 | Node 相关变更 | +| `checks-fast-contracts-channels` | 分片的渠道契约检查,并提供稳定的聚合检查结果 | Node 相关变更 | +| `checks-node-core-test` | Core Node 测试分片,不包括渠道、内置、契约和插件 lanes | Node 相关变更 | +| `check` | 分片的主本地 gate 等价检查:生产类型、lint、guard、测试类型和严格 smoke | Node 相关变更 | +| `check-additional` | 架构、边界、插件表面 guard、package-boundary 和 gateway-watch 分片 | Node 相关变更 | +| `build-smoke` | 构建后的 CLI smoke 测试和启动内存 smoke | Node 相关变更 | +| `checks` | 构建产物渠道测试的验证器 | Node 相关变更 | +| `checks-node-compat-node22` | Node 22 兼容性构建和 smoke lane | 发布的手动 CI dispatch | +| `check-docs` | 文档格式、lint 和失效链接检查 | 文档已变更 | +| `skills-python` | 面向 Python 后端 Skills 的 Ruff + pytest | Python Skill 相关变更 | +| `checks-windows` | Windows 专用进程/路径测试,以及共享运行时导入说明符回归检查 | Windows 相关变更 | +| `macos-node` | 使用共享构建产物的 macOS TypeScript 测试 lane | macOS 相关变更 | +| `macos-swift` | macOS 应用的 Swift lint、构建和测试 | macOS 相关变更 | +| `android` | 两种 flavor 的 Android 单元测试,以及一次调试 APK 构建 | Android 相关变更 | +| `test-performance-agent` | 在可信活动之后每日执行 Codex 慢测试优化 | Main CI 成功或手动 dispatch | -手动 CI 分发会运行与常规 CI 相同的作业图,但会强制启用每个非 Android 范围通道:Linux Node 分片、内置插件分片、渠道契约、Node 22 兼容性、`check`、`check-additional`、构建冒烟测试、文档检查、Python Skills、Windows、macOS 和 Control UI i18n。独立的手动 CI 分发仅在 `include_android=true` 时运行 Android;完整发布总入口通过传入 `include_android=true` 启用 Android。插件预发布静态检查、仅发布使用的 `agentic-plugins` 分片、完整插件批量扫描,以及插件预发布 Docker 通道都不包含在 CI 中。Docker 预发布套件仅在 `Full Release Validation` 分发单独的 `Plugin Prerelease` 工作流并启用发布验证门禁时运行。手动运行使用唯一并发组,因此候选发布的完整套件不会被同一 ref 上的其他推送或 PR 运行取消。可选的 `target_ref` 输入允许受信调用方使用所选分发 ref 中的工作流文件,针对分支、标签或完整提交 SHA 运行该作业图。 +手动 CI dispatch 会运行与普通 CI 相同的作业图,但会强制启用每个 +非 Android 范围的 lane:Linux Node 分片、内置插件分片、渠道 +契约、Node 22 兼容性、`check`、`check-additional`、构建 smoke、文档 +检查、Python Skills、Windows、macOS 和 Control UI i18n。独立手动 CI +dispatch 仅在 `include_android=true` 时运行 Android;完整发布 +umbrella 会通过传入 `include_android=true` 启用 Android。插件预发布 +静态检查、仅发布使用的 `agentic-plugins` 分片、完整插件 +批量 sweep,以及插件预发布 Docker lanes 都不包含在 CI 中。Docker +预发布套件仅在 `Full Release Validation` dispatch 启用了 +release-validation gate 的独立 `Plugin Prerelease` 工作流时运行。 +手动运行使用一个 +唯一的并发组,因此发布候选完整套件不会被同一 ref 上的另一次推送或 PR 运行取消。可选的 `target_ref` 输入允许 +可信调用方在使用所选 dispatch ref 中的工作流文件的同时, +针对分支、tag 或完整 commit SHA 运行该作业图。 ```bash gh workflow run ci.yml --ref release/YYYY.M.D @@ -263,45 +175,54 @@ gh workflow run ci.yml --ref main -f target_ref= -f include_andro gh workflow run full-release-validation.yml --ref main -f ref= ``` -## 快速失败顺序 +## Fail-fast 顺序 -作业会按顺序排列,使廉价检查在昂贵检查运行前先失败: +作业按顺序排列,使低成本检查在高成本检查运行前先失败: -1. `preflight` 决定哪些通道实际存在。`docs-scope` 和 `changed-scope` 逻辑是此作业中的步骤,而不是独立作业。 +1. `preflight` 决定哪些 lanes 实际存在。`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-core-test`、`checks`、`checks-windows`、`macos-node`、`macos-swift` 和 `android`。 +3. `build-artifacts` 会与快速 Linux lanes 重叠运行,因此下游消费者可以在共享构建就绪后立即开始。 +4. 更重的平台和运行时 lanes 会在之后展开:`checks-fast-core`、`checks-fast-contracts-channels`、`checks-node-core-test`、`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 原生构建;这些平台通道仍然限定于平台源码变更。 -仅 CI 路由编辑、选定的低成本核心测试 fixture 编辑,以及范围较窄的插件契约辅助程序/测试路由编辑,会使用快速的仅 Node 清单路径:预检、安全检查,以及单个 `checks-fast-core` 任务。当变更文件仅限于该快速任务会直接覆盖的路由或辅助表面时,该路径会避开构建产物、Node 22 兼容性、渠道契约、完整核心分片、内置插件分片,以及额外的防护矩阵。 -Windows Node 检查限定于 Windows 特定的进程/路径包装器、npm/pnpm/UI 运行器辅助程序、包管理器配置,以及执行该通道的 CI 工作流表面;不相关的源码、插件、安装冒烟和仅测试变更会留在 Linux Node 通道上,因此它们不会为正常测试分片已经覆盖的内容占用一个 16-vCPU Windows worker。 -独立的 `install-smoke` 工作流会通过它自己的 `preflight` 作业复用同一个作用域脚本。它将冒烟覆盖拆分为 `run_fast_install_smoke` 和 `run_full_install_smoke`。Pull request 会针对 Docker/包表面、内置插件包/清单变更,以及 Docker 冒烟作业覆盖的核心插件/渠道/Gateway 网关/插件 SDK 表面运行快速路径。仅源码的内置插件变更、仅测试编辑和仅文档编辑不会占用 Docker worker。快速路径会构建一次根 Dockerfile 镜像,检查 CLI,运行 agents delete shared-workspace CLI 冒烟测试,运行容器 gateway-network e2e,验证内置扩展构建参数,并在 240 秒聚合命令超时内运行有界的内置插件 Docker 配置文件,且每个场景的 Docker run 会单独设限。完整路径会保留 QR 包安装和安装器 Docker/更新覆盖,用于夜间计划运行、手动分发、workflow-call 发布检查,以及真正触及安装器/包/Docker 表面的 pull request。`main` 推送(包括合并提交)不会强制执行完整路径;当变更作用域逻辑会在推送上请求完整覆盖时,工作流会保留快速 Docker 冒烟,并把完整安装冒烟留给夜间或发布验证。较慢的 Bun 全局安装 image-provider 冒烟会由 `run_bun_global_install_smoke` 单独控制;它会在夜间计划和发布检查工作流中运行,手动 `install-smoke` 分发也可以选择加入它,但 pull request 和 `main` 推送不会运行它。QR 和安装器 Docker 测试保留各自面向安装的 Dockerfile。本地 `test:docker:all` 会预先构建一个共享的实时测试镜像,将 OpenClaw 打包一次为 npm tarball,并构建两个共享的 `scripts/e2e/Dockerfile` 镜像:一个用于安装器/更新/插件依赖通道的裸 Node/Git 运行器,以及一个功能镜像,该镜像会把同一个 tarball 安装到 `/app` 以供普通功能通道使用。Docker 通道定义位于 `scripts/lib/docker-e2e-scenarios.mjs`,规划器逻辑位于 `scripts/lib/docker-e2e-plan.mjs`,运行器只执行所选计划。调度器会使用 `OPENCLAW_DOCKER_E2E_BARE_IMAGE` 和 `OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE` 为每个通道选择镜像,然后以 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行通道;使用 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整默认主池槽位数 10,使用 `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM` 调整提供商敏感尾池槽位数 10。重型通道上限默认是 `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=10` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`,这样 npm install 和多服务通道不会让 Docker 超额承载,而较轻的通道仍会填满可用槽位。单个比有效上限更重的通道仍然可以从空池启动,然后会独占运行,直到它释放容量。默认情况下,通道启动会错开 2 秒,以避免本地 Docker daemon 出现 create 风暴;可用 `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=0` 或其他毫秒值覆盖。本地聚合会预检 Docker,移除陈旧的 OpenClaw E2E 容器,输出活动通道 Status,持久化通道耗时以用于最长优先排序,并支持 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 进行调度器检查。默认情况下,它会在第一次失败后停止调度新的池化通道,并且每个通道都有一个 120 分钟的回退超时,可用 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖;选定的实时/尾部通道使用更严格的逐通道上限。`OPENCLAW_DOCKER_ALL_LANES=` 会运行精确的调度器通道,包括仅发布通道(如 `install-e2e`)以及拆分的内置更新通道(如 `bundled-channel-update-acpx`),同时跳过清理冒烟,以便智能体能够复现某个失败通道。可复用的实时/E2E 工作流会询问 `scripts/test-docker-all.mjs --plan-json` 需要哪些包、镜像类型、实时镜像、通道和凭证覆盖,然后 `scripts/docker-e2e.mjs` 会把该计划转换成 GitHub 输出和摘要。它会通过 `scripts/package-openclaw-for-docker.mjs` 打包 OpenClaw,下载当前运行的包产物,或从 `package_artifact_run_id` 下载包产物;验证 tarball 清单;当计划需要已安装包的通道时,通过 Blacksmith 的 Docker 层缓存构建并推送带包摘要标签的 bare/functional GHCR Docker E2E 镜像;并复用提供的 `docker_e2e_bare_image`/`docker_e2e_functional_image` 输入或现有的包摘要镜像,而不是重新构建。Docker 镜像拉取会以有界的每次尝试 180 秒超时重试,因此卡住的 registry/cache 流会快速重试,而不是消耗 CI 关键路径的大部分时间。`Package Acceptance` 工作流是高级包门禁:它会从 npm、受信任的 `package_ref`、HTTPS tarball 加 SHA-256,或先前的工作流产物解析候选包,然后把那个单一的 `package-under-test` 产物传给可复用的 Docker E2E 工作流。它会让 `workflow_ref` 与 `package_ref` 保持分离,以便当前验收逻辑可以验证较旧的受信任提交,而无需检出旧工作流代码。发布检查会针对目标 ref 运行自定义 Package Acceptance 增量:内置渠道兼容性、离线插件 fixture,以及针对已解析 tarball 的 Telegram 包 QA。发布路径 Docker 套件会运行更小的分块作业,并使用 `OPENCLAW_SKIP_DOCKER_BUILD=1`,这样每个分块只拉取它需要的镜像类型,并通过同一个加权调度器执行多个通道(`OPENCLAW_DOCKER_ALL_PROFILE=release-path`、`OPENCLAW_DOCKER_ALL_CHUNK=core|package-update-openai|package-update-anthropic|package-update-core|plugins-runtime-plugins|plugins-runtime-services|plugins-runtime-install-a|plugins-runtime-install-b|plugins-runtime-install-c|plugins-runtime-install-d|plugins-runtime-install-e|plugins-runtime-install-f|plugins-runtime-install-g|plugins-runtime-install-h|bundled-channels`)。当完整发布路径覆盖请求 OpenWebUI 时,它会被并入 `plugins-runtime-services`,并且仅为只分发 OpenWebUI 的情况保留独立的 `openwebui` 分块。旧版聚合分块名称 `package-update`、`plugins-runtime-core`、`plugins-runtime` 和 `plugins-integrations` 仍可用于手动重跑,但发布工作流使用拆分分块,因此安装器 E2E 和内置插件安装/卸载 sweep 不会主导关键路径。`install-e2e` 通道别名仍然是两个提供商安装器通道的聚合手动重跑别名。`bundled-channels` 分块运行拆分的 `bundled-channel-*` 和 `bundled-channel-update-*` 通道,而不是串行的一体式 `bundled-channel-deps` 通道。每个分块都会上传 `.artifacts/docker-tests/`,其中包含通道日志、耗时、`summary.json`、`failures.json`、阶段耗时、调度器计划 JSON、慢通道表格以及逐通道重跑命令。工作流 `docker_lanes` 输入会针对准备好的镜像运行选定通道,而不是运行分块作业,这会把失败通道调试限定在一个目标 Docker 作业内,并为该运行准备、下载或复用包产物;如果选定通道是实时 Docker 通道,目标作业会为该重跑在本地构建实时测试镜像。生成的逐通道 GitHub 重跑命令会在这些值存在时包含 `package_artifact_run_id`、`package_artifact_name` 和准备好的镜像输入,因此失败通道可以复用失败运行中的精确包和镜像。使用 `pnpm test:docker:rerun ` 可从 GitHub 运行下载 Docker 产物并打印组合/逐通道目标重跑命令;使用 `pnpm test:docker:timings ` 可查看慢通道和阶段关键路径摘要。计划的实时/E2E 工作流每天运行完整发布路径 Docker 套件。内置更新矩阵会按更新目标拆分,因此重复的 npm update 和 Doctor 修复流程可以与其他内置检查分片运行。 +范围逻辑位于 `scripts/ci-changed-scope.mjs`,并由 `src/scripts/ci-changed-scope.test.ts` 中的单元测试覆盖。 +手动触发会跳过变更范围检测,并让预检清单 +表现得像每个作用域区域都已变更一样。 +CI 工作流编辑会验证 Node CI 图和工作流 lint,但不会仅凭自身强制运行 Windows、Android 或 macOS 原生构建;这些平台通道仍然仅限于平台源代码变更。 +仅 CI 路由编辑、选定的低成本核心测试 fixture 编辑,以及范围较窄的插件契约辅助工具/测试路由编辑,会使用一条快速的仅 Node 清单路径:预检、安全检查,以及单个 `checks-fast-core` 任务。当变更文件仅限于快速任务直接覆盖的路由或辅助工具表面时,这条路径会避开构建产物、Node 22 兼容性、渠道契约、完整核心分片、内置插件分片,以及额外的守护矩阵。 +Windows Node 检查仅限于 Windows 特定的进程/路径包装器、npm/pnpm/UI runner 辅助工具、包管理器配置,以及执行该通道的 CI 工作流表面;无关的源代码、插件、安装 smoke 和仅测试变更会留在 Linux Node 通道上,因此不会为普通测试分片已覆盖的内容占用 16-vCPU Windows worker。 +单独的 `install-smoke` 工作流通过自己的 `preflight` job 复用同一个范围脚本。它将 smoke 覆盖拆分为 `run_fast_install_smoke` 和 `run_full_install_smoke`。Pull request 会针对 Docker/包表面、内置插件包/manifest 变更,以及 Docker smoke job 覆盖的核心插件/渠道/Gateway 网关/插件 SDK 表面运行快速路径。仅源代码的内置插件变更、仅测试编辑和仅文档编辑不会占用 Docker worker。快速路径会构建一次根 Dockerfile 镜像,检查 CLI,运行 agents delete shared-workspace CLI smoke,运行容器 gateway-network e2e,验证一个内置插件构建参数,并在 240 秒聚合命令超时内运行有界的内置插件 Docker profile,同时每个场景的 Docker run 都单独设置上限。完整路径会把 QR 包安装和 installer Docker/update 覆盖保留给夜间定时运行、手动触发、workflow-call 发布检查,以及确实触碰 installer/package/Docker 表面的 pull request。`main` 推送(包括合并提交)不会强制运行完整路径;当变更范围逻辑会在一次推送上请求完整覆盖时,工作流会保留快速 Docker smoke,并把完整安装 smoke 留给夜间或发布验证。较慢的 Bun 全局安装 image-provider smoke 由 `run_bun_global_install_smoke` 单独控制;它会在夜间计划任务和发布检查工作流中运行,手动 `install-smoke` 触发可以选择加入它,但 pull request 和 `main` 推送不会运行它。QR 和 installer Docker 测试保留各自面向安装的 Dockerfile。本地 `test:docker:all` 会预构建一个共享 live-test 镜像,将 OpenClaw 作为 npm tarball 打包一次,并构建两个共享 `scripts/e2e/Dockerfile` 镜像:一个用于 installer/update/plugin-dependency 通道的裸 Node/Git runner,以及一个将同一个 tarball 安装到 `/app` 中、用于普通功能通道的功能镜像。Docker 通道定义位于 `scripts/lib/docker-e2e-scenarios.mjs`,规划器逻辑位于 `scripts/lib/docker-e2e-plan.mjs`,runner 只执行选中的计划。调度器用 `OPENCLAW_DOCKER_E2E_BARE_IMAGE` 和 `OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE` 为每个通道选择镜像,然后用 `OPENCLAW_SKIP_DOCKER_BUILD=1` 运行通道;用 `OPENCLAW_DOCKER_ALL_PARALLELISM` 调整默认值为 10 的主池 slot 数量,用 `OPENCLAW_DOCKER_ALL_TAIL_PARALLELISM` 调整默认值为 10 的提供商敏感 tail 池 slot 数量。重型通道上限默认是 `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`、`OPENCLAW_DOCKER_ALL_NPM_LIMIT=10` 和 `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`,这样 npm install 和多服务通道不会过度占用 Docker,而较轻的通道仍能填满可用 slot。单个比有效上限更重的通道仍可以从空池启动,然后独占运行直到释放容量。通道启动默认错开 2 秒,以避免本地 Docker daemon 出现创建风暴;可用 `OPENCLAW_DOCKER_ALL_START_STAGGER_MS=0` 或其他毫秒值覆盖。本地聚合会预检 Docker,移除陈旧的 OpenClaw E2E 容器,输出活动通道 Status,持久化通道耗时以便按最长优先排序,并支持 `OPENCLAW_DOCKER_ALL_DRY_RUN=1` 进行调度器检查。默认情况下,它会在第一次失败后停止调度新的池化通道,并且每个通道都有 120 分钟 fallback 超时,可用 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS` 覆盖;选定的 live/tail 通道使用更严格的每通道上限。`OPENCLAW_DOCKER_ALL_LANES=` 会运行精确的调度器通道,包括仅发布通道(如 `install-e2e`)和拆分的内置更新通道(如 `bundled-channel-update-acpx`),同时跳过 cleanup smoke,以便智能体能够复现一个失败通道。可复用 live/E2E 工作流会询问 `scripts/test-docker-all.mjs --plan-json` 需要哪些包、镜像类型、live 镜像、通道和凭证覆盖,然后 `scripts/docker-e2e.mjs` 将该计划转换为 GitHub 输出和摘要。它会通过 `scripts/package-openclaw-for-docker.mjs` 打包 OpenClaw,下载当前运行的包产物,或从 `package_artifact_run_id` 下载包产物;验证 tarball 清单;当计划需要已安装包的通道时,通过 Blacksmith 的 Docker layer cache 构建并推送带包 digest 标签的裸/功能 GHCR Docker E2E 镜像;并复用提供的 `docker_e2e_bare_image`/`docker_e2e_functional_image` 输入或现有包 digest 镜像,而不是重新构建。Docker 镜像拉取会用每次尝试 180 秒的有界超时重试,这样卡住的 registry/cache 流会快速重试,而不是消耗大部分 CI 关键路径。`Package Acceptance` 工作流是高层包 gate:它会从 npm、受信任的 `package_ref`、HTTPS tarball 加 SHA-256,或先前的工作流产物中解析候选包,然后把单个 `package-under-test` 产物传入可复用 Docker E2E 工作流。它将 `workflow_ref` 与 `package_ref` 分开,因此当前验收逻辑可以验证较旧的受信任提交,而不必检出旧工作流代码。发布检查会针对目标 ref 运行自定义 Package Acceptance 增量:内置渠道兼容性、离线插件 fixture,以及针对解析出的 tarball 的 Telegram 包 QA。发布路径 Docker 套件会运行较小的分块 job,并设置 `OPENCLAW_SKIP_DOCKER_BUILD=1`,因此每个分块只拉取所需的镜像类型,并通过同一个加权调度器执行多个通道(`OPENCLAW_DOCKER_ALL_PROFILE=release-path`,`OPENCLAW_DOCKER_ALL_CHUNK=core|package-update-openai|package-update-anthropic|package-update-core|plugins-runtime-plugins|plugins-runtime-services|plugins-runtime-install-a|plugins-runtime-install-b|plugins-runtime-install-c|plugins-runtime-install-d|plugins-runtime-install-e|plugins-runtime-install-f|plugins-runtime-install-g|plugins-runtime-install-h|bundled-channels`)。当完整 release-path 覆盖请求 OpenWebUI 时,它会合并到 `plugins-runtime-services` 中;只有 OpenWebUI-only 触发时才保留独立的 `openwebui` 分块。旧版聚合分块名称 `package-update`、`plugins-runtime-core`、`plugins-runtime` 和 `plugins-integrations` 仍可用于手动重跑,但发布工作流使用拆分分块,因此 installer E2E 和内置插件 install/uninstall 扫描不会主导关键路径。`install-e2e` 通道别名仍然是两个提供商 installer 通道的聚合手动重跑别名。`bundled-channels` 分块会运行拆分的 `bundled-channel-*` 和 `bundled-channel-update-*` 通道,而不是串行的一体化 `bundled-channel-deps` 通道。每个分块都会上传 `.artifacts/docker-tests/`,其中包含通道日志、耗时、`summary.json`、`failures.json`、阶段耗时、调度器计划 JSON、慢通道表格,以及每通道重跑命令。工作流 `docker_lanes` 输入会针对准备好的镜像运行选定通道,而不是运行分块 job,这会把失败通道调试限制在一个目标 Docker job 中,并为该运行准备、下载或复用包产物;如果选定通道是 live Docker 通道,目标 job 会为该重跑在本地构建 live-test 镜像。生成的每通道 GitHub 重跑命令在存在这些值时会包含 `package_artifact_run_id`、`package_artifact_name` 和准备好的镜像输入,因此失败通道可以复用失败运行中的精确包和镜像。使用 `pnpm test:docker:rerun ` 从 GitHub 运行下载 Docker 产物并打印组合/每通道目标重跑命令;使用 `pnpm test:docker:timings ` 查看慢通道和阶段关键路径摘要。定时 live/E2E 工作流每天运行完整 release-path Docker 套件。内置更新矩阵按更新目标拆分,因此重复的 npm update 和 Doctor repair pass 可以与其他内置检查一起分片。 -当前发布 Docker 分块是 `core`、`package-update-openai`、`package-update-anthropic`、`package-update-core`、`plugins-runtime-plugins`、`plugins-runtime-services`、`plugins-runtime-install-a`、`plugins-runtime-install-b`、`plugins-runtime-install-c`、`plugins-runtime-install-d`、`plugins-runtime-install-e`、`plugins-runtime-install-f`、`plugins-runtime-install-g`、`plugins-runtime-install-h`、`bundled-channels-core`、`bundled-channels-update-a`、`bundled-channels-update-discord`、`bundled-channels-update-b` 和 `bundled-channels-contracts`。聚合的 `bundled-channels` 分块仍可用于手动一次性重跑,`plugins-runtime-core`、`plugins-runtime` 和 `plugins-integrations` 仍保留为聚合的插件/运行时别名,但发布工作流会使用拆分分块,因此渠道冒烟、更新目标、插件运行时检查,以及内置插件安装/卸载 sweep 可以并行运行。目标 `docker_lanes` 分发也会在一次共享包/镜像准备步骤之后,把多个选定通道拆分为并行作业,并且内置渠道更新通道会对临时性 npm 网络故障重试一次。 +当前发布 Docker 分块是 `core`、`package-update-openai`、`package-update-anthropic`、`package-update-core`、`plugins-runtime-plugins`、`plugins-runtime-services`、`plugins-runtime-install-a`、`plugins-runtime-install-b`、`plugins-runtime-install-c`、`plugins-runtime-install-d`、`plugins-runtime-install-e`、`plugins-runtime-install-f`、`plugins-runtime-install-g`、`plugins-runtime-install-h`、`bundled-channels-core`、`bundled-channels-update-a`、`bundled-channels-update-discord`、`bundled-channels-update-b` 和 `bundled-channels-contracts`。聚合 `bundled-channels` 分块仍可用于手动一次性重跑,`plugins-runtime-core`、`plugins-runtime` 和 `plugins-integrations` 仍然是聚合插件/运行时别名,但发布工作流使用拆分分块,因此渠道 smoke、更新目标、插件运行时检查,以及内置插件 install/uninstall 扫描可以并行运行。目标 `docker_lanes` 触发也会在一个共享包/镜像准备步骤之后,将多个选定通道拆分为并行 job,并且内置渠道更新通道会对瞬时 npm 网络失败重试一次。 -本地变更通道逻辑位于 `scripts/changed-lanes.mjs`,并由 `scripts/check-changed.mjs` 执行。该本地检查门禁在架构边界方面比宽泛的 CI 平台作用域更严格:核心生产变更会运行核心生产和核心测试类型检查,以及核心 lint/防护;仅核心测试变更只运行核心测试类型检查和核心 lint;扩展生产变更会运行扩展生产和扩展测试类型检查,以及扩展 lint;仅扩展测试变更会运行扩展测试类型检查和扩展 lint。公共插件 SDK 或插件契约变更会扩展到扩展类型检查,因为扩展依赖这些核心契约,但 Vitest 扩展 sweep 是显式测试工作。仅发布元数据的版本 bump 会运行目标版本/配置/根依赖检查。未知的根目录/配置变更会故障安全地落到所有检查通道。 -本地变更测试路由位于 `scripts/test-projects.test-support.mjs`,并且有意比 `check:changed` 更低成本:直接测试编辑会运行自身,源码编辑优先使用显式映射,然后是同级测试和导入图依赖项。共享 group-room 投递配置是显式映射之一:对群组可见回复配置、源码回复投递模式或 message-tool 系统提示词的变更,会路由到核心回复测试以及 Discord 和 Slack 投递回归,因此共享默认值变更会在第一次 PR 推送前失败。仅当变更涉及 harness 范围足够广、以至于低成本映射集合不是可信代理时,才使用 `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`。 +本地变更通道逻辑位于 `scripts/changed-lanes.mjs`,并由 `scripts/check-changed.mjs` 执行。该本地检查 gate 在架构边界方面比宽泛的 CI 平台范围更严格:核心生产变更会运行核心生产和核心测试类型检查以及核心 lint/guard,仅核心测试变更只运行核心测试类型检查以及核心 lint,插件生产变更会运行插件生产和插件测试类型检查以及插件 lint,仅插件测试变更会运行插件测试类型检查以及插件 lint。公共插件 SDK 或插件契约变更会扩展到插件类型检查,因为插件依赖这些核心契约,但 Vitest 插件扫描是显式测试工作。仅发布元数据的版本升级会运行目标版本/配置/根依赖检查。未知根目录/配置变更会安全失败到所有检查通道。 +本地变更测试路由位于 `scripts/test-projects.test-support.mjs`, +并且有意比 `check:changed` 更低成本:直接测试编辑会运行自身, +源代码编辑优先使用显式映射,然后是 sibling 测试和 import-graph +依赖项。共享 group-room 传递配置是显式映射之一: +对 group visible-reply 配置、源回复传递模式,或 +message-tool 系统 prompt 的变更,会路由到核心回复测试以及 Discord 和 +Slack 传递回归,因此共享默认值变更会在第一次 PR +推送之前失败。只有当变更覆盖整个 harness,导致低成本映射集合不再是可信代理时, +才使用 `OPENCLAW_TEST_CHANGED_BROAD=1 pnpm test:changed`。 -对于 Testbox 验证,请从 repo 根目录运行,并且在做广泛证明时优先使用新预热的 box。在把慢速门禁花在一个被复用、已过期,或刚报告了异常大同步量的 box 上之前,先在该 box 内运行 `pnpm testbox:sanity`。当 `pnpm-lock.yaml` 等必需的根文件消失,或 `git status --short` 显示至少 200 个已跟踪删除时,完整性检查会快速失败。这通常意味着远程同步状态不是 PR 的可信副本。请停止该 box 并预热一个新的,而不是调试产品测试失败。对于有意进行大量删除的 PR,请为该完整性检查运行设置 `OPENCLAW_TESTBOX_ALLOW_MASS_DELETIONS=1`。`pnpm -testbox:run` 还会终止本地 Blacksmith CLI 调用,如果该调用在同步阶段停留超过五分钟且没有同步后的输出。设置 `OPENCLAW_TESTBOX_SYNC_TIMEOUT_MS=0` 可禁用该保护,或为异常大的本地 diff 使用更大的毫秒值。 +对于 Testbox 验证,请从仓库根目录运行,并且在做广泛证明时优先使用一个新的已预热 box。对于复用过、已过期,或刚刚报告了异常大同步量的 box,在把缓慢门禁耗在它上面之前,先在该 box 内运行 `pnpm testbox:sanity`。当 `pnpm-lock.yaml` 等必需根文件消失,或 `git status --short` 显示至少 200 个已跟踪删除时,完整性检查会快速失败。这通常意味着远端同步状态不是 PR 的可信副本。停止该 box,并预热一个新的,而不是调试产品测试失败。对于有意的大规模删除 PR,请为那次完整性检查设置 `OPENCLAW_TESTBOX_ALLOW_MASS_DELETIONS=1`。`pnpm +testbox:run` 还会终止在同步阶段停留超过五分钟且没有同步后输出的本地 Blacksmith CLI 调用。设置 `OPENCLAW_TESTBOX_SYNC_TIMEOUT_MS=0` 可禁用该保护,或者为异常大的本地差异使用更大的毫秒值。 -手动 CI 调度会运行 `checks-node-compat-node22` 作为广泛兼容性覆盖。Android 对独立手动 CI 通过 `include_android=true` 选择启用,并且始终在 `Full Release Validation` 中启用。`Plugin Prerelease` 是成本更高的产品/包覆盖,因此它是由 `Full Release Validation` 或明确操作员单独调度的工作流。普通拉取请求、`main` 推送和独立手动 CI 调度会保持该套件关闭。 +手动 CI 调度会运行 `checks-node-compat-node22` 作为广泛兼容性覆盖。Android 在独立手动 CI 中通过 `include_android=true` 选择启用,并且在 `Full Release Validation` 中始终启用。`Plugin Prerelease` 是成本更高的产品/包覆盖,因此它是一个单独的工作流,由 `Full Release Validation` 调度或由明确的操作员调度。普通 pull request、`main` 推送和独立手动 CI 调度会保持该套件关闭。 -最慢的 Node 测试族已被拆分或均衡,使每个作业保持较小且不会过度预留 runner:渠道契约作为三个加权分片运行,小型核心单元 lane 会配对,自动回复作为四个均衡 worker 运行,并将回复子树拆分为 agent-runner、dispatch 和 commands/state-routing 分片,而 agentic Gateway 网关/插件配置会分布到现有的仅源码 agentic Node 作业中,而不是等待构建产物。广泛的浏览器、QA、媒体和杂项插件测试使用它们专用的 Vitest 配置,而不是共享插件兜底项。`Plugin Prerelease` 会在八个插件 worker 间均衡内置插件测试;这些插件分片作业一次最多运行两个插件配置组,每组使用一个 Vitest worker 和更大的 Node 堆,以便导入量大的插件批次不会创建额外 CI 作业。广泛 agents lane 使用共享 Vitest 文件并行调度器,因为它主要受导入/调度影响,而不是由单个慢速测试文件主导。`runtime-config` 与 infra core-runtime 分片一起运行,以避免共享 runtime 分片承担尾部耗时。包含模式分片使用 CI 分片名称记录计时条目,因此 `.artifacts/vitest-shard-timings.json` 可以区分整个配置和经过过滤的分片。`check-additional` 将包边界编译/canary 工作放在一起,并将运行时拓扑架构与 Gateway 网关 watch 覆盖分开;边界保护分片会在一个作业内并发运行其小型独立保护。Gateway 网关 watch、渠道测试和核心支持边界分片会在 `build-artifacts` 中于 `dist/` 和 `dist-runtime/` 已构建后并发运行,保留它们旧的检查名称作为轻量验证作业,同时避免两个额外 Blacksmith worker 和第二个产物消费队列。 -Android CI 会同时运行 `testPlayDebugUnitTest` 和 `testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的源码集或 manifest;它的单元测试 lane 仍会使用 SMS/call-log BuildConfig 标志编译该 flavor,同时避免在每次 Android 相关推送时执行重复的 debug APK 打包作业。 -当较新的推送落到同一 PR 或 `main` ref 上时,GitHub 可能会将被取代的作业标记为 `cancelled`。除非同一 ref 的最新运行也失败,否则将其视为 CI 噪声。聚合分片检查使用 `!cancelled() && always()`,因此它们仍会报告正常的分片失败,但不会在整个工作流已被取代后继续排队。 -自动 CI 并发键带有版本号(`CI-v7-*`),因此 GitHub 端旧队列组中的僵尸项无法无限期阻塞较新的 main 运行。手动全套件运行使用 `CI-manual-v1-*`,并且不会取消正在进行的运行。 +最慢的 Node 测试族会被拆分或均衡,使每个 job 保持较小且不过度预留 runner:渠道契约以三个加权分片运行,小型核心单元 lane 会配对运行,auto-reply 以四个均衡 worker 运行,并将 reply 子树拆分为 agent-runner、dispatch 和 commands/state-routing 分片,agentic gateway/plugin 配置则分布在现有的仅源码 agentic Node job 中,而不是等待构建产物。广泛的浏览器、QA、媒体和杂项插件测试使用它们专用的 Vitest 配置,而不是共享的插件兜底项。`Plugin Prerelease` 会在八个扩展 worker 之间均衡内置插件测试;这些扩展分片 job 每次最多运行两个插件配置组,每组使用一个 Vitest worker 和更大的 Node heap,这样导入密集型插件批次不会创建额外 CI job。广泛 agents lane 使用共享的 Vitest 文件并行调度器,因为它主要受导入/调度影响,而不是由单个慢测试文件主导。`runtime-config` 与 infra core-runtime 分片一起运行,避免共享 runtime 分片承担尾部耗时。包含模式分片使用 CI 分片名称记录计时条目,因此 `.artifacts/vitest-shard-timings.json` 可以区分整个配置和经过过滤的分片。`check-additional` 将包边界编译/canary 工作放在一起,并将运行时拓扑架构与 gateway watch 覆盖分离;boundary guard 分片会在一个 job 内并发运行其小型独立 guard。Gateway watch、渠道测试和核心支持边界分片会在 `dist/` 和 `dist-runtime/` 已经构建完成后,在 `build-artifacts` 内并发运行,保留它们原有的检查名称作为轻量级验证 job,同时避免两个额外的 Blacksmith worker 和第二个产物消费者队列。 +Android CI 会同时运行 `testPlayDebugUnitTest` 和 `testThirdPartyDebugUnitTest`,然后构建 Play debug APK。第三方 flavor 没有单独的 source set 或 manifest;它的单元测试 lane 仍会使用 SMS/call-log BuildConfig 标志编译该 flavor,同时避免在每次 Android 相关推送中重复打包 debug APK job。 +当同一 PR 或 `main` ref 上有更新推送落地时,GitHub 可能会将被取代的 job 标记为 `cancelled`。除非同一 ref 的最新 run 也失败,否则将其视为 CI 噪声。聚合分片检查使用 `!cancelled() && always()`,因此它们仍会报告正常分片失败,但不会在整个工作流已经被取代后继续排队。 +自动 CI 并发键已版本化(`CI-v7-*`),因此 GitHub 侧旧队列组中的僵尸项不能无限期阻塞较新的 main run。手动完整套件 run 使用 `CI-manual-v1-*`,并且不会取消正在进行的 run。 ## Runners -| Runner | 作业 | +| Runner | Jobs | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ubuntu-24.04` | `preflight`,快速安全作业和聚合(`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速协议/契约/内置检查、分片渠道契约检查、除 lint 外的 `check` 分片、`check-additional` 分片和聚合、Node 测试聚合验证器、文档检查、Python Skills、workflow-sanity、labeler、auto-response;install-smoke preflight 也使用 GitHub 托管的 Ubuntu,以便 Blacksmith 矩阵可以更早排队 | -| `blacksmith-4vcpu-ubuntu-2404` | `CodeQL Critical Quality`、较低权重的插件分片、`checks-fast-core`、`checks-node-compat-node22`、`check-prod-types` 和 `check-test-types` | +| `ubuntu-24.04` | `preflight`、快速安全 job 和聚合项(`security-scm-fast`、`security-dependency-audit`、`security-fast`)、快速协议/契约/内置检查、分片渠道契约检查、除 lint 以外的 `check` 分片、`check-additional` 分片和聚合项、Node 测试聚合验证器、文档检查、Python Skills、workflow-sanity、labeler、auto-response;install-smoke preflight 也使用 GitHub 托管的 Ubuntu,以便 Blacksmith 矩阵可以更早排队 | +| `blacksmith-4vcpu-ubuntu-2404` | `CodeQL Critical Quality`、较低权重的扩展分片、`checks-fast-core`、`checks-node-compat-node22`、`check-prod-types` 和 `check-test-types` | | `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-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` |