@kenz1117/dsh-ui-usage-billing 1.0.19 → 1.0.21
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 +2 -2
- package/README.md +2 -2
- package/lib/client.js +3 -3
- package/lib/index.js +62 -6
- package/lib/types/balance.d.ts +14 -0
- package/lib/types/client/heatmap.d.ts +1 -1
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/round-chart.d.ts +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2919,6 +2919,42 @@ function queryXai(ctx, apiKeyEnv) {
|
|
|
2919
2919
|
};
|
|
2920
2920
|
});
|
|
2921
2921
|
}
|
|
2922
|
+
/** TokenDance 钱包端点(issue #27:GET + Bearer,与模型调用同一把 key)。 */
|
|
2923
|
+
const TOKENDANCE_BALANCE_URL = "https://tokendance.space/portal/api/v1/user/balance";
|
|
2924
|
+
/** 微元 → 元:TokenDance 全部金额字段以 1 元 = 1,000,000 微元计。 */
|
|
2925
|
+
const TOKENDANCE_MICRO_PER_YUAN = 1e6;
|
|
2926
|
+
/**
|
|
2927
|
+
* 从 TokenDance 余额响应提取剩余余额并换算为元。导出供测试:纯函数。
|
|
2928
|
+
* 优先用服务端现成的 `balance.balance`(= credits - credits_used);缺失时
|
|
2929
|
+
* 按两个明细字段相减推导,字段全部缺失返回 undefined。
|
|
2930
|
+
* @param data - 余额端点的 JSON 响应(`{ balance: { credits, credits_used, balance } }`,微元)。
|
|
2931
|
+
* @returns 剩余余额(元);提取不到返回 undefined。
|
|
2932
|
+
*/
|
|
2933
|
+
function pickTokenDanceBalanceCny(data) {
|
|
2934
|
+
const doc = data;
|
|
2935
|
+
const direct = toNumber$1(doc?.balance?.balance);
|
|
2936
|
+
if (direct !== void 0) return direct / TOKENDANCE_MICRO_PER_YUAN;
|
|
2937
|
+
const credits = toNumber$1(doc?.balance?.credits);
|
|
2938
|
+
const used = toNumber$1(doc?.balance?.credits_used);
|
|
2939
|
+
if (credits !== void 0 && used !== void 0) return (credits - used) / TOKENDANCE_MICRO_PER_YUAN;
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* 查询 TokenDance Space 钱包余额(issue #26/#27)。`queryBearerBalance` 已覆盖
|
|
2943
|
+
* 认证失败(401 → unauthorized)与熔断/超时/重试,这里只负责微元 → 元换算。
|
|
2944
|
+
* @param ctx - host context carrying the credentials seam.
|
|
2945
|
+
* @param apiKeyEnv - credential reference resolving the TokenDance API key.
|
|
2946
|
+
*/
|
|
2947
|
+
function queryTokenDance(ctx, apiKeyEnv) {
|
|
2948
|
+
return queryBearerBalance(ctx, TOKENDANCE_BALANCE_URL, apiKeyEnv, "TokenDance", "TokenDance", (data) => {
|
|
2949
|
+
const cny = pickTokenDanceBalanceCny(data);
|
|
2950
|
+
return {
|
|
2951
|
+
provider: "TokenDance",
|
|
2952
|
+
displayName: "TokenDance",
|
|
2953
|
+
currency: "CNY",
|
|
2954
|
+
...cny !== void 0 ? { totalBalance: cny } : {}
|
|
2955
|
+
};
|
|
2956
|
+
});
|
|
2957
|
+
}
|
|
2922
2958
|
/**
|
|
2923
2959
|
* Query the Zhipu GLM / Z.ai (国内 bigmodel-cn 域) account balance.
|
|
2924
2960
|
* 与订阅(zai-coding-cn 的 Coding Plan)互补:一个平台可同时有钱包余额与订阅
|
|
@@ -2976,6 +3012,16 @@ const QUERIERS = [
|
|
|
2976
3012
|
displayName: "智谱 AI",
|
|
2977
3013
|
querier: queryZhipu
|
|
2978
3014
|
},
|
|
3015
|
+
{
|
|
3016
|
+
route: "tokendance",
|
|
3017
|
+
displayName: "TokenDance",
|
|
3018
|
+
querier: queryTokenDance
|
|
3019
|
+
},
|
|
3020
|
+
{
|
|
3021
|
+
route: "tokendance-space",
|
|
3022
|
+
displayName: "TokenDance",
|
|
3023
|
+
querier: queryTokenDance
|
|
3024
|
+
},
|
|
2979
3025
|
{
|
|
2980
3026
|
route: "tencent-tokenhub",
|
|
2981
3027
|
displayName: "腾讯云 TokenHub",
|
|
@@ -3258,18 +3304,28 @@ function evalExtract(rule, data) {
|
|
|
3258
3304
|
}
|
|
3259
3305
|
return base;
|
|
3260
3306
|
}
|
|
3261
|
-
/**
|
|
3307
|
+
/**
|
|
3308
|
+
* 请求头占位符解析:值中任意位置的 `{{ENV_NAME}}` 经凭据 seam 替换(如
|
|
3309
|
+
* `Bearer {{KEY}}`、`token={{KEY}}`、一处多占位符);被引用的任一凭据缺失
|
|
3310
|
+
* 或为空 → 返回 null(fail-closed,与完整占位符形态的历史语义一致)。
|
|
3311
|
+
*/
|
|
3262
3312
|
async function resolveHeaders(ctx, headers) {
|
|
3263
3313
|
const resolved = {};
|
|
3264
3314
|
for (const [key, value] of Object.entries(headers)) {
|
|
3265
|
-
const
|
|
3266
|
-
if (
|
|
3315
|
+
const matches = [...value.matchAll(/\{\{([A-Z0-9_]+)\}\}/gi)];
|
|
3316
|
+
if (matches.length === 0) {
|
|
3267
3317
|
resolved[key] = value;
|
|
3268
3318
|
continue;
|
|
3269
3319
|
}
|
|
3270
|
-
const
|
|
3271
|
-
|
|
3272
|
-
|
|
3320
|
+
const hits = /* @__PURE__ */ new Map();
|
|
3321
|
+
for (const m of matches) {
|
|
3322
|
+
const name = m[1] ?? "";
|
|
3323
|
+
if (hits.has(name)) continue;
|
|
3324
|
+
const hit = await ctx.credentials.resolve(credentialRef(name));
|
|
3325
|
+
if (hit === void 0 || hit.value === "") return null;
|
|
3326
|
+
hits.set(name, hit.value);
|
|
3327
|
+
}
|
|
3328
|
+
resolved[key] = value.replace(/\{\{([A-Z0-9_]+)\}\}/gi, (raw, name) => hits.get(name) ?? raw);
|
|
3273
3329
|
}
|
|
3274
3330
|
return resolved;
|
|
3275
3331
|
}
|
package/lib/types/balance.d.ts
CHANGED
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
16
16
|
import type { CustomBalanceConfig, CustomBalanceExtract, ProviderBalance } from './pricing-shared.ts';
|
|
17
|
+
/**
|
|
18
|
+
* 从 TokenDance 余额响应提取剩余余额并换算为元。导出供测试:纯函数。
|
|
19
|
+
* 优先用服务端现成的 `balance.balance`(= credits - credits_used);缺失时
|
|
20
|
+
* 按两个明细字段相减推导,字段全部缺失返回 undefined。
|
|
21
|
+
* @param data - 余额端点的 JSON 响应(`{ balance: { credits, credits_used, balance } }`,微元)。
|
|
22
|
+
* @returns 剩余余额(元);提取不到返回 undefined。
|
|
23
|
+
*/
|
|
24
|
+
export declare function pickTokenDanceBalanceCny(data: unknown): number | undefined;
|
|
17
25
|
/**
|
|
18
26
|
* Query every configured provider's account balance. A provider is queried only
|
|
19
27
|
* when its llm-pi-ai route has an `apiKeyEnv`; absent routes answer
|
|
@@ -52,6 +60,12 @@ export declare function pickRemainingQuota(source: unknown): number | undefined;
|
|
|
52
60
|
* @returns 数值;取不到或结果非有限数返回 undefined。
|
|
53
61
|
*/
|
|
54
62
|
export declare function evalExtract(rule: CustomBalanceExtract, data: unknown): number | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* 请求头占位符解析:值中任意位置的 `{{ENV_NAME}}` 经凭据 seam 替换(如
|
|
65
|
+
* `Bearer {{KEY}}`、`token={{KEY}}`、一处多占位符);被引用的任一凭据缺失
|
|
66
|
+
* 或为空 → 返回 null(fail-closed,与完整占位符形态的历史语义一致)。
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveHeaders(ctx: Context, headers: Record<string, string>): Promise<Record<string, string> | null>;
|
|
55
69
|
/**
|
|
56
70
|
* 查询自定义 Provider 余额(插件 config 的 `customBalances`)。每个条目独立
|
|
57
71
|
* 成败:占位符凭据缺失 → unconfigured;401/403 → unauthorized;网络或提取
|
|
@@ -32,6 +32,6 @@ export declare function UsageHeatmap({ days, currency, now, t, range }: {
|
|
|
32
32
|
currency: CostCurrency;
|
|
33
33
|
now?: Date;
|
|
34
34
|
range?: 'month' | 'year';
|
|
35
|
-
t: (key: '
|
|
35
|
+
t: (key: 'costAbbr' | 'noData' | 'heatmapLess' | 'heatmapMore') => string;
|
|
36
36
|
}): React.ReactNode;
|
|
37
37
|
//# sourceMappingURL=heatmap.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Locale dictionaries for the usage billing surface. */
|
|
2
|
-
export type UsageBillingKey = '
|
|
2
|
+
export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'trendEmpty' | 'budget' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'project' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | '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' | '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' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'triggerMonthTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemove' | 'sessionStaleBadge' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'workspaces' | 'workspacesHint' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionIncluded' | 'free' | 'official' | 'thirdParty' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapMonth' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginVersion' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenCacheHitRate' | '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' | '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>;
|
|
@@ -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: '
|
|
35
|
+
t: (key: 'model' | 'costAbbr') => string;
|
|
36
36
|
}): React.ReactNode;
|
|
37
37
|
//# sourceMappingURL=round-chart.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.0.
|
|
4
|
+
"version": "1.0.21",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|