@linxin666/dsh-usage 0.3.18 → 0.3.19
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/client.js +12 -5
- package/lib/client.js.map +1 -1
- package/lib/types/client/locales.d.ts +1 -0
- package/package.json +2 -2
- package/src/client/UsageSectionCard.tsx +18 -8
- package/src/client/locales.ts +2 -0
package/lib/client.js
CHANGED
|
@@ -82,6 +82,7 @@ window.__ModuleLoader__.load({
|
|
|
82
82
|
"usage.balance": "余额",
|
|
83
83
|
"usage.balance.unsupported": "暂不支持余额查询",
|
|
84
84
|
"usage.balance.noCredential": "未配置凭据",
|
|
85
|
+
"usage.balance.noneConfigured": "没有已配置的提供方",
|
|
85
86
|
"usage.oauth": "OAuth 凭据,不做余额查询",
|
|
86
87
|
"usage.plan.reset": "{date} 重置",
|
|
87
88
|
"usage.plan.noPlan": "未检测到套餐数据",
|
|
@@ -125,6 +126,7 @@ window.__ModuleLoader__.load({
|
|
|
125
126
|
"usage.balance": "Balance",
|
|
126
127
|
"usage.balance.unsupported": "Balance query not supported",
|
|
127
128
|
"usage.balance.noCredential": "No credential configured",
|
|
129
|
+
"usage.balance.noneConfigured": "No providers configured",
|
|
128
130
|
"usage.oauth": "OAuth credential, no balance query",
|
|
129
131
|
"usage.plan.reset": "resets {date}",
|
|
130
132
|
"usage.plan.noPlan": "No plan data detected",
|
|
@@ -720,6 +722,10 @@ window.__ModuleLoader__.load({
|
|
|
720
722
|
if (percent >= 70) return usage_module_css_default.barWarn;
|
|
721
723
|
return usage_module_css_default.barFill;
|
|
722
724
|
}
|
|
725
|
+
/** A provider row backed by a configured credential (api key, env key, or OAuth grant). */
|
|
726
|
+
function isConfigured(provider) {
|
|
727
|
+
return provider.credential !== "none";
|
|
728
|
+
}
|
|
723
729
|
function TotalsRow(props) {
|
|
724
730
|
const { totals } = props;
|
|
725
731
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -881,7 +887,7 @@ window.__ModuleLoader__.load({
|
|
|
881
887
|
const currentProvider = snapshot.providers.find((provider) => provider.provider === current.provider);
|
|
882
888
|
const deepseekPeriod = deepseekPeriodAt(Date.now());
|
|
883
889
|
const deepseekVisible = current.provider !== void 0 && isDeepSeekProviderRoute(current.provider) || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider));
|
|
884
|
-
const planProviders = snapshot.providers.filter((provider) => provider.planSupported === true || provider.planSupported === void 0 && provider.plan !== void 0);
|
|
890
|
+
const planProviders = snapshot.providers.filter((provider) => isConfigured(provider) && (provider.planSupported === true || provider.planSupported === void 0 && provider.plan !== void 0));
|
|
885
891
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
886
892
|
className: usage_module_css_default.section,
|
|
887
893
|
"data-dsh-plugin": "usage",
|
|
@@ -991,19 +997,20 @@ window.__ModuleLoader__.load({
|
|
|
991
997
|
children: t("usage.balance")
|
|
992
998
|
}),
|
|
993
999
|
(() => {
|
|
994
|
-
const
|
|
1000
|
+
const configured = snapshot.providers.filter(isConfigured);
|
|
1001
|
+
const rows = configured.filter((provider) => provider.balanceSupported === true || provider.balanceSupported === void 0 && provider.supported);
|
|
995
1002
|
if (rows.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
996
1003
|
className: usage_module_css_default.muted,
|
|
997
|
-
children: t("usage.balance.unsupported")
|
|
1004
|
+
children: configured.length === 0 ? t("usage.balance.noneConfigured") : t("usage.balance.unsupported")
|
|
998
1005
|
});
|
|
999
1006
|
return rows.map((provider) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderRow, {
|
|
1000
1007
|
provider,
|
|
1001
1008
|
current: current.provider
|
|
1002
1009
|
}, provider.provider));
|
|
1003
1010
|
})(),
|
|
1004
|
-
snapshot.providers.some((provider) => provider.error !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1011
|
+
snapshot.providers.some((provider) => isConfigured(provider) && provider.error !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1005
1012
|
className: usage_module_css_default.errorLine,
|
|
1006
|
-
children: snapshot.providers.filter((provider) => provider.error !== void 0).map((provider) => `${provider.displayName}: ${t("usage.provider.error", { error: provider.error ?? "" })}`).join(t("usage.errorListSeparator"))
|
|
1013
|
+
children: snapshot.providers.filter((provider) => isConfigured(provider) && provider.error !== void 0).map((provider) => `${provider.displayName}: ${t("usage.provider.error", { error: provider.error ?? "" })}`).join(t("usage.errorListSeparator"))
|
|
1007
1014
|
})
|
|
1008
1015
|
]
|
|
1009
1016
|
}),
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["styles"],"sources":["../../../packages/dsh-usage/src/client/usage-store.ts","../../../packages/dsh-usage/src/client/locales.ts","../../../packages/dsh-usage/src/core/adapters.ts","../../../packages/dsh-usage/src/core/pricing.ts","../../../packages/dsh-usage/src/client/UsageSectionCard.tsx","../../../packages/dsh-usage/src/client/index.ts"],"sourcesContent":["/**\n * Browser-side usage store: the overview snapshot polled from the host plus\n * the fetch lifecycle. Section-local: the store lives while the settings\n * section is mounted, so polling only runs while the page is open.\n * @module @linxin666/dsh-usage/client/usage-store\n */\n\nimport { defineStore } from '@deepseek-ai/dsh-client-store'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-store'\nimport type { UsageOverviewView } from '../core/types.ts'\n\n/** Section UI state as consumers see it. */\nexport interface UsageUiState {\n /** Latest host overview; null before the first successful fetch. */\n snapshot: UsageOverviewView | null\n /** Fetch lifecycle. */\n status: 'loading' | 'ready' | 'error'\n /** Transport error message, when any. */\n error: string | null\n}\n\n/** Store write set. */\nexport type UsageUiActions = {\n /** Replace the overview (poll result). */\n setSnapshot: (draft: UsageUiState, snapshot: UsageOverviewView) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: UsageUiState, status: UsageUiState['status'], error: string | null) => void\n}\n\n/** Create the usage store handle (apply world only; never module-level). */\nexport function createUsageStore(): EngineStoreHandle<UsageUiState, UsageUiActions> {\n return defineStore({\n init: (): UsageUiState => ({\n snapshot: null,\n status: 'loading',\n error: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n draft.snapshot = snapshot\n draft.status = 'ready'\n draft.error = null\n },\n setState: (draft, status, error) => {\n draft.status = status\n draft.error = error\n },\n },\n })\n}\n\nexport type UsageStoreInstance = EngineStoreInstance<UsageUiState, UsageUiActions>\n","/**\n * dsh-usage locale dictionaries (zh/en). The zh dictionary is the key source;\n * `en` mirrors its full key set (packages/AGENTS.md bilingual discipline).\n * @module @linxin666/dsh-usage/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'dsh-web-ui-usage'\n\n/** Chinese copy. */\nexport const zh = {\n 'usage.title': '使用统计',\n 'usage.tab.usage': '用量',\n 'usage.tab.plans': '个人套餐',\n 'usage.refresh': '刷新',\n 'usage.refreshing': '刷新中…',\n 'usage.updated': '更新于 {time}',\n 'usage.loading': '正在加载用量数据…',\n 'usage.error': '加载失败:{error}',\n 'usage.current': '当前',\n 'usage.today': '今日用量',\n 'usage.today.cost': '今日消费(估算)',\n 'usage.peak.on': 'DeepSeek 高峰时段:计价 ×2,{time} 结束',\n 'usage.peak.off': 'DeepSeek 空闲时段:计价为高峰一半,{time} 进入高峰',\n 'usage.calls': '{n} 次调用',\n 'usage.tokens.total': '总 tokens',\n 'usage.tokens.input': '输入',\n 'usage.tokens.output': '输出',\n 'usage.tokens.cacheRead': '缓存读',\n 'usage.tokens.cacheWrite': '缓存写',\n 'usage.trend': '近 30 天',\n 'usage.noData': '暂无用量数据(统计自插件启用起)',\n 'usage.balance': '余额',\n 'usage.balance.unsupported': '暂不支持余额查询',\n 'usage.balance.noCredential': '未配置凭据',\n 'usage.oauth': 'OAuth 凭据,不做余额查询',\n 'usage.plan.reset': '{date} 重置',\n 'usage.plan.noPlan': '未检测到套餐数据',\n 'usage.plan.noneConfigured': '没有已配置的套餐类 provider(如 Kimi、GLM、OpenCode Go、MiniMax、Codex 订阅)',\n 'usage.plan.windows.5h': '5 小时',\n 'usage.plan.windows.week': '每周',\n 'usage.plan.windows.month': '每月',\n 'usage.provider.error': '查询失败:{error}',\n 'usage.errorListSeparator': ';',\n 'usage.config.title': '设置',\n 'usage.config.enabled': '启用插件',\n 'usage.config.pollIntervalSec': '轮询间隔(秒)',\n 'usage.config.bubbleMode': '宠物气泡',\n 'usage.config.bubbleMode.always': '常驻显示',\n 'usage.config.bubbleMode.change': '仅变化时',\n 'usage.config.bubbleMode.off': '关闭',\n}\n\n/** English mirror; every zh key present. */\nexport const en: Record<UsageKey, string> = {\n 'usage.title': 'Usage Statistics',\n 'usage.tab.usage': 'Usage',\n 'usage.tab.plans': 'Plans',\n 'usage.refresh': 'Refresh',\n 'usage.refreshing': 'Refreshing…',\n 'usage.updated': 'Updated {time}',\n 'usage.loading': 'Loading usage data…',\n 'usage.error': 'Failed to load: {error}',\n 'usage.current': 'Current',\n 'usage.today': 'Today',\n 'usage.today.cost': 'Today spend (estimated)',\n 'usage.peak.on': 'DeepSeek peak hours: 2x pricing, ends {time}',\n 'usage.peak.off': 'DeepSeek off-peak: half of peak pricing, peak returns {time}',\n 'usage.calls': '{n} calls',\n 'usage.tokens.total': 'Total tokens',\n 'usage.tokens.input': 'Input',\n 'usage.tokens.output': 'Output',\n 'usage.tokens.cacheRead': 'Cache read',\n 'usage.tokens.cacheWrite': 'Cache write',\n 'usage.trend': 'Last 30 days',\n 'usage.noData': 'No usage data yet (counting starts when the plugin is enabled)',\n 'usage.balance': 'Balance',\n 'usage.balance.unsupported': 'Balance query not supported',\n 'usage.balance.noCredential': 'No credential configured',\n 'usage.oauth': 'OAuth credential, no balance query',\n 'usage.plan.reset': 'resets {date}',\n 'usage.plan.noPlan': 'No plan data detected',\n 'usage.plan.noneConfigured': 'No plan-capable provider configured (such as Kimi, GLM, OpenCode Go, MiniMax, Codex subscription)',\n 'usage.plan.windows.5h': '5 hours',\n 'usage.plan.windows.week': 'Weekly',\n 'usage.plan.windows.month': 'Monthly',\n 'usage.provider.error': 'Query failed: {error}',\n 'usage.errorListSeparator': '; ',\n 'usage.config.title': 'Settings',\n 'usage.config.enabled': 'Enable plugin',\n 'usage.config.pollIntervalSec': 'Poll interval (seconds)',\n 'usage.config.bubbleMode': 'Pet bubble',\n 'usage.config.bubbleMode.always': 'Always visible',\n 'usage.config.bubbleMode.change': 'On change',\n 'usage.config.bubbleMode.off': 'Off',\n}\n\nexport type UsageKey = keyof typeof zh\n\n/**\n * Active dictionary, picked by the document language at call time. The\n * section resolves its copy the same tiny way the pet's DOM-injected surface\n * does (the settings section has no framework locale seat of its own).\n */\nexport function dictionary(): Record<UsageKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/** Translate a key with optional `{name}` template params; missing keys degrade to the key. */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-usage UI copy. */\n 'dsh-web-ui-usage': UsageKey\n }\n}\n","/**\n * Provider adapters: one entry per provider family this plugin can query for\n * a pay-as-you-go balance or a coding-plan quota. Adapters are pure — they\n * build an HTTP probe from a resolved credential and parse the response — so\n * the service stays transport-only and every endpoint detail lives here.\n *\n * All probes run host-side (Node fetch); API keys never reach the browser.\n * @module @linxin666/dsh-usage/core/adapters\n */\n\nimport type { PlanWindowView } from './types.ts'\n\n/** One HTTP probe the service executes verbatim. */\nexport interface ProbeSpec {\n url: string\n headers: Record<string, string>\n}\n\n/** Inputs an adapter may use to build a probe. */\nexport interface AdapterProbeContext {\n /** Resolved credential value: a raw API key, or an OAuth access token for the oauth-aware adapters. */\n apiKey: string\n /** Provider account id riding a dedicated header, when the credential carries one (Codex). */\n accountId?: string\n}\n\n/** A parsed balance fact. */\nexport interface BalanceParse {\n currency: string\n totalBalance: string\n}\n\n/** A parsed plan-quota fact. */\nexport interface PlanParse {\n planName?: string\n windows: PlanWindowView[]\n}\n\n/** One provider family's probing capability. */\nexport interface ProviderAdapter {\n /** Provider route keys this adapter serves. */\n ids: readonly string[]\n /** Fallback display name when the LLM runtime has none. */\n displayName: string\n balance?: {\n build(context: AdapterProbeContext): ProbeSpec\n parse(status: number, body: unknown): BalanceParse | undefined\n }\n plan?: {\n build(context: AdapterProbeContext): ProbeSpec\n parse(status: number, body: unknown): PlanParse | undefined\n }\n}\n\n/** Parse a string/number into a finite number, else undefined. */\nfunction toNum(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\n/** Read a string field that must be a non-empty string. */\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined\n}\n\n/** Format a number to a fixed 2-decimal display string. */\nfunction money(value: number): string {\n return value.toFixed(2)\n}\n\n/** Millisecond epoch or ISO string → normalized ISO 8601, else undefined. */\nfunction toIso(value: unknown): string | undefined {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n const ms = value < 1e12 ? value * 1000 : value\n const date = new Date(ms)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n }\n const text = str(value)\n if (text === undefined) return undefined\n // Epoch instants also arrive as strings (\"1756428000\"); route them through\n // the numeric branch instead of letting Date() reject them.\n if (/^\\d+$/.test(text)) return toIso(Number(text))\n const date = new Date(text)\n // The contract is \"normalized ISO 8601, else undefined\": returning the raw\n // text would render as \"Invalid Date\" downstream.\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/** Used-percent helper guarding zero/absent limits. */\nfunction usedPercent(used: unknown, limit: unknown): number | undefined {\n const usedNum = toNum(used)\n const limitNum = toNum(limit)\n if (usedNum === undefined || limitNum === undefined || limitNum <= 0) return undefined\n return Math.max(0, Math.min(100, (usedNum / limitNum) * 100))\n}\n\nfunction bearer(apiKey: string): Record<string, string> {\n return { authorization: `Bearer ${apiKey}` }\n}\n\n/**\n * The official pay-as-you-go balance. `deepseek` is the configurable-catalog\n * route key; `deepseek-official` is the live provider route the llm-deepseek\n * adapter registers (sessions and agent-default-model carry it), so both ids\n * must resolve here or the current provider would never be probed.\n */\nconst DEEPSEEK: ProviderAdapter = {\n ids: ['deepseek', 'deepseek-official'],\n displayName: 'DeepSeek',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://api.deepseek.com/user/balance', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const infos = (body as Record<string, unknown>).balance_infos\n if (!Array.isArray(infos) || infos.length === 0) return undefined\n const first = infos[0]\n if (typeof first !== 'object' || first === null) return undefined\n const currency = str((first as Record<string, unknown>).currency)\n const total = str((first as Record<string, unknown>).total_balance)\n if (currency === undefined || total === undefined) return undefined\n return { currency, totalBalance: total }\n },\n },\n}\n\n/** Moonshot pay-as-you-go balance; CN bills in CNY, international in USD. */\nfunction moonshotBalance(host: string, currency: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'Moonshot AI',\n balance: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/users/me/balance`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const available = toNum((data as Record<string, unknown>).available_balance)\n if (available === undefined) return undefined\n return { currency, totalBalance: money(available) }\n },\n },\n }\n}\n\n/** Kimi For Coding quota: top-level `usage` is the weekly summary, `limits[]` the per-window rows. */\nconst KIMI_CODING: ProviderAdapter = {\n ids: ['kimi-coding'],\n displayName: 'Kimi For Coding',\n plan: {\n build: ({ apiKey }) => ({ url: 'https://api.kimi.com/coding/v1/usages', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const root = body as Record<string, unknown>\n const windows: PlanWindowView[] = []\n const limits = root.limits\n if (Array.isArray(limits)) {\n for (const entry of limits) {\n if (typeof entry !== 'object' || entry === null) continue\n const detail = (entry as Record<string, unknown>).detail\n const window = (entry as Record<string, unknown>).window\n if (typeof detail !== 'object' || detail === null) continue\n const row = detail as Record<string, unknown>\n const duration = typeof window === 'object' && window !== null ? toNum((window as Record<string, unknown>).duration) : undefined\n const unit = typeof window === 'object' && window !== null ? str((window as Record<string, unknown>).timeUnit) : undefined\n const key = duration === 300 && unit === 'TIME_UNIT_MINUTE' ? '5h' : duration !== undefined ? `w-${duration}` : 'window'\n const percent = usedPercent(row.used, row.limit)\n windows.push({ key, name: str(row.name), percent, resetsAt: toIso(row.resetTime) })\n }\n }\n const usage = root.usage\n if (typeof usage === 'object' && usage !== null) {\n const weekly = usage as Record<string, unknown>\n windows.push({\n key: 'week',\n name: 'Weekly',\n percent: usedPercent(weekly.used, weekly.limit),\n resetsAt: toIso(weekly.resetTime),\n })\n }\n if (windows.length === 0) return undefined\n const user = root.user\n const membership = typeof user === 'object' && user !== null ? (user as Record<string, unknown>).membership : undefined\n const planName = typeof membership === 'object' && membership !== null ? str((membership as Record<string, unknown>).level) : undefined\n return { planName, windows }\n },\n },\n}\n\n/** GLM Coding Plan quota; auth is the RAW key without a Bearer prefix. */\nfunction glmPlan(host: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'GLM Coding Plan',\n plan: {\n build: ({ apiKey }) => ({\n url: `https://${host}/api/monitor/usage/quota/limit`,\n headers: { authorization: apiKey, 'accept-language': 'en-US,en' },\n }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const root = body as Record<string, unknown>\n if (root.success !== true) return undefined\n const data = root.data\n if (typeof data !== 'object' || data === null) return undefined\n const limits = (data as Record<string, unknown>).limits\n if (!Array.isArray(limits)) return undefined\n const windows: PlanWindowView[] = []\n for (const entry of limits) {\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const unit = toNum(row.unit)\n const percent = toNum(row.percentage)\n windows.push({\n key: unit === 3 ? '5h' : unit === 6 ? 'week' : unit !== undefined ? `unit-${unit}` : 'window',\n percent: percent === undefined ? undefined : Math.max(0, Math.min(100, percent)),\n resetsAt: toIso(row.nextResetTime),\n })\n }\n if (windows.length === 0) return undefined\n return { planName: str((data as Record<string, unknown>).level), windows }\n },\n },\n }\n}\n\n/** OpenCode Go quota: percent-only rolling/weekly/monthly windows. */\nconst OPENCODE_GO: ProviderAdapter = {\n ids: ['opencode-go'],\n displayName: 'OpenCode Go',\n plan: {\n build: ({ apiKey }) => ({ url: 'https://opencode.ai/zen/go/v1/usage', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const usage = (body as Record<string, unknown>).usage\n if (typeof usage !== 'object' || usage === null) return undefined\n const windows: PlanWindowView[] = []\n const keys: Array<[string, string]> = [['rolling', '5h'], ['weekly', 'week'], ['monthly', 'month']]\n for (const [field, key] of keys) {\n const entry = (usage as Record<string, unknown>)[field]\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const percent = toNum(row.percent)\n windows.push({\n key,\n percent: percent === undefined ? undefined : Math.max(0, Math.min(100, percent)),\n // percent 0 resetsAt is a placeholder (now + window); drop it.\n resetsAt: percent === 0 ? undefined : toIso(row.resetsAt),\n })\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n}\n\n/** MiniMax coding-plan remains: remaining-percent semantics, `general` model entry. */\nfunction minimaxPlan(host: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'MiniMax Coding Plan',\n plan: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/api/openplatform/coding_plan/remains`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const remains = (body as Record<string, unknown>).model_remains\n if (!Array.isArray(remains)) return undefined\n const general = remains.find((entry) => typeof entry === 'object' && entry !== null\n && (entry as Record<string, unknown>).model_name === 'general')\n if (typeof general !== 'object' || general === null) return undefined\n const row = general as Record<string, unknown>\n const windows: PlanWindowView[] = []\n const intervalRemaining = toNum(row.current_interval_remaining_percent)\n if (intervalRemaining !== undefined) {\n windows.push({ key: '5h', percent: Math.max(0, Math.min(100, 100 - intervalRemaining)), resetsAt: toIso(row.end_time) })\n }\n if (row.current_weekly_status === 1) {\n const weeklyRemaining = toNum(row.current_weekly_remaining_percent)\n if (weeklyRemaining !== undefined) {\n windows.push({ key: 'week', percent: Math.max(0, Math.min(100, 100 - weeklyRemaining)), resetsAt: toIso(row.weekly_end_time) })\n }\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n }\n}\n\nconst OPENROUTER: ProviderAdapter = {\n ids: ['openrouter'],\n displayName: 'OpenRouter',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://openrouter.ai/api/v1/credits', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const credits = toNum((data as Record<string, unknown>).total_credits)\n const used = toNum((data as Record<string, unknown>).total_usage) ?? 0\n if (credits === undefined) return undefined\n return { currency: 'USD', totalBalance: money(credits - used) }\n },\n },\n}\n\nfunction siliconFlow(host: string, ids: readonly string[], currency: string): ProviderAdapter {\n return {\n ids,\n displayName: 'SiliconFlow',\n balance: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/user/info`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const total = str((data as Record<string, unknown>).totalBalance)\n if (total === undefined) return undefined\n return { currency, totalBalance: total }\n },\n },\n }\n}\n\nconst ZENMUX: ProviderAdapter = {\n ids: ['zenmux'],\n displayName: 'ZenMux',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://zenmux.ai/api/v1/management/payg/balance', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const credits = toNum((data as Record<string, unknown>).total_credits)\n if (credits === undefined) return undefined\n return { currency: 'USD', totalBalance: money(credits) }\n },\n },\n}\n\n/**\n * OpenAI Codex (ChatGPT subscription) quota over the OAuth access token the\n * pi-ai grant stores — the one plan adapter whose credential is an OAuth\n * token rather than an API key. A 401 here means the stored access token has\n * expired; it refreshes when the harness next runs a Codex request, so the\n * error line tells the user to use Codex once and refresh.\n */\nconst OPENAI_CODEX: ProviderAdapter = {\n ids: ['openai-codex'],\n displayName: 'Codex (ChatGPT 订阅)',\n plan: {\n build: ({ apiKey, accountId }) => ({\n url: 'https://chatgpt.com/backend-api/wham/usage',\n headers: {\n authorization: `Bearer ${apiKey}`,\n 'user-agent': 'codex-cli',\n accept: 'application/json',\n ...(accountId !== undefined ? { 'chatgpt-account-id': accountId } : {}),\n },\n }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const rateLimit = (body as Record<string, unknown>).rate_limit\n if (typeof rateLimit !== 'object' || rateLimit === null) return undefined\n const windows: PlanWindowView[] = []\n const rows = (rateLimit as Record<string, unknown>).primary_window !== undefined || (rateLimit as Record<string, unknown>).secondary_window !== undefined\n ? [(rateLimit as Record<string, unknown>).primary_window, (rateLimit as Record<string, unknown>).secondary_window]\n : []\n for (const entry of rows) {\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const percent = toNum(row.used_percent)\n if (percent === undefined) continue\n const seconds = toNum(row.limit_window_seconds)\n windows.push({\n key: codexWindowKey(seconds),\n percent: Math.max(0, Math.min(100, percent)),\n // reset_at is unix seconds; toIso scales sub-millisecond-epoch values.\n resetsAt: toIso(row.reset_at),\n })\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n}\n\n/** Map a Codex window length in seconds onto the shared window key vocabulary. */\nfunction codexWindowKey(seconds: number | undefined): string {\n if (seconds === undefined) return 'window'\n if (seconds === 18000) return '5h'\n if (seconds === 604800) return 'week'\n if (seconds === 2_592_000) return 'month'\n const hours = seconds / 3600\n return hours >= 24 ? `${Math.round(hours / 24)}_day` : `${Math.round(hours)}_hour`\n}\n\n/**\n * The adapter registry, in no particular order. Route keys come from the\n * pi-ai provider catalog plus the routes this deployment observed in user\n * configuration (`zenmux`).\n */\nexport const PROVIDER_ADAPTERS: readonly ProviderAdapter[] = [\n DEEPSEEK,\n moonshotBalance('api.moonshot.cn', 'CNY', ['moonshotai-cn']),\n moonshotBalance('api.moonshot.ai', 'USD', ['moonshotai']),\n KIMI_CODING,\n glmPlan('open.bigmodel.cn', ['zai-coding-cn']),\n glmPlan('api.z.ai', ['zai-coding']),\n OPENCODE_GO,\n minimaxPlan('api.minimaxi.com', ['minimax-cn']),\n minimaxPlan('api.minimax.io', ['minimax']),\n OPENAI_CODEX,\n OPENROUTER,\n siliconFlow('api.siliconflow.cn', ['siliconflow', 'siliconflow-cn'], 'CNY'),\n siliconFlow('api.siliconflow.com', ['siliconflow-intl'], 'USD'),\n ZENMUX,\n]\n\n/** Find the adapter serving a provider route key, if any. */\nexport function adapterFor(provider: string): ProviderAdapter | undefined {\n return PROVIDER_ADAPTERS.find((adapter) => adapter.ids.includes(provider))\n}\n\n/**\n * Whether a provider route belongs to the official DeepSeek family: the only\n * family with a spend price book and a settings-section-owned env credential\n * (llm-deepseek) rather than a pi-ai profile. Drives the env fallback in\n * credential resolution and the fold-time cost stamping.\n */\nexport function isDeepSeekProviderRoute(provider: string): boolean {\n return adapterFor(provider) === DEEPSEEK\n}\n\n/**\n * Best-effort human message from a provider error body, for the per-provider\n * error line. Never throws; truncated to one short sentence.\n */\nexport function providerErrorMessage(status: number, body: unknown): string {\n let message: string | undefined\n if (typeof body === 'object' && body !== null) {\n const root = body as Record<string, unknown>\n const nested = typeof root.error === 'object' && root.error !== null ? (root.error as Record<string, unknown>) : undefined\n message = str(root.message) ?? str(root.msg) ?? (nested !== undefined ? str(nested.message) : undefined)\n }\n const detail = message === undefined ? '' : `: ${message.slice(0, 120)}`\n return `HTTP ${status}${detail}`\n}\n","/**\n * DeepSeek official peak/off-peak pricing: the published V4 price book plus\n * the peak-window clock, folded into a per-call spend estimate the ledger\n * stamps at fold time (the provider bills each request in the period the\n * request ran in, so pricing at the fold is the honest estimate).\n *\n * Policy (api-docs.deepseek.com pricing page, effective 2026-08-17): peak\n * hours are Beijing time Monday-Friday 09:00-12:00 and 14:00-18:00; every\n * other hour (nights, weekends) is off-peak and billed at half the peak\n * price. All prices here are CNY per million tokens.\n * @module @linxin666/dsh-usage/core/pricing\n */\n\nimport type { UsageTokenTotals } from './types.ts'\n\n/** One peak window in minutes-of-day, Beijing time. */\ninterface PeakWindow {\n /** Inclusive start, minutes since midnight. */\n from: number\n /** Exclusive end, minutes since midnight. */\n to: number\n}\n\n/** The published peak windows: weekday 09:00-12:00 and 14:00-18:00. */\nconst PEAK_WINDOWS: readonly PeakWindow[] = [\n { from: 9 * 60, to: 12 * 60 },\n { from: 14 * 60, to: 18 * 60 },\n]\n\n/** Beijing is UTC+8 year-round (no DST), so a fixed shift is exact. */\nconst BEIJING_UTC_OFFSET_MS = 8 * 3_600_000\n\n/** One price row in CNY per million tokens, split by billing period. */\ninterface ModelPrice {\n /** Input served from the provider prompt cache. */\n cacheHit: { offPeak: number; peak: number }\n /** Uncached input (DeepSeek reports no separate cache-write class). */\n inputMiss: { offPeak: number; peak: number }\n /** Output; reasoning tokens bill as output. */\n output: { offPeak: number; peak: number }\n}\n\n/** deepseek-v4-flash (and the flash-class vision experiment). */\nconst FLASH_PRICE: ModelPrice = {\n cacheHit: { offPeak: 0.05, peak: 0.1 },\n inputMiss: { offPeak: 1.5, peak: 3.0 },\n output: { offPeak: 4.5, peak: 9.0 },\n}\n\n/** deepseek-v4-pro. */\nconst PRO_PRICE: ModelPrice = {\n cacheHit: { offPeak: 0.15, peak: 0.3 },\n inputMiss: { offPeak: 4.5, peak: 9.0 },\n output: { offPeak: 13.5, peak: 27.0 },\n}\n\nfunction withinWindow(minuteOfDay: number): PeakWindow | undefined {\n return PEAK_WINDOWS.find((window) => minuteOfDay >= window.from && minuteOfDay < window.to)\n}\n\n/**\n * The DeepSeek billing period at `ms`, plus when it next flips. The clock is\n * Beijing time regardless of the host timezone (UTC+8 has no DST, so a fixed\n * shift is exact). `boundaryMs` is the instant the current period ends — the\n * window's close while peaking, the next window's open otherwise.\n */\nexport function deepseekPeriodAt(ms: number): { peak: boolean; boundaryMs: number } {\n const shifted = new Date(ms + BEIJING_UTC_OFFSET_MS)\n const weekday = shifted.getUTCDay()\n const minuteOfDay = shifted.getUTCHours() * 60 + shifted.getUTCMinutes()\n const current = weekday >= 1 && weekday <= 5 ? withinWindow(minuteOfDay) : undefined\n if (current !== undefined) {\n return { peak: true, boundaryMs: ms + (current.to - minuteOfDay) * 60_000 - shifted.getUTCSeconds() * 1000 - shifted.getUTCMilliseconds() }\n }\n // Next window start: later today (weekday only), else the following days'\n // first morning window; the scan bound makes a malformed clock terminate.\n for (let dayOffset = 0; dayOffset < 8; dayOffset += 1) {\n const day = new Date(ms + BEIJING_UTC_OFFSET_MS + dayOffset * 86_400_000)\n if (day.getUTCDay() < 1 || day.getUTCDay() > 5) continue\n const realDayStart = Date.UTC(day.getUTCFullYear(), day.getUTCMonth(), day.getUTCDate()) - BEIJING_UTC_OFFSET_MS\n for (const window of PEAK_WINDOWS) {\n if (dayOffset === 0 && window.from <= minuteOfDay) continue\n return { peak: false, boundaryMs: realDayStart + window.from * 60_000 }\n }\n }\n // Unreachable (the scan covers a full week); a conservative off-peak answer.\n return { peak: false, boundaryMs: ms + 86_400_000 }\n}\n\n/** The price row for a model id; unknown ids take the flash-class row (documented estimate). */\nfunction priceFor(model: string): ModelPrice {\n return model.includes('v4-pro') ? PRO_PRICE : FLASH_PRICE\n}\n\n/**\n * Estimate one call's DeepSeek spend in CNY from its token totals, priced in\n * the billing period at `atMs`. Uncatalogued model ids take the flash-class\n * row; ids from other providers never reach this function (the service gates\n * by route family). Rounded to micro-CNY so the ledger stays readable.\n */\nexport function deepseekModelSpend(model: string, totals: Readonly<UsageTokenTotals>, atMs: number): number {\n const price = priceFor(model)\n const period = deepseekPeriodAt(atMs)\n const column = period.peak ? 'peak' : 'offPeak'\n const spend = (totals.cacheReadTokens * price.cacheHit[column]\n + (totals.inputTokens + totals.cacheWriteTokens) * price.inputMiss[column]\n + totals.outputTokens * price.output[column]) / 1_000_000\n return Math.round(spend * 1e6) / 1e6\n}\n","/**\n * The usage statistics settings section: two tabs (用量: today's usage,\n * balances, trend; 个人套餐: per-provider plan quota windows) plus a compact\n * settings row. Data comes from the host's loopback-fenced\n * /api/dsh-usage/overview document; polling runs only while the section is\n * mounted and the tab is visible.\n * @module @linxin666/dsh-usage/client/UsageSectionCard\n */\n\nimport { useEffect, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { UsageStoreInstance } from './usage-store.ts'\nimport { t } from './locales.ts'\nimport styles from './usage.module.css'\nimport { isDeepSeekProviderRoute } from '../core/adapters.ts'\nimport { deepseekPeriodAt } from '../core/pricing.ts'\nimport type { ProviderSnapshotView, UsageOverviewView, UsageProviderSummary, UsageTokenTotals } from '../core/types.ts'\n\n/** The settings fields this section edits (immediate-apply semantics). */\nexport interface UsageSettings {\n enabled?: boolean\n pollIntervalSec?: number\n bubbleMode?: string\n}\n\n/** The registration-side face the section's slot entry injects. */\nexport interface UsageSectionFace {\n /** The section-local store (overview snapshot + lifecycle). */\n store: UsageStoreInstance\n /** Fetch one overview now. */\n poll: () => void\n /** Force a host probe cycle now (resolves with the fresh overview). */\n refresh: () => void\n /** Whether a forced refresh is in flight (component-local state mirrors it). */\n settings: SettingsScope<UsageSettings>\n}\n\nexport interface UsageSectionProps extends UsageSectionFace {\n /** Close the settings panel (the shell owns the open state). */\n close: () => void\n}\n\n/** Poll cadence while the section is open. */\nconst SECTION_POLL_MS = 10_000\n\n/** Compact token count: 12345 -> 12.3k, 1234567 -> 1.23M. */\nexport function formatTokens(value: number): string {\n if (!Number.isFinite(value) || value <= 0) return '0'\n if (value < 1000) return String(value)\n if (value < 1_000_000) return trim(value / 1000) + 'k'\n if (value < 1_000_000_000) return trim(value / 1_000_000) + 'M'\n return trim(value / 1_000_000_000) + 'B'\n}\n\nfunction trim(value: number): string {\n return value >= 100 ? String(Math.round(value)) : value.toFixed(value >= 10 ? 1 : 2).replace(/\\.?0+$/, '')\n}\n\nfunction formatTime(ms: number): string {\n try {\n return new Date(ms).toLocaleTimeString()\n } catch {\n return ''\n }\n}\n\nfunction formatClock(ms: number): string {\n try {\n return new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n } catch {\n return ''\n }\n}\n\n/** Formatted CNY spend estimate (the only priced currency today). */\nfunction formatCost(cost: number): string {\n return '¥' + cost.toFixed(2)\n}\n\nfunction toneClass(percent: number): string {\n if (percent >= 90) return styles.barLow\n if (percent >= 70) return styles.barWarn\n return styles.barFill\n}\n\nfunction TotalsRow(props: { totals: UsageTokenTotals }): ReactNode {\n const { totals } = props\n return (\n <div className={styles.statRow}>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens + totals.outputTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.total')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.input')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.outputTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.output')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.cacheReadTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.cacheRead')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.cacheWriteTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.cacheWrite')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.calls)}</span>\n <span className={styles.statLabel}>{t('usage.calls', { n: totals.calls })}</span>\n </div>\n </div>\n )\n}\n\nfunction balanceLine(provider: ProviderSnapshotView): ReactNode {\n if (provider.balance !== undefined) {\n return <span className={styles.providerBalance}>{provider.balance.currency.toUpperCase() === 'CNY' ? '¥' : provider.balance.currency.toUpperCase() === 'USD' ? '$' : ''}{provider.balance.totalBalance}{provider.balance.currency.toUpperCase() !== 'CNY' && provider.balance.currency.toUpperCase() !== 'USD' ? ' ' + provider.balance.currency.toUpperCase() : ''}</span>\n }\n if (provider.credential === 'oauth') return <span className={styles.muted}>{t('usage.oauth')}</span>\n if (provider.credential === 'none') return <span className={styles.muted}>{t('usage.balance.noCredential')}</span>\n if (!provider.supported) return <span className={styles.muted}>{t('usage.balance.unsupported')}</span>\n return null\n}\n\nfunction ProviderRow(props: { provider: ProviderSnapshotView; current?: string }): ReactNode {\n const { provider, current } = props\n return (\n <div className={styles.providerRow} data-dsh-part=\"provider-row\">\n <span className={styles.providerName}>\n {provider.displayName}\n {current === provider.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.providerTokens}>{balanceLine(provider)}</span>\n </div>\n )\n}\n\n/** The section component; the slot merges the face into these props. */\nexport function UsageSectionCard(props: UsageSectionProps): ReactNode {\n const { store, poll, refresh, settings } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const settingsSnapshot = settings.getSnapshot()\n const settingsValue = settingsSnapshot.value ?? {}\n const [tab, setTab] = useState<'usage' | 'plans'>('usage')\n const [refreshing, setRefreshing] = useState(false)\n\n // Poll while mounted and visible; the overview is cheap (no probes — the\n // host's own cycle owns those) so 10 s keeps balances fresh-ish between\n // manual refreshes.\n useEffect(() => {\n poll()\n let timer: number | undefined\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') timer = window.setInterval(poll, SECTION_POLL_MS)\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n poll()\n start()\n } else if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n if (timer !== undefined) window.clearInterval(timer)\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, [poll])\n\n const snapshot = ui.snapshot\n\n const onRefresh = (): void => {\n setRefreshing(true)\n try {\n refresh()\n } finally {\n // The POST resolves through the next poll tick; unlock shortly either way.\n window.setTimeout(() => setRefreshing(false), 3000)\n }\n }\n\n if (ui.status === 'error') {\n return <div className={styles.section} data-dsh-plugin=\"usage\">{t('usage.error', { error: ui.error ?? '' })}</div>\n }\n if (snapshot === null) {\n return <div className={styles.section} data-dsh-plugin=\"usage\">{t('usage.loading')}</div>\n }\n\n const current = snapshot.current\n const currentProvider = snapshot.providers.find((provider) => provider.provider === current.provider)\n // Peak-period line: only when the official DeepSeek family is in play this\n // session (the current route, or spend recorded under one today).\n const deepseekPeriod = deepseekPeriodAt(Date.now())\n const deepseekVisible = (current.provider !== undefined && isDeepSeekProviderRoute(current.provider))\n || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider))\n // Plans tab: only routes with a real coding-plan/subscription adapter\n // (planSupported; an older host without the flag falls back to \"has a plan\n // fact\"). Balance-only providers (DeepSeek, ZenMux, ...) never appear here.\n const planProviders = snapshot.providers.filter((provider) => provider.planSupported === true || (provider.planSupported === undefined && provider.plan !== undefined))\n\n return (\n <div className={styles.section} data-dsh-plugin=\"usage\">\n <div className={styles.header} data-dsh-part=\"header\">\n <span className={styles.currentProvider}>\n {currentProvider !== undefined\n ? `${currentProvider.displayName}${current.model !== undefined && current.model !== '' ? ' · ' + current.model : ''}`\n : t('usage.noData')}\n </span>\n <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <span className={styles.muted}>{t('usage.updated', { time: formatTime(snapshot.updatedAt) })}</span>\n <button type=\"button\" className={styles.refreshBtn} onClick={onRefresh} disabled={refreshing}>\n {refreshing ? t('usage.refreshing') : t('usage.refresh')}\n </button>\n </span>\n </div>\n\n <div className={styles.tabs} role=\"tablist\" data-dsh-part=\"tabs\">\n <button type=\"button\" role=\"tab\" aria-selected={tab === 'usage'} className={tab === 'usage' ? `${styles.tab} ${styles.tabActive}` : styles.tab} onClick={() => setTab('usage')}>\n {t('usage.tab.usage')}\n </button>\n <button type=\"button\" role=\"tab\" aria-selected={tab === 'plans'} className={tab === 'plans' ? `${styles.tab} ${styles.tabActive}` : styles.tab} onClick={() => setTab('plans')}>\n {t('usage.tab.plans')}\n </button>\n </div>\n\n {tab === 'usage' && (\n <>\n <div className={styles.card} data-dsh-part=\"today-card\">\n <span className={styles.cardTitle}>{t('usage.today')}</span>\n {deepseekVisible && (\n <span className={styles.muted} data-dsh-part=\"peak-status\">\n {t(deepseekPeriod.peak ? 'usage.peak.on' : 'usage.peak.off', { time: formatClock(deepseekPeriod.boundaryMs) })}\n </span>\n )}\n {snapshot.usage.today.totals.calls === 0\n ? <span className={styles.muted}>{t('usage.noData')}</span>\n : <TotalsRow totals={snapshot.usage.today.totals} />}\n {snapshot.usage.today.totals.cost > 0 && (\n <div className={styles.providerRow} data-dsh-part=\"today-cost\">\n <span className={styles.providerName}>{t('usage.today.cost')}</span>\n <span className={styles.providerTokens}>{formatCost(snapshot.usage.today.totals.cost)}</span>\n </div>\n )}\n {snapshot.usage.today.providers.length > 0 && (\n <div data-dsh-part=\"provider-list\">\n {snapshot.usage.today.providers.map((row) => (\n <div key={row.provider} className={styles.providerRow}>\n <span className={styles.providerName}>\n {snapshot.providers.find((provider) => provider.provider === row.provider)?.displayName ?? row.provider}\n {current.provider === row.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.providerTokens}>{formatTokens(row.totals.inputTokens + row.totals.cacheReadTokens + row.totals.cacheWriteTokens + row.totals.outputTokens)} · {t('usage.calls', { n: row.totals.calls })}{row.totals.cost > 0 ? ` · ${formatCost(row.totals.cost)}` : ''}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n\n <div className={styles.card} data-dsh-part=\"balance-card\">\n <span className={styles.cardTitle}>{t('usage.balance')}</span>\n {(() => {\n const rows = snapshot.providers.filter((provider) => provider.balanceSupported === true || (provider.balanceSupported === undefined && provider.supported))\n if (rows.length === 0) return <span className={styles.muted}>{t('usage.balance.unsupported')}</span>\n return rows.map((provider) => (\n <ProviderRow key={provider.provider} provider={provider} current={current.provider} />\n ))\n })()}\n {snapshot.providers.some((provider) => provider.error !== undefined) && (\n <span className={styles.errorLine}>\n {snapshot.providers.filter((provider) => provider.error !== undefined).map((provider) => `${provider.displayName}: ${t('usage.provider.error', { error: provider.error ?? '' })}`).join(t('usage.errorListSeparator'))}\n </span>\n )}\n </div>\n\n <RangeCard range={snapshot.usage.range} providers={snapshot.providers} currentProvider={current.provider} />\n\n <SettingsRow settings={settings} snapshot={settingsSnapshot.status === 'ready' ? settingsSnapshot : undefined} value={settingsValue} />\n </>\n )}\n\n {tab === 'plans' && (\n planProviders.length === 0\n ? <div className={styles.card}><span className={styles.muted}>{t('usage.plan.noneConfigured')}</span></div>\n : planProviders.map((provider) => <PlanCard key={provider.provider} provider={provider} current={current.provider} />)\n )}\n </div>\n )\n}\n\n/** How many model sub-bars render under one provider bar. */\nconst CHART_MODEL_CAP = 3\n\n/**\n * The 近 30 天 card: horizontal bars per provider over the trend window,\n * each with its heaviest models as nested sub-bars. Window totals come from\n * the host's aggregated `usage.range` (an older host without it renders no\n * card instead of a wrong one).\n */\nfunction RangeCard(props: { range?: UsageOverviewView['usage']['range']; providers: ProviderSnapshotView[]; currentProvider?: string }): ReactNode {\n const { range, providers, currentProvider } = props\n if (range === undefined) return null\n const grandTotal = range.totals.inputTokens + range.totals.outputTokens + range.totals.cacheReadTokens + range.totals.cacheWriteTokens\n const maxProvider = Math.max(1, ...range.providers.map((row) => totalOf(row.totals)))\n const nameOf = (id: string): string => providers.find((provider) => provider.provider === id)?.displayName ?? id\n return (\n <div className={styles.card} data-dsh-part=\"trend-card\">\n <span className={styles.cardTitle}>{t('usage.trend')}</span>\n {range.providers.length === 0 || grandTotal === 0\n ? <span className={styles.muted}>{t('usage.noData')}</span>\n : <div className={styles.chart} data-dsh-part=\"usage-chart\">\n {range.providers.map((row) => (\n <ChartProviderRow key={row.provider} row={row} name={nameOf(row.provider)} max={maxProvider} current={currentProvider === row.provider} />\n ))}\n <span className={styles.trendAxis}>\n <span>{range.from.slice(5)}</span>\n <span>{range.to.slice(5)}</span>\n </span>\n </div>}\n </div>\n )\n}\n\nfunction ChartProviderRow(props: { row: UsageProviderSummary; name: string; max: number; current: boolean }): ReactNode {\n const { row, name, max, current } = props\n const total = totalOf(row.totals)\n const maxModel = Math.max(1, ...row.models.slice(0, CHART_MODEL_CAP).map((model) => totalOf(model.totals)))\n return (\n <div className={styles.chartProvider}>\n <span className={styles.chartHead}>\n <span className={styles.providerName}>\n {name}\n {current && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.chartTokens}>{formatTokens(total)} · {t('usage.calls', { n: row.totals.calls })}</span>\n </span>\n <span className={styles.chartBar}>\n <span className={styles.chartFill} style={{ width: `${Math.max(2, Math.round((total / max) * 100))}%` }} />\n </span>\n {row.models.slice(0, CHART_MODEL_CAP).map((model) => {\n const modelTotal = totalOf(model.totals)\n return (\n <span key={model.model} className={styles.chartModel} title={`${row.provider} · ${model.model}: ${formatTokens(modelTotal)}`}>\n <span className={styles.chartModelName}>{model.model}</span>\n <span className={styles.chartModelBar}>\n <span className={styles.chartModelFill} style={{ width: `${Math.max(3, Math.round((modelTotal / maxModel) * 100))}%` }} />\n </span>\n <span className={styles.chartTokens}>{formatTokens(modelTotal)}</span>\n </span>\n )\n })}\n </div>\n )\n}\n\nfunction totalOf(totals: UsageTokenTotals): number {\n return totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens + totals.outputTokens\n}\n\nfunction PlanCard(props: { provider: ProviderSnapshotView; current?: string }): ReactNode {\n const { provider, current } = props\n return (\n <div className={`${styles.card} ${styles.planCard}`} data-dsh-part=\"plan-card\">\n <div className={styles.planHead}>\n <span className={styles.planName}>\n {provider.displayName}\n {current === provider.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n {provider.plan?.planName !== undefined ? ` · ${provider.plan.planName}` : ''}\n </span>\n </div>\n {provider.error !== undefined && <span className={styles.errorLine}>{t('usage.provider.error', { error: provider.error })}</span>}\n {provider.credential === 'none' && provider.plan === undefined\n ? <span className={styles.muted}>{t('usage.balance.noCredential')}</span>\n : provider.plan === undefined || provider.plan.windows.length === 0\n ? <span className={styles.muted}>{t('usage.plan.noPlan')}</span>\n : provider.plan.windows.map((window) => (\n <div key={window.key} className={styles.windowRow} data-dsh-part=\"plan-window\">\n <span className={styles.windowLabel}>\n <span>{window.name ?? t(`usage.plan.windows.${window.key}`)}</span>\n <span>{window.percent !== undefined ? `${window.percent >= 10 ? Math.round(window.percent) : window.percent.toFixed(1)}%` : ''}</span>\n </span>\n {window.percent !== undefined && (\n <span className={styles.bar}>\n <span className={toneClass(window.percent)} style={{ width: `${Math.min(100, Math.max(0, window.percent))}%`, display: 'block' }} />\n </span>\n )}\n {window.resetsAt !== undefined && (\n <span className={styles.resetLine}>{t('usage.plan.reset', { date: new Date(window.resetsAt).toLocaleString() })}</span>\n )}\n </div>\n ))}\n </div>\n )\n}\n\nfunction SettingsRow(props: {\n settings: UsageSectionProps['settings']\n snapshot?: { writable: boolean }\n value: UsageSettings\n}): ReactNode {\n const { settings, snapshot, value } = props\n const disabled = snapshot === undefined || !snapshot.writable\n const bubbleMode = typeof value.bubbleMode === 'string' && ['always', 'change', 'off'].includes(value.bubbleMode) ? value.bubbleMode : 'always'\n return (\n <div className={styles.card} data-dsh-part=\"settings-row\">\n <span className={styles.cardTitle}>{t('usage.config.title')}</span>\n <div className={styles.settingsGrid}>\n <label className={styles.settingItem}>\n <input\n type=\"checkbox\"\n checked={value.enabled ?? true}\n disabled={disabled}\n onChange={(event) => { void settings.set('enabled', event.target.checked) }}\n />\n {t('usage.config.enabled')}\n </label>\n <label className={styles.settingItem}>\n {t('usage.config.pollIntervalSec')}\n <input\n type=\"number\"\n min={30}\n max={3600}\n value={typeof value.pollIntervalSec === 'number' ? value.pollIntervalSec : 60}\n disabled={disabled}\n onChange={(event) => {\n const parsed = Number(event.target.value)\n if (Number.isFinite(parsed) && parsed >= 30 && parsed <= 3600) void settings.set('pollIntervalSec', Math.round(parsed))\n }}\n />\n </label>\n <label className={styles.settingItem}>\n {t('usage.config.bubbleMode')}\n <select\n value={bubbleMode}\n disabled={disabled}\n onChange={(event) => { void settings.set('bubbleMode', event.target.value) }}\n >\n <option value=\"always\">{t('usage.config.bubbleMode.always')}</option>\n <option value=\"change\">{t('usage.config.bubbleMode.change')}</option>\n <option value=\"off\">{t('usage.config.bubbleMode.off')}</option>\n </select>\n </label>\n </div>\n </div>\n )\n}\n","/**\n * dsh-usage browser half — seats the first-level 使用统计 settings section\n * (below the Workshop entry) and polls the host overview only while the\n * section is open. All provider probing and credential handling happens in\n * the host half; this bundle only renders the overview document.\n * @module @linxin666/dsh-usage/client\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: pulls the ctx.slots merge (the renderer owns the slot registry since 0.1.2).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport { createElement } from 'react'\nimport { createUsageStore, type UsageStoreInstance } from './usage-store.ts'\nimport { UsageSectionCard, type UsageSectionFace, type UsageSettings } from './UsageSectionCard.tsx'\nimport { NS, en, zh } from './locales.ts'\nimport type { UsageOverviewView } from '../core/types.ts'\n\n/** The host usage API as the browser sees it (same-origin JSON endpoints). */\ninterface UsageHttpApi {\n overview(): Promise<UsageOverviewView>\n refresh(): Promise<UsageOverviewView>\n}\n\n/** Hard ceiling for one usage API call; a stalled host must not pile up requests. */\nconst USAGE_FETCH_TIMEOUT_MS = 20_000\n\nasync function usageFetch<T>(path: string, method: 'GET' | 'POST'): Promise<T> {\n const response = await fetch(path, {\n ...(method === 'POST' ? { method: 'POST' } : {}),\n signal: AbortSignal.timeout(USAGE_FETCH_TIMEOUT_MS),\n })\n if (!response.ok) throw new Error('usage ' + path + ' failed: ' + response.status)\n return (await response.json()) as T\n}\n\nconst usageApi: UsageHttpApi = {\n overview: () => usageFetch('/api/dsh-usage/overview', 'GET'),\n refresh: () => usageFetch('/api/dsh-usage/refresh', 'POST'),\n}\n\n/** Settings namespace the section edits (the host plugin registers it). */\nconst USAGE_SETTINGS_NS = 'dsh-usage'\n\n/** First-level nav position: directly below the Workshop section (order 150). */\nconst SECTION_ORDER = 151\n\n/** Required services. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote']\n\nexport type { UsageSectionProps, UsageSectionFace } from './UsageSectionCard.tsx'\nexport type { UsageUiState } from './usage-store.ts'\nexport type { UsageSettings }\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries and seat the settings section.\n * The overview poll loop and the store live with the section component's\n * mount cycle, so no background traffic exists while the page is closed.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, { zh, en })\n } catch {\n return () => {}\n }\n }, 'dsh-usage: dictionaries')\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<UsageSettings>({ namespace: USAGE_SETTINGS_NS })\n\n // One store instance per apply body; the section mounts and unmounts with\n // the settings page, and the store survives between visits so the last\n // overview renders instantly on reopen.\n const store: UsageStoreInstance = createUsageStore().create()\n\n let pollSeq = 0\n const poll = (): void => {\n const seq = pollSeq + 1\n pollSeq = seq\n usageApi.overview().then((snapshot) => {\n if (seq !== pollSeq) return\n store.actions.setSnapshot(snapshot)\n }, () => {\n if (seq !== pollSeq) return\n store.actions.setState('error', 'usage.overview transport error')\n })\n }\n // The refresh response is authoritative: it reflects the completed probe\n // cycle, so it applies even when a faster section poll already bumped the\n // sequence past it (the poll's pre-cycle snapshot is the stale one).\n const refresh = (): void => {\n const seq = pollSeq + 1\n pollSeq = seq\n usageApi.refresh().then((snapshot) => {\n pollSeq = seq\n store.actions.setSnapshot(snapshot)\n }, () => {\n if (seq !== pollSeq) return\n store.actions.setState('error', 'usage.refresh transport error')\n })\n }\n\n const face = (): UsageSectionFace => ({ store, poll, refresh, settings: settingsScope })\n\n ctx.slots.inject('settings.section', () => {\n try {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'dsh-usage',\n order: SECTION_ORDER,\n label: () => ctx.locale.bind(NS)('usage.title'),\n locale: NS,\n inject: face,\n }, UsageSectionCard)\n return () => {\n unregister()\n }\n } catch {\n return () => {}\n }\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["styles"],"sources":["../../../packages/dsh-usage/src/client/usage-store.ts","../../../packages/dsh-usage/src/client/locales.ts","../../../packages/dsh-usage/src/core/adapters.ts","../../../packages/dsh-usage/src/core/pricing.ts","../../../packages/dsh-usage/src/client/UsageSectionCard.tsx","../../../packages/dsh-usage/src/client/index.ts"],"sourcesContent":["/**\n * Browser-side usage store: the overview snapshot polled from the host plus\n * the fetch lifecycle. Section-local: the store lives while the settings\n * section is mounted, so polling only runs while the page is open.\n * @module @linxin666/dsh-usage/client/usage-store\n */\n\nimport { defineStore } from '@deepseek-ai/dsh-client-store'\nimport type { EngineStoreHandle, EngineStoreInstance } from '@deepseek-ai/dsh-client-store'\nimport type { UsageOverviewView } from '../core/types.ts'\n\n/** Section UI state as consumers see it. */\nexport interface UsageUiState {\n /** Latest host overview; null before the first successful fetch. */\n snapshot: UsageOverviewView | null\n /** Fetch lifecycle. */\n status: 'loading' | 'ready' | 'error'\n /** Transport error message, when any. */\n error: string | null\n}\n\n/** Store write set. */\nexport type UsageUiActions = {\n /** Replace the overview (poll result). */\n setSnapshot: (draft: UsageUiState, snapshot: UsageOverviewView) => void\n /** Mark the fetch lifecycle. */\n setState: (draft: UsageUiState, status: UsageUiState['status'], error: string | null) => void\n}\n\n/** Create the usage store handle (apply world only; never module-level). */\nexport function createUsageStore(): EngineStoreHandle<UsageUiState, UsageUiActions> {\n return defineStore({\n init: (): UsageUiState => ({\n snapshot: null,\n status: 'loading',\n error: null,\n }),\n actions: {\n setSnapshot: (draft, snapshot) => {\n draft.snapshot = snapshot\n draft.status = 'ready'\n draft.error = null\n },\n setState: (draft, status, error) => {\n draft.status = status\n draft.error = error\n },\n },\n })\n}\n\nexport type UsageStoreInstance = EngineStoreInstance<UsageUiState, UsageUiActions>\n","/**\n * dsh-usage locale dictionaries (zh/en). The zh dictionary is the key source;\n * `en` mirrors its full key set (packages/AGENTS.md bilingual discipline).\n * @module @linxin666/dsh-usage/client/locales\n */\n\n/** Dictionary namespace this package registers. */\nexport const NS = 'dsh-web-ui-usage'\n\n/** Chinese copy. */\nexport const zh = {\n 'usage.title': '使用统计',\n 'usage.tab.usage': '用量',\n 'usage.tab.plans': '个人套餐',\n 'usage.refresh': '刷新',\n 'usage.refreshing': '刷新中…',\n 'usage.updated': '更新于 {time}',\n 'usage.loading': '正在加载用量数据…',\n 'usage.error': '加载失败:{error}',\n 'usage.current': '当前',\n 'usage.today': '今日用量',\n 'usage.today.cost': '今日消费(估算)',\n 'usage.peak.on': 'DeepSeek 高峰时段:计价 ×2,{time} 结束',\n 'usage.peak.off': 'DeepSeek 空闲时段:计价为高峰一半,{time} 进入高峰',\n 'usage.calls': '{n} 次调用',\n 'usage.tokens.total': '总 tokens',\n 'usage.tokens.input': '输入',\n 'usage.tokens.output': '输出',\n 'usage.tokens.cacheRead': '缓存读',\n 'usage.tokens.cacheWrite': '缓存写',\n 'usage.trend': '近 30 天',\n 'usage.noData': '暂无用量数据(统计自插件启用起)',\n 'usage.balance': '余额',\n 'usage.balance.unsupported': '暂不支持余额查询',\n 'usage.balance.noCredential': '未配置凭据',\n 'usage.balance.noneConfigured': '没有已配置的提供方',\n 'usage.oauth': 'OAuth 凭据,不做余额查询',\n 'usage.plan.reset': '{date} 重置',\n 'usage.plan.noPlan': '未检测到套餐数据',\n 'usage.plan.noneConfigured': '没有已配置的套餐类 provider(如 Kimi、GLM、OpenCode Go、MiniMax、Codex 订阅)',\n 'usage.plan.windows.5h': '5 小时',\n 'usage.plan.windows.week': '每周',\n 'usage.plan.windows.month': '每月',\n 'usage.provider.error': '查询失败:{error}',\n 'usage.errorListSeparator': ';',\n 'usage.config.title': '设置',\n 'usage.config.enabled': '启用插件',\n 'usage.config.pollIntervalSec': '轮询间隔(秒)',\n 'usage.config.bubbleMode': '宠物气泡',\n 'usage.config.bubbleMode.always': '常驻显示',\n 'usage.config.bubbleMode.change': '仅变化时',\n 'usage.config.bubbleMode.off': '关闭',\n}\n\n/** English mirror; every zh key present. */\nexport const en: Record<UsageKey, string> = {\n 'usage.title': 'Usage Statistics',\n 'usage.tab.usage': 'Usage',\n 'usage.tab.plans': 'Plans',\n 'usage.refresh': 'Refresh',\n 'usage.refreshing': 'Refreshing…',\n 'usage.updated': 'Updated {time}',\n 'usage.loading': 'Loading usage data…',\n 'usage.error': 'Failed to load: {error}',\n 'usage.current': 'Current',\n 'usage.today': 'Today',\n 'usage.today.cost': 'Today spend (estimated)',\n 'usage.peak.on': 'DeepSeek peak hours: 2x pricing, ends {time}',\n 'usage.peak.off': 'DeepSeek off-peak: half of peak pricing, peak returns {time}',\n 'usage.calls': '{n} calls',\n 'usage.tokens.total': 'Total tokens',\n 'usage.tokens.input': 'Input',\n 'usage.tokens.output': 'Output',\n 'usage.tokens.cacheRead': 'Cache read',\n 'usage.tokens.cacheWrite': 'Cache write',\n 'usage.trend': 'Last 30 days',\n 'usage.noData': 'No usage data yet (counting starts when the plugin is enabled)',\n 'usage.balance': 'Balance',\n 'usage.balance.unsupported': 'Balance query not supported',\n 'usage.balance.noCredential': 'No credential configured',\n 'usage.balance.noneConfigured': 'No providers configured',\n 'usage.oauth': 'OAuth credential, no balance query',\n 'usage.plan.reset': 'resets {date}',\n 'usage.plan.noPlan': 'No plan data detected',\n 'usage.plan.noneConfigured': 'No plan-capable provider configured (such as Kimi, GLM, OpenCode Go, MiniMax, Codex subscription)',\n 'usage.plan.windows.5h': '5 hours',\n 'usage.plan.windows.week': 'Weekly',\n 'usage.plan.windows.month': 'Monthly',\n 'usage.provider.error': 'Query failed: {error}',\n 'usage.errorListSeparator': '; ',\n 'usage.config.title': 'Settings',\n 'usage.config.enabled': 'Enable plugin',\n 'usage.config.pollIntervalSec': 'Poll interval (seconds)',\n 'usage.config.bubbleMode': 'Pet bubble',\n 'usage.config.bubbleMode.always': 'Always visible',\n 'usage.config.bubbleMode.change': 'On change',\n 'usage.config.bubbleMode.off': 'Off',\n}\n\nexport type UsageKey = keyof typeof zh\n\n/**\n * Active dictionary, picked by the document language at call time. The\n * section resolves its copy the same tiny way the pet's DOM-injected surface\n * does (the settings section has no framework locale seat of its own).\n */\nexport function dictionary(): Record<UsageKey, string> {\n const lang = typeof document !== 'undefined' ? document.documentElement.lang : 'zh'\n return lang.toLowerCase().startsWith('en') ? en : zh\n}\n\n/** Translate a key with optional `{name}` template params; missing keys degrade to the key. */\nexport function t(key: string, params?: Record<string, unknown>): string {\n let text: string = (dictionary() as Record<string, string>)[key] ?? key\n if (params !== undefined) {\n for (const [name, value] of Object.entries(params)) {\n text = text.replaceAll(`{${name}}`, String(value))\n }\n }\n return text\n}\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** dsh-usage UI copy. */\n 'dsh-web-ui-usage': UsageKey\n }\n}\n","/**\n * Provider adapters: one entry per provider family this plugin can query for\n * a pay-as-you-go balance or a coding-plan quota. Adapters are pure — they\n * build an HTTP probe from a resolved credential and parse the response — so\n * the service stays transport-only and every endpoint detail lives here.\n *\n * All probes run host-side (Node fetch); API keys never reach the browser.\n * @module @linxin666/dsh-usage/core/adapters\n */\n\nimport type { PlanWindowView } from './types.ts'\n\n/** One HTTP probe the service executes verbatim. */\nexport interface ProbeSpec {\n url: string\n headers: Record<string, string>\n}\n\n/** Inputs an adapter may use to build a probe. */\nexport interface AdapterProbeContext {\n /** Resolved credential value: a raw API key, or an OAuth access token for the oauth-aware adapters. */\n apiKey: string\n /** Provider account id riding a dedicated header, when the credential carries one (Codex). */\n accountId?: string\n}\n\n/** A parsed balance fact. */\nexport interface BalanceParse {\n currency: string\n totalBalance: string\n}\n\n/** A parsed plan-quota fact. */\nexport interface PlanParse {\n planName?: string\n windows: PlanWindowView[]\n}\n\n/** One provider family's probing capability. */\nexport interface ProviderAdapter {\n /** Provider route keys this adapter serves. */\n ids: readonly string[]\n /** Fallback display name when the LLM runtime has none. */\n displayName: string\n balance?: {\n build(context: AdapterProbeContext): ProbeSpec\n parse(status: number, body: unknown): BalanceParse | undefined\n }\n plan?: {\n build(context: AdapterProbeContext): ProbeSpec\n parse(status: number, body: unknown): PlanParse | undefined\n }\n}\n\n/** Parse a string/number into a finite number, else undefined. */\nfunction toNum(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\n/** Read a string field that must be a non-empty string. */\nfunction str(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined\n}\n\n/** Format a number to a fixed 2-decimal display string. */\nfunction money(value: number): string {\n return value.toFixed(2)\n}\n\n/** Millisecond epoch or ISO string → normalized ISO 8601, else undefined. */\nfunction toIso(value: unknown): string | undefined {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n const ms = value < 1e12 ? value * 1000 : value\n const date = new Date(ms)\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n }\n const text = str(value)\n if (text === undefined) return undefined\n // Epoch instants also arrive as strings (\"1756428000\"); route them through\n // the numeric branch instead of letting Date() reject them.\n if (/^\\d+$/.test(text)) return toIso(Number(text))\n const date = new Date(text)\n // The contract is \"normalized ISO 8601, else undefined\": returning the raw\n // text would render as \"Invalid Date\" downstream.\n return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n\n/** Used-percent helper guarding zero/absent limits. */\nfunction usedPercent(used: unknown, limit: unknown): number | undefined {\n const usedNum = toNum(used)\n const limitNum = toNum(limit)\n if (usedNum === undefined || limitNum === undefined || limitNum <= 0) return undefined\n return Math.max(0, Math.min(100, (usedNum / limitNum) * 100))\n}\n\nfunction bearer(apiKey: string): Record<string, string> {\n return { authorization: `Bearer ${apiKey}` }\n}\n\n/**\n * The official pay-as-you-go balance. `deepseek` is the configurable-catalog\n * route key; `deepseek-official` is the live provider route the llm-deepseek\n * adapter registers (sessions and agent-default-model carry it), so both ids\n * must resolve here or the current provider would never be probed.\n */\nconst DEEPSEEK: ProviderAdapter = {\n ids: ['deepseek', 'deepseek-official'],\n displayName: 'DeepSeek',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://api.deepseek.com/user/balance', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const infos = (body as Record<string, unknown>).balance_infos\n if (!Array.isArray(infos) || infos.length === 0) return undefined\n const first = infos[0]\n if (typeof first !== 'object' || first === null) return undefined\n const currency = str((first as Record<string, unknown>).currency)\n const total = str((first as Record<string, unknown>).total_balance)\n if (currency === undefined || total === undefined) return undefined\n return { currency, totalBalance: total }\n },\n },\n}\n\n/** Moonshot pay-as-you-go balance; CN bills in CNY, international in USD. */\nfunction moonshotBalance(host: string, currency: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'Moonshot AI',\n balance: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/users/me/balance`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const available = toNum((data as Record<string, unknown>).available_balance)\n if (available === undefined) return undefined\n return { currency, totalBalance: money(available) }\n },\n },\n }\n}\n\n/** Kimi For Coding quota: top-level `usage` is the weekly summary, `limits[]` the per-window rows. */\nconst KIMI_CODING: ProviderAdapter = {\n ids: ['kimi-coding'],\n displayName: 'Kimi For Coding',\n plan: {\n build: ({ apiKey }) => ({ url: 'https://api.kimi.com/coding/v1/usages', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const root = body as Record<string, unknown>\n const windows: PlanWindowView[] = []\n const limits = root.limits\n if (Array.isArray(limits)) {\n for (const entry of limits) {\n if (typeof entry !== 'object' || entry === null) continue\n const detail = (entry as Record<string, unknown>).detail\n const window = (entry as Record<string, unknown>).window\n if (typeof detail !== 'object' || detail === null) continue\n const row = detail as Record<string, unknown>\n const duration = typeof window === 'object' && window !== null ? toNum((window as Record<string, unknown>).duration) : undefined\n const unit = typeof window === 'object' && window !== null ? str((window as Record<string, unknown>).timeUnit) : undefined\n const key = duration === 300 && unit === 'TIME_UNIT_MINUTE' ? '5h' : duration !== undefined ? `w-${duration}` : 'window'\n const percent = usedPercent(row.used, row.limit)\n windows.push({ key, name: str(row.name), percent, resetsAt: toIso(row.resetTime) })\n }\n }\n const usage = root.usage\n if (typeof usage === 'object' && usage !== null) {\n const weekly = usage as Record<string, unknown>\n windows.push({\n key: 'week',\n name: 'Weekly',\n percent: usedPercent(weekly.used, weekly.limit),\n resetsAt: toIso(weekly.resetTime),\n })\n }\n if (windows.length === 0) return undefined\n const user = root.user\n const membership = typeof user === 'object' && user !== null ? (user as Record<string, unknown>).membership : undefined\n const planName = typeof membership === 'object' && membership !== null ? str((membership as Record<string, unknown>).level) : undefined\n return { planName, windows }\n },\n },\n}\n\n/** GLM Coding Plan quota; auth is the RAW key without a Bearer prefix. */\nfunction glmPlan(host: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'GLM Coding Plan',\n plan: {\n build: ({ apiKey }) => ({\n url: `https://${host}/api/monitor/usage/quota/limit`,\n headers: { authorization: apiKey, 'accept-language': 'en-US,en' },\n }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const root = body as Record<string, unknown>\n if (root.success !== true) return undefined\n const data = root.data\n if (typeof data !== 'object' || data === null) return undefined\n const limits = (data as Record<string, unknown>).limits\n if (!Array.isArray(limits)) return undefined\n const windows: PlanWindowView[] = []\n for (const entry of limits) {\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const unit = toNum(row.unit)\n const percent = toNum(row.percentage)\n windows.push({\n key: unit === 3 ? '5h' : unit === 6 ? 'week' : unit !== undefined ? `unit-${unit}` : 'window',\n percent: percent === undefined ? undefined : Math.max(0, Math.min(100, percent)),\n resetsAt: toIso(row.nextResetTime),\n })\n }\n if (windows.length === 0) return undefined\n return { planName: str((data as Record<string, unknown>).level), windows }\n },\n },\n }\n}\n\n/** OpenCode Go quota: percent-only rolling/weekly/monthly windows. */\nconst OPENCODE_GO: ProviderAdapter = {\n ids: ['opencode-go'],\n displayName: 'OpenCode Go',\n plan: {\n build: ({ apiKey }) => ({ url: 'https://opencode.ai/zen/go/v1/usage', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const usage = (body as Record<string, unknown>).usage\n if (typeof usage !== 'object' || usage === null) return undefined\n const windows: PlanWindowView[] = []\n const keys: Array<[string, string]> = [['rolling', '5h'], ['weekly', 'week'], ['monthly', 'month']]\n for (const [field, key] of keys) {\n const entry = (usage as Record<string, unknown>)[field]\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const percent = toNum(row.percent)\n windows.push({\n key,\n percent: percent === undefined ? undefined : Math.max(0, Math.min(100, percent)),\n // percent 0 resetsAt is a placeholder (now + window); drop it.\n resetsAt: percent === 0 ? undefined : toIso(row.resetsAt),\n })\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n}\n\n/** MiniMax coding-plan remains: remaining-percent semantics, `general` model entry. */\nfunction minimaxPlan(host: string, ids: readonly string[]): ProviderAdapter {\n return {\n ids,\n displayName: 'MiniMax Coding Plan',\n plan: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/api/openplatform/coding_plan/remains`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const remains = (body as Record<string, unknown>).model_remains\n if (!Array.isArray(remains)) return undefined\n const general = remains.find((entry) => typeof entry === 'object' && entry !== null\n && (entry as Record<string, unknown>).model_name === 'general')\n if (typeof general !== 'object' || general === null) return undefined\n const row = general as Record<string, unknown>\n const windows: PlanWindowView[] = []\n const intervalRemaining = toNum(row.current_interval_remaining_percent)\n if (intervalRemaining !== undefined) {\n windows.push({ key: '5h', percent: Math.max(0, Math.min(100, 100 - intervalRemaining)), resetsAt: toIso(row.end_time) })\n }\n if (row.current_weekly_status === 1) {\n const weeklyRemaining = toNum(row.current_weekly_remaining_percent)\n if (weeklyRemaining !== undefined) {\n windows.push({ key: 'week', percent: Math.max(0, Math.min(100, 100 - weeklyRemaining)), resetsAt: toIso(row.weekly_end_time) })\n }\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n }\n}\n\nconst OPENROUTER: ProviderAdapter = {\n ids: ['openrouter'],\n displayName: 'OpenRouter',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://openrouter.ai/api/v1/credits', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const credits = toNum((data as Record<string, unknown>).total_credits)\n const used = toNum((data as Record<string, unknown>).total_usage) ?? 0\n if (credits === undefined) return undefined\n return { currency: 'USD', totalBalance: money(credits - used) }\n },\n },\n}\n\nfunction siliconFlow(host: string, ids: readonly string[], currency: string): ProviderAdapter {\n return {\n ids,\n displayName: 'SiliconFlow',\n balance: {\n build: ({ apiKey }) => ({ url: `https://${host}/v1/user/info`, headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const total = str((data as Record<string, unknown>).totalBalance)\n if (total === undefined) return undefined\n return { currency, totalBalance: total }\n },\n },\n }\n}\n\nconst ZENMUX: ProviderAdapter = {\n ids: ['zenmux'],\n displayName: 'ZenMux',\n balance: {\n build: ({ apiKey }) => ({ url: 'https://zenmux.ai/api/v1/management/payg/balance', headers: bearer(apiKey) }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const data = (body as Record<string, unknown>).data\n if (typeof data !== 'object' || data === null) return undefined\n const credits = toNum((data as Record<string, unknown>).total_credits)\n if (credits === undefined) return undefined\n return { currency: 'USD', totalBalance: money(credits) }\n },\n },\n}\n\n/**\n * OpenAI Codex (ChatGPT subscription) quota over the OAuth access token the\n * pi-ai grant stores — the one plan adapter whose credential is an OAuth\n * token rather than an API key. A 401 here means the stored access token has\n * expired; it refreshes when the harness next runs a Codex request, so the\n * error line tells the user to use Codex once and refresh.\n */\nconst OPENAI_CODEX: ProviderAdapter = {\n ids: ['openai-codex'],\n displayName: 'Codex (ChatGPT 订阅)',\n plan: {\n build: ({ apiKey, accountId }) => ({\n url: 'https://chatgpt.com/backend-api/wham/usage',\n headers: {\n authorization: `Bearer ${apiKey}`,\n 'user-agent': 'codex-cli',\n accept: 'application/json',\n ...(accountId !== undefined ? { 'chatgpt-account-id': accountId } : {}),\n },\n }),\n parse: (status, body) => {\n if (status !== 200 || typeof body !== 'object' || body === null) return undefined\n const rateLimit = (body as Record<string, unknown>).rate_limit\n if (typeof rateLimit !== 'object' || rateLimit === null) return undefined\n const windows: PlanWindowView[] = []\n const rows = (rateLimit as Record<string, unknown>).primary_window !== undefined || (rateLimit as Record<string, unknown>).secondary_window !== undefined\n ? [(rateLimit as Record<string, unknown>).primary_window, (rateLimit as Record<string, unknown>).secondary_window]\n : []\n for (const entry of rows) {\n if (typeof entry !== 'object' || entry === null) continue\n const row = entry as Record<string, unknown>\n const percent = toNum(row.used_percent)\n if (percent === undefined) continue\n const seconds = toNum(row.limit_window_seconds)\n windows.push({\n key: codexWindowKey(seconds),\n percent: Math.max(0, Math.min(100, percent)),\n // reset_at is unix seconds; toIso scales sub-millisecond-epoch values.\n resetsAt: toIso(row.reset_at),\n })\n }\n if (windows.length === 0) return undefined\n return { windows }\n },\n },\n}\n\n/** Map a Codex window length in seconds onto the shared window key vocabulary. */\nfunction codexWindowKey(seconds: number | undefined): string {\n if (seconds === undefined) return 'window'\n if (seconds === 18000) return '5h'\n if (seconds === 604800) return 'week'\n if (seconds === 2_592_000) return 'month'\n const hours = seconds / 3600\n return hours >= 24 ? `${Math.round(hours / 24)}_day` : `${Math.round(hours)}_hour`\n}\n\n/**\n * The adapter registry, in no particular order. Route keys come from the\n * pi-ai provider catalog plus the routes this deployment observed in user\n * configuration (`zenmux`).\n */\nexport const PROVIDER_ADAPTERS: readonly ProviderAdapter[] = [\n DEEPSEEK,\n moonshotBalance('api.moonshot.cn', 'CNY', ['moonshotai-cn']),\n moonshotBalance('api.moonshot.ai', 'USD', ['moonshotai']),\n KIMI_CODING,\n glmPlan('open.bigmodel.cn', ['zai-coding-cn']),\n glmPlan('api.z.ai', ['zai-coding']),\n OPENCODE_GO,\n minimaxPlan('api.minimaxi.com', ['minimax-cn']),\n minimaxPlan('api.minimax.io', ['minimax']),\n OPENAI_CODEX,\n OPENROUTER,\n siliconFlow('api.siliconflow.cn', ['siliconflow', 'siliconflow-cn'], 'CNY'),\n siliconFlow('api.siliconflow.com', ['siliconflow-intl'], 'USD'),\n ZENMUX,\n]\n\n/** Find the adapter serving a provider route key, if any. */\nexport function adapterFor(provider: string): ProviderAdapter | undefined {\n return PROVIDER_ADAPTERS.find((adapter) => adapter.ids.includes(provider))\n}\n\n/**\n * Whether a provider route belongs to the official DeepSeek family: the only\n * family with a spend price book and a settings-section-owned env credential\n * (llm-deepseek) rather than a pi-ai profile. Drives the env fallback in\n * credential resolution and the fold-time cost stamping.\n */\nexport function isDeepSeekProviderRoute(provider: string): boolean {\n return adapterFor(provider) === DEEPSEEK\n}\n\n/**\n * Best-effort human message from a provider error body, for the per-provider\n * error line. Never throws; truncated to one short sentence.\n */\nexport function providerErrorMessage(status: number, body: unknown): string {\n let message: string | undefined\n if (typeof body === 'object' && body !== null) {\n const root = body as Record<string, unknown>\n const nested = typeof root.error === 'object' && root.error !== null ? (root.error as Record<string, unknown>) : undefined\n message = str(root.message) ?? str(root.msg) ?? (nested !== undefined ? str(nested.message) : undefined)\n }\n const detail = message === undefined ? '' : `: ${message.slice(0, 120)}`\n return `HTTP ${status}${detail}`\n}\n","/**\n * DeepSeek official peak/off-peak pricing: the published V4 price book plus\n * the peak-window clock, folded into a per-call spend estimate the ledger\n * stamps at fold time (the provider bills each request in the period the\n * request ran in, so pricing at the fold is the honest estimate).\n *\n * Policy (api-docs.deepseek.com pricing page, effective 2026-08-17): peak\n * hours are Beijing time Monday-Friday 09:00-12:00 and 14:00-18:00; every\n * other hour (nights, weekends) is off-peak and billed at half the peak\n * price. All prices here are CNY per million tokens.\n * @module @linxin666/dsh-usage/core/pricing\n */\n\nimport type { UsageTokenTotals } from './types.ts'\n\n/** One peak window in minutes-of-day, Beijing time. */\ninterface PeakWindow {\n /** Inclusive start, minutes since midnight. */\n from: number\n /** Exclusive end, minutes since midnight. */\n to: number\n}\n\n/** The published peak windows: weekday 09:00-12:00 and 14:00-18:00. */\nconst PEAK_WINDOWS: readonly PeakWindow[] = [\n { from: 9 * 60, to: 12 * 60 },\n { from: 14 * 60, to: 18 * 60 },\n]\n\n/** Beijing is UTC+8 year-round (no DST), so a fixed shift is exact. */\nconst BEIJING_UTC_OFFSET_MS = 8 * 3_600_000\n\n/** One price row in CNY per million tokens, split by billing period. */\ninterface ModelPrice {\n /** Input served from the provider prompt cache. */\n cacheHit: { offPeak: number; peak: number }\n /** Uncached input (DeepSeek reports no separate cache-write class). */\n inputMiss: { offPeak: number; peak: number }\n /** Output; reasoning tokens bill as output. */\n output: { offPeak: number; peak: number }\n}\n\n/** deepseek-v4-flash (and the flash-class vision experiment). */\nconst FLASH_PRICE: ModelPrice = {\n cacheHit: { offPeak: 0.05, peak: 0.1 },\n inputMiss: { offPeak: 1.5, peak: 3.0 },\n output: { offPeak: 4.5, peak: 9.0 },\n}\n\n/** deepseek-v4-pro. */\nconst PRO_PRICE: ModelPrice = {\n cacheHit: { offPeak: 0.15, peak: 0.3 },\n inputMiss: { offPeak: 4.5, peak: 9.0 },\n output: { offPeak: 13.5, peak: 27.0 },\n}\n\nfunction withinWindow(minuteOfDay: number): PeakWindow | undefined {\n return PEAK_WINDOWS.find((window) => minuteOfDay >= window.from && minuteOfDay < window.to)\n}\n\n/**\n * The DeepSeek billing period at `ms`, plus when it next flips. The clock is\n * Beijing time regardless of the host timezone (UTC+8 has no DST, so a fixed\n * shift is exact). `boundaryMs` is the instant the current period ends — the\n * window's close while peaking, the next window's open otherwise.\n */\nexport function deepseekPeriodAt(ms: number): { peak: boolean; boundaryMs: number } {\n const shifted = new Date(ms + BEIJING_UTC_OFFSET_MS)\n const weekday = shifted.getUTCDay()\n const minuteOfDay = shifted.getUTCHours() * 60 + shifted.getUTCMinutes()\n const current = weekday >= 1 && weekday <= 5 ? withinWindow(minuteOfDay) : undefined\n if (current !== undefined) {\n return { peak: true, boundaryMs: ms + (current.to - minuteOfDay) * 60_000 - shifted.getUTCSeconds() * 1000 - shifted.getUTCMilliseconds() }\n }\n // Next window start: later today (weekday only), else the following days'\n // first morning window; the scan bound makes a malformed clock terminate.\n for (let dayOffset = 0; dayOffset < 8; dayOffset += 1) {\n const day = new Date(ms + BEIJING_UTC_OFFSET_MS + dayOffset * 86_400_000)\n if (day.getUTCDay() < 1 || day.getUTCDay() > 5) continue\n const realDayStart = Date.UTC(day.getUTCFullYear(), day.getUTCMonth(), day.getUTCDate()) - BEIJING_UTC_OFFSET_MS\n for (const window of PEAK_WINDOWS) {\n if (dayOffset === 0 && window.from <= minuteOfDay) continue\n return { peak: false, boundaryMs: realDayStart + window.from * 60_000 }\n }\n }\n // Unreachable (the scan covers a full week); a conservative off-peak answer.\n return { peak: false, boundaryMs: ms + 86_400_000 }\n}\n\n/** The price row for a model id; unknown ids take the flash-class row (documented estimate). */\nfunction priceFor(model: string): ModelPrice {\n return model.includes('v4-pro') ? PRO_PRICE : FLASH_PRICE\n}\n\n/**\n * Estimate one call's DeepSeek spend in CNY from its token totals, priced in\n * the billing period at `atMs`. Uncatalogued model ids take the flash-class\n * row; ids from other providers never reach this function (the service gates\n * by route family). Rounded to micro-CNY so the ledger stays readable.\n */\nexport function deepseekModelSpend(model: string, totals: Readonly<UsageTokenTotals>, atMs: number): number {\n const price = priceFor(model)\n const period = deepseekPeriodAt(atMs)\n const column = period.peak ? 'peak' : 'offPeak'\n const spend = (totals.cacheReadTokens * price.cacheHit[column]\n + (totals.inputTokens + totals.cacheWriteTokens) * price.inputMiss[column]\n + totals.outputTokens * price.output[column]) / 1_000_000\n return Math.round(spend * 1e6) / 1e6\n}\n","/**\n * The usage statistics settings section: two tabs (用量: today's usage,\n * balances, trend; 个人套餐: per-provider plan quota windows) plus a compact\n * settings row. Data comes from the host's loopback-fenced\n * /api/dsh-usage/overview document; polling runs only while the section is\n * mounted and the tab is visible.\n * @module @linxin666/dsh-usage/client/UsageSectionCard\n */\n\nimport { useEffect, useState, useSyncExternalStore, type ReactNode } from 'react'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { UsageStoreInstance } from './usage-store.ts'\nimport { t } from './locales.ts'\nimport styles from './usage.module.css'\nimport { isDeepSeekProviderRoute } from '../core/adapters.ts'\nimport { deepseekPeriodAt } from '../core/pricing.ts'\nimport type { ProviderSnapshotView, UsageOverviewView, UsageProviderSummary, UsageTokenTotals } from '../core/types.ts'\n\n/** The settings fields this section edits (immediate-apply semantics). */\nexport interface UsageSettings {\n enabled?: boolean\n pollIntervalSec?: number\n bubbleMode?: string\n}\n\n/** The registration-side face the section's slot entry injects. */\nexport interface UsageSectionFace {\n /** The section-local store (overview snapshot + lifecycle). */\n store: UsageStoreInstance\n /** Fetch one overview now. */\n poll: () => void\n /** Force a host probe cycle now (resolves with the fresh overview). */\n refresh: () => void\n /** Whether a forced refresh is in flight (component-local state mirrors it). */\n settings: SettingsScope<UsageSettings>\n}\n\nexport interface UsageSectionProps extends UsageSectionFace {\n /** Close the settings panel (the shell owns the open state). */\n close: () => void\n}\n\n/** Poll cadence while the section is open. */\nconst SECTION_POLL_MS = 10_000\n\n/** Compact token count: 12345 -> 12.3k, 1234567 -> 1.23M. */\nexport function formatTokens(value: number): string {\n if (!Number.isFinite(value) || value <= 0) return '0'\n if (value < 1000) return String(value)\n if (value < 1_000_000) return trim(value / 1000) + 'k'\n if (value < 1_000_000_000) return trim(value / 1_000_000) + 'M'\n return trim(value / 1_000_000_000) + 'B'\n}\n\nfunction trim(value: number): string {\n return value >= 100 ? String(Math.round(value)) : value.toFixed(value >= 10 ? 1 : 2).replace(/\\.?0+$/, '')\n}\n\nfunction formatTime(ms: number): string {\n try {\n return new Date(ms).toLocaleTimeString()\n } catch {\n return ''\n }\n}\n\nfunction formatClock(ms: number): string {\n try {\n return new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n } catch {\n return ''\n }\n}\n\n/** Formatted CNY spend estimate (the only priced currency today). */\nfunction formatCost(cost: number): string {\n return '¥' + cost.toFixed(2)\n}\n\nfunction toneClass(percent: number): string {\n if (percent >= 90) return styles.barLow\n if (percent >= 70) return styles.barWarn\n return styles.barFill\n}\n\n/** A provider row backed by a configured credential (api key, env key, or OAuth grant). */\nfunction isConfigured(provider: ProviderSnapshotView): boolean {\n // An older wire document without the credential field renders as before.\n return provider.credential !== 'none'\n}\n\nfunction TotalsRow(props: { totals: UsageTokenTotals }): ReactNode {\n const { totals } = props\n return (\n <div className={styles.statRow}>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens + totals.outputTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.total')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.input')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.outputTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.output')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.cacheReadTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.cacheRead')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.cacheWriteTokens)}</span>\n <span className={styles.statLabel}>{t('usage.tokens.cacheWrite')}</span>\n </div>\n <div className={styles.stat}>\n <span className={styles.statValue}>{formatTokens(totals.calls)}</span>\n <span className={styles.statLabel}>{t('usage.calls', { n: totals.calls })}</span>\n </div>\n </div>\n )\n}\n\nfunction balanceLine(provider: ProviderSnapshotView): ReactNode {\n if (provider.balance !== undefined) {\n return <span className={styles.providerBalance}>{provider.balance.currency.toUpperCase() === 'CNY' ? '¥' : provider.balance.currency.toUpperCase() === 'USD' ? '$' : ''}{provider.balance.totalBalance}{provider.balance.currency.toUpperCase() !== 'CNY' && provider.balance.currency.toUpperCase() !== 'USD' ? ' ' + provider.balance.currency.toUpperCase() : ''}</span>\n }\n if (provider.credential === 'oauth') return <span className={styles.muted}>{t('usage.oauth')}</span>\n if (provider.credential === 'none') return <span className={styles.muted}>{t('usage.balance.noCredential')}</span>\n if (!provider.supported) return <span className={styles.muted}>{t('usage.balance.unsupported')}</span>\n return null\n}\n\nfunction ProviderRow(props: { provider: ProviderSnapshotView; current?: string }): ReactNode {\n const { provider, current } = props\n return (\n <div className={styles.providerRow} data-dsh-part=\"provider-row\">\n <span className={styles.providerName}>\n {provider.displayName}\n {current === provider.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.providerTokens}>{balanceLine(provider)}</span>\n </div>\n )\n}\n\n/** The section component; the slot merges the face into these props. */\nexport function UsageSectionCard(props: UsageSectionProps): ReactNode {\n const { store, poll, refresh, settings } = props\n const ui = useSyncExternalStore(store.subscribe, store.getSnapshot)\n const settingsSnapshot = settings.getSnapshot()\n const settingsValue = settingsSnapshot.value ?? {}\n const [tab, setTab] = useState<'usage' | 'plans'>('usage')\n const [refreshing, setRefreshing] = useState(false)\n\n // Poll while mounted and visible; the overview is cheap (no probes — the\n // host's own cycle owns those) so 10 s keeps balances fresh-ish between\n // manual refreshes.\n useEffect(() => {\n poll()\n let timer: number | undefined\n const start = (): void => {\n if (timer === undefined && document.visibilityState === 'visible') timer = window.setInterval(poll, SECTION_POLL_MS)\n }\n const onVisibility = (): void => {\n if (document.visibilityState === 'visible') {\n poll()\n start()\n } else if (timer !== undefined) {\n window.clearInterval(timer)\n timer = undefined\n }\n }\n start()\n document.addEventListener('visibilitychange', onVisibility)\n return () => {\n if (timer !== undefined) window.clearInterval(timer)\n document.removeEventListener('visibilitychange', onVisibility)\n }\n }, [poll])\n\n const snapshot = ui.snapshot\n\n const onRefresh = (): void => {\n setRefreshing(true)\n try {\n refresh()\n } finally {\n // The POST resolves through the next poll tick; unlock shortly either way.\n window.setTimeout(() => setRefreshing(false), 3000)\n }\n }\n\n if (ui.status === 'error') {\n return <div className={styles.section} data-dsh-plugin=\"usage\">{t('usage.error', { error: ui.error ?? '' })}</div>\n }\n if (snapshot === null) {\n return <div className={styles.section} data-dsh-plugin=\"usage\">{t('usage.loading')}</div>\n }\n\n const current = snapshot.current\n const currentProvider = snapshot.providers.find((provider) => provider.provider === current.provider)\n // Peak-period line: only when the official DeepSeek family is in play this\n // session (the current route, or spend recorded under one today).\n const deepseekPeriod = deepseekPeriodAt(Date.now())\n const deepseekVisible = (current.provider !== undefined && isDeepSeekProviderRoute(current.provider))\n || snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider))\n // Plans tab: only configured routes with a real coding-plan/subscription\n // adapter (planSupported; an older host without the flag falls back to \"has\n // a plan fact\"). Balance-only providers (DeepSeek, ZenMux, ...) and\n // unconfigured routes (credential 'none') never appear here.\n const planProviders = snapshot.providers.filter((provider) => isConfigured(provider) && (provider.planSupported === true || (provider.planSupported === undefined && provider.plan !== undefined)))\n\n return (\n <div className={styles.section} data-dsh-plugin=\"usage\">\n <div className={styles.header} data-dsh-part=\"header\">\n <span className={styles.currentProvider}>\n {currentProvider !== undefined\n ? `${currentProvider.displayName}${current.model !== undefined && current.model !== '' ? ' · ' + current.model : ''}`\n : t('usage.noData')}\n </span>\n <span style={{ display: 'flex', gap: 8, alignItems: 'center' }}>\n <span className={styles.muted}>{t('usage.updated', { time: formatTime(snapshot.updatedAt) })}</span>\n <button type=\"button\" className={styles.refreshBtn} onClick={onRefresh} disabled={refreshing}>\n {refreshing ? t('usage.refreshing') : t('usage.refresh')}\n </button>\n </span>\n </div>\n\n <div className={styles.tabs} role=\"tablist\" data-dsh-part=\"tabs\">\n <button type=\"button\" role=\"tab\" aria-selected={tab === 'usage'} className={tab === 'usage' ? `${styles.tab} ${styles.tabActive}` : styles.tab} onClick={() => setTab('usage')}>\n {t('usage.tab.usage')}\n </button>\n <button type=\"button\" role=\"tab\" aria-selected={tab === 'plans'} className={tab === 'plans' ? `${styles.tab} ${styles.tabActive}` : styles.tab} onClick={() => setTab('plans')}>\n {t('usage.tab.plans')}\n </button>\n </div>\n\n {tab === 'usage' && (\n <>\n <div className={styles.card} data-dsh-part=\"today-card\">\n <span className={styles.cardTitle}>{t('usage.today')}</span>\n {deepseekVisible && (\n <span className={styles.muted} data-dsh-part=\"peak-status\">\n {t(deepseekPeriod.peak ? 'usage.peak.on' : 'usage.peak.off', { time: formatClock(deepseekPeriod.boundaryMs) })}\n </span>\n )}\n {snapshot.usage.today.totals.calls === 0\n ? <span className={styles.muted}>{t('usage.noData')}</span>\n : <TotalsRow totals={snapshot.usage.today.totals} />}\n {snapshot.usage.today.totals.cost > 0 && (\n <div className={styles.providerRow} data-dsh-part=\"today-cost\">\n <span className={styles.providerName}>{t('usage.today.cost')}</span>\n <span className={styles.providerTokens}>{formatCost(snapshot.usage.today.totals.cost)}</span>\n </div>\n )}\n {snapshot.usage.today.providers.length > 0 && (\n <div data-dsh-part=\"provider-list\">\n {snapshot.usage.today.providers.map((row) => (\n <div key={row.provider} className={styles.providerRow}>\n <span className={styles.providerName}>\n {snapshot.providers.find((provider) => provider.provider === row.provider)?.displayName ?? row.provider}\n {current.provider === row.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.providerTokens}>{formatTokens(row.totals.inputTokens + row.totals.cacheReadTokens + row.totals.cacheWriteTokens + row.totals.outputTokens)} · {t('usage.calls', { n: row.totals.calls })}{row.totals.cost > 0 ? ` · ${formatCost(row.totals.cost)}` : ''}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n\n <div className={styles.card} data-dsh-part=\"balance-card\">\n <span className={styles.cardTitle}>{t('usage.balance')}</span>\n {(() => {\n const configured = snapshot.providers.filter(isConfigured)\n const rows = configured.filter((provider) => provider.balanceSupported === true || (provider.balanceSupported === undefined && provider.supported))\n if (rows.length === 0) {\n return <span className={styles.muted}>{configured.length === 0 ? t('usage.balance.noneConfigured') : t('usage.balance.unsupported')}</span>\n }\n return rows.map((provider) => (\n <ProviderRow key={provider.provider} provider={provider} current={current.provider} />\n ))\n })()}\n {snapshot.providers.some((provider) => isConfigured(provider) && provider.error !== undefined) && (\n <span className={styles.errorLine}>\n {snapshot.providers.filter((provider) => isConfigured(provider) && provider.error !== undefined).map((provider) => `${provider.displayName}: ${t('usage.provider.error', { error: provider.error ?? '' })}`).join(t('usage.errorListSeparator'))}\n </span>\n )}\n </div>\n\n <RangeCard range={snapshot.usage.range} providers={snapshot.providers} currentProvider={current.provider} />\n\n <SettingsRow settings={settings} snapshot={settingsSnapshot.status === 'ready' ? settingsSnapshot : undefined} value={settingsValue} />\n </>\n )}\n\n {tab === 'plans' && (\n planProviders.length === 0\n ? <div className={styles.card}><span className={styles.muted}>{t('usage.plan.noneConfigured')}</span></div>\n : planProviders.map((provider) => <PlanCard key={provider.provider} provider={provider} current={current.provider} />)\n )}\n </div>\n )\n}\n\n/** How many model sub-bars render under one provider bar. */\nconst CHART_MODEL_CAP = 3\n\n/**\n * The 近 30 天 card: horizontal bars per provider over the trend window,\n * each with its heaviest models as nested sub-bars. Window totals come from\n * the host's aggregated `usage.range` (an older host without it renders no\n * card instead of a wrong one).\n */\nfunction RangeCard(props: { range?: UsageOverviewView['usage']['range']; providers: ProviderSnapshotView[]; currentProvider?: string }): ReactNode {\n const { range, providers, currentProvider } = props\n if (range === undefined) return null\n const grandTotal = range.totals.inputTokens + range.totals.outputTokens + range.totals.cacheReadTokens + range.totals.cacheWriteTokens\n const maxProvider = Math.max(1, ...range.providers.map((row) => totalOf(row.totals)))\n const nameOf = (id: string): string => providers.find((provider) => provider.provider === id)?.displayName ?? id\n return (\n <div className={styles.card} data-dsh-part=\"trend-card\">\n <span className={styles.cardTitle}>{t('usage.trend')}</span>\n {range.providers.length === 0 || grandTotal === 0\n ? <span className={styles.muted}>{t('usage.noData')}</span>\n : <div className={styles.chart} data-dsh-part=\"usage-chart\">\n {range.providers.map((row) => (\n <ChartProviderRow key={row.provider} row={row} name={nameOf(row.provider)} max={maxProvider} current={currentProvider === row.provider} />\n ))}\n <span className={styles.trendAxis}>\n <span>{range.from.slice(5)}</span>\n <span>{range.to.slice(5)}</span>\n </span>\n </div>}\n </div>\n )\n}\n\nfunction ChartProviderRow(props: { row: UsageProviderSummary; name: string; max: number; current: boolean }): ReactNode {\n const { row, name, max, current } = props\n const total = totalOf(row.totals)\n const maxModel = Math.max(1, ...row.models.slice(0, CHART_MODEL_CAP).map((model) => totalOf(model.totals)))\n return (\n <div className={styles.chartProvider}>\n <span className={styles.chartHead}>\n <span className={styles.providerName}>\n {name}\n {current && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n </span>\n <span className={styles.chartTokens}>{formatTokens(total)} · {t('usage.calls', { n: row.totals.calls })}</span>\n </span>\n <span className={styles.chartBar}>\n <span className={styles.chartFill} style={{ width: `${Math.max(2, Math.round((total / max) * 100))}%` }} />\n </span>\n {row.models.slice(0, CHART_MODEL_CAP).map((model) => {\n const modelTotal = totalOf(model.totals)\n return (\n <span key={model.model} className={styles.chartModel} title={`${row.provider} · ${model.model}: ${formatTokens(modelTotal)}`}>\n <span className={styles.chartModelName}>{model.model}</span>\n <span className={styles.chartModelBar}>\n <span className={styles.chartModelFill} style={{ width: `${Math.max(3, Math.round((modelTotal / maxModel) * 100))}%` }} />\n </span>\n <span className={styles.chartTokens}>{formatTokens(modelTotal)}</span>\n </span>\n )\n })}\n </div>\n )\n}\n\nfunction totalOf(totals: UsageTokenTotals): number {\n return totals.inputTokens + totals.cacheReadTokens + totals.cacheWriteTokens + totals.outputTokens\n}\n\nfunction PlanCard(props: { provider: ProviderSnapshotView; current?: string }): ReactNode {\n const { provider, current } = props\n return (\n <div className={`${styles.card} ${styles.planCard}`} data-dsh-part=\"plan-card\">\n <div className={styles.planHead}>\n <span className={styles.planName}>\n {provider.displayName}\n {current === provider.provider && <span className={styles.currentBadge}>{t('usage.current')}</span>}\n {provider.plan?.planName !== undefined ? ` · ${provider.plan.planName}` : ''}\n </span>\n </div>\n {provider.error !== undefined && <span className={styles.errorLine}>{t('usage.provider.error', { error: provider.error })}</span>}\n {provider.credential === 'none' && provider.plan === undefined\n ? <span className={styles.muted}>{t('usage.balance.noCredential')}</span>\n : provider.plan === undefined || provider.plan.windows.length === 0\n ? <span className={styles.muted}>{t('usage.plan.noPlan')}</span>\n : provider.plan.windows.map((window) => (\n <div key={window.key} className={styles.windowRow} data-dsh-part=\"plan-window\">\n <span className={styles.windowLabel}>\n <span>{window.name ?? t(`usage.plan.windows.${window.key}`)}</span>\n <span>{window.percent !== undefined ? `${window.percent >= 10 ? Math.round(window.percent) : window.percent.toFixed(1)}%` : ''}</span>\n </span>\n {window.percent !== undefined && (\n <span className={styles.bar}>\n <span className={toneClass(window.percent)} style={{ width: `${Math.min(100, Math.max(0, window.percent))}%`, display: 'block' }} />\n </span>\n )}\n {window.resetsAt !== undefined && (\n <span className={styles.resetLine}>{t('usage.plan.reset', { date: new Date(window.resetsAt).toLocaleString() })}</span>\n )}\n </div>\n ))}\n </div>\n )\n}\n\nfunction SettingsRow(props: {\n settings: UsageSectionProps['settings']\n snapshot?: { writable: boolean }\n value: UsageSettings\n}): ReactNode {\n const { settings, snapshot, value } = props\n const disabled = snapshot === undefined || !snapshot.writable\n const bubbleMode = typeof value.bubbleMode === 'string' && ['always', 'change', 'off'].includes(value.bubbleMode) ? value.bubbleMode : 'always'\n return (\n <div className={styles.card} data-dsh-part=\"settings-row\">\n <span className={styles.cardTitle}>{t('usage.config.title')}</span>\n <div className={styles.settingsGrid}>\n <label className={styles.settingItem}>\n <input\n type=\"checkbox\"\n checked={value.enabled ?? true}\n disabled={disabled}\n onChange={(event) => { void settings.set('enabled', event.target.checked) }}\n />\n {t('usage.config.enabled')}\n </label>\n <label className={styles.settingItem}>\n {t('usage.config.pollIntervalSec')}\n <input\n type=\"number\"\n min={30}\n max={3600}\n value={typeof value.pollIntervalSec === 'number' ? value.pollIntervalSec : 60}\n disabled={disabled}\n onChange={(event) => {\n const parsed = Number(event.target.value)\n if (Number.isFinite(parsed) && parsed >= 30 && parsed <= 3600) void settings.set('pollIntervalSec', Math.round(parsed))\n }}\n />\n </label>\n <label className={styles.settingItem}>\n {t('usage.config.bubbleMode')}\n <select\n value={bubbleMode}\n disabled={disabled}\n onChange={(event) => { void settings.set('bubbleMode', event.target.value) }}\n >\n <option value=\"always\">{t('usage.config.bubbleMode.always')}</option>\n <option value=\"change\">{t('usage.config.bubbleMode.change')}</option>\n <option value=\"off\">{t('usage.config.bubbleMode.off')}</option>\n </select>\n </label>\n </div>\n </div>\n )\n}\n","/**\n * dsh-usage browser half — seats the first-level 使用统计 settings section\n * (below the Workshop entry) and polls the host overview only while the\n * section is open. All provider probing and credential handling happens in\n * the host half; this bundle only renders the overview document.\n * @module @linxin666/dsh-usage/client\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: pulls the locale plugin's Context merge (ctx.locale).\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only: pulls the ctx.slots merge (the renderer owns the slot registry since 0.1.2).\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport { createElement } from 'react'\nimport { createUsageStore, type UsageStoreInstance } from './usage-store.ts'\nimport { UsageSectionCard, type UsageSectionFace, type UsageSettings } from './UsageSectionCard.tsx'\nimport { NS, en, zh } from './locales.ts'\nimport type { UsageOverviewView } from '../core/types.ts'\n\n/** The host usage API as the browser sees it (same-origin JSON endpoints). */\ninterface UsageHttpApi {\n overview(): Promise<UsageOverviewView>\n refresh(): Promise<UsageOverviewView>\n}\n\n/** Hard ceiling for one usage API call; a stalled host must not pile up requests. */\nconst USAGE_FETCH_TIMEOUT_MS = 20_000\n\nasync function usageFetch<T>(path: string, method: 'GET' | 'POST'): Promise<T> {\n const response = await fetch(path, {\n ...(method === 'POST' ? { method: 'POST' } : {}),\n signal: AbortSignal.timeout(USAGE_FETCH_TIMEOUT_MS),\n })\n if (!response.ok) throw new Error('usage ' + path + ' failed: ' + response.status)\n return (await response.json()) as T\n}\n\nconst usageApi: UsageHttpApi = {\n overview: () => usageFetch('/api/dsh-usage/overview', 'GET'),\n refresh: () => usageFetch('/api/dsh-usage/refresh', 'POST'),\n}\n\n/** Settings namespace the section edits (the host plugin registers it). */\nconst USAGE_SETTINGS_NS = 'dsh-usage'\n\n/** First-level nav position: directly below the Workshop section (order 150). */\nconst SECTION_ORDER = 151\n\n/** Required services. */\nexport const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote']\n\nexport type { UsageSectionProps, UsageSectionFace } from './UsageSectionCard.tsx'\nexport type { UsageUiState } from './usage-store.ts'\nexport type { UsageSettings }\n\ndeclare module '@deepseek-ai/cordis' {\n interface Context {\n /**\n * Optional rc.6 compatibility binder provided by dsh-web-settings;\n * absent when that group plugin is not installed, so callers fall back to\n * the official settings scope.\n */\n webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }\n }\n}\n\n/**\n * Client plugin body: register dictionaries and seat the settings section.\n * The overview poll loop and the store live with the section component's\n * mount cycle, so no background traffic exists while the page is closed.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n try {\n return ctx.locale.register(NS, { zh, en })\n } catch {\n return () => {}\n }\n }, 'dsh-usage: dictionaries')\n\n const binder = ctx.get('webUiSettings') ?? ctx.settingsScope\n const settingsScope = binder.bind<UsageSettings>({ namespace: USAGE_SETTINGS_NS })\n\n // One store instance per apply body; the section mounts and unmounts with\n // the settings page, and the store survives between visits so the last\n // overview renders instantly on reopen.\n const store: UsageStoreInstance = createUsageStore().create()\n\n let pollSeq = 0\n const poll = (): void => {\n const seq = pollSeq + 1\n pollSeq = seq\n usageApi.overview().then((snapshot) => {\n if (seq !== pollSeq) return\n store.actions.setSnapshot(snapshot)\n }, () => {\n if (seq !== pollSeq) return\n store.actions.setState('error', 'usage.overview transport error')\n })\n }\n // The refresh response is authoritative: it reflects the completed probe\n // cycle, so it applies even when a faster section poll already bumped the\n // sequence past it (the poll's pre-cycle snapshot is the stale one).\n const refresh = (): void => {\n const seq = pollSeq + 1\n pollSeq = seq\n usageApi.refresh().then((snapshot) => {\n pollSeq = seq\n store.actions.setSnapshot(snapshot)\n }, () => {\n if (seq !== pollSeq) return\n store.actions.setState('error', 'usage.refresh transport error')\n })\n }\n\n const face = (): UsageSectionFace => ({ store, poll, refresh, settings: settingsScope })\n\n ctx.slots.inject('settings.section', () => {\n try {\n const unregister = ctx.slots.register({\n name: 'settings.section',\n id: 'dsh-usage',\n order: SECTION_ORDER,\n label: () => ctx.locale.bind(NS)('usage.title'),\n locale: NS,\n inject: face,\n }, UsageSectionCard)\n return () => {\n unregister()\n }\n } catch {\n return () => {}\n }\n })\n}\n"],"mappings":"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"}
|
|
@@ -31,6 +31,7 @@ export declare const zh: {
|
|
|
31
31
|
'usage.balance': string;
|
|
32
32
|
'usage.balance.unsupported': string;
|
|
33
33
|
'usage.balance.noCredential': string;
|
|
34
|
+
'usage.balance.noneConfigured': string;
|
|
34
35
|
'usage.oauth': string;
|
|
35
36
|
'usage.plan.reset': string;
|
|
36
37
|
'usage.plan.noPlan': string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-usage",
|
|
3
3
|
"description": "Usage statistics plugin for the dsh web GUI: per-provider balance and coding-plan quota detection plus a live token usage ledger, with a dedicated pet bubble for the current provider",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.19",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
67
67
|
"@deepseek-ai/dsh-session": "^0.1.2-rc.1",
|
|
68
68
|
"@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
|
|
69
|
-
"@linxin666/dsh-pet": "0.3.
|
|
69
|
+
"@linxin666/dsh-pet": "0.3.19",
|
|
70
70
|
"@testing-library/dom": "^10.4.1",
|
|
71
71
|
"@testing-library/react": "^16.3.2",
|
|
72
72
|
"@types/node": "^22.20.0",
|
|
@@ -83,6 +83,12 @@ function toneClass(percent: number): string {
|
|
|
83
83
|
return styles.barFill
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/** A provider row backed by a configured credential (api key, env key, or OAuth grant). */
|
|
87
|
+
function isConfigured(provider: ProviderSnapshotView): boolean {
|
|
88
|
+
// An older wire document without the credential field renders as before.
|
|
89
|
+
return provider.credential !== 'none'
|
|
90
|
+
}
|
|
91
|
+
|
|
86
92
|
function TotalsRow(props: { totals: UsageTokenTotals }): ReactNode {
|
|
87
93
|
const { totals } = props
|
|
88
94
|
return (
|
|
@@ -199,10 +205,11 @@ export function UsageSectionCard(props: UsageSectionProps): ReactNode {
|
|
|
199
205
|
const deepseekPeriod = deepseekPeriodAt(Date.now())
|
|
200
206
|
const deepseekVisible = (current.provider !== undefined && isDeepSeekProviderRoute(current.provider))
|
|
201
207
|
|| snapshot.usage.today.providers.some((row) => isDeepSeekProviderRoute(row.provider))
|
|
202
|
-
// Plans tab: only routes with a real coding-plan/subscription
|
|
203
|
-
// (planSupported; an older host without the flag falls back to "has
|
|
204
|
-
// fact"). Balance-only providers (DeepSeek, ZenMux, ...)
|
|
205
|
-
|
|
208
|
+
// Plans tab: only configured routes with a real coding-plan/subscription
|
|
209
|
+
// adapter (planSupported; an older host without the flag falls back to "has
|
|
210
|
+
// a plan fact"). Balance-only providers (DeepSeek, ZenMux, ...) and
|
|
211
|
+
// unconfigured routes (credential 'none') never appear here.
|
|
212
|
+
const planProviders = snapshot.providers.filter((provider) => isConfigured(provider) && (provider.planSupported === true || (provider.planSupported === undefined && provider.plan !== undefined)))
|
|
206
213
|
|
|
207
214
|
return (
|
|
208
215
|
<div className={styles.section} data-dsh-plugin="usage">
|
|
@@ -265,15 +272,18 @@ export function UsageSectionCard(props: UsageSectionProps): ReactNode {
|
|
|
265
272
|
<div className={styles.card} data-dsh-part="balance-card">
|
|
266
273
|
<span className={styles.cardTitle}>{t('usage.balance')}</span>
|
|
267
274
|
{(() => {
|
|
268
|
-
const
|
|
269
|
-
|
|
275
|
+
const configured = snapshot.providers.filter(isConfigured)
|
|
276
|
+
const rows = configured.filter((provider) => provider.balanceSupported === true || (provider.balanceSupported === undefined && provider.supported))
|
|
277
|
+
if (rows.length === 0) {
|
|
278
|
+
return <span className={styles.muted}>{configured.length === 0 ? t('usage.balance.noneConfigured') : t('usage.balance.unsupported')}</span>
|
|
279
|
+
}
|
|
270
280
|
return rows.map((provider) => (
|
|
271
281
|
<ProviderRow key={provider.provider} provider={provider} current={current.provider} />
|
|
272
282
|
))
|
|
273
283
|
})()}
|
|
274
|
-
{snapshot.providers.some((provider) => provider.error !== undefined) && (
|
|
284
|
+
{snapshot.providers.some((provider) => isConfigured(provider) && provider.error !== undefined) && (
|
|
275
285
|
<span className={styles.errorLine}>
|
|
276
|
-
{snapshot.providers.filter((provider) => provider.error !== undefined).map((provider) => `${provider.displayName}: ${t('usage.provider.error', { error: provider.error ?? '' })}`).join(t('usage.errorListSeparator'))}
|
|
286
|
+
{snapshot.providers.filter((provider) => isConfigured(provider) && provider.error !== undefined).map((provider) => `${provider.displayName}: ${t('usage.provider.error', { error: provider.error ?? '' })}`).join(t('usage.errorListSeparator'))}
|
|
277
287
|
</span>
|
|
278
288
|
)}
|
|
279
289
|
</div>
|
package/src/client/locales.ts
CHANGED
|
@@ -33,6 +33,7 @@ export const zh = {
|
|
|
33
33
|
'usage.balance': '余额',
|
|
34
34
|
'usage.balance.unsupported': '暂不支持余额查询',
|
|
35
35
|
'usage.balance.noCredential': '未配置凭据',
|
|
36
|
+
'usage.balance.noneConfigured': '没有已配置的提供方',
|
|
36
37
|
'usage.oauth': 'OAuth 凭据,不做余额查询',
|
|
37
38
|
'usage.plan.reset': '{date} 重置',
|
|
38
39
|
'usage.plan.noPlan': '未检测到套餐数据',
|
|
@@ -77,6 +78,7 @@ export const en: Record<UsageKey, string> = {
|
|
|
77
78
|
'usage.balance': 'Balance',
|
|
78
79
|
'usage.balance.unsupported': 'Balance query not supported',
|
|
79
80
|
'usage.balance.noCredential': 'No credential configured',
|
|
81
|
+
'usage.balance.noneConfigured': 'No providers configured',
|
|
80
82
|
'usage.oauth': 'OAuth credential, no balance query',
|
|
81
83
|
'usage.plan.reset': 'resets {date}',
|
|
82
84
|
'usage.plan.noPlan': 'No plan data detected',
|