从 BlueBubbles 迁移到 OpenClaw 官方 iMessage 插件:配置翻译、群组注册表陷阱与切换验证指南
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
本篇指南面向仍在使用旧版channels.bluebubbles配置的 OpenClaw 用户,完整讲解如何将旧配置翻译为官方@openclaw/imessage插件的channels.imessage配置,包括配置键映射表、imsg的本地验证流程、群组双重闸门(sender allowlist + group registry)的工作原理,以及切换后的逐项验证方法。读完本文,你将能够在不依赖任何 BlueBubbles 服务器的情况下,把 iMessage 通道平稳迁移到基于imsg的原生方案,并定位迁移中最常见的"群组静默"问题。
背景:BlueBubbles 已被移除
OpenClaw 不再内置 BlueBubbles 通道。iMessage 支持只通过官方@openclaw/imessage插件提供,该插件驱动steipete/imsg,其中明确指出:唯一的迁移路径就是把channels.bluebubbles配置迁移到channels.imessage,没有其他受支持的迁移途径。在当前版本 OpenClaw 中,遗留的channels.bluebubbles配置块是惰性的——没有任何运行时读取它。
架构上的核心变化是:一个 CLI 二进制替代了 BlueBubbles 的"服务器 + 客户端 App + webhook 管线"整套组件。旧方案有 REST 端点、有 webhook 鉴权密码;新方案没有任何 HTTP 服务器、没有 webhook URL、没有后台守护进程、没有 launch agent、也没有需要暴露的端口。
imsg是什么:JSON-RPC over stdio
imsg是一个运行在登录了 Messages.app 的 Mac 上的本地 CLI。OpenClaw Gateway 以子进程方式启动imsg rpc,通过标准输入/输出(stdin/stdout)以换行分隔的 JSON-RPC 2.0 协议通信。从插件源码 extensions/imessage/src/client.ts 可以看到,IMessageRpcClient.start()使用spawn(cliPath, ["rpc", "--json"])拉起子进程,并用一个 LF 帧解析器逐行读取 stdout 上的 JSON 响应、把 stderr 诊断写入运行时日志。
imsg的各条数据路径分工如下:
- 读取:来自
~/Library/Messages/chat.db,使用只读 SQLite 句柄。 - 实时入站:来自
imsg watch/watch.subscribe,它监听chat.db的文件系统事件,并带轮询兜底。 - 发送:普通文本和文件发送走 Messages.app 自动化(AppleScript/AppleEvents)。
- 高级动作:使用
imsg launch将imsghelper 注入 Messages.app,从而解锁已读回执、正在输入指示器、富文本发送、编辑、撤回、线程回复、tapback、投票和群组管理。
需要特别说明平台限制:Linux 构建可以读取拷贝过来的chat.db,但无法发送、无法 watch 实时 Mac 数据库、也无法驱动 Messages.app。因此 OpenClaw iMessage 必须让imsg运行在已登录的 Mac 上,或者通过 SSH wrapper 转发到那台 Mac。
关于桥接健康还有一个值得注意的实现细节:源码 extensions/imessage/src/private-api-status.ts 维护了按cliPath键控的 private API 状态缓存。当imsg自身的结构化等待错误(Timed out waiting for response to ...)表明桥接卡死时,extensions/imessage/src/client.ts 会先失效缓存,再调用 extensions/imessage/src/bridge-recovery.ts 自动执行一次imsg launch重新注入 dylib,同时刻意不重放失败的那次发送——因为imsg可能已经完成了发送,重放会导致消息重复。
迁移检查清单
如果你已经清楚旧 BlueBubbles 配置,最短的安全路径如下:
- 用
openclaw plugins install @openclaw/imessage安装官方插件,然后重启 Gateway。 - 在运行 Messages.app 的 Mac 上直接验证
imsg(imsg chats、imsg history、imsg send、imsg rpc --help)。 - 把行为类配置键从
channels.bluebubbles复制到channels.imessage:dmPolicy、allowFrom、groupPolicy、groupAllowFrom、groups、includeAttachments、attachmentRoots、mediaMaxMb、textChunkLimit和actions。 - 丢弃不再存在的传输类配置键:
serverUrl、password、webhook URL 以及 BlueBubbles 服务器相关设置。 - 如果 Gateway 不在 Messages Mac 上运行,把
channels.imessage.cliPath设置为 Gateway 本地、指向 SSH wrapper 的绝对路径,并让dbPath保持为该 Mac 上的绝对路径;复杂 wrapper 场景下把remoteHost设为 Messages Mac 的主机名或user@host。OpenClaw 会自动识别简单的透明 wrapper 形态以保持兼容。 - 启用
channels.imessage,重启 Gateway,然后运行openclaw channels status --probe --channel imessage。 - 测试一条 DM、一个获准的群组、按需测试附件,以及你期望 agent 使用的每一个私有 API 动作。
- 确认 iMessage 路径可用后,删除 BlueBubbles 服务器和旧的
channels.bluebubbles配置块。
一个已知的远程限制需要注意:远程imsgv0.13.4 有两个狭窄的 RPC 限制——投票必须使用pollOptionId而不能用下标或选项文本;附件回复不能指向非零的消息片段下标。本地imsg行为不受影响。
开始之前:安装与验证imsg
1. 在运行 Messages.app 的 Mac 上安装imsg
brew install steipete/tap/imsg brew update && brew upgrade imsg imsg --version imsg chats --limit 3对于常规本地部署,OpenClaw setup 可以在已登录的 Messages Mac 上提供经用户确认的 Homebrew 安装或更新。手动安装和 SSH wrapper 拓扑仍需运维自行管理:务必在实际运行imsg的那个本地或远程用户上下文里重复 Homebrew 更新。
如果imsg chats报unable to open database file、返回空输出或authorization denied,需要给启动imsg的终端、编辑器、Node 进程、Gateway 服务或 SSH 父进程授予完全磁盘访问权限(Full Disk Access),然后重新打开该父进程。
2. 修改 OpenClaw 配置前,先验证读、watch、发送和 RPC 四个面
imsg chats --limit 10 --json | jq -s imsg history --chat-id 42 --limit 10 --attachments --json | jq -s imsg watch --chat-id 42 --reactions --json imsg send --chat-id 42 --text "OpenClaw imsg test" imsg rpc --help把42替换为imsg chats输出的真实 chat id。发送需要 Messages.app 的自动化(Automation)权限。如果 OpenClaw 通过 SSH 运行,请用同样的 SSH wrapper 或用户上下文执行这些命令。如果读取正常但发送报 AppleEvents-1743,请检查 Automation 权限是否落到了/usr/libexec/sshd-keygen-wrapper上,详见 iMessage 文档 中的权限说明。
3. 启用私有 API 桥接(强烈建议)
imsg launch imsg status --jsonimsg launch要求关闭 SIP(现代 macOS 上还需要放宽库验证,见 iMessage 文档)。基本的发送、历史记录和 watch 不需要imsg launch也能工作,但 OpenClaw iMessage 的完整动作面(回复、tapback、特效、投票、附件回复、群组动作)依赖它。
4. 启用channels.imessage并启动 Gateway 后,通过 OpenClaw 验证桥接
openclaw channels status --probeiMessage 账户应报告works;加--json时,probe 负载中应包含privateApi.available: true。如果报告false,先修复这个问题(见 iMessage 文档 的能力检测章节)。注意:probe 需要 Gateway 可达(否则 CLI 退化为仅输出配置),并且只探测已配置、已启用的账户。
5. 快照你的配置
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.bak配置翻译:逐键对照表
iMessage 与 BlueBubbles 共享大部分通道级行为键,变化的是传输层(REST 服务器 → 本地 CLI)和群组注册表的键格式。
| BlueBubbles | iMessage 插件 | 说明 |
|---|---|---|
channels.bluebubbles.enabled | channels.imessage.enabled | 语义相同(配置块存在时默认true)。 |
channels.bluebubbles.serverUrl | (已移除) | 没有 REST 服务器——插件在 stdio 上拉起imsg rpc。 |
channels.bluebubbles.password | (已移除) | 无需 webhook 鉴权。 |
| (隐式) | channels.imessage.cliPath | imsg路径(默认imsg);SSH 场景用指向 Gateway 主机上 wrapper 的绝对路径。 |
| (隐式) | channels.imessage.dbPath | 可选的chat.db覆盖路径;SSH 场景下是 Messages Mac 上的绝对路径,绝不会相对 Gateway 家目录展开。 |
| (隐式) | channels.imessage.remoteHost | Messages Mac,格式为host或user@host;显式配置优先,简单的透明 SSH wrapper 每个进程自动检测一次。用于通过严格 SSH/SCP 拉取入站附件与执行仅属主出站暂存。清理是尽力而为,失败会告警并可能残留属主文件。 |
channels.bluebubbles.dmPolicy | channels.imessage.dmPolicy | 取值相同(pairing/allowlist/open/disabled),默认pairing。 |
channels.bluebubbles.allowFrom | channels.imessage.allowFrom | handle 格式相同(+15555550123、user@example.com)。配对存储中的审批不会迁移,见下文。 |
channels.bluebubbles.groupPolicy | channels.imessage.groupPolicy | 取值相同(allowlist/open/disabled),默认allowlist。 |
channels.bluebubbles.groupAllowFrom | channels.imessage.groupAllowFrom | 相同。未设置时 iMessage 回退到allowFrom;显式设置groupAllowFrom: []会在groupPolicy: "allowlist"下拦截所有群消息。 |
channels.bluebubbles.groups | channels.imessage.groups | 原样复制"*"通配项;按数字 iMessagechat_id重新键控逐群条目——见"群组注册表陷阱"。requireMention、tools、toolsBySender、systemPrompt原样继承。 |
channels.bluebubbles.sendReadReceipts | channels.imessage.sendReadReceipts | 默认true,仅在私有 API probe 通过后生效。 |
channels.bluebubbles.includeAttachments | channels.imessage.includeAttachments | 结构相同,同样默认关闭。如果 BlueBubbles 时代有附件流动,请显式开启——否则入站照片/媒体会被静默丢弃(日志里连Inbound message行都不会有)。 |
channels.bluebubbles.attachmentRoots | channels.imessage.attachmentRoots | 本地根目录,通配规则相同。 |
| (N/A) | channels.imessage.remoteAttachmentRoots | 仅在设置remoteHost时用于 SCP 拉取。 |
channels.bluebubbles.mediaMaxMb | channels.imessage.mediaMaxMb | iMessage 默认 16 MB(BlueBubbles 默认 8 MB)。想保持低上限就显式设置。 |
channels.bluebubbles.textChunkLimit | channels.imessage.textChunkLimit | 两者默认都是 4000。 |
channels.bluebubbles.coalesceSameSenderDms | (已移除) | 不要迁移这个键。imsg0.13.1 及更新版本会在 OpenClaw 收到消息前合并 Apple 的 URL 预览拆分发送;openclaw doctor --fix会移除过期的 iMessage 键。 |
channels.bluebubbles.enrichGroupParticipantsFromContacts | (N/A) | imsg已从chat.db提供发送者显示名。 |
channels.bluebubbles.actions.* | channels.imessage.actions.* | 逐动作开关相同(reactions、edit、unsend、reply、sendWithEffect、renameGroup、setGroupIcon、addParticipant、removeParticipant、leaveGroup、sendAttachment),外加新增的polls。全部默认启用;私有 API 动作仍要求桥接可用。 |
多账户配置(channels.bluebubbles.accounts.*)一对一翻译为channels.imessage.accounts.*。
从源码看,这些键在 extensions/imessage/src/config-schema.ts 中均有对应的 Zod schema 约束:actions是带 12 个布尔开关的严格对象;remoteHost通过isSafeScpRemoteHost校验(只接受 SSH host 或user@host,不含空格/选项);cliPath是ExecutableTokenSchema;groups是记录类型(record),每个条目可携带requireMention、工具策略等选项。dmPolicy的取值约束通过refineChannelDmPolicy应用到通道级与每个账户级。
群组注册表陷阱:双重闸门
iMessage 插件对群消息连续执行两道闸门,必须同时通过消息才会到达 agent:
- 发送者 / 会话目标 allowlist(
channels.imessage.groupAllowFrom)——匹配发送者 handle 或会话目标(chat_id:、chat_guid:、chat_identifier:条目)。groupAllowFrom未设置时回退到allowFrom;显式groupAllowFrom: []则禁用回退,并在groupPolicy: "allowlist"下丢弃所有群消息。 - 群组注册表(
channels.imessage.groups)——以数字 iMessagechat_id为键:- 没有
groups块(或为空):只要闸门 1 的有效发送者 allowlist 非空,群消息就通过本闸门,由发送者过滤控制访问,且不会触发 drop-all 启动告警。 groups有条目但没有"*":只有列出的chat_id键能通过。只要列出了任何群,注册表就变成 allowlist,即使groupPolicy: "open"也一样。groups: { "*": { ... } }:所有群都通过本闸门。
- 没有
迁移陷阱就在这里:BlueBubbles 用 chat GUID / chat identifier 作为groups条目的键,而 iMessage 注册表用数字chat_id。逐条照抄的 per-group 条目会形成一个非空注册表,但键永远匹配不上,于是每条群消息都在闸门 2 被丢弃。正确做法:"*"通配项原样复制;具体群条目用imsg chats返回的chat_id重新键控。
两条丢弃路径在默认日志级别下都可见,均为warn行:
- 每个账户在启动时打印一次——当
groupPolicy: "allowlist"且有效群发送者 allowlist 为空时:imessage: groupPolicy="allowlist" for account "<id>" but no group sender allowlist is configured ...。设置groupAllowFrom(或allowFrom)来放行发送者;单独添加groups不能满足发送者闸门。 - 每个
chat_id在运行时打印一次——注册表丢弃某群时:imessage: dropping group message from chat_id=<id> ... not in channels.imessage.groups allowlist,并明确指出需要添加的键。
注意:DM 无论怎样都正常工作——它们走的是另一条代码路径,所以 DM 成功并不能证明群组路由正常。
groupPolicy: "allowlist"下的最小发送者作用域配置:
{ channels: { imessage: { groupPolicy: "allowlist", groupAllowFrom: ["+15555550123", "chat_guid:any;-;..."], }, }, }这会放行配置中的发送者进入任何群。如需限定允许的会话或设置requireMention等 per-chat 选项,再添加groups条目;BlueBubbles 的"*"条目原样复制,具体条目用数字chat_id重新键控。群策略的运行时解析实现见 extensions/imessage/src/group-policy.ts,它通过 SDK 的buildChannelGroupsScopeTree构建作用域树,再解析requireMention与工具策略。
逐步切换
翻译配置。编辑期间保持新块禁用;旧的
channels.bluebubbles块会被当前 OpenClaw 忽略,可以留着作参考:{ channels: { imessage: { enabled: false, // 就绪后翻成 true 即可切换 cliPath: "/opt/homebrew/bin/imsg", dmPolicy: "pairing", allowFrom: ["+15555550123"], // 从 bluebubbles.allowFrom 复制 groupPolicy: "allowlist", groupAllowFrom: [], // 从 bluebubbles.groupAllowFrom 复制 groups: { "*": { requireMention: true } }, // 通配项原样复制;per-chat 条目按 chat_id 重新键控 // actions 默认全部启用;需要禁用的动作显式设为 false }, }, }切换并探测。设置
channels.imessage.enabled: true,重启 Gateway,确认通道健康:openclaw gateway restart openclaw channels status --probe --channel imessage # 期望 "works";--json 显示 privateApi.available: trueprobe 要求 Gateway 可达,且只探测已配置、已启用的账户。用上面"开始之前"里的
imsg直接命令验证 Mac 本身。验证 DM。给 agent 发一条私信,确认回复到达。
单独验证群组。DM 和群组走不同代码路径——DM 成功不能证明群组在路由。在获准的群聊里发一条消息,确认回复到达。如果群组静默(无 agent 回复、无报错),去 gateway 日志查上面"群组注册表陷阱"提到的两条
warn行:启动告警意味着有效发送者 allowlist 为空;per-chat_id告警意味着已填充的groups注册表不包含该会话。验证动作面。从已配对的 DM 中,让 agent 依次执行:点按回应(react)、编辑、撤回、回复、发送照片,以及在群里重命名群组或添加/移除成员。每个动作都应原生落在 Messages.app 中。如果某动作抛出
iMessage <action> requires the imsg private API bridge,重新运行imsg launch并用openclaw channels status --probe刷新能力检测。删除 BlueBubbles 服务器和
channels.bluebubbles配置块,前提是 iMessage 的 DM、群组和动作都已验证。OpenClaw 不读取channels.bluebubbles。
动作平价一览
| 动作 | 旧 BlueBubbles | iMessage 插件 |
|---|---|---|
| 发送文本 / SMS 回退 | ✅ | ✅ |
| 发送媒体(照片、视频、文件、语音) | ✅ | ✅ |
线程回复(reply_to_guid) | ✅ | ✅ |
点按回应(react) | ✅ | ✅ |
| 编辑 / 撤回(macOS 13+ 接收方) | ✅ | ✅ |
| 带屏幕特效发送 | ✅ | ✅ |
| 富文本粗体 / 斜体 / 下划线 / 删除线 | ✅ | ✅(通过 attributedBody 的 typed-run 格式化) |
| 原生 Messages 投票(创建与投票) | ❌ | ✅(actions.polls;接收方需 iOS/macOS 26+ 才能原生渲染) |
| 重命名群组 / 设置群图标 | ✅ | ✅ |
| 添加 / 移除成员、退出群组 | ✅ | ✅ |
| 已读回执与正在输入指示 | ✅ | ✅(以私有 API probe 通过为前提) |
| Apple URL 预览拆分发送合并 | ✅ | ✅(由imsg0.13.1 及更新版本在上游处理,无 OpenClaw 配置项) |
| 重启后的入站恢复 | ✅ | ✅(自动:since_rowid重放 + GUID 去重;本地场景窗口更宽) |
重启后的入站恢复值得展开:Gateway 宕机期间错过的消息会在启动时自动补回——通过imsg watch.subscribe的since_rowid从最后已分发的 rowid 重放,按 GUID 去重,并用过期积压年龄围栏(stale-backlog age fence)抑制 Apple 在 Push 恢复后可能冲刷的"积压炸弹"。这个机制跑在imsgRPC 连接之上,因此远程 SSHcliPath部署同样生效;本地部署因为能直接读chat.db,恢复窗口更宽。详见 iMessage 文档。
配对、会话与 ACP 绑定
- allowlist 按 handle 原样继承。
channels.imessage.allowFrom识别与 BlueBubbles 相同的+15555550123/user@example.com字符串,直接原样复制。 - 配对存储中的审批不会迁移。配对存储是按通道隔离的,没有任何机制迁移旧 BlueBubbles 存储。仅通过配对获得审批的发送者需要在 iMessage 下重新配对一次,或者你把这些 handle 加入
allowFrom。 - 会话保持按 agent + 会话隔离。默认
session.dmScope=main下 DM 折叠进 agent 主会话;默认session.groupScope="per-group"下群会话按chat_id隔离(agent:<agentId>:imessage:group:<chat_id>)。BlueBubbles 会话键下的旧对话历史不会进入 iMessage 会话。 - ACP 绑定中引用
match.channel: "bluebubbles"的必须改成"imessage"。match.peer.id的形状(chat_id:、chat_guid:、chat_identifier:、裸 handle)完全一致。
没有回滚通道
不存在可以切回的受支持 BlueBubbles 运行时。如果 iMessage 验证失败,处理方式是把channels.imessage.enabled: false、重启 Gateway、修复imsg阻塞点,然后重试切换。
回复缓存保存在 SQLite 插件状态里。openclaw doctor --fix会在旧imessage/reply-cache.jsonlsidecar 存在时将其导入并归档。
相关文档
- BlueBubbles removal and the imsg iMessage path — 移除公告与运维摘要。
- iMessage — 完整 iMessage 通道参考,包括
imsg launch配置与能力检测。 - Pairing — DM 鉴权与配对流程。
- Channel routing — Gateway 如何为出站回复选择通道。
- 插件包说明见 extensions/imessage/README.md:插件 id
imessage,包名@openclaw/imessage,最低 OpenClaw 主机版本2026.7.2;RPC 超时默认值(probe 10 秒、发送 180 秒)见 extensions/imessage/src/constants.ts。
【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 🦞项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考