kimi-code-feishu
v0.1.1
Published
让 Kimi Code CLI 通过飞书机器人远程通信:派任务、看进度、批权限
Readme
kimi-code-feishu
让 Kimi Code CLI 连上飞书机器人:在任何地方用手机跟你的本机 Kimi Code 持续对话、看实时进度、批准/拒绝权限请求、接管终端会话。
机制
┌─────────────┐ 飞书长连接(WebSocket) ┌───────────────────────────────────┐
│ 手机飞书 │ ◄────────────────────► │ 本桥接服务 │
│ (消息/卡片) │ │ FeishuChannel ⇄ Bridge │
└─────────────┘ │ HookServer(127.0.0.1:17781) │
│ ChatSessionManager(tmux 托管) │
│ WireWatcher(wire.jsonl 转录监听) │
└──────────┬────────────────────────┘
send-keys 注入 / capture-pane 抓屏 │ hooks(审批/进度事件)
┌──────────────────────────────────────────┼───────────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ 0 号会话 │ │ 终端会话(tmux) │ │ 终端会话(pts) │
│ kcf-chat-xxx │ │ kimi-code-feishu │ │ 裸终端里的 kimi │
│ 每聊天一个常驻 │ │ tmux 启动 │ │ TIOCSTI 注入 │
│ 交互式 kimi │ │ send-keys 注入 │ │ wire 转录代替抓屏 │
└───────────────┘ └──────────────────┘ └──────────────────┘核心设计:
- 每聊天一个常驻交互式 kimi 会话(0 号):桥在 tmux 里为每个飞书聊天拉起
kimi(kcf-chat-xxx)。消息用send-keys注入,结果从该会话的wire.jsonl(kimi 磁盘实时转录)聚合回推,防抖判定轮次结束。进程常驻 = 上下文连续,服务端 prefix cache 吃同一会话前缀;交互模式下模型还能调AskUserQuestion反问你(飞书弹答题卡,点选后真实按键作答) - hooks 是审批与监控通道:
PreToolUse→ 飞书审批卡片(点击内联更新,全端同步);PostToolUse/Stop/SessionStart等 → 进度推送;PermissionRequest→ 终端权限等待的兜底通知 - 终端会话可发现可接管:
ps全量扫描发现所有 kimi 进程——tmux 会话用send-keys/capture-pane注入与抓屏;普通终端(pts)用 TIOCSTI 注入(屏幕读不到,用磁盘转录代替) - fail-closed:桥掉线 = 需审批操作默认拒绝;桥启动通知 + systemd 常驻保证在线
- Dashboard 按需开启:用的时候才拉起(新 token、可选 cloudflared 公网隧道、闲置自动关、页面只读)
一、安装
| 依赖 | 说明 |
|---|---|
| Node.js ≥ 18 | 用到全局 fetch、AbortSignal.timeout |
| Kimi Code CLI | 已安装并完成 /login |
| 飞书账号 | 能创建企业自建应用 |
npm install -g kimi-code-feishu # 发布后
# 或从源码安装
npm install && npm run build && npm link安装后得到 kimi-code-feishu 命令。
二、创建飞书应用(约 1 分钟)
kimi-code-feishu onboardonboard 提供两种接入方式,启动后按提示选择:
方式 1:扫码即创(推荐)
终端显示二维码,用飞书手机 App 扫码并在官方确认页点确认即可——飞书服务端自动创建好带机器人能力和消息权限的应用,app_id / app_secret 和你的 open_id(自动加入 allowed_user_ids)会直接写入配置,无需手动创建应用。
原理:飞书官方账号体系的 Device-Flow 应用注册协议(与官方开源 larksuite/cli 相同),全程匿名调用、无需公网回调,配置写入后仍需手动
run启动桥。 扫码创建用的是官方预置模板(PersonalAgent),审批卡片所需的card.action.trigger等配置已包含;如后续需要额外权限(文档、日历等),仍需到开发者后台补配。 应用名默认为官方模板名(如「CLI 助手」):扫码时在手机确认页上可以直接修改;事后改名请到开发者后台「凭证与基础信息」页(https://open.feishu.cn/app/<appId>/baseinfo),需创建新版本并发布才生效。
方式 2:手动输入已有应用的凭证
在 onboard 菜单选 2,粘贴已有的 App ID / App Secret(open_id 可留空,之后私聊机器人发 /id 获取再补填)。适合已经有配置好的应用、或需要自定义权限模板的场景。也可用 kimi-code-feishu init 生成配置模板后手动编辑。
手动创建应用的步骤:
- 飞书开放平台 → 创建企业自建应用(如「Kimi 遥控器」)。
- 添加应用能力 → 机器人。
- 权限管理 开通:
im:message、im:message:send_as_bot、im:message:readonly。 - 事件订阅:
- 接收方式选 使用长连接接收事件(不需要公网 IP 的关键);
- 添加事件
im.message.receive_v1; - 添加回调
card.action.trigger(审批按钮依赖它)。
- 版本管理与发布 → 创建版本并发布。
- 凭证与基础信息 页复制 App ID / App Secret。
三、配置与启动
kimi-code-feishu onboard # 扫码创建应用并自动写入配置(推荐,见上一节)
# 或手动方式:kimi-code-feishu init 生成 ~/.kimi-code-feishu/config.toml 后自行填入凭证
kimi-code-feishu run # 启动桥(保持运行)
# 手动方式还需:手机飞书私聊机器人发 /id → 把返回的 ou_xxx 填入 allowed_user_ids → 重启桥
# 另一个终端:把 hooks 写入 Kimi CLI 配置(自动探测 ~/.kimi 或 ~/.kimi-code,自动备份)
kimi-code-feishu installinstall 写入的 hook 命令形如:
[[hooks]]
event = "PreToolUse"
command = " /usr/bin/node /path/to/dist/hook.js pre_tool_use"
timeout = 180重启正在运行的 kimi 会话后生效;CLI 内
/hooks可确认。hook 用绝对路径直接调用。
后台常驻(推荐 systemd 用户服务,崩溃自动重启、开机自启):
sh deploy/install-service.sh # 渲染并启用 ~/.config/systemd/user/kimi-code-feishu.service
journalctl --user -u kimi-code-feishu -f # 看日志
# 或简单方式:nohup kimi-code-feishu run > ~/.kimi-code-feishu/bridge.log 2>&1 &桥在线很重要:fail-closed 设计下桥掉线 = 需审批操作全部默认拒绝。桥每次启动会往最近活跃的聊天发一条「✅ 桥已上线」(附 Dashboard 地址),出门在外能确认它活着。
四、使用方法
会话模型
每个飞书聊天 = 一个桥托管的常驻交互式 kimi 会话(tmux 里跑 kimi,命名 kcf-chat-xxx,编号 0):
- 直接发消息 = send-keys 注入该会话;上下文连续(进程常驻 + 磁盘转录),服务端 prefix cache 友好
- 结果从会话的 wire.jsonl 转录聚合回推;进度消息滚动更新工具调用
- 模型可以提问(交互模式)——
AskUserQuestion会弹出飞书选项卡片,点选后真实按键作答 - 会话落在包安装路径(可用
/bind改);/new杀掉重开;/stop发 Esc 中断;/i用 Ctrl+S 优先插话
终端模式(/a 序号 绑定任何其他终端会话后):纯文本自动注入那个会话;/0 <文本> 发回 0 号会话;/a free 或 /a 0 切回。终端的 /命令(/model /yolo…)一律 /t /model 注入。注入后的轮次结果也会从磁盘转录聚合回传到飞书(防抖判定轮次结束)。
飞书里(手机/电脑均可)
全部命令
| 输入 | 作用 |
|---|---|
| 任意普通文本 | 发给当前会话(飞书模式=0 号会话;终端模式=绑定的终端会话) |
| /0 <文本> | 发给 0 号会话(飞书本身的常驻会话;终端模式下用它发回) |
| /new | 杀掉 0 号会话,下一条消息全新开始 |
| /stop | 中断当前轮次(发 Esc) |
| /status | 当前状态:模式、任务、目录、会话、待审批数 |
| /bind <目录> | 绑定 0 号会话的工作目录(同时重置会话) |
| /a | 列出所有终端会话(序号/目录/可控性/当前绑定) |
| /a <序号> | 绑定终端会话,进入终端模式(纯文本自动注入) |
| /a free 或 /a 0 | 释放绑定,切回飞书模式 |
| /t <文本> | 注入绑定会话(含终端的 /命令:/t /model、/t /yolo) |
| /i <文本> | 优先插话(Ctrl+S,立即插入运行中的轮次) |
| /s | 查看绑定会话:tmux=实时抓屏;pts=读磁盘转录尾部 |
| /c | 审批池列表(各终端会话在池状态;🔓=已释放审批) |
| /c <序号或路径> | 切换某会话/目录的池状态(进池才弹审批卡/推进度) |
| /c free <序号或路径> | 释放/恢复该目录会话的审批 hook(释放后直接放行,回落终端原生权限) |
| /p | 审批黑白名单(按目录):显示当前模式与名单 |
| /p mode | 切换白名单/黑名单模式(黑名单模式=未命中黑名单都直接放行) |
| /p allow\|deny <规则> | 加规则:Bash 整工具 或 Bash:/rm|git push/ 内容正则(命中黑名单仍弹卡) |
| /p del allow\|deny <序号> | 删规则 |
| /d | = /dashboard:开启实时输出面板(本地链接) |
| /d public | 开启并拉 cloudflared 公网隧道(手机外用) |
| /d off | 关闭面板(页面「关闭 Dashboard」按钮等效) |
| /id | 查看你的 open_id(配白名单用) |
| /help | 帮助 |
远程操控终端会话(tmux / pts 注入)
/a 列出所有活着的 kimi 终端会话(ps 全量扫描 + 进程树匹配),分两级:
- ⌨️可控(tmux):
kimi-code-feishu tmux启动的会话,/tsend-keys 注入、/scapture-pane 抓屏 - ⌨️仅注入(pts)/ 👀仅发现:普通终端里的 kimi。本机已启用
legacy_tiocsti=1+ 免密 sudo 时可注入(TIOCSTI 把按键塞进终端输入队列);不满足时降级为仅发现。注意 pts 终端永远无法抓屏(pts 只写不读,原理限制),/s改读磁盘转录尾部;审批卡走 hook 不受影响
kimi-code-feishu tmux # 在 tmux 里启动 kimi(kcf-* 命名),Ctrl+B D 脱离- 飞书发
/a列出会话(序号/目录/可控性),/a 2绑定进入终端模式(纯文本自动注入);/s看一眼屏幕/转录再决定敲什么 /t注入等价于坐在终端前打字:回答AskUserQuestion提问、对权限提示敲y、下新指令都行;终端的/命令(/model /yolo…)也一律/t /model注入- pts 注入依赖:
/etc/sysctl.d/90-kcf-tiocsti.conf(legacy_tiocsti=1)+ 免密 sudo;其他机器不满足时自动降级,不影响其余功能
工作目录绑定(/bind)
一个聊天 ≈ 一个项目的远程遥控窗口。/bind 决定三件事:
- 会话工作目录:0 号会话以绑定目录为 cwd 拉起,AI 读写文件、跑命令都在这个目录下;不绑则用配置里的默认
work_dir(默认包安装路径)。 - 会话转录位置:kimi 的 wire.jsonl 按工作目录组织(
~/.kimi-code/sessions/wd_<目录>_*),换绑目录即开启另一条会话线,上下文互不串。 - 终端会话路由:终端里手动跑
kimi时产生的审批卡片和进度推送,按会话 cwd 匹配到绑了同目录的聊天。
不同聊天(多个私聊/群)可各绑各的项目,互不影响;/bind 会同时重置该聊天的会话(等同 /new)。
审批卡片
- ✅ 批准:放行这一次;🔁 本会话允许:同会话同类工具自动放行;❌ 拒绝:阻断并把原因反馈给模型
- 超时未点(默认 150s)按
on_timeout处理,默认拒绝 - 不弹卡片的情况:只读工具(
auto_allow_tools)直接放行;命中auto_deny_patterns(如rm -rf /)直接拒绝 - 配置了
dashboard_public_url后,卡片底部附「📊 查看实时输出」链接,点开看清现场再决定
提问卡片(tmux 交互会话)
tmux 会话里模型调 AskUserQuestion 时,hook 直接放行让 TUI 出题,同时飞书弹出选项卡片:
- 点选项 = 桥把对应数字键敲进终端,模型拿到的是真实 UI 作答(不是 hack 回传)
- 多选题可多点后「✔️ 确认选择」;「🚫 拒绝回答」= 敲 Esc
- 自定义答案:用
/t直接打字输入 - 目前支持单题卡片;多题提问或会话不在 tmux 里时回落普通审批/终端作答
监控终端会话
终端里跑 kimi --yolo,审批闸门即完全交给飞书卡片,出门在外也能远程点头。
审批池(/c)
多个终端会话同时跑时卡片会刷屏,审批池控制哪些会话走飞书:
- 按目录(cwd)记池:
/c列出的会话对应目录进池后,它的 PreToolUse 才弹审批卡、进度才转发;池外会话 hook 直接放行,回落终端原生权限流程(你在机器前正常点,飞书完全安静) - 0 号会话(飞书聊天自己的常驻会话)不受池限制,永远弹卡
- 兜底:池外会话在终端进入权限等待时,桥发一条被动通知(
PermissionRequest事件)——不会无声无息卡住,/c加池或/a绑定后/t作答即可;终端弹出AskUserQuestion选择器时同样会收卡片或被动提醒 - 池持久化在
state.json,重启不丢 /c free <序号或路径>彻底释放某目录会话的审批 hook:桥直接放行,完全回落终端原生权限(再执行一次恢复)
审批黑白名单(/p)
进池(或 0 号会话)的会话默认每个操作都弹卡。/p 按目录维护一份持久化名单,减少点卡次数:
- 白名单模式(默认):命中白名单规则直接放行,其余弹卡
- 黑名单模式(
/p mode切换):未命中黑名单的全部直接放行,命中才弹卡——适合信任本机操作、只盯危险命令 - 规则写法:
Bash(整工具)或Bash:/rm|git push/(工具+内容正则,能精确放过ls但拦住rm) - 优先级:全局
auto_deny_patterns(直接拒)>/c free(全放行)>/p名单 > 审批卡;名单只对接管的会话(进池目录或 0 号会话)生效
五、Dashboard(按需开启的 WebUI)
Dashboard 采用按需开启的安全模型——桥启动时不开启,用的时候才临时拉起:
/dashboard # 默认开本地/局域网链接(本机浏览器直接开,方便排查)
/dashboard public # 才拉 cloudflared 公网隧道(手机/外出用)
/dashboard off # 手动关闭(页面上的「关闭 Dashboard」按钮等效)- 页面只读,唯一操作是「关闭 Dashboard」按钮;SSE 断开时显示断连横幅(不会装死空白页)
- 每次开启:新随机 token;
/dashboard public才拉 cloudflared quick tunnel(新随机域名),上次的公网链接全部作废;默认本地模式不产生任何公网暴露 - 双阈值自动关闭:没有打开的页面约 3 分钟关;页面停看(心跳停)10 分钟关——离开后不会一直挂在公网上
- 配置见
[dashboard]节:dashboard_idle_timeout_page/dashboard_idle_timeout_nopage/dashboard_public_url(固定域名才填)/cloudflared_bin等
页面结构(自上而下):
- ⏳ 待你处理:待审批(工具+命令)与待回答提问(原文+选项),标注怎么回(飞书卡片 /
/t) - 🖥 终端会话(实时画面,2s 刷新):tmux 会话内嵌
capture-pane实时屏幕——和tmux attach看到的一样;pts 会话标注「仅注入」 - 🏃 任务:进行中的任务与耗时
- 💬 对话与事件:飞书消息(👤 绿)/ 桥回复(🤖 蓝)/ 任务输出 / 审批进度
- 📡 本地会话转录(紫色):桥监听
~/.kimi-code/sessions/**/wire.jsonl(kimi 实时会话转录),把任何会话(含普通终端 pts)的用户输入、思考、回复、工具调用与结果流式推送——pts 屏幕读不到,但对话全在这里
⚠️ 终端输出可能包含敏感信息,带 token 的链接等同于终端内容本身,请勿转发;cloudflared 隧道全程 HTTPS。
六、安全设计
分层暴露原则:无认证的只给本机,有认证的才暴露。
- hook 服务(17781)只听
127.0.0.1,这是刻意的:hook 端点没有任何认证(无 token/签名/来源校验),回环绑定本身就是它的全部访问控制。它承载每次工具调用的完整细节(命令、路径、会话)并左右审批——绑到局域网等于让同网段任何人都能窥探你的执行、伪造请求轰炸审批卡片、甚至把恶意 kimi 的审批引到你的飞书上。本机进程与你在同一信任域,所以本机不需要认证;要开放网络访问,必须先给 hook 加认证 - Dashboard(17772)可以绑
0.0.0.0或走公网隧道:因为它有 token 认证(无 token 401),暴露面由 token 兜底。但带 token 的链接等同于终端内容本身,请勿转发 - 白名单:仅
allowed_user_ids中的 open_id 能发指令、点卡片;其他人点击被忽略并记日志 - 飞书侧无入站端口:走 WebSocket 长连接
- Dashboard 生命周期:按需开启 + 一次性 token/域名 + 双阈值自动关 + 页面只读,公网暴露窗口最小化
- fail-closed:桥掉线时默认拒绝需审批操作(
fail_closed = false可改回官方 fail-open) - 紧急旁路:
KCF_DISABLED=1后所有 hook 直接放行 - 对话日志:完整对话落盘
~/.kimi-code-feishu/logs/YYYY-MM-DD.jsonl(含终端输出,可能敏感;文件 600/目录 700,默认保留 30 天自动清理) - Kimi hooks 是 Beta 且 fail-open 设计,不要当作唯一安全防线
七、开发与自检
npm install
npm run build # tsc → dist/
node dist/selfcheck.js # 104 项端到端自检(假通道 + 假 kimi,不需要真实飞书)
npm pack # 产出可分发的 .tgz(约 20KB)八、常见问题
Q:支持 Telegram / 微信吗?
src/channel.ts 是抽象接口,照 feishuChannel.ts 实现 Telegram long-polling 通道即可复用全部逻辑;个人微信无官方机器人 API,建议走企业微信。
Q:交互式会话的权限怎么管?
0 号会话和 tmux 会话都是交互模式,工具调用的闸门是 PreToolUse hook:每次调用先问飞书卡片(或命中自动规则/审批池)。请保持桥在线(fail-closed 保证桥不在线时默认拒绝)。
Q:WSClient 断线重连?
SDK 1.71+ 已修复旧版 reConnect() 定时器泄漏(上游 #177),autoReconnect: true 开箱即用;feishuChannel.ts 里留有注释说明。
Q:Windows? 核心逻辑跨平台;hook 命令不含 shell 变量前缀,但进程组终止在 Windows 上退化为单进程 kill。
项目结构
src/
├── cli.ts # bin 入口:onboard / init / install / uninstall / run / doctor
├── appRegistration.ts # 扫码创建飞书应用(官方 Device-Flow 注册协议)
├── config.ts # 配置加载(smol-toml + KCF_* 环境变量)
├── bridge.ts # 核心编排:审批、进度、指令三条链路
├── feishuChannel.ts # 飞书长连接通道(@larksuiteoapi/node-sdk)
├── channel.ts # 通道抽象接口
├── hook.ts # Kimi CLI hook 入口(stdin JSON → 桥 → 退出码/结构化输出)
├── hookServer.ts # 127.0.0.1 HTTP 服务
├── chatSession.ts # 飞书聊天的常驻交互会话(tmux 托管 + wire 结果流)
├── sessionWire.ts # wire.jsonl 转录监听(结构化解析 + 多订阅)
├── dashboard.ts # 按需开启的 WebUI(SSE 实时输出,双阈值自动关)
├── tunnel.ts # cloudflared quick tunnel 托管(/dashboard 开启时拉起)
├── tmux.ts # tmux/pts 会话发现 + 注入 + 抓屏
├── chatLogger.ts # 飞书对话日志落盘(JSONL 按天分文件)
├── streamParser.ts # stream-json 容错解析
├── approvals.ts # 待审批注册表(Promise 挂起/唤醒)
├── state.ts # 聊天绑定、会话路由持久化
├── installer.ts # hooks 注入/移除(自动备份)
└── selfcheck.ts # 104 项端到端自检License
MIT
