dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Telegram out-of-band login approval and task-completion notifications for DeepSeek Harness (DSH)
给 DeepSeek Harness (DSH) 的 Web 访问加两层 Telegram 能力:
DSH 0.2.1-rc.2 已原生支持 WebUI 远程持久设置,建议设置 remoteSettings: false,
保留上游实现;关闭的是旧适配,不会关闭新版原生设置能力。
旧 DSH 0.2.0-rc.2 仍可用默认开启的适配,为配置的公开 HTTPS origin 恢复 settings 整页 Host 持久配置。
两种方式都在原生模型页编辑模型、提供商、API Key 和默认模型,不增加独立网页或 TG 模型编辑,
不改变登录批准、会话及 Host/Origin 保护。详见 远程原生设置。
零 npm 依赖(只用 Node 内置能力),以 Cordis 插件形式挂载,不改 DSH 源码。
DSH 的 Web 登录是「每进程随机 token → 换取30 天签名 cookie」。设计本身很强
(HMAC 签名、绑定 authority、SameSite=Strict、不信任转发头),但有两个缺口:
| 缺口 | 说明 |
|---|---|
| token 泄露即接管 | 拿到正确 token 可直接换到 30 天会话,中间没有任何人工确认环节 |
| 没有 logout | 官方文档明确:撤销只能「删凭据记录 + 重启 dsh」 |
本插件补上这两个缺口:旧 launch token 不能直接登录;必须输入独立密钥并经 Telegram 批准。 同时增加持久化、可撤销的独立浏览器会话。升级后旧浏览器须重新登录一次,不保留仅有旧 DSH Cookie 的兼容路径。
不接管 / 的静态提供,而是包装共享 ctx.connection.authorizeIndex、admit 和 requestRejection,
在 DSH 原生签名 Cookie 之外叠加独立持久化会话,并在 WebServer upgrade 分发前检查。
index.html 的提供、目录遍历防护、<base> 注入及原 Host/Origin fence 全部保持 DSH 原样。
升级须保留本地 src/index.ts 的 admit 双入口补丁:0.2.0-rc.2 是 admit → requestRejection,
0.2.1-rc.2 是 requestRejection → admit,新版 HTTP RPC 直接走 admit。
只保留旧 requestRejection 包装会让原生有效 Cookie 绕过额外会话层;不要用未含此补丁的旧 Git HEAD 覆盖工作树。
本插件不是全局 HTTP 路由中间件:任意第三方 webServer.register() 路由若不调用 Connection,不会自动受保护。
登录入口不再依赖 URL 里的 token,而是要求一个长期独立 Secret Key:
GET / → 未认证,返回登录表单(不落 DSH 的 401)
POST /login (secret) → 校验 Secret Key
├─ 错误 → 401;错误桶冷却中为 429,不发通知
├─ 已有待批准/已批准 → 303 复用同一请求(不轰炸通知)
├─ 正确但通知配额耗尽 → 429
└─ 正确 → 新建批准请求
├─ 下发 nonce cookie
├─ 发 Telegram 确认卡片
└─ 303 → /login/status?request=...
GET /login/status?request=… → 校验 nonce cookie 与请求绑定
├─ pending → 200 等待页(meta refresh 自动刷新)
├─ denied / expired → 403 / 410
└─ approved → 单次消费,交回 DSH 完成 303 + Set-Cookie
关键安全性质:批准只是「准入」,不是「认证」。批准后仍由 DSH 原方法签发会话 cookie,
且批准单次消费、绑定浏览器 nonce——重放等待页只会得到 401。
还会签发独立 __Host-dsh-web-session Cookie;服务端只存随机凭证哈希和会话元数据。
任意多个浏览器可分别获批,不互相顶掉;过期/撤销实际拦截首页、API 与新 WS,并关闭该会话已有 WS。
早期版本(M1)用 URL 里的 ?token=... 作为门禁信号。该模型已移除:
带 token 查询参数的请求现在被明确拒绝(401),不再进入批准流程。
规格文档见 docs/dsh-telegram-login-approval.md(M1 历史设计)。
/newbot,拿到 token建议与其它 TG 插件使用不同的 Bot。Telegram 的
getUpdates一个 Bot 只能有一个消费者, 共用 Bot 会导致两个插件互相抢更新。
登录不再是「拿到 URL 就能进」,而是要输入一个独立密钥:
install -d -m 700 /etc/dsh
install -m 600 /dev/null /etc/dsh/web-secret.key
node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('base64url'))" > /etc/dsh/web-secret.key
文件必须是 root 所有、权限 600 的普通文件(32 字节随机数据编码为无填充 base64url,正好 43 字符;与管理台重置密钥一致)。 插件启动时会强制校验,不满足直接抛错——不会静默放行。
在管理员 Telegram 私聊发送 /key 可取回密钥;发送 /web 或 /start 打开 🌐 Web 管理台。
管理台展示实际绑定、内部/公开地址、密钥、Web 会话数、待批准数和各期限;支持交互设置、会话列表与踢出。
重置密钥、逐个/全部踢出均二次确认。完整说明见 Web 管理台与启动衔接。
git clone https://github.com/JJDonald/dsh-telegram-login-approval.git
复制 cordis.example.yml,把 <PLUGIN_DIR> 换成插件在本机的绝对路径:
- insert:
- id: telegram-login-approval
name: '/home/you/dsh-telegram-login-approval/src/index.ts' # Windows: 'F:/path/...'
config:
botTokenRef: TELEGRAM_LOGIN_BOT_TOKEN
adminChatId: 123456789 # 改成你自己的数字 ID
publicOrigin: 'https://dsh.example.com' # 你的公开 HTTPS origin
botUsername: 'example_bot' # 你的 Bot 用户名(不含 @)
secretFile: '/etc/dsh/web-secret.key' # 上一步生成的文件
dryRun: false
export TELEGRAM_LOGIN_BOT_TOKEN='123456:ABC...'
cd /path/to/deepseek-harness
node --import tsx/esm apps/cli/src/bin.ts \
--profile web \
--patch /path/to/cordis.yml \
--no-open --port 8188
--patch属于dsh层级参数,必须放在--profile之后、app 参数(--no-open等)之前。
要让管理台保存的绑定地址/端口生效,后续部署必须采用 scripts/serve.ts
启动衔接(配置 scripts/serve.example.json),或人工将保存值写入实际启动参数。
脚本从 web-settings.json 加载绑定与公开地址,传入 --host/--port/--trusted-host;
保留 READY/UNLOADED fail-closed 衔接并截获含 launch token 的上游 URL 输出。
详见 部署步骤。面板不会自动重启或修改 nginx/DNS。
直接访问 https://<你的 publicOrigin>/,输入 Secret Key。
浏览器显示「等待 Telegram 确认」,同时 Telegram 收到带 ✅ 确认登录 / ❌ 拒绝 按钮的卡片。
点确认后页面自动跳转进入。
| 键 | 默认 | 说明 |
|---|---|---|
enabled | true | false 时完全不包装(等价于未安装) |
remoteSettings | true | 仅适配旧 0.2.0-rc.2 的 settings 客户端;新版 0.2.1-rc.2 建议设 false,保留原生远程设置 |
publicOrigin | 必填 | 本部署唯一的公开 HTTPS origin,如 https://dsh.example.com |
botUsername | '' | Bot 用户名(不含 @);仅用于页面文案与 /key@<bot> 匹配 |
secretFile | 必填 | root-only Secret Key 文件的绝对路径 |
botTokenRef | — | Bot Token 的环境变量名(推荐) |
botToken | — | 直接写 token(不推荐,容易误提交) |
adminChatId | 必填 | 唯一被接受的 Telegram 数字用户 ID |
ttlSeconds | 300 | 新批准有效期,30–3600 整数秒;nonce Cookie 同步使用该期限 |
sessionDays | 30 | 新Web会话有效期,整数天;不得超过当前DSH原生 cookieMaxAgeDays |
failureCooldownSeconds | 600 | 全局10次错误密钥/60秒后仅限制错误输入,60–86400整数秒;正确密钥不受此冷却影响 |
windowSeconds | 600 | Telegram确认通知触发限流窗口(与错误密钥冷却独立) |
maxTriggersPerWindow | 3 | 窗口内最多触发次数 |
cooldownSeconds | 600 | 通知触发超限后的冷却时长(不控制错误密钥冷却) |
notifyOnDeny | true | 是否回发拒绝提示 |
dryRun | false | true = 不碰网络,仅本地验证门禁行为 |
dataDir | $DSH_HOME/storages/telegram-login-approval | offset、Web设置与会话落盘目录;与启动衔接配置保持一致 |
配置缺失会直接抛错,而不是静默退化成「无门禁」——这是安全路径,
不允许「忘配就等于不生效」。publicOrigin 与 botUsername 都是配置项,
源码里不写死任何具体域名或 Bot。面板设置保存后优先于 overlay 默认值;
期限设置立即用于新条目,旧会话/批准/已启动冷却保持原截止时间。
绑定/公开地址只在下次人工启动加载,面板同时显示运行值与待生效值。
DSH 0.2.1-rc.2 原生客户端已通过 remote.settings.describe/mutate 使用 Host 设置,不再依赖 loopback 选择表达式。
该版本建议 remoteSettings: false;即使保持 true,已注册的新客户端源码不匹配时也不会安装旧 registry 补丁。
没有实际适配的模块,其 rebuilt 仍委托原生方法;不猜测替换新版源码,不重打旧补丁。
默认开启的旧适配继续支持 DSH 0.2.0-rc.2 的 dsh-client-ui-settings / clientModules 适配点。
经正常登录后在原生模型页配置,仍调用原生 settings/describe/update/mutate、credentials/set 等接口;
保存由 DSH 的 profile 配置编辑器和凭据 provider 完成。环境变量提供的只读 API Key 仍不可覆盖。
插件不伪造 isLoopback、不改全局 fetch、不放宽后端认证或其他 origin。
实现仅在内存中改原生 settings 模块的单一 host/memory 选择表达式,
使用当前运行 publicOrigin(已保存启动值优先);TG 修改地址仍只在下次启动生效。
原生 registry 为转换后的源码生成独立模块/combo revision,source map 使用转换源码的 identity map;
HMR 再发布时仍适配同一模块,卸载恢复原始方法和当前上游源码。不修改 DSH 安装文件。
这是一项可选 UI 功能:缺 clientModules 不阻塞登录门禁;源码或接口不兼容时明确记录
remoteSettings 未启用,保留上游行为,门禁仍正常。DSH 升级后应检查该诊断并重新验证适配。
后续部署如果还有旧手工修改,可另行经批准恢复原版文件;本插件不自动修改它。
关闭功能也不会移除安装文件中已有的手工条件。完整边界见 功能说明。
订阅 session/event 与 agent/status,在一次「任务段」结束时推送一条消息。
为什么不逐轮通知:一个任务常跨多轮(工具调用、计划步骤),每轮结束都推会刷屏。 因此把轮次聚合成任务段:
turn/start 开启任务段,后续轮次并入同一段;idle 后等待静置窗口(SETTLE_MS = 800ms);状态:✅ 完成 / ⏹ 已中止 / ❌ 失败 / ⚠️ 输出上限 / ⚠️ 进程重启中断。
正文超过 3200 字符自动截断。
npm install # 只装 tsx
npm test
或在 DSH 仓库内直接运行:
cd /path/to/deepseek-harness
node --experimental-transform-types /path/to/dsh-telegram-login-approval/tests/gate.test.mts # 新版调用方向,24 个用例
TLA_TEST_CONNECTION_MODE=legacy node --experimental-transform-types /path/to/dsh-telegram-login-approval/tests/gate.test.mts # 同一套用例,旧版调用方向
node --experimental-transform-types /path/to/dsh-telegram-login-approval/tests/task-watch.test.mts # 12 个用例
全部用例不需要真实 Bot:用 fake ctx + 假 HTTPS 请求驱动完整的 「Secret Key → 带外批准 → 单次凭证交换 → 不可重放」链路。
覆盖的关键性质:
GET / → 200 登录表单(不落入 DSH 的 401);旧 ?token= → 401adminChatId / 非 HTTPS publicOrigin / 非法 botUsername 均抛错prototype 双入口补丁确实生效;新版/旧版相反调用方向均可验证,卸载后还原 admit 与 requestRejection--check 入口拒绝旧通配设置dispose 后不再触发__Host- cookie)。sha256(nonce)。node:https,不经过全局 fetch,避免 bili 等同进程模型代理钩子记录含 Bot Token 的 URL;网络异常与 API 错误说明中的 Token 统一脱敏。adminChatId 的私聊消息,其余静默忽略并记日志。X-Forwarded-*)。真正的按 IP 限流应放在反代层
(nginx limit_req / fail2ban)。ctx.connection.authorizeIndex / admit / requestRejection / browserAuth.maxAgeMilliseconds 与 ctx.webServer.server,
DSH 升级后建议重跑测试。authorizeIndex 无服务端副作用(只签名 cookie、不写状态)。
若上游改为写会话表,探测方式需要重新评估。/web 的逐个/全部踢出按钮,不提供独立 /revoke 指令。127.0.0.1;支持本机具体 IPv4/IPv6,不接受 0.0.0.0、:: 或其 IPv4-mapped 通配形式。为保持标准 URL 展示,暂不支持 %zone;主机名与带方括号的输入也不接受。接口是否属于本机仍由 DSH/操作系统绑定时判断。四个容易踩的坑,都已写进代码注释:
ctx.connection 是 Cordis 的服务 facade,
每次属性访问都返回新对象,实例上的 defineProperty 会被静默丢弃。ctx.effect(fn) 会立即调用 fn,并把返回值当 disposer。
写成 effect(() => callback()) 会在安装那一刻就执行清理。authorizeIndex 不能用返回值判态。它成功时也返回 false(已写 303),
失败时已经写过 401。判态要用 requestRejection(纯检查、不写响应)。writeHead,确认它确实写了 303 + Set-Cookie 才放行;
否则说明 DSH 侧认证未完成,直接抛错而不是发一个空会话。