lijian-ui/dsh-im-gateway

为 DeepSeek Harness 提供多 IM 通道接入的网关插件:钉钉 / QQ / 个人微信,支持扫码绑定与流式回复。

项目介绍Project Overview

dsh-im-gateway 是 DSH 平台的多 IM 通道网关客户端插件,单一插件聚合钉钉、QQ、个人微信三类通道,提供扫码登录、流式回复、会话管理与内置斜杠命令。适用于需要在统一网关下接入多个 IM 平台并实时响应用户消息的场景。注意:QQ 群聊与个人微信不支持流式输出,插件会自动回退为纯文本回复。

dsh-im-gateway is a DSH client plugin that unifies DingTalk, QQ, and personal WeChat into a single IM gateway, offering QR-code binding, streaming replies, session management, and built-in slash commands. Use it to connect multiple IM platforms under one gateway with real-time responses. Note that QQ group chats and personal WeChat fall back to plain text, as streaming is unsupported on those channels.

或使用命令行安装(适合开发者)Or use CLI install (for developers)

命令行安装CLI Install

dsh plugin --profile web add @lijian-ui/dsh-im-gateway

lijian-ui/dsh-im-gateway 加入你的 DSH 配置(web profile)即可启用。

READMEREADME

@lijian-ui/dsh-im-gateway

English | 简体中文

DeepSeek Harness (dsh) 提供多 IM 通道接入的网关插件:钉钉 / QQ / 个人微信,支持扫码绑定、流式回复、工具审批、交互提问、长文本分片、多段合并、双语界面。

npm version License: MIT


功能特性

通道与核心

  • 统一网关服务 — 一个插件、三个通道。所有通道都汇聚到单一的 ctx.imGateway 核心:会话管理、斜杠命令、流式回复、状态广播。
  • 钉钉 — 出站 WebSocket 长连接,群聊 + 单聊,@ 提及过滤,AI 卡片流式输出(实时增量回复),斜杠命令。
  • QQ — WebSocket 网关(官方 qqbot-nodejs SDK),私聊(c2c)+ 群聊,扫码绑定机器人(免去开放平台手动创建),流式消息(c2c)。
  • 个人微信(iLink) — 官方 iLink 长轮询协议,扫码登录 + 配对码,仅单聊,媒体(AES-128-ECB CDN)收发。
  • 多机器人实例 — 同一通道类型可配置多个实例(例如两个钉钉机器人),各自独立凭据。
  • 设置页 UI — 在官方 dsh web UI 内渲染完整的设置页(「IM 通道」),扫码绑定就在这里完成。
  • 流式回复 — 钉钉 AI 卡片、QQ stream_messages;渠道不支持流式时自动回退纯文本。
  • 单例锁 — 通过 DSH_HOME 文件锁防止多个实例并发写坏 session log。

交互增强

  • 工具审批桥 — agent 调用需要审批的工具时,在 IM 里直接回复「批准」或「拒绝」即可放行/拦截,超时自动委托回 dsh 原生审批体系。
  • 交互提问桥 — agent 调用 ask_user_question 时,问题同步推送到 IM,用户回复选项编号/文字即可作答,与 Web 端竞速第一答生效。
  • 长回复分片 — 超过渠道单条上限的回复自动按句号/换行切分,带 (1/3) 分段前缀,逐条发送。
  • 多段输入合并 — 用户连续发多条消息时自动合并为一条(可配超时窗口);.. 续传合并、!! 立即提交。
  • 文件发送工具 — agent 可调用 im_send_file 工具把工作区文件(图片/视频/文档)直接发送到当前 IM 会话。
  • 双语界面 — 配置 language: 'zh' | 'en' 切换所有用户可见回复的语言。

会话管理

  • 内置斜杠命令/help/model/status/new/reset/stop/sessions/continue/workspaces/workspace(见下文)。
  • 会话列表与继续/sessions 列出最近会话(含标题),/continue <id> 恢复指定会话。
  • 工作区管理/workspaces 列出所有工作区及会话数,/workspace <路径> 切换工作区(自动重置当前会话,下次消息在新工作区创建新会话)。
  • 用户白名单 — 配置 allowAllUsersallowedUserIds 精确控制谁可以使用机器人。

安装

需要 DeepSeek Harness (dsh)——本插件是标准 dsh bundle,通过官方插件通道安装。

从 npm 安装(推荐)

dsh plugin --profile web add @lijian-ui/dsh-im-gateway

npm 包自带预构建的 lib/无需构建授权(不需要 allowBuilds)。

从 tarball 安装

npm pack @lijian-ui/dsh-im-gateway
dsh plugin --profile web add ./dsh-im-gateway-0.1.1.tgz

从 GitHub 安装

dsh plugin --profile web add github:lijian-ui/dsh-im-gateway

Git 安装拉取的是源码,首次安装需要批准包的 prepare 构建脚本(pnpm ≥ 10)。按提示把包键加进 profile 的 pnpm-workspace.yamlallowBuilds 即可。优先用 npm / tarball 方式可跳过此步。

