@kenz1117/dsh-ui-usage-billing 0.9.7 → 0.9.9

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.
@@ -27,6 +27,34 @@ export declare const DEFAULT_SUBSCRIPTION_PROVIDERS: readonly string[];
27
27
  * 调用与费用分桶展示;部署可由配置覆盖(见 {@link AggregateOptions})。
28
28
  */
29
29
  export declare function isOfficialProvider(provider: string): boolean;
30
+ /** 一个 provider 路由的站点视图(来自 llm-pi-ai providers 的 baseURL)。 */
31
+ export interface ProviderRouteView {
32
+ /** 该路由配置的端点地址;无值 = 直连厂商(非中转站)。 */
33
+ baseURL?: string;
34
+ }
35
+ /** 站点归属分类:site=中转站(有 baseURL origin);direct=直连;unknown=未知路由(配置已删)。 */
36
+ export type SiteKind = 'site' | 'direct' | 'unknown';
37
+ /** 一个 provider 路由归类后的站点引用。 */
38
+ export interface SiteRef {
39
+ kind: SiteKind;
40
+ /** 站点归一化 origin(仅 site)。 */
41
+ origin?: string;
42
+ /** 原 provider 路由名。 */
43
+ provider: string;
44
+ }
45
+ /** 由 baseURL 归一化出站点 origin(协议 + 主机 + 端口);解析失败回退原值。 */
46
+ export declare function siteOriginOf(baseURL: string): string;
47
+ /**
48
+ * 把一个 provider 路由归类为站点引用。判定顺序(与路由在 provider 配置里的状态一致):
49
+ * - 路由存在于当前配置且配了 baseURL → 中转站 `site`(按 origin 归组,同站多 key 合并);
50
+ * - 路由存在于当前配置但无 baseURL → 厂商直连 `direct`;
51
+ * - 路由不在当前配置里 → `unknown`(改过名 / 删除过,是「读不到」而非「直连」)。
52
+ * @param provider - 会话日志里的 provider 路由名(request/header 的 `config.provider`)。
53
+ * @param routes - 当前 provider 路由视图(来自 llm-pi-ai providers)。
54
+ */
55
+ export declare function siteRefOf(provider: string, routes: Readonly<Record<string, ProviderRouteView>>): SiteRef;
56
+ /** 站点桶的稳定 key:`site:<origin>` 与 `direct:<provider>` 分开,`unknown` 单一桶。 */
57
+ export declare function siteBucketKey(ref: SiteRef): string;
30
58
  /** Aggregation tuning options. */
