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.
- package/README.en.md +10 -8
- package/README.md +10 -8
- package/lib/client.js +2 -2
- package/lib/coding-plans.js +2 -1
- package/lib/gateway-quota-adapters.js +6 -2
- package/lib/index.js +3 -1
- package/lib/store.js +23 -0
- package/lib/typert.host.js +2 -0
- package/package.json +11 -4
package/lib/coding-plans.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
`${
|
|
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)。
|
package/lib/typert.host.js
CHANGED
|
@@ -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.
|
|
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
|
}
|