验证安装

dsh --profile web --dump-config     # 应看到 "# == @lijian-ui/dsh-im-gateway" 配置层
dsh --profile web                   # 启动后浏览器打开设置 → 「IM 通道」

快速上手

  1. 打开 dsh web UI → 设置 → IM 通道
  2. 点击添加通道
  3. 选择通道类型:
    • QQ:点击扫码登录 → 手机 QQ 扫码 → 凭据自动填入 → 保存。
    • 个人微信:点击扫码登录 → 手机微信扫码 →(如要求则输入配对码)→ 凭据自动填入 → 保存。
    • 钉钉:手动填写 AppKey / AppSecret(或直接编辑配置文件)→ 保存。
  4. 在 IM 客户端给机器人发消息 — 回复实时流式返回。

配置存储在 ~/.dsh/settings.yamlim-gateway.channels)。在 UI 保存配置会热重载通道(无需重启)。


斜杠命令

在任何 IM 通道里发给机器人:

命令 说明
/help 列出可用命令
/model 用 emoji 编号列出模型;/model 1/model <名称> 切换(无会话时 → 设为下次会话默认模型)
/status 通道 / cwd / 当前模型 / agent 状态
/new /reset /clear 开启全新会话
/stop 中止当前回复
/sessions 列出最近 10 个会话(标记当前会话,显示标题)
/continue <会话id> 继续指定会话(用 /sessions 查看可用 id)
/workspaces 列出所有工作区(按最近活动排序,显示会话数)
/workspace <路径> 切换到指定工作区(重置当前会话,下次消息在新工作区创建新会话)
/workspace 不带参数时等同于 /workspaces

多段输入控制后缀

后缀 说明
(无) 进入合并窗口,等待后续消息(默认 3 秒超时后自动提交)
.. 续传合并:把本条加入缓冲,继续等待
!! 立即提交:把缓冲 + 本条合并后马上发给 agent

审批回复

当 agent 调用需要审批的工具时,直接回复:

回复 效果
批准 / 同意 / yes / y / allow 放行工具执行
拒绝 / no / n / reject / deny 拦截工具执行

超时后自动委托回 dsh 原生审批体系。


配置

所有配置都可在设置页编辑;底层 schema 在 ~/.dsh/settings.yaml

im-gateway:
  language: zh                    # 界面语言:zh(中文)| en(英文)
  approvalTimeoutSecs: 120        # 工具审批超时(秒)
  questionTimeoutSecs: 600        # 交互提问超时(秒)
  mergeTimeoutSecs: 3             # 多段输入合并窗口(秒)
  allowAllUsers: false            # 全局放行所有用户(仅开发用)
  allowedUserIds:                 # 白名单:{ channelId: string[] } 或用 '*' 匹配任意渠道
    "*":
      - user-abc
  channels:
    - id: dingtalk-main
      type: dingtalk
      name: 主机器人
      enabled: true
      config:
        clientId: "..."
        clientSecret: "..."
        # callbackBaseUrl, appId, botAppId, baseUrl, botId, cdnBaseUrl, pollIntervalMs...

网关级配置

字段 默认值 含义
language zh 界面语言(zh 中文 / en 英文),影响所有用户可见回复
streamThrottleMs 800 流式推送节流间隔(毫秒)
slashCommands true 是否启用斜杠命令
approvalTimeoutSecs 120 工具审批 IM 等待超时(秒),超时后委托回 dsh 原生审批
questionTimeoutSecs 600 交互提问 IM 等待超时(秒),超时后转回 Web 端
mergeTimeoutSecs 3 多段输入合并窗口(秒),用户连续发消息时合并为一条
allowAllUsers false 全局放行所有用户(仅开发用,生产环境勿开)
allowedUserIds {} 白名单;key 为 channelId(* 匹配任意),value 为用户 ID 数组

通道级配置

字段 适用渠道 含义
clientId / clientSecret dingtalk 钉钉应用 key / secret(Stream 模式)
appId / clientSecret qq QQ 开放平台凭据(扫码绑定所得)
token / botId / baseUrl / cdnBaseUrl weixin iLink 凭据(扫码绑定所得)
enabled 全部 该实例是否连接

架构

IM 客户端 ──► 通道适配器 (dingtalk / qq / weixin)
                   │  ImInboundMessage
                   ▼
             ctx.imGateway(核心)
                   │  多段合并 → 白名单检查 → 审批/提问拦截 → 斜杠命令
                   │  ensureSession → agent.followup
                   ▼
            dsh harness agent(LLM 循环)
                   │  会话事件 (turn/start, assistant/chunk, tool/call, turn/end)
                   ▼
        EventDispatcher → 流式回复 / 分片 / 工具提示
                   │  (AI 卡片 / stream_messages / 纯文本回退)
                   ▼
                IM 客户端

模块结构

