voyager
Nagi-ovo
Enhancement suite for Gemini, AI Studio, Claude & ChatGPT — plus a prompt manager for any web UI, DeepSeek Harness included. / 面向 Gemini、AI Studio、Claude 与 ChatGPT 的增强套件;提示词管理器可用于任意 Web UI,含 DeepSeek Harness。
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:cershuang/dsh-fschannel
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
English | 中文
让 DeepSeek Harness Web 会话连接到飞书机器人:新建会话时选择「连接飞书」,之后在飞书里给机器人发消息,消息进入该 DSH 会话由 Agent 处理,回复自动发回飞书聊天。Web 与飞书可同时驱动同一个会话。
传输层使用飞书官方 @larksuite/channel SDK —— WebSocket 长连接,无需公网回调地址。
$DSH_HOME/feishu-bindings.jsonresume 恢复;重启后绑定关系保留output: 'stream'):回合开始即开卡(先显示「正在处理…」),回复以打字机卡片逐字呈现;工具调用显示「正在调用工具:{name}」引用行;回合结束时卡片保留最终结果,并自动排版为结构化卡片(段落/表格/代码块按 markdown 渲染,无输出或失败也留在卡片内);无卡片权限时自动回退普通消息)若指向会话工作区内的本地图片文件,会自动上传并以卡片 img 元素内嵌(不显示远程链接/死链);远程 URL 或无法读取的路径不会出现在卡片中(保留说明文字)output: 'plain' 可切换为「每步一条 markdown 消息」.dsh-fschannel-images/)feishu-bindings.json),不再写入会话日志 —— 旧版本写入的 feishu/image 事件类型不为 harness 所知,会导致整个会话历史无法加载;v0.1.5.3 起插件在启动时自动修复历史日志(给这类事件补上 harness 认可的 ignorable 标记),并自动补录历史图片索引send_feishu_image 工具把图片发送到绑定的飞书聊天(可附带说明文字);发送的图片同时进入该会话的 Web 画廊。图片路径必须位于会话工作区内(防穿越),支持 PNG/JPEG/WebP/GIF;sendImageTool: false 可关闭此工具send_feishu_file 工具把生成的任意文件(报告、导出、日志等,≤ 30 MiB)发送到绑定的飞书聊天,可指定显示文件名(默认取原文件名)并附带说明文字;sendFileTool: false 可关闭此工具requireMention);未绑定聊天收到引导提示(可关)/model — 弹出同一张卡片(查询当前模型与 effort)/model list — 模型目录(含各模型支持的 effort 级别)/model use <provider>/<model> — 切换模型(下一条消息起生效)/model effort <off|high|max> — 切换思考强度/status — 会话与机器人状态;/stop — 停止当前回合;/help — 命令清单apiProxy(与 Web UI 同一通道),会话级持久:重启后自动重放前置:Node 22.19+,已安装 dsh CLI 与 pnpm(npm i -g @deepseek-ai/dsh pnpm)。
# 1. 准备环境(.env 只放路径配置;凭据在启动后于设置页填写)
cp example.env .env # 按需修改 FSCHANNEL_REPO / FSCHANNEL_ENV_FILE
# 2. 构建客户端(每次修改源码后执行)
npm install && npm run build
# 3. 安装进 web profile(file: 协议会把包复制进 pnpm store 并安装其依赖;
# <插件仓库路径> 也可写在 .env 的 FSCHANNEL_REPO 中)
dsh plugin --profile web add file:<插件仓库路径>
# 4. 重启 dsh web 生效
dsh web
首次安装时 pnpm 可能因构建脚本策略报
ERR_PNPM_IGNORED_BUILDS(protobufjs)。 protobufjs 是生产传递依赖(@larksuite/channel→@larksuiteoapi/node-sdk→ protobufjs) 且带安装脚本,但本插件不需要它构建。在 profile 目录的$DSH_HOME/profiles/<profile>/pnpm-workspace.yaml(不在本仓库内,需要你自己创建或追加) 中加入:allowBuilds: protobufjs: false
注册行 feishu-bot(来自包内 cordis.patch.yml,作为 bundle 层自动叠加):
| 字段 | 默认 | 说明 |
|---|---|---|
envFile |
<cwd>/.env |
路径配置文件(FSCHANNEL_* 键);不再承担凭据 |
appId / appSecret |
凭据库 | 入口配置直接覆盖;设置页「连接凭据」保存到 DSH 凭据库 |
requireMention |
true |
群聊仅响应 @ 机器人;单成员群(1 用户 + 机器人)免 @ |
output |
stream |
stream=流式打字机卡片(失败回退普通消息);plain=每步一条消息 |
modelCardTriggers |
true |
提到模型/effort 时自动弹出按钮卡片 |
queueAck |
true |
Agent 忙碌时回复排队位置提示 |
ackInbound |
false |
空闲时也回复「收到,处理中…」 |
reactInbound |
true |
收到消息时加表情反馈(👍→✅/☹️) |
reactReceived / reactDone / reactError |
THUMBSUP / DONE / SAD |
各阶段表情(飞书标准 emoji_type,可自定义) |
holdImages |
true |
暂存飞书图片,随下一条文字一起识别 |
holdHint |
true |
纯图片消息回「已收到 N 张图片…」提示 |
maxHeldImages |
10 |
每个聊天暂存图片上限 |
maxHeldImageBytes |
10 MiB |
单张图片大小上限 |
holdTtlMs |
0 |
暂存过期时间(毫秒,0=不过期) |
imageDir |
<cwd>/.dsh-fschannel-images |
暂存目录(须在会话工作区内) |
hintUnbound |
true |
未绑定聊天回复引导提示 |
hintText |
内置 | 自定义引导文案 |
bindingsFile |
$DSH_HOME/feishu-bindings.json |
绑定持久化路径 |
凭据解析优先级:插件配置 appId/appSecret > 凭据服务(shell 导出的环境变量 > DSH 凭据库 $DSH_HOME/.credentials.yaml > 项目 .env > ~/.dsh/.env)> 插件 envFile。推荐做法:在设置页「飞书机器人 → 连接凭据」填写 appId/appSecret,保存到凭据库(appId 仅显示掩码,secret 永不回显;.env 中不要再放凭据)。
路径配置(放 .env,不入库):FSCHANNEL_REPO(插件仓库根目录,脚本/重启用)、FSCHANNEL_ENV_FILE(.env 自身路径,默认取工作目录)、FSCHANNEL_BINDINGS_FILE(绑定数据文件,默认 $DSH_HOME/feishu-bindings.json)。cordis.patch.yml 的 envFile 解析顺序:FSCHANNEL_ENV_FILE 环境变量(由 scripts/restart-dsh.ps1 从 .env 导出)→ <cwd>/.env。
设置 → 飞书机器人 标签页的「绑定管理」中可管理:已绑定会话可「断开」;待绑定会话可「取消待绑定」(例如创建后未使用、不再需要的会话)。
| 输入 | 效果 |
|---|---|
| 「调整模型」「切换模型」「把 effort 调一下」等 | 弹出按钮卡片(模型 Pro/Flash + effort off/high/max 一键切换) |
/model |
弹出同一张卡片 |
/model list |
列出模型目录 |
/model use <provider>/<model> |
切换模型 |
/model effort <off\|high\|max> |
切换推理等级 |
/status |
会话与机器人状态 |
/stop |
停止当前回合 |
/help |
命令清单 |
.env 的 FSCHANNEL_REPO),改代码后需 npm run build(改了客户端时)→ dsh plugin --profile web add file:<插件仓库路径>(重新复制进 store)→ 重启 dsh web 才生效。cordis.patch.yml 里的配置需重启生效。$DSH_HOME/.credentials.yaml(0600 权限,dsh 凭据库),设置页永不回显;.env 只放路径配置,可安全提交(仓库 .env 仍被 .gitignore 忽略,example.env 可安全提交)。/feishu/* 只接受 127.0.0.1 访问(与 Web 同源)。output: 'plain' 可整体关闭。.dsh-fschannel-images/),随下一条文字一起由 agent 通过 vision-tools 识别;其他文件类型归一化为文本提示;流式卡片只含文本与工具活动行。im:message 权限(与收消息同一权限);表情操作失败仅记日志,不影响收发。| 层 | 位置 | dsh 升级后 |
|---|---|---|
| 插件本体 | 插件仓库(git 仓库,路径见 .env 的 FSCHANNEL_REPO) |
保留 |
| 注册行 | 包内 cordis.patch.yml(bundle 层,dsh plugin 自动 reconcile) |
保留 |
| 绑定数据 | $DSH_HOME/feishu-bindings.json |
保留 |
无任何对 dsh 包内部文件的修改,升级后无需重打补丁。
npm test # 门禁:lint + 构建 + 全部 10 个离线冒烟(提交前跑这个)
npm run lint # 仅静态检查(no-undef —— 漏写 import 只有它能拦住)
npm run build # esbuild 构建客户端 bundle (lib/client.js)
npm run watch # 监听重建
# 改完代码要生效,必须重装进 profile —— dsh 是把插件**复制**进
# ~/.dsh/profiles/web/node_modules/dsh-fschannel 的,改仓库文件本身没有任何效果。
# 而且 pnpm 按 (路径, 版本号) 复用 store 条目,只 add 一次不会覆盖,必须 remove + add。
# scripts/restart-dsh.ps1 已经把这一串串起来,并在启动前比对 lib/*.js 的哈希,
# 不一致直接失败而不是打出 UP —— 手动做等价于:
# npm run build
# dsh plugin --profile web remove dsh-fschannel
# dsh plugin --profile web add file:<插件仓库路径>
# 单独跑某个冒烟(npm test 会全部跑一遍):
node scripts/smoke-test.mjs # 服务端冒烟(env/绑定/持久化)
node scripts/smoke-env-example.mjs # example.env 不得含凭据
node scripts/smoke-locales.mjs # zh/en 字典键与占位符对齐
node scripts/smoke-client.mjs # 客户端 bundle 冒烟
node scripts/smoke-settings-render.mjs # 设置页渲染冒烟(绑定表格 + 排版)
node scripts/smoke-cards.mjs # 模型卡片与触发词测试
node scripts/smoke-render.mjs # markdown 分段与结果卡片渲染
node scripts/smoke-stream.mjs # 流式卡片缓冲与失败回退
node scripts/smoke-images.mjs # 图片校验、暂存与提示语组装
node scripts/smoke-repair.mjs # 会话日志修复(feishu/image ignorable 标记 + seq 冲突)
# 需要真实环境,不在 npm test 内:
node scripts/audit-sessions.mjs # 全量审计所有会话日志的 seq 连续性(需真实 $DSH_HOME)
node scripts/integration-test.mjs # 集成测试(真实连接飞书 + mock apiProxy)
lib/index.js — 宿主插件:传输、入向/出向桥接、命令通道、卡片动作、/feishu HTTP APIlib/cards.js — 模型/推理设置按钮卡片(构建、触发词、动作解析)lib/stream.js — 流式卡片(缓冲 + 失败回退)lib/bindings.js — 绑定存储(JSON 原子写,含会话级模型路由)lib/env.js — .env 解析(路径配置)+ 凭据分层解析(凭据服务 > envFile)lib/images.js — 图片类型表、校验、暂存缓冲与提示语组装lib/render.js — markdown 分段与最终结果卡片渲染lib/repair.js — 历史会话日志修复(zstd 帧解码、外来事件、seq 冲突)lib/locales.js — 飞书侧文案(zh/en,跟随宿主语言)lib/send-image-tool.js — send_feishu_image 工具(agent 发送生成图片到绑定聊天)lib/send-file-tool.js — send_feishu_file 工具(agent 发送生成文件到绑定聊天)src/client/index.jsx → lib/client.js — 浏览器端:会话头 chip + 设置行POST /feishu/repair-logs 是运维专用端点:没有 UI 调用它,限流为每分钟一次,且整个修复过程是同步的,执行期间会阻塞整个 harness 进程(所有 agent、Web 服务、飞书传输)本项目基于 MIT License 发布(见仓库根目录 LICENSE):
Copyright (c) 2026 CersHuang
允许任何修改、使用、复制、合并、发布与商用(包括闭源衍生),唯一要求:任何修改或衍生作品(含代码与文档)必须保留上述版权声明与本许可声明(开发者信息)。
以下流程参照飞书自建应用的完整配置路径,含两个已知的坑(个人版建不了应用、未发布就配长连接会报错)。控制台界面可能随版本微调。
cli_ 开头)和 App Secret(Secret 首次不可见,点「重置」或「查看」获取完整值)dsh web 后,打开设置 →「飞书机器人」→「连接凭据」,填入 App ID 与 App Secret(保存到 DSH 凭据库,appId 仅显示掩码,secret 永不回显)。历史版本曾放在
.env(FEISHU_APP_ID=.../FEISHU_APP_SECRET=...);v0.1.5.1 起.env不再承担凭据,凭据一律走设置页/凭据库。
左侧「权限管理」→ 逐个搜索并开通以下 6 个权限:
| 权限标识 | 用途 |
|---|---|
im:message |
接收用户发给机器人的单聊消息 |
im:message:send_as_bot |
以应用身份发消息(回复、卡片) |
im:message.group_at_msg:readonly |
接收群组中 @ 机器人的消息 |
im:chat |
获取群信息(绑定后显示群名) |
im:chat.members:bot_access |
获取群成员信息(单成员群免 @ 判定、绑定后显示群名) |
cardkit:card:write |
发送/更新交互卡片(流式卡片、模型设置卡片) |
左侧「版本管理与发布」→ 创建版本时的「可用范围」→ 只勾选你自己这一个成员,不要选「全部成员」。这是防止别人拿到你会话控制权的唯一屏障。
左侧「应用能力」→ 添加「机器人」能力。没有这一步,私聊发消息不会触发任何事件。
⚠️ 已知的坑:没有已发布版本时,事件订阅页保存长连接配置会报「应用未建立长连接」。先发布,再配长连接。
左侧「事件与回调」:
im.message.receive_v1(接收消息)card.action.trigger(卡片按钮回调,模型设置卡片点击依赖它)保存长连接配置时若报错,通常是本地桥接还没起来:先把 dsh web 跑起来(本插件随 dsh web 启动长连接),再回来保存。
im.message.receive_v1。im:message:send_as_bot 且版本已发布。requireMention 默认开启),并开通 im:message.group_at_msg:readonly。cardkit:card:write 并配置了 card.action.trigger 回调。CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: feishu-bot。