npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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

前置条件

  1. 一个飞书企业自建应用飞书开放平台 → 开发者后台 → 创建企业自建应用)

  2. 在应用配置里:

    • 机器人:启用机器人能力
    • 事件订阅:添加 接收消息 im.message.receive_v1 事件
    • 权限
      • im:messageim:message:send_as_bot(发送消息)—— markdown 通知必需
      • im:message(更新消息)—— 进度心跳原地刷新回执消息需要(同一条权限,部分场景需 im:message:send_as_bot 已具备)
    • 长连接:事件订阅方式选择 使用长连接接收事件(SDK WebSocket 方式,无需公网 URL)
  3. 拿到应用的 App IDApp 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),扩展自动生效:

  1. 收到通知:pi 每次任务结束(agent_settled)自动向飞书发送一条通知,包含项目名、会话 ID、时间与最近一次回复摘要,方便多任务区分:

    ✅ pi 主对话已完成
    项目: my-project
    会话: 01a00b5b
    时间: 2026/8/17 00:17:00
    
    <任务结果摘要>
    
    回复本消息可继续指挥该会话。
  2. 回复指挥:在飞书里直接回复这条通知,输入你要的指令(无需 @ 机器人)。消息会注入到对应的 pi 会话继续执行,执行完成后会再收到新的通知。

    • 群聊回复:只要该群是「通知目标 chatId」或「allowedChatIds 白名单」或「自动识别过的群」,回复都会被接收(requireMention: false 时无需 @ 机器人)。
    • 重启后仍可回注:pi 重启会生成新的 session id,但回复会自动回退到同一项目(cwd 相同)的当前会话继续执行——不再因为旧会话 id 对不上而只回一句"会话已结束"。
  3. 手动通知:在 pi 里执行 /feishu-notify <消息> 可手动发送一条通知到飞书;/feishu-notify 无参数时查看扩展状态(含是否静音、最短时长阈值、是否已自动识别 userId)。

减少日志打扰 / 控制通知频率

  • 日志安静:默认 logLevel: 'normal' 只输出警告和错误,日常任务完成的 [feishu-notify] notification-sent {...} 不再刷屏。想更安静设 'quiet'(只报错),需要排查设 'verbose'(含发送细节)。
  • 长任务才通知:设置 minDurationMs(毫秒),任务从 agent_startagent_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.jsonmessage_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 继续时:

  1. 收到回复 → 先发一条「已收到你的回复,正在处理…」回执
  2. 长任务期间 → 该回执消息会原地刷新「⏳ 仍在处理中,已用时 Xs… 项目「xxx」/ 会话 a1b2c3d4」(默认每 15s 一次,im.v1.message.update),让你知道 bot 还活着、在干活、处理的是哪个项目/会话,不会傻等
  3. 处理完成 → 回执更新为「✅ 处理完成,结果见下一条消息」,随后新发一条 markdown 通知,只含过滤后的最终结果lastAssistantText,对最后一条 assistant 消息做 extractAssistantText,跳过 thinking / toolCall 部分)
  4. 你仍可回复这条最终结果消息继续指挥

为什么不做实时流式:流式需要把 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

License

MIT