@kenz1117/dsh-ui-usage-billing 1.4.20 → 1.4.22

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
@@ -2720,6 +2720,7 @@ function serializeFold(fold) {
2720
2720
  bySite: Object.fromEntries(fold.bySite),
2721
2721
  unpricedModels: [...fold.unpricedModels],
2722
2722
  planCalls: Object.fromEntries(fold.planCalls),
2723
+ planSiteCalls: Object.fromEntries(fold.planSiteCalls),
2723
2724
  turns: fold.turns,
2724
2725
  perfDigest,
2725
2726
  perfHourDigest,
@@ -2741,6 +2742,7 @@ function deserializeFold(fold) {
2741
2742
  bySite: new Map(Object.entries(fold.bySite)),
2742
2743
  unpricedModels: new Set(fold.unpricedModels),
2743
2744
  planCalls: new Map(Object.entries(fold.planCalls)),
2745
+ planSiteCalls: new Map(Object.entries(fold.planSiteCalls ?? {})),
2744
2746
  turns: fold.turns,
2745
2747
  perf: [],
2746
2748
  perfDigest: new Map(Object.entries(fold.perfDigest)),
@@ -2796,6 +2798,14 @@ function modelDayCell(map, day, modelKey) {
2796
2798
  }
2797
2799
  return usageCell(models, modelKey);
2798
2800
  }
2801
+ /** 订阅计数的三维键:日期×模型×站点三元组的 JSON 形态(免分隔符转义歧义)。 */
2802
+ function planSiteKey(day, modelKey, siteKey) {
2803
+ return JSON.stringify([
2804
+ day,
2805
+ modelKey,
2806
+ siteKey
2807
+ ]);
2808
+ }
2799
2809
  /** Get-or-create one day×model×site cell inside the three-dimensional map. */
2800
2810
  function modelDaySiteCell(map, day, modelKey, siteKey) {
2801
2811
  let models = map.get(day);
@@ -2853,6 +2863,7 @@ function freshFold() {
2853
2863
  bySite: /* @__PURE__ */ new Map(),
2854
2864
  unpricedModels: /* @__PURE__ */ new Set(),
2855
2865
  planCalls: /* @__PURE__ */ new Map(),
2866
+ planSiteCalls: /* @__PURE__ */ new Map(),
2856
2867
  turns: [],
2857
2868
  perf: [],
2858
2869
  perfDigest: /* @__PURE__ */ new Map(),
@@ -3014,6 +3025,10 @@ function foldInto(fold, machine, events, subscriptionProviders, officialProvider
3014
3025
  foldUsage(usageCell(fold.bySite, siteBucket), usage, modelKey, subscription, event.time, official, thisCost, unverifiedChannel);
3015
3026
  foldUsage(usageCell(fold.byTier, tierAt(event.time)), usage, modelKey, subscription, event.time, official, thisCost, unverifiedChannel);
3016
3027
  if (subscription) fold.planCalls.set(modelKey, (fold.planCalls.get(modelKey) ?? 0) + 1);
3028
+ if (subscription) {
3029
+ const sitePlanKey = planSiteKey(day, modelKey, siteBucket);
3030
+ fold.planSiteCalls.set(sitePlanKey, (fold.planSiteCalls.get(sitePlanKey) ?? 0) + 1);
3031
+ }
3017
3032
  const turn = event.data.turn ?? -1;
3018
3033
  const state = turnState(turns, turn);
3019
3034
  state.model = modelKey;
@@ -3177,6 +3192,13 @@ function configFingerprint(subscriptionMatcher, officialProviderIds, routeAliase
3177
3192
  String(searchEstimate)
3178
3193
  ].join("|");
3179
3194
  }
3195
+ /** 会话格式拒读(宿主 SessionFormatUnsupportedError):日志完好,仅本构建读不了。
3196
+ * 宿主错误类显式落 error.name;经包装/重建的路径只在 message 里保留类名,两处都认。
3197
+ * 据此把「格式不受支持」与「损坏/其他读取失败」分开计数,供面板按原因归因(issue #84)。 */
3198
+ function isFormatUnsupportedError(error) {
3199
+ if (!(error instanceof Error)) return false;
3200
+ return error.name === "SessionFormatUnsupportedError" || error.message.includes("SessionFormatUnsupportedError");
3201
+ }
3180
3202
  /**
3181
3203
  * Create the incremental usage aggregator.
3182
3204
  * @param persistence - the session persistence service.
@@ -3210,7 +3232,7 @@ function createUsageAggregator(persistence, options = {}) {
3210
3232
  id,
3211
3233
  ...cwd === void 0 ? {} : { cwd },
3212
3234
  ...stamp === null ? {} : { stamp },
3213
- foldVersion: 17,
3235
+ foldVersion: 18,
3214
3236
  fingerprint: foldFingerprint,
3215
3237
  fold: serializeFold(fold)
3216
3238
  };
@@ -3286,12 +3308,22 @@ function createUsageAggregator(persistence, options = {}) {
3286
3308
  const included = /* @__PURE__ */ new Set();
3287
3309
  const folds = [];
3288
3310
  const skipped = [];
3311
+ let carriedUnreadable = 0;
3312
+ let carriedFormatUnreadable = 0;
3313
+ let formatUnreadable = 0;
3314
+ let noLedgerUnreadable = 0;
3289
3315
  let staleLedgerSessions = 0;
3290
3316
  for (const meta of metas) {
3291
3317
  const id = String(meta.id);
3292
3318
  seen.add(id);
3293
3319
  const stamp = await stampOf(meta);
3294
- if (stamp !== null && unreadable.get(id) === stamp) continue;
3320
+ const mark = unreadable.get(id);
3321
+ if (stamp !== null && mark !== void 0 && mark.stamp === stamp) {
3322
+ carriedUnreadable += 1;
3323
+ if (mark.format) carriedFormatUnreadable += 1;
3324
+ if (!ledger.has(id)) noLedgerUnreadable += 1;
3325
+ continue;
3326
+ }
3295
3327
  const hit = cache.get(id);
3296
3328
  if (hit !== void 0 && stamp !== null && hit.stamp === stamp) {
3297
3329
  cache.delete(id);
@@ -3307,7 +3339,7 @@ function createUsageAggregator(persistence, options = {}) {
3307
3339
  try {
3308
3340
  const cwd = meta.cwd;
3309
3341
  const ledgerRow = ledger.get(id);
3310
- if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 17 && ledgerRow.fingerprint === foldFingerprint) {
3342
+ if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 18 && ledgerRow.fingerprint === foldFingerprint) {
3311
3343
  const fold = deserializeFold(ledgerRow.fold);
3312
3344
  folds.push({
3313
3345
  id,
@@ -3374,7 +3406,13 @@ function createUsageAggregator(persistence, options = {}) {
3374
3406
  recordLedger(id, cwd, stamp, fold);
3375
3407
  } catch (error) {
3376
3408
  skipped.push(id);
3377
- if (stamp !== null) unreadable.set(id, stamp);
3409
+ const format = isFormatUnsupportedError(error);
3410
+ if (format) formatUnreadable += 1;
3411
+ if (!ledger.has(id)) noLedgerUnreadable += 1;
3412
+ if (stamp !== null) unreadable.set(id, {
3413
+ stamp,
3414
+ format
3415
+ });
3378
3416
  console.warn("[usage-billing] skip unreadable session", id, error);
3379
3417
  }
3380
3418
  }
@@ -3389,7 +3427,7 @@ function createUsageAggregator(persistence, options = {}) {
3389
3427
  for (const entry of ledger.values()) {
3390
3428
  if (included.has(entry.id)) continue;
3391
3429
  try {
3392
- const stale = (entry.foldVersion ?? 1) < 17 || entry.fingerprint !== foldFingerprint;
3430
+ const stale = (entry.foldVersion ?? 1) < 18 || entry.fingerprint !== foldFingerprint;
3393
3431
  folds.push({
3394
3432
  id: entry.id,
3395
3433
  ...entry.cwd === void 0 ? {} : { cwd: entry.cwd },
@@ -3415,6 +3453,7 @@ function createUsageAggregator(persistence, options = {}) {
3415
3453
  const bySite = /* @__PURE__ */ new Map();
3416
3454
  const unpricedModels = /* @__PURE__ */ new Set();
3417
3455
  const planCalls = /* @__PURE__ */ new Map();
3456
+ const planSiteCalls = /* @__PURE__ */ new Map();
3418
3457
  const sessionRows = [];
3419
3458
  const turnRows = [];
3420
3459
  const workspaceMap = /* @__PURE__ */ new Map();
@@ -3443,6 +3482,7 @@ function createUsageAggregator(persistence, options = {}) {
3443
3482
  for (const [toolName, count] of fold.byTool) byTool.set(toolName, (byTool.get(toolName) ?? 0) + count);
3444
3483
  for (const id of fold.unpricedModels) unpricedModels.add(id);
3445
3484
  for (const [modelKey, count] of fold.planCalls) planCalls.set(modelKey, (planCalls.get(modelKey) ?? 0) + count);
3485
+ for (const [key, count] of fold.planSiteCalls) planSiteCalls.set(key, (planSiteCalls.get(key) ?? 0) + count);
3446
3486
  for (const sample of fold.perf) {
3447
3487
  let modelAccum = perfModel.get(sample.model);
3448
3488
  if (modelAccum === void 0) {
@@ -3525,7 +3565,12 @@ function createUsageAggregator(persistence, options = {}) {
3525
3565
  return record;
3526
3566
  };
3527
3567
  const toModelDayRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries(models)]));
3528
- const toModelDaySiteRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries(sites)]))]));
3568
+ const toModelDaySiteRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries([...sites].map(([site, cell]) => {
3569
+ return [site, (planSiteCalls.get(planSiteKey(day, model, site)) ?? 0) === cell.calls && cell.calls > 0 ? {
3570
+ ...cell,
3571
+ plan: true
3572
+ } : cell];
3573
+ }))]))]));
3529
3574
  const modelKeys = /* @__PURE__ */ new Set([...perfModel.keys(), ...perfModelDigest.keys()]);
3530
3575
  const hourKeys = /* @__PURE__ */ new Set([...perfHourModel.keys(), ...perfHourDigest.keys()]);
3531
3576
  const perf = modelKeys.size === 0 && hourKeys.size === 0 ? void 0 : {
@@ -3547,6 +3592,8 @@ function createUsageAggregator(persistence, options = {}) {
3547
3592
  }).filter((entry) => entry !== null))];
