dsh-cost-meter 1.5.5

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.
@@ -0,0 +1,402 @@
1
+ /**
2
+ * dsh-cost-meter 的 Host 面 Typert 清单(由 typert-loader 自动扫描注册)。
3
+ * 手写清单,结构与 @deepseek-ai/dsh-typert-generator 产物一致:
4
+ * `./typert` 导出 TYPERT,invocations 的 codec 必须是 zod v4 实例。
5
+ */
6
+
7
+ import { z } from 'zod'
8
+
9
+ const num = z.number()
10
+
11
+ const sessionSchema = z.object({
12
+ id: z.string(),
13
+ input: num,
14
+ output: num,
15
+ cacheRead: num,
16
+ cacheWrite: num,
17
+ reasoning: num.optional(),
18
+ calls: num,
19
+ cost: num,
20
+ byProviderModel: z.record(z.string(), z.object({ input: num, output: num, cacheRead: num, cacheWrite: num, reasoning: num.optional(), calls: num, cost: num })).optional(),
21
+ })
22
+
23
+ const daySchema = z.object({
24
+ date: z.string(),
25
+ input: num,
26
+ output: num,
27
+ cacheRead: num,
28
+ cacheWrite: num,
29
+ reasoning: num.optional(),
30
+ calls: num,
31
+ cost: num,
32
+ byProviderModel: z.record(z.string(), z.object({ input: num, output: num, cacheRead: num, cacheWrite: num, reasoning: num.optional(), calls: num, cost: num })).optional(),
33
+ sessions: z.array(sessionSchema),
34
+ })
35
+
36
+ const priceTierSchema = z.object({
37
+ cacheHit: num,
38
+ cacheMiss: num,
39
+ output: num,
40
+ reasoning: num.optional(),
41
+ })
42
+
43
+ const providerPriceSchema = z.object({
44
+ input: num.optional(),
45
+ cachedInput: num.optional(),
46
+ cacheRead: num.optional(),
47
+ cacheWrite: num.optional(),
48
+ cacheCreation5m: num.optional(),
49
+ cacheCreation1h: num.optional(),
50
+ output: num.optional(),
51
+ reasoning: num.optional(),
52
+ unpriced: z.boolean().optional(),
53
+ billingMode: z.enum(['flat', 'deepseek-peak', 'batch']).optional(),
54
+ sourceUrl: z.string().optional(),
55
+ checkedAt: z.string().optional(),
56
+ notes: z.string().optional(),
57
+ })
58
+
59
+ /** 拓展价格目录条目:兼容三桶价(DeepSeek,含峰谷子档)与两档简写/未核价(第三方)。 */
60
+ const catalogEntrySchema = providerPriceSchema.extend({
61
+ cacheHit: num.optional(),
62
+ cacheMiss: num.optional(),
63
+ unpriced: z.boolean().optional(),
64
+ legacy: z.boolean().optional(),
65
+ offPeak: priceTierSchema.optional(),
66
+ peak: priceTierSchema.optional(),
67
+ legacyBase: priceTierSchema.optional(),
68
+ })
69
+
70
+ const priceSchema = z.object({
71
+ cacheHit: num,
72
+ cacheMiss: num,
73
+ output: num,
74
+ reasoning: num.optional(),
75
+ billingMode: z.enum(['flat', 'deepseek-peak', 'batch']).optional(),
76
+ offPeak: priceTierSchema.optional(),
77
+ peak: priceTierSchema.optional(),
78
+ legacy: z.boolean().optional(),
79
+ legacyBase: priceTierSchema.optional(),
80
+ sourceUrl: z.string().optional(),
81
+ checkedAt: z.string().optional(),
82
+ notes: z.string().optional(),
83
+ })
84
+
85
+ const configSchema = z.object({
86
+ locale: z.enum(['auto', 'zh', 'en']),
87
+ position: z.enum(['dock', 'header', 'off']),
88
+ sidebar: z.boolean(),
89
+ currency: z.string(),
90
+ symbol: z.string(),
91
+ decimals: num,
92
+ exchangeRate: num,
93
+ peakEnabled: z.boolean(),
94
+ peakEffectiveAt: z.string(),
95
+ peakWindows: z.array(z.object({ start: num, end: num })),
96
+ peakNotice: z.boolean().optional(),
97
+ peakStyle: z.enum(['compact', 'classic']).optional(),
98
+ priceMatch: z.enum(['auto', 'exact']).optional(),
99
+ priceOverrides: z.record(z.string(), z.string()).optional(),
100
+ priceTableDisplay: z.record(z.string(), z.boolean()).optional(),
101
+ prices: z.object({
102
+ models: z.record(z.string(), priceSchema),
103
+ default: priceSchema,
104
+ providers: z.record(z.string(), z.object({ models: z.record(z.string(), providerPriceSchema) })).optional(),
105
+ }),
106
+ budget: z.object({
107
+ enabled: z.boolean(),
108
+ amount: num,
109
+ period: z.enum(['day', 'month', 'all', 'custom']),
110
+ customStart: z.union([z.string(), z.null()]),
111
+ customEnd: z.union([z.string(), z.null()]),
112
+ detail: z.boolean(),
113
+ }),
114
+ codingPlans: z.record(z.string(), z.object({
115
+ enabled: z.boolean().optional(),
116
+ display: z.enum(['sidebar', 'settings', 'both', 'off']).optional(),
117
+ refreshMinutes: num.optional(),
118
+ apiKey: z.string().optional(),
119
+ })).optional(),
120
+ balance: z.object({
121
+ display: z.enum(['sidebar', 'settings', 'both', 'off']),
122
+ refreshMinutes: num,
123
+ showProgressBar: z.boolean().optional(),
124
+ budgetCap: z.union([num, z.null()]).optional(),
125
+ reconcile: z.boolean().optional(),
126
+ }),
127
+ goQuota: z.object({
128
+ enabled: z.boolean(),
129
+ display: z.enum(['sidebar', 'settings', 'both', 'off']),
130
+ refreshMinutes: num,
131
+ apiKey: z.string(),
132
+ main: z.enum(['rolling', 'weekly', 'monthly']),
133
+ detail: z.boolean(),
134
+ }),
135
+ customBalance: z.object({
136
+ enabled: z.boolean(),
137
+ label: z.string(),
138
+ labelEn: z.string().optional(),
139
+ display: z.enum(['sidebar', 'settings', 'both', 'off']),
140
+ unit: z.enum(['USD', 'CNY', 'EUR']).optional(),
141
+ refreshMinutes: num,
142
+ request: z.object({
143
+ url: z.string(),
144
+ method: z.string().optional(),
145
+ headers: z.record(z.string(), z.string()).optional(),
146
+ body: z.unknown().optional(),
147
+ }),
148
+ extract: z.record(z.string(), z.unknown()),
149
+ }).optional(),
150
+ corner: z.object({
151
+ enabled: z.boolean(),
152
+ goRolling: z.boolean(),
153
+ goWeekly: z.boolean(),
154
+ goMonthly: z.boolean(),
155
+ budget: z.boolean(),
156
+ }),
157
+ historyDays: num,
158
+ fetchedAt: z.union([z.string(), z.null()]),
159
+ priceSource: z.string(),
160
+ })
161
+
162
+ const balanceSchema = z.object({
163
+ status: z.enum(['off', 'ok', 'error']),
164
+ message: z.string(),
165
+ fetchedAt: num,
166
+ currency: z.string(),
167
+ totalBalance: num,
168
+ grantedBalance: num,
169
+ toppedUpBalance: num,
170
+ })
171
+
172
+ const goWindowSchema = z.union([
173
+ z.object({ percent: num, resetsAt: z.string() }),
174
+ z.null(),
175
+ ])
176
+
177
+ const goQuotaSchema = z.object({
178
+ status: z.enum(['off', 'ok', 'error']),
179
+ message: z.string(),
180
+ fetchedAt: num,
181
+ rolling: goWindowSchema,
182
+ weekly: goWindowSchema,
183
+ monthly: goWindowSchema,
184
+ })
185
+
186
+ const customBalanceSchema = z.object({
187
+ status: z.enum(['off', 'ok', 'error']),
188
+ message: z.string(),
189
+ fetchedAt: num,
190
+ label: z.string(),
191
+ unit: z.string(),
192
+ remaining: num,
193
+ maxBudget: z.union([num, z.null()]),
194
+ spend: z.union([num, z.null()]),
195
+ })
196
+
197
+ // Coding plan 额度状态条目(运行时合并配置与查询结果;windows 为各用量窗口)。
198
+ const codingPlanSchema = z.object({
199
+ enabled: z.boolean(),
200
+ display: z.enum(['sidebar', 'settings', 'both', 'off']),
201
+ refreshMinutes: num,
202
+ apiKey: z.string(),
203
+ status: z.enum(['off', 'ok', 'error']),
204
+ message: z.string(),
205
+ fetchedAt: num,
206
+ windows: z.record(z.string(), z.object({ percent: num.optional(), resetsAt: z.string(), text: z.string().optional() })),
207
+ })
208
+
209
+ export const stateSchema = z.object({
210
+ today: daySchema,
211
+ month: daySchema,
212
+ total: daySchema,
213
+ budgetUsed: num,
214
+ balance: balanceSchema,
215
+ goQuota: goQuotaSchema,
216
+ // optional:兼容旧快照/降级路径(与 codingPlans/priceCatalog 同策略,避免 strict codec 击穿)。
217
+ customBalance: customBalanceSchema.optional(),
218
+ // 余额差交叉校验提示(issue #18):旧快照无此字段,optional 防击穿。
219
+ reconcile: z.object({ ok: z.boolean(), message: z.string() }).optional(),
220
+ codingPlans: z.record(z.string(), codingPlanSchema),
221
+ history: z.array(daySchema),
222
+ config: configSchema,
223
+ priceCatalog: z.record(z.string(), z.record(z.string(), z.record(z.string(), catalogEntrySchema))).optional(),
224
+ meta: z.object({
225
+ now: num,
226
+ timezoneOffsetMinutes: num,
227
+ dayKey: z.string(),
228
+ monthKey: z.string(),
229
+ }),
230
+ })
231
+
232
+ const patchSchema = z.record(z.string(), z.unknown())
233
+
234
+ const fetchPricesSchema = z.object({
235
+ ok: z.boolean(),
236
+ message: z.string(),
237
+ state: stateSchema.optional(),
238
+ })
239
+
240
+ const _state$codec = { mode: 'strict', typeSymbol: 'dsh-cost-meter#CostState', schema: stateSchema }
241
+ const _patch$codec = { mode: 'strict', typeSymbol: 'dsh-cost-meter#ConfigPatch', schema: patchSchema }
242
+ const _fetch$codec = { mode: 'strict', typeSymbol: 'dsh-cost-meter#FetchPricesResult', schema: fetchPricesSchema }
243
+ const _provider$codec = { mode: 'strict', typeSymbol: 'dsh-cost-meter#CodingPlanProvider', schema: z.string() }
244
+
245
+ export const TYPERT = {
246
+ package: 'dsh-cost-meter',
247
+ face: 'host',
248
+ schemas: [],
249
+ invocations: [
250
+ {
251
+ id: 'dsh-cost-meter#costMeter/getState',
252
+ service: 'costMeter',
253
+ namespace: 'costMeter',
254
+ method: 'getState',
255
+ invocation: { kind: 'direct' },
256
+ parameters: [],
257
+ result: _state$codec,
258
+ },
259
+ {
260
+ id: 'dsh-cost-meter#costMeter/updateConfig',
261
+ service: 'costMeter',
262
+ namespace: 'costMeter',
263
+ method: 'updateConfig',
264
+ invocation: { kind: 'direct' },
265
+ parameters: [
266
+ { name: 'patch', wire: 'patch', source: 'json', codec: _patch$codec },
267
+ ],
268
+ result: _state$codec,
269
+ },
270
+ {
271
+ id: 'dsh-cost-meter#costMeter/fetchPrices',
272
+ service: 'costMeter',
273
+ namespace: 'costMeter',
274
+ method: 'fetchPrices',
275
+ invocation: { kind: 'direct' },
276
+ parameters: [],
277
+ result: _fetch$codec,
278
+ },
279
+ {
280
+ id: 'dsh-cost-meter#costMeter/refreshBalance',
281
+ service: 'costMeter',
282
+ namespace: 'costMeter',
283
+ method: 'refreshBalance',
284
+ invocation: { kind: 'direct' },
285
+ parameters: [],
286
+ result: _fetch$codec,
287
+ },
288
+ {
289
+ id: 'dsh-cost-meter#costMeter/refreshGoQuota',
290
+ service: 'costMeter',
291
+ namespace: 'costMeter',
292
+ method: 'refreshGoQuota',
293
+ invocation: { kind: 'direct' },
294
+ parameters: [],
295
+ result: _fetch$codec,
296
+ },
297
+ {
298
+ id: 'dsh-cost-meter#costMeter/refreshCustomBalance',
299
+ service: 'costMeter',
300
+ namespace: 'costMeter',
301
+ method: 'refreshCustomBalance',
302
+ invocation: { kind: 'direct' },
303
+ parameters: [],
304
+ result: _fetch$codec,
305
+ },
306
+ {
307
+ id: 'dsh-cost-meter#costMeter/refreshCodingPlan',
308
+ service: 'costMeter',
309
+ namespace: 'costMeter',
310
+ method: 'refreshCodingPlan',
311
+ invocation: { kind: 'direct' },
312
+ parameters: [
313
+ { name: 'provider', wire: 'provider', source: 'json', codec: _provider$codec },
314
+ ],
315
+ result: _fetch$codec,
316
+ },
317
+ {
318
+ id: 'dsh-cost-meter#costMeter/resetHistory',
319
+ service: 'costMeter',
320
+ namespace: 'costMeter',
321
+ method: 'resetHistory',
322
+ invocation: { kind: 'direct' },
323
+ parameters: [],
324
+ result: _state$codec,
325
+ },
326
+ ],
327
+ model: {
328
+ services: [
329
+ {
330
+ description: 'dsh-cost-meter 账本与配置服务(ctx.costMeter),聚合每日模型用量与费用。Ledger and config service (ctx.costMeter) aggregating daily model usage and cost.',
331
+ summary: 'dsh-cost-meter 账本与配置服务 (dsh-cost-meter ledger & config service)。',
332
+ tags: [],
333
+ jsDoc: '/** dsh-cost-meter 账本与配置服务(ctx.costMeter)。dsh-cost-meter ledger & config service (ctx.costMeter). */',
334
+ key: 'costMeter',
335
+ exportName: 'CostMeterService',
336
+ members: [
337
+ {
338
+ kind: 'method',
339
+ name: 'getState',
340
+ signature: 'getState(): CostState',
341
+ summary: '读取今日/本月/累计聚合、历史记录与当前配置。Read today/month/total aggregates, history, and current config.',
342
+ jsDoc: '/**\n * 读取今日/本月/累计聚合、历史记录与当前配置。\n * @returns 完整账本快照。\n * Read today/month/total aggregates, history, and current config.\n * @returns The full ledger snapshot.\n */',
343
+ },
344
+ {
345
+ kind: 'method',
346
+ name: 'updateConfig',
347
+ signature: 'updateConfig(patch: ConfigPatch): CostState',
348
+ summary: '深合并一份配置补丁并持久化。Deep-merge a config patch and persist it.',
349
+ jsDoc: '/**\n * 深合并一份配置补丁并持久化。\n * @param patch - 配置补丁。\n * @returns 更新后的完整快照。\n * Deep-merge a config patch and persist it.\n * @param patch - The config patch.\n * @returns The updated full snapshot.\n */',
350
+ },
351
+ {
352
+ kind: 'method',
353
+ name: 'fetchPrices',
354
+ signature: 'fetchPrices(): Promise<FetchPricesResult>',
355
+ summary: '抓取官方定价页并应用解析出的价格。Fetch the official pricing page and apply the parsed prices.',
356
+ jsDoc: '/**\n * 抓取官方定价页并应用解析出的价格。\n * @returns 抓取与应用结果。\n * Fetch the official pricing page and apply the parsed prices.\n * @returns The fetch-and-apply result.\n */',
357
+ },
358
+ {
359
+ kind: 'method',
360
+ name: 'refreshBalance',
361
+ signature: 'refreshBalance(): Promise<FetchPricesResult>',
362
+ summary: '立即查询官方开放平台账户余额。Query the official open-platform account balance immediately.',
363
+ jsDoc: '/**\n * 立即查询官方开放平台账户余额。\n * @returns 查询结果与最新快照。\n * Query the official open-platform account balance immediately.\n * @returns The query result and the latest snapshot.\n */',
364
+ },
365
+ {
366
+ kind: 'method',
367
+ name: 'refreshGoQuota',
368
+ signature: 'refreshGoQuota(): Promise<FetchPricesResult>',
369
+ summary: '立即查询 OpenCode Go 订阅额度。Query the OpenCode Go subscription quota immediately.',
370
+ jsDoc: '/**\n * 立即查询 OpenCode Go 订阅额度(滚动5小时/本周/本月用量百分比)。\n * @returns 查询结果与最新快照。\n * Query the OpenCode Go subscription quota immediately (rolling-5h/weekly/monthly usage percent).\n * @returns The query result and the latest snapshot.\n */',
371
+ },
372
+ {
373
+ kind: 'method',
374
+ name: 'refreshCustomBalance',
375
+ signature: 'refreshCustomBalance(): Promise<FetchPricesResult>',
376
+ summary: '立即查询自定义 Provider 余额。Query the configured custom provider balance immediately.',
377
+ jsDoc: '/**\n * 立即查询自定义 Provider 余额(可配置 HTTP 请求 + extract 规则)。\n * @returns 查询结果与最新快照。\n * Query the configured custom provider balance immediately (configurable HTTP request + extract rules).\n * @returns The query result and the latest snapshot.\n */',
378
+ },
379
+ {
380
+ kind: 'method',
381
+ name: 'refreshCodingPlan',
382
+ signature: 'refreshCodingPlan(provider: string): Promise<FetchPricesResult>',
383
+ summary: '立即查询指定厂商的 coding plan 额度。Query a vendor coding plan quota immediately.',
384
+ jsDoc: '/**\n * 立即查询指定厂商(anthropic | zai | minimax | kimi | openrouter | siliconflow)的 coding plan 额度。\n * @param provider - 提供商标识。\n * @returns 查询结果与最新快照。\n * Query a vendor (anthropic | zai | minimax | kimi | openrouter | siliconflow) coding plan quota immediately.\n * @param provider - The provider id.\n * @returns The query result and the latest snapshot.\n */',
385
+ },
386
+ {
387
+ kind: 'method',
388
+ name: 'resetHistory',
389
+ signature: 'resetHistory(): CostState',
390
+ summary: '清空全部历史记录。Clear all history records.',
391
+ jsDoc: '/**\n * 清空全部历史记录。\n * @returns 清空后的完整快照。\n * Clear all history records.\n * @returns The full snapshot after clearing.\n */',
392
+ },
393
+ ],
394
+ types: [],
395
+ },
396
+ ],
397
+ events: [],
398
+ objects: [],
399
+ },
400
+ }
401
+
402
+ export default TYPERT
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "dsh-cost-meter",
3
+ "version": "1.5.5",
4
+ "description": "DeepSeek Harness 会话费用统计插件:本会话成本、当日费用、历史记录与官方价格同步,支持多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配)、主流 Coding Plan 订阅额度查询与显示(6 家)、自定义 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 6 vendors, custom provider balance lookup (configurable HTTP endpoint) and balance progress bar, in a bilingual (Chinese/English) UI.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "default": "./lib/index.js"
10
+ },
11
+ "./client": {
12
+ "default": "./lib/client.js"
13
+ },
14
+ "./typert": {
15
+ "default": "./lib/typert.host.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "dsh": {
20
+ "bundle": {
21
+ "patch": "./cordis.patch.yml"
22
+ },
23
+ "client": {
24
+ "platform": "web"
25
+ }
26
+ },
27
+ "dshhub": {
28
+ "schemaVersion": 1,
29
+ "displayName": "dsh-cost-meter",
30
+ "summary": "DeepSeek Harness 会话费用统计:本会话费用、预算图框、官方余额、自定义 Provider 余额查询、Coding Plan 额度查询、历史记录,支持峰谷计价与官方价格一键同步。",
31
+ "categories": ["费用", "Web UI", "观测"],
32
+ "surfaces": ["host", "web"],
33
+ "capabilities": {
34
+ "provides": ["service:cost-meter"]
35
+ },
36
+ "compatibility": {
37
+ "dsh": ">=0.1.0-rc.5",
38
+ "node": ">=20"
39
+ },
40
+ "permissions": {
41
+ "network": ["https://api.deepseek.com", "https://api-docs.deepseek.com"]
42
+ }
43
+ },
44
+ "files": [
45
+ "lib",
46
+ "cordis.patch.yml",
47
+ "docs/provider-pricing.json"
48
+ ],
49
+ "dependencies": {
50
+ "@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
51
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
52
+ "zod": "^4.4.3"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/Han-1413141/dsh-cost-meter.git"
57
+ },
58
+ "homepage": "https://github.com/Han-1413141/dsh-cost-meter#readme",
59
+ "keywords": [
60
+ "deepseek",
61
+ "deepseek-harness",
62
+ "dsh",
63
+ "dsh-plugin",
64
+ "client-plugin",
65
+ "cost",
66
+ "usage",
67
+ "billing",
68
+ "budget",
69
+ "i18n",
70
+ "bilingual",
71
+ "zh",
72
+ "en",
73
+ "language"
74
+ ],
75
+ "license": "MIT"
76
+ }