@kenz1117/dsh-ui-usage-billing 1.4.2 → 1.4.4

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.
@@ -13,8 +13,9 @@
13
13
  import { SessionLogOffset } from '@deepseek-ai/dsh-session/types';
14
14
  import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session/types';
15
15
  import type { TokenUsage } from '@deepseek-ai/dsh-llm';
16
- import { MODEL_KEY_ALIASES, resolveCatalogKey } from './client/pricing.ts';
17
- export { MODEL_KEY_ALIASES, resolveCatalogKey };
16
+ import { resolveCatalogKey } from './client/pricing.ts';
17
+ import { BUILTIN_MODEL_KEY_ALIASES } from './builtin-catalog.ts';
18
+ export { BUILTIN_MODEL_KEY_ALIASES, resolveCatalogKey };
18
19
  /**
19
20
  * 走订阅套餐(coding / token plan / opencode 订阅)的 provider id:这些通道的
20
21
  * 调用按套餐计费,不再按 token 计费,因此即使模型 id 与计费表撞名也一律豁免。
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 内置价格目录与别名表的纯数据模块:只随 node 半构建发布(宿主聚合计价使用),
3
+ * 并经 /api/billing/pricing 下发给客户端注入——client bundle 不携带这份数据
4
+ * (瘦身:lib/client.js 约 -30 KiB)。条目结构与消费逻辑见 ./client/pricing.ts。
5
+ */
6
+ import type { ModelEntry } from './client/pricing.ts';
7
+ export declare const BUILTIN_MODEL_CATALOG: readonly ModelEntry[];
8
+ /**
9
+ * 真实 provider model id → 计费目录键(`MODEL_CATALOG[].key`)的映射。未知 id
10
+ * 原样保留并落回 `other`(未知模型不估算费用)。聚合层(aggregate.ts)在折叠时
11
+ * 用同一张表把日志里的 model id 归并为目录键,客户端渲染(`modelOf`)也按它
12
+ * 解析,两侧共用一份映射,避免同一模型两侧不一致导致「未收录」。
13
+ */
14
+ export declare const BUILTIN_MODEL_KEY_ALIASES: Readonly<Record<string, string>>;
15
+ //# sourceMappingURL=builtin-catalog.d.ts.map
@@ -40,10 +40,11 @@ export type TrendMetric = 'cost' | 'tokens';
40
40
  * @param props.currency - display currency for the cost labels.
41
41
  * @param props.metric - `cost` (stacked per-model CNY, default) or `tokens` (single-color total tokens).
42
42
  */
