dsh-download-monitor
DSH Web GUI 下载监控插件:agent 拉起下载后即可离开,插件自动监控并在页面右下角以 悬浮窗展示进度;任务完成 / 停滞 / 失败时,插件向注册会话发送结构化报告并把 agent 唤醒; 用户可一键停止任务(不唤醒、留痕);历史任务留痕(合并批次、可删可关)。
特性
- 注册制监控:agent 把下载任务写进 manifest(
~/.dsh/dlmon/tasks.json),插件 host 半区
自动监听;支持三种任务形态: - dir:监控目录(对其直接子文件求和算进度),适合 cascette/aria2 这类下载器; - process:dir + 进程存活检测(进程退出且未完成 → 失败;停止时直接终止进程); - job:DSH 后台任务(browser 半区合并 job 状态显示;完成通知走 DSH 原生机制)。
- 状态机:
pending → running → completed / failed / stalled / stopped;判定依据:
期望字节数、日志完成/错误正则(donePatterns / errorPatterns)、进程存活、 停滞阈值(默认 120 秒无字节增长)。
- 悬浮窗:
shell.overlay槽位(官方浮动层),右下角,面板与折叠胶囊都可拖拽、
可折叠;「进行中 / 历史」两个分区;每任务显示状态点、进度条、速度、ETA、失败原因; 皮肤全适配(--dsw-* 令牌)。
- 停止任务:运行/停滞任务行有「停止」(两步确认防误触):
process任务直接终止进程;
dir/job 标记停止。停止 = 终态、不生成唤醒报告(并清掉已排队的报告),manifest 写入 stoppedBy: 'user' 标记,agent 可直接看到是用户主动停止。
- 历史留痕:终态任务(完成/失败/停止)按批次合并记录到
~/.dsh/dlmon/history.json
(同一次 manifest 写入自动成批;batchId 显式成批),每条含简短总结(结果、进度、 来源会话 id、时间);默认保留 20 条(historyCap 可调),可单条删除,可整体关闭 (historyEnabled)。
- 唤醒闭环:完成/失败/停滞 → host 入队 → 浏览器经
session.prompt向注册会话发送
【下载监控】 报告 → agent 处理后可 ack(确认后不再重复打扰);每次状态转换只唤醒一次, 会话存储去重、可全局关闭唤醒;prompt 被拒绝(如 sessionId 不存在)会重试并告警,绝不误报已投递。
- 工作流固化:全局技能
download-monitor(~/.dsh/skills/download-monitor/SKILL.md)——
所有会话在涉及下载时自动按本协议注册任务、处理唤醒、查询历史。
- 零框架依赖:不 import 任何
@deepseek-ai/*值(全部结构类型,dsh-archive-viewer 先例),
不修改 DSH 源码,纯 cordis.patch.yml + profile 挂载。
安装
前置:Node.js ≥ 22、pnpm、dsh CLI(不在 PATH 时用 node --import tsx/esm <deepseek-harness>/apps/cli/src/bin.ts)。
git clone https://github.com/keepermttl/dsh-download-monitor.git
cd dsh-download-monitor
pnpm install
pnpm build
# 安装进 web profile(link: 指向本目录)
dsh plugin --profile web add link:$(pwd) # POSIX
dsh plugin --profile web add link:E:\path\to\dsh-download-monitor # Windows重启 dsh web,浏览器 Ctrl+F5 硬刷新。重启后悬浮窗在右下角出现(有任务时)。
任务注册(manifest 协议)
~/.dsh/dlmon/tasks.json:
{
"version": 1,
"tasks": [
{
"id": "wow-base",
"label": "魔兽基础包续传",
"kind": "process",
"targetDir": "E:\\WoW_68974\\Data\\data",
"expectedBytes": 82900000000,
"pid": 12345,
"logPath": "C:\\Users\\32701\\AppData\\Local\\Temp\\opencode\\wow_base_watchdog.log",
"errorPatterns": ["All connection attempts failed", "Traceback"],
"donePatterns": ["Installing 0 files"],
"sessionId": "<注册该任务的会话 id>",
"notify": { "onDone": true, "onProblem": true },
"ack": false
}
]
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id / label | 是 | 唯一 id / 显示名 |
kind | 是 | dir / process / job |
targetDir | dir/process | 监控目录(直接子文件求和 = 进度) |
expectedBytes | 否 | 完成判据之一(字节达标即完成) |
pid | process | 进程存活检测 |
logPath + errorPatterns / donePatterns | 否 | 日志尾随(最近 64KB)正则;patterns-without-log 会被拒绝 |
sessionId | 是 | 唤醒目标会话——必须用完整 id(含 session- 前缀,如 session-65d36e62-…),取自 GUI 会话信息或 session.list;裸 uuid 会被 host 拒绝(插件会警告) |
notify | 否 | 默认全开 |
ack | 否 | agent 处理后置 true,插件停止该任务唤醒 |
也可经 RPC 注册(浏览器/agent 侧工具):POST /dsh-download-monitor/register,见 src/types.ts 的 wire 契约。
agent 行为协议
1. 拉起下载(后台 job 或独立进程)→ 写 manifest(sessionId 填当前会话)→ 结束回合; 2. 收到 【下载监控】 前缀消息 = 插件唤醒:按报告处置(续传/重试/校验/下一阶段); 3. 处理完后把 manifest 里该任务 ack 置 true(或悬浮窗点「确认」)→ 再次结束回合休息。
悬浮窗操作
- 胶囊 → 点击展开面板;胶囊与面板头部都可拖拽(位置记忆在 localStorage)。
- 「进行中」每行:状态点 / 进度条 / 字节 / 速度 / ETA / 失败原因;运行/停滞任务可
「停止」(两步确认:process 直接杀进程,dir/job 仅标记,均不唤醒); 完成/失败任务可点「确认(ack)」。
- 「历史」分区:按批次合并的终态任务(标签/结果徽标/总结/来源会话/完成时间),可单条删除。
- 「设置」:唤醒报告总开关、历史留痕开关、历史保留条数(5-200)、停滞阈值、采样间隔
(写回 ~/.dsh/dlmon/state.json)。
agent 使用协议(技能)
全局技能 download-monitor 已注册(~/.dsh/skills/download-monitor/SKILL.md),所有会话在 涉及下载时自动加载。核心:启动下载 → 注册 manifest(sessionId 必须带 session- 前缀; 批量任务同一次写入或显式 batchId)→ 结束回合休息;收到 【下载监控】 唤醒 → 处理并 ack; 用户停止的任务(stoppedBy: 'user')不重启不补报告;查询历史读 history.json。
开发
pnpm typecheck # tsc --noEmit
pnpm test # vitest(状态机 / manifest 校验 / 报告文本 / job 合并)
pnpm build # tsdown:lib/index.js(host)+ lib/client.js(browser)已知边界(v1)
- 不自动重启下载器(观察 + 唤醒,决策权留给 agent);
job形态任务:只做状态显示,唤醒走 DSH 原生 job 完成通知;- 日志尾随只取最后 64KB,错误正则建议用无锚点的行级表达式;
- 悬浮窗只对注册了任务时可见;多标签页各自渲染,唤醒按 manifest 的
sessionId投递。
排障(本机实战教训)
dsh plugin add后启动报duplicate loader entry id: ui-web-ui-settings:
pnpm 重装会把 profile 的 node_modules 重排,file: 链接可能出现新旧两份导致 loader 重复挂载同一 patch。修复:在 profile 目录重跑 pnpm install(或 pnpm add <同款依赖> 触发重解析),再重启。建议在重启前先做预检: node --import tsx/esm <checkout>/apps/cli/src/bin.ts web --port 3099(另一个端口), 确认无 loader 报错且端口监听后再动真身。
- GUI 进程就是 agent 进程:重启 dsh web 会杀掉当前回合的 agent。脚本
scripts/restart-gui.ps1 用独立进程完成"杀旧起新"(状态写 %TEMP%\dsh-web-restart.status.json), 但保险起见:重启前先告知用户,重启后刷新页面并发消息恢复。
- 验证 host 半区存活(无需重启、无需浏览器):
```powershell
$body = @{ type='client-request'; rpcId='p'; method='snapshot'; payload=@{} } | ConvertTo-Json -Compress
Invoke-WebRequest -Uri 'http://127.0.0.1:3080/dsh-download-monitor/snapshot' -Method POST -ContentType 'application/json' -Body $body -UseBasicParsing ` 返回 ok:true` 即 host 已挂载(配置与任务清单都在响应里)。
许可
[MIT](LICENSE) © keepermttl。欢迎使用、修改、引用或收录进自己的插件合集,保留 LICENSE 与出处即可。