31
59
  export interface AggregateOptions {
32
60
  /** 订阅制 provider id 列表;默认 {@link DEFAULT_SUBSCRIPTION_PROVIDERS}。 */
@@ -36,6 +64,12 @@ export interface AggregateOptions {
36
64
  /** 每会话折叠缓存的上限(默认 {@link DEFAULT_MAX_CACHE_SESSIONS});
37
65
  * 超限时按最近使用先后淘汰最久未用的会话,防长期运行内存膨胀。 */
38
66
  maxCacheSessions?: number;
67
+ /** 中转站归组来源:返回当前 provider 路由视图(llm-pi-ai providers 的 baseURL)。
68
+ * 每次聚合时调用取最新值;缺省时全部路由按「未知路由」处理(无配置发现)。 */
69
+ resolveRoutes?: () => Readonly<Record<string, ProviderRouteView>>;
70
+ /** 工作区标题解析:给定会话 cwd 返回项目显示标题;undefined = 回退到 cwd 末级目录名
71
+ * (host 的 workspaceRegistry 为可选依赖,缺失时不注入,行为保持不变)。 */
72
+ resolveWorkspaceTitle?: (cwd: string) => string | undefined;
39
73
  }
40
74
  /** 每会话折叠缓存默认上限:超过则按 LRU 淘汰(P1-6 峰值内存治理)。 */
41
75
  export declare const DEFAULT_MAX_CACHE_SESSIONS = 400;
@@ -101,6 +135,12 @@ export interface UsageStatsDocument {
101
135
  byTurn?: TurnUsageRow[];
102
136
  /** 工作区聚合:按 cwd 末级目录归并,按费用倒序;旧快照可能缺失。 */
103
137
  byWorkspace?: WorkspaceUsageRow[];
138
+ /**
139
+ * 中转站归组:按 provider 路由归类到站点(有 baseURL 按 origin 归组)、直连、未知路由;
140
+ * key 为 {@link siteBucketKey} 的稳定值(`site:<origin>` / `direct:<provider>` / `unknown`)。
141
+ * 旧快照可能缺失。
142
+ */
143
+ bySite?: Record<string, ModelUsage>;
104
144
  /**
105
145
  * 按角色费用归因(人民币元):助手输出成本为实测计价;输入成本按会话内
106
146
  * 用户消息 / 工具结果的文本长度占比启发式摊分(日志无角色级 token 实测,
@@ -227,6 +267,8 @@ interface SessionFold {
227
267
  byModel: Map<string, ModelUsage>;
228
268
  byDay: Map<string, ModelUsage>;
229
269
  byDayModels: Map<string, Map<string, ModelUsage>>;
270
+ /** 中转站归组:按 provider 路由归类到站点/直连/未知路由(key = {@link siteBucketKey})。 */
271
+ bySite: Map<string, ModelUsage>;
230
272
  /** 每个模型 key 在本会话内走订阅通道的调用数(合并时跨会话累加判定 plan)。 */
231
273
  planCalls: Map<string, number>;
232
274
  /** 每轮费用明细(按轮次号升序,不含 sessionId);sessionId 在合并时补齐。 */
@@ -268,7 +310,7 @@ export declare function foldSession(events: readonly {
268
310
  type: string;
269
311
  time: number;
270
312
  data: never;
271
- }[], subscriptionProviders: ReadonlySet<string>, officialProviderIds?: ReadonlySet<string>): SessionFold;
313
+ }[], subscriptionProviders: ReadonlySet<string>, officialProviderIds?: ReadonlySet<string>, routes?: Readonly<Record<string, ProviderRouteView>>): SessionFold;
272
314
  /**
273
315
  * 增量聚合器:按会话缓存折叠结果,用日志文件的 mtime+size 作失效键——
274
316
  * 日志没动的会话直接复用,只有写过的会话重新折叠;整份文档另有短 TTL
@@ -190,6 +190,16 @@ export interface UsageStats {
190
190
  output: number;
191
191
  lastActive: number;
192
192
  }[];
193
+ /** 中转站归组(key = `site:<origin>` / `direct:<provider>` / `unknown`);旧快照可能缺失。 */
194
+ bySite?: Record<string, {
195
+ calls: number;
196
+ input: number;
197
+ output: number;
198
+ cacheHit: number;
199
+ cacheMiss: number;
200
+ cost: number;
201
+ reasoning: number;
202
+ }>;
193
203
  /** 按角色费用归因(估算口径:输出实测,输入按消息长度摊分);旧快照可能缺失。 */
