Skip to content

BaronCyrus/feishu-codex-bridge

Repository files navigation

Feishu Codex Bridge

feishu_codex_bridge 是一个基于 lark-cli 的本地常驻守护工具,用来把飞书 P2P 私聊消息桥接到同一台机器上的长驻 Codex CLI 会话。

它的重点不是再造一层飞书协议封装,而是直接复用 lark-cli 已有的事件订阅、身份认证、消息回复与技能调用能力,把飞书入口接到本地 Codex 代理上。桥接层本身很薄,但背后的 Codex 会话可以直接调用本机已安装、已授权的 lark-* skills,而不只是回一条 IM 消息。

Codex 固定运行在一个可 attach 的 mux session 中:macOS / Linux 使用 tmux,Windows 原生使用 psmux。你可以从任意本机终端随时 attach 进去看真实界面、直接接手当前任务,也可以 detach 后让它继续后台工作。

日常使用时,你可以全程只在飞书里给 Codex 发需求、看进度、收结果,不需要盯着本地终端。这个 mux 会话更像一个随时可进入的“驾驶舱”: 平时可以不打开,需要排障、观察真实 TUI、或者临时手动接管时再进去。

核心特性

  • 基于 lark-cli 能力栈:消息订阅、身份、回消息、技能调用都建立在现成的 lark-cli / lark-* skills 之上,不额外发明一套飞书接入方式
  • Codex 可直接操作全部飞书 skills:只要当前机器上的 Codex 环境里已经安装并可访问对应的 lark-* skills,它就能直接调用,例如 lark-imlark-doclark-sheetslark-baselark-calendarlark-task
  • 全程飞书对话驱动:启动桥接后,日常工作流可以只在飞书里完成,需求、追问、进度同步、最终回复都走飞书线程
  • 任意终端可接手同一会话:Codex 固定跑在命名的 mux session 里,你可以从任意本机终端 attach 到同一个会话继续操作
  • 会话可后台持续运行:你可以随时 detach,桥接服务和 Codex 会继续在后台工作,不要求终端窗口一直挂在前台

它只做三件事:

  1. lark-cli event +subscribe --event-types im.message.receive_v1 --compact --quiet --as bot 订阅飞书消息
  2. 只接收指定 allowed_sender_open_idp2p 消息
  3. 把消息送进本地 Codex CLI 会话,并要求 Codex 优先通过 lark-im 回消息;如有需要,再继续调用本机可用的其他飞书 skills 去完成文档、表格、日历、任务、知识库等操作

桥接层不会把 stdout/stderr 自动发回飞书;终端输出只用于本地日志和诊断。

目录内容

  • bridge.py: 守护进程主脚本
  • bridge.example.toml: 配置模板
  • start_bridge.py: 跨平台公共启动器
  • start_bridge.command: macOS 双击启动脚本
  • start_bridge.cmd: Windows 双击启动脚本
  • bin/lark-cli.ps1: Windows PowerShell 下优先命中的 lark-cli 包装入口
  • bin/lark-cli.cmd: Windows cmd.exe 下的 lark-cli 包装入口
  • bin/lark_cli_wrapper.py: lark-cli 兼容包装层,负责参数规范化和桥接增强
  • start_bridge_powershell.cmd: Windows PowerShell 双击启动脚本
  • feishu-codex-bridge.plist.example: macOS launchd 示例

前置条件

