dsh-feishu-cui

remotetested: passnpmMIT

Remote DSH access that needs no hosting of your own — the Feishu DM chat is the interface over the bot's outbound long connection, with cards for workspace, session (switch and rename), model, reasoning effort, permission preset and balance, and tool approvals and agent questions answered in that sa

下载量 / Downloads
1,845
依赖体积 / node_modules
38.83 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-feishu-cui

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-feishu-cui

免运维的远程 DSH:一套完整的远程使用方案:拿飞书私聊当操作界面(CUI)——装一个插件、填一对凭据、在飞书里配对一次,之后在手机上就能用 DSH;常用操作都是卡片和菜单里点,不用像 TUI 那样记命令:发消息排进当前会话的队列、回答回到你那条消息上,工作区 / 会话(切换与改名)/ 模型 / 推理深度 / 权限 / 余额都在飞书里点,agent 反问与工具审批也在飞书答。

往下看:[这是什么](#这是什么) · [怎么用](#怎么用) · [边界与约定](#边界与约定) · [代码结构](#代码结构) · [用例脉络](#用例脉络) · [看日志](#看日志) · [术语表](#术语表) · [还没做](#还没做)

这是什么

一套远程使用 DSH 的入口:拿飞书私聊当操作界面(CUI)——提问、切会话、切模型与权限、打断、答反问、批工具都在飞书里做。执行面在宿主:真正跑命令、改文件的是 DSH 里的 agent,沙箱与审批策略由 DSH 决定。它由两半组成——宿主半边的插件(lib/,跑在 dsh web 里)和浏览器半边的设置页(lib/settings/client.js,挂在 DSH 的 Web 界面上)。

它替你省掉的运维(这是它跟「自己搭一套远程访问」最大的区别):

  • 不用公网入口:不需要公网 IP / 域名 / HTTPS 证书 / 反向代理。
  • 不用内网穿透:不需要 frp / ngrok / tmate 这类东西。
  • 不用开端口、改防火墙 / 安全组:机器人往飞书出站建一条长连接,网络侧什么都不用动。
  • 不用自建中转服务:消息走飞书官方通道(机器人长连接 + 卡片回调)。
  • 不用写客户端:飞书本身就是客户端——私聊收回答,卡片做选择,菜单做入口。
  • 不用自己搓鉴权:配对码一次性绑定 owner,机器人只服务他一个人。
  • 不用盯着断线:自动重连 + 存活看门狗(见[长连接的存活与恢复](#长连接的存活与恢复))。
  • 不用额外起服务:它就是 dsh web 的一个插件,装完跟着宿主加载。
  • 不用搭服务端:服务端就是飞书。要自己动手的只有开发者后台那几步——建一个自建应用、加 3 项订阅(两个事件 + 卡片回调)、补权限(想在设置页看姓名再多两条)、配 8 个菜单项、发一次版本(见[飞书那边](#飞书那边))。

跟 TUI 比,省的是「记」:终端里每一步都要敲命令、记参数、盯滚动;这里工作区 / 会话 / 模型 / 推理深度 / 权限 / 余额各是一张卡,切会话、改名、打断、答反问、批审批都在飞书的消息和按钮里完成,回答做成卡片回到你发消息的那条上。

能做什么

  • 不用记命令:日常操作全在卡片和菜单里点,没有需要背的斜杠命令——要输入文字的只有配对码与会话重命名这两处(提问本身当然还是打字)。
  • 私聊发一句话 → 排进「当前会话」的队列(不打断正在跑的那一轮),回答回到那条消息上:卡片跟着状态走——被取走后是「处理中」(带「停止」按钮),刚进队列还没被取走是「排队中」(带「撤回」按钮),之后逐段把正文接上去,跑完收尾成「已完成」/「已停止」/「处理失败」。正文超过卡片体积上限(30KB)就换成「任务失败」+ 一句「去网页端看」。
  • 菜单八项:申请配对、工作区列表、会话列表(含「新建」)、会话重命名、模型、推理深度、权限、余额。
  • agent 反问(ask_user_question)与工具审批(工具要授权)都在飞书弹卡:谁触发的谁答。
  • 别处(网页端 / CLI)改了当前会话的模型 / 权限 / 会话名 → 主动私聊通知 owner 一张「模型已修改」/「权限已修改」/「会话名已修改」卡。
  • 长连接自愈:休眠 / 半开连接有存活看门狗(见[长连接的存活与恢复](#长连接的存活与恢复))。

不做什么(能力边界,详细取舍见[边界与约定](#边界与约定))

  • 只服务 owner 一个人:别人发消息、点菜单一律回同一句「CUI会话未匹配」,且不区分「还没绑定」和「不是 owner」。
  • 只认私聊:支持文本、图片、文件和 post 富文本;语音、视频、表情包不支持。
  • 只认「飞书当前会话」:别的会话怎么聊都不回答、也不推送。
  • 反问只接「有选项」的题;全靠打字的题让给网页端(让路之前先回一句「这题飞书答不了」)。单选与多选都接,每题在卡片上标着【单选】/【多选】。
  • 模型 / 推理深度 / 权限都是会话级设置,只作用于当前会话。
  • 卡片上不能自由输入文字(只有配对填配对码、会话重命名填名字这两张卡能输)。

前置条件

  • 本机已装 DSH(dsh 与 dsh plugin 可用),profile 用 web——设置页挂在它的 Web 服务器上。
  • 一个自己的飞书自建应用(要能开长连接),并且只给自己用:开发者后台的可用范围只勾自己。
  • 别跟别的飞书插件共用同一个飞书应用:长连接是集群模式、不广播,同 App 会互相抢事件。本插件读自己的凭据引用名 FEISHU_CUI_APP_ID / FEISHU_CUI_APP_SECRET。

依赖的宿主能力(缺了哪个,对应功能就自动退化,并在日志里说明原因)

credentials、settings、webServer、sessionController(投喂 / 打断 / 撤排队项 / 模型目录 / 会话句柄)这四项写在 inject 里;另外按需取 sessionQuery(列会话)、workspaceRegistry(读工作区)、permissionPresets(读权限预设)、userQuestions 与 approval(两条要人答的 waterfall)、sessionProjections(读当前模型)。卡片按飞书卡片 JSON 2.0 写。

怎么用

安装与前置

dsh plugin --profile web add dsh-feishu-cui

装完重启 dsh web。装在别的 profile 上就把 --profile 换成那个名字;本机改这个插件自己的代码时,用 link: 指到仓库目录也一样。

DSH 版本:当前 0.2.x 需要 DSH 0.1.7-rc.1 及以上——设置走的是 0.1.7 起的插件条目 config(带 volatile 那几个字段)。
还在 DSH 0.1.5 上的,装最后一个支持它的版本:
```sh
dsh plugin --profile web add dsh-feishu-cui@0.1.0
```
装的时候如果收尾报 ERR_PNPM_IGNORED_BUILDS: protobufjs,插件其实没装上。
@larksuiteoapi/node-sdk 的依赖里有 protobufjs,它带 postinstall 脚本,pnpm 默认不跑;dsh 把 pnpm 的非零退出当成整体失败,于是没把插件登记进 profile。先放行再装一次:
```yaml
# ~/.dsh/profiles/<profile>/pnpm-workspace.yaml
allowBuilds:
protobufjs: true
```

插件分两半:宿主半边 lib/index.js 跟着 dsh web 加载;浏览器半边是设置页,插件装上后在 DSH 的 Web 界面里出现「飞书CUI会话」分区。

改完代码要生效得重启 dsh web——插件只在启动时加载一次。

飞书那边

开发者后台 → 你的自建应用:

  • 事件与回调的订阅方式选 长连接;
  • 事件加 im.message.receive_v1、application.bot.menu_v6;
  • 卡片回调加 card.action.trigger;
  • 权限补 im:message.p2p_msg:readonly、im:message:send_as_bot、im:message:readonly(下载消息里的图片/文件)。
  • 想在设置页看到绑定人的姓名,再加两条权限(控制台里按后面括号里的词搜):
  • contact:contact.base:readonly(接口级,加了接口才调得通;搜「通讯录」);
  • contact:user.base:readonly(字段级,接口通了之后靠它响应里才带姓名;搜「用户基本信息」)——只加前一条的话,接口返回成功但姓名是空的。
  • 同时把绑定人放进应用的通讯录权限范围(开发配置 → 权限管理 → 数据权限),否则接口会报 41050 no user authority。
  • 上面这几步不做也能跑,只是那一栏显示「已绑定」。
  • 可用范围只勾自己——机器人只对你一个人开放,别放给部门或全员。

机器人自定义菜单:下面这套是推荐配置,不是硬要求。

硬约束只有三条,少一条就不工作:

  • 子菜单都选 推送事件;
  • event_key 必须和下表完全一致(插件按它分流);
  • 父菜单只当容器、不配事件——配了它会推一个插件不认识的 event_key:点了没反应(它不执行动作,也不回话),只有日志里多一句「这个菜单项还没有去处」。

至于分几个父菜单、叫什么名字、谁挂在谁下面、子菜单怎么排序,插件一概不看(它只认叶子的 event_key),怎么分都行;展示形式也随你。飞书后台的顺序是先建子菜单、再建父菜单。

下面这套是现在后台里的实际配置,可以照抄:

父菜单 · 子菜单(从上到下) · event_key · 点了会怎样

会话设置 · 模型 · model · 发「模型」卡

· 推理深度 · effort · 发「推理深度」卡

· 重命名 · session-rename · 发一张输入框卡片,填新名字(改的是当前会话)

· 权限 · permission · 发「权限」卡

导航 · 会话列表 · sessions · 发「选择会话」卡(底部多一个「新建」)

· 工作区列表 · workspaces · 发「选择工作区」卡

操作 · 查询余额 · balance · 发「账户余额」卡

· 申请配对 · pairing · 发一张输入框卡片,让你填配对码(见下一节)

「模型」和「推理深度」是两张独立的卡,所以两个菜单项都要配;工作区 / 会话 / 推理深度 / 权限各是一张卡带一组选项,模型卡是一张卡按 provider 分组(一家一组、组抬头是 provider 名,行标签是纯模型名,摘要行「现在是【X】」里带供应商),也是一个菜单项就够,不需要为每个选项各配一项。权限那三个预设(read-only / workspace-write / danger-full-access)配不配都行,配了就也打开权限卡,只是冗余。

  • 创建版本并发布,菜单生效要等 5 分钟左右。
机器人只对 owner 一个人开放。 可用范围里只勾你自己,这是第一道闸;插件这层是第二道:
除了配对,别人发消息、点菜单一律只收到「CUI会话未匹配」,卡片上的操作也只回一句同文案的提示——
不静默、不执行任何动作,而且不区分「还没绑定」和「不是 owner」(对外不暴露绑定状态)。
所以聊天里不会提示你去配对,配对指路只在设置页和本 README 里。
别和别的飞书插件共用同一个飞书应用:长连接是集群模式、不广播,同 App 会互相抢事件。

插件里配对

在飞书里点菜单「申请配对」,机器人发一张输入框卡片,卡片上写着「设置页里有一串配对码,把它填到下面这个输入框里。」打开 DSH 的设置页 →「飞书CUI会话」,那里显示这串码;把它填进卡片、点「确定」,填的那个人就成为 owner。

  • 配对码 10 分钟有效、只能用一次;已经绑过就不再生成(再点菜单「申请配对」只会收到「存在绑定会话,无法申请」)。
  • 设置页的状态快照里有:长连接是否建立、当前绑定的 user(查得到姓名就显示姓名,否则显示「已绑定」)、在册的配对码、两项凭据配没配。页面不轮询,随时点「刷新」重新读一次。
  • 换人要先解绑:设置页点「解绑」→ 清掉绑定的 user、在册的配对码一并作废;同时给原 owner 私聊发一张「已解绑」的卡(发不出去只记日志,不影响解绑)。之后回飞书重新点「申请配对」拿新码。

解绑不掐正在跑的那一轮、也不撤排队里的消息:那一轮照旧跑完、回答卡也会走完,但原 owner 之后发消息、点卡片就只会收到「CUI会话未匹配」。

  • 凭据(App ID / App Secret)也在同一页填:保存后不再回显,存进 $DSH_HOME/.credentials.yaml,保存即按新凭据重建长连接。

日常用法

  • 私聊机器人发消息(owner 专用):回你一张卡片(回复在你发的那条消息上),之后这一轮每产出一段就把那张卡整张换掉,跑完收尾。

上屏的只有正文(助手消息里的 text 块):思考、工具调用、工具结果都不上屏——思考一轮能写几万字、工具结果常常是整份文件,一张卡片装不下。想看完整过程在网页端看。

这句话同时排进当前会话的队列,不打断正在跑的那一轮;队列不设上限。

卡片的时机:进队列时先不开卡

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::11,最后更新 2026-10-27

Links

GitHub · Homepage · npm

Same category