Skip to content
    ↑↓ select↵ openesc close
    moyuanhua

    Feishu

    opencode-feishu-plugin·v0.2.30·Notifications

    Feishu/Lark plugin for OpenCode V2 — single-user topics as sessions, in-card permission approvals, streaming cards. Minimal permissions, no public endpoint.

    GitHub stars

    4

    +1 in 30 days

    Monthly installs

    5,931

    640 in 7 days

    Composite score

    51.0

    Multi-signal model

    Last commit

    5 days ago

    2026-10-16

    Install and configure

    opencode.json

    Writes to this project's opencode.json — applies to this repository only.

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["opencode-feishu-plugin@0.2.30"]
    }

    OpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.

    English | 简体中文

    把 OpenCode 接进飞书:一个飞书话题 = 一个 OpenCode 会话,权限审批直接在飞书卡片上点按钮。

    • 只支持 OpenCode V2(@opencode/plugin,Plugin.define),不依赖任何 V1 包。
    • 纯长连接(WebSocket)收发事件与卡片回调:不监听端口、不需要公网地址。

    效果预览

    飞书话题内的完整会话:工具调用、权限审批卡与强制停止

    亮点

    说明
    🔐 最小权限 只要 2 个 scope,不申请任何群权限,机器人物理上收不到群消息
    💬 话题 = 会话 一个飞书话题对应一个 OpenCode 会话;主聊天流只做管理,互不串台
    🚀 一键建会话 /new 一张表单(目录 + 模型 + 权限档位)一次填完,提交即建会话并自动开话题
    ✅ 卡片审批 权限请求变飞书卡片:允许一次 / 始终允许 / 本会话内允许 / 拒绝,自签 token 防伪防重放
    📊 实时可见 「思考中」回执 → 工具调用实时上卡 → 文本流式更新,页脚显示当前模型
    ⏹ 可控可停 每张回复卡带「强制停止」;看门狗自动中断卡死会话;忙时新消息默认插队(可配置为排队),/steer /now 随时可用
    📎 图片 / 文件 飞书里的图片 / 文件自动下载并挂进会话,支持视觉 / 文件的模型直接看图、读文件;图片+文字一起发也会提取内联图片(可多张)
    🧭 AI 会话管理(主聊天流) 主聊天流发任务文本或建会话/管理类命令(/new /sessions /use /resume 等),都先交给 AI 承接意图、找好工作目录;目录拿不准时直接在对话里追问,确认后一键建会话并开始处理
    🚫 无端口 全程长连接,服务器无需开放任何入站端口

    一、飞书后台配置(约 3 分钟)

    1. 打开 飞书开放平台 → 创建企业自建应用。

    2. 添加应用能力 → 机器人。

    3. 权限管理 → API 权限:先开这两个必开 scope:

      • im:message.p2p_msg:readonly —— 读取用户发给机器人的单聊消息
      • im:message:send_as_bot —— 以应用身份发消息(也用于更新卡片)

      接收图片 / 文件需要再加开一个(不需要该功能可跳过):

      • im:message:readonly —— 获取消息中的资源文件(图片 / 文件下载的必要条件)

      ⚠️ 未开通 im:message:readonly 时:图片/文件不会被下载,消息照常送达 AI,但只带占位文本("…下载失败:…")。开通后要重新创建版本并发布才生效。

    4. 事件与回调 → 事件配置:订阅方式选**「使用长连接接收事件」**(不要选 Webhook),添加事件 im.message.receive_v1。

    5. 事件与回调 → 回调配置:订阅方式同样选长连接,添加回调 card.action.trigger(零权限要求)。

    6. 版本管理与发布:可用范围 = 仅本人,创建版本并发布。⚠️ 不发布就是开发态,长连接连不上,机器人不会有任何反应。

    7. 记下 App ID(cli_…)与 App Secret。

    为什么不申请群权限? 本插件是"一个人的遥控台"。不申请群权限,机器人物理上收不到群消息,单人边界由平台 scope 层保证,而不是只靠代码判断。

    二、安装

    1. 安装插件

    # 方式 A:CLI(推荐)
    opencode plugin add opencode-feishu-plugin
    
    // 方式 B:手写配置(追加到已有 plugins 数组,别覆盖整个文件)
    { "plugins": ["opencode-feishu-plugin"] }
    

    插件入口是自包含的 dist/index.js(已打包飞书 SDK),运行时无需手动 npm install。

    2. 写配置

    新建 ~/.config/opencode/plugins/feishu.json(configDir = OPENCODE_CONFIG_DIR 或 ~/.config/opencode):

    install -m 600 /dev/null ~/.config/opencode/plugins/feishu.json
    cat > ~/.config/opencode/plugins/feishu.json <<'JSON'
    {
      "appId": "cli_xxxxxxxx",
      "appSecret": "xxxxxxxx",
      "logFile": true
    }
    JSON
    
    • 凭证优先级:plugins[].options > feishu.json > 环境变量。也可在值里用 {env:NAME} / ${NAME} 占位符从环境变量取值。
    • logFile: true 建议开启:服务模式下 stderr 会被丢弃,开着才有日志可查。

    3. 生效与验证

    opencode reload
    tail -f ~/.config/opencode/plugins/feishu.log   # 应看到「飞书长连接已启动(WSClient)」
    

    然后在飞书里给机器人发一条消息。第一次发消息的人会被自动绑定为 owner,机器人回复卡片即安装成功;之后其他人会被静默忽略。

    升级插件或改用全局插件目录加载后,需要 opencode service restart 才会重新 import。

    三、快速上手

    主聊天流(管理台)

    主聊天流只做管理,普通文本不会进入任何会话。建会话 / 管理类命令(下表)与普通文本一样,先交给 AI 承接意图,再向下推进(AI 拿不准时会直接在对话里追问你):

    命令 作用
    /new [标题] 发建会话表单,提交即建会话并自动开话题(与 /form 等价)
    /sessions(/ls) 全部会话列表(含本机所有 opencode 会话),可翻页、进话题、新建
    /resume [序号] 对最近(或列表第 N 个)会话发恢复卡,回复该卡即续聊
    /current、/stop 查看当前会话 / 中断当前任务
    /steer <文本>、/now 立即插队发消息 / 把排队消息改为立即执行
    /dir、/model、/perm 为建会话表单预填工作目录 / 模型 / 权限档位
    /cancel、/help 放弃未提交的表单 / 命令列表

    建会话表单卡:目录、模型、权限一次填完 会话列表卡片:翻页、进入/再开、新建

    AI 会话管理(主聊天流)

    主聊天流发普通文本或建会话 / 管理类命令(/new /form /dir /model /perm /sessions /use /resume),AI 会判断意图并直接处理:

    • 建会话(目录优先):AI 先把工作目录定下来,再就地给出预填表单——确认或微调后点「✅ 创建会话」即可,自动开话题开工:

      你:帮我修一下 zlib 的下载 bug,用高风险审批
      → 📝 建会话表单(目录 `/Users/code/zlib` ✓ 匹配历史目录;权限「高风险审批」)
         [✅ 创建会话]
      
      你:股票研究
      → 📝 建会话表单(目录 `/Users/code/stock-research` ➕ AI 新建,不存在时会在创建时自动创建)
         [✅ 创建会话]
      

      目录决策顺序:① 你明确给的路径 → ② 语义匹配现成目录(AI 会先看允许根目录的一级子目录,再看最近使用 / 历史会话目录)→ ③ 都不匹配则按主题在允许根目录下新建(<允许根目录>/<英文短横线主题>)→ ④ 兜底允许根目录。表单永远带目录,不会出现空目录。

    • 目录拿不准 → 对话追问:当 AI 无法确定用哪个目录(表述含糊 / 既可能用现成也可能要新建)时,它不擅自替你选,而是发一条纯文本追问(列出候选或询问是否允许新建);你直接回复即可,AI 接着把流程走完(下一条消息即使是 /路径 也当作回答):

      你:帮我搞个股票项目
      → 你想用哪个目录?回复目录名,或回复「新建 stock-research」
      
      你:新建 stock-research
      → 📝 建会话表单(目录 `/Users/code/stock-research` ➕ AI 新建)
         [✅ 创建会话]
      
    • 列会话 / 进入会话:说「我有哪些会话」→ 会话列表卡;说「继续上次那个」//resume → 直接进话题;

    • 闲聊 / 其它:照旧回管理台提示卡。

    防误伤:AI 新造的路径必须落在允许范围(allowedRoots)内、模型必须命中可选列表;你明确指定但越界的路径不会静默替换——表单不预填并给出警示,由你修改。最终一定经过表单确认。quickNew: false 可整体关闭(回到纯命令矩阵)。

    话题内(干活)

    一个话题 = 一个会话,发普通文本就是给 AI 下指令。

    命令 作用
    /model 切换本会话模型(只影响后续回复)
    /perm 修改本会话权限档位
    /cd <路径> 迁移本会话工作目录
    /steer <文本>、/now 插队 / 立即执行排队消息
    /current、/stop、/help 同主聊天流,作用于本话题会话

    权限档位

    档位 含义
    🔒 只读 只看不改(禁止 edit / shell)
    ✏️ 可编辑 改文件免审批,跑命令要问
    ⚠️ 高风险审批 放行常规操作,仅 shell / 编辑 / 越目录逐次审批(自包含,不继承目录配置)
    🔓 完全信任 什么都不问

    权限请求会变成审批卡:✅ 允许一次 / 🔓 始终允许 / ✅ 本会话内允许该工具 / ❌ 拒绝。操作后卡片自动撤回(超出飞书撤回时限才降级为结果卡),不残留影响查看;换档(/perm)会清除本会话「本会话内允许」授权。

    表单提问(question 工具)

    agent 反问时表单会变成飞书卡片:可自由输入的字段会直接在卡里渲染输入框,填好点「✅ 提交」即可;也能点选项按钮,或直接在话题里发文字作答(无需先点按钮)。作答后卡片自动撤回。纯选项题直接回序号 / 字母即可;若发的是其它内容,会当作普通消息交给 AI。

    四、配置项(常用)

    <configDir>/plugins/feishu.json(或 OpenCode plugins[].options),支持 {env:NAME} / ${NAME} 展开。

    字段 类型 默认 说明
    appId string — 飞书 App ID(必填,缺失则禁用插件)
    appSecret string — 飞书 App Secret(必填,永不写入日志)
    domain feishu|lark feishu 飞书 / Lark 国际版
    allowUsers string[] [] open_id 白名单;空 = 仅 owner
    permissionGate off|notify|gate|lockdown gate 全局审批门档位
    allowTools string[] ["read","glob","grep","webfetch"] 免审批白名单,支持 prefix*
    denyTools string[] [] 强制拒绝(优先于白名单)
    allowedRoots string[] [用户家目录] 允许的工作目录根;越界 / 系统目录拒绝(允许根之下的系统目录除外,如 root 用户的 /root)
    defaultModel string 无 建会话表单的默认模型,"providerID/modelID"(也接受 {providerID,id} 对象)。留空 = 用 opencode 自己的默认模型;都没有才回退「最近用过 → 列表首个」。无论排在第几位,它都会被并入下拉并置顶预选
    stream boolean true 流式回填回复
    threadRouting boolean true 话题路由总开关
    logLevel debug|info|warn|error info 日志级别
    logFile string | boolean — true = 写 <configDir>/plugins/feishu.log,服务模式建议开启
    approvalTtlMs number 600000 审批 token / 卡片有效期
    staleExecutionMs number 300000 看门狗阈值(0–60 分钟;0 = 关闭看门狗;待答表单 / 未决审批期间不判卡死)
    quickNew boolean true 主聊天流「AI 会话管理」:AI 承接普通文本与建会话/管理类命令,判意图 + 找目录(拿不准时对话追问);false = 回到纯命令矩阵
    busyDelivery steer|queue steer 忙时新消息投递方式:steer = 立即插队打断当前步骤;queue = 原生排队(长命令场景更温和)
    messageBatchMs number 1500 消息缓冲窗口:同一会话该窗口内连发的消息合并成一次 prompt / 一张回执卡(发图片/文件被拆成多条、连发多张图时不再刷屏);0 = 关闭
    staleMessageMs number 120000 入站消息时效窗口:飞书长连接首次未确认会重推消息,晚到超过该窗口的视为过期、直接忽略(避免 AI 回复早已过时的指令);0 = 关闭
    gatewayLocation string — 只在该 location(及其子目录)启动网关;留空 = 任意 location 生效
    gatewayLeader boolean true 跨 location / 进程网关收敛:用 state 目录做机器级 leader 选举,只让一个实例起飞书长连接(事件是服务端全局的)——修「表单失效 / 审批 not found」类问题

    完整配置(含 cardMaxTables、topicStatus*、resumeSummary*、keepalive*、gatewayMatchGraceMs 等进阶项)见 docs/advanced.md。

    五、故障排查

    现象 处理
    发消息没反应 ① 应用是否已发布、可用范围是否勾了你;② 订阅是否选了长连接(不是 Webhook);③ 是否开通 im:message.p2p_msg:readonly
    图片 / 文件收不到(只显示占位 / “下载失败”) 开通 im:message:readonly(权限管理 → API 权限,搜“获取消息中的资源文件”)→ 重新创建版本并发布;具体失败原因看 feishu.log 的「附件下载失败」
    改了 feishu.json 不生效 确认路径,然后 opencode reload
    插件完全没被加载 npm 方式确认包名在 plugins 数组;目录方式确认 plugins/<名>/index.js 存在
    审批卡收不到 该会话不是从飞书发起的(无映射),插件按设计不接管
    点按钮提示凭证无效 token 过期(默认 10 分钟)或点击者不在白名单
    会话像卡死、只排队 看门狗默认 5 分钟后自动中断(待答表单 / 未决审批期间不中断;staleExecutionMs: 0 可关闭);也可点「⏹ 强制停止」或发 /stop
    空闲约 1 小时后失联 opencode 每 60 分钟会回收空闲 location(无法从插件侧阻止);插件在被回收后秒级自动重建(v0.2.20,含单 location 场景),见 docs/advanced.md
    插件频繁重载 / 长连接反复重连 logFile 落在了 opencode 配置目录内(写日志会被当成配置变更 → 每次写日志都触发重载)。改用默认值,或把日志移到配置目录之外(默认 ~/.local/state/opencode/feishu-plugin.log)
    看不到插件日志 服务模式下 stderr 被丢弃,设 logFile: true(默认写 ~/.local/state/opencode/feishu-plugin.log)
    多个长连接 / 重复回复 默认已由 gatewayLeader(机器级 leader 选举)自动收敛为一个实例;gatewayLocation 可进一步限定 location。若关掉 gatewayLeader 后多 location / 进程并跑,回调可能落到不持有请求的实例 → 卡片会显示「❌ 审批未生效」并可重试
    审批卡显示「❌ 审批未生效」/ 提示「审批未生效」 permission.reply 没能送达:多为多实例(请求在另一个进程)或请求已过期。点卡片上的「🔁 重试」;仍失败就回到该会话重新触发一次操作,或只保留一个 opencode 实例

    六、已知限制

    • 图片 / 文件消息会下载到本地并挂进会话(需开 im:message:readonly);图片+文字一起发(飞书富文本 post)也会提取内联图片(可多张);音频 / 视频 / 表情包仍只给文字占位。
    • 只接管从飞书发起的会话的审批;本地 TUI 会话不受影响。
    • 建会话最终都经表单卡确认(AI 预填目录 / 模型 / 权限):/new、/form 或直接描述任务都会走到这张卡。
    • 表单为 JSON 2.0,老客户端对 select_static 有最低版本要求(≥ V3.7.0)。
    • 话题首条消息可能不带 thread_id:插件会靠 root_id 兜底路由;新话题敲命令落到主聊天流时,直接进话题发消息即可。
    • 生态里另有 opencode-feishu(V1 插件),与本插件不兼容、不共用代码。

    七、下一步规划(Roadmap)

    按真实使用反馈迭代,当前规划:

    • 接收图片 / 文件:已支持——自动下载到会话工作目录下的 .opencode/temp/opencode-feishu-plugin/(内置 .gitignore,不污染 git status;可用 attachmentsDir 覆盖;单附件默认 ≤20MB)。
    • 忙时新消息默认插队:已支持——忙碌时新消息默认直接插队(打断当前步骤优先执行);busyDelivery: "queue" 可切回原生排队(长命令场景更温和)。

    八、更新日志

    版本 亮点
    v0.2.30 修 pid 复用导致的网关空窗:docker restart / 容器重建后进程仍是 PID 1,leader 锁里记的 pid: 1 于是永远「存活」,只按 pid 存活 + TTL 判断会让所有新实例都让位 → 飞书长连接不会自动回来(0.2.26 只覆盖了「pid 已死」,没覆盖「pid 被复用」)。现在 owner 文件额外记下进程启动时刻,同 pid 但启动时刻不同即判定为陈旧锁、立即接管
    v0.2.29 修 建会话表单选不到想要的模型:旧实现的默认模型只看「最近用过 → 模型列表第 0 个」,完全不读 opencode.json 的 model,且候选被硬上限截断(SETUP_FORM_MAX_MODELS=15,opencode-go/deepseek-v4.1-flash 恰好排在第 16 位)→ 表单里根本没有它、默认还落到别的模型上。现在默认模型按 feishu.json 的 defaultModel → opencode 自己的默认模型(ctx.model.default)→ 最近/首个 解析,并且该模型始终并入下拉并置顶预选,排在第几位都不影响
    v0.2.28 修复三个真实环境 bug:① HTTP 审批答复恒失败——兜底请求体的键名写成了 reply,而服务端要求 decision(2.0.16 / 2.0.26 均如此),于是每次点「允许」都 400 Missing key at ['decision']、卡片显示「❌ 审批未生效」;② 非 service 部署(opencode serve 直跑,Docker 常见)下主聊天流 AI 恒失效——服务发现只读 state 目录 service.json,直跑不生成该文件 → 列不出会话 → 静默回退「管理台」卡;现在回退读配置目录 service.json(hostname+port+password,0.0.0.0 等通配地址自动改写为 127.0.0.1),并且列不出会话时 fail-loud 发可见提示不再静默;③ 以 root 运行时任何目录都建不了会话——allowedRoots 默认为家目录(/root)却又命中系统目录黑名单,现「允许根之下的系统目录」不再拦截(允许根之外的系统目录仍拒绝)
    v0.2.27 新增消息时效窗口 staleMessageMs(默认 120s,0=关):飞书长连接首次未确认会重推消息,晚到/跨重启补发的按创建时间判定为过期后直接忽略,避免 AI 回复早已过时的指令
    v0.2.26 修复 leader 锁导致的重启空窗:opencode service restart / 崩溃后旧锁文件仍在(owner 进程已死但 mtime 未过期),新进程会在 TTL 内一直让位,叠加 20 分钟看门狗 → 空窗约 17 分钟。现按 owner 进程存活(pid) 判定,已死立即接管
    v0.2.25 修复 图片+文字一起发时飞书以 post(富文本)投递、此前只按 image/file 解析而丢图的问题:现在从 post 内联元素提取图片/文件(支持多张)并挂进会话;同消息多附件按 fileKey 后缀命名,不再互相覆盖
    v0.2.24 跨 location / 进程网关收敛:新增机器级 leader 选举(state 目录原子目录锁),只让一个实例起飞书长连接 + 事件订阅——修「表单失效 / 审批被静默吞掉」的多实例根因(gatewayLeader,默认开);⚠️ 高风险审批档位改为自包含(放行常规操作,仅 shell / 编辑 / 越目录逐次审批,不再继承目录配置)
    v0.2.23 消息缓冲:同一会话内窗口(messageBatchMs,默认 1500ms)连发的多条消息合并成一次 prompt / 一张回执卡——发图片/文件被拆成多条、连发多张图不再各出一张卡刷屏;回执卡仍在首条消息时立即发出,prompt 在最后一条消息静默该窗口后合并提交。messageBatchMs: 0 关闭
    v0.2.22 修复 issue #4「审批放行被静默吞掉」:permission.reply 失败不再假成功/误撤回卡片,改为显示红色「❌ 审批未生效」卡 + 🔁 重试(重签 token);reply 失败自动走本机 HTTP 兜底(跨实例也能投递到持有请求的 location);回调落到未跟踪实例时发可见失败提示;点击回执改为「已提交,正在处理…」
    v0.2.21 表单/提问卡片:可自由输入的字段直接在卡里渲染输入框 + 「✅ 提交」,不再要求用户到话题里发文字作答;仍兼容「点选项按钮 / 话题内直接发文字」
    v0.2.20 位置驱逐治理:实测确认驱逐无法从插件侧阻止(硬编码 60 分钟 TTL + 续期通道无效)→ 改为被驱逐后秒级复活(dispose 时安排 +1s/+5s/+20s 探针),空窗从 20 分钟压到 ~10 秒
    v0.2.19 主聊天流全面 AI 化:建会话 / 管理类命令(/new /form /dir /model /perm /sessions /use /resume)不再直接执行,改为和普通文本一样先交给 AI 承接意图再向下推进;AI 拿不准工作目录时不再擅自选定,改为在对话里直接追问(列候选或询问是否允许新建),下一条消息即当作回答;移除机器人自定义菜单(输入框上方的 /new /sessions 快捷按钮);审批卡操作后自动撤回(超飞书撤回时限才降级结果卡)
    v0.2.18 修复日志触发的插件重载风暴:默认日志改为 ~/.local/state/opencode/feishu-plugin.log(opencode 监听整个配置目录,在其中写文件会被当成配置变更 → 每次写日志都重载插件、长连接反复重连);显式把日志放进配置目录会给出告警
    v0.2.17 位置保活重做:探针改用 GET /api/plugin(实测唯一能续期 / 重建 location 的通道);进程级看门狗持独立日志 sink,单 location / headless 被回收后一个心跳间隔内自愈(无需外部 cron)
    v0.2.16 建会话目录优先:AI 先定好工作目录再预填表单,表单永不空目录;目录候选新增「允许根目录的一级子目录」,先复用现成目录,找不到才按主题新建
    v0.2.15 AI 会话管理(主聊天流由 AI 判意图,直接给预填表单 / 会话列表);忙时默认插队(busyDelivery: "queue" 可切回排队);修复 opencode-go 下临时生成必失败
    v0.2.14 一句话建会话;修复流式文本重复 / 跨段重叠
    v0.2.13 修复排队回执卡终态后永久「等待中」;补充执行事件诊断日志
    v0.2.12 修复看门狗误杀(子代理 / 待答 / 未决审批期间不再判卡死);看门狗可关闭
    v0.2.11 机器人自定义菜单(输入框快捷按钮 /new、/sessions);兼容 SDK 拍平的事件形状
    v0.2.10 附件默认落盘到会话工作目录 .opencode/temp/opencode-feishu-plugin/(内置 .gitignore)
    v0.2.9 接收图片 / 文件(自动下载并挂进会话,需 im:message:readonly)
    v0.2.8 事件订阅自动重连(SSE 断流按指数退避重连,不再永久失明);进历史会话自动开话题;摘要截取兜底

    完整历史见 GitHub Releases。

    高级主题与开发

    安全设计、位置保活、卡片守卫、会话恢复、多实例网关选举、完整配置项与开发架构见 docs/advanced.md。

    许可证

    MIT

    Similar plugins