194
204
  byRole?: {
195
205
  user: number;
@@ -1,5 +1,5 @@
1
1
  /** Locale dictionaries for the usage billing surface. */
2
- export type UsageBillingKey = 'billing.title' | 'billing.subtitle' | 'billing.cost' | 'billing.todayCost' | 'billing.monthCost' | 'billing.yearCost' | 'billing.monthProjected' | 'billing.liveTurn' | 'billing.liveSession' | 'billing.totalCost' | 'billing.calls' | 'billing.cacheHitRate' | 'billing.tokens' | 'billing.inputTokens' | 'billing.outputTokens' | 'billing.avgCost' | 'billing.trend' | 'billing.trend7d' | 'billing.trend30d' | 'billing.trendEmpty' | 'billing.budget' | 'billing.sessions' | 'billing.project' | 'billing.lastActive' | 'billing.sessionOverflow' | 'billing.budgetTierBody' | 'billing.models' | 'billing.providerBilling' | 'billing.estimated' | 'billing.actual' | 'billing.pricing' | 'billing.showPricing' | 'billing.hidePricing' | 'billing.pricePerM' | 'billing.input' | 'billing.output' | 'billing.cacheHit' | 'billing.peak' | 'billing.offPeak' | 'billing.flat' | 'billing.peakHours' | 'billing.band' | 'billing.openDashboard' | 'billing.close' | 'billing.lastUpdated' | 'billing.noData' | 'billing.todayRate' | 'billing.rateLive' | 'billing.rateBuiltin' | 'billing.pricingTip' | 'billing.balance' | 'billing.balanceUnconfigured' | 'billing.balanceUnauthorized' | 'billing.balanceUnreachable' | 'billing.uncatalogued' | 'billing.estimatedPricing' | 'billing.balanceDays' | 'billing.balanceLowBody' | 'billing.subscriptions' | 'billing.subscriptionNotConfigured' | 'billing.subscriptionUnauthorized' | 'billing.subscriptionUnavailable' | 'billing.subscriptionInvalid' | 'billing.subscriptionRateLimited' | 'billing.subscriptionSession' | 'billing.subscriptionWeekly' | 'billing.subscriptionMonthly' | 'billing.subscriptionBilling' | 'billing.subscriptionRemaining' | 'billing.subscriptionExhausted' | 'billing.subscriptionReset' | 'billing.subscriptionNoApi' | 'billing.heatmapLess' | 'billing.heatmapMore' | 'billing.currency' | 'billing.currencyCny' | 'billing.currencyUsd' | 'billing.heatmap' | 'billing.rounds' | 'billing.anomaly' | 'billing.workspaces' | 'billing.plan' | 'billing.remaining' | 'billing.unknownModel' | 'billing.model' | 'billing.currentRound' | 'billing.costAbbr' | 'billing.tabOverview' | 'billing.tabTrends' | 'billing.tabProviders' | 'billing.tabDetails' | 'billing.tabPricing' | 'billing.tabSettings' | 'billing.settingsHead' | 'billing.settingsHint' | 'billing.budgetHint' | 'billing.peakAlertHint' | 'billing.peakAlertDescPeak' | 'billing.peakAlertDescOff' | 'billing.export' | 'billing.exportCsvDay' | 'billing.exportCsvSession' | 'billing.exportJson' | 'billing.peakShare' | 'billing.peakShareHint' | 'billing.weekCost' | 'billing.roleCost' | 'billing.roleUser' | 'billing.roleAssistant' | 'billing.roleTool' | 'billing.roleHint' | 'billing.tierPeak' | 'billing.tierOff' | 'billing.tierToPeak' | 'billing.tierToOff' | 'billing.tierAlertEnterPeak' | 'billing.tierAlertEnterOff' | 'billing.peakAlertTitlePeak' | 'billing.peakAlertTitleOff' | 'billing.peakAlert' | 'billing.peakAlertLeadMin' | 'billing.peakAlertPosCorner' | 'billing.peakAlertPosCenter' | 'billing.peakAlertModePeak' | 'billing.peakAlertModeOff' | 'billing.peakAlertModeBoth' | 'billing.peakAlertWebNotify' | 'billing.peakAlertPreview' | 'billing.planTypeCode' | 'billing.planTypeToken' | 'billing.subscriptionFeePerMonth' | 'billing.triggerToday' | 'billing.triggerMonth' | 'billing.subscriptionIncluded' | 'billing.free' | 'billing.official' | 'billing.thirdParty' | 'billing.officialCost' | 'billing.thirdPartyCost' | 'billing.perfSamples' | 'billing.perfTtft' | 'billing.perfP50' | 'billing.perfP90' | 'billing.perfTps' | 'billing.perfLatency' | 'billing.perfEstimated' | 'billing.perfEmpty' | 'billing.perfTpsUnit' | 'billing.perfTitle' | 'billing.perfHint' | 'billing.heatmapYear' | 'billing.heatmapMonth' | 'billing.activeDays' | 'billing.streakDays' | 'billing.subscriptionAutoDetect' | 'billing.pluginInfo' | 'billing.pluginName' | 'billing.pluginDescription' | 'billing.pluginVersion' | 'billing.pluginAuthor' | 'billing.pluginRepository' | 'billing.pluginNpm' | 'billing.pluginLicense' | 'billing.tabToken' | 'billing.tokenExport' | 'billing.tokenExportCsv' | 'billing.tokenCacheHitRate' | 'billing.tokenReasoningShare' | 'billing.tokenReasoningShort' | 'billing.tokenIo' | 'billing.tokenPeak' | 'billing.tokenDaily' | 'billing.tokenByModel' | 'billing.tokenMiss' | 'billing.tokenHit' | 'billing.tokenOutput' | 'billing.tokenTotal' | 'billing.tokenShare' | 'billing.usageStatsTool' | 'billing.usageStatsToolHint';
2
+ export type UsageBillingKey = 'billing.title' | 'billing.subtitle' | 'billing.cost' | 'billing.todayCost' | 'billing.monthCost' | 'billing.yearCost' | 'billing.monthProjected' | 'billing.liveTurn' | 'billing.liveSession' | 'billing.totalCost' | 'billing.calls' | 'billing.cacheHitRate' | 'billing.tokens' | 'billing.inputTokens' | 'billing.outputTokens' | 'billing.avgCost' | 'billing.trend' | 'billing.trend7d' | 'billing.trend30d' | 'billing.trendEmpty' | 'billing.budget' | 'billing.sessions' | 'billing.project' | 'billing.lastActive' | 'billing.sessionOverflow' | 'billing.budgetTierBody' | 'billing.models' | 'billing.providerBilling' | 'billing.estimated' | 'billing.actual' | 'billing.pricing' | 'billing.showPricing' | 'billing.hidePricing' | 'billing.pricePerM' | 'billing.input' | 'billing.output' | 'billing.cacheHit' | 'billing.peak' | 'billing.offPeak' | 'billing.flat' | 'billing.peakHours' | 'billing.band' | 'billing.openDashboard' | 'billing.close' | 'billing.lastUpdated' | 'billing.noData' | 'billing.todayRate' | 'billing.rateLive' | 'billing.rateBuiltin' | 'billing.pricingTip' | 'billing.balance' | 'billing.balanceUnconfigured' | 'billing.balanceUnauthorized' | 'billing.balanceUnreachable' | 'billing.uncatalogued' | 'billing.estimatedPricing' | 'billing.balanceDays' | 'billing.balanceLowBody' | 'billing.subscriptions' | 'billing.subscriptionNotConfigured' | 'billing.subscriptionUnauthorized' | 'billing.subscriptionUnavailable' | 'billing.subscriptionInvalid' | 'billing.subscriptionRateLimited' | 'billing.subscriptionSession' | 'billing.subscriptionWeekly' | 'billing.subscriptionMonthly' | 'billing.subscriptionBilling' | 'billing.subscriptionRemaining' | 'billing.subscriptionExhausted' | 'billing.subscriptionReset' | 'billing.subscriptionNoApi' | 'billing.heatmapLess' | 'billing.heatmapMore' | 'billing.currency' | 'billing.currencyCny' | 'billing.currencyUsd' | 'billing.heatmap' | 'billing.rounds' | 'billing.anomaly' | 'billing.workspaces' | 'billing.plan' | 'billing.remaining' | 'billing.unknownModel' | 'billing.model' | 'billing.currentRound' | 'billing.costAbbr' | 'billing.tabOverview' | 'billing.tabTrends' | 'billing.tabProviders' | 'billing.tabDetails' | 'billing.tabPricing' | 'billing.tabSettings' | 'billing.settingsHead' | 'billing.settingsHint' | 'billing.budgetHint' | 'billing.peakAlertHint' | 'billing.peakAlertDescPeak' | 'billing.peakAlertDescOff' | 'billing.export' | 'billing.exportCsvDay' | 'billing.exportCsvSession' | 'billing.exportJson' | 'billing.peakShare' | 'billing.peakShareHint' | 'billing.weekCost' | 'billing.roleCost' | 'billing.roleUser' | 'billing.roleAssistant' | 'billing.roleTool' | 'billing.roleHint' | 'billing.tierPeak' | 'billing.tierOff' | 'billing.tierToPeak' | 'billing.tierToOff' | 'billing.tierAlertEnterPeak' | 'billing.tierAlertEnterOff' | 'billing.peakAlertTitlePeak' | 'billing.peakAlertTitleOff' | 'billing.peakAlert' | 'billing.peakAlertLeadMin' | 'billing.peakAlertPosCorner' | 'billing.peakAlertPosCenter' | 'billing.peakAlertModePeak' | 'billing.peakAlertModeOff' | 'billing.peakAlertModeBoth' | 'billing.peakAlertWebNotify' | 'billing.peakAlertPreview' | 'billing.planTypeCode' | 'billing.planTypeToken' | 'billing.subscriptionFeePerMonth' | 'billing.triggerToday' | 'billing.triggerMonth' | 'billing.subscriptionIncluded' | 'billing.free' | 'billing.official' | 'billing.thirdParty' | 'billing.officialCost' | 'billing.thirdPartyCost' | 'billing.perfSamples' | 'billing.perfTtft' | 'billing.perfP50' | 'billing.perfP90' | 'billing.perfTps' | 'billing.perfLatency' | 'billing.perfEstimated' | 'billing.perfEmpty' | 'billing.perfTpsUnit' | 'billing.perfTitle' | 'billing.perfHint' | 'billing.heatmapYear' | 'billing.heatmapMonth' | 'billing.activeDays' | 'billing.streakDays' | 'billing.subscriptionAutoDetect' | 'billing.pluginInfo' | 'billing.pluginName' | 'billing.pluginDescription' | 'billing.pluginVersion' | 'billing.pluginAuthor' | 'billing.pluginRepository' | 'billing.pluginNpm' | 'billing.pluginLicense' | 'billing.tabToken' | 'billing.tokenExport' | 'billing.tokenExportCsv' | 'billing.tokenCacheHitRate' | 'billing.tokenReasoningShare' | 'billing.tokenReasoningShort' | 'billing.tokenIo' | 'billing.tokenPeak' | 'billing.tokenDaily' | 'billing.tokenByModel' | 'billing.tokenMiss' | 'billing.tokenHit' | 'billing.tokenOutput' | 'billing.tokenTotal' | 'billing.tokenShare' | 'billing.usageStatsTool' | 'billing.usageStatsToolHint' | 'billing.balanceGranted' | 'billing.balanceTopped' | 'billing.balanceDaily' | 'billing.balanceDaysLong' | 'billing.balanceDaysUnit' | 'billing.popTodayModel' | 'billing.popNoConsumption' | 'billing.popQuotaAlert' | 'billing.popRiskNone' | 'billing.popTitle' | 'billing.popDirectLead' | 'billing.popSubLead' | 'billing.popBalanceNormal' | 'billing.popBalanceLow' | 'billing.popQuotaNormal' | 'billing.popQuotaLow' | 'billing.alertBalanceLow' | 'billing.alertQuotaLow' | 'billing.panelRelay' | 'billing.relaySite' | 'billing.relayDirect' | 'billing.relayUnknown' | 'billing.panelRelayQuota' | 'billing.relayBalance' | 'billing.relayNoQuota' | 'billing.relayWindowUsed' | 'billing.relayKindNewApi' | 'billing.relayKindSub2Api' | 'billing.relayKindUnknown' | 'billing.relayCalls';
3
3
  export declare const NS = "usageBilling";
4
4
  export declare const zh: Record<UsageBillingKey, string>;
5
5
  export declare const en: Record<UsageBillingKey, string>;
@@ -38,7 +38,30 @@ export interface UsageBillingConfig {
38
38
  /** Required services: the web server, the persisted session log store, and user settings. */
39
39
  export declare const inject: string[];
40
40
  /**
41
- * 解析订阅适配器需要的 API Key:从 llm-pi-ai 设置的 `providers.<id>.apiKeyEnv`
41
+ * 读取 llm-pi-ai 设置的 `providers` 字典(`<route> → { apiKeyEnv? }`)。
42
+ * 余额查询复用同一份来源:部署为某个 provider 配一次 key,多个 surface 共享。
43
+ * @param settings - the settings service (reads the llm-pi-ai namespace).
44
+ * @returns the providers dict; empty when the namespace is unreadable.
45
+ */
46
+ /** 一个 llm-pi-ai provider 路由的读取视图:只取三块——apiKeyEnv(凭据引用)、
47
+ * baseURL(中转站零配置发现的来源)、displayName(站点显示名)。 */
48
+ export interface PiAiProviderRoute {
49
+ apiKeyEnv?: string;
50
+ baseURL?: string;
51
+ displayName?: string;
52
+ }
53
+ /** 同步读取 provider 路由的 baseURL 视图(中转站零配置发现来源)。
54
+ * `settings.describe` 是同步调用,聚合器每次折叠取最新站点映射,无需缓存/过期。
55
+ * 注意:返回**全部可读路由**(baseURL 可选),聚合层据此区分「路由存在但无
56
+ * baseURL=直连」与「路由已删除=未知路由」两种不同归属。
57
+ * @param settings - the settings service (reads the llm-pi-ai namespace).
58
+ * @returns `<route> → { baseURL? }`;命名空间不可读时返回空。
59
+ */
60
+ export declare function readPiAiProviderRoutes(settings: SettingsProvider): Readonly<Record<string, {
61
+ baseURL?: string;
62
+ }>>;
63
+ /**
64
+ * 解析订阅适配器需要的 API Key:从 llm-pi-ai 设置的 `providers.<id>.apiKeyEnv`
42
65
  * 读引用(如 kimi-coding → KIMI_CODING_API_KEY),再经凭据 seam 解析成实际值。
43
66
  * 同时识别出用户配置了 key 的订阅套餐(供面板只显示已识别的)。
44
67
  * @param settings - the settings service (reads the llm-pi-ai namespace).
@@ -103,6 +103,27 @@ export interface SubscriptionQuota {
103
103
  export interface SubscriptionResponse {
104
104
  quotas: readonly SubscriptionQuota[];
105
105
  }
106
+ /** 中转站程序类型;`unknown` = 两个端点都没读出额度。 */
107
+ export type RelayKind = 'new-api' | 'sub2api' | 'unknown';
108
+ /** 中转站额度的一行(`/api/billing/relay-quotas` 的一行)。 */
109
+ export interface RelayQuota {
110
+ /** llm-pi-ai providers 路由名。 */
111
+ route: string;
112
+ /** 站点 origin(baseURL 归一化)。 */
113
+ origin: string;
114
+ /** 站点显示名。 */
115
+ displayName: string;
116
+ kind: RelayKind;
117
+ status: SubscriptionStatus;
118
+ /** 上游明确携带的钱包余额(不猜币种,币种由上游决定)。 */
119
+ balance?: number;
120
+ /** 解析出的滚动额度窗口(used/total 比值)。 */
121
+ windows?: readonly SubscriptionWindow[];
122
+ }
123
+ /** Response of `/api/billing/relay-quotas`. */
124
+ export interface RelayResponse {
125
+ quotas: readonly RelayQuota[];
126
+ }
106
127
  /** Config for one subscription adapter (validated in apply). */
107
128
  export interface SubscriptionPlanConfig {
108
129
  /** Adapter id: `kimi` / `zai` / `opencode-go`. */
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 中转站额度查询(node 半区):识别并读取 New API 系与 Sub2API 的「余额 / 额度窗口」。
3
+ *
4
+ * 适用场景:用户把某条 llm-pi-ai provider 路由的 `baseURL` 指向第三方中转站
5
+ * (New API / One API / VoAPI / Sub2API 等)。这类站点不卖官方余额,卖的是
6
+ * 按 key 的额度(used/total)或多个滚动窗口。本模块对**配了 baseURL 且有
7
+ * apiKeyEnv** 的路由逐个探测两个已知端点,能解析出额度就返回;解析不出的
8
+ * 静默标记 unavailable,绝不臆造金额(与 balance/subscriptions 一致的姿态)。
9
+ *
10
+ * 探测顺序:先 Sub2API `/v1/usage`(标准化程度高),再 New API `/api/status`;
11
+ * 404 = 不是该套程序,继续试下一种;401/403 = 是但 key 不对(unauthorized);
12
+ * 网络/5xx 走熔断门短路一段时间。同一站点多把 key 是独立额度,分别列出。
13
+ */
14
+ import type { Context } from '@deepseek-ai/cordis';
15
+ import type { RelayQuota, SubscriptionWindow } from './pricing-shared.ts';
16
+ /** 一个待探测的中转站路由(来自 llm-pi-ai providers 的 baseURL + apiKeyEnv)。 */
17
+ export interface RelayRoute {
18
+ /** llm-pi-ai providers 路由名(key)。 */
19
+ route: string;
20
+ /** 该路由配置的端点地址(origin 来源)。 */
21
+ baseURL: string;
22
+ /** 该路由解析到的 apiKeyEnv 引用。 */
23
+ apiKeyEnv: string;
24
+ /** 站点显示名;缺省用路由名。 */
25
+ displayName?: string;
26
+ }
27
+ /**
28
+ * 解析 Sub2API `/v1/usage` 响应:能取到 balance 或 quota/used 就识别为 sub2api。
29
+ * 三种形态(窗口 / 分组 / 钱包余额)都宽容处理:有 `quota/total` 给出窗口,
30
+ * 有 `balance` 给出余额,两者可同时存在。
31
+ * @param data - `/v1/usage` 的 JSON 响应。
32
+ * @returns 解析结果;两者都取不到返回 null(不是 Sub2API 或响应漂移)。
33
+ */
34
+ export declare function parseSub2ApiUsage(data: unknown): {
35
+ balance?: number;
36
+ windows?: readonly SubscriptionWindow[];
37
+ } | null;
38
+ /**
39
+ * 解析 New API `/api/status` 响应:New API 系(One API / VoAPI 分支)的额度是
40
+ * 按记录行的 ratio(已用比例)。只给出窗口,不猜金额(币种防猜)。
41
+ * @param data - `/api/status` 的 JSON 响应。
42
+ * @returns 窗口;取不到比例返回 null(响应漂移)。
43
+ */
44
+ export declare function parseNewApiStatus(data: unknown): {
45
+ windows?: readonly SubscriptionWindow[];
46
+ } | null;
47
+ /**
48
+ * 查询单个中转站路由的额度。先试 Sub2API,再试 New API;任一读出额度即返回。
49
+ * @param ctx - host context carrying the credentials seam.
50
+ * @param route - 待探测的路由(baseURL + apiKeyEnv)。
51
+ * @returns 该路由的一行额度结果(status 标记成败)。
52
+ */
53
+ export declare function queryRelayQuota(ctx: Context, route: RelayRoute): Promise<RelayQuota>;
54
+ /**
55
+ * 批量查询多个中转站路由的额度(每个独立成败,互不影响)。
56
+ * @param ctx - host context carrying the credentials seam.
57
+ * @param routes - 配了 baseURL 且 apiKeyEnv 有值的路由列表。
58
+ * @returns 每个路由一行的额度结果。
59
+ */
60
+ export declare function queryRelayQuotas(ctx: Context, routes: readonly RelayRoute[]): Promise<readonly RelayQuota[]>;
61
+ //# sourceMappingURL=relay.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": "0.9.7",
4
+ "version": "0.9.9",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },