先看懂三条链路,再开始配置
你可能常常看到有人直接在飞书里下指令,让电脑上的 WorkBuddy 或 Codex 自动处理任务,却不知道这套流程究竟是怎么搭起来的。这篇教程会从创建飞书应用开始,带你逐步完成权限配置、消息连接和应用发布,实际跑通飞书与 WorkBuddy 的双向对话和定时推送,同时讲清 Codex 通过独立应用和本地桥接接入飞书的完整路径。
本文会完整实操 WorkBuddy 双向对话和定时推送;Codex 部分只讲清独立接入路径并完成本机安全验证,飞书端桥接开发不在本篇实操范围。
1. 先看懂三条链路,准备账号与安全边界
开始配置之前,先把三条消息路径分开。它们都以飞书为入口或出口,但接收消息、执行任务和回传结果的方式并不一样。分清每条路径使用的应用和机器人,后面配置权限、事件和凭证时才不会混淆。
人在外面发出任务,办公室电脑继续执行并返回结果
即时对话:用飞书向 WorkBuddy 发任务
即时对话是本文的主路径:
手机飞书 → 企业自建应用机器人 → WebSocket 长连接 → WorkBuddy → 电脑执行 → 飞书回复
你在手机飞书中把任务发给企业自建应用机器人。WebSocket 长连接让电脑上的 WorkBuddy 持续接收飞书消息;WorkBuddy 收到任务后在电脑上执行,再通过机器人把结果回复到飞书。这条路径负责一问一答的双向消息,也是后面从创建应用到完成首次对话的配置主线。
定时推送:把自动化结果主动送到飞书
定时推送使用一条独立的补充路径:
WorkBuddy 自动化 → 本地发送脚本 → 飞书群自定义机器人 → 手机收到结果
WorkBuddy 自动化按设定运行后,由电脑上的本地发送脚本调用飞书群自定义机器人的 Webhook,将结果发到测试群。Webhook 是群机器人接收推送的专用地址。这条路径只负责把自动化结果送到飞书,不是即时对话机器人接通后自动附带的能力。
Codex:通过独立桥接接收和回复消息
Codex 使用第三条进阶路径:
飞书负责发起,本地桥接负责判断,Codex 只执行被允许的任务
独立飞书应用 → 本地受控桥接 → codex exec → 飞书回复
独立飞书应用接收消息后,本地受控桥接先检查发送者、任务范围和运行权限,再把允许执行的任务交给 codex exec,最后将结果回复到飞书。WorkBuddy 在设置中提供“飞书集成”配置,Codex 不能直接沿用这套接入方式,因此需要单独的飞书应用和本地桥接程序。
这三条路径不能合并成同一个入口。即时对话依赖企业自建应用机器人接收消息,定时推送依赖群自定义机器人向群里发送结果,两者的消息方向和用途不同。WorkBuddy 与 Codex 也不能共享同一个飞书应用:同一应用连接多个长连接客户端时,飞书会随机分发事件,并不会把每条消息同时发送给所有客户端。混用后,一条原本发给 WorkBuddy 的任务可能被 Codex 桥接收到,反之亦然。
开始前准备好这些条件
你需要一个能够进入飞书开放平台的飞书账号,以及一台已经安装并能运行 WorkBuddy 的电脑。账号应当具备创建企业自建应用、配置权限和发布应用的权限;如果所在企业要求管理员审批,发布时还需要等待管理员通过。
任务执行期间,电脑必须保持开机、联网,并避免进入深度睡眠。WorkBuddy 退出只会中断 WorkBuddy 即时对话和自动化任务。电脑睡眠或网络中断会影响所有依赖本机运行的链路。Codex 桥接尚待搭建,未来运行时也需要电脑保持唤醒和联网。
先划清远程执行的安全边界
应用密钥(App Secret)、Webhook 地址等敏感信息只保存在自己的设备上,例如写入不对外分享的私有配置文件;不要把它们放进公开文档、聊天记录或截图。后续首次连通时,先发送只要求返回固定文字的低风险任务,不要一开始就让 AI 读取、修改或删除文件。
只允许远程任务访问明确指定的目录,并只授予完成任务所需的操作权限。只有的确 需要写入文件时才开放写入权限,不把整个电脑或含有私人资料的目录交给远程任务;Codex 桥接也应检查发送者,并为任务设置受控工作目录、最小权限和超时限制。
✓ 完成检查
继续下一步之前,请确认:你能区分 WorkBuddy 即时对话、定时推送和 Codex 桥接三条路径;WorkBuddy 与 Codex 将分别使用独立的飞书应用;飞书账号具备相应的创建与发布权限;电脑可以在测试期间保持开机联网;所有凭证都不会泄露,远程操作也只会在明确允许的目录内进行。
2. 在飞书开放平台创建 WorkBuddy 应用
这一部分先创建一个企业自建应用,并为它添加机器人能力。完成后,你会得到一个可以继续配置的飞书应用;权限、事件订阅和 WorkBuddy 接入将在后面的步骤中处理。事件订阅是飞书把收到的新消息通知给应用的机制。
1. 从开发者后台打开创建入口
从飞书开发者后台进入企业自建应用创建入口
打开飞书开放平台并进入开发者后台。在首页找到“创建企业自建应用”,单击进入创建表单。
如果首页显示了多个已有应用,先确认自己仍在开发者后台的应用列表页,再找创建入口,不要进入其他应用的详情页修改设置。
2. 填写应用信息并创建
填写应用名称、描述与图标后创建应用
在创建表单中填写应用名称和描述,再按页面提供的方式选择或上传图标。名称应能让你在飞书中辨认出它的用途,例如“远程任务助手”;描述可以写成“接收飞书任务并交给 WorkBuddy 处理”。应用名称、描述和图标都可能在应用页面中显示。填写名称和描述时,不要使用真实姓名、手机号、公司内部项目名等个人或业务敏感信息;选择或上传图标时,也不要使用含有个人头像、内部标识等可识别敏感内容的图片。
确认信息无误后,单击表单中的创建按钮。系统跳转到新应用的详情页,并显示刚才填写的应用信息,就说明企业自建应用已经创建成功。
3. 添加机器人能力
机器人能力启用后才能继续配置消息权限与事件
在应用详情页打开“应用能力”,再进入“添加应用能力”。找到“机器人”卡片,单击“+添加”,按页面提示完成机器人能力的添加。
完成后,“应用能力”页面中应当能看到机器人,并可以继续进入它的配置页面。这里先确认机器人能力已经添加,不要提前配置权限、事件或其他扩展能力。
✓ 完成检查
你目前应当位于新应用的详情页,页面显示正确的应用名称、描述和图标;“应用能力”中可以看到已经添加或启用的机器人。满足这两个条件后,再继续配置消息权限。
3. 只开需要的权限,找到并保护应用凭证
机器人已经添加,但还不能直接收发消息。接下来先开通机器人单聊所需的两项核心权限;群聊、文档、日历等功能对应的权限暂不增加,的确 需要时再逐项开通。
1. 开通单聊所需的两项核心权限
在权限管理中按权限名称和代码逐项开通
在应用详情页左侧打开“开发配置”,进入“权限管理”。在权限列表中依次搜索下面两项权限,并开通对应的应用身份权限:
- im:message.p2p_msg:readonly:读取用户发给机器人的单聊消息。
- im:message:send_as_bot:以应用机器人身份发送消息。
选择权限时,同时核对页面显示的权限名称和代码,避免只凭相近名称判断。开通后,返回“权限管理”页面;两项权限都出目前“已开通”列表中,即可确认当前应用已经开通单聊收发消息所需的权限。这里看到的是当前应用的权限配置,之后仍需发布应用版本,权限才会正式生效。
2. 需要在群聊中 @ 机器人时再加权限
如果你只准备与机器人单聊,不需要开通群聊权限。只有的确 要在群聊中通过 @ 机器人发送任务时,再搜索并开通:
- im:message.group_at_msg:readonly:读取群聊中 @ 机器人的消息。
开通后,这项权限同样应出目前“权限管理”的已开通列表中。如果你暂时不用群聊,可以跳过这一步,不要为了“后来可能用到”提前扩大授权范围。
3. 不要把完整权限清单当成简单对话的必选项
WorkBuddy 官方指南中的完整权限清单覆盖了更广的能力,例如读写飞书文档和多维表格,以及查看或操作日历和任务。部分扩展能力还会涉及创建、修改或删除内容。如果只做机器人单聊,不需要把这些权限全部开通。
本教程先从两项核心消息权限开始。只有的确 要让 WorkBuddy 操作文档、多维表格、日历或任务时,才按照实际功能逐类核对用途和风险,缺哪一项再增加哪一项。
4. 找到 App ID 和 App Secret
App ID 与 App Secret 位于凭证与基础信息页面,具体值必须遮挡
在应用详情页左侧打开“基础信息”,进入“凭证与基础信息”。在页面中找到 App ID 和 App Secret:App ID 用于标识这个应用,App Secret 是验证应用身份的应用密钥。
这里只需确认两个字段的位置,等后面配置 WorkBuddy 时再复制需要的内容。不要在教程、截图或聊天中展示任何具体值,也不要把 App Secret 复制到公开文档或提交到代码仓库。
App Secret 只应填入 WorkBuddy 的飞书配置,并保存在自己的设备上。如果它意外出目前公开截图、文档、聊天记录或代码仓库中,应立即回到凭证页面重置密钥;重置后,旧密钥将不能继续使用,相关本地配置也需要改用新密钥。
✓ 完成检查
“权限管理”中已经能看到
im:message.p2p_msg:readonly 和 im:message:send_as_bot;只有需要群聊 @ 时,才额外出现
im:message.group_at_msg:readonly。“凭证与基础信息”页面中可以找到 App ID 和 App Secret,但具体值没有出目前截图、文档、聊天记录或代码仓库中。满足这些条件后,再继续把应用接入 WorkBuddy。
4. 在 WorkBuddy 中注册飞书通道
飞书应用和核心消息权限准备好后,接下来把这个应用注册到 WorkBuddy。本文使用 WorkBuddy 5.3.8 的 WebSocket 长连接,不需要准备公网服务器或回调地址。
1. 打开“飞书集成”配置
从 WorkBuddy 设置进入助理设置与飞书集成
打开 WorkBuddy,进入左侧的“助理”。单击助理标题旁的齿轮,打开“助理设置”,再找到“飞书集成”,进入配置页面。
如果没有看到“飞书集成”,先确认当前打开的是助理设置,而不是某个任务或自动化的设置页面。不同版本的入口位置可能变化,本文实测路径以 WorkBuddy 5.3.8 为准。
2. 选择 WebSocket 长连接
WebSocket 长连接只需填写 App ID 与 App Secret 后注册
在飞书通道的连接方式中,选择“WebSocket 长连接”。它的作用是让本机上的 WorkBuddy 与飞书保持连接,持续接收飞书发来的消息。
页面还会提供 URL 回调方式。它适合已经有公网回调地址、能够接收飞书请求的场景;本文面向没有公网服务器的本机接入,不使用这条路径。
3. 输入应用凭证并注册
注册完成后,飞书通道应显示已连接
WorkBuddy 本地助理顶部显示飞书已连接
从飞书应用的“凭证与基础信息”页面复制 App ID 和 App Secret,分别填入 WorkBuddy 配置页面中对应的输入框。确认没有多复制空格或遗漏字符后,单击“注册”。
输入凭证时不要截图或录屏,也不要把内容临时粘贴到聊天窗口、公开文档或代码仓库。App Secret 是应用密钥,只应在飞书凭证页面与 WorkBuddy 配置之间传递,不要向任何人展示具体值。
提交后,WorkBuddy 会尝试建立连接。界面显示“已连接”,就说明飞书通道已经注册成功;如果仍显示未连接,先重新核对 App ID、App Secret、电脑网络和 WorkBuddy 的运行状态。此处的“已连接”只表明 WorkBuddy 已经建立飞书通道,不代表机器人已经完成全部消息接收配置。
✓ 完成检查
“助理设置 → 飞书集成”中已经选择 WebSocket 长连接,App ID 和 App Secret 已填入对应字段,页面状态显示“已连接”,并且凭证没有出目前截图、录屏、聊天、公开文档或代码仓库中。满足这些条件后,WorkBuddy 端的飞书通道就已注册完成。
5. 订阅接收消息事件并发布应用
WorkBuddy 显示“已连接”后,还需要告知飞书:机器人收到新消息时,应当把这条消息交给已连接的 WorkBuddy。完成事件订阅并发布应用后,前面配置的机器人能力、权限和消息事件才会在飞书中正式生效。
1. 把事件订阅方式设为长连接
事件订阅使用长连接,不需要公网回调地址
回到飞书开放平台,进入刚才创建的应用详情页。在左侧“开发配置”中打开“事件与回调”,进入事件配置区域。
找到“订阅方式”,选择“长连接”,再按页面提示保存。这里的长连接与 WorkBuddy 中选择的 WebSocket 长连接对应:飞书通过这条持续连接把事件发送给本机上的 WorkBuddy,不需要填写公网回调地址。
✓ 完成检查
重新查看事件配置,订阅方式仍显示为“长连接”,说明选择已经保存。如果页面提示尚未建立连接,先确认 WorkBuddy 保持运行,并且“飞书集成”仍显示“已连接”。
2. 添加接收消息事件
事件列表中应出现接收消息事件
在同一页面的事件配置中,单击“添加事件”。搜索并添加下面这项事件:
- im.message.receive_v1:接收消息。它表明机器人收到消息时,飞书会发出一条事件通知,并通过长连接交给已经连接的 WorkBuddy。
添加后,返回事件列表检查事件代码,不要只凭中文名称判断。普通文字对话只需要先订阅接收消息事件;卡片回调用于处理消息卡片中的交互,只有的确 要使用 WorkBuddy 的卡片功能时才按需配置,不是普通文字对话的必选项。
✓ 完成检查
事件列表中已经出现 im.message.receive_v1,并且订阅方式仍为长连接,就说明接收消息事件已经加入当前应用配置。
3. 创建版本并设置可用范围
创建新版本并核对当前发布状态
版本状态显示已发布后,机器人配置才正式生效
打开左侧“应用发布”,进入“版本管理与发布”。单击“创建版本”,在版本配置页设置应用的可用范围,确保当前用于测试的飞书账号包含在范围内。可用范围没有包含当前账号时,即使应用已经发布,也可能无法在飞书中找到机器人。
确认版本信息和可用范围后,提交发布。应用能力、权限或事件发生变化时,都需要通过新版本发布后才能正式生效。
提交后,在版本列表中查看状态。如果企业允许自建应用免审,版本可能直接进入已发布状态;如果企业规则要求管理员审核,页面会显示相应的审核或发布状态,需要等待管理员通过。不要把“已提交”当成“已发布”,也不要假定审批会立即完成。
✓ 完成检查
“版本管理与发布”中可以看到刚创建的版本和当前状态;可用范围包含测试账号;版本最终显示为“已发布”。如果页面仍显示审核中或等待审批,先等待管理员完成审核,状态变为“已发布”后再继续。
6. 在飞书里完成第一轮真实对话
应用已经发布,并且当前账号处于可用范围内,目前可以验证整条即时对话链路。第一次测试只要求返回一段固定文字,不让 AI 读取或改动文件,这样既容易判断是否成功,也能把风险降到最低。
1. 在飞书中打开机器人单聊
在飞书里打开 WorkBuddy 助理并发送低风险测试指令
打开飞书客户端,搜索刚才创建的机器人或应用名称。在搜索结果中找到对应应用,打开机器人单聊。
进入会话后,先确认页面中有消息输入框并且可以发送消息。如果搜不到机器人,先跳到第 9 部分第 1 项排查;如果会话中没有输入框,先看第 9 部分第 2 项。恢复正常并看到输入框后,再回到这里继续。
2. 发送一条低风险测试任务
在机器人单聊中发送:
请只回复 WORKBUDDY_FEISHU_OK,不要读取、创建、修改或删除任何文件
这条任务只要求返回固定文字,不需要访问本机文件。发送后先保留飞书会话,同时转到电脑端查看 WorkBuddy。
3. 确认 WorkBuddy 收到并执行任务
WorkBuddy 收到任务并将结果返回同一轮飞书会话
在电脑端查看 WorkBuddy 中是否出现刚才从飞书发来的任务。看到对应任务后,等待它执行完成;任务内容应与飞书中发送的测试语句一致。
再回到飞书会话,确认机器人回复:
WORKBUDDY_FEISHU_OK
只有 WorkBuddy 收到了任务,并且指定回复重新出目前飞书中,才说明“手机发出任务—电脑接收并执行—飞书收到结果”的双向消息链路已经跑通。
可选:在群聊中 @ 机器人
如果之前已经开通
im:message.group_at_msg:readonly,并发布了包含该权限的新版本,可以把机器人加入群聊,再通过 @ 机器人发送任务。没有群聊需求时继续使用单聊即可,不必为了测试额外扩大权限范围。
完成这次验证后,人在外面也可以用手机飞书交代整理资料、检查内容等受控任务,由保持在线的电脑运行 WorkBuddy,再把结果回复到飞书。实际使用时仍要限定允许访问的目录和操作权限,不要把低风险测试直接扩大成不受限制的远程操作。
✓ 完成检查
机器人单聊中可以正常发送消息;电脑端 WorkBuddy 收到了内容一致的测试任务;飞书中收到了指定回复 WORKBUDDY_FEISHU_OK。这三个结果同时出现,才代表第一轮真实双向对话已经完成。
7. 让定时任务主动把结果送到飞书
前面配置的 WorkBuddy 飞书集成负责双向对话,但接通它并不等于自动获得定时结果推送。WorkBuddy 官方自动化功能的结果推送列表里目前没有飞书选项,因此这里使用一条独立的补充通道:WorkBuddy 自动化完成任务后调用本地脚本,再由飞书群自定义机器人把结果送到手机。不要把这条通道误认为 WorkBuddy 原生支持飞书自动化通知。
下面这套方案已经实际跑通过,但换到你的电脑后,仍要先测试脚本,再新建一个一次性自动化完成现场验证。
1. 创建只供测试的飞书群和自定义机器人
在测试群设置中进入群机器人
Webhook 位于自定义机器人详情页,地址必须完整遮挡
在飞书中创建一个只供自己测试的群聊。先不要邀请同事,也不要使用已有工作群,以免配置过程中的测试消息发给其他人。
打开测试群的设置,进入“群机器人”,选择添加机器人,再添加“自定义机器人”。完成创建后,飞书会提供一个 Webhook。Webhook 是群机器人接收推送的专用地址;脚本把文字发送到这个地址后,机器人就会把内容发到当前群聊。
复制 Webhook 后,不要把它粘贴到截图、录屏、聊天、公开文档或代码仓库中。后面只把它保存到本机的私有配置文件;Webhook 一旦泄露,其他人可能利用它向群里发送消息。
2. 查出账号短名称并确认 Node.js 可用
打开 macOS 的“终端”,先运行:
whoami
终端返回的文字就是当前 macOS 账号短名称,不是你的姓名,也不是 Apple 账户名。记下这个结果;后面示例路径中的 yourname 都要替换成它。
接着运行:
node --version
node 是运行本地发送脚本的程序。这份脚本需要 Node.js 20 或更高版本。运行 node –version 后,版本号开头的主版本数字应当不小于 20,例如 v20、v22 或 v24。如果出现 command not found,或者主版本低于 20,请从 [Node.js 官方下载页](
https://nodejs.org/en/download) 安装 macOS 的长期支持版(LTS);安装完成后关闭并重新打开终端,再运行一次 node –version,确认主版本已经达到 20 或更高。
3. 用终端创建专用目录和空文件
“绝对路径”是从 /Users/你的账号短名称/ 开始写出的完整位置。下面假设专用项目放在
/Users/yourname/WorkBuddy-Feishu-Push。先把命令中的 yourname 全部替换成 whoami 返回的结果,再逐行运行:
mkdir -p "/Users/yourname/WorkBuddy-Feishu-Push/.work/private"
mkdir -p "/Users/yourname/WorkBuddy-Feishu-Push/.work/result"
mkdir -p "/Users/yourname/WorkBuddy-Feishu-Push/scripts"
touch "/Users/yourname/WorkBuddy-Feishu-Push/.work/private/feishu-webhook.env"
touch "/Users/yourname/WorkBuddy-Feishu-Push/.work/result/test-message.txt"
touch "/Users/yourname/WorkBuddy-Feishu-Push/scripts/feishu_push.mjs"
chmod 700 "/Users/yourname/WorkBuddy-Feishu-Push/.work/private"
chmod 600 "/Users/yourname/WorkBuddy-Feishu-Push/.work/private/feishu-webhook.env"
其中,chmod 700 把私有目录限制为只有当前 macOS 账号可以进入和读写,chmod 600 把 Webhook 配置文件限制为只有当前账号可以读写。
这些命令会直接创建以点号开头的隐藏目录和文件,不需要在访达中手动寻找。最终结构应当是:
/Users/yourname/WorkBuddy-Feishu-Push/
├── .work/
│ ├── private/
│ │ └── feishu-webhook.env
│ └── result/
│ └── test-message.txt
└── scripts/
└── feishu_push.mjs
Git 是保存项目版本记录的工具。如果你从未主动配置或使用 Git,直接跳过本段。只有已经用 Git 管理这个目录时,才需要在项目根目录的 .gitignore 文件中单独加入一行 .work/private/,防止私有配置被提交。可以先运行下面两条命令创建并打开该文件,再用文本编辑器加入这一行:
touch "/Users/yourname/WorkBuddy-Feishu-Push/.gitignore"
open -e "/Users/yourname/WorkBuddy-Feishu-Push/.gitignore"
4. 用文本编辑器保存私有 Webhook
不要用带有真实 Webhook 的 echo 命令写文件,否则地址可能留在终端历史中。运行下面的命令,用 macOS 文本编辑器打开已经创建好的私有配置文件:
open -e "/Users/yourname/WorkBuddy-Feishu-Push/.work/private/feishu-webhook.env"
在文件中只填写下面一行:
FEISHU_WEBHOOK_URL=<你的 Webhook>
把 <你的 Webhook> 整体替换为刚才复制的真实地址,等号两边不要添加空格,然后保存并关闭文件。编辑时不要截图或录屏,也不要把真实地址复制到终端命令、WorkBuddy 任务说明或其他公开位置。文件已经预先以 .env 结尾,保存后确认名称没有变成 feishu-webhook.env.txt。
脚本会固定读取这份私有文件。后来运行时,不需要每次在终端重新填写 Webhook。
5. 保存本地发送脚本
运行下面的命令,用文本编辑器打开脚本文件:
open -e "/Users/yourname/WorkBuddy-Feishu-Push/scripts/feishu_push.mjs"
把下面的代码完整粘贴进去,保存并关闭文件。它负责读取私有配置和指定的结果文件,按飞书群机器人要求发送文字,并在成功后输出“飞书推送成功”。代码中不包含真实 Webhook。
#!/usr/bin/env node
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const projectDir = path.dirname(scriptDir);
const envPath = path.join(projectDir, ".work", "private", "feishu-webhook.env");
function parseArgs(argv) {
const result = { text: "", file: "" };
for (let index = 0; index < argv.length; index += 1) {
if (argv[index] === "--text") result.text = argv[index + 1] ?? "";
if (argv[index] === "--file") result.file = argv[index + 1] ?? "";
}
return result;
}
function parseEnv(source) {
const values = {};
for (const line of source.split(/
?
/)) {
const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
if (match) values[match[1]] = match[2];
}
return values;
}
const args = parseArgs(process.argv.slice(2));
const env = parseEnv(await readFile(envPath, "utf8"));
const webhook = env.FEISHU_WEBHOOK_URL;
if (!webhook) throw new Error("未找到 FEISHU_WEBHOOK_URL");
let text = args.text;
if (args.file) text = await readFile(path.resolve(args.file), "utf8");
if (!text.trim()) throw new Error("请使用 --text 或 --file 提供推送内容");
if (!text.includes("[WorkBuddy]")) text = `[WorkBuddy] ${text}`;
async function sendOnce() {
const response = await fetch(webhook, {
method: "POST",
headers: { "content-type": "application/json; charset=utf-8" },
body: JSON.stringify({ msg_type: "text", content: { text } }),
signal: AbortSignal.timeout(15_000),
});
const payload = await response.json();
if (!response.ok || payload.code !== 0) {
throw new Error(`飞书返回错误:${payload.msg ?? response.status}`);
}
}
let sent = false;
let lastError;
for (let attempt = 1; attempt <= 3; attempt += 1) {
try {
await sendOnce();
console.log(`飞书推送成功(第 ${attempt} 次尝试)`);
sent = true;
break;
} catch (error) {
lastError = error;
let reason = `其他错误:${error.message}`;
if (error?.name === "TimeoutError") reason = "请求超时(15 秒)";
else if (error instanceof TypeError) reason = `网络错误:${error.message}`;
else if (error?.message?.startsWith("飞书返回错误")) reason = error.message;
console.error(`第 ${attempt} 次发送失败:${reason}`);
}
}
if (!sent) {
throw new Error(`飞书推送失败:总共尝试 3 次。最后错误:${lastError?.message ?? "未知错误"}`);
}
脚本的每次网络请求最多等待 15 秒,总共最多尝试 3 次。每次失败都会在输出中记录“请求超时”“网络错误”或“飞书返回错误”;三次都失败后,脚本会停止并保留最后的错误。脚本通过自己的位置找到同一项目中的
.work/private/feishu-webhook.env,所以不要改变 scripts、.work/private 和配置文件之间的层级。保存后确认脚本文件名仍是 feishu_push.mjs,末尾没有多出 .txt。
6. 使用绝对路径单独测试脚本
先单独测试本地脚本是否能把消息送到飞书
先运行下面的命令,在测试结果文件中写入一条不含敏感信息的固定文字:
printf '%s
' 'WORKBUDDY_AUTOMATION_WEBHOOK_OK' > "/Users/yourname/WorkBuddy-Feishu-Push/.work/result/test-message.txt"
再使用脚本和结果文件的绝对路径运行:
node "/Users/yourname/WorkBuddy-Feishu-Push/scripts/feishu_push.mjs" --file "/Users/yourname/WorkBuddy-Feishu-Push/.work/result/test-message.txt"
两条命令中的 yourname 都要替换为 whoami 返回的结果。终端输出“飞书推送成功”后,再打开测试群;群里应当出现以 [WorkBuddy] 开头的测试消息。只有终端成功输出和飞书实际消息同时出现,才说明私有配置、脚本和 Webhook 已经连通。
7. 创建一次性 WorkBuddy 自动化
WorkBuddy 自动化页面提供定时任务与运行记录入口
逐项填写名称、工作空间、提示词、周期与执行时间
保持 WorkBuddy 运行,在左侧进入“自动化”,单击右上角的新建入口。任务名称可以填写“飞书定时推送测试”。在当前界面名为“工作空间”或“工作目录”的字段中,选择前面建立的
/Users/yourname/WorkBuddy-Feishu-Push 专用项目目录。执行方式选择一次性,并把执行时间设在几分钟后,方便现场观察。
把下面的模板填入任务说明。粘贴前,将其中三处 yourname 换成 whoami 返回的账号短名称;这里的替换只用于保证脚本路径和结果文件路径正确。工作空间或工作目录需要在 WorkBuddy 界面中单独选择。
请严格执行以下任务:
1. 生成一条少于 200 字、不含任何敏感信息的测试提醒。
2. 只把这条提醒保存到:
/Users/yourname/WorkBuddy-Feishu-Push/.work/result/automation-result.txt
3. 保存成功后运行:
node "/Users/yourname/WorkBuddy-Feishu-Push/scripts/feishu_push.mjs" --file "/Users/yourname/WorkBuddy-Feishu-Push/.work/result/automation-result.txt"
4. 看到“飞书推送成功”后结束任务。
权限边界:
- 任务只允许生成并写入结果文件:
/Users/yourname/WorkBuddy-Feishu-Push/.work/result/automation-result.txt
- 任务只允许运行上面指定的 `node` 发送命令,不运行其他脚本或命令。
- 发送脚本会在内部读取 `.work/private/feishu-webhook.env`;任务本身不得打开、输出、复制或改写这个文件及其中的 Webhook。
- 除生成并写入结果文件、运行指定发送脚本外,不读取、创建、修改或删除其他文件。
- 发送脚本内部总共最多尝试 3 次;如果最终失败,立即停止,不要再次运行脚本,并在运行记录中保留超时、网络或飞书返回的错误。
不要把真实 Webhook 加进任务名称、任务说明或提示词。保存自动化后,不要退出 WorkBuddy,也不要让电脑断网。
关闭显示器和电脑进入睡眠不是一回事:显示器熄灭时,电脑仍可能保持运行;电脑睡眠后,本地自动化就无法继续执行。在测试完成前,保持笔记本打开并接通电源。你可以在“系统设置”的“电池”或“节能”页面中,临时启用显示器关闭时仍防止电脑自动睡眠的选项;不同 macOS 版本的文字可能略有差异。也可以另开一个终端窗口运行:
caffeinate
让这个终端窗口保持打开,Mac 就会临时保持唤醒。验证结束后在该窗口按 Control-C 停止,不要合上笔记本电脑。
8. 核对三项成功信号
自动化、关键词校验和本地发送脚本均已连通
飞书群收到实际每日提醒,定时推送闭环完成
到达设定时间后,在 WorkBuddy 的“运行记录”中找到这次一次性任务,确认任务显示完成,并查看脚本执行输出。随后打开飞书测试群,核对实际消息。
WorkBuddy 的完成记录证明一次性任务已经执行;脚本输出“飞书推送成功”说明发送成功;飞书群出现以 [WorkBuddy] 开头的消息,说明结果已经通过补充通道送达。三项同时满足,才算这条补充推送通道已经配置成功并通过验证。
✓ 完成检查
独立测试时,终端输出“飞书推送成功”,测试群收到固定消息;一次性自动化运行后,WorkBuddy 中出现完成记录和“飞书推送成功”的脚本输出;飞书测试群收到以 [WorkBuddy] 开头的自动化结果。另外,运行下面的命令核对本机访问权限:
ls -ld "/Users/yourname/WorkBuddy-Feishu-Push/.work/private" "/Users/yourname/WorkBuddy-Feishu-Push/.work/private/feishu-webhook.env"
私有目录权限应以 drwx—— 开头,Webhook 配置文件权限应以 -rw——- 开头,表明只有当前 macOS 账号可以访问。上述结果全部出现后,定时任务的补充推送通道才算真正跑通。
8. Codex 为什么需要另一条接入路径
前面使用的是 WorkBuddy 设置中的图形化飞书集成:填入应用凭证、选择长连接,就能让 WorkBuddy 接收飞书消息。长连接是一种让电脑持续等待并接收飞书消息的连接方式。Codex 的接法不同,它需要一个独立飞书应用,再由电脑上的本地受控桥接连接飞书与 codex exec。
这里的“桥接”可以理解为一个守门并转交任务的本地程序。它先接收飞书消息,确认消息来自允许的发送者、任务只涉及指定工作目录,再把符合条件的任务交给 Codex;Codex 执行结束后,桥接程序读取结果并回复到飞书。
完整路径是:
独立飞书应用 → 本地受控桥接 → codex exec → 飞书回复
WorkBuddy 与 Codex 不要共用飞书应用
WorkBuddy 和 Codex 都需要接收飞书发来的消息,但不能把两者连接到同一个飞书应用。同一应用同时连着两个接收程序时,飞书会把一条事件,也就是一条新消息通知,随机交给其中一个程序。它不会广播这条事件,也就是不会让两个程序同时收到。
如果共用应用,原本要交给 WorkBuddy 的任务可能被 Codex 桥接收到,反过来也一样。分别创建应用,才能让两条消息路径各自接收自己的任务,也便于分别设置允许发送消息的飞书账号名单、应用权限和可用范围。
本地桥接先把权限收紧
桥接程序不能收到什么就执行什么。至少要在调用 codex exec 前完成这些限制:
- 只接受白名单中的飞书发送者,不处理陌生账号发来的任务。
- 只允许 Codex 在指定的工作目录内执行任务,不把整个电脑交给远程消息控制。
- 为 Codex 设置沙箱。沙箱是一组执行限制,用来约束任务可以访问和改动的内容;只做验证时使用 read-only,限制 Codex 任务写入文件。
- 设置任务超时和回复长度上限。任务超过时间就停止并返回清楚的失败缘由;结果过长时先截断或整理,再回复飞书。
这些限制应由桥接程序统一执行,不能只依赖飞书消息中的文字提醒。
先在本机验证 codex exec
在连接飞书之前,先确认本机能够以只读方式调用 Codex。下面是本机实际验证使用的命令,其中“允许的工作目录”需要替换为你明确授权的绝对路径:
/Applications/ChatGPT.app/Contents/Resources/codex exec
--skip-git-repo-check
--sandbox read-only
--cd "允许的工作目录"
--output-last-message "/tmp/codex-last-message.txt"
"只回复 CODEX_BRIDGE_OK,不要读取、创建、修改或删除任何文件。"
–sandbox read-only 限制 Codex 任务本身,禁止它在允许的工作目录中写入文件;测试提示词又进一步要求任务不要读取或改动任何文件。–output-last-message 则由 Codex 命令行程序(CLI)在任务结束后,把最终回复写入命令中明确指定的临时结果文件。这个结果导出动作不会让 Codex 任务获得写入工作目录的权限。
首次实测没有加入 –skip-git-repo-check,由于所选目录不是 Git 仓库,命令因此停止。确认目录安全后加入这个参数,表明跳过 Git 仓库检查;它不会撤销只读沙箱。再次执行后,最终得到:
CODEX_BRIDGE_OK
这次运行中,实时连接失败后先进行了重试,随后改用普通网络请求继续,具体表现为 WebSocket 失败后回退到 HTTPS 并完成请求。桥接程序需要给这种重试留出时间,总超时不能设得过短;如果最终失败,应把“超时”“网络连接失败”或 Codex 返回的错误清楚回复给用户,不能让飞书会话一直没有结果。
这一步验证了什么
看到 CODEX_BRIDGE_OK,只能证明这次本机验证中,codex exec 可以在指定目录和只读限制下运行。它不代表飞书应用、本地桥接和 Codex 已经完成端到端连接。即使已经建好了专门给 Codex 使用的独立飞书应用,只要还没有完成端到端验证,就不能说它“已经接通”。
本文在这里讲清独立接入路径和本机安全验证,不展开第二套飞书后台配置。真正上线时,桥接程序还要接收飞书消息并核对发送者,避免同一条飞书消息多次触发同一项任务,为任务设置超时并限制回复长度,最后把结果回复到飞书。
✓ 完成检查
你已经能说清“独立飞书应用接收消息—本地受控桥接检查任务—codex exec 执行—结果回复飞书”的路径,并理解它不能与 WorkBuddy 共用同一个飞书应用;本机只读命令执行后得到 CODEX_BRIDGE_OK。完成检查只确认路径理解和本机命令可用,不表明飞书端已经接通。
9. 按现象排查,确认已经完成的部分与仍待搭建的部分
如果某一步没有出现对应的成功信号,不要从头反复重配。先按你看到的现象找到下面对应的一项,再按其中的步骤检查。排查时不要展示应用密钥(App Secret),也不要展示群自定义机器人接收推送所用的专用地址(Webhook);不要截图、粘贴或公开这两类敏感信息。
1. 飞书里搜不到机器人
- 可能缘由: 应用还没有发布、仍在等待管理员审批、当前账号不在可用范围内,或者机器人能力没有添加成功。
- 处理方法: 回到飞书开放平台,先看“版本管理与发布”中的状态,再核对可用范围是否包含当前账号,最后到“应用能力”中确认机器人已经添加。
- 成功信号: 应用状态显示“已发布”,当前账号位于可用范围内,并且可以在飞书客户端搜索到机器人。
2. 机器人会话中没有输入框
- 可能缘由: 应用尚未处于可用状态、当前账号不在可用范围内,或者打开了名称相近的其他机器人。
- 处理方法: 核对应用发布状态和可用范围,再对照开放平台中的应用名称与图标,重新打开正确的机器人会话。
- 成功信号: 正确的机器人单聊页面出现消息输入框,并且可以发送文字。
3. WorkBuddy 显示“未连接”
- 可能缘由: App ID 或 App Secret 填写错误,飞书的连接方式未设为 WebSocket 长连接,电脑网络中断,或者 WorkBuddy 已退出。WebSocket 长连接是让飞书把新消息实时送到 WorkBuddy 的连接方式。
- 处理方法: 打开“助理设置 → 飞书集成”,确认连接方式为 WebSocket 长连接;从飞书“凭证与基础信息”页面重新核对凭证,并确认电脑联网、WorkBuddy 保持运行。核对时不要截图或展示 App Secret。
- 成功信号: WorkBuddy 的“飞书集成”状态重新显示“已连接”。
4. 飞书能发消息,但 WorkBuddy 没收到
- 可能缘由: “接收消息”事件 im.message.receive_v1 没有加入事件列表,订阅方式没有保存为长连接,或者消息已经到达飞书,但对应的事件通知没有送到 WorkBuddy 当前保持的长连接。
- 处理方法: 回到应用的“事件与回调”,确认订阅方式仍为长连接,事件列表中存在 im.message.receive_v1。随后到“运营监控 → 日志检索”查看刚才的测试消息:
– 如果日志中没有这条消息对应的事件,回到“事件与回调”重新检查事件订阅和长连接方式,再确认包含这些配置的应用版本已经发布、当前账号位于可用范围内。完成后发送一条新的测试消息。 – 如果日志中已有事件,但 WorkBuddy 没有出现任务,打开“助理设置 → 飞书集成”检查长连接状态,重新核对 App ID 和 App Secret,并重新注册连接。状态恢复为“已连接”后,再发送一条新的测试消息。
- 成功信号: 飞书日志中可以找到新测试消息的事件记录,并且 WorkBuddy 收到了内容一致的任务。
5. WorkBuddy 已收到并执行,但飞书没有回复
- 可能缘由: WorkBuddy 中的任务尚未真正完成,机器人缺少 im:message:send_as_bot 发送权限,包含该权限的应用版本尚未发布,或者回复消息时发生错误。
- 处理方法: 先在 WorkBuddy 的任务详情中确认任务已经执行完成,并生成了预期回复。再到飞书开放平台的“权限管理”确认 im:message:send_as_bot 出目前“已开通”列表中;到“版本管理与发布”确认包含该权限的最新版本已经发布;最后到“运营监控 → 日志检索”查看机器人发送或回复消息时是否留下错误记录。如果刚补开权限,创建并发布新版本后,再发送一条新的低风险测试消息。
- 成功信号: WorkBuddy 中的新测试任务显示完成,飞书机器人单聊收到指定回复 WORKBUDDY_FEISHU_OK。
6. 一次性自动化没有执行
- 可能缘由: 电脑睡眠或断网、WorkBuddy 已退出、执行时间设置错误、“工作空间”选错,或者任务说明中的绝对路径不正确。
- 处理方法: 确认电脑保持唤醒并联网,WorkBuddy 持续运行;重新核对一次性执行时间。WorkBuddy 5.3.8 中应检查“工作空间”字段,部分版本可能显示为“工作目录”,两者指的是同一处设置;这里应选择第 7 部分建立的、存放发送脚本的 /Users/yourname/WorkBuddy-Feishu-Push 专用目录。最后核对任务说明中脚本和结果文件的绝对路径。
- 成功信号: 到达设定时间后,“运行记录”中出现对应任务,并显示已经开始或执行完成。
7. WorkBuddy 显示成功,但飞书群没有消息
- 可能缘由: Node.js 版本低于 20,或者终端出现 fetch is not defined;本地发送脚本没有真正发送成功;.work/private/feishu-webhook.env 中缺少 FEISHU_WEBHOOK_URL 或地址已经失效;群自定义机器人被停用;或者发送时网络中断。
- 处理方法: 先运行 node –version。如果主版本低于 20,或者脚本报错 fetch is not defined,按第 7 部分的说明升级到 Node.js 20 或更高版本。然后回到第 7 部分“使用绝对路径单独测试脚本”,重新运行 scripts/feishu_push.mjs。命令中的 –file 后面填写测试结果文件的绝对路径,例如:
“bash node “/Users/yourname/WorkBuddy-Feishu-Push/scripts/feishu_push.mjs” –file “/Users/yourname/WorkBuddy-Feishu-Push/.work/result/test-message.txt” “
把 yourname 替换为 whoami 返回的账号短名称。随后依次检查私有配置文件名是否正确、文件中是否使用变量名 FEISHU_WEBHOOK_URL、群自定义机器人是否仍启用,以及电脑网络是否正常。真实 Webhook 只能在本机私有文件中核对,绝不能粘贴到聊天、截图、公开文档或代码仓库。
- 成功信号: 独立脚本测试输出“飞书推送成功”,测试群收到以 [WorkBuddy] 开头的消息;再次运行自动化后,群里也收到对应结果。
8. 本机 codex exec 验证失败
- 可能缘由: 命令中的工作目录不存在或当前账号无权访问,非 Git 目录缺少 –skip-git-repo-check,只读模式与测试任务的写入要求冲突,或者网络连接仍在重试。
- 处理方法: 回到第 8 部分“先在本机验证 codex exec”,重新运行只回复 CODEX_BRIDGE_OK 的完整只读验证命令,并按看到的情况逐项处理:
– –cd 应指向一个的确 存在、由你本人选择并有权访问的测试目录。 – Git 仓库是使用 Git 保存版本记录的项目目录。如果命令提示当前目录不是 Git 仓库,确认目录安全后加入 –skip-git-repo-check。 – –sandbox read-only 是只读限制。如果测试任务要求创建或修改文件,应撤销写入要求,而不是把只读验证改成不受限制的执行。 – 实时连接失败后,程序可能重试并改用普通网络请求继续,因此要给本机命令留出重试时间。最终仍失败时,以终端显示的具体错误为准。
- 成功信号: 本机命令在设定时间内返回 CODEX_BRIDGE_OK。这个结果只证明本机 Codex 调用链可用,不代表飞书端桥接已经完成。
9. 未来接通后,飞书桥接出现超时
- 适用时机: 这一项不是当前已经接通环境的故障排查。只有独立飞书应用和本地受控桥接完成端到端搭建后,才检查桥接是否能按时回复。
- 需要预先实现: 桥接程序应为实时连接重试和改用普通网络请求留出时间,同时设置明确的总超时。任务成功时把结果回复飞书;超过总时限或调用失败时,把“超时”“网络连接失败”或 Codex 返回的错误清楚回复飞书。
- 届时成功信号: 飞书提交的每项任务都能在时限内收到执行结果或明确错误,不会一直停留在等待状态。这个信号仍待未来桥接完成后验证。
10. App Secret 或 Webhook 已经泄露
- 可能缘由: 敏感值出目前截图、录屏、聊天、公开文档、代码仓库或终端历史中。
- 处理方法: 立即停止使用泄露的值。App Secret 泄露时,到飞书应用的凭证页面重置应用密钥,再更新 WorkBuddy 中的本机配置;Webhook 泄露时,到对应群自定义机器人的设置中重置地址,再更新 .work/private/feishu-webhook.env。不要继续用旧值测试。
- 成功信号: 本机配置已经改用新值,WorkBuddy 恢复“已连接”或独立脚本重新发送成功,旧值不再出目前任何正在使用的配置中。
✓ 确认已经完成的部分与仍待搭建的部分
WorkBuddy 双向对话和定时推送补充通道已经具备可观察的闭环信号;Codex 目前只完成本机调用验证,飞书端桥接仍待搭建。
- WorkBuddy 双向对话: 飞书机器人单聊可以发送测试任务,电脑端 WorkBuddy 收到同一任务,飞书收到指定回复 WORKBUDDY_FEISHU_OK。
- 定时推送补充通道: 独立脚本测试成功;一次性自动化的运行记录显示完成并输出“飞书推送成功”;飞书测试群收到以 [WorkBuddy] 开头的结果。这条通道由 WorkBuddy 自动化、本地脚本和群自定义机器人组成,不是即时对话集成自带的能力。
- Codex 本机验证: 已验证的系统信号只有本机只读 codex exec 返回 CODEX_BRIDGE_OK,它证明本机 Codex 调用链可用。“独立飞书应用—本地受控桥接—Codex—飞书回复”是计划中的接入路径,本节没有完成飞书端到端验证。
完成上述已验证部分的检查后,可以继续把已经验证的一次性提醒改为周期提醒,或在指定工作目录内安排资料整理和内容检查,并继续使用最小读写权限。未来搭建独立桥接时,必须同时配置允许向桥接提交任务的飞书账号白名单和执行日志,用来追踪每次任务的来源与结果。
以上。既然看到这里了,如果觉得不错,随手点个赞、关注、转发三连吧,这对我有特别大的协助!
谢谢你看我的文章。我们下次再见。
版权声明:本文为 @David Roto 原创内容,未经授权,不得转载、摘编或以其他方式使用。