mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-01 03:41:44 +00:00
Docs: add zh-CN translations
This commit is contained in:
424
docs/zh-CN/refactor/clawnet.md
Normal file
424
docs/zh-CN/refactor/clawnet.md
Normal file
@@ -0,0 +1,424 @@
|
||||
---
|
||||
read_when:
|
||||
- 规划节点 + 操作者客户端的统一网络协议
|
||||
- 重新设计跨设备的审批、配对、TLS 和在线状态
|
||||
summary: Clawnet 重构:统一网络协议、角色、认证、审批、身份
|
||||
title: Clawnet 重构
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:37:22Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 719b219c3b326479658fe6101c80d5273fc56eb3baf50be8535e0d1d2bb7987f
|
||||
source_path: refactor/clawnet.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Clawnet 重构(协议 + 认证统一)
|
||||
|
||||
## 你好
|
||||
|
||||
你好 Peter — 方向很棒;这将带来更简洁的用户体验和更强的安全性。
|
||||
|
||||
## 目的
|
||||
|
||||
一份严谨的统一文档,涵盖:
|
||||
|
||||
- 现状:协议、流程、信任边界。
|
||||
- 痛点:审批、多跳路由、UI 重复。
|
||||
- 新方案:单一协议、作用域角色、统一认证/配对、TLS 固定。
|
||||
- 身份模型:稳定 ID + 可爱别名。
|
||||
- 迁移计划、风险、待解决问题。
|
||||
|
||||
## 目标(来自讨论)
|
||||
|
||||
- 所有客户端(mac 应用、CLI、iOS、Android、无头节点)使用同一协议。
|
||||
- 每个网络参与者均经过认证和配对。
|
||||
- 角色清晰:节点 vs 操作者。
|
||||
- 集中审批,路由到用户所在位置。
|
||||
- 所有远程流量使用 TLS 加密 + 可选固定。
|
||||
- 最小化代码重复。
|
||||
- 单台机器只显示一次(不出现 UI/节点重复条目)。
|
||||
|
||||
## 非目标(明确声明)
|
||||
|
||||
- 移除能力隔离(仍需最小权限)。
|
||||
- 在无作用域检查的情况下暴露完整 Gateway 控制平面。
|
||||
- 让认证依赖人类标签(别名仍为非安全性要素)。
|
||||
|
||||
---
|
||||
|
||||
# 现状(当前状态)
|
||||
|
||||
## 两套协议
|
||||
|
||||
### 1) Gateway WebSocket(控制平面)
|
||||
|
||||
- 完整 API 接口:配置、渠道、模型、会话、智能体运行、日志、节点等。
|
||||
- 默认绑定:回环地址。远程访问通过 SSH/Tailscale。
|
||||
- 认证:通过 `connect` 使用令牌/密码。
|
||||
- 无 TLS 固定(依赖回环/隧道)。
|
||||
- 代码:
|
||||
- `src/gateway/server/ws-connection/message-handler.ts`
|
||||
- `src/gateway/client.ts`
|
||||
- `docs/gateway/protocol.md`
|
||||
|
||||
### 2) Bridge(节点传输)
|
||||
|
||||
- 窄白名单接口,节点身份 + 配对。
|
||||
- 基于 TCP 的 JSONL;可选 TLS + 证书指纹固定。
|
||||
- TLS 在发现 TXT 记录中广播指纹。
|
||||
- 代码:
|
||||
- `src/infra/bridge/server/connection.ts`
|
||||
- `src/gateway/server-bridge.ts`
|
||||
- `src/node-host/bridge-client.ts`
|
||||
- `docs/gateway/bridge-protocol.md`
|
||||
|
||||
## 当前控制平面客户端
|
||||
|
||||
- CLI → 通过 `callGateway` 连接 Gateway WS(`src/gateway/call.ts`)。
|
||||
- macOS 应用 UI → Gateway WS(`GatewayConnection`)。
|
||||
- Web 控制 UI → Gateway WS。
|
||||
- ACP → Gateway WS。
|
||||
- 浏览器控制使用独立的 HTTP 控制服务器。
|
||||
|
||||
## 当前节点
|
||||
|
||||
- macOS 应用在节点模式下连接 Gateway bridge(`MacNodeBridgeSession`)。
|
||||
- iOS/Android 应用连接 Gateway bridge。
|
||||
- 配对 + 每节点令牌存储在 Gateway。
|
||||
|
||||
## 当前审批流程(执行)
|
||||
|
||||
- 智能体通过 Gateway 使用 `system.run`。
|
||||
- Gateway 通过 bridge 调用节点。
|
||||
- 节点运行时决定审批。
|
||||
- UI 提示在 mac 应用上显示(当节点 == mac 应用时)。
|
||||
- 节点向 Gateway 返回 `invoke-res`。
|
||||
- 多跳,UI 绑定在节点主机上。
|
||||
|
||||
## 当前在线状态 + 身份
|
||||
|
||||
- Gateway 在线状态条目来自 WS 客户端。
|
||||
- 节点在线状态条目来自 bridge。
|
||||
- mac 应用可能为同一台机器显示两个条目(UI + 节点)。
|
||||
- 节点身份存储在配对存储中;UI 身份独立存储。
|
||||
|
||||
---
|
||||
|
||||
# 问题 / 痛点
|
||||
|
||||
- 需要维护两套协议栈(WS + Bridge)。
|
||||
- 远程节点的审批:提示出现在节点主机上,而非用户所在位置。
|
||||
- TLS 固定仅存在于 bridge;WS 依赖 SSH/Tailscale。
|
||||
- 身份重复:同一台机器显示为多个实例。
|
||||
- 角色模糊:UI + 节点 + CLI 的能力未清晰分离。
|
||||
|
||||
---
|
||||
|
||||
# 新方案(Clawnet)
|
||||
|
||||
## 单一协议,两种角色
|
||||
|
||||
带角色 + 作用域的单一 WS 协议。
|
||||
|
||||
- **角色:node**(能力宿主)
|
||||
- **角色:operator**(控制平面)
|
||||
- 操作者可选**作用域**:
|
||||
- `operator.read`(状态 + 查看)
|
||||
- `operator.write`(智能体运行、发送)
|
||||
- `operator.admin`(配置、渠道、模型)
|
||||
|
||||
### 角色行为
|
||||
|
||||
**节点**
|
||||
|
||||
- 可注册能力(`caps`、`commands`、权限)。
|
||||
- 可接收 `invoke` 命令(`system.run`、`camera.*`、`canvas.*`、`screen.record` 等)。
|
||||
- 可发送事件:`voice.transcript`、`agent.request`、`chat.subscribe`。
|
||||
- 不能调用配置/模型/渠道/会话/智能体控制平面 API。
|
||||
|
||||
**操作者**
|
||||
|
||||
- 完整控制平面 API,受作用域限制。
|
||||
- 接收所有审批请求。
|
||||
- 不直接执行 OS 操作;路由到节点执行。
|
||||
|
||||
### 关键规则
|
||||
|
||||
角色按连接划分,而非按设备划分。一个设备可以分别打开两种角色。
|
||||
|
||||
---
|
||||
|
||||
# 统一认证 + 配对
|
||||
|
||||
## 客户端身份
|
||||
|
||||
每个客户端提供:
|
||||
|
||||
- `deviceId`(稳定,从设备密钥派生)。
|
||||
- `displayName`(人类可读名称)。
|
||||
- `role` + `scope` + `caps` + `commands`。
|
||||
|
||||
## 配对流程(统一)
|
||||
|
||||
- 客户端未认证连接。
|
||||
- Gateway 为该 `deviceId` 创建**配对请求**。
|
||||
- 操作者收到提示;批准/拒绝。
|
||||
- Gateway 签发绑定以下信息的凭据:
|
||||
- 设备公钥
|
||||
- 角色
|
||||
- 作用域
|
||||
- 能力/命令
|
||||
- 客户端持久化令牌,重新认证连接。
|
||||
|
||||
## 设备绑定认证(防止持有者令牌重放)
|
||||
|
||||
推荐方案:设备密钥对。
|
||||
|
||||
- 设备一次性生成密钥对。
|
||||
- `deviceId = fingerprint(publicKey)`。
|
||||
- Gateway 发送 nonce;设备签名;Gateway 验证。
|
||||
- 令牌签发给公钥(持有证明),而非字符串。
|
||||
|
||||
替代方案:
|
||||
|
||||
- mTLS(客户端证书):最强,运维复杂度更高。
|
||||
- 短期持有者令牌仅作为临时过渡(尽早轮换 + 撤销)。
|
||||
|
||||
## 静默审批(SSH 启发式)
|
||||
|
||||
需精确定义以避免薄弱环节。推荐以下方案之一:
|
||||
|
||||
- **仅限本地**:客户端通过回环/Unix socket 连接时自动配对。
|
||||
- **SSH 验证**:Gateway 签发 nonce;客户端通过获取 nonce 证明 SSH 访问。
|
||||
- **物理存在窗口**:在 Gateway 主机 UI 上进行本地审批后,在短时间窗口内(如 10 分钟)允许自动配对。
|
||||
|
||||
始终记录自动审批日志。
|
||||
|
||||
---
|
||||
|
||||
# 全面启用 TLS(开发 + 生产)
|
||||
|
||||
## 复用现有 bridge TLS
|
||||
|
||||
使用当前 TLS 运行时 + 指纹固定:
|
||||
|
||||
- `src/infra/bridge/server/tls.ts`
|
||||
- `src/node-host/bridge-client.ts` 中的指纹验证逻辑
|
||||
|
||||
## 应用到 WS
|
||||
|
||||
- WS 服务器使用相同证书/密钥 + 指纹支持 TLS。
|
||||
- WS 客户端可固定指纹(可选)。
|
||||
- 发现服务为所有端点广播 TLS + 指纹。
|
||||
- 发现服务仅作为定位提示;绝不作为信任锚点。
|
||||
|
||||
## 原因
|
||||
|
||||
- 减少对 SSH/Tailscale 的机密性依赖。
|
||||
- 使远程移动端连接默认安全。
|
||||
|
||||
---
|
||||
|
||||
# 审批重新设计(集中化)
|
||||
|
||||
## 现状
|
||||
|
||||
审批发生在节点主机上(mac 应用节点运行时)。提示出现在节点运行的位置。
|
||||
|
||||
## 新方案
|
||||
|
||||
审批由 **Gateway 托管**,UI 推送到操作者客户端。
|
||||
|
||||
### 新流程
|
||||
|
||||
1. Gateway 收到 `system.run` 意图(智能体)。
|
||||
2. Gateway 创建审批记录:`approval.requested`。
|
||||
3. 操作者 UI 显示提示。
|
||||
4. 审批决定发送到 Gateway:`approval.resolve`。
|
||||
5. 如果批准,Gateway 调用节点命令。
|
||||
6. 节点执行,返回 `invoke-res`。
|
||||
|
||||
### 审批语义(加固)
|
||||
|
||||
- 广播给所有操作者;仅活跃 UI 显示模态框(其他显示通知提示)。
|
||||
- 首个决定生效;Gateway 拒绝后续的重复决定。
|
||||
- 默认超时:N 秒后拒绝(如 60 秒),记录原因。
|
||||
- 决定需要 `operator.approvals` 作用域。
|
||||
|
||||
## 优势
|
||||
|
||||
- 提示出现在用户所在位置(mac/手机)。
|
||||
- 远程节点的审批行为一致。
|
||||
- 节点运行时保持无头;无 UI 依赖。
|
||||
|
||||
---
|
||||
|
||||
# 角色清晰示例
|
||||
|
||||
## iPhone 应用
|
||||
|
||||
- **节点角色**用于:麦克风、摄像头、语音聊天、位置、按键通话。
|
||||
- 可选 **operator.read** 用于状态和聊天查看。
|
||||
- 仅在明确启用时才有可选 **operator.write/admin**。
|
||||
|
||||
## macOS 应用
|
||||
|
||||
- 默认为操作者角色(控制 UI)。
|
||||
- 启用"Mac 节点"时为节点角色(system.run、屏幕、摄像头)。
|
||||
- 两个连接使用相同 deviceId → 合并为一个 UI 条目。
|
||||
|
||||
## CLI
|
||||
|
||||
- 始终为操作者角色。
|
||||
- 作用域由子命令决定:
|
||||
- `status`、`logs` → read
|
||||
- `agent`、`message` → write
|
||||
- `config`、`channels` → admin
|
||||
- 审批 + 配对 → `operator.approvals` / `operator.pairing`
|
||||
|
||||
---
|
||||
|
||||
# 身份 + 别名
|
||||
|
||||
## 稳定 ID
|
||||
|
||||
认证必需;永不更改。
|
||||
推荐方案:
|
||||
|
||||
- 密钥对指纹(公钥哈希)。
|
||||
|
||||
## 可爱别名(龙虾主题)
|
||||
|
||||
仅作为人类标签。
|
||||
|
||||
- 示例:`scarlet-claw`、`saltwave`、`mantis-pinch`。
|
||||
- 存储在 Gateway 注册表中,可编辑。
|
||||
- 冲突处理:`-2`、`-3`。
|
||||
|
||||
## UI 分组
|
||||
|
||||
相同 `deviceId` 跨角色 → 单个"实例"行:
|
||||
|
||||
- 标记:`operator`、`node`。
|
||||
- 显示能力 + 最后在线时间。
|
||||
|
||||
---
|
||||
|
||||
# 迁移策略
|
||||
|
||||
## 阶段 0:文档 + 对齐
|
||||
|
||||
- 发布本文档。
|
||||
- 盘点所有协议调用 + 审批流程。
|
||||
|
||||
## 阶段 1:在 WS 中添加角色/作用域
|
||||
|
||||
- 扩展 `connect` 参数,增加 `role`、`scope`、`deviceId`。
|
||||
- 为节点角色添加白名单控制。
|
||||
|
||||
## 阶段 2:Bridge 兼容
|
||||
|
||||
- 保持 bridge 运行。
|
||||
- 并行添加 WS 节点支持。
|
||||
- 通过配置开关控制功能。
|
||||
|
||||
## 阶段 3:集中审批
|
||||
|
||||
- 在 WS 中添加审批请求 + 决定事件。
|
||||
- 更新 mac 应用 UI 以显示提示和响应。
|
||||
- 节点运行时停止 UI 提示。
|
||||
|
||||
## 阶段 4:TLS 统一
|
||||
|
||||
- 使用 bridge TLS 运行时为 WS 添加 TLS 配置。
|
||||
- 为客户端添加固定。
|
||||
|
||||
## 阶段 5:弃用 bridge
|
||||
|
||||
- 将 iOS/Android/mac 节点迁移到 WS。
|
||||
- 保留 bridge 作为回退;稳定后移除。
|
||||
|
||||
## 阶段 6:设备绑定认证
|
||||
|
||||
- 所有非本地连接要求基于密钥的身份认证。
|
||||
- 添加撤销 + 轮换 UI。
|
||||
|
||||
---
|
||||
|
||||
# 安全说明
|
||||
|
||||
- 角色/白名单在 Gateway 边界强制执行。
|
||||
- 无操作者作用域的客户端无法获得"完整"API。
|
||||
- 所有连接均需配对。
|
||||
- TLS + 固定降低移动端中间人攻击风险。
|
||||
- SSH 静默审批是便利功能;仍被记录且可撤销。
|
||||
- 发现服务绝不作为信任锚点。
|
||||
- 能力声明由服务器按平台/类型的白名单验证。
|
||||
|
||||
# 流式传输 + 大负载(节点媒体)
|
||||
|
||||
WS 控制平面适合小消息,但节点还需要处理:
|
||||
|
||||
- 摄像头片段
|
||||
- 屏幕录制
|
||||
- 音频流
|
||||
|
||||
方案:
|
||||
|
||||
1. WS 二进制帧 + 分块 + 背压规则。
|
||||
2. 独立流式端点(仍使用 TLS + 认证)。
|
||||
3. 对媒体密集型命令保留 bridge 更久,最后迁移。
|
||||
|
||||
实现前选定一个方案,避免分歧。
|
||||
|
||||
# 能力 + 命令策略
|
||||
|
||||
- 节点报告的 caps/commands 视为**声明**。
|
||||
- Gateway 执行按平台的白名单。
|
||||
- 任何新命令需要操作者审批或显式白名单变更。
|
||||
- 带时间戳审计变更。
|
||||
|
||||
# 审计 + 速率限制
|
||||
|
||||
- 记录:配对请求、批准/拒绝、令牌签发/轮换/撤销。
|
||||
- 对配对请求和审批提示进行速率限制。
|
||||
|
||||
# 协议规范
|
||||
|
||||
- 显式协议版本 + 错误码。
|
||||
- 重连规则 + 心跳策略。
|
||||
- 在线状态 TTL 和最后在线语义。
|
||||
|
||||
---
|
||||
|
||||
# 待解决问题
|
||||
|
||||
1. 单设备运行两种角色:令牌模型
|
||||
- 建议每个角色使用独立令牌(节点 vs 操作者)。
|
||||
- 相同 deviceId;不同作用域;更清晰的撤销。
|
||||
|
||||
2. 操作者作用域粒度
|
||||
- read/write/admin + 审批 + 配对(最小可行方案)。
|
||||
- 后续考虑按功能划分作用域。
|
||||
|
||||
3. 令牌轮换 + 撤销用户体验
|
||||
- 角色变更时自动轮换。
|
||||
- 按 deviceId + 角色撤销的 UI。
|
||||
|
||||
4. 发现服务
|
||||
- 扩展当前 Bonjour TXT 以包含 WS TLS 指纹 + 角色提示。
|
||||
- 仅作为定位提示。
|
||||
|
||||
5. 跨网络审批
|
||||
- 广播给所有操作者客户端;活跃 UI 显示模态框。
|
||||
- 首个响应生效;Gateway 保证原子性。
|
||||
|
||||
---
|
||||
|
||||
# 摘要(简述)
|
||||
|
||||
- 现状:WS 控制平面 + Bridge 节点传输。
|
||||
- 痛点:审批 + 重复 + 两套协议栈。
|
||||
- 方案:带显式角色 + 作用域的单一 WS 协议,统一配对 + TLS 固定,Gateway 托管审批,稳定设备 ID + 可爱别名。
|
||||
- 成果:更简洁的用户体验、更强的安全性、更少的重复、更好的移动端路由。
|
||||
323
docs/zh-CN/refactor/exec-host.md
Normal file
323
docs/zh-CN/refactor/exec-host.md
Normal file
@@ -0,0 +1,323 @@
|
||||
---
|
||||
read_when:
|
||||
- 设计执行主机路由或执行审批
|
||||
- 实现节点运行器 + UI IPC
|
||||
- 添加执行主机安全模式和斜杠命令
|
||||
summary: 重构计划:执行主机路由、节点审批和无头运行器
|
||||
title: 执行主机重构
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:36:56Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 53a9059cbeb1f3f1dbb48c2b5345f88ca92372654fef26f8481e651609e45e3a
|
||||
source_path: refactor/exec-host.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 执行主机重构计划
|
||||
|
||||
## 目标
|
||||
|
||||
- 添加 `exec.host` + `exec.security` 以在 **sandbox**、**gateway** 和 **node** 之间路由执行。
|
||||
- 保持默认值**安全**:除非明确启用,否则不进行跨主机执行。
|
||||
- 将执行拆分为**无头运行器服务**,并通过本地 IPC 提供可选 UI(macOS 应用)。
|
||||
- 提供**按智能体**的策略、允许列表、询问模式和节点绑定。
|
||||
- 支持与允许列表*配合*或*不配合*使用的**询问模式**。
|
||||
- 跨平台:Unix socket + token 认证(macOS/Linux/Windows 一致性)。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不进行旧版允许列表迁移或旧版 schema 支持。
|
||||
- 不为节点执行提供 PTY/流式输出(仅支持聚合输出)。
|
||||
- 不在现有 Bridge + Gateway 之外新增网络层。
|
||||
|
||||
## 决策(已锁定)
|
||||
|
||||
- **配置键:**`exec.host` + `exec.security`(允许按智能体覆盖)。
|
||||
- **提权:**保留 `/elevated` 作为 gateway 完全访问的别名。
|
||||
- **询问默认值:**`on-miss`。
|
||||
- **审批存储:**`~/.openclaw/exec-approvals.json`(JSON,不迁移旧版)。
|
||||
- **运行器:**无头系统服务;UI 应用托管 Unix socket 用于审批。
|
||||
- **节点身份:**使用现有的 `nodeId`。
|
||||
- **Socket 认证:**Unix socket + token(跨平台);后续需要时再拆分。
|
||||
- **节点主机状态:**`~/.openclaw/node.json`(节点 ID + 配对 token)。
|
||||
- **macOS 执行主机:**在 macOS 应用内运行 `system.run`;节点主机服务通过本地 IPC 转发请求。
|
||||
- **不使用 XPC helper:**坚持使用 Unix socket + token + 对端检查。
|
||||
|
||||
## 核心概念
|
||||
|
||||
### 主机
|
||||
|
||||
- `sandbox`:Docker 执行(当前行为)。
|
||||
- `gateway`:在 gateway 主机上执行。
|
||||
- `node`:通过 Bridge 在节点运行器上执行(`system.run`)。
|
||||
|
||||
### 安全模式
|
||||
|
||||
- `deny`:始终阻止。
|
||||
- `allowlist`:仅允许匹配项。
|
||||
- `full`:允许所有(等同于提权模式)。
|
||||
|
||||
### 询问模式
|
||||
|
||||
- `off`:从不询问。
|
||||
- `on-miss`:仅在允许列表不匹配时询问。
|
||||
- `always`:每次都询问。
|
||||
|
||||
询问与允许列表**相互独立**;允许列表可与 `always` 或 `on-miss` 配合使用。
|
||||
|
||||
### 策略解析(每次执行)
|
||||
|
||||
1. 解析 `exec.host`(工具参数 → 智能体覆盖 → 全局默认值)。
|
||||
2. 解析 `exec.security` 和 `exec.ask`(相同优先级)。
|
||||
3. 如果主机是 `sandbox`,使用本地沙箱执行。
|
||||
4. 如果主机是 `gateway` 或 `node`,在该主机上应用安全 + 询问策略。
|
||||
|
||||
## 默认安全
|
||||
|
||||
- 默认 `exec.host = sandbox`。
|
||||
- `gateway` 和 `node` 默认 `exec.security = deny`。
|
||||
- 默认 `exec.ask = on-miss`(仅在安全策略允许时相关)。
|
||||
- 如果未设置节点绑定,**智能体可以指向任何节点**,但仅在策略允许时。
|
||||
|
||||
## 配置接口
|
||||
|
||||
### 工具参数
|
||||
|
||||
- `exec.host`(可选):`sandbox | gateway | node`。
|
||||
- `exec.security`(可选):`deny | allowlist | full`。
|
||||
- `exec.ask`(可选):`off | on-miss | always`。
|
||||
- `exec.node`(可选):当 `host=node` 时使用的节点 ID/名称。
|
||||
|
||||
### 配置键(全局)
|
||||
|
||||
- `tools.exec.host`
|
||||
- `tools.exec.security`
|
||||
- `tools.exec.ask`
|
||||
- `tools.exec.node`(默认节点绑定)
|
||||
|
||||
### 配置键(按智能体)
|
||||
|
||||
- `agents.list[].tools.exec.host`
|
||||
- `agents.list[].tools.exec.security`
|
||||
- `agents.list[].tools.exec.ask`
|
||||
- `agents.list[].tools.exec.node`
|
||||
|
||||
### 别名
|
||||
|
||||
- `/elevated on` = 为智能体会话设置 `tools.exec.host=gateway`、`tools.exec.security=full`。
|
||||
- `/elevated off` = 为智能体会话恢复之前的执行设置。
|
||||
|
||||
## 审批存储(JSON)
|
||||
|
||||
路径:`~/.openclaw/exec-approvals.json`
|
||||
|
||||
用途:
|
||||
|
||||
- **执行主机**(gateway 或节点运行器)的本地策略 + 允许列表。
|
||||
- 无 UI 可用时的询问回退。
|
||||
- UI 客户端的 IPC 凭据。
|
||||
|
||||
建议的 schema(v1):
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"socket": {
|
||||
"path": "~/.openclaw/exec-approvals.sock",
|
||||
"token": "base64-opaque-token"
|
||||
},
|
||||
"defaults": {
|
||||
"security": "deny",
|
||||
"ask": "on-miss",
|
||||
"askFallback": "deny"
|
||||
},
|
||||
"agents": {
|
||||
"agent-id-1": {
|
||||
"security": "allowlist",
|
||||
"ask": "on-miss",
|
||||
"allowlist": [
|
||||
{
|
||||
"pattern": "~/Projects/**/bin/rg",
|
||||
"lastUsedAt": 0,
|
||||
"lastUsedCommand": "rg -n TODO",
|
||||
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意事项:
|
||||
|
||||
- 不支持旧版允许列表格式。
|
||||
- `askFallback` 仅在需要询问但无 UI 可达时生效。
|
||||
- 文件权限:`0600`。
|
||||
|
||||
## 运行器服务(无头)
|
||||
|
||||
### 角色
|
||||
|
||||
- 在本地强制执行 `exec.security` + `exec.ask`。
|
||||
- 执行系统命令并返回输出。
|
||||
- 发送 Bridge 事件用于执行生命周期(可选但建议启用)。
|
||||
|
||||
### 服务生命周期
|
||||
|
||||
- macOS 上为 Launchd/daemon;Linux/Windows 上为系统服务。
|
||||
- 审批 JSON 为执行主机本地文件。
|
||||
- UI 托管本地 Unix socket;运行器按需连接。
|
||||
|
||||
## UI 集成(macOS 应用)
|
||||
|
||||
### IPC
|
||||
|
||||
- Unix socket 位于 `~/.openclaw/exec-approvals.sock`(0600)。
|
||||
- Token 存储在 `exec-approvals.json`(0600)中。
|
||||
- 对端检查:仅限相同 UID。
|
||||
- 质询/响应:nonce + HMAC(token, request-hash) 以防止重放。
|
||||
- 短 TTL(例如 10 秒)+ 最大负载 + 速率限制。
|
||||
|
||||
### 询问流程(macOS 应用执行主机)
|
||||
|
||||
1. 节点服务从 gateway 接收 `system.run`。
|
||||
2. 节点服务连接本地 socket 并发送提示/执行请求。
|
||||
3. 应用验证对端 + token + HMAC + TTL,然后在需要时显示对话框。
|
||||
4. 应用在 UI 上下文中执行命令并返回输出。
|
||||
5. 节点服务将输出返回给 gateway。
|
||||
|
||||
如果 UI 不可用:
|
||||
|
||||
- 应用 `askFallback`(`deny|allowlist|full`)。
|
||||
|
||||
### 图示(SCI)
|
||||
|
||||
```
|
||||
Agent -> Gateway -> Bridge -> Node Service (TS)
|
||||
| IPC (UDS + token + HMAC + TTL)
|
||||
v
|
||||
Mac App (UI + TCC + system.run)
|
||||
```
|
||||
|
||||
## 节点身份 + 绑定
|
||||
|
||||
- 使用 Bridge 配对中现有的 `nodeId`。
|
||||
- 绑定模型:
|
||||
- `tools.exec.node` 将智能体限制到特定节点。
|
||||
- 如果未设置,智能体可以选择任何节点(策略仍然强制执行默认值)。
|
||||
- 节点选择解析:
|
||||
- `nodeId` 精确匹配
|
||||
- `displayName`(规范化)
|
||||
- `remoteIp`
|
||||
- `nodeId` 前缀(>= 6 个字符)
|
||||
|
||||
## 事件
|
||||
|
||||
### 谁可以看到事件
|
||||
|
||||
- 系统事件是**按会话**的,在下一个提示时展示给智能体。
|
||||
- 存储在 gateway 的内存队列中(`enqueueSystemEvent`)。
|
||||
|
||||
### 事件文本
|
||||
|
||||
- `Exec started (node=<id>, id=<runId>)`
|
||||
- `Exec finished (node=<id>, id=<runId>, code=<code>)` + 可选的输出尾部
|
||||
- `Exec denied (node=<id>, id=<runId>, <reason>)`
|
||||
|
||||
### 传输
|
||||
|
||||
方案 A(推荐):
|
||||
|
||||
- 运行器发送 Bridge `event` 帧 `exec.started` / `exec.finished`。
|
||||
- Gateway `handleBridgeEvent` 将其映射为 `enqueueSystemEvent`。
|
||||
|
||||
方案 B:
|
||||
|
||||
- Gateway `exec` 工具直接处理生命周期(仅同步)。
|
||||
|
||||
## 执行流程
|
||||
|
||||
### Sandbox 主机
|
||||
|
||||
- 现有 `exec` 行为(Docker 或非沙箱时的主机执行)。
|
||||
- 仅在非沙箱模式下支持 PTY。
|
||||
|
||||
### Gateway 主机
|
||||
|
||||
- Gateway 进程在自身机器上执行。
|
||||
- 强制执行本地 `exec-approvals.json`(安全/询问/允许列表)。
|
||||
|
||||
### Node 主机
|
||||
|
||||
- Gateway 通过 `node.invoke` 调用 `system.run`。
|
||||
- 运行器强制执行本地审批。
|
||||
- 运行器返回聚合的 stdout/stderr。
|
||||
- 可选的 Bridge 事件用于启动/完成/拒绝。
|
||||
|
||||
## 输出限制
|
||||
|
||||
- stdout+stderr 合计上限为 **200k**;事件保留**尾部 20k**。
|
||||
- 截断时添加明确后缀(例如 `"… (truncated)"`)。
|
||||
|
||||
## 斜杠命令
|
||||
|
||||
- `/exec host=<sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>`
|
||||
- 按智能体、按会话覆盖;除非通过配置保存,否则不持久化。
|
||||
- `/elevated on|off|ask|full` 仍作为 `host=gateway security=full` 的快捷方式(`full` 跳过审批)。
|
||||
|
||||
## 跨平台方案
|
||||
|
||||
- 运行器服务是可移植的执行目标。
|
||||
- UI 是可选的;如果不可用,则应用 `askFallback`。
|
||||
- Windows/Linux 支持相同的审批 JSON + socket 协议。
|
||||
|
||||
## 实施阶段
|
||||
|
||||
### 阶段 1:配置 + 执行路由
|
||||
|
||||
- 添加 `exec.host`、`exec.security`、`exec.ask`、`exec.node` 的配置 schema。
|
||||
- 更新工具管道以遵循 `exec.host`。
|
||||
- 添加 `/exec` 斜杠命令并保留 `/elevated` 别名。
|
||||
|
||||
### 阶段 2:审批存储 + gateway 强制执行
|
||||
|
||||
- 实现 `exec-approvals.json` 读写器。
|
||||
- 为 `gateway` 主机强制执行允许列表 + 询问模式。
|
||||
- 添加输出限制。
|
||||
|
||||
### 阶段 3:节点运行器强制执行
|
||||
|
||||
- 更新节点运行器以强制执行允许列表 + 询问。
|
||||
- 添加 Unix socket 提示桥接到 macOS 应用 UI。
|
||||
- 接入 `askFallback`。
|
||||
|
||||
### 阶段 4:事件
|
||||
|
||||
- 添加节点 → gateway 的 Bridge 事件用于执行生命周期。
|
||||
- 映射到 `enqueueSystemEvent` 用于智能体提示。
|
||||
|
||||
### 阶段 5:UI 完善
|
||||
|
||||
- Mac 应用:允许列表编辑器、按智能体切换器、询问策略 UI。
|
||||
- 节点绑定控制(可选)。
|
||||
|
||||
## 测试计划
|
||||
|
||||
- 单元测试:允许列表匹配(glob + 大小写不敏感)。
|
||||
- 单元测试:策略解析优先级(工具参数 → 智能体覆盖 → 全局)。
|
||||
- 集成测试:节点运行器拒绝/允许/询问流程。
|
||||
- Bridge 事件测试:节点事件 → 系统事件路由。
|
||||
|
||||
## 未解决风险
|
||||
|
||||
- UI 不可用:确保 `askFallback` 被正确执行。
|
||||
- 长时间运行的命令:依赖超时 + 输出限制。
|
||||
- 多节点歧义:除非有节点绑定或明确的节点参数,否则报错。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [执行工具](/tools/exec)
|
||||
- [执行审批](/tools/exec-approvals)
|
||||
- [节点](/nodes)
|
||||
- [提权模式](/tools/elevated)
|
||||
92
docs/zh-CN/refactor/outbound-session-mirroring.md
Normal file
92
docs/zh-CN/refactor/outbound-session-mirroring.md
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
description: Track outbound session mirroring refactor notes, decisions, tests, and open items.
|
||||
title: 出站会话镜像重构(Issue
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:36:30Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: b88a72f36f7b6d8a71fde9d014c0a87e9a8b8b0d449b67119cf3b6f414fa2b81
|
||||
source_path: refactor/outbound-session-mirroring.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 出站会话镜像重构(Issue #1520)
|
||||
|
||||
## 状态
|
||||
|
||||
- 进行中。
|
||||
- 核心 + 插件渠道路由已针对出站镜像进行更新。
|
||||
- Gateway 发送现在在省略 sessionKey 时自动推导目标会话。
|
||||
|
||||
## 背景
|
||||
|
||||
出站发送过去被镜像到*当前*智能体会话(工具会话键)而非目标渠道会话。入站路由使用渠道/对端会话键,因此出站响应落入了错误的会话,且首次联系的目标通常缺少会话条目。
|
||||
|
||||
## 目标
|
||||
|
||||
- 将出站消息镜像到目标渠道会话键。
|
||||
- 在缺少会话条目时,于出站时创建会话条目。
|
||||
- 保持线程/话题作用域与入站会话键对齐。
|
||||
- 覆盖核心渠道及捆绑扩展。
|
||||
|
||||
## 实现摘要
|
||||
|
||||
- 新增出站会话路由辅助模块:
|
||||
- `src/infra/outbound/outbound-session.ts`
|
||||
- `resolveOutboundSessionRoute` 使用 `buildAgentSessionKey`(dmScope + identityLinks)构建目标 sessionKey。
|
||||
- `ensureOutboundSessionEntry` 通过 `recordSessionMetaFromInbound` 写入最小化的 `MsgContext`。
|
||||
- `runMessageAction`(发送)推导目标 sessionKey 并传递给 `executeSendAction` 用于镜像。
|
||||
- `message-tool` 不再直接镜像;它仅从当前会话键解析 agentId。
|
||||
- 插件发送路径使用推导的 sessionKey 通过 `appendAssistantMessageToSessionTranscript` 进行镜像。
|
||||
- Gateway 发送在未提供 sessionKey 时推导目标会话键(默认智能体),并确保会话条目存在。
|
||||
|
||||
## 线程/话题处理
|
||||
|
||||
- Slack:replyTo/threadId -> `resolveThreadSessionKeys`(后缀)。
|
||||
- Discord:threadId/replyTo -> `resolveThreadSessionKeys`,`useSuffix=false` 以匹配入站(线程频道 ID 已限定会话作用域)。
|
||||
- Telegram:话题 ID 通过 `buildTelegramGroupPeerId` 映射为 `chatId:topic:<id>`。
|
||||
|
||||
## 已覆盖的扩展
|
||||
|
||||
- Matrix、MS Teams、Mattermost、BlueBubbles、Nextcloud Talk、Zalo、Zalo Personal、Nostr、Tlon。
|
||||
- 备注:
|
||||
- Mattermost 目标现在为 DM 会话键路由去除 `@` 前缀。
|
||||
- Zalo Personal 对 1:1 目标使用 DM 对端类型(仅在存在 `group:` 时使用群组)。
|
||||
- BlueBubbles 群组目标去除 `chat_*` 前缀以匹配入站会话键。
|
||||
- Slack 自动线程镜像不区分频道 ID 大小写进行匹配。
|
||||
- Gateway 发送在镜像前将提供的会话键转为小写。
|
||||
|
||||
## 决策
|
||||
|
||||
- **Gateway 发送会话推导**:如果提供了 `sessionKey`,则直接使用。如果省略,则从目标 + 默认智能体推导 sessionKey 并镜像到该会话。
|
||||
- **会话条目创建**:始终使用 `recordSessionMetaFromInbound`,其 `Provider/From/To/ChatType/AccountId/Originating*` 与入站格式对齐。
|
||||
- **目标规范化**:出站路由在可用时使用已解析的目标(经过 `resolveChannelTarget` 处理后)。
|
||||
- **会话键大小写**:在写入和迁移时将会话键规范化为小写。
|
||||
|
||||
## 新增/更新的测试
|
||||
|
||||
- `src/infra/outbound/outbound-session.test.ts`
|
||||
- Slack 线程会话键。
|
||||
- Telegram 话题会话键。
|
||||
- 使用 Discord 的 dmScope identityLinks。
|
||||
- `src/agents/tools/message-tool.test.ts`
|
||||
- 从会话键推导 agentId(不传递 sessionKey)。
|
||||
- `src/gateway/server-methods/send.test.ts`
|
||||
- 省略时推导会话键并创建会话条目。
|
||||
|
||||
## 待办事项 / 后续跟进
|
||||
|
||||
- 语音通话插件使用自定义 `voice:<phone>` 会话键。此处的出站映射尚未标准化;如果 message-tool 需要支持语音通话发送,需添加显式映射。
|
||||
- 确认是否有外部插件使用超出捆绑集合的非标准 `From/To` 格式。
|
||||
|
||||
## 涉及的文件
|
||||
|
||||
- `src/infra/outbound/outbound-session.ts`
|
||||
- `src/infra/outbound/outbound-send-service.ts`
|
||||
- `src/infra/outbound/message-action-runner.ts`
|
||||
- `src/agents/tools/message-tool.ts`
|
||||
- `src/gateway/server-methods/send.ts`
|
||||
- 测试文件:
|
||||
- `src/infra/outbound/outbound-session.test.ts`
|
||||
- `src/agents/tools/message-tool.test.ts`
|
||||
- `src/gateway/server-methods/send.test.ts`
|
||||
221
docs/zh-CN/refactor/plugin-sdk.md
Normal file
221
docs/zh-CN/refactor/plugin-sdk.md
Normal file
@@ -0,0 +1,221 @@
|
||||
---
|
||||
read_when:
|
||||
- 定义或重构插件架构
|
||||
- 将渠道连接器迁移到插件 SDK/运行时
|
||||
summary: 计划:为所有消息连接器提供一套统一的插件 SDK + 运行时
|
||||
title: 插件 SDK 重构
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:36:45Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: d1964e2e47a19ee1d42ddaaa9cf1293c80bb0be463b049dc8468962f35bb6cb0
|
||||
source_path: refactor/plugin-sdk.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 插件 SDK + 运行时重构计划
|
||||
|
||||
目标:每个消息连接器都是一个插件(内置或外部),使用统一稳定的 API。
|
||||
插件不直接从 `src/**` 导入任何内容。所有依赖项均通过 SDK 或运行时获取。
|
||||
|
||||
## 为什么现在做
|
||||
|
||||
- 当前连接器混用多种模式:直接导入核心模块、仅 dist 的桥接方式以及自定义辅助函数。
|
||||
- 这使得升级变得脆弱,并阻碍了干净的外部插件接口。
|
||||
|
||||
## 目标架构(两层)
|
||||
|
||||
### 1)插件 SDK(编译时,稳定,可发布)
|
||||
|
||||
范围:类型、辅助函数和配置工具。无运行时状态,无副作用。
|
||||
|
||||
内容(示例):
|
||||
|
||||
- 类型:`ChannelPlugin`、适配器、`ChannelMeta`、`ChannelCapabilities`、`ChannelDirectoryEntry`。
|
||||
- 配置辅助函数:`buildChannelConfigSchema`、`setAccountEnabledInConfigSection`、`deleteAccountFromConfigSection`、
|
||||
`applyAccountNameToChannelSection`。
|
||||
- 配对辅助函数:`PAIRING_APPROVED_MESSAGE`、`formatPairingApproveHint`。
|
||||
- 上手引导辅助函数:`promptChannelAccessConfig`、`addWildcardAllowFrom`、上手引导类型。
|
||||
- 工具参数辅助函数:`createActionGate`、`readStringParam`、`readNumberParam`、`readReactionParams`、`jsonResult`。
|
||||
- 文档链接辅助函数:`formatDocsLink`。
|
||||
|
||||
交付方式:
|
||||
|
||||
- 以 `openclaw/plugin-sdk` 发布(或从核心以 `openclaw/plugin-sdk` 导出)。
|
||||
- 使用语义化版本控制,提供明确的稳定性保证。
|
||||
|
||||
### 2)插件运行时(执行层,注入式)
|
||||
|
||||
范围:所有涉及核心运行时行为的内容。
|
||||
通过 `OpenClawPluginApi.runtime` 访问,确保插件永远不会导入 `src/**`。
|
||||
|
||||
建议的接口(最小但完整):
|
||||
|
||||
```ts
|
||||
export type PluginRuntime = {
|
||||
channel: {
|
||||
text: {
|
||||
chunkMarkdownText(text: string, limit: number): string[];
|
||||
resolveTextChunkLimit(cfg: OpenClawConfig, channel: string, accountId?: string): number;
|
||||
hasControlCommand(text: string, cfg: OpenClawConfig): boolean;
|
||||
};
|
||||
reply: {
|
||||
dispatchReplyWithBufferedBlockDispatcher(params: {
|
||||
ctx: unknown;
|
||||
cfg: unknown;
|
||||
dispatcherOptions: {
|
||||
deliver: (payload: {
|
||||
text?: string;
|
||||
mediaUrls?: string[];
|
||||
mediaUrl?: string;
|
||||
}) => void | Promise<void>;
|
||||
onError?: (err: unknown, info: { kind: string }) => void;
|
||||
};
|
||||
}): Promise<void>;
|
||||
createReplyDispatcherWithTyping?: unknown; // adapter for Teams-style flows
|
||||
};
|
||||
routing: {
|
||||
resolveAgentRoute(params: {
|
||||
cfg: unknown;
|
||||
channel: string;
|
||||
accountId: string;
|
||||
peer: { kind: "dm" | "group" | "channel"; id: string };
|
||||
}): { sessionKey: string; accountId: string };
|
||||
};
|
||||
pairing: {
|
||||
buildPairingReply(params: { channel: string; idLine: string; code: string }): string;
|
||||
readAllowFromStore(channel: string): Promise<string[]>;
|
||||
upsertPairingRequest(params: {
|
||||
channel: string;
|
||||
id: string;
|
||||
meta?: { name?: string };
|
||||
}): Promise<{ code: string; created: boolean }>;
|
||||
};
|
||||
media: {
|
||||
fetchRemoteMedia(params: { url: string }): Promise<{ buffer: Buffer; contentType?: string }>;
|
||||
saveMediaBuffer(
|
||||
buffer: Uint8Array,
|
||||
contentType: string | undefined,
|
||||
direction: "inbound" | "outbound",
|
||||
maxBytes: number,
|
||||
): Promise<{ path: string; contentType?: string }>;
|
||||
};
|
||||
mentions: {
|
||||
buildMentionRegexes(cfg: OpenClawConfig, agentId?: string): RegExp[];
|
||||
matchesMentionPatterns(text: string, regexes: RegExp[]): boolean;
|
||||
};
|
||||
groups: {
|
||||
resolveGroupPolicy(
|
||||
cfg: OpenClawConfig,
|
||||
channel: string,
|
||||
accountId: string,
|
||||
groupId: string,
|
||||
): {
|
||||
allowlistEnabled: boolean;
|
||||
allowed: boolean;
|
||||
groupConfig?: unknown;
|
||||
defaultConfig?: unknown;
|
||||
};
|
||||
resolveRequireMention(
|
||||
cfg: OpenClawConfig,
|
||||
channel: string,
|
||||
accountId: string,
|
||||
groupId: string,
|
||||
override?: boolean,
|
||||
): boolean;
|
||||
};
|
||||
debounce: {
|
||||
createInboundDebouncer<T>(opts: {
|
||||
debounceMs: number;
|
||||
buildKey: (v: T) => string | null;
|
||||
shouldDebounce: (v: T) => boolean;
|
||||
onFlush: (entries: T[]) => Promise<void>;
|
||||
onError?: (err: unknown) => void;
|
||||
}): { push: (v: T) => void; flush: () => Promise<void> };
|
||||
resolveInboundDebounceMs(cfg: OpenClawConfig, channel: string): number;
|
||||
};
|
||||
commands: {
|
||||
resolveCommandAuthorizedFromAuthorizers(params: {
|
||||
useAccessGroups: boolean;
|
||||
authorizers: Array<{ configured: boolean; allowed: boolean }>;
|
||||
}): boolean;
|
||||
};
|
||||
};
|
||||
logging: {
|
||||
shouldLogVerbose(): boolean;
|
||||
getChildLogger(name: string): PluginLogger;
|
||||
};
|
||||
state: {
|
||||
resolveStateDir(cfg: OpenClawConfig): string;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
备注:
|
||||
|
||||
- 运行时是访问核心行为的唯一方式。
|
||||
- SDK 故意保持小巧和稳定。
|
||||
- 每个运行时方法都映射到现有的核心实现(无重复代码)。
|
||||
|
||||
## 迁移计划(分阶段,安全)
|
||||
|
||||
### 阶段 0:基础搭建
|
||||
|
||||
- 引入 `openclaw/plugin-sdk`。
|
||||
- 在 `OpenClawPluginApi` 中添加带有上述接口的 `api.runtime`。
|
||||
- 在过渡期内保留现有导入方式(添加弃用警告)。
|
||||
|
||||
### 阶段 1:桥接清理(低风险)
|
||||
|
||||
- 用 `api.runtime` 替换每个扩展中的 `core-bridge.ts`。
|
||||
- 优先迁移 BlueBubbles、Zalo、Zalo Personal(已经接近完成)。
|
||||
- 移除重复的桥接代码。
|
||||
|
||||
### 阶段 2:轻度直接导入的插件
|
||||
|
||||
- 将 Matrix 迁移到 SDK + 运行时。
|
||||
- 验证上手引导、目录、群组提及逻辑。
|
||||
|
||||
### 阶段 3:重度直接导入的插件
|
||||
|
||||
- 迁移 Microsoft Teams(使用运行时辅助函数最多的插件)。
|
||||
- 确保回复/正在输入的语义与当前行为一致。
|
||||
|
||||
### 阶段 4:iMessage 插件化
|
||||
|
||||
- 将 iMessage 移入 `extensions/imessage`。
|
||||
- 用 `api.runtime` 替换直接的核心调用。
|
||||
- 保持配置键、CLI 行为和文档不变。
|
||||
|
||||
### 阶段 5:强制执行
|
||||
|
||||
- 添加 lint 规则 / CI 检查:禁止 `extensions/**` 从 `src/**` 导入。
|
||||
- 添加插件 SDK/版本兼容性检查(运行时 + SDK 语义化版本)。
|
||||
|
||||
## 兼容性与版本控制
|
||||
|
||||
- SDK:语义化版本控制,已发布,变更有文档记录。
|
||||
- 运行时:按核心版本进行版本控制。添加 `api.runtime.version`。
|
||||
- 插件声明所需的运行时版本范围(例如 `openclawRuntime: ">=2026.2.0"`)。
|
||||
|
||||
## 测试策略
|
||||
|
||||
- 适配器级单元测试(使用真实核心实现验证运行时函数)。
|
||||
- 每个插件的黄金测试:确保行为无偏差(路由、配对、允许列表、提及过滤)。
|
||||
- CI 中使用单个端到端插件示例(安装 + 运行 + 冒烟测试)。
|
||||
|
||||
## 待解决问题
|
||||
|
||||
- SDK 类型托管在哪里:独立包还是核心导出?
|
||||
- 运行时类型分发:在 SDK 中(仅类型)还是在核心中?
|
||||
- 如何为内置插件与外部插件暴露文档链接?
|
||||
- 过渡期间是否允许仓库内插件有限地直接导入核心模块?
|
||||
|
||||
## 成功标准
|
||||
|
||||
- 所有渠道连接器都是使用 SDK + 运行时的插件。
|
||||
- `extensions/**` 不再从 `src/**` 导入。
|
||||
- 新连接器模板仅依赖 SDK + 运行时。
|
||||
- 外部插件可以在无需访问核心源码的情况下进行开发和更新。
|
||||
|
||||
相关文档:[插件](/plugin)、[渠道](/channels/index)、[配置](/gateway/configuration)。
|
||||
100
docs/zh-CN/refactor/strict-config.md
Normal file
100
docs/zh-CN/refactor/strict-config.md
Normal file
@@ -0,0 +1,100 @@
|
||||
---
|
||||
read_when:
|
||||
- 设计或实现配置验证行为时
|
||||
- 处理配置迁移或 doctor 工作流时
|
||||
- 处理插件配置 schema 或插件加载控制时
|
||||
summary: 严格配置验证 + 仅通过 doctor 执行迁移
|
||||
title: 严格配置验证
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:36:38Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 5bc7174a67d2234e763f21330d8fe3afebc23b2e5c728a04abcc648b453a91cc
|
||||
source_path: refactor/strict-config.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# 严格配置验证(仅通过 doctor 执行迁移)
|
||||
|
||||
## 目标
|
||||
|
||||
- **在所有层级(根级 + 嵌套级)拒绝未知配置键**。
|
||||
- **拒绝没有 schema 的插件配置**;不加载该插件。
|
||||
- **移除加载时的遗留自动迁移**;迁移仅通过 doctor 执行。
|
||||
- **启动时自动运行 doctor(dry-run 模式)**;如果配置无效,阻止非诊断命令。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 加载时的向后兼容性(遗留键不会自动迁移)。
|
||||
- 静默丢弃未识别的键。
|
||||
|
||||
## 严格验证规则
|
||||
|
||||
- 配置必须在每个层级精确匹配 schema。
|
||||
- 未知键是验证错误(根级和嵌套级均不允许透传)。
|
||||
- `plugins.entries.<id>.config` 必须由插件的 schema 进行验证。
|
||||
- 如果插件缺少 schema,**拒绝插件加载**并显示明确的错误信息。
|
||||
- 未知的 `channels.<id>` 键是错误,除非插件清单声明了该渠道 id。
|
||||
- 所有插件都必须提供插件清单(`openclaw.plugin.json`)。
|
||||
|
||||
## 插件 schema 强制执行
|
||||
|
||||
- 每个插件为其配置提供严格的 JSON Schema(内联在清单中)。
|
||||
- 插件加载流程:
|
||||
1. 解析插件清单 + schema(`openclaw.plugin.json`)。
|
||||
2. 根据 schema 验证配置。
|
||||
3. 如果缺少 schema 或配置无效:阻止插件加载,记录错误。
|
||||
- 错误信息包括:
|
||||
- 插件 id
|
||||
- 原因(缺少 schema / 配置无效)
|
||||
- 验证失败的路径
|
||||
- 已禁用的插件保留其配置,但 Doctor + 日志会显示警告。
|
||||
|
||||
## Doctor 流程
|
||||
|
||||
- 每次加载配置时都会运行 Doctor(默认 dry-run 模式)。
|
||||
- 如果配置无效:
|
||||
- 打印摘要 + 可操作的错误信息。
|
||||
- 提示:`openclaw doctor --fix`。
|
||||
- `openclaw doctor --fix`:
|
||||
- 应用迁移。
|
||||
- 移除未知键。
|
||||
- 写入更新后的配置。
|
||||
|
||||
## 命令控制(配置无效时)
|
||||
|
||||
允许执行(仅限诊断命令):
|
||||
|
||||
- `openclaw doctor`
|
||||
- `openclaw logs`
|
||||
- `openclaw health`
|
||||
- `openclaw help`
|
||||
- `openclaw status`
|
||||
- `openclaw gateway status`
|
||||
|
||||
其他所有命令必须直接失败并提示:"Config invalid. Run `openclaw doctor --fix`."
|
||||
|
||||
## 错误用户体验格式
|
||||
|
||||
- 单个摘要标题。
|
||||
- 分组展示:
|
||||
- 未知键(完整路径)
|
||||
- 遗留键 / 需要迁移
|
||||
- 插件加载失败(插件 id + 原因 + 路径)
|
||||
|
||||
## 实现涉及的文件
|
||||
|
||||
- `src/config/zod-schema.ts`:移除根级 passthrough;所有对象使用 strict 模式。
|
||||
- `src/config/zod-schema.providers.ts`:确保渠道 schema 为 strict 模式。
|
||||
- `src/config/validation.ts`:遇到未知键时失败;不应用遗留迁移。
|
||||
- `src/config/io.ts`:移除遗留自动迁移;始终运行 doctor dry-run。
|
||||
- `src/config/legacy*.ts`:将用法迁移到仅限 doctor 使用。
|
||||
- `src/plugins/*`:添加 schema 注册表 + 加载控制。
|
||||
- `src/cli` 中的 CLI 命令控制。
|
||||
|
||||
## 测试
|
||||
|
||||
- 未知键拒绝(根级 + 嵌套级)。
|
||||
- 插件缺少 schema → 插件加载被阻止并显示明确错误。
|
||||
- 配置无效 → Gateway 启动被阻止,诊断命令除外。
|
||||
- Doctor dry-run 自动运行;`doctor --fix` 写入修正后的配置。
|
||||
Reference in New Issue
Block a user