@modesto_ma/dsh-channels 0.5.0-beta.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/lib/client.js.map +1 -1
- package/lib/index.js +42 -15
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/access/cards.ts +5 -1
- package/src/access/controller.ts +14 -6
- package/src/access/policy.ts +7 -2
- package/src/access/requests.ts +1 -0
- package/src/config.ts +9 -2
- package/src/domain.ts +2 -2
- package/src/service.ts +1 -0
- package/src/settings/contract.ts +1 -0
- package/src/settings/surface.ts +8 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
|
|
|
11
11
|
- **知识库读写**:开启 `feishuTools.wikiRead` / `feishuTools.wikiWrite`(默认都关)并在 `wikiRoots` 里配置知识空间链接 `https://<租户>.feishu.cn/wiki/space/<space_id>` 或节点链接 `https://<租户>.feishu.cn/wiki/<node_token>` 后,模型可用 `feishu_wiki_list` 浏览、`feishu_wiki_read` 读取飞书文档(Markdown,或带 block id 的飞书文档 XML,支持目录/章节/区间/关键词局部读取)、`feishu_wiki_create` 在范围内新建飞书文档、电子表格或多维表格(新节点也可作为子目录继续往下建)、`feishu_wiki_edit` 修改**本应用创建的**文档(追加、在块后插入、替换/删除块、文本替换、整篇重写;必须带上读取时的 `revision_id`,文档被他人改过会被拒绝)。电子表格用 `feishu_wiki_sheet_read` 按子表和 A1 范围读成 CSV(每行带 `[row=N]` 真实行号),`feishu_wiki_sheet_edit` 向**本应用创建的**表格写入单元格值或公式、在数据末尾追加行(追加位置会越过空行找到真正的最后一行,目标单元格非空时失败、不会覆盖;同样校验 `revision_id`;不改样式、图表和行列结构)。多维表格用 `feishu_wiki_base_read` 列数据表、读字段和记录(支持筛选、排序、关键词搜索、分页),`feishu_wiki_base_edit` 增改删记录:**本应用创建的**多维表格可增改删,其他多维表格**只能新增记录**(不改表和字段结构)。所有调用都用应用身份,并且:只有**管理员或知识库白名单**(`/auth wiki add @用户`)里的人发起的话题能用;`wikiRoots` 之外一律拒绝;打开某个文档或表格、每次新建、每次修改都会在话题里弹出**授权卡**,只有话题发起人能点(读取授权在本话题内对该文档持续有效,`/renew` 或重启后重新询问),超时(`approvalTimeoutMs`)自动拒绝;浏览目录不弹卡。需开通 `wiki:wiki`、`docx:document`(只读可用 `wiki:wiki:readonly` / `docx:document:readonly`),电子表格和多维表格另需细分权限 `sheets:spreadsheet:read` / `sheets:spreadsheet:write_only`、`base:table:read` / `base:field:read` / `base:record:read` / `base:record:create|update|delete`(旧版 `sheets:spreadsheet` / `bitable:app` 不生效),且应用要是目标知识空间的成员(可把包含机器人的群加为空间成员/管理员)
|
|
12
12
|
- **HITL 审批**:当模型请求需要授权的动作(如 bash 沙箱升级 `sandbox_permissions`)时,插件监听主机 `approval/request` 事件,在同一话题里**新发一张独立审批决策卡**:卡上直接显示将执行的命令(或工具参数)和提权目标,提供 ✅ 允许 / ❌ 拒绝 / ♾️ 本话题不再询问 三个按钮;也可以在话题里直接回复「允许」「拒绝」「全部允许」,回复其他内容视为拒绝并作为下一条指令。等待期间流式主卡显示「⏳ 等待你审批」。超时(`approvalTimeoutMs`,默认 5 分钟)自动拒绝(fail-closed),非会话发起人点击或回复无效
|
|
13
13
|
- **结构化提问**:模型调用 `ask_user_question` 时,插件监听主机 `user-questions/request` 瀑布并发一张问答卡(每个问题一组选项按钮,多选先勾选再「提交本题选择」),答案回填给模型;主机自带的应答者只有 Web 客户端,没有本插件时纯飞书会话会以 `NO_PROVIDER` 失败。超时(`questionTimeoutMs`,默认 5 分钟)或回合取消时本轮提问失败(fail-closed);没有可选项的问题直接提示用文字作答,**阻塞期间普通文本回复即作为该问题的自由文本答案**(同时支持多选后的自定义文本)
|
|
14
|
-
- **准入与授权(默认开启)**:**管理员不受白名单限制**(私聊、任意群聊——包括尚未授权的群——都能直接发起会话);其他人私聊只收到一条固定提示(`/userid`
|
|
14
|
+
- **准入与授权(默认开启)**:**管理员不受白名单限制**(私聊、任意群聊——包括尚未授权的群——都能直接发起会话);其他人私聊只收到一条固定提示(`/userid` 例外;打开 `dmAllowWhitelist` 后用户白名单里的人也可以私聊),群聊需要「群已授权 + 用户在全局白名单」两层同时命中。机器人被拉进群后私聊管理员发**群信息卡**(未授权群附授权按钮,用于放行白名单用户);已授权群里未授权用户 @ 会触发用户授权卡。授权入口:飞书 `/auth` 命令、授权卡、Web 设置面板;管理员 user_id 在面板配置(私聊 `/userid` 获取),**留空时自动使用飞书应用所有者**
|
|
15
15
|
- 全部基于飞书官方 SDK [`@larksuiteoapi/node-sdk`](https://www.npmjs.com/package/@larksuiteoapi/node-sdk)(WebSocket 长连接,免公网)
|
|
16
16
|
- **Web 设置面板**:Web/桌面端左侧栏 **飞书渠道** 入口(主区整页),顶部状态头 + 7 个标签页(连接 / 会话 / 模型 / 附件与工具 / 访问授权 / 知识库 / 高级);凭据不完整时插件不连接、面板显示三步引导,填写保存后自动连接(保存写入本机覆盖文件,部分字段需重连生效);详见下文「Web 设置面板」章节
|
|
17
17
|
- **对话里的飞书来源**:从飞书发起的会话在 Web/桌面端对话标题旁多一个来源按钮(群名或「飞书私聊」),点开在右侧栏「飞书来源」标签页查看群聊/私聊、话题、发起人、开始与最近活跃时间,并可跳回飞书;其他会话不显示;详见下文「对话里的飞书来源」章节
|
|
@@ -80,7 +80,7 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
|
|
|
80
80
|
| 会话 | 「触发」:私聊、普通群需要 @、话题被拒时降级为会话级(均为开关);「工作区」:工作区根目录、话题工作目录隔离方式(`topicIsolation`);「话题与卡片」:话题过期(`routeTtlMs`,按天)、审批 / 提问卡片等待(`approvalTimeoutMs` / `questionTimeoutMs`,按秒)、流式刷新与状态行保活间隔;「飞书侧功能」:首次私聊引导、斜杠指令面板同步、权限切换 |
|
|
81
81
|
| 模型 | 服务商、模型、推理档位、单次输出上限(`maxTokens`,留空使用模型默认)、Agent 预设 |
|
|
82
82
|
| 附件与工具 | 「入站附件」:接收图片和文件、使用 DSH 原生附件库(开关,关闭则存到话题目录 `attachments/`)、图片预取、首轮话题历史;「工具」:`feishu_attachment_fetch` / `feishu_topic_messages` / `feishu_send_file` 开关;「限额与超时」:预取上限、读取附件上限与超时、读取话题消息超时、引用摘录与历史超时(共 8 项,默认收起,有改动或错误时自动展开) |
|
|
83
|
-
| 访问授权 | 待处理申请(置顶,一键授权 / 忽略)、调试模式(`accessMode`,开关)、管理员 user_id、申请有效期;已授权群 / 用户白名单(添加表单默认收起) |
|
|
83
|
+
| 访问授权 | 待处理申请(置顶,一键授权 / 忽略)、调试模式(`accessMode`,开关)、白名单用户可私聊(`dmAllowWhitelist`,开关)、管理员 user_id、申请有效期;已授权群 / 用户白名单(添加表单默认收起) |
|
|
84
84
|
| 知识库 | 知识库读 / 写开关(`feishuTools.wikiRead` / `feishuTools.wikiWrite`)、可访问范围 `wikiRoots`(每条链接一个输入框,逐条校验);「可使用的人」:知识库白名单增删(与用户白名单独立,等同 `/auth wiki add|remove`) |
|
|
85
85
|
| 高级 | 日志分析接口(监听地址 `analyzeListen` 与访问令牌)、SDK 日志级别、Web 覆盖文件路径与「恢复 profile 配置」 |
|
|
86
86
|
|
|
@@ -106,7 +106,7 @@ DeepSeek Harness(DSH)飞书渠道插件:**一个 dsh 实例对应一个飞
|
|
|
106
106
|
|
|
107
107
|
**可视化选择工作区根目录**:输入框旁的「选择目录…」按钮会调用**宿主机的系统目录选择框**(macOS 的 NSOpenPanel / Windows 的文件夹对话框),选中后把绝对路径填进草稿,仍需点「保存更改」才落盘。该能力转发自宿主可选的 `ctx.directoryPicker` 服务:桌面端/本机 GUI 会话(native 后端)提供按钮;headless、远程或 SSH 会话没有可服务的显示会话(browse 后端),面板不显示按钮、保留手写输入,插件在缺少该服务的旧宿主上照常加载。选择框打开期间请求保持挂起;取消是正常结果,不会报错。
|
|
108
108
|
|
|
109
|
-
**访问授权**:「访问授权」标签页集中管理准入:管理员 user_id(私聊 `/userid`
|
|
109
|
+
**访问授权**:「访问授权」标签页集中管理准入:管理员 user_id(私聊 `/userid` 获取)、调试模式、白名单用户可私聊、授权申请有效期;「已授权群」「已授权用户」列表支持增删(群 ID 可从入群群信息卡或 `/groupid` 获得,用户 user_id 可用 `/userid` 查看);「待处理申请」可一键授权/忽略。白名单只约束非管理员:管理员在任何私聊/群聊都直接放行。管理员留空时自动使用飞书应用所有者(连接时通过「获取应用信息」接口查询),管理员设置下方的「当前管理员」一行会注明“应用所有者”;`guarded` 且既未配置管理员、也查不到应用所有者时,标签页顶部会红字提示“当前会拒绝所有对话”并附上查询失败原因。管理员、群和用户白名单、待处理申请、知识库白名单在每次进入页面时实时读取飞书头像和名字(`GET /dsh-channels/profiles?users=…&chats=…`,每类最多 200 个,Host 侧缓存 30 秒):用户需要 `contact:user.base:readonly` 且在应用的通讯录权限范围内,群需要 `im:chat:readonly`;读不到时显示授权时记下的名字或 ID,并在标签页顶部注明失败原因。授权白名单与申请记录保存在插件 `storageDomain`(`channels_routes_v2` 域),重启后保留;未建立飞书连接时白名单尚未加载,标签页会提示「连接建立后加载授权列表」并禁用写操作。
|
|
110
110
|
|
|
111
111
|
**配置层级**:`configSchema` 默认值 < profile `cordis.patch.yml` 的 `config` < Web 面板覆盖。面板只把可编辑字段写入 `$DSH_HOME/dsh-channels/settings.json`;「高级 → 恢复 profile 配置」确认后删除该文件并回落到 profile 配置。`baseDir` 与 `topicIsolation` 在「会话 → 工作区」编辑(只影响之后新建的话题);话题过期、审批 / 提问卡片等待与 `maxTokens` 保存后立即生效,不需要重连。凭据**值**在「连接 → 飞书应用凭据」编辑,凭据**引用名**(`appId` / `appSecret` 配置项)仍由 profile 配置决定。已移除的目录清理配置 `workspaceRetention` / `workspaceRetentionAuto` 与覆盖文件路径配置 `settingsFile` 若仍留在 profile 或覆盖文件中会被静默忽略,面板下次保存时从覆盖文件中去掉。
|
|
112
112
|
|
|
@@ -136,14 +136,14 @@ Web 里直接新建的会话不显示按钮。来源在 Host 侧按会话 id 持
|
|
|
136
136
|
|
|
137
137
|
## 发布
|
|
138
138
|
|
|
139
|
-
版本号由 semantic-release 按 Conventional Commits
|
|
139
|
+
版本号由 semantic-release 按 Conventional Commits 计算。推送(含合并 PR)到 `main` 在检查通过后**自动发布**正式版;推送到 `dev` 只跑检查,beta 版**只在手动触发时发布**,可以先累积多笔提交。
|
|
140
140
|
|
|
141
141
|
| 分支 | 版本 | npm dist-tag |
|
|
142
142
|
| --- | --- | --- |
|
|
143
143
|
| `main` | 正式版,如 `0.5.0` | `latest` |
|
|
144
144
|
| `dev` | 预发布版,如 `0.5.0-beta.1` | `beta` |
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
手动发布:在 GitHub Actions 的 `pipeline` workflow 点击 **Run workflow**,选择 `dev`(或需要补发时选 `main`):`dry_run` 默认勾选,只预览下一个版本号;取消勾选才真正发布 npm 包、打 tag 并创建 GitHub Release。安装 beta 版用 `@modesto_ma/dsh-channels@beta`。
|
|
147
147
|
|
|
148
148
|
## 目录结构
|
|
149
149
|
|
|
@@ -299,6 +299,7 @@ npm 发行包已包含构建产物:Host 入口是 `lib/index.js`(profile 直
|
|
|
299
299
|
slashCommandPanel: false # 连接时把命令同步到飞书原生 / 面板(需 app_slash_command 读写权限;改后需重连)
|
|
300
300
|
allowPermissionSwitch: false # 是否允许 /permissions <name> 从飞书侧切换权限预设(安全敏感)
|
|
301
301
|
accessMode: 'guarded' # 准入模式:guarded(默认,仅管理员+白名单)| open(旧行为,不安全)
|
|
302
|
+
dmAllowWhitelist: false # guarded 下用户白名单里的人也可私聊(默认关:私聊仅管理员)
|
|
302
303
|
adminUserId: 'a1b2c3d4' # 管理员 user_id(可留空,留空时自动使用应用所有者;都没有时 guarded 下拒绝所有对话;面板可改)
|
|
303
304
|
authRequestTtlDays: 7 # 入群/用户授权申请卡有效期(天,1–30)
|
|
304
305
|
analyzeListen: '0.0.0.0:3090' # 日志平台分析接口的独立监听地址(不填 = 关闭;改后需重连)
|
|
@@ -445,7 +446,7 @@ Content-Type: application/json
|
|
|
445
446
|
- **重启修复(流式中断)**:每条正在流式的 turn 会把自己的修复凭据(trigger 消息、Typing reaction id、全部段卡 id)写入 `channels_routes_v2` 域的 `inflight` 表(键 = 主卡 message id,turn 结束即删)。重启后长连接一建立,插件就把上一实例留下的中途卡片 patch 成「⚠️ 服务已重启,本次回复中断,请重新发送消息。」终态并补 DONE 表情;patch 失败重试一次(上限 2 次)后放弃。持久化盘点与缺口清单见 [docs/persistence.md](docs/persistence.md)。
|
|
446
447
|
- **消息去重(WS 重投幂等)**:飞书长连接重连会重投未 ack 的事件;每条消息在任何副作用之前先落一条 `processed` 记录(键 = message id,飞书全局唯一),重投消息被直接丢弃——同进程靠内存守卫、跨重启靠持久化记录,每条消息至多处理一次。记录保留 24 小时后由 GC 清扫。
|
|
447
448
|
- **路由表 GC 与过期话题拒绝**:超过 `routeTtlMs`(默认 30 天)无活动的真实话题路由会先写入最小持久化墓碑,再删除活动路由;此后该话题中的普通消息和 Session 命令会收到「话题已过期」提示,插件不会恢复旧 Session,也不会在可见历史仍属于旧 Session 的话题里新建 Session(`/help`、`/userid`、`/groupid`、`/health` 仍可用)。墓碑跨重启保留,仅含 thread/chat/owner/工作区路径/会话目录/最后活动时间;旧 DSH Session、日志与话题目录都不删除。降级的 chat-scoped 路由没有独立话题边界,仍直接清理。陈年 in-flight 快照同时在启动时及每 6 小时清扫。`routeTtlMs: 0` 关闭后续路由过期处理,已产生的墓碑仍保持拒绝语义。
|
|
448
|
-
- **准入与授权**:`accessMode: 'guarded'`(默认)下,入站先过频道准入:**管理员不受任何白名单限制**,私聊/任意群聊(含未授权群)都可直接开会话;**非管理员**私聊回固定提示(`/userid`
|
|
449
|
+
- **准入与授权**:`accessMode: 'guarded'`(默认)下,入站先过频道准入:**管理员不受任何白名单限制**,私聊/任意群聊(含未授权群)都可直接开会话;**非管理员**私聊回固定提示(`/userid` 例外;`dmAllowWhitelist: true` 时全局用户白名单里的人可私聊,命令与群聊相同,`/auth` 仍仅管理员),群聊要求 **群在白名单** 且 **user_id 在全局用户白名单**,两者缺一不可。未授权消息不建 Session、不建话题目录、不落附件、不调模型;群里静默、私聊回固定文案。机器人被拉进任意群时(订阅 `im.chat.member.bot.added_v1`),插件私聊管理员发群信息卡(群名/`chat_id`/成员数/邀请人),未授权群附「授权本群 / 忽略」按钮;已授权群里未授权用户 @ 机器人时,管理员会收到用户授权申请卡(含昵称/user_id)。授权状态持久化在 `channels_routes_v2` 域的 `access_groups` / `access_users` / `access_requests` 表,撤销立即生效。管理员 user_id 在 Web 面板配置,留空时为飞书应用所有者(连接时查询,需可选的「获取应用信息」权限;查询失败则 fail closed,手动填写管理员即可);`/auth` 命令、授权卡、面板三种入口等价。
|
|
449
450
|
- **HITL 审批**:插件以 `prepend: true` 注册为宿主 `approval/request` 瀑布的应答者——本频道路由的 session 的审批请求**优先于** Web 网关的通用应答(其面板对飞书用户不可见),非本频道的请求原样 `next()` 交给其他应答者。审批卡是**独立消息**(锚定到该 session 最新流式卡、同话题),绝不嵌入流式卡(其全量替换 patch 会冲掉按钮)。宿主请求不带工具参数,插件在 `session/event` 中按会话记录**当前回合**的 `tool/call`(回合结束即清空,不回读历史),按 `callId` 取出命令显示在卡上(超过 600 字截断并注明)。决策仅在会话发起人操作时生效:允许 → `allowed-once`;本话题不再询问 → `allowed-once`,并在内存里记下「工具名 + 提权目标」,该 session 之后的同类请求直接 `allowed-once`、不再发卡,同 session 里其他被覆盖的待审批卡一并允许(`/renew` 换 session 或重启后失效,提升到 `danger-full-access` 时按钮标红);拒绝 → `rejected`;文字回复中「允许 / 同意 / 好 / y」等、「拒绝 / 不行 / n」等、「全部允许」等关键词作用于该 session 最新一张待审批卡,其他文字把该 session 所有待审批卡按 `rejected` 结算后照常作为下一条消息交给 Agent;超时 → `rejected`(fail-closed,卡面显示「已超时」)、请求被撤回(abort)→ `cancelled`、卡片发送失败 → `unavailable`(fail-closed);每次决策后卡片更新为终态(注明按钮 / 文字回复 / 不再询问 / 被新消息取代)并移除按钮。审批记录(`approval/asked`/`decided`)由宿主会话日志负责,插件不做持久化——重启后遗留问题随 turn 消亡,与 Web 网关一致。需要飞书应用订阅 **card.action.trigger** 回调(见「飞书侧准备」);启动日志 `HITL 审批应答已就绪` 表示生效,`主机未挂载 approval 服务` 表示该部署未启用 HITL。
|
|
450
451
|
- **终态与表情**:成功轮的面板标题变为「回复结束」(无推理内容时不渲染面板),并把 `Typing` 换成 `DONE`;`turn/end.kind = error` 时面板标题为「回复失败」(同样仅当有推理内容),错误只在正文出现一次(`❌ **本轮运行失败**` + 结构化错误消息与错误码),并把 `Typing` 换成 `No`。模型/推理强度/本轮缓存样本不会继承上一轮。
|
|
451
452
|
- **限频**:卡片更新单条消息 5 QPS、应用级 1000 次/分钟;500ms(2 QPS)默认安全,`refreshMs` 下限 200ms;活动状态行的保活心跳默认 5s(`statusRefreshMs` 下限 1s),远低于限频。
|