模块 职责
im-gateway.ts 核心服务 ImGatewayService:会话管理、消息路由、工具注册
events.ts EventDispatcher:SessionEvent → IM 渠道操作(流式、分片、工具提示)
commands.ts CommandHandler:斜杠命令处理(/help /reset /model /status /stop /sessions /continue /workspaces /workspace)
stream.ts StreamThrottle:流式节流器,攒批 text-delta 后按间隔推送
approval.ts ApprovalBroker:工具审批桥,挂起 approval/request 等待 IM 回复
questions.ts QuestionBroker:交互提问桥,挂起 ask_user_question 等待 IM 回复
split.ts splitText:长文本分片,按句号/换行切分,带分段前缀
merge.ts SessionMerger:多段输入合并,支持 .. / !! 控制后缀
i18n.ts Translator:中英文双语翻译表
instance-lock.ts acquireInstanceLock:DSH_HOME 文件锁,防止并发写坏 session log
types.ts 接口定义:ImChannelAdapter、ImGatewayConfig、ImGateway 等
  • Host 半(node):src/index.ts(apply)、src/gateway/(核心 + 上述模块)、src/channels/(dingtalk / qq / weixin + 协议助手)、src/remote.ts(设置页的 Typert RPC)、src/sync.ts(保存配置后热重载通道)。
  • Client 半(浏览器):src/client/ — 设置页「IM 通道」(添加/编辑弹窗 + 扫码登录 + 状态点)。
  • 多机器人channels 是数组,同一 type 可多次出现。

扩展点

第三方可以不 fork 直接注册自己的通道:

import { ImChannelAdapter } from '@lijian-ui/dsh-im-gateway'   // peerDependency 引用核心

class MyChannelAdapter implements ImChannelAdapter { /* ... */ }
ctx.imGateway.registerChannel(myAdapter)

ImChannelAdapter 接口可选方法:

方法 说明
sendText(convId, text) 必需。发送纯文本消息
sendMedia(convId, filePath, caption?) 可选。发送文件/图片/视频(im_send_file 工具使用)
beginStream(convId) 可选。开启流式回复(首个文本增量时调用)
streamText(convId, text) 可选。流式覆盖更新(节流推送)
endStream(convId, fullText) 可选。结束流式回复(turn/end 时调用)
updateCard(convId, text) 可选。遗留单次卡片更新
authorizes(userId) 可选。渠道本地授权检查(返回 false 拦截)
maxMessageChars 可选。单条消息字符上限(默认 4000,用于分片)
label 可选。渠道显示名称(用于提问回执)

开发

git clone https://github.com/lijian-ui/dsh-im-gateway.git
cd dsh-im-gateway
npm install
npm run build          # tsdown → lib/
npm run watch          # 保存自动重编译
npm run typecheck
npm test               # node --test tests/*.test.mjs

本地 link 进 dsh profile:

dsh plugin --profile web add ./   # 从本目录安装(link)

Windows 注意:dsh 子进程从 package.jsonmain 加载 lib/index.js — 修改 src/ 后必须 npm run build 再重启 dsh 进程(它的 require 缓存会保留旧模块)。

测试

测试使用 Node.js 内置测试运行器(node:test),位于 tests/ 目录:

测试文件 覆盖模块 测试数
approval.test.mjs ApprovalBroker 8
questions.test.mjs QuestionBroker + parseQuestionReply + formatQuestionPrompt 8
split.test.mjs splitText 8
merge.test.mjs SessionMerger + stripControlSuffix 9

常见问题

  • 插件没有任何日志 — cordis 默认把 ctx.logger.* 缓存进内存。本插件在 apply 时注册了 console exporter,日志会出现在 dsh 子进程 stderr(桌面壳会加 [dsh] 前缀)。
  • QQ 客户端一直显示「连接中」 — 流式开得太早或没收干净。本插件在第一个文本增量时才开流,并在 turn/end 无条件收流(0.1.x 已修复)。
  • 能对话但不流式 — 渠道回退到了纯文本(例如 QQ 群聊不支持 stream_messages;微信本身没有流式概念)。这是设计行为。
  • 回复被截断成多条 — 超过渠道 maxMessageChars 上限时自动分片,带 (1/3) 前缀。这是设计行为,不是 bug。
  • 多段消息被合并了 — 默认 3 秒合并窗口内连续发的消息会合并为一条。发 !! 立即提交,或调大 mergeTimeoutSecs
  • 审批/提问超时了 — 调大 approvalTimeoutSecs / questionTimeoutSecs。超时后会自动委托回 Web 端。
  • 切换英文后部分文本仍是中文formatAnswerSummary 中的分隔符( )和 (空) 目前固定中文,因为它们是格式符号而非自然语言。

许可

MIT © lijian-ui

DeepSeek Harness 构建 — 独立插件,与 DeepSeek 无隶属或背书关系。

上一个 Prev dsh-remote-tunnel 下一个 Next DSH-agent-bridge