@kenz1117/dsh-ui-usage-billing 1.1.11 → 1.1.13

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/lib/index.js CHANGED
@@ -222,6 +222,36 @@ const PEAK_ERA_START_MS = Date.parse("2026-08-16T16:00:00Z");
222
222
  */
223
223
  const WEEKEND_OFFPEAK_START_MS = Date.parse("2026-08-22T16:00:00Z");
224
224
  /**
225
+ * flash 系调价分界(UTC 2026-09-10T04:00:00Z,即北京时间 2026-09-10 12:00):
226
+ * 官方自此刻起 V4 Flash / V4 Flash Vision (Exp) 调价——谷档 1.5/0.05/4.5 →
227
+ * 1/0.02/4(峰 = 谷 × 2)。目录条目写现行(新)价;分界前的历史事件由
228
+ * {@link computeCostAt} 按 {@link FLASH_REPRICED_OFFPEAK} 回算旧价,与
229
+ * {@link PEAK_ERA_START_MS} 同一「按事件时刻分段适用规则」口径。
230
+ */
231
+ const FLASH_REPRICE_MS = Date.parse("2026-09-10T04:00:00Z");
232
+ /**
233
+ * flash 系调价前的官方谷档价(CNY / 1M tokens):{@link FLASH_REPRICE_MS}
234
+ * 之前的 flash / flash-vision-exp 事件按此回算(峰档 = 谷档 × 2)。pro 的
235
+ * 同表条目是 V4 Pro 峰谷时代刊例(谷 4.5/0.15/13.5):官方公告自
236
+ * {@link FLASH_REPRICE_MS} 起 V4 Pro 请求全部路由至 V4.1 Flash 并按其单价
237
+ * 计费(V4.1 Pro 上线前),故分界前回算 V4 Pro 刊例、分界后走目录的 Flash 价。
238
+ * 用户价 = 实付价,与 legacy 口径相同地跳过本表。
239
+ */
240
+ const FLASH_PRE_REPRICE_BAND = {
241
+ input: 1.5,
242
+ cacheHit: .05,
243
+ output: 4.5
244
+ };
245
+ const FLASH_REPRICED_OFFPEAK = {
246
+ flash: FLASH_PRE_REPRICE_BAND,
247
+ "flash-vision-exp": FLASH_PRE_REPRICE_BAND,
248
+ pro: {
249
+ input: 4.5,
250
+ cacheHit: .15,
251
+ output: 13.5
252
+ }
253
+ };
254
+ /**
225
255
  * DeepSeek V4 峰谷时代之前的官方基础价(CNY / 1M tokens):官方中文定价页
226
256
  * 峰谷改版前的基础价档(缓存写沿用历史规则按命中价计)。键为内置目录键,
227
257
  * flash-vision-exp 与 flash 同价。仅当事件时刻早于 {@link PEAK_ERA_START_MS}
@@ -284,6 +314,8 @@ function isBeijingWeekend(timeMs) {
284
314
  * splits peak (09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate
285
315
  * from 2026-08-17, and Gemini's Flex tier discounts spare-capacity traffic.
286
316
  */
