pi-feishu-notify
v0.6.0
Published
Bidirectional bridge between pi main conversations and Feishu (Lark): sends a Feishu notification when a pi task finishes, and lets you reply to the notification in Feishu to remotely command that pi session to continue. No lark-cli dependency, built on t
Maintainers
Readme
pi-feishu-notify
Languages: English · 简体中文
pi 主对话 ⇄ 飞书双向桥:pi 任务完成时推送飞书通知,在飞书里回复通知即可远程指挥对应 pi 会话继续执行。
不依赖 lark-cli —— 基于官方 @larksuiteoapi/node-sdk 的 WebSocket 长连接直连飞书,无需安装任何额外 CLI 工具,也无需公网回调。
特性
- ✅ 不依赖 lark-cli:官方 SDK 直连,零额外工具链
- ✅ session 级通知:任务结束(
agent_settled)自动推送飞书通知 - ✅ 回复回注:在飞书里回复通知,指令自动注入对应 pi 会话继续执行
- ✅ 双向桥:pi → 飞书(通知)/ 飞书 → pi(指令)
- ✅ markdown 美化:通知默认以飞书富文本(post)渲染,标题/加粗/代码块/引用清晰美观
- ✅ 只发最终结果:回复回注后同样只发过滤后的最终结果(markdown 通知),thinking/工具调用内容不会推给飞书
- ✅ 进度反馈:长任务期间在回执消息上原地刷新「已用时 Xs + 项目/会话」,不会在飞书侧干等
- ✅ 跨进程去重:多 pi 进程场景下同一消息只处理一次
- ✅ 崩溃自愈:自动清理死进程残留状态
- ✅ 配置灵活:全局 + 项目级覆盖,支持环境变量插值,自动识别 userId/chatId
安装
# 通过 npm 安装(发布到 pi.dev/packages 社区目录)
pi install npm:pi-feishu-notify
# 或通过 git 安装
pi install git:github.com/xiaohuzai/pi-feishu-notify
# 或本地路径
pi install ./path/to/pi-feishu-notify前置条件
一个飞书企业自建应用(飞书开放平台 → 开发者后台 → 创建企业自建应用)
在应用配置里:
- 机器人:启用机器人能力
- 事件订阅:添加
接收消息 im.message.receive_v1事件 - 权限:
im:message、im:message:send_as_bot(发送消息)—— markdown 通知必需im:message(更新消息)—— 进度心跳原地刷新回执消息需要(同一条权限,部分场景需im:message:send_as_bot已具备)
- 长连接:事件订阅方式选择 使用长连接接收事件(SDK WebSocket 方式,无需公网 URL)
拿到应用的
App ID和App Secret,并把机器人加为联系人(私聊)或拉进群聊(群通知)。
注意权限变更生效时机:修改权限后,需到 版本管理 发布一个新版本,权限才会真正生效(很多人改完配置后不生效,是因为漏了这步)。
通知目标
userId(open_id)和chatId(群 chat_id)无需手动获取:配置好并启动后,给机器人发一条消息即可自动识别(见下文「userId / chatId 怎么获取」)。
配置
在 ~/.pi/agent/settings.json(全局)或项目 .pi/settings.json(项目覆盖)中添加:
{
"feishu-notify": {
"enabled": true,
"appId": "${FEISHU_APP_ID}",
"appSecret": "${FEISHU_APP_SECRET}",
"replyEnabled": true, // 是否允许回复回注
"receipt": true, // 转达后回执一条"已收到"
"requireMention": false, // 群聊时是否要求 @ 机器人
"allowedSenderIds": [], // 私聊白名单(open_id 列表),不填则只处理 p2p 单聊
"allowedChatIds": [], // 群聊白名单(chat_id 列表);不填时仍会放行"通知目标 chatId"与"自动识别过的群"里的回复
"includeSummary": true,
"minDurationMs": 0, // 任务最短时长(毫秒),>= 该值才发通知;0/缺省=不限制
"logLevel": "normal", // 'quiet'|'normal'|'verbose':日志详细度,normal 默认不再刷 notification-sent
"messageFormat": "markdown", // 通知/回复格式:'markdown'(默认,飞书富文本)| 'text'(纯文本)
}
}
userId/chatId不是必填:不配置时,扩展会自动识别通知目标——给机器人发一条私聊消息即识别出userId,群里发消息即识别出chatId,并自动用于发送(可用/feishu-notify bind把识别结果固定写入配置)。只有想锁定发送目标时才手动配置。
安全建议:
appSecret使用${ENV_VAR}占位符,通过环境变量注入,避免明文写入配置文件。支持${NAME}和${NAME:-fallback}语法。
userId / chatId 怎么获取?可以自动识别
userId(open_id)和 chatId(群 chat_id)不需要手动查。只要你在飞书里给机器人发一条消息,扩展就会自动记住:
- 私聊:你给机器人发一条消息 → 自动识别出你的
userId(open_id) - 群聊:把机器人拉进群,在群里 @ 机器人发一条消息 → 自动识别出该群的
chatId
识别到后:
- 自动回退生效:如果 settings 里没配
userId,通知会自动发到刚识别出来的私聊用户(首次会自动提示可用/feishu-notify bind持久化)。 /feishu-notify whoami:查看当前已识别的userId/chatId(一般不需要,排查或想手动锁定目标时用)。/feishu-notify bind:把识别到的值自动写入项目.pi/settings.json(只补写缺失字段,不覆盖已有配置),重启或/reload后固定生效。- 重启也能记住:识别结果会存到
~/.pi/agent/feishu-notify-discovered.json。下次启动时若 settings 仍未配置userId但已识别过,pi 里会弹一条一次性提示,提醒你用/feishu-notify bind一键写入。
提示:私聊自动回退是最顺滑的入门方式——配好
appId/appSecret、机器人加好友后发条消息,再把 task 交给 pi,通知就会发回来。
使用
配置完成后启动 pi(或 /reload),扩展自动生效:
收到通知:pi 每次任务结束(
agent_settled)自动向飞书发送一条通知,包含项目名、会话 ID、时间与最近一次回复摘要,方便多任务区分:✅ pi 主对话已完成 项目: my-project 会话: 01a00b5b 时间: 2026/8/17 00:17:00 <任务结果摘要> 回复本消息可继续指挥该会话。回复指挥:在飞书里直接回复这条通知,输入你要的指令(无需 @ 机器人)。消息会注入到对应的 pi 会话继续执行,执行完成后会再收到新的通知。
- 群聊回复:只要该群是「通知目标
chatId」或「allowedChatIds白名单」或「自动识别过的群」,回复都会被接收(requireMention: false时无需 @ 机器人)。 - 重启后仍可回注:pi 重启会生成新的 session id,但回复会自动回退到同一项目(cwd 相同)的当前会话继续执行——不再因为旧会话 id 对不上而只回一句"会话已结束"。
- 群聊回复:只要该群是「通知目标
手动通知:在 pi 里执行
/feishu-notify <消息>可手动发送一条通知到飞书;/feishu-notify无参数时查看扩展状态(含是否静音、最短时长阈值、是否已自动识别 userId)。
减少日志打扰 / 控制通知频率
- 日志安静:默认
logLevel: 'normal'只输出警告和错误,日常任务完成的[feishu-notify] notification-sent {...}不再刷屏。想更安静设'quiet'(只报错),需要排查设'verbose'(含发送细节)。 - 长任务才通知:设置
minDurationMs(毫秒),任务从agent_start到agent_settled不足该时长则不发送。例如minDurationMs: 60000表示 1 分钟以内的短任务不打扰。 - 按会话静音:在 pi 里执行
/feishu-notify off(或mute)静音当前会话,任务完成后不再自动发通知;/feishu-notify on(或unmute)恢复。适合某些只想本地、不想推飞书的工作场景。
工作原理
┌────────────┐ agent_settled ┌──────────────┐ SDK WebSocket ┌────────┐
│ pi session │ ─────────────────▶ │ Notification │ 长连接(直连) │ 飞书 │
│ (主对话) │ ◀───────────────── │ Router │ │ App │
└────────────┘ sendUserMessage └──────────────┘ ◀── 回复通知 ── └────────┘
▲ │ │
│ │ └── 按 replyToMessageId 反查目标 session
└────────────────┴── 指令回注- 下行:
agent_settled→ 发送 markdown 富文本通知,拿到message_id,记录到~/.pi/agent/feishu-notify-router.json(message_id → session映射) - 上行:SDK 长连接收到消息 → 若
replyToMessageId命中路由表 → 跨进程去重认领 →pi.sendUserMessage回注指令 - 常驻单例:WebSocket consumer 是进程级单例,跨 session 共享,不随 session 切换断开
- 去重:
~/.pi/agent/feishu-notify-dedup.json记录已处理消息,跨进程互斥
markdown 美化 & 回复行为
通知 markdown 美化(默认)
任务完成通知默认以飞书富文本(post 消息 + md 元素)发送,由飞书原生渲染标题、加粗、列表、代码块、引用:
## ✅ pi 主对话已完成
**项目**:my-app
**会话**:a1b2c3d4
**时间**:2026-08-21 12:00:00
---
(任务摘要,代码块/列表原样保留)
> 回复本消息可继续指挥该会话设为 messageFormat: "text" 可回退为纯文本。
回复回注:只发最终结果 + 进度反馈
当你在飞书回复通知指挥 pi 继续时:
- 收到回复 → 先发一条「已收到你的回复,正在处理…」回执
- 长任务期间 → 该回执消息会原地刷新「⏳ 仍在处理中,已用时 Xs… 项目「xxx」/ 会话 a1b2c3d4」(默认每 15s 一次,
im.v1.message.update),让你知道 bot 还活着、在干活、处理的是哪个项目/会话,不会傻等 - 处理完成 → 回执更新为「✅ 处理完成,结果见下一条消息」,随后新发一条 markdown 通知,只含过滤后的最终结果(
lastAssistantText,对最后一条 assistant 消息做extractAssistantText,跳过 thinking / toolCall 部分) - 你仍可回复这条最终结果消息继续指挥
为什么不做实时流式:流式需要把
text_delta实时逐条推给飞书;部分 provider 会把推理/工具调用也以普通文本增量下发,导致 thinking、工具调用细节跟着流出去。因此默认只发「结算后」的最终结果,与初始任务通知行为一致,保证飞书看到的永远是干净的最终答案。
进度心跳需要更新消息权限:刷新回执用
im.v1.message.update(同im:message权限);若应用未开通该权限,进度刷新会静默失败,不影响回执和最终结果发送。
回复兼容
SDK 会把收到的富文本(post)消息转成纯文本,因此无论你回复的是 markdown 通知还是普通文本消息,回注指令都能正确提取。
文件结构
pi-feishu-notify/
├── extensions/
│ └── feishu-notify.ts # pi 扩展入口(事件钩子 + 命令)
├── src/
│ ├── types.ts # 类型定义
│ ├── config.ts # 配置加载(全局+项目+env 插值+旧配置迁移)
│ ├── feishu.ts # 飞书 SDK 客户端(发送 + 长连接,进程级单例)
│ ├── router.ts # 通知路由 + 跨进程去重
│ ├── sessions.ts # 会话注册表(崩溃自愈)
│ ├── filter.ts # 发送/日志过滤(minDurationMs、logLevel)
│ ├── notify.ts # 通知内容构建(markdown 美化 + 回复文本提取)
│ ├── settings.ts # 自动识别 ID 持久化(/feishu-notify bind)
│ ├── discovery.ts # 识别结果跨进程持久化(重启提示用)
│ └── state.ts # 状态文件原子读写 + 目录锁
└── test/ # 单元测试(vitest)卸载
# npm 安装方式
pi remove npm:pi-feishu-notify
# git 安装方式
pi remove git:github.com/xiaohuzai/pi-feishu-notify