@kenz1117/dsh-ui-usage-billing 1.6.2 → 1.6.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.
- package/README.en.md +58 -40
- package/README.md +36 -27
- package/lib/index.js +300 -153
- package/lib/types/balance.d.ts +4 -2
- package/lib/types/config.d.ts +21 -0
- package/lib/types/declarative.d.ts +3 -2
- package/lib/types/index.d.ts +9 -5
- package/lib/types/relay.d.ts +9 -8
- package/lib/types/route-keys.d.ts +20 -0
- package/lib/types/routes/usage-tool.d.ts +2 -0
- package/lib/types/subscriptions.d.ts +32 -5
- package/package.json +1 -1
package/lib/types/balance.d.ts
CHANGED
|
@@ -24,8 +24,10 @@ import type { CustomBalanceConfig, CustomBalanceExtract, ProviderBalance } from
|
|
|
24
24
|
export declare function pickTokenDanceBalanceCny(data: unknown): number | undefined;
|
|
25
25
|
/**
|
|
26
26
|
* Query every configured provider's account balance. A provider is queried only
|
|
27
|
-
* when its llm-pi-ai route has an `apiKeyEnv
|
|
28
|
-
*
|
|
27
|
+
* when its llm-pi-ai route has a key: an explicit `apiKeyEnv`, or — on hosts
|
|
28
|
+
* whose settings UI stores route keys as credentials refs — the route's
|
|
29
|
+
* conventional ref (route-keys.ts). Absent routes answer `unconfigured` so the
|
|
30
|
+
* dashboard shows a stable state instead of dropping the row.
|
|
29
31
|
* @param ctx - host context carrying the credentials seam.
|
|
30
32
|
* @param providers - the llm-pi-ai providers dict (`<route> → { apiKeyEnv? }`).
|
|
31
33
|
* @returns the balance rows (one per provider).
|
package/lib/types/config.d.ts
CHANGED
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
* 引用(作为 Deps 类型签名),把它单独放这里让 route 文件能从 src/ 根 import。
|
|
5
5
|
* 仍由 src/index.ts 的 apply(ctx, config) 消费:路由层只通过 Deps 拿到它,
|
|
6
6
|
* 不直接构造。
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ Config 运行时 schema 必须经 src/index.ts re-export(issue #98):宿主
|
|
9
|
+
* dsh-settings@0.2.1-rc.2 的 settings.update 写回要求本插件条目在 loader fiber
|
|
10
|
+
* 上有 `Config` schema(`entry.fiber.runtime.Config` 且带 toJSON),缺失时抛
|
|
11
|
+
* `No configurable plugin entry`——v1.6.0 拆分时把 schema 退化成纯类型,「注入
|
|
12
|
+
* 用量查询」开关随之失效(#80 方案的回归)。schemastery 非 strict object 会
|
|
13
|
+
* merge 未声明键,其余 config 字段(statsPath / routeAliases 等)不受影响。
|
|
7
14
|
*/
|
|
15
|
+
import z from '@deepseek-ai/schemastery';
|
|
8
16
|
import type { CustomBalanceConfig, DeclaredEndpointConfig, SubscriptionPlanConfig } from './pricing-shared.ts';
|
|
9
17
|
/** 宿主 0.1.7+ 经 Config schema 解析后,volatile 字段在 config 中的稳定引用形态
|
|
10
18
|
* (cosmokit `Volatile<T>` 的最小结构镜像,避免为类型引入直接依赖)。 */
|
|
@@ -12,6 +20,19 @@ export interface VolatileToggle {
|
|
|
12
20
|
/** @returns 当前生效的布尔快照(设置写入后原位更新,无需重载插件)。 */
|
|
13
21
|
get(): boolean;
|
|
14
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* 宿主侧条目 config schema(#80 / #98):宿主 0.1.7+ 的设置系统据此把
|
|
25
|
+
* `enableUsageStatsTool` 投影为 volatile 引用(设置写入即时反映 + 写回走
|
|
26
|
+
* settings.update)。**必须**经 src/index.ts re-export 给 loader——宿主从插件
|
|
27
|
+
* 运行时模块的 `Config` 导出发现它;只声明类型等于没有。schemastery 非 strict
|
|
28
|
+
* object 透传未声明键,其余 config 字段不受影响;未导出 Config 的旧宿主直接
|
|
29
|
+
* 把原始 config 交给 apply,普通布尔形态由读取端兼容。
|
|
30
|
+
*/
|
|
31
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
32
|
+
enableUsageStatsTool: z<boolean, boolean, "volatile-defined">;
|
|
33
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
34
|
+
enableUsageStatsTool: z<boolean, boolean, "volatile-defined">;
|
|
35
|
+
}>>, "plain">;
|
|
15
36
|
/** Plugin configuration. */
|
|
16
37
|
export interface UsageBillingConfig {
|
|
17
38
|
/** Absolute path to a `.dsh-usage-stats.json` fallback file. */
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* 2. `path` 必须单斜杠绝对路径:`//evil.example/x` 是协议相对 URL,`new URL()` 会
|
|
15
15
|
* 把它解析到别的主机;构造后还会再校验一次 origin。
|
|
16
16
|
* 3. 只发 GET,无请求体,无自定义 method / headers。
|
|
17
|
-
* 4.
|
|
18
|
-
*
|
|
17
|
+
* 4. 凭据仍只从匹配 provider 自己的凭据取:显式 `apiKeyEnv` 优先,缺省按路由
|
|
18
|
+
* 约定 ref(新版宿主的存放形态,见 route-keys.ts)经同一凭据 seam 解析;声明
|
|
19
|
+
* 不能指定任何凭据。
|
|
19
20
|
* 5. 跨源重定向直接失败,不跟随——那是绕过第 1 条最省事的办法。
|
|
20
21
|
* 6. 响应体有大小上限与共享超时,坏 / 恶意端点拖不住面板。
|
|
21
22
|
* 7. 声明不能覆盖内置读法:只在内置表答不上来时它才轮到。
|
package/lib/types/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ interface SettingsReader {
|
|
|
28
28
|
}[];
|
|
29
29
|
}
|
|
30
30
|
export type { UsageBillingConfig } from './config.ts';
|
|
31
|
+
export { Config } from './config.ts';
|
|
31
32
|
/**
|
|
32
33
|
* Create the atomic file-backed durable-ledger store. The previous complete file
|
|
33
34
|
* is retained as `.bak`; a malformed/missing main file falls back to that backup.
|
|
@@ -41,12 +42,14 @@ export declare const inject: string[];
|
|
|
41
42
|
* @param settings - the settings service (reads the llm-pi-ai namespace).
|
|
42
43
|
* @returns the providers dict; empty when the namespace is unreadable.
|
|
43
44
|
*/
|
|
44
|
-
/** 一个 llm-pi-ai provider
|
|
45
|
-
*
|
|
45
|
+
/** 一个 llm-pi-ai provider 路由的读取视图:apiKeyEnv(凭据引用)、baseURL(中转站
|
|
46
|
+
* 零配置发现)、displayName(站点显示名)、hasModels(新版宿主设置页保存的
|
|
47
|
+
* models 列表——订阅识别的第二配置信号)。 */
|
|
46
48
|
export interface PiAiProviderRoute {
|
|
47
49
|
apiKeyEnv?: string;
|
|
48
50
|
baseURL?: string;
|
|
49
51
|
displayName?: string;
|
|
52
|
+
hasModels?: boolean;
|
|
50
53
|
}
|
|
51
54
|
/** 同步读取 provider 路由的 baseURL 视图(中转站零配置发现来源)。
|
|
52
55
|
* `settings.describe` 是同步调用,聚合器每次折叠取最新站点映射,无需缓存/过期。
|
|
@@ -75,9 +78,10 @@ export declare function readRegisteredProviderIds(get: ((name: string) => unknow
|
|
|
75
78
|
reason?: string;
|
|
76
79
|
};
|
|
77
80
|
/**
|
|
78
|
-
|
|
79
|
-
* 读引用(如 kimi-coding → KIMI_CODING_API_KEY
|
|
80
|
-
*
|
|
81
|
+
* 解析订阅适配器需要的 API Key:经典形态从 llm-pi-ai 设置的 `providers.<id>.apiKeyEnv`
|
|
82
|
+
* 读引用(如 kimi-coding → KIMI_CODING_API_KEY);新版宿主设置页把 key 直接存成
|
|
83
|
+
* 凭据 refs(路由约定名 `<ROUTE>_API_KEY`,pi-ai 目录 ambient 名补充),显式引用
|
|
84
|
+
* 解析不到时按约定名找回。同时识别出已配置的订阅套餐(供面板只显示已识别的)。
|
|
81
85
|
* @param settings - the settings service (reads the llm-pi-ai namespace).
|
|
82
86
|
* @param credentials - the credentials service (resolves the env refs).
|
|
83
87
|
*/
|
package/lib/types/relay.d.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 适用场景:用户把某条 llm-pi-ai provider 路由的 `baseURL` 指向第三方中转站
|
|
5
5
|
* (New API / One API / VoAPI / Sub2API 等)。这类站点不卖官方余额,卖的是
|
|
6
|
-
* 按 key 的额度(used/total)或多个滚动窗口。本模块对**配了 baseURL
|
|
7
|
-
* apiKeyEnv
|
|
8
|
-
*
|
|
6
|
+
* 按 key 的额度(used/total)或多个滚动窗口。本模块对**配了 baseURL 且 key 可得**
|
|
7
|
+
* (显式 `apiKeyEnv`,或新版宿主存进凭据 refs 的路由约定名,见 route-keys.ts)
|
|
8
|
+
* 的路由逐个探测两个已知端点,能解析出额度就返回;解析不出的静默标记
|
|
9
|
+
* unavailable,绝不臆造金额(与 balance/subscriptions 一致的姿态)。
|
|
9
10
|
*
|
|
10
11
|
* 探测顺序:先 Sub2API `/v1/usage`(标准化程度高),再 New API `/api/status`;
|
|
11
12
|
* 404 = 不是该套程序,继续试下一种;401/403 = 是但 key 不对(unauthorized);
|
|
@@ -13,14 +14,14 @@
|
|
|
13
14
|
*/
|
|
14
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
16
|
import type { RelayQuota, SubscriptionWindow } from './pricing-shared.ts';
|
|
16
|
-
/** 一个待探测的中转站路由(来自 llm-pi-ai providers 的 baseURL +
|
|
17
|
+
/** 一个待探测的中转站路由(来自 llm-pi-ai providers 的 baseURL + key 候选 ref)。 */
|
|
17
18
|
export interface RelayRoute {
|
|
18
19
|
/** llm-pi-ai providers 路由名(key)。 */
|
|
19
20
|
route: string;
|
|
20
21
|
/** 该路由配置的端点地址(origin 来源)。 */
|
|
21
22
|
baseURL: string;
|
|
22
|
-
/**
|
|
23
|
-
|
|
23
|
+
/** 该路由的凭据 ref 候选(按优先序:显式 apiKeyEnv 在首,路由约定名补后)。 */
|
|
24
|
+
apiKeyRefs: readonly string[];
|
|
24
25
|
/** 站点显示名;缺省用路由名。 */
|
|
25
26
|
displayName?: string;
|
|
26
27
|
}
|
|
@@ -53,14 +54,14 @@ export declare function isOfficialBaseUrl(baseURL: string): boolean;
|
|
|
53
54
|
/**
|
|
54
55
|
* 查询单个中转站路由的额度。先试 Sub2API,再试 New API;任一读出额度即返回。
|
|
55
56
|
* @param ctx - host context carrying the credentials seam.
|
|
56
|
-
* @param route - 待探测的路由(baseURL +
|
|
57
|
+
* @param route - 待探测的路由(baseURL + 凭据 ref 候选)。
|
|
57
58
|
* @returns 该路由的一行额度结果(status 标记成败)。
|
|
58
59
|
*/
|
|
59
60
|
export declare function queryRelayQuota(ctx: Context, route: RelayRoute): Promise<RelayQuota>;
|
|
60
61
|
/**
|
|
61
62
|
* 批量查询多个中转站路由的额度(每个独立成败,互不影响)。
|
|
62
63
|
* @param ctx - host context carrying the credentials seam.
|
|
63
|
-
* @param routes - 配了 baseURL 且
|
|
64
|
+
* @param routes - 配了 baseURL 且 key 候选可得(显式引用存在或约定 ref 解析出值)的路由列表。
|
|
64
65
|
* @returns 每个路由一行的额度结果。
|
|
65
66
|
*/
|
|
66
67
|
export declare function queryRelayQuotas(ctx: Context, routes: readonly RelayRoute[]): Promise<readonly RelayQuota[]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主 llm-pi-ai 新代际的路由 key 约定(订阅额度 / 余额 / 中转站 / 声明端点共用)。
|
|
3
|
+
*
|
|
4
|
+
* 新版宿主设置页把路由 key 直接存成凭据 refs(命名 = 路由 id 大写下划线 +
|
|
5
|
+
* `_API_KEY`),providers 配置里只剩 models 列表;chat 认证走 pi-ai 目录路由的
|
|
6
|
+
* ambient 发现,不再需要显式 `apiKeyEnv`。插件侧所有「按路由取 key」的读取口
|
|
7
|
+
* 共用本模块的候选名推导与顺序解析——显式引用优先,约定名补后。
|
|
8
|
+
*/
|
|
9
|
+
/** 候选 ref 名:路由 id 约定名(`<ROUTE 大写下划线>_API_KEY`)优先,其后补宿主
|
|
10
|
+
* pi-ai 目录的 ambient env 名(仅当与约定名不同——目录把 kimi-coding 指到
|
|
11
|
+
* KIMI_API_KEY、opencode-go 与 opencode 同用 OPENCODE_API_KEY)。 */
|
|
12
|
+
export declare function routeKeyRefCandidates(route: string): readonly string[];
|
|
13
|
+
/** 宽容的凭据解析口:ref 名 → 非空值;未配置 / 解析失败返回 undefined。 */
|
|
14
|
+
export type RefResolver = (name: string) => Promise<string | undefined>;
|
|
15
|
+
/** 依次解析候选 ref,返回第一个解析出非空值的 ref 与值;全部落空返回 undefined。 */
|
|
16
|
+
export declare function firstResolvedRef(refs: readonly string[], resolveRef: RefResolver): Promise<{
|
|
17
|
+
ref: string;
|
|
18
|
+
value: string;
|
|
19
|
+
} | undefined>;
|
|
20
|
+
//# sourceMappingURL=route-keys.d.ts.map
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* usage_stats 工具开关:插件自带的读/写接口(不依赖宿主浏览器设置白名单)。
|
|
5
5
|
* GET 返回当前是否注入;POST 写设置命名空间(重启应用后工具注入随新值生效)。
|
|
6
|
+
* 写回失败回 503 + 宿主错误文案(issue #98:此前 200 + 笼统 invalid,开关无声
|
|
7
|
+
* 弹回且无任何诊断线索);body 非 JSON 回 400。
|
|
6
8
|
*
|
|
7
9
|
* 双世代宿主(issue #19 + #28 + v1.4.19)的接线在装配器里完成:
|
|
8
10
|
* - 旧宿主 ≤0.1.6 走 `settings.register` 命名空间 scope。
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
* other subscription providers the harness recognizes (volcengine / baidu /
|
|
10
10
|
* qwen / xiaomi token plans, agent plans…) are identified and listed with a
|
|
11
11
|
* "no quota API" marker rather than hidden. API keys come from the `llm-pi-ai`
|
|
12
|
-
* settings namespace
|
|
12
|
+
* settings namespace resolved through the credentials seam: the classic
|
|
13
|
+
* `apiKeyEnv` refs, or — on hosts whose settings UI stores route keys as
|
|
14
|
+
* credentials refs named after the route (`<ROUTE>_API_KEY`, catalog ambient
|
|
15
|
+
* names as fallback) — the conventional refs, matched against the same
|
|
16
|
+
* subscription-class route ids.
|
|
13
17
|
*/
|
|
14
18
|
import type { SubscriptionPlanConfig, SubscriptionQuota, SubscriptionWindow } from './pricing-shared.ts';
|
|
15
19
|
/** 订阅适配器需要的凭据(来自 llm-pi-ai 设置命名空间)。 */
|
|
@@ -46,6 +50,17 @@ export interface SubscriptionKeys {
|
|
|
46
50
|
export declare function parseOpenCodeAuthDocument(document: unknown): string;
|
|
47
51
|
/** 空凭据:全部未配置时的初始值。 */
|
|
48
52
|
export declare const EMPTY_SUBSCRIPTION_KEYS: SubscriptionKeys;
|
|
53
|
+
/**
|
|
54
|
+
* key 解析的可测核心(无文件 I/O):先显式 `apiKeyEnv`(旧口径,优先),key 仍缺
|
|
55
|
+
* 时按约定 ref 名解析(新版宿主的存放形态,见 route-keys.ts)。`keyConfigured`
|
|
56
|
+
* 回报每条路由的 key 是否解析成功,供识别层作为第三配置信号(refResolved)。
|
|
57
|
+
* @param providers - llm-pi-ai 的 providers 视图。
|
|
58
|
+
* @param resolveRef - 凭据 seam:ref 名 → 非空 key 值;未配置/失败返回 undefined。
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveSubscriptionAccess(providers: Readonly<Record<string, SubscriptionRouteConfigView | undefined>>, resolveRef: (name: string) => Promise<string | undefined>): Promise<{
|
|
61
|
+
keys: SubscriptionKeys;
|
|
62
|
+
keyConfigured: ReadonlySet<string>;
|
|
63
|
+
}>;
|
|
49
64
|
/** 已识别的一个订阅套餐(用户在 llm-pi-ai 里配置了 key 的订阅类 provider)。 */
|
|
50
65
|
export interface IdentifiedSubscriptionPlan {
|
|
51
66
|
/** llm-pi-ai 的 provider id(如 kimi-coding、xiaomi-token-plan-cn)。 */
|
|
@@ -57,16 +72,28 @@ export interface IdentifiedSubscriptionPlan {
|
|
|
57
72
|
/** 适配器区域覆盖:两个 Z.ai 路由各自固定区域,互不跟随 keys.zaiRegion。 */
|
|
58
73
|
region?: 'global' | 'bigmodel-cn';
|
|
59
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* 一条 llm-pi-ai 路由的订阅识别视图:三个「已配置」信号任一为真即识别。
|
|
77
|
+
* 新版宿主(内置目录 + ambient 发现)把路由 key 存进凭据 refs,providers 配置里
|
|
78
|
+
* 只剩 models 列表——`apiKeyEnv` 不再是唯一的配置信号。
|
|
79
|
+
*/
|
|
80
|
+
export interface SubscriptionRouteConfigView {
|
|
81
|
+
/** 显式凭据引用(旧口径)。 */
|
|
82
|
+
apiKeyEnv?: string;
|
|
83
|
+
/** 路由声明了非空 models 列表(新版设置页保存的配置形态)。 */
|
|
84
|
+
hasModels?: boolean;
|
|
85
|
+
/** 路由约定 ref(`<ROUTE>_API_KEY` / 目录 ambient 名)解析出了非空 key。 */
|
|
86
|
+
refResolved?: boolean;
|
|
87
|
+
}
|
|
60
88
|
/** 是否是订阅类 provider id(如 kimi-coding、xiaomi-token-plan-cn)。 */
|
|
61
89
|
export declare function isSubscriptionProviderId(providerId: string): boolean;
|
|
62
90
|
/**
|
|
63
|
-
* 从 llm-pi-ai 设置里识别订阅套餐:带订阅类 id
|
|
91
|
+
* 从 llm-pi-ai 设置里识别订阅套餐:带订阅类 id 且任一配置信号成立的 provider
|
|
92
|
+
* (显式 apiKeyEnv / models 已配置 / 约定 ref 解析出了 key)。
|
|
64
93
|
* @param providers - the `providers` map of the llm-pi-ai settings namespace.
|
|
65
94
|
* @returns identified plans in configuration order.
|
|
66
95
|
*/
|
|
67
|
-
export declare function identifySubscriptionPlans(providers: Record<string,
|
|
68
|
-
apiKeyEnv?: string;
|
|
69
|
-
} | undefined> | undefined): IdentifiedSubscriptionPlan[];
|
|
96
|
+
export declare function identifySubscriptionPlans(providers: Record<string, SubscriptionRouteConfigView | undefined> | undefined): IdentifiedSubscriptionPlan[];
|
|
70
97
|
/**
|
|
71
98
|
* 解析 MiniMax Token Plan `/v1/token_plan/remains` 响应。取 general(或 MiniMax-M*)
|
|
72
99
|
* 一行抽出 5h/7d 窗口(total_count 常为 0,以 remaining_percent 为准),不按模型拆条。
|
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.6.
|
|
4
|
+
"version": "1.6.4",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"deepseek",
|