@kenz1117/dsh-ui-usage-billing 1.4.6 → 1.4.8

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
@@ -318,7 +318,7 @@ function tierAtWithBounds(timeMs, bounds) {
318
318
  }
319
319
  /** 时刻是否落在北京时间周末(周六/周日)。 */
320
320
  function isBeijingWeekend(timeMs) {
321
- const day = new Date(timeMs + 8 * 36e5).getUTCDay();
321
+ const day = new Date(timeMs + 288e5).getUTCDay();
322
322
  return day === 0 || day === 6;
323
323
  }
324
324
  /** 峰谷切换边界(北京时间的当日分钟数):09:00 / 12:00 / 14:00 / 18:00。 */
@@ -330,7 +330,7 @@ const TIER_BOUNDARY_MINUTES = [
330
330
  ];
331
331
  /** 北京时间的当日毫秒数(0–86,400,000)。 */
332
332
  function beijingMillisOfDay(timeMs) {
333
- return ((timeMs + 8 * 36e5) % 864e5 + 864e5) % 864e5;
333
+ return ((timeMs + 288e5) % 864e5 + 864e5) % 864e5;
334
334
  }
335
335
  /**
336
336
  * 内置目录与别名表的运行时注入态:数据本体在 src/builtin-catalog.ts(仅随 node
@@ -3275,8 +3275,8 @@ function createUsageAggregator(persistence, options = {}) {
3275
3275
  };
3276
3276
  const toModelDayRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries(models)]));
3277
3277
  const toModelDaySiteRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries(sites)]))]));
3278
- const modelKeys = new Set([...perfModel.keys(), ...perfModelDigest.keys()]);
3279
- const hourKeys = new Set([...perfHourModel.keys(), ...perfHourDigest.keys()]);
3278
+ const modelKeys = /* @__PURE__ */ new Set([...perfModel.keys(), ...perfModelDigest.keys()]);
3279
+ const hourKeys = /* @__PURE__ */ new Set([...perfHourModel.keys(), ...perfHourDigest.keys()]);
3280
3280
  const perf = modelKeys.size === 0 && hourKeys.size === 0 ? void 0 : {
3281
3281
  byModel: Object.fromEntries([...modelKeys].map((model) => {
3282
3282
  const live = perfModel.get(model);
@@ -3287,7 +3287,7 @@ function createUsageAggregator(persistence, options = {}) {
3287
3287
  byHourModel: Object.fromEntries([...hourKeys].sort().map((hour) => {
3288
3288
  const liveModels = perfHourModel.get(hour);
3289
3289
  const digestModels = perfHourDigest.get(hour);
3290
- const modelKeysInHour = new Set([...liveModels?.keys() ?? [], ...digestModels?.keys() ?? []]);
3290
+ const modelKeysInHour = /* @__PURE__ */ new Set([...liveModels?.keys() ?? [], ...digestModels?.keys() ?? []]);
3291
3291
  return [hour, Object.fromEntries([...modelKeysInHour].map((model) => {
3292
3292
  const live = liveModels?.get(model);
3293
3293
  const restored = digestModels?.get(model);
@@ -3319,6 +3319,7 @@ function createUsageAggregator(persistence, options = {}) {
3319
3319
  ...searchEstimate > 0 ? { searchCallEstimateCny: searchEstimate } : {},
3320
3320
  ...perf === void 0 ? {} : { perf },
3321
3321
  ...staleLedgerSessions > 0 ? { staleLedgerSessions } : {},
3322
+ ...skipped.length > 0 ? { unreadableSessions: skipped.length } : {},
3322
3323
  byRole: (() => {
3323
3324
  const chars = roles.userChars + roles.toolChars;
3324
3325
  const userShare = chars > 0 ? roles.userChars / chars : .5;
@@ -3490,7 +3491,11 @@ function tc3Authorization(secretId, secretKey, payload, timestamp) {
3490
3491
  const canonicalRequest = `POST\n/\n\n${`content-type:application/json; charset=utf-8\nhost:${TOKENHUB_HOST}\n`}\ncontent-type;host\n${createHash("sha256").update(payload).digest("hex")}`;
3491
3492
  const hashedCanonical = createHash("sha256").update(canonicalRequest).digest("hex");
3492
3493
  const stringToSign = `TC3-HMAC-SHA256\n${String(timestamp)}\n${date}/${TOKENHUB_SERVICE}/tc3_request\n${hashedCanonical}`;
3493
- return `TC3-HMAC-SHA256 Credential=${secretId}/${date}/${TOKENHUB_SERVICE}/tc3_request, SignedHeaders=content-type;host, Signature=${createHmac("sha256", createHmac("sha256", createHmac("sha256", createHmac("sha256", date).update(secretKey).digest()).update(TOKENHUB_SERVICE).digest()).update("tc3_request").digest()).update(stringToSign).digest("hex")}`;
3494
+ const kDate = createHmac("sha256", date).update(secretKey).digest();
3495
+ const kService = createHmac("sha256", kDate).update(TOKENHUB_SERVICE).digest();
3496
+ const kSigning = createHmac("sha256", kService).update("tc3_request").digest();
3497
+ const signature = createHmac("sha256", kSigning).update(stringToSign).digest("hex");
3498
+ return `TC3-HMAC-SHA256 Credential=${secretId}/${date}/${TOKENHUB_SERVICE}/tc3_request, SignedHeaders=content-type;host, Signature=${signature}`;
3494
3499
  }
3495
3500
  /**
3496
3501
  * 调用一次 TokenHub 管控面接口:TC3 签名 + 超时保护,返回响应 JSON 的 `Response`。
@@ -4254,7 +4259,7 @@ const declaredGate = createCooldownGate({
4254
4259
  cooldownMs: 6e4
4255
4260
  });
4256
4261
  /** 会触及原型链的路径段:一律拒绝(读结果不是文档自带字段)。 */
4257
- const FORBIDDEN_SEGMENTS = new Set([
4262
+ const FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set([
4258
4263
  "__proto__",
4259
4264
  "constructor",
4260
4265
  "prototype"
@@ -4832,7 +4837,7 @@ function asPrice(value) {
4832
4837
  /** models.dev 常把同一模型收录在多个 provider 下(厂商 + 转售)。这些 id 是模型
4833
4838
  * 自身的厂商,其报价视为权威:转售可能加价,也可能省略 cache_read 而落到下方
4834
4839
  * `input * 0.1` 的猜测值。去重时优先采用。 */
4835
- const FIRST_PARTY_PRICE_SOURCES = new Set([
4840
+ const FIRST_PARTY_PRICE_SOURCES = /* @__PURE__ */ new Set([
4836
4841
  "anthropic",
4837
4842
  "openai",
4838
4843
  "google",
@@ -4861,7 +4866,7 @@ const FIRST_PARTY_PRICE_SOURCES = new Set([
4861
4866
  */
4862
4867
  function buildExtraModels(data) {
4863
4868
  if (data === null || typeof data !== "object") return [];
4864
- const catalogKeys = new Set([...BUILTIN_MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(BUILTIN_MODEL_KEY_ALIASES).map((key) => BUILTIN_MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
4869
+ const catalogKeys = /* @__PURE__ */ new Set([...BUILTIN_MODEL_CATALOG.map((entry) => entry.key.toLowerCase()), ...Object.keys(BUILTIN_MODEL_KEY_ALIASES).map((key) => BUILTIN_MODEL_KEY_ALIASES[key]?.toLowerCase() ?? key.toLowerCase())]);
4865
4870
  const offers = /* @__PURE__ */ new Map();
4866
4871
  const familyVendorVotes = /* @__PURE__ */ new Map();
4867
4872
  for (const [providerId, providerDoc] of Object.entries(data)) {
@@ -5784,7 +5789,7 @@ const relayGate = createCooldownGate({
5784
5789
  /** Abort a relay fetch when the upstream hangs beyond this budget. */
5785
5790
  const FETCH_TIMEOUT_MS = 8e3;
5786
5791
  /** 指纹识别缓存 TTL(毫秒):识别结果低频变化,5 分钟内同 origin 不再重复探测。 */
5787
- const FINGERPRINT_TTL_MS = 300 * 1e3;
5792
+ const FINGERPRINT_TTL_MS = 3e5;
5788
5793
  /** 每 origin 的识别结果缓存:`kind` 是识别出的中转站程序,`at` 是探测时刻。 */
5789
5794
  const fingerprintCache = /* @__PURE__ */ new Map();
5790
5795
  /** Number, or null when the value is not a finite number (nor numeric string). */
@@ -5920,7 +5925,7 @@ function originOf(baseURL) {
5920
5925
  * 子路径只会得到 404/非中转站格式,因而面板应排除它们,避免误判为
5921
5926
  * 「未识别」。中转站面板只列真正的第三方中转程序。
5922
5927
  */
5923
- const OFFICIAL_HOSTS = new Set([
5928
+ const OFFICIAL_HOSTS = /* @__PURE__ */ new Set([
5924
5929
  "api.deepseek.com",
5925
5930
  "api.openai.com",
5926
5931
  "open.bigmodel.cn",
@@ -6190,13 +6195,13 @@ const usageBillingSettingsNs = validateSettingsNamespace(BILLING_SETTINGS_NAMESP
6190
6195
  /** 该命名空间的 wire schema:`enableUsageStatsTool` 布尔,默认关闭(issue 诉求)。 */
6191
6196
  const UsageBillingSettingsSchema = z.object({ [ENABLE_USAGE_STATS_TOOL_FIELD]: z.boolean().default(false) });
6192
6197
  /** 实时定价的后台刷新间隔(毫秒):汇率/模型价低频变化,6 小时一次足够。 */
6193
- const PRICING_REFRESH_INTERVAL_MS = 360 * 60 * 1e3;
6198
+ const PRICING_REFRESH_INTERVAL_MS = 216e5;
6194
6199
  /** 历史回放预热的延迟:等宿主启动高峰(插件加载 / 路由挂载)过去再全量折叠,
6195
6200
  * 避免抢启动期的 CPU;纯延迟不阻塞任何请求,面板提前打开也只会提前聚合。 */
6196
6201
  const WARMUP_DELAY_MS = 3e3;
6197
6202
  /** 订阅套餐额度缓存时长(毫秒):上游配额 API 低频变化,5 分钟足够。 */
6198
- const SUBSCRIPTION_CACHE_MS = 300 * 1e3;
6199
- const BALANCE_CACHE_MS = 300 * 1e3;
6203
+ const SUBSCRIPTION_CACHE_MS = 3e5;
6204
+ const BALANCE_CACHE_MS = 3e5;
6200
6205
  /** DeepSeek 余额查询的默认凭据引用(与 llm-deepseek 的默认引用一致)。 */
6201
6206
  const DEFAULT_BALANCE_API_KEY_ENV = "DEEPSEEK_API_KEY";
6202
6207
  /**
@@ -6213,7 +6218,7 @@ const SNAPSHOT_INTERVAL_MS = 3e5;
6213
6218
  * 轻量用户暖缓存下的聚合是毫秒级,预算几乎不会触发。 */
6214
6219
  const RESPONSE_BUDGET_MS = 1500;
6215
6220
  /** 鉴权失败告警冷却(毫秒):同一 provider 在窗口内只提示一次,避免 30 秒轮询刷屏。 */
6216
- const AUTH_WARN_COOLDOWN_MS = 1800 * 1e3;
6221
+ const AUTH_WARN_COOLDOWN_MS = 18e5;
6217
6222
  /**
6218
6223
  * 鉴权失败分类告警(P1-5):余额 / 订阅查询返回 unauthorized 时,按
6219
6224
  * `source:provider` 去重并冷却告警,提示检查 llm-pi-ai 里该 provider 的 apiKeyEnv。
@@ -6564,7 +6569,8 @@ function apply(ctx, config = {}) {
6564
6569
  })();
6565
6570
  const persistReconcileRef = () => {
6566
6571
  if (reconcileRef === null) return;
6567
- writeFileAtomic(reconcilePath, JSON.stringify({ ref: reconcileRef }), {
6572
+ const payload = JSON.stringify({ ref: reconcileRef });
6573
+ writeFileAtomic(reconcilePath, payload, {
6568
6574
  mode: 384,
6569
6575
  dirMode: 448
6570
6576
  }).catch(() => {});
@@ -6691,7 +6697,9 @@ function apply(ctx, config = {}) {
6691
6697
  }
6692
6698
  }), "usage-billing: notify-claim route");
6693
6699
  ctx.inject(["settings"], (sctx) => {
6694
- const scope = sctx.settings.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
6700
+ const legacy = sctx.settings;
6701
+ if (typeof legacy.register !== "function") return;
6702
+ const scope = legacy.register(usageBillingSettingsNs, UsageBillingSettingsSchema, { base: { enableUsageStatsTool: config.enableUsageStatsTool ?? false } });
6695
6703
  usageSettingsScope = scope;
6696
6704
  ctx.inject(["tools"], (toolsCtx) => {
6697
6705
  if (scope.get().enableUsageStatsTool) toolsCtx.tools.register(defineTool({
@@ -274,6 +274,8 @@ export interface UsageStatsDocument {
274
274
  perf?: PerfStats;
275
275
  /** 只存在于账本、且缺 foldVersion 的旧会话数;无旧行时省略。 */
276
276
  staleLedgerSessions?: number;
277
+ /** 读时迁移拒读而未统计的会话数(原始日志未动,上游修复后自动恢复);无拒读时省略。 */
278
+ unreadableSessions?: number;
277
279
  }
278
280
  /** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
279
281
  export interface RoleCost {
@@ -313,6 +313,8 @@ export interface UsageStats {
313
313
  perf?: ClientPerf;
314
314
  /** 旧版算法账本行兜底的会话数(模型归属可能失真);0 或缺省 = 全部数据可信。 */
315
315
  staleLedgerSessions?: number;
316
+ /** 读时迁移拒读而未统计的会话数;0 或缺省 = 全部会话已统计。 */
317
+ unreadableSessions?: number;
316
318
  /** 插件版本号(服务端读自包 package.json;旧快照缺失)。 */
317
319
  pluginVersion?: string;
318
320
  }
@@ -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' | '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' | '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';
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' | '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';
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>;
@@ -15,10 +15,18 @@
15
15
  import type { IncomingMessage, ServerResponse } from 'node:http';
16
16
  import type { Context } from '@deepseek-ai/cordis';
17
17
  import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
18
- import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
19
18
  import { type UsageLedgerStore, type UsagePersistence } from './aggregate.ts';
20
19
  import type { CustomBalanceConfig, DeclaredEndpointConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
21
20
  import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscriptions.ts';
21
+ /** 设置服务的读取面:两代宿主运行时都提供 describe(条目键为 profile entry id)。 */
22
+ interface SettingsReader {
23
+ describe(options?: {
24
+ redactSecrets?: boolean;
25
+ }): readonly {
26
+ ns: string;
27
+ value: unknown;
28
+ }[];
29
+ }
22
30
  /** 校验 Host 头是本机回环(精确 127.0.0.0/8 / ::1 / localhost 或空,供 curl 不带 Host 的极简请求)。
23
31
  * 拒绝 `127.0.0.1.attacker.com` 这类以 `127.` 开头但解析到外部的 DNS rebinding 域名:
24
32
  * 只用 `startsWith('127.')` 会被它穿透,必须精确匹配回环 IP 的字面量。 */
@@ -127,7 +135,7 @@ export interface PiAiProviderRoute {
127
135
  * @param settings - the settings service (reads the llm-pi-ai namespace).
128
136
  * @returns `<route> → { baseURL? }`;命名空间不可读时返回空。
129
137
  */
130
- export declare function readPiAiProviderRoutes(settings: SettingsProvider): Readonly<Record<string, {
138
+ export declare function readPiAiProviderRoutes(settings: SettingsReader): Readonly<Record<string, {
131
139
  baseURL?: string;
132
140
  }>>;
133
141
  /**
@@ -137,7 +145,7 @@ export declare function readPiAiProviderRoutes(settings: SettingsProvider): Read
137
145
  * @param settings - the settings service (reads the llm-pi-ai namespace).
138
146
  * @param credentials - the credentials service (resolves the env refs).
139
147
  */
140
- export declare function resolveSubscriptionKeys(settings: SettingsProvider, credentials: CredentialProvider): Promise<{
148
+ export declare function resolveSubscriptionKeys(settings: SettingsReader, credentials: CredentialProvider): Promise<{
141
149
  keys: SubscriptionKeys;
142
150
  identified: IdentifiedSubscriptionPlan[];
143
151
  }>;
@@ -159,4 +167,5 @@ export declare function adaptSessionPersistence(raw: unknown): UsagePersistence;
159
167
  * @param config - optional statsPath override.
160
168
  */
161
169
  export declare function apply(ctx: Context, config?: UsageBillingConfig): void;
170
+ export {};
162
171
  //# sourceMappingURL=index.d.ts.map
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.6",
4
+ "version": "1.4.8",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",
@@ -66,8 +66,12 @@
66
66
  "0.1.5-alpha.2": "compatible",
67
67
  "0.1.5-rc.1": "compatible",
68
68
  "0.1.5-rc.2": "compatible",
69
+ "0.1.5-rc.3": "compatible",
69
70
  "0.1.6-alpha.1": "compatible",
70
- "0.1.6-alpha.2": "compatible"
71
+ "0.1.6-alpha.2": "compatible",
72
+ "0.1.7-alpha.1": "compatible",
73
+ "0.1.7-alpha.2": "compatible",
74
+ "0.1.7-rc.1": "compatible"
71
75
  }
72
76
  }
73
77
  },
@@ -112,43 +116,69 @@
112
116
  "@deepseek-ai/cordis": "*",
113
117
  "@deepseek-ai/cordis-plugin-include": "^1.0.7",
114
118
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
115
- "@deepseek-ai/dsh-api-gateway": "0.1.2-rc.1",
116
- "@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
117
- "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
118
- "@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
119
- "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
120
- "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
121
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
122
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
123
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
124
- "@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-rc.1",
125
- "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
126
- "@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
127
- "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
128
- "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
129
- "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
130
- "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
131
- "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
132
- "@deepseek-ai/dsh-session": "0.1.2-rc.1",
133
- "@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
134
- "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
135
- "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
136
- "@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
119
+ "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.1",
120
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
121
+ "@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.1",
122
+ "@deepseek-ai/dsh-atomic-write": "0.1.7-rc.1",
123
+ "@deepseek-ai/dsh-agent": "0.1.7-rc.1",
124
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
125
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
126
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
127
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
128
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
129
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.1",
130
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
131
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.1",
132
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
133
+ "@deepseek-ai/dsh-home-paths": "0.1.7-rc.1",
134
+ "@deepseek-ai/dsh-invariants": "0.1.7-rc.1",
135
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.1",
136
+ "@deepseek-ai/dsh-ptc-runtime": "0.1.7-rc.1",
137
+ "@deepseek-ai/dsh-sandbox": "0.1.7-rc.1",
138
+ "@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.1",
139
+ "@deepseek-ai/dsh-scope": "0.1.7-rc.1",
140
+ "@deepseek-ai/dsh-session": "0.1.7-rc.1",
141
+ "@deepseek-ai/dsh-session-persistence": "0.1.7-rc.1",
142
+ "@deepseek-ai/dsh-settings": "0.1.7-rc.1",
143
+ "@deepseek-ai/dsh-system-prompt": "0.1.7-rc.1",
144
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.1",
145
+ "@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.1",
146
+ "@deepseek-ai/dsh-user-approval": "0.1.7-rc.1",
147
+ "@deepseek-ai/dsh-util-workspace-path": "0.1.7-rc.1",
137
148
  "@deepseek-ai/schemastery": "^3.18.2",
149
+ "@shikijs/langs": "^4.3.1",
138
150
  "@testing-library/dom": "^10.4.1",
139
151
  "@testing-library/react": "^16.1.0",
140
152
  "@types/node": "^22.20.0",
141
153
  "@types/react": "~18.3.1",
142
154
  "@types/react-dom": "^19.2.7",
155
+ "immer": "^10.1.1",
156
+ "anser": "^2.3.5",
157
+ "diff": "^9.0.0",
143
158
  "jsdom": "^26.1.0",
159
+ "katex": "^0.16.47",
144
160
  "lightningcss": "^1.32.0",
161
+ "mdast-util-from-markdown": "^2.0.3",
162
+ "mdast-util-gfm": "^3.1.0",
163
+ "mdast-util-math": "^3.0.0",
164
+ "micromark-core-commonmark": "^2.0.3",
165
+ "micromark-extension-gfm": "^3.0.0",
166
+ "micromark-extension-math": "^3.1.0",
167
+ "micromark-factory-space": "^2.0.1",
168
+ "micromark-util-character": "^2.1.1",
169
+ "micromark-util-classify-character": "^2.0.1",
170
+ "micromark-util-sanitize-uri": "^2.0.1",
171
+ "micromark-util-symbol": "^2.0.1",
145
172
  "react": "^18.2.0",
146
173
  "react-dom": "^18.2.0",
147
174
  "semver": "^7.7.2",
175
+ "shiki": "^4.3.1",
176
+ "simple-icons": "^16.32.0",
148
177
  "tsdown": "^0.22.2",
149
178
  "typescript": "^6.0.3",
150
179
  "use-sync-external-store": "1.2.0",
151
- "vitest": "^4.1.8"
180
+ "vitest": "^4.1.8",
181
+ "zustand": "~4.4.7"
152
182
  },
153
183
  "files": [
154
184
  "lib/index.js",