2008924/dsh-progress-viz
Real-time stage & ETA dashboard for DeepSeek Harness (dsh) from session event stream | dsh 任务进度可视化:会话事件流实时阶段+ETA 看板
Listed
1
Dev
Bundle verified
Preview
What it does
Real-time stage, ETA & cost dashboard for headless dsh tasks: turns the black-box session event stream into a live multi-task grid (cordis plugin + standalone local dashboard, no API calls).
Best for
- Operators monitoring long-running headless DSH tasks that otherwise expose only a final answer.
- Users who need a local multi-task view of stages, recent actions, ETA, and estimated cost.
- Teams wanting a standalone dashboard, CLI status table, or optional Feishu completion notification without model API calls.
Not ideal for
- Interactive workflows where task progress is already visible and a separate dashboard adds little value.
- Environments without readable local DSH `session.jsonl.zstd` event streams.
- Users needing exact completion times or authoritative billing; ETA and cost are estimates derived from available events.
- Historical reporting beyond the documented recent-session window and eight-task dashboard limit.
README
dsh Progress Viz
实时可视化 dsh(headless 模式)任务执行过程的独立看板工具包——读取会话事件流,呈现阶段进度、ETA 与实时动态流,纯本地运行。
English: dsh Progress Viz is a self-contained dashboard that visualizes dsh headless task execution in real time. It parses the local session event stream (
session.jsonl.zstd) to show stage progress, ETA and a live activity feed — 100% local, no API calls, no tokens consumed.