43
- export declare function TrendChart({ data, models, currency, metric }: {
43
+ export declare function TrendChart({ data, models, currency, metric, t }: {
44
44
  data: readonly TrendPoint[];
45
45
  models?: readonly TrendSeriesModel[];
46
46
  currency?: CostCurrency;
47
47
  metric?: TrendMetric;
48
+ t?: (key: 'trendEmpty' | 'trendTotal' | 'calls') => string;
48
49
  }): React.ReactNode;
49
50
  //# sourceMappingURL=TrendChart.d.ts.map
@@ -20,18 +20,24 @@ export interface HeatmapDay {
20
20
  value: number;
21
21
  }
22
22
  /**
23
- * Render the month or year heatmap.
24
- * @param props.days - daily cost rows (keys are `YYYY-MM-DD`).
25
- * @param props.currency - display currency for the hover amount.
23
+ * Render the month or week-column heatmap.
24
+ * @param props.days - daily value rows (keys are `YYYY-MM-DD`); the value's
25
+ * meaning follows `props.unit` (CNY cost or raw token count).
26
+ * @param props.currency - display currency for the hover amount (cost unit).
26
27
  * @param props.now - anchor date (defaults to today); injectable for tests.
27
28
  * @param props.t - locale function (used for the legend labels).
28
- * @param props.range - `month` (calendar month) or `year` (last 52 weeks, GitHub style).
29
+ * @param props.range - `month` (calendar month), `half` (last 26 weeks, large
30
+ * cells — the screenshot-friendly Codex-style view) or `year` (last 52
31
+ * weeks, GitHub style).
32
+ * @param props.unit - value metric: `cost` (default, money formatting) or
33
+ * `tokens` (compact token formatting).
29
34
  */
30
- export declare function UsageHeatmap({ days, currency, now, t, range }: {
35
+ export declare function UsageHeatmap({ days, currency, now, t, range, unit }: {
31
36
  days: readonly HeatmapDay[];
32
37
  currency: CostCurrency;
33
38
  now?: Date;
34
- range?: 'month' | 'year';
39
+ range?: 'month' | 'half' | 'year';
40
+ unit?: 'cost' | 'tokens';
35
41
  t: (key: 'costAbbr' | 'noData' | 'heatmapLess' | 'heatmapMore') => string;
36
42
  }): React.ReactNode;
37
43
  //# sourceMappingURL=heatmap.d.ts.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 费率表附加行文案本地化:目录条目里的 `extraRows` `label`/`note`(`显式缓存创建`、
3
+ * `长期半价` 等)是数据层的中文字面量,同时充当渲染 key。与 provider-display.ts
4
+ * 同样的做法:不改动数据层形态,只在渲染层提供一张「中文文案 → 英文文案」映射,
5
+ * 未收录的字符串原样返回。集中一处便于统一管理与将来扩展第三语言。
6
+ */
7
+ type LabelLang = 'zh' | 'en';
8
+ /**
9
+ * 把费率表附加行的 label/note 按界面语言本地化:中文映射成英文,其余原样返回。
10
+ * 仅在渲染层调用,不影响数据层的中文字面量与 key 匹配。
11
+ * @param text - 数据层原始文案(可能为 undefined,例如没有 note 的行)。
12
+ * @param lang - 当前界面语言。
13
+ * @returns 本地化后的文案;`undefined` 原样传回。
14
+ */
15
+ export declare function localizeRowLabel(text: string, lang: LabelLang): string;
16
+ export declare function localizeRowLabel(text: undefined, lang: LabelLang): undefined;
17
+ export declare function localizeRowLabel(text: string | undefined, lang: LabelLang): string | undefined;
18
+ export {};
19
+ //# sourceMappingURL=label-display.d.ts.map
@@ -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' | 'trendEmpty' | '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' | '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' | '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' | 'heatmapMonth' | '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' | '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' | '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>;
@@ -341,14 +341,17 @@ export interface ModelEntry {
341
341
  */
342
342
  retired?: boolean;
343
343
  }
344
- export declare const MODEL_CATALOG: readonly ModelEntry[];
345
344
  /**
346
- * 真实 provider model id → 计费目录键(`MODEL_CATALOG[].key`)的映射。未知 id
347
- * 原样保留并落回 `other`(未知模型不估算费用)。聚合层(aggregate.ts)在折叠时
348
- * 用同一张表把日志里的 model id 归并为目录键,客户端渲染(`modelOf`)也按它
349
- * 解析,两侧共用一份映射,避免同一模型两侧不一致导致「未收录」。
345
+ * 注入内置目录与别名表(两侧各调用一次:宿主 activate、客户端首个 pricing 响应)。
346
+ * 重复注入整体替换并重建归一化索引。
347
+ * @param catalog - 内置目录条目(BUILTIN_MODEL_CATALOG 全量)。
348
+ * @param aliases - 内置别名表(BUILTIN_MODEL_KEY_ALIASES 全量)。
350
349
  */
351
- export declare const MODEL_KEY_ALIASES: Readonly<Record<string, string>>;
350
+ export declare function applyBuiltinCatalog(catalog: readonly ModelEntry[], aliases: Readonly<Record<string, string>>): void;
351
+ /** 内置目录当前快照:未注入为空数组(client 首帧前渲染加载态)。 */
352
+ export declare function modelCatalog(): readonly ModelEntry[];
353
+ /** 内置别名表当前快照:未注入为空对象。 */
354
+ export declare function modelKeyAliases(): Readonly<Record<string, string>>;
352
355
  /**
353
356
  * 模型 id 归一化:小写、去括号附注(如 `gpt5.6 luna(go)` 只看主体)、再去所有
354
357
  * 非字母数字分隔符(空格 / 横杠 / 点 / 下划线)。用于日志里的模型 id 与计费
@@ -32,6 +32,6 @@ export declare function RoundCostChart({ rounds, flags, currency, t }: {
32
32
  rounds: readonly RoundChartRow[];
33
33
  flags: readonly AnomalyFlag[];
34
34
  currency: CostCurrency;
35
- t: (key: 'model' | 'costAbbr') => string;
35
+ t: (key: 'model' | 'costAbbr' | 'roundsUnit') => string;
36
36
  }): React.ReactNode;
37
37
  //# sourceMappingURL=round-chart.d.ts.map
@@ -6,7 +6,7 @@
6
6
  * client 半区在「设置」Tab 渲染开关并写入同一命名空间。缺省的默认行为是关闭——
7
7
  * 避免该工具默认占用模型每次请求的上下文(coding 场景通常在仪表盘看用量)。
8
8
  */
9
- import type { UserPriceEntry } from './pricing.ts';
9
+ import type { CostCurrency, UserPriceEntry } from './pricing.ts';
10
10
  /** 设置命名空间 id(小写 kebab-case)。 */
11
11
  export declare const BILLING_SETTINGS_NAMESPACE = "ui-usage-billing";
12
12
  /** 该命名空间下用户可编辑的字段名。 */
@@ -107,6 +107,20 @@ export declare const LIVE_COST_BAR_PREF_EVENT = "dsh.ui-usage-billing.livecost-p
107
107
  export declare function loadLiveCostBarPrefs(): LiveCostBarPrefs;
108
108
  /** 写入即时代费条偏好。失败静默(展示偏好非关键)。 */
109
109
  export declare function saveLiveCostBarPrefs(prefs: LiveCostBarPrefs): void;
110
+ /**
111
+ * 显示币种(¥ / ≈$)。纯 client 偏好,存 localStorage;仪表盘、侧边栏卡片与
112
+ * 输入框胶囊分属不同 React 树,跨树同步走 localStorage + `CURRENCY_PREF_EVENT`
113
+ * CustomEvent(同文档即时生效,跨标签页靠 storage 事件)——与即时代费条偏好同一套做法。
114
+ */
115
+ export declare const CURRENCY_STORAGE_KEY = "dsh.ui-usage-billing.currency";
116
+ /** 币种切换后派发的 CustomEvent 名(另一棵树监听它即时重读)。 */
117
+ export declare const CURRENCY_PREF_EVENT = "dsh.ui-usage-billing.currency-pref";
118
+ /** 默认币种:人民币(保持历史行为)。 */
119
+ export declare const DEFAULT_CURRENCY: CostCurrency;
120
+ /** 读取显示币种(损坏/缺失/非法值一律回退默认)。仅在浏览器半区调用。 */
121
+ export declare function loadCurrency(): CostCurrency;
122
+ /** 写入显示币种。失败静默(展示偏好非关键)。 */
123
+ export declare function saveCurrency(currency: CostCurrency): void;
110
124
  /** 用户自定义单价(与 client/pricing.ts 的 `UserPriceEntry` 同形;type import,无运行时依赖)。 */
111
125
  export type StoredUserPrice = UserPriceEntry;
112
126
  /** 自定义价表:模型(+可选来源)→ 单价,条目列表(支持同名模型绑定不同中转站 origin)。 */
@@ -19,6 +19,16 @@ import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
19
19
  import { type UsageLedgerStore, type UsagePersistence } from './aggregate.ts';
20
20
  import type { CustomBalanceConfig, DeclaredEndpointConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
21
21
  import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscriptions.ts';
22
+ /** 校验 Host 头是本机回环(精确 127.0.0.0/8 / ::1 / localhost 或空,供 curl 不带 Host 的极简请求)。
23
+ * 拒绝 `127.0.0.1.attacker.com` 这类以 `127.` 开头但解析到外部的 DNS rebinding 域名:
24
+ * 只用 `startsWith('127.')` 会被它穿透,必须精确匹配回环 IP 的字面量。 */
25
+ /**
26
+ * 信任主机名归一化:去空白、去端口、转小写,丢弃空项。与请求侧
27
+ * `host.split(':')[0].toLowerCase()` 同口径,因此匹配忽略大小写与端口。
28
+ * @param hosts - 配置里的原始名单。
29
+ * @returns 归一化后的主机名集合(空集 = 与历史版本行为一致)。
30
+ */
31
+ export declare function normalizeTrustedHosts(hosts: readonly string[] | undefined): ReadonlySet<string>;
22
32
  /**
23
33
  * 回环防护守卫:仅接受回环 GET 请求(peer socket 地址 + Host 头同时校验)。
24
34
  * 不满足时返回 403 并结束响应;调用方在 handler 顶部调用,返回 false 即已拒绝。
@@ -26,7 +36,7 @@ import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscr
26
36
  * @param res - 当前响应。
27
37
  * @returns 是否放行;false = 已拒绝并结束响应。
28
38
  */
29
- export declare function guardLoopback(req: IncomingMessage, res: ServerResponse): boolean;
39
+ export declare function guardLoopback(req: IncomingMessage, res: ServerResponse, trustedHosts?: ReadonlySet<string>): boolean;
30
40
  /** Plugin configuration. */
31
41
  export interface UsageBillingConfig {
32
42
  /** Absolute path to a `.dsh-usage-stats.json` fallback file. */
@@ -60,6 +70,14 @@ export interface UsageBillingConfig {
60
70
  * 写死在 declarative.ts。缺省空。
61
71
  */
62
72
  declaredEndpoints?: readonly DeclaredEndpointConfig[];
73
+ /**
74
+ * 允许通过 Host 头校验的额外主机名(反向代理场景);缺省空,行为与历史版本完全一致。
75
+ * 仅在 peer socket 已通过回环校验后才参考:socket 校验仍为强制且不受本字段影响,
76
+ * 本名单只放宽「纵深防御」的第二层。匹配为**精确主机名**,忽略大小写与端口;
77
+ * 不支持后缀 / 通配符,因此 `evil.com` 永远无法满足 `trusted.com`。
78
+ * 例:`['llm.example.com']`。
79
+ */
80
+ trustedHosts?: string[];
63
81
  /** `usage_stats` 工具注入的组合 base(默认 false:不注入);与设置命名空间同字段,
64
82
  * 作为用户设置(设置 Tab 开关)的组合兜底。该工具占用每次请求的上下文,coding 场景多在仪表盘查看。 */
65
83
  enableUsageStatsTool?: boolean;
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.2",
4
+ "version": "1.4.4",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",
@@ -1,12 +0,0 @@
1
- /**
2
- * Vendor logos: inlined data URIs fetched from models.dev /logos/{id}.svg.
3
- * Kept to the highest-traffic providers only to hold the client bundle under the
4
- * DSH Store 256 KiB single-file cap. Every other provider falls back to a
5
- * brand-color letter badge in the UI (see VendorLogo in UsageBilling.tsx), so no
6
- * external asset or copyright risk is introduced for unlisted vendors. 月之暗面 is
7
- * a hand-redrawn minimal crescent — its official SVG is a multi-thousand-node path
8
- * that alone cost ~6.8 KiB.
9
- */
10
- export declare const VENDOR_LOGOS: Readonly<Record<string, string>>;
11
- export declare function vendorLogoOf(provider: string): string | undefined;
12
- //# sourceMappingURL=vendor-logos.d.ts.map