3548
3593
  }))
3549
3594
  };
3595
+ const unreadableTotal = skipped.length + carriedUnreadable;
3596
+ const formatUnreadableTotal = formatUnreadable + carriedFormatUnreadable;
3550
3597
  lastDoc = {
3551
3598
  version: 4,
3552
3599
  updatedAt: now,
@@ -3570,7 +3617,9 @@ function createUsageAggregator(persistence, options = {}) {
3570
3617
  ...searchEstimate > 0 ? { searchCallEstimateCny: searchEstimate } : {},
3571
3618
  ...perf === void 0 ? {} : { perf },
3572
3619
  ...staleLedgerSessions > 0 ? { staleLedgerSessions } : {},
3573
- ...skipped.length > 0 ? { unreadableSessions: skipped.length } : {},
3620
+ ...unreadableTotal > 0 ? { unreadableSessions: unreadableTotal } : {},
3621
+ ...formatUnreadableTotal > 0 ? { unreadableFormatSessions: formatUnreadableTotal } : {},
3622
+ ...noLedgerUnreadable > 0 ? { unreadableNoLedgerSessions: noLedgerUnreadable } : {},
3574
3623
  byRole: (() => {
3575
3624
  const chars = roles.userChars + roles.toolChars;
3576
3625
  const userShare = chars > 0 ? roles.userChars / chars : .5;
@@ -295,8 +295,12 @@ export interface UsageStatsDocument {
295
295
  perf?: PerfStats;
296
296
  /** 只存在于账本、且缺 foldVersion 的旧会话数;无旧行时省略。 */
297
297
  staleLedgerSessions?: number;
298
- /** 读时迁移拒读而未统计的会话数(原始日志未动,上游修复后自动恢复);无拒读时省略。 */
298
+ /** 本轮未完整计入的会话数(新失败 + 负缓存命中,跨轮次稳定;有账本旧行的按存档计入最近可用点);无失败时省略。 */
299
299
  unreadableSessions?: number;
300
+ /** 未统计中因会话格式拒读(SessionFormatUnsupportedError)的子集;无格式拒读时省略。 */
301
+ unreadableFormatSessions?: number;
302
+ /** 未统计中无任何账本存档、完全未计入的子集(区别于有存档按最近可用点计入的);无存档缺失时省略。 */
303
+ unreadableNoLedgerSessions?: number;
300
304
  }
301
305
  /** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
302
306
  export interface RoleCost {
@@ -464,6 +468,10 @@ export interface SessionFold {
464
468
  unpricedModels: Set<string>;
465
469
  /** 每个模型 key 在本会话内走订阅通道的调用数(合并时跨会话累加判定 plan)。 */
466
470
  planCalls: Map<string, number>;
471
+ /** 三维格(日期×模型×站点)各自的订阅调用数,键 = {@link planSiteKey}:
472
+ * provider 视图的行 plan 按本格判定,不再借模型全局口径(混通道模型会把
473
+ * 本格全订阅的行判成无标记)。 */
474
+ planSiteCalls: Map<string, number>;
467
475
  /** 每轮费用明细(按轮次号升序,不含 sessionId);sessionId 在合并时补齐。 */
468
476
  turns: SessionTurnRow[];
469
477
  /** 性能样本(有可测 TTFT 的调用,按事件次序折叠);仅进程内折叠产生,
@@ -505,6 +513,8 @@ export interface SerializedSessionFold {
505
513
  bySite: Record<string, ModelUsage>;
506
514
  unpricedModels: string[];
507
515
  planCalls: Record<string, number>;
516
+ /** 三维格订阅计数;旧账本行缺失(合并按空处理,格级 plan 保守不置位)。 */
517
+ planSiteCalls?: Record<string, number>;
508
518
  turns: SessionTurnRow[];
509
519
  /** v16 起以摘要持久化性能(体积 O(模型数) 而非 O(样本数));旧行的
510
520
  * `perf` 逐样本数组由加载边界迁移转换为摘要。 */
@@ -554,7 +564,7 @@ export interface UsageLedgerDocument {
554
564
  * 会话费用只剩最近一段,issue #29)。
555
565
  * 持久账本行据此区分新旧算法:日志已删/不可读而只能沿用旧行时,UI 标注置信度提示。
556
566
  */
557
- export declare const FOLD_VERSION = 17;
567
+ export declare const FOLD_VERSION = 18;
558
568
  /**
559
569
  * 一次性账本迁移:id 唯一,apply 在加载边界对原始文档执行,已应用过的跳过。
560
570
  * 未来账本/schema 字段变更(重命名、拆桶、语义调整)时,在此追加一条迁移并
@@ -26,7 +26,7 @@ export interface TrendPoint {
26
26
  cost: number;
27
27
  /** API calls that day (total across models). */
28
28
  calls: number;
29
- /** Total tokens that day (input+output+cache); used by the `tokens` metric. */
29
+ /** Total tokens that day (input+output — stats.input already includes cache hit/miss); used by the `tokens` metric. */
30
30
  tokens?: number;
31
31
  /** Per-model cost that day (stats key → CNY); absent entries plot zero. */
32
32
  byModel?: Readonly<Record<string, number>>;
@@ -110,6 +110,15 @@ export declare function activeDaysOf(byDay: Record<string, {
110
110
  export declare function streakDaysOf(byDay: Record<string, {
111
111
  cost: number;
112
112
  }>, now?: number): number;
113
+ /** 按日 Token 口径:输入 + 输出。stats.input 本身已含缓存命中与未命中
114
+ * (aggregate foldUsage:input = cacheHit + cacheMiss),再叠加缓存字段即重复
115
+ * 计数——趋势图/热力图/触发卡/峰值日的按日 Token 一律走本函数(issue #85);
116
+ * 跨日合计在各行之上累加(KPI 见 kpiAgg,触发卡见 month/weekTokens)。
117
+ * 导出供测试:纯函数。 */
118
+ export declare function dayTokensOf(day: {
119
+ input: number;
120
+ output: number;
121
+ }): number;
113
122
  /**
114
123
  * 近 7 天费用序列(含今天,缺日补 0):触发卡 hover 速览的迷你柱数据源。
115
124
  * 导出供测试:纯函数(日期取本地时区)。
@@ -250,6 +259,8 @@ export interface UsageStats {
250
259
  cacheHit: number;
251
260
  cacheMiss: number;
252
261
  cost: number;
262
+ /** 该格全部调用都走订阅通道(聚合侧按三维格置位);旧快照缺失,回退全局口径。 */
263
+ plan?: boolean;
253
264
  }>>>;
254
265
  /**
255
266
  * 峰谷分桶(全量逐调用真实判档):1.0.8 起服务端按调用时刻精确归桶,
@@ -313,8 +324,12 @@ export interface UsageStats {
313
324
  perf?: ClientPerf;
314
325
  /** 旧版算法账本行兜底的会话数(模型归属可能失真);0 或缺省 = 全部数据可信。 */
315
326
  staleLedgerSessions?: number;
316
- /** 读时迁移拒读而未统计的会话数;0 或缺省 = 全部会话已统计。 */
327
+ /** 本轮未完整计入的会话数(跨轮次稳定);0 或缺省 = 全部会话已统计。 */
317
328
  unreadableSessions?: number;
329
+ /** 未统计中因会话格式拒读的子集(过新待宿主升级、过旧需迁移);0 或缺省 = 无格式拒读。 */
330
+ unreadableFormatSessions?: number;
331
+ /** 未统计中无账本存档、完全未计入的子集;0 或缺省 = 拒读会话均有存档兜底。 */
332
+ unreadableNoLedgerSessions?: number;
318
333
  /** 插件版本号(服务端读自包 package.json;旧快照缺失)。 */
319
334
  pluginVersion?: string;
320
335
  }
@@ -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' | 'kpiRange' | 'avgRangeToday' | 'avgRange7d' | 'avgRangeWeek' | 'avgRangeMonth' | 'avgRangeAll' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'pricingSearch' | 'bandPinHint' | 'trendEmpty' | 'trendTotal' | 'roundsUnit' | 'healthModelsLive' | 'healthVendorsDown' | 'healthVendorsUnavailable' | 'budget' | 'budgetRemain' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'directTag' | 'relayTag' | 'unknownTag' | 'unknownChannelHint' | 'planCountUnit' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'pricingSyncedAt' | 'pricingSyncNow' | 'pricingSyncing' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'recharge' | 'rechargeHint' | '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' | 'floatPrimaryToday' | 'floatPrimaryWeek' | 'floatPrimaryMonth' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'cardSpan' | 'cardSpanDay' | 'cardSpanWeek' | 'cardSpanMonth' | 'triggerMonthTokens' | 'triggerTodayTokens' | 'triggerWeekTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'unreadableNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'currencyEur' | 'language' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'peakAlertDescPeakZhipu' | 'peakAlertDescOffZhipu' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'tierAlertEnterPeakZhipu' | 'tierAlertEnterOffZhipu' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionTag' | 'free' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapHalf' | 'heatmapMonth' | 'metricCost' | 'metricTokens' | 'heatmapTotal' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | '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' | 'expandAll' | 'collapseAll' | 'todayOnly' | 'todayNoConsumption';
2
+ export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'kpiRange' | 'avgRangeToday' | 'avgRange7d' | 'avgRangeWeek' | 'avgRangeMonth' | 'avgRangeAll' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'pricingSearch' | 'bandPinHint' | 'trendEmpty' | 'trendTotal' | 'roundsUnit' | 'healthModelsLive' | 'healthVendorsDown' | 'healthVendorsUnavailable' | 'budget' | 'budgetRemain' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'directTag' | 'relayTag' | 'unknownTag' | 'unknownChannelHint' | 'planCountUnit' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'pricingSyncedAt' | 'pricingSyncNow' | 'pricingSyncing' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'recharge' | 'rechargeHint' | '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' | 'floatPrimaryToday' | 'floatPrimaryWeek' | 'floatPrimaryMonth' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'cardSpan' | 'cardSpanDay' | 'cardSpanWeek' | 'cardSpanMonth' | 'triggerMonthTokens' | 'triggerTodayTokens' | 'triggerWeekTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'unreadableNotice' | 'unreadableFormatNotice' | 'unreadableNoLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'currencyEur' | 'language' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'peakAlertDescPeakZhipu' | 'peakAlertDescOffZhipu' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'tierAlertEnterPeakZhipu' | 'tierAlertEnterOffZhipu' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionTag' | 'cellEstimateHint' | 'free' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapHalf' | 'heatmapMonth' | 'metricCost' | 'metricTokens' | 'heatmapTotal' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | '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' | 'expandAll' | 'collapseAll' | 'todayOnly' | 'todayNoConsumption';
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>;
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.4.20",
4
+ "version": "1.4.22",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",