背景痛点
dsh 在 headless 模式下只输出最终答案,执行过程(读哪些文件、跑了什么命令、任务清单推进到哪一步)完全不可见。任务一跑就是几分钟甚至更久,期间无法判断:
- 任务是否还活着、卡在哪一步?
- 当前处于整个任务的哪个阶段、还剩多少阶段?
- 大概还要等多久?
原理
dsh 会把每次任务的完整事件流实时追加写入本地会话文件:
~/.dsh/sessions/<cwd编码>/<session-id>/session.jsonl.zstd
(zstd 压缩的 JSONL,每行一个事件,type 字段区分类型。)
本工具关心的事件(v1.1 扩展后):
| 事件类型 | 含义 | 用途 |
|---|---|---|
todo/write |
模型写入任务清单(data.todos,每项有 content + status) |
阶段数据源:取第一个未完成项为当前阶段 |
step/start |
步骤边界 | 无 todo 时的兜底计数 |
tool/call |
工具调用(data.name + data.arguments) |
最近动作 + 事件流/时间线 |
session/title |
dsh 自动生成的任务标题(data.title) |
任务标题(卡片优先显示,无标题回退 cwd basename) |
assistant/message |
模型消息(data.usage 含 token 统计) |
成本估算:累计 tokens × DeepSeek 定价常量 |
解压必须用 zstandard 的 stream_reader 全量流式解压(decompressobj 只能解第一个 frame 是已知的坑)。看板每 4 秒轮询一次(重新扫描最近任务列表并逐任务解析),页面每 5 秒自动刷新。
安装与快速开始
# 方式一:只装依赖,直接运行脚本(无需安装本项目)
pip install "zstandard>=0.21"
python dashboard.py 8123
# 方式二:按项目安装(pyproject.toml,自动带上 zstandard 依赖)
pip install .
python dashboard.py 8123 # 或 python -m dashboard 8123
本项目是纯脚本型包(无 console script 入口、不提供
import模块),pip install .只是安装依赖并注册元信息;运行方式与直接运行脚本完全一致: 在项目目录执行python dashboard.py [port](或python -m dashboard [port])。
启动行为:
- 自动打开浏览器:服务器启动成功后自动用系统默认浏览器打开 http://127.0.0.1:<实际端口>;
- 端口冲突自动递增:指定端口(或默认 8123)被占用时自动 +1 重试 (最多 5 次),并打印「端口 X 被占用,改用 Y」;连续 5 个端口都被占用才报错退出;
-
--no-open:启动时不自动打开浏览器(如远程/无头环境):python dashboard.py 8123 --no-open; -
--status:不启动服务器,直接打印当前任务状态表 (状态 / 标题 / 阶段 k/N / ETA / 成本)后退出(exit 0),无任务打印「暂无任务」:python dashboard.py --status; -
飞书完成通知:
--feishu-webhook <URL>(或环境变量FEISHU_WEBHOOK)启用—— 任务从 running 变为 completed 时(看板轮询检测到状态翻转)向 webhook POST 一条 文本消息「✅ dsh 任务完成:<标题>(标题>
浏览器打开 http://127.0.0.1:8123:
-
多任务分栏:看板扫描
~/.dsh/sessions下全部会话,只保留最近 1 小时内有写入 (文件 mtime 距今 < 3600s)的任务,按 mtime 降序取前 8 个,以网格分栏展示 (≥1400px 三列 / ≥900px 两列 / 其余单列,响应式); - 运行中置顶分区(v1.2):任务按状态分为「🟢 运行中」与「✅ 已完成」两个区块 (运行中在上、已完成沉底),各带计数徽章;点击区块标题栏可整体折叠/展开(默认展开), 长列表防信息过载;
- 运行中卡片(mtime 距今 ≤ 30s):绿色状态点「正在运行」+ 任务标题 + cwd + 会话 id(前 8 位)+ 已运行时长 + 阶段进度条(阶段 k/N + 名称)+ ETA(预计完成时刻 + 剩余时间 + 推算方式)+ 成本估算(有 usage 数据才显示)+ 「详情」展开区(点击展开完整时间线,等宽字体)+ 事件流(默认展开,max-height 滚动);
- 已完成卡片(mtime 距今 > 30s):灰色「已完成」+ 标题 + cwd + 耗时 + 最后阶段名 (stage 或「步骤N」),事件流默认折叠(点击展开),避免信息过载;
- 无任务时:显示「等待 dsh 任务开始…」,启动一个 dsh headless 任务后自动出现分栏;
- 标题区实时显示任务总数与运行中数(如「共 8 个任务 · 运行中 2 · 每 5 秒自动刷新」)。
/api/live 返回最近任务列表 JSON:{"tasks": [ {id, cwd, title, status, stage,
stage_idx, stage_total, stage_pct, action, eta_s, eta_mode, eta_at, elapsed_s,
tail, cost_est, timeline} ]},无任务时 tasks=[]。每个任务的 ETA 独立计算
(融合算法复用,历史会话排除任务自身);单个会话扫描/解析失败会静默跳过,
不影响其他任务。
v1.1 新功能说明
-
成本显示(A):解析
assistant/message(或tool/result)事件的data.usage(实测本机格式:inputTokens/outputTokens/cacheReadTokens), 按模块级常量PRICES(DeepSeek 官方定价,元/百万 tokens,注释标明价格与日期、 可改)估算成本,卡片显示「≈¥0.0123」并标注估算。事件流无 usage 数据时cost_est置None,前端不显示(不硬编假数据)。 -
任务标题(D):解析
session/title事件的data.title(取最后一个非空), 任务卡片优先显示标题;无标题回退 cwd 的 basename;再回退完整 cwd。 -
CLI 状态查询(C):
--status不启动服务器,直接打印当前任务状态表。 -
飞书完成通知(B):
--feishu-webhook/FEISHU_WEBHOOK启用;任务 running→completed 翻转时通知一次(去重),启动时已 completed 不通知,失败静默。 -
详情时间线(E):
/api/live任务对象新增timeline字段([{t, type, desc}], 最多 50 条,chunk 连续合并、取最近);运行中卡片「详情」点击展开。
v1.2 看板 UI 更新
- 运行中置顶:任务按状态分为「运行中」「已完成」两个区块,运行中在上、已完成沉底;
- 区块折叠:两个区块均可点击标题栏折叠/展开(默认展开),长列表防信息过载;
- 计数徽章:标题与区块同步显示「共 N 个任务 · 运行中 M」实时计数;
- 跨平台修复:cwd 标题回退在 Linux/macOS 上正确取 basename(反斜杠归一化为正斜杠, 修复 test_v11 在 macos/ubuntu 上的失败)。
插件版(dsh-progress-viz-plugin)
除了「独立版」(看板直接 zstd 解析会话文件),本工具提供 cordis 插件版:
插件挂载到 dsh profile(任务执行的地方,如 headless),实时监听会话事件,
只保留语义事件(todo/write、step/start、step/end、tool/call、
tool/result、assistant/message、turn/start、turn/end、session/title、
session),过滤 chunk 等中间态噪音(assistant/chunk、reasoning-chunks、
tool-call-chunks、text-chunks、request/* 等),原子重写
<DSH_HOME>/progress/<session-id>.json(及 current.json)供看板消费。
与独立版的关系:看板新增数据源,优先读插件输出(实时、已过滤噪音);
<DSH_HOME>/progress/ 缺失或没有文件时,自动回退现有 zstd 解析路径
(行为不变,新增优先级不影响回退)。/api/live 字段语义不变(插件任务
eta_* 为 None、tail 由 timeline 派生)。
安装(dsh plugin add)
插件源码在 plugin/ 目录(TypeScript、ESM、lib/index.js 产物)。构建并挂载:
cd publish/dsh-progress-viz/plugin
pnpm install --registry https://registry.npmmirror.com # 国内 registry
pnpm build # tsc → lib/index.js
# 回到任意目录,把插件挂到 headless profile(本地目录绝对路径):
dsh plugin --profile headless add <plugin 目录绝对路径>
dsh --profile headless --dump-config # 验证输出包含 progress-viz
dsh plugin add会执行pnpm add <路径>并把本包加入 profile 的dsh.profile.bundles(插件包声明了dsh.bundle.patch)。若环境不支持dsh plugin,可手动把插件加入 profilepackage.json的dependencies与dsh.profile.bundles数组后执行pnpm install。插件零配置可挂载。
输出格式
每个会话一个文件:<DSH_HOME>/progress/<session-id>.json(原子写:临时文件 +
rename,每次语义事件更新重写);会话结束(session/disposed 或空闲超时)后
标记 finished: true 并保留文件;新会话开始(session/created)时重置状态。
{
"session_id": "session-xxxxxxxx-...",
"title": "任务标题", "cwd": "C:\\work",
"stage": "当前阶段", "stage_idx": 2, "stage_total": 3, "stage_pct": 67,
"action": "运行 bash 命令: pytest -q",
"cost_est": 0.0123, "elapsed_s": 42,
"updated_at": "2026-08-15T12:00:00.000Z", "finished": false,
"timeline": [{"t": "12:00:01", "type": "todo/write", "desc": "当前第 2 项/共 3 项"}]
}
阶段逻辑与独立版一致(todo 第一个未完成项优先、step/start 计数兜底);
成本按同一 DeepSeek 定价常量估算(无 usage → null)。详见
plugin/README.md。
阶段与 ETA 说明
-
阶段来自模型的
todo/write任务清单(取第一个未完成项,idx 从 1 开始);模型必须使用 todo 工具维护清单才能显示阶段。建议在任务提示里引导,例如:【输出约定】请使用 todo 工具维护你的任务清单:每个主要步骤列一项 (开始标记 in_progress、完成标记 completed,清单变化时更新)。 -
ETA 为线性外推 + 历史均值融合:
- 线性分量 linear_s:已走过阶段的平均耗时 × 剩余阶段数;
-
历史分量 hist_s:同 cwd 目录下历史会话(排除当前监控会话)耗时的中位数
(每个历史会话耗时 = 事件流最大
time− 最小time;无历史/扫描失败时视为不可用); -
融合公式:
eta = α·linear_s + (1−α)·hist_s,α 随阶段进度自适应:k≥3 → α=0.7,k==2 → α=0.5,k<2 → α=0(纯历史均值); -
回退链(
eta_mode标记):有阶段信息(k≥2 且 n>k)且历史可用 →blend; 有阶段但无历史 → 纯linear(α=1);无阶段但有历史 → 纯history(α=0);都无 →none(不显示 ETA); - 历史会话每次全量扫描(会话数少,代价可接受),结果按会话目录 mtime 缓存, 4 秒轮询时 mtime 未变化直接复用 hist_s。
测试
python tests/make_fixtures.py # 生成合成 fixtures(无真实会话数据)
python tests/test_session_progress.py # 会话解析单测(无需 pytest),exit 0 全过
python tests/test_eta_blend.py # ETA 融合算法单测(历史会话 fixture 生成到临时目录)
python tests/test_multi_pane.py # 多任务分栏单测(1 小时窗口 / mtime 排序 / status 判定)
python tests/test_tail_format.py # tail 可读性单测(chunk 合并 / 时间戳 / 动作高亮 / 截断)
python tests/test_port.py # 端口冲突自动递增单测(socket 占用模拟,随机高位端口)
python tests/test_cache.py # 解析缓存单测(mtime 未变不重新解压 / 修改后重解析 / 缓存清理)
python tests/test_v11.py # v1.1 五项增强单测(标题 / 成本 / CLI --status / 飞书通知 / 时间线)
python tests/test_plugin_progress.py # 插件版数据源单测(优先级 / 回退 / status / 去重 / 窗口)
GitHub Actions CI(.github/workflows/ci.yml)在 push 到 main 与 pull_request 时,
以 ubuntu / macos / windows × Python 3.9 / 3.11 六种组合逐套运行上述单测。
常见问题
遇到「zstandard 未安装」「看不到任务」「端口被占用」「路径显示异常」「显示步骤N 而不是任务名」「任务完成后卡片还在」「如何卸载/停止看板」等问题,请查阅 docs/FAQ.md(7 问 7 答)。
文件结构
dsh-progress-viz/
├── .github/workflows/ci.yml # GitHub Actions CI(3 平台 × 2 Python 矩阵)
├── dashboard.py # 独立看板服务器(全库扫描 + 4s 轮询 + 解析缓存 + 多任务分栏 +
│ # ETA + 成本估算 + 任务标题 + 详情时间线 + 飞书完成通知 + CLI --status)
├── session_progress.py # 会话事件流 → 阶段解析器(纯本地)
├── index.html # 看板页面(深色主题,多任务网格分栏,标题/成本/详情时间线,5s 自动刷新)
├── pyproject.toml # 项目元信息 / 依赖声明(pip install . 用)
├── tests/
│ ├── make_fixtures.py # 合成 fixtures 生成器(含历史会话 fixture)
│ ├── test_session_progress.py # 单测(无需 pytest)
│ ├── test_eta_blend.py # ETA 融合算法单测
│ ├── test_multi_pane.py # 多任务分栏单测
│ ├── test_tail_format.py # tail 可读性单测
│ ├── test_port.py # 端口冲突自动递增单测
│ ├── test_cache.py # 解析缓存单测
│ ├── test_v11.py # v1.1 五项增强单测(标题/成本/CLI/飞书/时间线)
│ ├── test_plugin_progress.py # 插件版数据源单测(优先级/回退/status/去重/窗口)
│ └── fixtures/session-synthetic.jsonl.zstd
├── docs/
│ ├── FAQ.md # 常见问题(7 问 7 答)
│ └── screenshot.png # 看板效果图
├── README.md
├── LICENSE
└── requirements.txt
License
MIT(Copyright (c) 2026 dsh-progress-viz contributors)
Frequently Asked QuestionsFAQ
Use the verified command dsh plugin --profile default add github:2008924/dsh-progress-viz in a DSH-enabled shell. The command resolves the public package metadata and keeps the plugin attached to the catalog identity shown on this page.
Compatibility follows the bundle and profile status shown above. If a profile is not detected, keep the plugin disabled there and check the repository documentation before enabling it in production.
The GitHub link and activity metadata are the source of truth for releases and maintenance. Revisit this page after a new release to confirm the catalog has observed the latest version.