Repository navigation
Releases
v2.0.39
v2.0.39 — 紧急修复:v2.0.38 会让插件在启动时崩溃(Failed to load plugins)
v2.0.38 是坏的,请勿使用,直接装 2.0.39。 2.0.38 已在 npm 上被标记为弃用(deprecated)。
现象(真机实测)
另一台电脑装完 2.0.38、重启 dsh 后,界面直接报:
HARNESS
Failed to load plugins
@faith1688/dsh-usage-meter-harness
failed to apply loader entry 68ef7fc2 (@faith1688/dsh-usage-meter-harness):
cannot get property "locale" without inject
根因:类型转换不是探测
v2.0.38 为了让插件在任何组合下都能挂上,把客户端的 locale 从 inject 里删掉了(cordis 没有"可选依赖":声明了却没人提供,apply 就永远不执行)。但代码里仍然直接读 ctx.locale,只是外面套了一层 TypeScript 的类型转换:
const svc = (ctx as unknown as { locale?: … }).locale; // ← 编译期没事,运行时会抛ctx 在运行时是代理:读一个「已提供但没 inject」的服务名,它会直接抛错。类型转换只能骗过编译器,骗不过宿主。于是插件加载失败——比它想修的问题更糟。
修法:所有可选服务一律走真正的探测(与服务端 webServer/llm 同一套):
function readOptionalService(ctx, name) {
// 优先用反射助手 ctx.get(name);没有它时退化到带 try/catch 的属性读
}新增两道回归防线(这次的教训是"验证没覆盖到就一定会漏")
scripts/test-inject-contract.mjs—— 用 TypeScript AST 解析lib/*.js:凡是ctx.<已知服务名>的属性访问,必须在同文件的inject里;否则 FAIL。已用它跑过坏的 2.0.38 产物,确认能报出ctx.locale(先证明检查有效,再看它变绿)。scripts/test-client-apply.mjs—— 用一个复刻 cordis 行为的代理 ctx,真的跑一遍client.tsx的apply:读未 inject 的服务会抛错,ctx.get()不抛。内含元测试先证明这个替身真的会抛,再断言真实apply跑通、两个席位都注册、locale只走探测路径。
验证(发布前实跑)
tsc --noEmit、npm run bundle零报错。scripts/test-inject-contract.mjs→ ALL PASSED(对坏产物先 FAIL 过,证明有效)。scripts/test-client-apply.mjs→ ALL PASSED(含两条元测试)。- 既有回归:
test-live-rate/test-usage-accounting/test-shared-balance-lock/sim-live-balance全 PASS。 - 旧版 DSH 0.1.2 真机:web 组合 5 个端点全 200;headless 组合只出现降级说明、不谎报。
注意事项
- 装完/更新完必须重启
dsh web。 - 发布后 24 小时内,从插件市场更新可能被 pnpm 冷静期拦下并自动回滚——等 24h,或把包名加入
minimumReleaseAgeExclude,或用npx -y @faith1688/dsh-usage-meter-harness@latest。
v2.0.39 — English
Hotfix: v2.0.38 broke plugin startup (Failed to load plugins). Do not use 2.0.38 — install 2.0.39. 2.0.38 is marked deprecated on npm.
Symptom: after installing 2.0.38 and restarting dsh, the harness failed with failed to apply loader entry … cannot get property "locale" without inject.
Cause: v2.0.38 removed locale from the client inject list (cordis has no optional inject: a declared service nobody provides parks apply forever) but kept reading ctx.locale directly, hidden behind a TypeScript cast. At runtime ctx is a proxy that throws when you read a provided-but-not-injected service; a cast fools the compiler, not the host. The plugin failed to load — worse than the problem it set out to fix.
Fix: every optional service now goes through a real probe (same helper shape the host side uses for webServer/llm): prefer the ctx.get(name) reflection helper, fall back to a guarded property read.
Two new regression guards (the lesson: a gap in verification will be found in production):
scripts/test-inject-contract.mjs— AST-based check overlib/*.js: anyctx.<known service>property read must be present in that file'sinjectarray. Verified to FAIL against the broken 2.0.38 artifact before going green on the fix.scripts/test-client-apply.mjs— runs the realclient.tsxapplyagainst a proxy ctx that reproduces cordis semantics (reading a non-injected service throws;ctx.get()does not), with meta-tests proving the double actually throws.
Verification: tsc/bundle clean; both new checks pass (contract check demonstrably fails on the broken build); existing regressions (test-live-rate, test-usage-accounting, test-shared-balance-lock, sim-live-balance) all pass; DSH 0.1.2 real host: web composition returns 200 on all five endpoints, headless degrades with an explicit notice and no false "registered".
Notes: restart dsh web after installing. Within 24h of release the marketplace update may be blocked by the pnpm release-age cooldown.
v2.0.35
v2.0.35 — 从 1.0.34 升级:全部新增功能一览
npm 上的上一个发布版本是 1.0.34。本次发布 2.0.35,使用 1.0.34 的用户升级后会一次性获得下面全部更新(v1.0.35 → v2.0.35 一整条线)。
安装/升级命令见文末。
一、新增功能
主题与外观(2.0.x → 2.0.34)
- 20 个内置主题:浅色系在前、深色系在后,默认
forest-beach-dawn(林滩晓色)。用量弹窗顶部⇄直接快切,设置页「主题设置」有完整配色预览。 - 每主题可自定义配色:胶囊色、呼吸光晕色、预警三色(余额足 / 不足 / 透支)、速度三档色(0-50 / 51-100 / 101+ tokens/s);改动即时保存到本机,切换主题不丢。
- 胶囊身份色 = 主题色(2.0.30):峰 / 谷 / 平时态跟随主题 brand 家族渲染,不再是写死的固定色。
- 峰谷胶囊 + 峰谷徽章(2.0.29 / 2.0.31):谷价浅绿、峰价浅红,计费中带柔和呼吸光晕,一眼看出当前是峰价还是谷价。
- 胶囊按余额三档着色(2.0.32):余额足 / 不足 / 透支三色,「余额足」的颜色可在设置页单独调。
用量与看板
- 用量展板(1.0.36):弹窗右上角
▦打开,三种视图 —— 全部 / 按供应商 / 按模型;数据来自服务端跨会话累计的/api/usage-meter/stats。 - 当前会话看板(2.0.x):弹窗默认展开「当前会话」—— 总计 + 分项金额 + 每轮费用明细;坐标标签用
provider · model,同名官方/自定义模型不会混淆。 - 余额预警阈值 + 呼吸色(1.0.36):全局「预算预警 %」与「余额预警下限」;模型与供应商可各自覆盖(其他设置 → 预警)。触发时输入框旁的胶囊呼吸变红。官方模型按实时余额金额比对,其他模型按预算百分比。
计费
- 一键同步官方价格(2.0.x):设置页一键抓取 DeepSeek 官网价格页,解析最新单价与峰谷时段并写入覆盖,随时保持官方价最新(需先填全局 API Key)。
- 计费模板导出 / 导入(1.0.35):导出到共享持久目录(默认
$DSH_HOME/usage-meter/exports),支持覆盖 / 重命名 / 复制 / 删除 —— 多机、多 profile 共用同一套计费模板。
设置与易用性
- 设置页选项卡化(1.0.36):余额预警 / 主题设置 / 模型后缀识别 / 工作目录设置分区,找设置不用长滚动。
- 视觉包装识别可配(1.0.36):逐行「标记 + 开关」配置模型名前缀 / 后缀,可增行、删行,每行独立生效。
- 中英双语三选项(2.0.x):跟随系统 / 中文 / English,选择自动记住,设置页与弹窗全部即时生效,英文界面完整(389 条英文词条)。1.0.34 只能「中文 / English」二选一,且不记住选择。
- 未保存 / 已保存 状态(2.0.x):模型卡编辑后显示「未保存」,保存后显示「已保存」——不再有含糊的「保存中」。
- DS API Key 守卫(2.0.x):非官方模型绝不会回落使用 DeepSeek 官方全局 key,未设置时明确显示「未配置」。
- 「本对话费用」文案(2.0.x):会话级费用的措辞更明确。
二、计费准确性修复
- 回合归因(2.0.14 + 2.0.35):会话中途切换模型时,正在进行的回合不再沿用上一回合的模型标签。实测问题:切到
deepseek-flash后正在跑的第 80 轮仍显示旧模型(ngrok-ollama/Qwen3.8-27B)。现在识别 DSH 官方model/selection事件,回合归属 = 本回合第一个真正产生用量的请求,定标后永不改写。 - 回合币种(2.0.35):此前
currency参数从未真正生效,回合币种恒为 CNY —— 美元定价的回合总额少换算一个汇率。现在以本回合首个计费用量的定价币种定标。 - 退化峰谷窗口(2.0.35):手工或 API 写入的畸形窗口
{start === end}是空集,此前判定恰好铺满全天 → 全天都按峰价计费。现已视作无窗口。 - 看板统计漏计(2.0.35):统计改用
(turn, step)基线去重,修复三处漏计 —— 会话中途切模型后该会话看板样本全丢、新 step 首段用量被 clamp 成 0、相邻两步等值样本被误判为重复。 - 统一计费时钟(2.0.35):看板此前按 usage 到达时刻定价,与钱包按 step 起始时刻不一致 —— 11:59(峰)发起、12:01(谷)才收单的请求,两本账会对不上。现已统一为 step 起始时刻。
- 中文 token 速度(2.0.24):token 估算改为 CJK 感知(CJK 字符 = 1,其余 /4),此前中文速度约为真实值的 1/4。
三、升级方式(从 1.0.34 升上来必看)
从 1.0.34 升到 2.0.35 必须显式带版本号,否则 pnpm 可能因 profile 里的旧锁定直接跳过:
dsh plugin --profile web add @faith1688/dsh-usage-meter-harness@2.0.35或从 profile 目录内(~/.dsh/profiles/web):
pnpm update @faith1688/dsh-usage-meter-harness若提示 Already up to date / downloaded 0(profile 里是 file: 或固定绑定),改用一键安装器强制改写绑定:
npx -y @faith1688/dsh-usage-meter-harness@latest升级后重启 dsh web。注意:重启本身不会拉新版本,必须先把新版本装进 node_modules。
English
v2.0.35 — everything new since 1.0.34. The previous npm release was 1.0.34; this release is 2.0.35, so upgrading brings the entire line at once (v1.0.35 → v2.0.35).
New features
Themes & appearance
- 20 built-in themes (light first, dark after; default
forest-beach-dawn) — quick switch via⇄at the top of the usage popup, full palette preview on the settings page. - Per-theme custom colors: pill, breathing glow, alert tiers (balance ok / low / overdrawn) and speed tiers (0-50 / 51-100 / 101+ tokens/s); saved locally, survive theme switches.
- Pill identity colour = theme colour (2.0.30): peak / off-peak / flat states render from the theme brand family instead of hard-coded colours.
- Peak/off-peak pill + badge (2.0.29 / 2.0.31): light green off-peak, light red peak, with a soft breathing glow while billing.
- Balance-tier pill colour (2.0.32), with the "balance ok" colour configurable.
Usage & dashboard
- Usage dashboard (1.0.36): opens from
▦in the popup; three views — all / by provider / by model — served from cross-session/api/usage-meter/stats. - Current-session board (2.0.x): the popup opens straight into the current session — total + per-category cost + per-turn ledger, labelled
provider · model. - Balance alert thresholds + breathing colour (1.0.36): global budget-alert % and balance floor, overridable per model/provider; the pill breathes red when tripped.
Billing
- One-click official price sync (2.0.x): fetches the DeepSeek pricing page, parses the latest prices and peak windows and writes them as overrides.
- Billing-config export / import (1.0.35): exports to a shared persistent directory (
$DSH_HOME/usage-meter/exportsby default) with overwrite / rename / duplicate / delete.
Settings & UX
- Tabbed settings page (1.0.36): balance alerts / theme / model-suffix recognition / working directory.
- Configurable vision-wrapper recognition (1.0.36): per-line marker + toggle (model prefix / suffix), add or remove rows.
- Three-way language switch (2.0.x): follow system / 中文 / English, remembered, applied live everywhere; the English UI is complete (389 entries). 1.0.34 only offered 中文 / English and did not remember the choice.
- Unsaved / Saved state (2.0.x) on model cards; DS API Key guard (custom models never fall back to the official global key); clearer "this conversation" cost wording.
Accuracy fixes
- Turn attribution (2.0.14 + 2.0.35): mid-session model switches no longer mislabel the running turn — attribution follows the first usage-producing request, recognising the official
model/selectionevent. - Turn currency (2.0.35): the
currencyargument never took effect, so turns were always CNY — USD-priced turns were short by one exchange rate. Now fixed at the turn's first billed usage. - Degenerate peak window (2.0.35):
{start === end}used to fill the whole day at peak price; now treated as no window. - Dashboard under-counting (2.0.35): stats dedupe on a
(turn, step)baseline, fixing three lost-usage paths. - One billing clock (2.0.35): dashboard and wallet now both price from the step start time.
- Chinese token speed (2.0.24): CJK-aware token estimation (CJK = 1, others /4); Chinese throughput used to read ~1/4 of the real value.
Upgrading from 1.0.34
dsh plugin --profile web add @faith1688/dsh-usage-meter-harness@2.0.35If your profile pins the plugin via file: or an exact version, use the one-line installer instead: npx -y @faith1688/dsh-usage-meter-harness@latest. Then restart dsh web — a restart alone never fetches a new version.
v1.0.34
v1.0.34: fix duplicate billing when a request reports usage via both assistant/chunk and assistant/message (issue #1).
- usageEventOf chunk branch reads turn/step from the host's OUTER event.data (previously chunk.turn ?? 0 → (0,0)), so chunk + final message dedup to one charge.
- ignore all-zero init samples (must not clobber the running baseline).
- clamp per-bucket delta to >= 0 (no negative tokens/cost on retry or smaller correction samples).
- corrected the sim's chunk event shape (was also turn/step nested) and added regression phases 9-11.
- Regression: scripts/sim-live-balance.mjs