本机需要满足:

  • Python 3.11+ 可用(双击启动脚本会优先尝试较新的 python3.x
  • macOS / Linux:tmux 可用
  • Windows:psmux 可用
  • lark-cli 已安装并完成 lark-cli config init
  • Codex 运行环境可以访问你希望它使用的 lark-* skills
  • 飞书开放平台已开启长连接事件订阅
  • 已添加 im.message.receive_v1
  • 已开通 im:message:receive_as_bot
  • Codex CLI 可用

如果你打算让 Codex 在处理过程中读取 lark-imlark-shared 技能文件,推荐把 command 配成:

codex --no-alt-screen --dangerously-bypass-approvals-and-sandbox

如果你不用完全放开模式,至少要确保 Codex 能读取技能目录和 cwd 外的必要路径。

配置

先复制模板:

cp Tools/feishu_codex_bridge/bridge.example.toml Tools/feishu_codex_bridge/bridge.toml

然后填写真实值:

cwd = "/ABSOLUTE/PATH/TO/YOUR/WORKSPACE"
command = "codex --no-alt-screen --dangerously-bypass-approvals-and-sandbox"
allowed_sender_open_id = "ou_xxxxxxxxxxxxxxxxx"
message_scope = "p2p"
skill_path = "/Users/zhangyoufei/.agents/skills/lark-im/SKILL.md"
ack_text = "已收到,正在转给本地 Codex 处理,后续回复会直接回到这条消息下。"
session_idle_minutes = 30
log_path = "/ABSOLUTE/PATH/TO/feishu-codex-bridge.log"
tmux_session_name = "feishu-codex-bridge"
bridge_lark_app_id = "cli_xxxxxxxxxxxxx"         # 可选,只影响桥接层监听/回执
bridge_lark_app_secret = "xxxxxxxxxxxxx"         # 可选,只影响桥接层监听/回执
bridge_lark_brand = "feishu"                     # 可选,默认 feishu
bridge_lark_cli_profile = "my-listener-bot"      # 可选;不填会自动生成桥接专用 profile

字段说明:

  • cwd: Codex 工作目录
  • command: 原样执行的 Codex 启动命令
  • allowed_sender_open_id: 只允许这个飞书用户触发
  • message_scope: v1 固定为 p2p
  • skill_path: 注入给 Codex 的默认回复技能文件路径,通常填 lark-im,用于明确“先通过飞书把进度和结果回出去”
  • ack_text: 服务收到有效消息后立即回的确认文案
  • session_idle_minutes: 会话空闲多久后自动重建
  • log_path: 本地日志文件
  • tmux_session_name: 长驻 mux 会话名。字段名保持不变;macOS / Linux 下对应 tmux 会话名,Windows 下对应 psmux 会话名
  • bridge_lark_app_id: 可选。监听机器人 App ID,只给桥接守护进程外层使用
  • bridge_lark_app_secret: 可选。监听机器人 App Secret,只给桥接守护进程外层使用
  • bridge_lark_brand: 可选。feishulark,默认 feishu
  • bridge_lark_cli_profile: 可选。桥接层外部监听机器人使用的 profile 名;如果同时填写了 bridge_lark_app_id / bridge_lark_app_secret,桥接启动时会自动创建或更新这个 profile;如果不填,会按 tmux_session_name 自动生成一个桥接专用 profile

补充说明:

  • cwdskill_pathlog_path 都支持相对路径
  • 相对路径会以 bridge.toml 所在目录作为基准来解析
  • 如果你把整个 feishu_codex_bridge 文件夹打包发给别人,别人通常至少需要改这三项:cwdallowed_sender_open_idskill_path
  • skill_path 只是桥接启动时注入给 Codex 的默认沟通入口,不代表 Codex 只能用这一个 skill;只要同一环境里的其他 lark-* skills 可用,Codex 也可以直接调用
  • 如果你只填 bridge_lark_cli_profile,桥接层会直接用现有 profile 执行 lark-cli --profile <profile> event +subscribe ... --as bot
  • 如果你填了 bridge_lark_app_idbridge_lark_app_secret,桥接启动时会先自动执行一次等价于 lark-cli config init --name <profile> --app-id <id> --app-secret-stdin --brand <brand> 的配置写入,然后再用该 profile 监听和回执
  • 上面这套 bridge 专用机器人配置只影响桥接守护进程外层;Codex 会话里之后执行的 lark-cli 仍然走你本地当前默认配置,不会被桥接层改写
  • 如果你不想把凭据写进 bridge.toml,仍然可以继续只填 bridge_lark_cli_profile

运行

命令行手动启动时,请显式使用本机的 Python 3.11+ 解释器;下面以 python3.11 为例,实际也可以换成 python3.12python3.13 等。

先做环境检查:

python3.11 Tools/feishu_codex_bridge/bridge.py --config Tools/feishu_codex_bridge/bridge.toml --check

再以前台方式启动:

python3.11 Tools/feishu_codex_bridge/bridge.py --config Tools/feishu_codex_bridge/bridge.toml

启动后通常会先看到:

Starting Feishu event subscription

然后在订阅进程稳定约 2 秒后,你会看到这条 ready 日志:

Feishu event subscription ready (connection stable, you can now send messages from Feishu)

看到这条后,就表示当前已经过了启动窗口期,可以去飞书发送第一条消息了。

如果后面又看到:

Feishu event subscription is receiving events (first event observed)

说明这条订阅连接已经实际收到了至少一条飞书事件。

如果你希望直接双击启动:

这两个文件都会用相对位置自动找到同目录下的 bridge.pybridge.toml

双击启动时,启动窗口默认只显示桥接服务状态日志,不显示 Codex CLI 的完整对话转录;这样窗口更适合长期挂着观察服务是否在线、订阅是否 ready、是否发生错误。

如果你需要看真实 Codex TUI、完整对话内容、或者临时手动接管,请直接 attach 当前 mux 会话,而不是依赖启动窗口:

启动后如果你想进入真实 Codex 界面,可以从任意本机终端执行:

tmux attach -t feishu-codex-bridge

Windows 原生请改用:

psmux attach -t feishu-codex-bridge

如果你改了 tmux_session_name,把上面的名字换掉即可。

tmux / psmux 里 detach 不会停止桥接服务,也不会中断飞书侧的工作流。

如果你的 Codex 环境本来就装有完整的飞书 skills,这个桥接启动后,等于就是把“飞书私聊”直接变成了这些 skills 的远程入口:你在飞书里提需求,Codex 可以在本机直接调用 lark-cli 和对应 lark-* skills 去查文档、改表格、发消息、查日历、写任务,而结果仍然回到同一条飞书线程里。

如果你只是想关闭当前 mux 会话,但保留桥接服务继续运行:

tmux kill-session -t feishu-codex-bridge

Windows 原生对应命令是:

psmux kill-session -t feishu-codex-bridge

下次再收到普通飞书消息时,桥接服务会自动重建一个新的 mux 会话。

如果桥接层检测到当前 mux 会话已经失活、pane 已死、或者实际只剩一个普通 shell 而不是活的 Codex 进程,也会直接执行同等效果的 kill-session 并重新拉起一个新的会话;Windows 下同样走 psmux

Windows 原生运行

如果你在 Windows 原生环境运行,不需要 WSL。桥接会直接使用 psmux 作为 tmux 的平替。

  • 先安装 psmuxwinget install psmux
  • start_bridge.cmd 启动前会先检查 psmux 是否可用;如果没装,会直接提示安装命令
  • bridge.py --check 在 Windows 上也会检查 psmux,不会再报泛化的 tmux is not available in PATH
  • cwdskill_pathlog_path 请填写 Windows 本机可访问的路径;如果你用的是 Windows 版 pythoncodexlark-cli,就按 Windows 路径来写
  • 桥接会把仓库内的 bin 目录放到当前会话的 PATH 最前面;在 PowerShell 下会优先命中 bin/lark-cli.ps1,在 cmd.exe 下会命中 bin/lark-cli.cmd
  • 这个包装层会先把 --params--data--json 规范化成稳定 JSON,再调用真实 lark-cli,减少 Windows shell 对引号和编码的破坏
  • 包装层还补了几条桥接增强:docs +fetch 遇到 Wiki 里的 BITABLE 会返回 handoff 信息,docs +search 会为 BITABLE 结果补 resolved_obj_tokenbase +record-get 支持 --fieldsbase +record-list 支持 --format object
  • 这些兼容和增强只保证桥接会话里的包装入口生效,不代表你系统里全局安装的上游 lark-cli 原生命令已经具备同样行为

工作方式

收到有效飞书消息后,桥接层会先执行:

lark-cli im +messages-reply --message-id <incoming_message_id> --text "<ack_text>" --as bot

然后把消息包装成一段 envelope 送给当前 Codex 会话。启动时还会先注入一段 bootstrap prompt,明确告诉 Codex

  • 当前运行在飞书桥接模式
  • 用户可见回复不要依赖终端输出
  • 回复时优先使用 lark-im
  • 先读取 lark-shared
  • 对当前消息优先调用 lark-cli im +messages-reply --message-id <id> --text ... --as bot
  • 不要等所有回答完成才一起发,收到消息后先发一条简短进度,再在处理中持续追加回复
  • 如果 Codex 在 10 秒内还没有主动通过飞书回用户,桥接层会自动补一条“仍在处理中”的 watchdog 提示;之后每 30 秒补一条心跳,直到 Codex 真正开始通过飞书回复或任务结束
  • 当前版本只处理订阅通道实时收到的消息;如果服务启动前或订阅建立窗口期有消息漏掉,桥接层不会补捞历史消息

这里的“优先使用 lark-im”是为了保证用户能先收到进度和结果,不是为了限制 Codex 的能力边界。只要你的本地 Codex 会话里可访问其他飞书 skills,它在处理任务时也可以直接继续调用这些 skill。

完整的桥接上下文不会在每条普通消息里重复注入;它只会在一段 Codex 对话的首条普通消息里发送一次。后续普通消息只会补当前 message_id、回复方式提醒和用户原文。只有当用户显式发送 /new 开启新对话,或本地 Codex 会话被重建后,下一条普通消息才会重新附带完整桥接上下文。

桥接层会自动创建并复用配置里的 tmux_session_name;如果启动时发现已经存在一个活的 Codex mux 会话,就会直接接管并继续使用,不会重复创建第二个同名会话。桥接 bootstrap 规则会按 mux 会话维度记录,复用一个已经完成 bootstrap 的会话时不会重复注入;但如果用户显式发送 /new,或者本地 Codex 会话被重建,下一轮仍会重新注入 bridge 规则。

如果会话空闲超时,服务会销毁旧 session 并在下一条消息到来时重建;如果检测到的是失活/异常会话,则会直接强制 kill 当前 session 再拉起新的 Codex。默认使用方式仍然是“人在飞书里发消息,Codex 在后台持续处理”,mux 会话只是这个后台会话的可见入口。

行为边界

v1 固定行为:

  • 只支持 p2p
  • 只允许一个 allowed_sender_open_id
  • 只接收 text / post 两类文本可读消息
  • 图片、文件、卡片等消息会收到“当前仅支持文本消息”
  • Codex 输出只记本地日志,不自动桥接回飞书
  • 桥接日志会尽量压缩 TUI 噪音;完整画面请直接 attach 到当前 mux 会话

控制命令

私聊机器人时,除了普通消息,还支持下面这些桥接层原生命令:

  • /status:立即返回桥接状态,包括当前 mux 会话是否在线、是否正在处理、排队消息数、工作目录以及 attach 命令
  • /new:不重建 mux 会话;而是把精确匹配的 /new 直接送进当前 Codex CLI,触发 Codex 自己开启新对话。之后下一条普通消息会重新附带完整桥接上下文
  • /interrupt:立即向当前 mux 会话中的 Codex 发送中断信号,尝试停止当前这一轮处理,但不重建整个会话

推荐把它们理解成“桥接层保留命令”,而不是交给 Codex 解释的普通对话。

精确匹配规则:

  • 只有消息内容在去掉首尾空白后,完全等于 /status/new/interrupt 之一时,才会触发桥接层命令
  • 例如 /status 现在怎么样/new 请帮我重新开始/interrupt 帮我停一下 都不会命中保留命令
  • 其他以 / 开头但不精确匹配这三个命令的内容,会被当成普通用户消息,照常转发给 Codex

飞书端示例:

/status
/new
/interrupt

说明:

  • /new 会复用当前 mux 会话和当前 Codex 进程,只在会话内向 Codex CLI 发送一条 /new
  • /interrupt 走立即执行路径,不排队;如果当前没有正在执行的任务,会直接回你“当前没有正在执行的任务”
  • 如果 /interrupt 后当前这一轮仍未停止,可以直接 attach 到当前 mux 会话手动接管
  • /status 现在会额外显示两段桥接状态:订阅进程事件通道
  • 事件通道:连接中,正在建立事件通道 表示还没 ready,建议再等一下
  • 事件通道:已就绪,当前可以在飞书发送消息 表示现在可以发送第一条飞书消息
  • 事件通道:已收到事件(最近 X 秒前) 表示订阅已经实际收到过消息事件

推荐使用流程

第一次启动或重启桥接后,推荐按这个顺序操作:

  1. 启动桥接服务
  2. 等待本地终端出现 Feishu event subscription ready (connection stable, you can now send messages from Feishu)
  3. 再去飞书发送第一条普通消息,或者先发 /status
  4. 后续默认直接在飞书里持续对话、补充需求、接收结果,不需要一直打开本地终端
  5. 如果想看真实界面或临时手动接手当前会话,再从任意本机终端执行 attach 命令:macOS / Linux 用 tmux attach -t <tmux_session_name>,Windows 用 psmux attach -t <tmux_session_name>
  6. 如果当前任务卡住,先发 /interrupt;还不行就直接 attach 到当前 mux 会话手动处理

launchd

如果你希望它在 macOS 上常驻,复制并修改:

cp Tools/feishu_codex_bridge/feishu-codex-bridge.plist.example ~/Library/LaunchAgents/com.example.feishu-codex-bridge.plist
launchctl load ~/Library/LaunchAgents/com.example.feishu-codex-bridge.plist

记得把 ProgramArgumentsWorkingDirectoryStandardOutPathStandardErrorPath 改成你自己的绝对路径。

故障排查

  • 启动后完全收不到消息:优先检查飞书开放平台是否开启长连接订阅,以及 im.message.receive_v1
  • 能收消息但回不了消息:检查 bot scope 和群/会话可见范围;messages-reply 需要 bot 身份
  • Codex 无法读取技能:检查 command 的权限模式,确保它能访问 skill_path
  • 日志里看到 Do you trust the contents of this directory?:首次运行时脚本会自动确认
  • 日志里看到 Continue anyway? [y/N]:脚本会自动接受兼容提示,并为子进程设置 TERM=xterm-256color
  • 想看真正的交互界面:按平台执行 attach 命令;macOS / Linux 用 tmux attach -t <tmux_session_name>,Windows 用 psmux attach -t <tmux_session_name>

License

This project is licensed under the MIT License.

About

一个基于lark-cli的飞书桥

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages