dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
本地优先的 DSH 用量分析:订阅会话事件,把工具调用、回合、错误、耗时与能力使用聚合为日表,在侧栏面板展示(6 个 KPI、14 日趋势、Top 工具、能力分布),并通过受围栏保护的本地 HTTP 接口暴露 18 个 JSON-RPC 方法。运行时零依赖,local 模式零出站,不采集任何消息正文。
一款给 DeepSeek Harness(DSH)用的本地用量分析插件:订阅会话事件,把工具调用、回合、错误、耗时与能力使用情况聚合成日表,在侧栏面板里展示,并对外提供一套本地 HTTP 查询接口。默认 local 模式、零出站,不采集任何消息正文。
配图说明:上图是本插件随包发布的
icon.svg(32×32,侧栏与插件列表共用)。面板截图需要在宿主加载插件后由本机浏览器截取,见docs/design-mapping.md的「验证状态」一节。
import node: 内置模块(node:crypto),不需要 TS/打包工具链,装上即可跑。meta.cursors[sid])+ fork 起点(inheritedEventCount)+ 回填去重,重启、重载、fork 都不会重复计数。flushIntervalMs(默认 10s)只把脏表落盘,写放大可控。storageDomain 时自动切内存档,面板与接口照常工作,只是重启即丢(diagnostics.storage.kind === 'memory')。collect:false 停止采集,selfExclude 排除本插件的工具调用,retentionDays 控制保留天数(同时裁剪内存与落盘)。Host 必须是回环名(127.0.0.1/localhost/[::1],挡 DNS rebinding)+ Sec-Fetch-Site + Origin/Host 一致性校验,写方法面每秒 10 次上限,请求体 64KB 上限。正路(推荐):
dsh plugin --profile <profile> add github:wjf1/dsh-plugin-usage-analytics
无需构建:package.json 声明了 dsh.bundle(patch: ./cordis.patch.yml)与 dsh.client(web UI),lib/ 产物随仓库提交。
离线安装 / 手工等价(本机当前就是这种装法,等价于上面三步):
node_modules(Junction 或直接拷贝):
C:\Users\admin\.dsh\profiles\desktop\node_modules\dsh-plugin-usage-analyticspackage.json 的 dsh.profile.bundles 数组里追加 "dsh-plugin-usage-analytics"(不要加进 dependencies,bundle 不需要 npm 解析)。装好后打开侧栏「用量分析」即可看到面板;GET /dsh-plugin-usage-analytics/api/ 应返回 {"ok":true,"value":{"ready":true,...}}。
写在 cordis.patch.yml 的 config 段(也可在运行时通过设置接口热改,仅白名单键):
| 键 | 默认 | 说明 |
|---|---|---|
mode | local | local 只落本地;remote/opt-in 为未来上报预留,当前不产生任何出站流量 |
collect | true | 总开关,false 时不再消费会话事件 |
sampleRate | 1 | 采样率(0..1),按 tool/call+tool/result 成对抽稀,丢弃量记在 meta.sampled |
retentionDays | 90 | 保留天数(1..400),定时任务按此裁剪 |
maxEventsPerDay | 200000 | 单日事件上限,超出记 meta.dropped |
flushIntervalMs | 10000 | 合并落盘间隔,最小 2000 |
layout | single | 存储布局,single 整单元重写 / per-record 逐条写(小数据量更稳,大数据量注意小文件数量) |
selfExclude | false | 是否排除本插件自身产生的调用 |
profileName | null | 存储 domain 的 profile 后缀。默认自动探测(config → profile 服务 → app.profile → DSH_PROFILE/DSH_PROFILE_NAME/DSH_PROFILE_DIR → --profile= → ctx.baseDir 的 profiles/<name> → default),显式指定可保证多 profile 隔离 |
| 检查项 | 结果 | 证据 |
|---|---|---|
| 宿主加载 bundle | ✅ | diagnostics.version 有值、routes 注册成功 |
| HTTP 前缀可达 | ✅ | GET /dsh-plugin-usage-analytics/api/ → ready:true(签名 cookie 鉴权后) |
| 真实数据聚合 | ✅ | 1181 次调用 / 22 次错误 / 4 会话 / tokensIn 3,354,030 / avgMs 3505 |
| 存储落盘 | ✅ | storage.kind=domain、writes=158、flushes=32、C:\Users\admin\.dsh\storages\plugin_analytics_default.json 14KB |
| JSON-RPC 面 | ✅ | POST / 的 diagnostics 返回统一信封 |
| 客户端包注册 | ✅ | GUI boot 清单含 dsh-plugin-usage-analytics/client.js |
| 面板肉眼验收 | ⚠️ | 需重启宿主后在 GUI 侧栏确认(会话内无浏览器控制权) |
侧栏入口「用量分析」,主面板包含:
collect 勾选、sampleRate 滑杆、retentionDays 数字框;配色只用宿主 --dsw-alias-* 主题 token(带回退值),跟随浅色/深色主题。
前缀 API_PREFIX = /dsh-plugin-usage-analytics/api。
POST <prefix>/,请求体 { "method": "...", "params": { ... } },响应 {ok:true,value} 或 {ok:false,error:{code,message,retryable?,detail?}}。
| method | params | 说明 |
|---|---|---|
overview | {range} | 4 KPI + 窗口汇总 + Top 工具 |
plugins | {range,q?,sort?,cursor?} | 工具/能力榜,每页 50 |
plugin | {pluginId,range} | 单工具逐日明细 |
capabilities | {range,type?,top?} | 能力排行(mcp/skill) |
env | {range} | 宿主与插件版本、存储状态 |
series | {range,metric} | 逐日序列(calls/errors/sessions/turns) |
events | {limit?} | 最近会话摘要(聚合粒度,见偏差说明) |
settings.get / settings.set | {} / {patch} | 读/改设置(白名单) |
consent.get / consent.set | {} / {mode,version} | 同意态(只支持 local/off) |
report.preview / report.now / report.clear | {} | 上报预览/立即/清空(未启用上报,返回空载荷) |
data.delete | {confirm:'DELETE'} | 删除本地全部数据 |
export | {range,format:'json'|'csv'|'ndjson'} | 内联导出 |
diagnostics | {} | 运行态诊断 |
reset | {} | 清空并重扫 |
range 接受数字、"7d"、{days}、{from,to},上限 366 天;错误码 E_ORIGIN/E_RATE/E_RANGE/E_PAYLOAD/E_CONSENT/E_METHOD/E_INTERNAL 映射到设计 §7.5 的状态码表。
GET /、/overview?days=、/series?days=、/sessions?limit=、/tools?days=、/tool?name=&days=、/settings、/diagnostics、/export(NDJSON 下载);POST /settings、/flush、/reset?confirm=RESET。与 JSON-RPC 共用同一批聚合函数。
分页上限统一由
lib/shared.js的PAGE_SIZE/MAX_PAGE_SIZE定义(REST 与 JSON-RPC 共用),/sessions默认 20 条。
POST /reset与 RPC 的reset都必须显式带确认(?confirm=RESET/params.confirm:"RESET"),否则回E_CONFIRM。
每个 profile 一个 domain:plugin_analytics_<profile 名>(storageDomain.open),默认 layout: 'single',落盘位置由宿主的 DSH_HOME/storages 决定。表:
meta:installId、settings、cursors、dropped、sampleddaily:按天的总量(toolCalls/toolErrors/turns/sessions/tokens/ms)toolDaily:day + tool 维度capDaily:day + 能力键 维度(MCP / skill)sessions:会话首见与累计(turns/toolCalls/errors/model)sha256(sessionId + ':' + installId) 截 16 位存储,卸载重装即换新 installId,历史无法互相拼接。local 模式,代码里没有任何出站请求(可 grep -R "http" lib/ 自证:只有 http://localhost 的 URL 解析基线)。无构建步骤,直接改 lib/*.js:
node --test test/store.test.mjs test/aggregate.test.mjs test/collector.test.mjs \
test/routes.test.mjs test/rpc.test.mjs test/index.test.mjs
75 条测试全通过。node --test test/(只给目录)在 Windows 上会报 Cannot find module ...\test,必须显式列文件。
见 docs/design-mapping.md:设计里的 plugins/env/events/report/SDK/上报通道在本实现中是明确降级(数据模型不含插件清单与逐事件行),逐条列了原因与替代方案。设计 v1.0 与评审报告不在本仓库内。
MIT。
A local-only usage analytics plugin for DeepSeek Harness. It subscribes to session events, aggregates tool calls, turns, errors, latencies and capability usage into daily rows, renders them in a sidebar panel, and exposes a fenced local HTTP API.
node: builtins; no build step.inheritedEventCount fork boundary + backfill dedupe.flushIntervalMs (default 10s).storageDomain is unavailable (diagnostics.storage.kind === 'memory').collect, selfExclude, sampleRate, retentionDays.Host header must be a loopback name (127.0.0.1/localhost/[::1] — this is what stops DNS rebinding), plus Sec-Fetch-Site/Origin checks, 10 writes per second, 64KB body cap.Image note:
icon.svg(32×32) ships with the package and is used by both the sidebar and the plugin list. Panel screenshots must be taken in a local browser after the host has loaded the plugin; see the "validation status" section ofdocs/design-mapping.md.
Preferred: dsh plugin --profile <profile> add github:wjf1/dsh-plugin-usage-analytics — package.json declares a dsh.bundle manifest (plus dsh.client for the web UI), so it installs with no build step.
Offline / manual equivalent:
node_modules (junction or plain copy):
~/.dsh/profiles/<profile>/node_modules/dsh-plugin-usage-analytics"dsh-plugin-usage-analytics" to dsh.profile.bundles in the profile's package.json (not to dependencies — a bundle needs no npm resolution).Once loaded, open "Usage Analytics" in the sidebar; GET /dsh-plugin-usage-analytics/api/ should return {"ok":true,"value":{"ready":true,...}}.
Keys are written under config in cordis.patch.yml (whitelisted keys are also hot-editable at runtime via the settings method):
| Key | Default | Meaning |
|---|---|---|
mode | local | local keeps everything on disk; remote/opt-in are reserved for future reporting and produce no outbound traffic today |
collect | true | Master switch; false stops consuming session events |
sampleRate | 1 | Sampling rate (0..1), thins tool/call+tool/result pairs; drops are counted in meta.sampled |
retentionDays | 90 | Retention window (1..400), pruned by a scheduled job |
maxEventsPerDay | 200000 | Per-day event cap; overflow is counted in meta.dropped |
flushIntervalMs | 10000 | Dirty-table flush interval, minimum 2000 |
layout | single | Storage layout: single rewrites the whole unit / per-record writes row by row (safer for small datasets, watch the file count when data grows) |
selfExclude | false | Exclude calls made by this plugin itself |
profileName | null | Profile suffix for the storage domain; auto-detected by default (config → profile service → app.profile → DSH_PROFILE/DSH_PROFILE_NAME/DSH_PROFILE_DIR → --profile= → profiles/<name> under ctx.baseDir → default). Set explicitly to guarantee isolation between profiles |
POST /dsh-plugin-usage-analytics/api/ with {method, params} — 18 design methods: overview, plugins, plugin, capabilities, env, series, events, settings.get/set, consent.get/set, report.preview/now/clear, data.delete, export, diagnostics, reset. Convenience REST GETs back the sidebar panel. Requests are gated by a loopback Host (blocks DNS rebinding) plus Sec-Fetch-Site and Origin/Host consistency, capped at 10 writes per second and 64KB per body. Both the REST /reset?confirm=RESET endpoint and the RPC reset method require an explicit confirmation.
| Check | Result | Evidence |
|---|---|---|
| Bundle loaded by host | ✅ | diagnostics.version populated, routes registered |
| HTTP prefix reachable | ✅ | GET /dsh-plugin-usage-analytics/api/ → ready:true (with signed cookie) |
| Real data aggregated | ✅ | 1181 calls / 22 errors / 4 sessions / tokensIn 3,354,030 / avgMs 3505 |
| Storage persisted | ✅ | storage.kind=domain, writes=158, flushes=32, a 14KB storage unit on disk |
| JSON-RPC surface | ✅ | POST / diagnostics returns the standard envelope |
| Client half registered | ✅ | GUI boot manifest lists dsh-plugin-usage-analytics/client.js |
| Panel eyeball check | ⚠️ | Needs a host restart, then a look at the GUI sidebar |
No build step — edit lib/*.js directly and run:
node --test test/store.test.mjs test/aggregate.test.mjs test/collector.test.mjs \
test/routes.test.mjs test/rpc.test.mjs test/index.test.mjs
75 tests, all passing. Passing only the test/ directory makes node --test fail on Windows (Cannot find module ...\test) — list the files explicitly.
See docs/design-mapping.md: the plugins/env/events/report methods, the SDK and the reporting channel are deliberately degraded in this implementation (the data model keeps no plugin list and no per-event rows); each case is listed with its reason and the substitute used instead. Design v1.0 and its review report live outside this repository.
Metadata only: tool name, capability class, ok/error, latency, short session hash, model name, turn/step counts, token counters. No message bodies, arguments, outputs, paths, or usernames. Local mode performs zero outbound requests.
MIT.