Back to home@zhm20001

dsh-usage-board

No description

Stars
0
Language
TypeScript
Created
Aug 27, 2026
Updated
Aug 27, 2026
GitHub repo

Introduction

dsh-usage-board

DSH(DeepSeek Harness / Cordis)的用量·成本·诊断面板插件:实时采集 token/耗时/错误指标,冷启动回溯全部历史会话,按 Sub-agent DAG 聚合,需要时按指针回查原文。

  • 架构:CQRS——DSH 会话文件是只读的 Write Model,本插件维护的 SQLite 是可重建的 Read Model。
  • 双层读模型:暖层 SQLite 物化汇总 + 热层进程内不可变快照,UI 一次 fetch 拿到渲染就绪的全帧。
  • 零依赖:零运行时依赖、零 Worker、零本地编译;仅用 Node ≥ 22.5 内置 node:sqlite

快速上手

npm install      # 只装 devDependencies(typescript 与 @types/node)
npm test         # node --test,全量测试(无网络、无外部进程)
npm run typecheck

作为库嵌入(结构化 HostContext,无需真实 Cordis 运行时):

import { createEngine } from './src/index.ts'

const engine = createEngine(ctx, { dbPath, sessionsRoot })
await engine.ready                       // 冷启动回溯完成(已就绪则秒开跳过)
const rollup = engine.query.getRollupBySession(rootSessionId)
const heat  = engine.query.getHeatmapData(12)
const fails = engine.query.getTimelineAnomalies(100)
const text  = engine.query.loadTurnDetails({ sessionFilePath, turnIndex })
await engine.dispose()                   // 逆序清理,幂等

双层读模型与快照端点

第二阶段在查询之上加预处理层:

  • 暖层:SQLite 物化汇总表 daily_rollups / session_rollups,按脏分区从 turns 整段重算;金额永不落库,由价目表现算。
  • 热层:进程内不可变快照主帧(零 Worker),一次 get() 拿到渲染就绪的全部汇总——KPI 总账、84 天热力图、24 小时峰谷剖面、byModel、最近会话、异常计数、计价信息(含 nextSwitchEpochMs 与反事实省额)、余额块。
  • revision 条件请求:写入事务原子递增 data_revision;UI 携带旧 revision 轮询,无新数据时端点应答 body 级 unchanged,不触发重渲染。

注入 ctx.webServer 时自动注册端点(回环 same-origin 限定;未注入时引擎作为库完整可用):

端点语义
GET /api/usage-dashboard/snapshot?rev=Nrev 命中 → {unchanged:true, revision};否则全量主帧(含 snapshotVersion
GET /api/usage-dashboard/snapshot?refresh=1余额透传:旁路 TTL 强制出网一次,必答全量新余额块
GET /api/usage-dashboard/turns?limit=N&format=csv最近 turn 详情帧(垃圾值/<1 回默认 500、>500 钳到 500);行含 toolMs(按 step 聚合工具时长,无调用为 null)与 delegationDepth(子代理深度);format=csv 出 CSV(列序 = JSON 字段序)
GET /api/usage-dashboard/turns?session_id=&root_session_id=按会话过滤(可选,可同给 = 交集):session_id 精确匹配该会话、root_session_id 匹配整棵会话树;垃圾值视同缺省 → 全库最近 N 条

UI 层

浏览器半(src/client/lib/client.js)经宿主 slot 系统挂载,只消费上表端点,不碰文件系统与 SQLite:

  • 挂载点(宿主 slot 注入,共三个):
    • conversation.view —— 会话页 "Usage" Tab(该会话树根聚合:KPI、五桶构成、模型占比、step 耗时瀑布);
    • sidebar.footer.action —— 侧栏底栏图标按钮,开合大盘;
    • shell.overlay —— 全屏大盘(余额卡 + 可烧天数、异常与反事实、84 天热力图、byModel、24 小时剖面、最近会话、详情表 + Export CSV)。
  • 数据层src/client/store/,纯函数、node --test 直测):手动刷新、无自动轮询——快照 ?rev= 条件请求(unchanged 应答不重渲染),?refresh=1 强制余额出网;turns 懒加载(?limit=&session_id=&root_session_id=,CSV 同端点);投影形选择器把真实帧投影成组件消费形状。
  • 开发工作流
    npm run build                                   # tsdown 双产物:lib/index.js(node 半)+ lib/client.js(浏览器半)
    npm run dev                                     # tsdown --watch(配合宿主 client-hmr 热替换)
    dsh plugin --profile web add "$(pwd)"           # 安装到 web profile(pnpm link + bundle 清单登记)
    dsh --profile web --port 3099                   # 起宿主;UI 打开 http://127.0.0.1:3099
    
    开发态改 src/client/** → watch 重编 lib/client.js → 宿主 stat-poll 触发 clientModules.rebuilt → SSE 推浏览器热替换;生产态 dsh web 按 rev(bundle sha256 前 12 位)服务 /plugins/<id>/client.js?rev=,内容变更即换址免缓存。

余额查询配置

大盘的余额卡读取 DeepSeek 官方 GET /user/balance,key 复用宿主凭证:在「设置 → 模型」里配置 DEEPSEEK_API_KEY,或写入 ~/.dsh/.credentials.yaml(文件权限须 0600,顶层 DEEPSEEK_API_KEY: <key>)。未配置时余额块显示占位态,其余功能不受影响(引擎中唯一出网组件就是余额服务,其余一切纯本地)。

数据位置

内容路径
投影库(Read Model)~/.dsh/data/usage.sqlite(0600,WAL)
真相源(Write Model,只读)~/.dsh/sessions/<projectKey>/<sessionId>/session.jsonl.zstd

删库重建

投影库可随时删除(先停用插件,再 rm ~/.dsh/data/usage.sqlite*)。下次启动 backfill 会重扫全部历史会话重建投影;正在进行的活会话由事件总线继续增量补齐。Write Model 永远不要删。

License

MIT