@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/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
- /** 请求头占位符解析:`{{ENV_NAME}}` 经凭据 seam 替换;解析失败返回 null。 */
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 match = /^\{\{([A-Z0-9_]+)\}\}$/i.exec(value.trim());
3266
- if (match === null) {
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 hit = await ctx.credentials.resolve(credentialRef(match[1] ?? ""));
3271
- if (hit === void 0 || hit.value === "") return null;
3272
- resolved[key] = value.replace(match[0], hit.value);
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
  }
@@ -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: 'billing.costAbbr' | 'billing.noData' | 'billing.heatmapLess' | 'billing.heatmapMore') => string;
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 = '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.trendMetric' | 'billing.trendMetricCost' | 'billing.trendMetricTokens' | 'billing.trendEmpty' | 'billing.budget' | 'billing.budgetAmount' | 'billing.budgetSummary' | 'billing.sessions' | 'billing.sessionTitle' | '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.footer' | 'billing.footerCredit' | 'billing.lastUpdated' | 'billing.noData' | 'billing.todayRate' | 'billing.rateLive' | 'billing.rateBuiltin' | 'billing.promoBadge' | 'billing.promoUntil' | 'billing.promoOpenEnded' | 'billing.pricingTip' | 'billing.pricingUnit' | 'billing.pricingNotes' | 'billing.ubPeak' | 'billing.ubOff' | 'billing.ubStd' | 'billing.peakBand' | 'billing.pricingSource' | 'billing.noteCache' | 'billing.noteBand' | 'billing.noteSource' | 'billing.balance' | 'billing.balanceUnconfigured' | 'billing.balanceUnauthorized' | 'billing.balanceUnreachable' | 'billing.uncatalogued' | 'billing.estimatedPricing' | 'billing.balanceDays' | 'billing.balanceLowBody' | 'billing.reconcileDrift' | 'billing.reconcileDismiss' | '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.floatWindow' | 'billing.floatModeCombined' | 'billing.floatModeSubscription' | 'billing.floatMode' | 'billing.floatTargets' | 'billing.floatWindowHint' | 'billing.floatNoTargets' | 'billing.floatNoTargetsHint' | 'billing.cardDisplay' | 'billing.cardDisplayHint' | 'billing.cardMetric' | 'billing.cardMetricMoney' | 'billing.cardMetricTokens' | 'billing.triggerMonthTokens' | 'billing.floatPrev' | 'billing.floatNext' | 'billing.subscriptionsStale' | 'billing.staleLedgerNotice' | 'billing.tokenCacheWrite' | 'billing.toolRank' | 'billing.toolName' | 'billing.userPrices' | 'billing.userPricesHint' | 'billing.userPriceSave' | 'billing.userPriceModel' | 'billing.userPriceSource' | 'billing.userPriceSourceHint' | 'billing.userPriceOffPeak' | 'billing.userPriceCurrency' | 'billing.userPriceAdd' | 'billing.userPriceRemove' | 'billing.sessionStaleBadge' | 'billing.heatmapLess' | 'billing.heatmapMore' | 'billing.currency' | 'billing.currencyCny' | 'billing.currencyUsd' | 'billing.heatmap' | 'billing.rounds' | 'billing.roundsHint' | 'billing.anomaly' | 'billing.workspaces' | 'billing.workspacesHint' | 'billing.plan' | 'billing.remaining' | 'billing.unknownModel' | 'billing.model' | 'billing.thModel' | 'billing.thInputMiss' | 'billing.thInputHit' | '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.peakSharePerCall' | 'billing.offPeakSavings' | 'billing.perfMax' | '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.peakAlertPos' | 'billing.peakAlertMode' | '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.perfAll' | 'billing.perfChartEmpty' | '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.tokenViewStructure' | 'billing.tokenViewModel' | 'billing.tokenHitShort' | 'billing.tokenMissShort' | '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.unpricedHint' | 'billing.searchEstimateHint' | 'billing.siteListDisplay' | 'billing.siteListDisplayHint' | 'billing.liveCostBar' | 'billing.liveCostBarHint' | 'billing.exportCsvSite' | 'billing.panelRelayQuota' | 'billing.relayBalance' | 'billing.relayNoQuota' | 'billing.relayWindowUsed' | 'billing.relayKindNewApi' | 'billing.relayKindSub2Api' | 'billing.relayKindUnknown';
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: 'billing.model' | 'billing.costAbbr') => string;
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.19",
4
+ "version": "1.0.21",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },