dsh-cost-meter 1.7.13 → 1.7.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -903,7 +903,8 @@ export function qwenTokenPlanWindows(days, entry, nowMs) {
903
903
  const total = Number(entry?.planCredits)
904
904
  if (!Number.isFinite(total) || total <= 0) return null
905
905
  // 用户覆盖的抵扣率优先,内置表兜底;键与模型名均做字母数字归一比较。
906
- const overrides = entry?.rates !== null && typeof entry?.rates === 'object' && !Array.isArray(entry?.rates) ? entry.rates : {}
906
+ const rawOverrides = entry?.rates !== null && typeof entry?.rates === 'object' && !Array.isArray(entry?.rates) ? entry.rates : {}
907
+ const overrides = Object.fromEntries(Object.entries(rawOverrides).map(([model, rate]) => [scnetCanonModelId(model), rate]))
907
908
  const rateOf = canon => {
908
909
  if (overrides[canon] !== undefined && overrides[canon] !== null && typeof overrides[canon] === 'object') return overrides[canon]
909
910
  return QWEN_CREDIT_RATES_BY_CANON[canon]
@@ -270,7 +270,11 @@ export function parseAntigravityQuota(payload, ctx = {}) {
270
270
  for (let gi = 0; gi < groups.length; gi++) {
271
271
  const group = groups[gi]
272
272
  if (group === null || typeof group !== 'object' || Array.isArray(group)) continue
273
- const groupId = slug(pickField(group, 'groupId', 'group_id', 'id', 'name') ?? `group-${gi}`)
273
+ const explicitId = pickField(group, 'groupId', 'group_id', 'id')
274
+ const rawGroupTitle = pickField(group, 'displayName', 'display_name', 'name') ?? explicitId
275
+ let groupTitle = rawGroupTitle ? String(rawGroupTitle).replace(/\s+models$/i, '').trim() : `group-${gi}`
276
+ if (groupTitle.toLowerCase().includes('claude') && groupTitle.toLowerCase().includes('gpt')) groupTitle = 'Claude / GPT'
277
+ const groupId = slug(explicitId ?? groupTitle)
274
278
  const buckets = Array.isArray(group.buckets) ? group.buckets : []
275
279
  let added = 0
276
280
  for (const bucket of buckets) {
@@ -288,7 +292,7 @@ export function parseAntigravityQuota(payload, ctx = {}) {
288
292
  const percent = clampPct((1 - fraction) * 100)
289
293
  windows.push(makeWindow(
290
294
  id,
291
- `${groupId} · ${meta.label}`,
295
+ `${groupTitle} · ${meta.label}`,
292
296
  percent,
293
297
  absoluteResetAt(pickField(bucket, 'resetTime', 'reset_time', 'resetTime')),
294
298
  meta.periodHours,
package/lib/index.js CHANGED
@@ -2668,6 +2668,8 @@ export function apply(ctx) {
2668
2668
  // 指纹窗口去重:包装层样本改挂上游 id 后,与上游真实流按 (model, 五桶指纹)
2669
2669
  // 10s 窗互斥入账,无论到达顺序只记一次;包装层单链照常入账(不再漏计)。
2670
2670
  const usageDeduper = createUsageDeduper()
2671
+ // 记忆插件等可创建隔离的 llm 服务;账本是全局观察者,须跨服务作用域接收用量。
2672
+ // 子会话仍按自己的 sessionId 入账;无 sessionId 的后台调用只进入日/月总计。
2671
2673
  ctx.on('llm/stream', createLlmStreamBilling({
2672
2674
  account: (usage, model, sessionId, atMs, provider) => {
2673
2675
  const buckets = {
@@ -2683,7 +2685,7 @@ export function apply(ctx) {
2683
2685
  if (effective === null) return
2684
2686
  ledger.account(buckets, model, sessionId, atMs, effective)
2685
2687
  },
2686
- }))
2688
+ }), { global: true })
2687
2689
 
2688
2690
  // costUsage 投影:向会话历史页/推送帧提供 token 桶(客户端计价)。
2689
2691
  ctx.inject(['sessionProjections'], (projectionCtx) => {
package/lib/store.js CHANGED
@@ -135,9 +135,11 @@ export function defaultConfig() {
135
135
  hideOfficialBalance: false, // 隐藏官方账户余额(issue #45):开启后侧边栏/会话页/设置页的官方余额 UI 整体不渲染
136
136
  hideTodayCost: false, // 隐藏今日消耗金额(issue #46):开启后侧栏今日费用行/预算明细今日行/概览今日卡片不渲染
137
137
  showTotalWithPlan: false, // 「含 Plan 总额」全局开关(v1.6.0):开启后全部金额展示按总等值(cost)计;默认按真金白银(apiCost)
138
+ sidebarStyle: 'standard', // 侧边栏进度条样式:standard(标准,纵向单列) | compact(紧凑,额度卡两列网格)
138
139
  legacyAutoImportedAt: 0, // 安装前历史自动导入标记(issue #27):完成时刻 ms;0 = 尚未跑过
139
140
  peakStyle: 'compact', // 峰谷时段条样式:compact(简洁单行/竖向同构) | classic(经典分段/胶囊芯片)
140
141
  priceMatch: 'auto', // 未知模型名自动匹配价格表:auto(去后缀/前缀/家族相似) | exact(仅精确)
142
+ priceMatchDismissed: [], // 从匹配设置中移除的 provider:model 行;不删除用量或改变计价规则。
141
143
  priceOverrides: {}, // 手动匹配覆盖:{ 'provider:modelId': 'provider:模型 | deepseek:__default__' };裸模型名 = 同渠道换名(旧版遗留,跨渠道裸 DeepSeek 名由查价兜底自愈,issue #56)
142
144
  priceTableDisplay: {}, // 费用设置直接显示(按模型):键 'provider:modelId' → 布尔;缺省 = DeepSeek 模型直接显示、第三方收入拓展价格表(含 DeepSeek 模型也可逐模型收入)
143
145
  prices: {
@@ -722,9 +724,11 @@ const VALIDATION_MESSAGES = {
722
724
  hideOfficialBalance: 'hideOfficialBalance 必须是布尔值',
723
725
  hideTodayCost: 'hideTodayCost 必须是布尔值',
724
726
  showTotalWithPlan: 'showTotalWithPlan 必须是布尔值',
727
+ sidebarStyle: 'sidebarStyle 必须是 standard / compact',
725
728
  peakStyle: 'peakStyle 必须是 compact / classic',
726
729
  priceMatch: 'priceMatch 必须是 auto / exact',
727
730
  priceOverrides: 'priceOverrides 必须是字符串→字符串映射',
731
+ priceMatchDismissed: 'priceMatchDismissed 必须是模型键字符串数组(最多 1000 项,每项 1–512 字符)',
728
732
  historyDays: 'historyDays 必须是 7-3650 的整数',
729
733
  locale: 'locale 必须是 auto / zh / en',
730
734
  budget: 'budget 非法',
@@ -818,9 +822,11 @@ const VALIDATION_MESSAGES = {
818
822
  hideOfficialBalance: 'hideOfficialBalance must be a boolean',
819
823
  hideTodayCost: 'hideTodayCost must be a boolean',
820
824
  showTotalWithPlan: 'showTotalWithPlan must be a boolean',
825
+ sidebarStyle: 'sidebarStyle must be standard / compact',
821
826
  peakStyle: 'peakStyle must be compact / classic',
822
827
  priceMatch: 'priceMatch must be auto / exact',
823
828
  priceOverrides: 'priceOverrides must be a string→string map',
829
+ priceMatchDismissed: 'priceMatchDismissed must contain at most 1000 model keys of 1–512 characters',
824
830
  historyDays: 'historyDays must be an integer from 7 to 3650',
825
831
  locale: 'locale must be auto / zh / en',
826
832
  budget: 'Invalid budget',
@@ -990,6 +996,12 @@ export function applyConfigPatch(current, rawPatch) {
990
996
  if (errors.length > 0) return { config: current, errors }
991
997
  // 结构化克隆,拒绝补丁时不得污染活配置(mergeDeep 共享未触及子树,校验期的就地收敛会泄漏进活配置)。
992
998
  const candidate = mergeDeep(structuredClone(current), patch)
999
+ // 这两张编辑表随对应设置整表提交;逐键深合并会把用户移除的键补回来。
1000
+ if (Object.hasOwn(patch, 'priceOverrides')) candidate.priceOverrides = structuredClone(patch.priceOverrides)
1001
+ const qwenPatch = patch.codingPlans?.qwen
1002
+ if (qwenPatch !== null && typeof qwenPatch === 'object' && !Array.isArray(qwenPatch) && Object.hasOwn(qwenPatch, 'rates')) {
1003
+ candidate.codingPlans.qwen.rates = structuredClone(qwenPatch.rates)
1004
+ }
993
1005
  // prices.models 是可编辑列表:客户端提交完整列表时必须按替换语义处理,
994
1006
  // 否则 mergeDeep 会把已删除的旧模型重新合并回来。
995
1007
  if (patch.prices !== null && typeof patch.prices === 'object' && !Array.isArray(patch.prices)) {
@@ -1092,9 +1104,16 @@ export function applyConfigPatch(current, rawPatch) {
1092
1104
  if (candidate.hideTodayCost !== undefined && typeof candidate.hideTodayCost !== 'boolean') errors.push(vmsg(locale, 'hideTodayCost'))
1093
1105
  // 「含 Plan 总额」全局开关(v1.6.0):布尔,可缺省(默认关)。
1094
1106
  if (candidate.showTotalWithPlan !== undefined && typeof candidate.showTotalWithPlan !== 'boolean') errors.push(vmsg(locale, 'showTotalWithPlan'))
1107
+ // 侧边栏进度条样式:standard(默认,纵向单列) | compact(额度卡两列网格)。
1108
+ if (candidate.sidebarStyle !== undefined && candidate.sidebarStyle !== 'standard' && candidate.sidebarStyle !== 'compact') errors.push(vmsg(locale, 'sidebarStyle'))
1095
1109
  if (candidate.peakStyle !== 'compact' && candidate.peakStyle !== 'classic') errors.push(vmsg(locale, 'peakStyle'))
1096
1110
  if (candidate.priceMatch !== 'auto' && candidate.priceMatch !== 'exact') errors.push(vmsg(locale, 'priceMatch'))
1097
1111
  const overrides = candidate.priceOverrides
1112
+ if (candidate.priceMatchDismissed !== undefined && (!Array.isArray(candidate.priceMatchDismissed)
1113
+ || candidate.priceMatchDismissed.length > 1000
1114
+ || candidate.priceMatchDismissed.some(key => typeof key !== 'string' || key.length === 0 || key.length > 512))) {
1115
+ errors.push(vmsg(locale, 'priceMatchDismissed'))
1116
+ }
1098
1117
  if (overrides === null || typeof overrides !== 'object' || Array.isArray(overrides)
1099
1118
  || Object.entries(overrides).some(([k, v]) => typeof k !== 'string' || typeof v !== 'string')) {
1100
1119
  errors.push(vmsg(locale, 'priceOverrides'))
@@ -1392,6 +1411,10 @@ export function sanitizeConfig(raw) {
1392
1411
  out.hideOfficialBalance = out.hideOfficialBalance === true
1393
1412
  out.hideTodayCost = out.hideTodayCost === true
1394
1413
  out.showTotalWithPlan = out.showTotalWithPlan === true
1414
+ out.sidebarStyle = oneOf(out.sidebarStyle, ['standard', 'compact'], 'standard')
1415
+ out.priceMatchDismissed = Array.isArray(out.priceMatchDismissed)
1416
+ ? [...new Set(out.priceMatchDismissed.filter(key => typeof key === 'string' && key.length > 0 && key.length <= 512))].slice(0, 1000)
1417
+ : []
1395
1418
  // v1.5.38 的隐私模式字段已废弃(改为 UI 隐藏开关):清洗旧账本残留,避免僵尸配置长期留存。
1396
1419
  delete out.hideAmounts
1397
1420
  // 峰/谷切换弹窗提醒:非法值定向收敛(开关默认开、提前量回 2、类型回 both)。
@@ -119,6 +119,8 @@ const configSchema = z.object({
119
119
  hideTodayCost: z.boolean().optional(),
120
120
  // 「含 Plan 总额」全局开关(v1.6.0):开启后全部金额展示按总等值(cost)计。
121
121
  showTotalWithPlan: z.boolean().optional(),
122
+ sidebarStyle: z.enum(['standard', 'compact']).optional(),
123
+ priceMatchDismissed: z.array(z.string()).optional(),
122
124
  // 安装前历史自动导入完成时刻(issue #27,内部标记;0/缺席 = 尚未跑过)。
123
125
  legacyAutoImportedAt: num.optional(),
124
126
  peakStyle: z.enum(['compact', 'classic']).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-cost-meter",
3
- "version": "1.7.13",
3
+ "version": "1.7.14",
4
4
  "description": "DeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(9 家,含 SCNet Token Plan 本地 Credits 计量与火山方舟 Volcano Ark Coding Plan AK/SK 签名查询)、自定义 Provider 余额查询(可配任意 HTTP 端点)与余额进度条、峰谷计价时段显示与切换前弹窗/系统通知提醒(位置/提前量/类型可配),界面中英双语。Session cost tracking plugin for DeepSeek Harness: per-conversation cost, daily totals, history and official price sync, with multi-vendor model pricing (built-in 90+ model catalog with auto-matching), Coding Plan quota queries & display for 9 vendors (incl. local credits metering for the SCNet Token Plan and Volcano Ark Coding Plan via AK/SK signature), plan/API dual-track billing with per-1% & full-window Token Plan usage estimates, custom provider balance lookup (configurable HTTP endpoint) and balance progress bar, plus peak/off-peak pricing display with pre-switch popup & system-notification alerts (position / lead time / type configurable), in a bilingual (Chinese/English) UI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -24,7 +24,10 @@
24
24
  "dshReleases": {
25
25
  "0.1.2-alpha.3": "compatible",
26
26
  "0.1.2-alpha.4": "compatible",
27
- "0.1.2-alpha.5": "compatible"
27
+ "0.1.2-alpha.5": "compatible",
28
+ "0.1.2-rc.1": "compatible",
29
+ "0.1.3-alpha.1": "unknown",
30
+ "0.1.3-alpha.2": "compatible"
28
31
  }
29
32
  }
30
33
  },
@@ -80,10 +83,12 @@
80
83
  "docs/provider-pricing.json"
81
84
  ],
82
85
  "dependencies": {
83
- "@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
84
- "@deepseek-ai/dsh-home-paths": "0.1.0-rc.8",
85
86
  "zod": "4.5.1"
86
87
  },
88
+ "peerDependencies": {
89
+ "@deepseek-ai/dsh-credentials": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0",
90
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0"
91
+ },
87
92
  "repository": {
88
93
  "type": "git",
89
94
  "url": "git+https://github.com/Han-1413141/dsh-cost-meter.git"
@@ -115,6 +120,8 @@
115
120
  "test": "node test/verify.mjs"
116
121
  },
117
122
  "devDependencies": {
123
+ "@deepseek-ai/dsh-credentials": "0.1.0-rc.8",
124
+ "@deepseek-ai/dsh-home-paths": "0.1.0-rc.8",
118
125
  "esbuild": "^0.28.2"
119
126
  }
120
127
  }