@kenz1117/dsh-ui-usage-billing 1.0.42 → 1.0.44

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
@@ -238,6 +238,36 @@ const PEAK_ERA_START_MS = Date.parse("2026-08-16T16:00:00Z");
238
238
  */
239
239
  const WEEKEND_OFFPEAK_START_MS = Date.parse("2026-08-22T16:00:00Z");
240
240
  /**
241
+ * flash 系调价分界(UTC 2026-09-10T04:00:00Z,即北京时间 2026-09-10 12:00):
242
+ * 官方自此刻起 V4 Flash / V4 Flash Vision (Exp) 调价——谷档 1.5/0.05/4.5 →
243
+ * 1/0.02/4(峰 = 谷 × 2)。目录条目写现行(新)价;分界前的历史事件由
244
+ * {@link computeCostAt} 按 {@link FLASH_REPRICED_OFFPEAK} 回算旧价,与
245
+ * {@link PEAK_ERA_START_MS} 同一「按事件时刻分段适用规则」口径。
246
+ */
247
+ const FLASH_REPRICE_MS = Date.parse("2026-09-10T04:00:00Z");
248
+ /**
249
+ * flash 系调价前的官方谷档价(CNY / 1M tokens):{@link FLASH_REPRICE_MS}
250
+ * 之前的 flash / flash-vision-exp 事件按此回算(峰档 = 谷档 × 2)。pro 的
251
+ * 同表条目是 V4 Pro 峰谷时代刊例(谷 4.5/0.15/13.5):官方公告自
252
+ * {@link FLASH_REPRICE_MS} 起 V4 Pro 请求全部路由至 V4.1 Flash 并按其单价
253
+ * 计费(V4.1 Pro 上线前),故分界前回算 V4 Pro 刊例、分界后走目录的 Flash 价。
254
+ * 用户价 = 实付价,与 legacy 口径相同地跳过本表。
255
+ */
256
+ const FLASH_PRE_REPRICE_BAND = {
257
+ input: 1.5,
258
+ cacheHit: .05,
259
+ output: 4.5
260
+ };
261
+ const FLASH_REPRICED_OFFPEAK = {
262
+ flash: FLASH_PRE_REPRICE_BAND,
263
+ "flash-vision-exp": FLASH_PRE_REPRICE_BAND,
264
+ pro: {
265
+ input: 4.5,
266
+ cacheHit: .15,
267
+ output: 13.5
268
+ }
269
+ };
270
+ /**
241
271
  * DeepSeek V4 峰谷时代之前的官方基础价(CNY / 1M tokens):官方中文定价页
242
272
  * 峰谷改版前的基础价档(缓存写沿用历史规则按命中价计)。键为内置目录键,
243
273
  * flash-vision-exp 与 flash 同价。仅当事件时刻早于 {@link PEAK_ERA_START_MS}
@@ -300,6 +330,8 @@ function isBeijingWeekend(timeMs) {
300
330
  * splits peak (09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate
301
331
  * from 2026-08-17, and Gemini's Flex tier discounts spare-capacity traffic.
302
332
  */