317
+ /** DeepSeek 官方高峰时段说明(峰谷分时计费目录条目共用)。 */
318
+ const DEEPSEEK_PEAK_HOURS = "09:00-12:00 / 14:00-18:00";
287
319
  const MODEL_CATALOG = [
288
320
  {
289
321
  key: "flash",
@@ -292,16 +324,16 @@ const MODEL_CATALOG = [
292
324
  colorVar: "dsw-static-blue-500",
293
325
  price: {
294
326
  currency: "CNY",
295
- input: 3,
296
- cacheHit: .1,
297
- output: 9,
327
+ input: 2,
328
+ cacheHit: .04,
329
+ output: 8,
298
330
  offPeak: {
299
- input: 1.5,
300
- cacheHit: .05,
301
- output: 4.5
331
+ input: 1,
332
+ cacheHit: .02,
333
+ output: 4
302
334
  }
303
335
  },
304
- peakHours: "09:00-12:00 / 14:00-18:00"
336
+ peakHours: DEEPSEEK_PEAK_HOURS
305
337
  },
306
338
  {
307
339
  key: "flash-vision-exp",
@@ -310,16 +342,16 @@ const MODEL_CATALOG = [
310
342
  colorVar: "dsw-static-blue-500",
311
343
  price: {
312
344
  currency: "CNY",
313
- input: 3,
314
- cacheHit: .1,
315
- output: 9,
345
+ input: 2,
346
+ cacheHit: .04,
347
+ output: 8,
316
348
  offPeak: {
317
- input: 1.5,
318
- cacheHit: .05,
319
- output: 4.5
349
+ input: 1,
350
+ cacheHit: .02,
351
+ output: 4
320
352
  }
321
353
  },
322
- peakHours: "09:00-12:00 / 14:00-18:00"
354
+ peakHours: DEEPSEEK_PEAK_HOURS
323
355
  },
324
356
  {
325
357
  key: "pro",
@@ -328,16 +360,16 @@ const MODEL_CATALOG = [
328
360
  colorVar: "dsw-static-deepseek-500",
329
361
  price: {
330
362
  currency: "CNY",
331
- input: 9,
332
- cacheHit: .3,
333
- output: 27,
363
+ input: 2,
364
+ cacheHit: .04,
365
+ output: 8,
334
366
  offPeak: {
335
- input: 4.5,
336
- cacheHit: .15,
337
- output: 13.5
367
+ input: 1,
368
+ cacheHit: .02,
369
+ output: 4
338
370
  }
339
371
  },
340
- peakHours: "09:00-12:00 / 14:00-18:00"
372
+ peakHours: DEEPSEEK_PEAK_HOURS
341
373
  },
342
374
  {
343
375
  key: "glm",
@@ -1372,6 +1404,7 @@ const MODEL_CATALOG = [
1372
1404
  const MODEL_KEY_ALIASES = {
1373
1405
  "deepseek-v4-flash": "flash",
1374
1406
  "deepseek-v4-flash-vision-exp": "flash-vision-exp",
1407
+ "deepseek-v4.1-flash": "flash",
1375
1408
  "deepseek-v4-pro": "pro",
1376
1409
  "glm-5.2": "glm",
1377
1410
  "glm-4.5-air": "glm-4.5-air",
@@ -1656,6 +1689,12 @@ function computeCostAt(entry, buckets, timeMs, peakShare = DEFAULT_PEAK_SHARE) {
1656
1689
  const priced = applyPromo(entry, timeMs);
1657
1690
  const legacy = timeMs < PEAK_ERA_START_MS && entry.userPriced !== true ? LEGACY_DEEPSEEK_BANDS[entry.key] : void 0;
1658
1691
  if (legacy !== void 0) return priceBandCost(legacy, buckets, "CNY");
1692
+ const repriced = timeMs < FLASH_REPRICE_MS && entry.userPriced !== true ? FLASH_REPRICED_OFFPEAK[entry.key] : void 0;
1693
+ if (repriced !== void 0) return priceBandCost((timeMs < WEEKEND_OFFPEAK_START_MS ? tariffV1At(timeMs) : tierAt(timeMs)) === "peak" ? {
1694
+ input: repriced.input * 2,
1695
+ cacheHit: repriced.cacheHit * 2,
1696
+ output: repriced.output * 2
1697
+ } : repriced, buckets, "CNY");
1659
1698
  if (priced.price.offPeak === void 0) return priceBandCost(priced.price, buckets, priced.price.currency);
1660
1699
  return priceBandCost((timeMs < WEEKEND_OFFPEAK_START_MS ? tariffV1At(timeMs) : tierAt(timeMs)) === "peak" ? priced.price : priced.price.offPeak, buckets, priced.price.currency);
1661
1700
  }
@@ -2329,7 +2368,7 @@ function createUsageAggregator(persistence, options = {}) {
2329
2368
  id,
2330
2369
  ...cwd === void 0 ? {} : { cwd },
2331
2370
  ...stamp === null ? {} : { stamp },
2332
- foldVersion: 6,
2371
+ foldVersion: 8,
2333
2372
  fold: serializeFold(fold)
2334
2373
  };
2335
2374
  const row = ledger.get(id);
@@ -2419,7 +2458,7 @@ function createUsageAggregator(persistence, options = {}) {
2419
2458
  try {
2420
2459
  const cwd = meta.cwd;
2421
2460
  const ledgerRow = ledger.get(id);
2422
- if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 6) {
2461
+ if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 8) {
2423
2462
  const fold = deserializeFold(ledgerRow.fold);
2424
2463
  folds.push({
2425
2464
  id,
@@ -2502,7 +2541,7 @@ function createUsageAggregator(persistence, options = {}) {
2502
2541
  for (const entry of ledger.values()) {
2503
2542
  if (included.has(entry.id)) continue;
2504
2543
  try {
2505
- const stale = (entry.foldVersion ?? 1) < 6;
2544
+ const stale = (entry.foldVersion ?? 1) < 8;
2506
2545
  folds.push({
2507
2546
  id: entry.id,
2508
2547
  ...entry.cwd === void 0 ? {} : { cwd: entry.cwd },
@@ -5219,13 +5258,7 @@ function apply(ctx, config = {}) {
5219
5258
  const now = Date.now();
5220
5259
  if (now - lastSnapshotAt < SNAPSHOT_INTERVAL_MS) return;
5221
5260
  lastSnapshotAt = now;
5222
- const payload = JSON.stringify({
5223
- ...doc,
5224
- _writer: {
5225
- pid: process.pid,
5226
- at: now
5227
- }
5228
- });
5261
+ const payload = JSON.stringify(doc);
5229
5262
  (async () => {
5230
5263
  try {
5231
5264
  const existing = await readFile(snapshotPath, "utf8");
@@ -5249,13 +5282,67 @@ function apply(ctx, config = {}) {
5249
5282
  } catch {}
5250
5283
  return null;
5251
5284
  };
5252
- (async () => {
5253
- try {
5254
- const text = await readFile(snapshotPath, "utf8");
5255
- const writer = JSON.parse(text)._writer;
5256
- if (writer?.pid !== void 0 && writer.pid !== process.pid && writer.at !== void 0 && Date.now() - writer.at < 6e4) console.warn(`[usage-billing] 检测到另一实例(pid ${writer.pid})正在提供用量统计,双实例可能导致提醒重复。`);
5257
- } catch {}
5258
- })();
5285
+ const notifyClaimsPath = `${snapshotPath}.notify-claims.json`;
5286
+ const notifyClaims = {
5287
+ loaded: false,
5288
+ keys: []
5289
+ };
5290
+ ctx.effect(() => ctx.webServer.register({
5291
+ kind: "exact",
5292
+ path: "/api/billing/notify-claim",
5293
+ handler: async (req, res) => {
5294
+ if (!guardLoopback(req, res)) return;
5295
+ if (req.method !== "POST") {
5296
+ res.writeHead(405, { "content-type": "application/json; charset=utf-8" });
5297
+ res.end(JSON.stringify({ error: "method not allowed" }));
5298
+ return;
5299
+ }
5300
+ if (!isLoopbackOrigin(req.headers.origin) || !(req.headers["content-type"] ?? "").toLowerCase().includes("application/json")) {
5301
+ res.writeHead(403, { "content-type": "application/json; charset=utf-8" });
5302
+ res.end(JSON.stringify({ error: "forbidden: loopback only" }));
5303
+ return;
5304
+ }
5305
+ try {
5306
+ let body = "";
5307
+ for await (const chunk of req) {
5308
+ body += String(chunk);
5309
+ if (body.length > 2048) {
5310
+ res.end(JSON.stringify({ error: "body too large" }));
5311
+ return;
5312
+ }
5313
+ }
5314
+ const parsed = JSON.parse(body === "" ? "{}" : body);
5315
+ if (typeof parsed.key !== "string" || parsed.key === "" || parsed.key.length > 200) {
5316
+ res.writeHead(400, { "content-type": "application/json; charset=utf-8" });
5317
+ res.end(JSON.stringify({ error: "invalid key" }));
5318
+ return;
5319
+ }
5320
+ if (!notifyClaims.loaded) {
5321
+ notifyClaims.loaded = true;
5322
+ try {
5323
+ const stored = JSON.parse(await readFile(notifyClaimsPath, "utf8"));
5324
+ if (Array.isArray(stored.keys)) notifyClaims.keys = stored.keys.filter((k) => typeof k === "string");
5325
+ } catch {}
5326
+ }
5327
+ if (notifyClaims.keys.includes(parsed.key)) {
5328
+ res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
5329
+ res.end(JSON.stringify({ claimed: false }));
5330
+ return;
5331
+ }
5332
+ notifyClaims.keys.push(parsed.key);
5333
+ if (notifyClaims.keys.length > 400) notifyClaims.keys = notifyClaims.keys.slice(-200);
5334
+ writeFileAtomic(notifyClaimsPath, JSON.stringify({ keys: notifyClaims.keys }), {
5335
+ mode: 384,
5336
+ dirMode: 448
5337
+ }).catch(() => {});
5338
+ res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
5339
+ res.end(JSON.stringify({ claimed: true }));
5340
+ } catch {
5341
+ res.writeHead(400, { "content-type": "application/json; charset=utf-8" });
5342
+ res.end(JSON.stringify({ error: "invalid" }));
5343
+ }
5344
+ }
5345
+ }), "usage-billing: notify-claim route");
5259
5346
  ctx.inject(["settings"], (sctx) => {
5260
5347
  const scope = sctx.settings.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
5261
5348
  usageSettingsScope = scope;
@@ -427,8 +427,12 @@ export interface UsageLedgerDocument {
427
427
  * `session/end-seed`,resume 续写后继续对话时把本会话历史段误判为种子丢弃,
428
428
  * 会话费用只剩最近一段,issue #29)。
429
429
  * 持久账本行据此区分新旧算法:日志已删/不可读而只能沿用旧行时,UI 标注置信度提示。
430
+ * 8:官方公告自 2026-09-10 12:00(北京)起 V4 Pro 请求路由至 V4.1 Flash 按 Flash
431
+ * 单价计费(V4.1 Pro 上线前)——pro 目录价改写为 Flash 价、V4 Pro 刊例(峰
432
+ * 9/0.3/27、谷 4.5/0.15/13.5)进 FLASH_REPRICED_OFFPEAK 供分界前回算;v7 及更早
433
+ * 的行把分界后的 pro 全部按 V4 Pro 刊例折算(高估数倍),bump 全量重折对齐时间线。
430
434
  */
431
- export declare const FOLD_VERSION = 6;
435
+ export declare const FOLD_VERSION = 8;
432
436
  /**
433
437
  * 一次性账本迁移:id 唯一,apply 在加载边界对原始文档执行,已应用过的跳过。
434
438
  * 未来账本/schema 字段变更(重命名、拆桶、语义调整)时,在此追加一条迁移并
@@ -84,6 +84,15 @@ export declare function projectMonthCost(byDay: Record<string, {
84
84
  * @returns 重估后的文档;无用户价或缺 byDayModels 时原样返回。
85
85
  */
86
86
  export declare function recostWithUserPrices(stats: UsageStats): UsageStats;
87
+ /**
88
+ * 跨实例提醒认领后发系统通知(issue #44):多实例(CLI + 桌面等)合法共存,
89
+ * 各自轮询会重复弹通知。两级去重——进程内 Set 挡同进程的并发/重渲染(trigger
90
+ * 与仪表盘是两个组件实例,effect 同周期并发时本地 store 标记读到的还是旧值);
91
+ * 跨进程走宿主认领端点(先到先得),另一实例刚发过则跳过。端点不可达(旧版本/
92
+ * 网络故障)时放行,退化为原有 per-客户端去重。通知构造失败(平台限制)静默。
93
+ * Set 导出供测试重置(模块级状态会跨用例泄漏)。
94
+ */
95
+ export declare const notifiedKeys: Set<string>;
87
96
  /** 近 7 天费用序列(含今天,缺日补 0):触发卡 hover 速览的迷你柱数据源。
88
97
  * 导出供测试:纯函数(日期取本地时区)。 */
89
98
  export declare function activeDaysOf(byDay: Record<string, {
@@ -126,6 +135,10 @@ interface SessionBillingRow {
126
135
  calls: number;
127
136
  cost: number;
128
137
  lastActive: number;
138
+ /** 展示层合并标记(issue #42):同标题会话归并后的段数(>1 时行尾显示 ×N)。 */
139
+ mergedCount?: number;
140
+ /** 合并组的段明细(展示层专用):一级行默认收起,展开后逐段显示。合并函数需 push,故非 readonly。 */
141
+ children?: SessionBillingRow[];
129
142
  }
130
143
  /** Usage stats structure from `.dsh-usage-stats.json`. */
131
144
  export interface UsageStats {
@@ -1,5 +1,5 @@
1
1
  /** Locale dictionaries for the usage billing surface. */
2
- export type UsageBillingKey = 'title' | 'subtitle' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'totalCost' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'project' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'estimated' | 'actual' | 'pricing' | 'showPricing' | 'hidePricing' | 'pricePerM' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'peakHours' | 'band' | 'openDashboard' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptions' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'triggerMonthTokens' | 'floatPrev' | 'floatNext' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemove' | 'sessionStaleBadge' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'workspaces' | 'workspacesHint' | 'plan' | 'remaining' | 'unknownModel' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'currentRound' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabDetails' | 'tabPricing' | 'tabSettings' | 'settingsHead' | 'settingsHint' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionIncluded' | 'free' | 'official' | 'thirdParty' | 'officialCost' | 'thirdPartyCost' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginInfo' | 'pluginName' | 'pluginDescription' | 'pluginVersion' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popQuotaAlert' | 'popRiskNone' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'popBalanceNormal' | 'popBalanceLow' | 'popQuotaNormal' | 'popQuotaLow' | 'alertBalanceLow' | 'alertQuotaLow' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'exportCsvSite' | 'panelRelay' | 'relaySite' | 'relayDirect' | 'relayUnknown' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown' | 'relayCalls';
2
+ export type UsageBillingKey = 'title' | 'subtitle' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'totalCost' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'project' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'estimated' | 'actual' | 'pricing' | 'showPricing' | 'hidePricing' | 'pricePerM' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'peakHours' | 'band' | 'openDashboard' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptions' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'triggerMonthTokens' | 'floatPrev' | 'floatNext' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemove' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'workspaces' | 'workspacesHint' | 'plan' | 'remaining' | 'unknownModel' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'currentRound' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabDetails' | 'tabPricing' | 'tabSettings' | 'settingsHead' | 'settingsHint' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionIncluded' | 'free' | 'official' | 'thirdParty' | 'officialCost' | 'thirdPartyCost' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginInfo' | 'pluginName' | 'pluginDescription' | 'pluginVersion' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popQuotaAlert' | 'popRiskNone' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'popBalanceNormal' | 'popBalanceLow' | 'popQuotaNormal' | 'popQuotaLow' | 'alertBalanceLow' | 'alertQuotaLow' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'exportCsvSite' | 'panelRelay' | 'relaySite' | 'relayDirect' | 'relayUnknown' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown' | 'relayCalls';
3
3
  export declare const NS = "usageBilling";
4
4
  export declare const zh: Record<UsageBillingKey, string>;
5
5
  export declare const en: Record<UsageBillingKey, string>;
@@ -111,6 +111,14 @@ export declare const PEAK_ERA_START_MS: number;
111
111
  * 档位判定按事件所在时段适用各自的规则,不得统一套现行规则重算历史。
112
112
  */
113
113
  export declare const WEEKEND_OFFPEAK_START_MS: number;
114
+ /**
115
+ * flash 系调价分界(UTC 2026-09-10T04:00:00Z,即北京时间 2026-09-10 12:00):
116
+ * 官方自此刻起 V4 Flash / V4 Flash Vision (Exp) 调价——谷档 1.5/0.05/4.5 →
117
+ * 1/0.02/4(峰 = 谷 × 2)。目录条目写现行(新)价;分界前的历史事件由
118
+ * {@link computeCostAt} 按 {@link FLASH_REPRICED_OFFPEAK} 回算旧价,与
119
+ * {@link PEAK_ERA_START_MS} 同一「按事件时刻分段适用规则」口径。
120
+ */
121
+ export declare const FLASH_REPRICE_MS: number;
114
122
  /** 计费时段档位:高峰 / 空闲(官方 DeepSeek 刊例价:高峰 = 空闲 × 2)。 */
115
123
  export type PriceTierId = 'peak' | 'offPeak';
116
124
  /** 成本显示币种:人民币(国内模型直价)/ 美元(国外模型直价或换算显示)。 */
@@ -257,21 +265,6 @@ export interface ModelEntry {
257
265
  /** 该条目当前按用户自定义单价计价(设置面板可维护);费率表标注「自定义」。 */
258
266
  userPriced?: boolean;
259
267
  }
260
- /**
261
- * Built-in catalog of current mainstream models as of 2026-08-16, priced from
262
- * each provider's official price page. Domestic providers are OpenAI-API
263
- * compatible and publish RMB prices directly; overseas providers publish USD
264
- * and convert through the exchange rate at estimate time. Retired models
265
- * (GPT-4o family, Gemini 2.x, GLM-4.x-lite, older Qwen) are deliberately
266
- * absent, as are Anthropic Claude models (their native API is not
267
- * OpenAI-compatible, so the harness cannot drive them directly). DeepSeek
268
- * keys match the harness stats file so real usage prices from the catalog;
269
- * unknown keys fall back to `other`.
270
- *
271
- * Time-of-day billing (peak/off-peak) is now real: DeepSeek V4 officially
272
- * splits peak (09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate
273
- * from 2026-08-17, and Gemini's Flex tier discounts spare-capacity traffic.
274
- */
275
268
  export declare const MODEL_CATALOG: readonly ModelEntry[];
276
269
  /**
277
270
  * 真实 provider model id → 计费目录键(`MODEL_CATALOG[].key`)的映射。未知 id
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kenz1117/dsh-ui-usage-billing",
3
3
  "description": "Usage billing dashboard for DeepSeek Harness: sidebar cost metrics plus a full dashboard modal, priced from a current multi-provider catalog with real usage aggregated from session logs.",
4
- "version": "1.1.11",
4
+ "version": "1.1.13",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },