@kenz1117/dsh-ui-usage-billing 0.5.0 → 0.7.0

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.
@@ -79,6 +79,18 @@ export interface UsageStatsDocument {
79
79
  byTurn?: TurnUsageRow[];
80
80
  /** 工作区聚合:按 cwd 末级目录归并,按费用倒序;旧快照可能缺失。 */
81
81
  byWorkspace?: WorkspaceUsageRow[];
82
+ /**
83
+ * 按角色费用归因(人民币元):助手输出成本为实测计价;输入成本按会话内
84
+ * 用户消息 / 工具结果的文本长度占比启发式摊分(日志无角色级 token 实测,
85
+ * 属估算口径,UI 需标注)。旧快照可能缺失。
86
+ */
87
+ byRole?: RoleCost;
88
+ }
89
+ /** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
90
+ export interface RoleCost {
91
+ user: number;
92
+ assistant: number;
93
+ tool: number;
82
94
  }
83
95
  /** 会话明细行:仪表盘「会话明细」面板的数据源。 */
84
96
  export interface SessionUsageRow {
@@ -141,11 +153,27 @@ interface SessionFold {
141
153
  planCalls: Map<string, number>;
142
154
  /** 每轮费用明细(按轮次号升序,不含 sessionId);sessionId 在合并时补齐。 */
143
155
  turns: SessionTurnRow[];
156
+ /** 角色归因中间量:消息文本长度(user/tool)与输入/输出成本实测拆分。 */
157
+ roles: RoleFold;
144
158
  /** 日志里最新的 session/title 文本(无标题事件时 undefined)。 */
145
159
  title?: string;
146
160
  /** 最后一个事件的时间戳(毫秒);空日志为 0。 */
147
161
  lastActive: number;
148
162
  }
163
+ /** 角色归因的会话级中间量:字符占比用于把输入成本摊到 user/tool。 */
164
+ interface RoleFold {
165
+ userChars: number;
166
+ toolChars: number;
167
+ /** 输入侧成本(缓存命中 + 未命中 + 缓存写入,人民币元)。 */
168
+ inputCost: number;
169
+ /** 输出侧成本(人民币元)。 */
170
+ outputCost: number;
171
+ }
172
+ /**
173
+ * 消息文本长度:user/tool 角色分摊输入成本的启发式依据。字符串内容取其
174
+ * 长度;内容块数组累计文本块长度;其余形状按 0 计(durable 边界收窄)。
175
+ */
176
+ export declare function messageTextLength(message: unknown): number;
149
177
  /**
150
178
  * Fold one session's events into a {@link SessionFold}. 每个 LLM 调用归属到
151
179
  * 其前置 request/header 记录的模型;同时提取最新会话标题、最后活跃时间,
@@ -14,7 +14,7 @@
14
14
  * provider's key once and every surface reuses it.
15
15
  */
16
16
  import type { Context } from '@deepseek-ai/cordis';
17
- import type { ProviderBalance } from './pricing-shared.ts';
17
+ import type { CustomBalanceConfig, CustomBalanceExtract, ProviderBalance } from './pricing-shared.ts';
18
18
  /**
19
19
  * Query every configured provider's account balance. A provider is queried only
20
20
  * when its llm-pi-ai route has an `apiKeyEnv`; absent routes answer
@@ -26,4 +26,20 @@ import type { ProviderBalance } from './pricing-shared.ts';
26
26
  export declare function queryBalances(ctx: Context, providers: Readonly<Record<string, {
27
27
  apiKeyEnv?: string;
28
28
  }>>): Promise<readonly ProviderBalance[]>;
29
+ /**
30
+ * 按 extract 规则从响应 JSON 求值。导出供测试:纯函数。
31
+ * @param rule - 提取规则(const / path / add / subtract / divide)。
32
+ * @param data - 响应 JSON。
33
+ * @returns 数值;取不到或结果非有限数返回 undefined。
34
+ */
35
+ export declare function evalExtract(rule: CustomBalanceExtract, data: unknown): number | undefined;
36
+ /**
37
+ * 查询自定义 Provider 余额(插件 config 的 `customBalances`)。每个条目独立
38
+ * 成败:占位符凭据缺失 → unconfigured;401/403 → unauthorized;网络或提取
39
+ * 失败 → unreachable。
40
+ * @param ctx - host context carrying the credentials seam.
41
+ * @param configs - 自定义余额配置列表。
42
+ * @returns 每个配置一行的余额结果。
43
+ */
44
+ export declare function queryCustomBalances(ctx: Context, configs: readonly CustomBalanceConfig[]): Promise<readonly ProviderBalance[]>;
29
45
  //# sourceMappingURL=balance.d.ts.map
@@ -12,6 +12,7 @@
12
12
  import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
13
13
  import type { SidebarFooterActionOwnerProps } from '@deepseek-ai/dsh-client-ui-sidebar/client';
14
14
  import type { createBillingBudgetStore } from './budget-store.ts';
15
+ import { type CatalogModel } from './pricing.ts';
15
16
  import { NS, type UsageBillingKey } from './locales.ts';
16
17
  /** Model-connectivity health reported by the host model directory probe. */
17
18
  export interface ModelHealth {
@@ -27,12 +28,14 @@ export interface ModelHealth {
27
28
  okProviders: readonly string[];
28
29
  /** Display names of providers whose catalog probe failed. */
29
30
  badProviders: readonly string[];
31
+ /** 探活得到的模型清单(系统里实际配置/预制的模型;无价格,费率表据此对标)。 */
32
+ catalog?: readonly CatalogModel[];
30
33
  }
31
34
  /** 仪表盘分区 Tab id。 */
32
35
  export type DashboardTab = 'overview' | 'trends' | 'providers' | 'details' | 'pricing';
33
36
  /**
34
37
  * Tab 定义(顺序即渲染顺序):概览=主数字/预算/KPI/热力图,趋势=趋势图/每轮费用,
35
- * 厂商=厂商计费与订阅,明细=工作区/会话明细,单价=模型单价表。导出供测试断言
38
+ * 明细=厂商计费与订阅,统计=工作区/会话明细,费率=模型单价表。导出供测试断言
36
39
  * tab 与文案 key 对齐、decor 锚点落在正确分区。
37
40
  */
38
41
  export declare const DASHBOARD_TABS: readonly {
@@ -67,6 +70,31 @@ export declare function providerFromModelKey(modelKey: string): string | undefin
67
70
  export declare function projectMonthCost(byDay: Record<string, {
68
71
  cost: number;
69
72
  }>, monthPrefix: string, today: string): number;
73
+ /**
74
+ * 峰谷时段费用分摊:按每轮的起始时刻(北京时间高峰 9-12 / 14-18)把费用
75
+ * 归入高峰 / 空闲两档。导出供测试:纯函数。
76
+ * @param turns - 每轮费用行(需带 startedAt 与 cost)。
77
+ * @returns 两档费用合计(人民币元)。
78
+ */
79
+ export declare function peakOffpeakCost(turns: readonly {
80
+ startedAt: number;
81
+ cost: number;
82
+ }[]): {
83
+ peak: number;
84
+ offPeak: number;
85
+ };
86
+ /**
87
+ * 近 7 天费用序列(含今天,缺日补 0):触发卡 hover 速览的迷你柱数据源。
88
+ * 导出供测试:纯函数(日期取本地时区)。
89
+ * @param byDay - 按日费用表。
90
+ * @returns 7 个 `{ date, cost }`,最旧在前。
91
+ */
92
+ export declare function lastSevenDays(byDay: Record<string, {
93
+ cost: number;
94
+ }>): readonly {
95
+ date: string;
96
+ cost: number;
97
+ }[];
70
98
  /** 组件注入面:探活 + 计费指标写入(billing 自身写入,主题插件经服务读取)。 */
71
99
  export interface UsageBillingInjected {
72
100
  checkModels: () => Promise<ModelHealth>;
@@ -12,17 +12,20 @@ export interface BudgetPrefsState {
12
12
  enabled: boolean;
13
13
  /** 用户设置的月度预算(人民币元);0 = 未设置(回退到宿主默认值)。 */
14
14
  amount: number;
15
- /** 最近一次超支通知的日期戳(YYYY-MM-DD):超支通知每天最多一次,跨重启生效。 */
16
- lastAlertDay: string;
15
+ /** 各档提醒的最后通知日期戳(档位百分比字符串 → YYYY-MM-DD):每档每天最多一次。 */
16
+ tierAlertDays: Record<string, string>;
17
17
  /** 最近一次余额不足通知的日期戳(YYYY-MM-DD):余额告警同样每天最多一次。 */
18
18
  lastBalanceAlertDay: string;
19
+ /** 最近一次峰谷切换提醒的切换点时刻(毫秒):同一切换点只提醒一次。 */
20
+ lastTierSwitchAt: number;
19
21
  }
20
22
  /** 预算偏好的完整写面(组件只能经这些 action 写入);type 别名以兼容 ActionsDecl 的索引签名约束。 */
21
23
  export type BudgetPrefsActions = {
22
24
  setEnabled: (d: BudgetPrefsState, on: boolean) => void;
23
25
  setAmount: (d: BudgetPrefsState, value: number) => void;
24
- markAlerted: (d: BudgetPrefsState, day: string) => void;
26
+ markTierAlerted: (d: BudgetPrefsState, tiers: readonly number[], day: string) => void;
25
27
  markBalanceAlerted: (d: BudgetPrefsState, day: string) => void;
28
+ markTierSwitchAlerted: (d: BudgetPrefsState, at: number) => void;
26
29
  };
27
30
  /**
28
31
  * Declare the budget-preferences store handle.
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 数据导出:把用量统计导出为 CSV / JSON 供对账。
3
+ *
4
+ * 纯函数生成文本(按日 / 按会话两个视角),DOM 下载副作用单独隔离在
5
+ * `downloadText`;金额一律人民币元(与聚合口径一致),文件名带日期范围。
6
+ */
7
+ /** 按日聚合行(与 UsageStatsDocument.byDay 的行同形)。 */
8
+ export interface DayExportRow {
9
+ calls: number;
10
+ input: number;
11
+ output: number;
12
+ cacheHit: number;
13
+ cacheMiss: number;
14
+ cost: number;
15
+ }
16
+ /** 会话导出行(与 SessionUsageRow 同形,结构化声明避免跨文件引用)。 */
17
+ export interface SessionExportRow {
18
+ id: string;
19
+ title?: string;
20
+ cwd?: string;
21
+ calls: number;
22
+ cost: number;
23
+ lastActive: number;
24
+ }
25
+ /** 按日 CSV:日期,调用,输入,输出,缓存命中,缓存未命中,费用(元)。 */
26
+ export declare function dayRowsCsv(byDay: Record<string, DayExportRow>): string;
27
+ /** 按会话 CSV:会话 id,标题,项目,调用,费用(元),最后活跃(ISO)。 */
28
+ export declare function sessionRowsCsv(rows: readonly SessionExportRow[]): string;
29
+ /** 导出文件名:带日期范围(usage-2026-08-01_2026-08-22.csv);无日期时只带前缀。 */
30
+ export declare function exportFileName(prefix: string, ext: 'csv' | 'json', dates: readonly string[]): string;
31
+ /** 触发浏览器下载(唯一 DOM 副作用;调用方在 click 手势里使用)。 */
32
+ export declare function downloadText(filename: string, text: string, mime: string): void;
33
+ //# sourceMappingURL=export.d.ts.map
@@ -9,9 +9,15 @@
9
9
  * the current session id off the framework snapshot (`useSession` parent of
10
10
  * `sessionId`) and matches `bySession` (session total) and `byTurn` (latest
11
11
  * turn cost). Rendering is a pure function of the snapshot, never a side effect.
12
+ *
13
+ * The bar also carries two ambient signals: the current peak/off-peak pricing
14
+ * tier with a switch countdown (DeepSeek time-of-day pricing), and quota chips
15
+ * for subscription plans running low (≤20% remaining), so cost pressure is
16
+ * visible without opening the dashboard.
12
17
  */
13
18
  import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
14
19
  import type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client';
20
+ import type { UsageBillingKey } from './locales.ts';
15
21
  /** The usage-stats shape the composer bar needs (a thin slice, not the whole doc). */
16
22
  export interface LiveStats {
17
23
  bySession?: readonly {
@@ -24,6 +30,15 @@ export interface LiveStats {
24
30
  cost: number;
25
31
  }[];
26
32
  }
33
+ /** 订阅额度的薄切片(/api/billing/subscriptions 响应的行)。 */
34
+ export interface QuotaSlice {
35
+ displayName: string;
36
+ status: string;
37
+ windows: readonly {
38
+ kind: string;
39
+ remainingPercent: number;
40
+ }[];
41
+ }
27
42
  /**
28
43
  * 当前会话累计费用:bySession 里会话 id 匹配的那行;缺省为 0。
29
44
  * 导出供测试:纯函数。
@@ -41,11 +56,22 @@ export declare function sessionCostOf(stats: LiveStats | null, sessionId: string
41
56
  * @returns 最新一轮费用(人民币元)。
42
57
  */
43
58
  export declare function turnCostOf(stats: LiveStats | null, sessionId: string | undefined): number;
59
+ /**
60
+ * 低额度预警 chips:查询成功(ok)且任一窗口剩余 ≤ threshold 的套餐,
61
+ * 按剩余升序、最多 3 枚。导出供测试:纯函数。
62
+ * @param quotas - 订阅额度行切片。
63
+ * @param threshold - 剩余百分比阈值(默认 20%)。
64
+ */
65
+ export declare function lowQuotaChips(quotas: readonly QuotaSlice[], threshold?: number): readonly {
66
+ name: string;
67
+ kind: string;
68
+ pct: number;
69
+ }[];
44
70
  /** Props: the session-scope snapshot selector the framework injects. */
45
71
  export interface LiveCostBarProps {
46
72
  useSession: SnapshotSelectorHook<ConversationSnapshot>;
47
73
  /** The owning dock's locale seat (bound to the billing NS). */
48
- t: (key: 'billing.liveTurn' | 'billing.liveSession') => string;
74
+ t: (key: UsageBillingKey) => string;
49
75
  }
50
76
  /**
51
77
  * Render the live cost ticker for the current session.
@@ -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.budgetOverBody' | '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.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.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';
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.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.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';
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>;
@@ -27,6 +27,22 @@ export declare const USD_TO_CNY = 6.79;
27
27
  * @param pricing - the `/api/billing/pricing` response.
28
28
  */
29
29
  export declare function applyLivePricing(pricing: LivePricing): void;
30
+ /**
31
+ * 注入探活得到的「系统里实际配置/预制的模型」清单(host 的 llm.models 返回
32
+ * groups[].models[],含模型 id/name,无价格)。费率表据此对标现实可用模型——
33
+ * 有价的补价(内置目录 / models.dev 补充),无价的标「未收录」。纯内存状态,
34
+ * 供 `catalogEntries()` 渲染。
35
+ */
36
+ export declare function applyLiveCatalogModels(models: readonly CatalogModel[]): void;
37
+ /** 探活模型清单条目(host 的 ModelCatalogModel 投影出需要的字段)。 */
38
+ export interface CatalogModel {
39
+ /** 模型 id(如 `deepseek-v4-flash`)。 */
40
+ id: string;
41
+ /** 显示名;缺省用 id。 */
42
+ name?: string;
43
+ /** 厂商显示名(探活 group 名)。 */
44
+ provider: string;
45
+ }
30
46
  /**
31
47
  * 当前生效的 USD → CNY 汇率及其来源:live = 启动时实时拉取成功,
32
48
  * builtin = 实时拉取失败、正在用内置默认值。
@@ -52,6 +68,30 @@ export declare function isPeakHour(beijingHour: number): boolean;
52
68
  * @param timeMs - Unix epoch 毫秒;null/undefined/NaN 视为未知。
53
69
  */
54
70
  export declare function tierAt(timeMs: number | null | undefined): PriceTierId;
71
+ /**
72
+ * 当前峰谷档位与距下次切换的时长。导出供测试:纯函数。
73
+ * @param nowMs - 当前时刻(epoch 毫秒)。
74
+ * @returns 当前档位与到下一个切换边界的毫秒数。
75
+ */
76
+ export declare function tierCountdown(nowMs: number): {
77
+ tier: PriceTierId;
78
+ nextSwitchInMs: number;
79
+ };
80
+ /**
81
+ * 峰/谷切换预告:距下次切换不足 leadMs 时返回即将进入的档位与切换时刻,
82
+ * 否则 null。导出供测试:纯函数。
83
+ * @param nowMs - 当前时刻(epoch 毫秒)。
84
+ * @param leadMs - 提前量(毫秒)。
85
+ */
86
+ export declare function upcomingTierSwitch(nowMs: number, leadMs: number): {
87
+ entering: PriceTierId;
88
+ atMs: number;
89
+ } | null;
90
+ /**
91
+ * 切换倒计时短格式:`1h23m` / `45m` / `3m`。导出供测试:纯函数。
92
+ * @param ms - 剩余毫秒数。
93
+ */
94
+ export declare function formatSwitchCountdown(ms: number): string;
55
95
  /** Usage buckets consumed by one model (counts in raw tokens). */
56
96
  export interface TokenUsageBuckets {
57
97
  /** Uncached input tokens. */
@@ -100,6 +140,8 @@ export interface ModelEntry {
100
140
  * 展示时标注以免误当正式定价;正式定价公布后移除。
101
141
  */
102
142
  estimated?: boolean;
143
+ /** 探活命中但无内置/models.dev 价:费率表标「未收录」,不参与计价。 */
144
+ uncatalogued?: boolean;
103
145
  }
104
146
  /**
105
147
  * Built-in catalog of current mainstream models as of 2026-08-16, priced from
@@ -126,6 +168,17 @@ export declare const MODEL_CATALOG: readonly ModelEntry[];
126
168
  export declare const MODEL_KEY_ALIASES: Readonly<Record<string, string>>;
127
169
  /** Lookup a model by its stats key; falls back to the generic `other` entry. */
128
170
  export declare function modelOf(key: string): ModelEntry;
171
+ /**
172
+ * 模型是否可计价:内置目录或 models.dev 补充条目命中。聚合层的计价闸门
173
+ * (目录外模型不产生费用,避免兜底档误估)。
174
+ */
175
+ export declare function isPriced(key: string): boolean;
176
+ /**
177
+ * 费率表渲染的完整目录:内置 + models.dev 补充条目 + 探活模型(无价标记)。
178
+ * 探活模型去重(按归一化 id):内置/补充已有的不再重复;无价的保留并标记
179
+ * `uncatalogued`,费率表据此显示「未收录」。
180
+ */
181
+ export declare function catalogEntries(): readonly ModelEntry[];
129
182
  /** Resolve a price-table row by its CSS variable name (theme token or fallback color). */
130
183
  export declare function resolveToken(name: string): string;
131
184
  /**
@@ -12,7 +12,7 @@
12
12
  import type { Context } from '@deepseek-ai/cordis';
13
13
  import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
14
14
  import type { SettingsProvider } from '@deepseek-ai/dsh-settings';
15
- import type { SubscriptionPlanConfig } from './pricing-shared.ts';
15
+ import type { CustomBalanceConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
16
16
  import { type IdentifiedSubscriptionPlan, type SubscriptionKeys } from './subscriptions.ts';
17
17
  /** Plugin configuration. */
18
18
  export interface UsageBillingConfig {
@@ -29,6 +29,8 @@ export interface UsageBillingConfig {
29
29
  /** 余额不足告警阈值(人民币元):余额低于此值时仪表盘每天提醒一次;
30
30
  不设置则客户端按默认阈值(50 元)兜底。 */
31
31
  lowBalanceThreshold?: number;
32
+ /** 自定义 Provider 余额查询(任意 HTTP 端点 + extract 规则,适配 NewApi/LiteLLM 等)。 */
33
+ customBalances?: readonly CustomBalanceConfig[];
32
34
  }
33
35
  /** Required services: the web server, the persisted session log store, and user settings. */
34
36
  export declare const inject: string[];
@@ -7,7 +7,15 @@
7
7
  * half caches whatever succeeded and the browser dashboard falls back to the
8
8
  * catalog for the rest — a total outage answers `{ source: 'builtin' }`.
9
9
  */
10
- import type { LivePricing } from './pricing-shared.ts';
10
+ import type { ExtraModelPrice, LivePricing } from './pricing-shared.ts';
11
+ /**
12
+ * models.dev 响应 → 目录外补充条目。不再按厂商白名单过滤:凡是有有效
13
+ * cost 的模型都纳入(探活模型可能来自任何预制厂商,白名单会漏掉)。厂商
14
+ * 显示名优先取映射,未命中用 provider id。导出供测试:纯函数。
15
+ * @param data - `https://models.dev/api.json` 的响应体。
16
+ * @returns 补充条目(按 provider 顺序稳定;仅含可计价的模型)。
17
+ */
18
+ export declare function buildExtraModels(data: unknown): ExtraModelPrice[];
11
19
  /**
12
20
  * Fetch the live pricing once at boot. Both upstreams run in parallel; a
13
21
  * failure in either degrades independently to the built-in value.
@@ -21,6 +21,22 @@ export interface LivePricing {
21
21
  rate?: number;
22
22
  /** Overrides keyed by built-in catalog key (present when router matches succeeded). */
23
23
  prices?: Record<string, LivePrice>;
24
+ /**
25
+ * models.dev 补充的目录外模型价(USD / 1M tokens):与宿主预制提供方对齐——
26
+ * pi-ai 预制目录的上游就是 models.dev,补进来的条目让「提供方支持但我们的
27
+ * 内置目录未收录」的模型也能计价并出现在费率表。
28
+ */
29
+ extraModels?: readonly ExtraModelPrice[];
30
+ }
31
+ /** models.dev 补充的目录外模型价(USD / 1M tokens)。 */
32
+ export interface ExtraModelPrice {
33
+ /** 归一化模型 id(计费键,如 `deepseek-v4.5-flash`)。 */
34
+ key: string;
35
+ /** 显示名。 */
36
+ name: string;
37
+ /** 厂商显示名(与仪表盘厂商组同口径)。 */
38
+ provider: string;
39
+ price: LivePrice;
24
40
  }
25
41
  /** 余额查询失败的原因,前端据此显示文案。 */
26
42
  export type BalanceError = 'unconfigured' | 'unauthorized' | 'unreachable';
@@ -86,4 +102,38 @@ export interface SubscriptionPlanConfig {
86
102
  /** Z.ai region override; defaults to the settings-namespace `zaiRegion`. */
87
103
  region?: 'global' | 'bigmodel-cn';
88
104
  }
105
+ /**
106
+ * 余额提取规则:从响应 JSON 取数。
107
+ * - `const`:数字常量;
108
+ * - `path`:点路径取值(如 `data.total_available`);
109
+ * - `op: 'add' | 'subtract'` + `paths`:多路径加 / 减;
110
+ * - `op: 'divide'` + `path` + `by`:按除数缩放(适配 NewApi 等 quota 端点,
111
+ * 如 1 USD = 500000 quota)。
112
+ */
113
+ export interface CustomBalanceExtract {
114
+ const?: number;
115
+ path?: string;
116
+ op?: 'add' | 'subtract' | 'divide';
117
+ paths?: readonly string[];
118
+ by?: number;
119
+ }
120
+ /** 一个自定义 Provider 余额查询配置(插件 config 的 `customBalances` 条目)。 */
121
+ export interface CustomBalanceConfig {
122
+ /** 显示名(中文)。 */
123
+ label: string;
124
+ /** 显示名(英文);缺省用 label。 */
125
+ labelEn?: string;
126
+ /** 余额币种(如 CNY / USD);缺省 CNY。 */
127
+ unit?: string;
128
+ /** 查询端点(完整 URL)。 */
129
+ url: string;
130
+ /** HTTP 方法;缺省 GET。 */
131
+ method?: string;
132
+ /** 请求头;值支持 `{{ENV_NAME}}` 占位符,经凭据 seam 解析(仅请求头支持)。 */
133
+ headers?: Record<string, string>;
134
+ /** 余额提取规则。 */
135
+ extract: {
136
+ remaining: CustomBalanceExtract;
137
+ };
138
+ }
89
139
  //# sourceMappingURL=pricing-shared.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.5.0",
4
+ "version": "0.7.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -48,6 +48,7 @@
48
48
  "license": "MIT",
49
49
  "peerDependencies": {
50
50
  "@deepseek-ai/dsh-api-remotes": "*",
51
+ "@deepseek-ai/dsh-atomic-write": "*",
51
52
  "@deepseek-ai/dsh-client-connection": "*",
52
53
  "@deepseek-ai/dsh-host-webserver": "*",
53
54
  "@deepseek-ai/dsh-client-locale": "*",
@@ -62,6 +63,7 @@
62
63
  "@deepseek-ai/dsh-session": "*",
63
64
  "@deepseek-ai/dsh-session-persistence": "*",
64
65
  "@deepseek-ai/dsh-settings": "*",
66
+ "@deepseek-ai/dsh-tools": "*",
65
67
  "@deepseek-ai/schemastery": "*",
66
68
  "@deepseek-ai/cordis": "*",
67
69
  "react": "^18.2.0"