333
+ /** DeepSeek 官方高峰时段说明(峰谷分时计费目录条目共用)。 */
334
+ const DEEPSEEK_PEAK_HOURS = "09:00-12:00 / 14:00-18:00";
303
335
  const MODEL_CATALOG = [
304
336
  {
305
337
  key: "flash",
@@ -308,16 +340,16 @@ const MODEL_CATALOG = [
308
340
  colorVar: "dsw-static-blue-500",
309
341
  price: {
310
342
  currency: "CNY",
311
- input: 3,
312
- cacheHit: .1,
313
- output: 9,
343
+ input: 2,
344
+ cacheHit: .04,
345
+ output: 8,
314
346
  offPeak: {
315
- input: 1.5,
316
- cacheHit: .05,
317
- output: 4.5
347
+ input: 1,
348
+ cacheHit: .02,
349
+ output: 4
318
350
  }
319
351
  },
320
- peakHours: "09:00-12:00 / 14:00-18:00"
352
+ peakHours: DEEPSEEK_PEAK_HOURS
321
353
  },
322
354
  {
323
355
  key: "flash-vision-exp",
@@ -326,16 +358,16 @@ const MODEL_CATALOG = [
326
358
  colorVar: "dsw-static-blue-500",
327
359
  price: {
328
360
  currency: "CNY",
329
- input: 3,
330
- cacheHit: .1,
331
- output: 9,
361
+ input: 2,
362
+ cacheHit: .04,
363
+ output: 8,
332
364
  offPeak: {
333
- input: 1.5,
334
- cacheHit: .05,
335
- output: 4.5
365
+ input: 1,
366
+ cacheHit: .02,
367
+ output: 4
336
368
  }
337
369
  },
338
- peakHours: "09:00-12:00 / 14:00-18:00"
370
+ peakHours: DEEPSEEK_PEAK_HOURS
339
371
  },
340
372
  {
341
373
  key: "pro",
@@ -344,16 +376,16 @@ const MODEL_CATALOG = [
344
376
  colorVar: "dsw-static-deepseek-500",
345
377
  price: {
346
378
  currency: "CNY",
347
- input: 9,
348
- cacheHit: .3,
349
- output: 27,
379
+ input: 2,
380
+ cacheHit: .04,
381
+ output: 8,
350
382
  offPeak: {
351
- input: 4.5,
352
- cacheHit: .15,
353
- output: 13.5
383
+ input: 1,
384
+ cacheHit: .02,
385
+ output: 4
354
386
  }
355
387
  },
356
- peakHours: "09:00-12:00 / 14:00-18:00"
388
+ peakHours: DEEPSEEK_PEAK_HOURS
357
389
  },
358
390
  {
359
391
  key: "glm",
@@ -1388,6 +1420,7 @@ const MODEL_CATALOG = [
1388
1420
  const MODEL_KEY_ALIASES = {
1389
1421
  "deepseek-v4-flash": "flash",
1390
1422
  "deepseek-v4-flash-vision-exp": "flash-vision-exp",
1423
+ "deepseek-v4.1-flash": "flash",
1391
1424
  "deepseek-v4-pro": "pro",
1392
1425
  "glm-5.2": "glm",
1393
1426
  "glm-4.5-air": "glm-4.5-air",
@@ -1725,6 +1758,12 @@ function computeCostAt(entry, buckets, timeMs, peakShare = DEFAULT_PEAK_SHARE) {
1725
1758
  const priced = applyPromo(entry, timeMs);
1726
1759
  const legacy = timeMs < PEAK_ERA_START_MS && entry.userPriced !== true ? LEGACY_DEEPSEEK_BANDS[entry.key] : void 0;
1727
1760
  if (legacy !== void 0) return priceBandCost(legacy, buckets, "CNY");
1761
+ const repriced = timeMs < FLASH_REPRICE_MS && entry.userPriced !== true ? FLASH_REPRICED_OFFPEAK[entry.key] : void 0;
1762
+ if (repriced !== void 0) return priceBandCost((timeMs < WEEKEND_OFFPEAK_START_MS ? tariffV1At(timeMs) : tierAt(timeMs)) === "peak" ? {
1763
+ input: repriced.input * 2,
1764
+ cacheHit: repriced.cacheHit * 2,
1765
+ output: repriced.output * 2
1766
+ } : repriced, buckets, "CNY");
1728
1767
  if (priced.price.offPeak === void 0) return priceBandCost(priced.price, buckets, priced.price.currency);
1729
1768
  return priceBandCost((timeMs < WEEKEND_OFFPEAK_START_MS ? tariffV1At(timeMs) : tierAt(timeMs)) === "peak" ? priced.price : priced.price.offPeak, buckets, priced.price.currency);
1730
1769
  }
@@ -2459,7 +2498,7 @@ function createUsageAggregator(persistence, options = {}) {
2459
2498
  id,
2460
2499
  ...cwd === void 0 ? {} : { cwd },
2461
2500
  ...stamp === null ? {} : { stamp },
2462
- foldVersion: 10,
2501
+ foldVersion: 12,
2463
2502
  fingerprint: foldFingerprint,
2464
2503
  fold: serializeFold(fold)
2465
2504
  };
@@ -2556,7 +2595,7 @@ function createUsageAggregator(persistence, options = {}) {
2556
2595
  try {
2557
2596
  const cwd = meta.cwd;
2558
2597
  const ledgerRow = ledger.get(id);
2559
- if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 10 && ledgerRow.fingerprint === foldFingerprint) {
2598
+ if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 12 && ledgerRow.fingerprint === foldFingerprint) {
2560
2599
  const fold = deserializeFold(ledgerRow.fold);
2561
2600
  folds.push({
2562
2601
  id,
@@ -2638,7 +2677,7 @@ function createUsageAggregator(persistence, options = {}) {
2638
2677
  for (const entry of ledger.values()) {
2639
2678
  if (included.has(entry.id)) continue;
2640
2679
  try {
2641
- const stale = (entry.foldVersion ?? 1) < 10 || entry.fingerprint !== foldFingerprint;
2680
+ const stale = (entry.foldVersion ?? 1) < 12 || entry.fingerprint !== foldFingerprint;
2642
2681
  folds.push({
2643
2682
  id: entry.id,
2644
2683
  ...entry.cwd === void 0 ? {} : { cwd: entry.cwd },
@@ -5714,13 +5753,7 @@ function apply(ctx, config = {}) {
5714
5753
  const now = Date.now();
5715
5754
  if (now - lastSnapshotAt < SNAPSHOT_INTERVAL_MS) return;
5716
5755
  lastSnapshotAt = now;
5717
- const payload = JSON.stringify({
5718
- ...doc,
5719
- _writer: {
5720
- pid: process.pid,
5721
- at: now
5722
- }
5723
- });
5756
+ const payload = JSON.stringify(doc);
5724
5757
  (async () => {
5725
5758
  try {
5726
5759
  const existing = await readFile(snapshotPath, "utf8");
@@ -5744,13 +5777,67 @@ function apply(ctx, config = {}) {
5744
5777
  } catch {}
5745
5778
  return null;
5746
5779
  };
5747
- (async () => {
5748
- try {
5749
- const text = await readFile(snapshotPath, "utf8");
5750
- const writer = JSON.parse(text)._writer;
5751
- 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})正在提供用量统计,双实例可能导致提醒重复。`);
5752
- } catch {}
5753
- })();
5780
+ const notifyClaimsPath = `${snapshotPath}.notify-claims.json`;
5781
+ const notifyClaims = {
5782
+ loaded: false,
5783
+ keys: []
5784
+ };
5785
+ ctx.effect(() => ctx.webServer.register({
5786
+ kind: "exact",
5787
+ path: "/api/billing/notify-claim",
5788
+ handler: async (req, res) => {
5789
+ if (!guardLoopback(req, res)) return;
5790
+ if (req.method !== "POST") {
5791
+ res.writeHead(405, { "content-type": "application/json; charset=utf-8" });
5792
+ res.end(JSON.stringify({ error: "method not allowed" }));
5793
+ return;
5794
+ }
5795
+ if (!isLoopbackOrigin(req.headers.origin) || !(req.headers["content-type"] ?? "").toLowerCase().includes("application/json")) {
5796
+ res.writeHead(403, { "content-type": "application/json; charset=utf-8" });
5797
+ res.end(JSON.stringify({ error: "forbidden: loopback only" }));
5798
+ return;
5799
+ }
5800
+ try {
5801
+ let body = "";
5802
+ for await (const chunk of req) {
5803
+ body += String(chunk);
5804
+ if (body.length > 2048) {
5805
+ res.end(JSON.stringify({ error: "body too large" }));
5806
+ return;
5807
+ }
5808
+ }
5809
+ const parsed = JSON.parse(body === "" ? "{}" : body);
5810
+ if (typeof parsed.key !== "string" || parsed.key === "" || parsed.key.length > 200) {
5811
+ res.writeHead(400, { "content-type": "application/json; charset=utf-8" });
5812
+ res.end(JSON.stringify({ error: "invalid key" }));
5813
+ return;
5814
+ }
5815
+ if (!notifyClaims.loaded) {
5816
+ notifyClaims.loaded = true;
5817
+ try {
5818
+ const stored = JSON.parse(await readFile(notifyClaimsPath, "utf8"));
5819
+ if (Array.isArray(stored.keys)) notifyClaims.keys = stored.keys.filter((k) => typeof k === "string");
5820
+ } catch {}
5821
+ }
5822
+ if (notifyClaims.keys.includes(parsed.key)) {
5823
+ res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
5824
+ res.end(JSON.stringify({ claimed: false }));
5825
+ return;
5826
+ }
5827
+ notifyClaims.keys.push(parsed.key);
5828
+ if (notifyClaims.keys.length > 400) notifyClaims.keys = notifyClaims.keys.slice(-200);
5829
+ writeFileAtomic(notifyClaimsPath, JSON.stringify({ keys: notifyClaims.keys }), {
5830
+ mode: 384,
5831
+ dirMode: 448
5832
+ }).catch(() => {});
5833
+ res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
5834
+ res.end(JSON.stringify({ claimed: true }));
5835
+ } catch {
5836
+ res.writeHead(400, { "content-type": "application/json; charset=utf-8" });
5837
+ res.end(JSON.stringify({ error: "invalid" }));
5838
+ }
5839
+ }
5840
+ }), "usage-billing: notify-claim route");
5754
5841
  ctx.inject(["settings"], (sctx) => {
5755
5842
  const scope = sctx.settings.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
5756
5843
  usageSettingsScope = scope;
@@ -478,7 +478,7 @@ export interface UsageLedgerDocument {
478
478
  * 会话费用只剩最近一段,issue #29)。
479
479
  * 持久账本行据此区分新旧算法:日志已删/不可读而只能沿用旧行时,UI 标注置信度提示。
480
480
  */
481
- export declare const FOLD_VERSION = 10;
481
+ export declare const FOLD_VERSION = 12;
482
482
  /**
483
483
  * 一次性账本迁移:id 唯一,apply 在加载边界对原始文档执行,已应用过的跳过。
484
484
  * 未来账本/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' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | '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' | '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' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'workspaceSubtotal' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | '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' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenViewStructure' | 'tokenViewModel' | 'tokenHitShort' | 'tokenMissShort' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'liveCostBar' | 'liveCostBarHint' | 'liveCostBarPosition' | 'liveCostBarPosBelow' | 'liveCostBarPosAbove' | 'liveCostBarPosToolbar' | 'exportCsvSite' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown';
2
+ export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | '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' | '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' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | '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' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenViewStructure' | 'tokenViewModel' | 'tokenHitShort' | 'tokenMissShort' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'liveCostBar' | 'liveCostBarHint' | 'liveCostBarPosition' | 'liveCostBarPosBelow' | 'liveCostBarPosAbove' | 'liveCostBarPosToolbar' | 'exportCsvSite' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown';
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>;
@@ -153,6 +153,14 @@ export declare const PEAK_ERA_START_MS: number;
153
153
  * 档位判定按事件所在时段适用各自的规则,不得统一套现行规则重算历史。
154
154
  */
155
155
  export declare const WEEKEND_OFFPEAK_START_MS: number;
156
+ /**
157
+ * flash 系调价分界(UTC 2026-09-10T04:00:00Z,即北京时间 2026-09-10 12:00):
158
+ * 官方自此刻起 V4 Flash / V4 Flash Vision (Exp) 调价——谷档 1.5/0.05/4.5 →
159
+ * 1/0.02/4(峰 = 谷 × 2)。目录条目写现行(新)价;分界前的历史事件由
160
+ * {@link computeCostAt} 按 {@link FLASH_REPRICED_OFFPEAK} 回算旧价,与
161
+ * {@link PEAK_ERA_START_MS} 同一「按事件时刻分段适用规则」口径。
162
+ */
163
+ export declare const FLASH_REPRICE_MS: number;
156
164
  /** 计费时段档位:高峰 / 空闲(官方 DeepSeek 刊例价:高峰 = 空闲 × 2)。 */
157
165
  export type PriceTierId = 'peak' | 'offPeak';
158
166
  /** 成本显示币种:人民币(国内模型直价)/ 美元(国外模型直价或换算显示)。 */
@@ -299,21 +307,6 @@ export interface ModelEntry {
299
307
  /** 该条目当前按用户自定义单价计价(设置面板可维护);费率表标注「自定义」。 */
300
308
  userPriced?: boolean;
301
309
  }
302
- /**
303
- * Built-in catalog of current mainstream models as of 2026-08-16, priced from
304
- * each provider's official price page. Domestic providers are OpenAI-API
305
- * compatible and publish RMB prices directly; overseas providers publish USD
306
- * and convert through the exchange rate at estimate time. Retired models
307
- * (GPT-4o family, Gemini 2.x, GLM-4.x-lite, older Qwen) are deliberately
308
- * absent, as are Anthropic Claude models (their native API is not
309
- * OpenAI-compatible, so the harness cannot drive them directly). DeepSeek
310
- * keys match the harness stats file so real usage prices from the catalog;
311
- * unknown keys fall back to `other`.
312
- *
313
- * Time-of-day billing (peak/off-peak) is now real: DeepSeek V4 officially
314
- * splits peak (09:00-12:00 / 14:00-18:00 Beijing) at 2x the off-peak rate
315
- * from 2026-08-17, and Gemini's Flex tier discounts spare-capacity traffic.
316
- */
317
310
  export declare const MODEL_CATALOG: readonly ModelEntry[];
318
311
  /**
319
312
  * 真实 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.0.42",
4
+ "version": "1.0.44",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",