dsh-bottom-info-bar 1.19.2 → 1.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -167,14 +167,16 @@ Every model response is recorded (usage × unit price) and aggregated four ways:
167
167
 
168
168
  ## Updating
169
169
 
170
- The plugin checks npm for a newer version at startup and re-checks at most every 15 minutes. Pick how it should act in **Version and updates** on the plugin settings page:
170
+ The plugin checks npm shortly after each DSH start. Pick how it should act in **Version and updates** on the plugin settings page:
171
171
 
172
172
  - **Automatic** (default): download, verify and replace the plugin's own files, then all you do is restart DSH once.
173
- - **Manual**: it only checks and tells you; nothing is downloaded until you press **Check for updates**.
173
+ - **Manual**: it only checks and tells you; nothing is downloaded until you press **Check now**. That button only appears in manual mode.
174
174
 
175
- Every file is verified before it lands, and a failure rolls the whole batch back, so a bad download never breaks the copy you are running. **Roll back to the previous version** is one click away in the same section. Only this plugin's own files are touched — never the dependency tree or the profile manifest.
175
+ The check happens at startup only — a new version needs a restart to take effect anyway, and the restart itself triggers the next check. So the settings page shows **last checked**, and you never have to guess whether it is doing anything.
176
176
 
177
- When something needs you, the bar shows two short badges: **Restart to apply** (a new version is on disk) and **Update failed** (the last automatic update did not go through). Each one has its own switch under **Notices** and takes no space otherwise.
177
+ Every file is verified before it lands, and a failure rolls the whole batch back, so a bad download never breaks the copy you are running. If the new version misbehaves, **Roll back to the previous version** appears in the same section along with the reason; once you are satisfied it stops taking up space. Only this plugin's own files are touched — never the dependency tree or the profile manifest.
178
+
179
+ When something needs you, the bar shows two short badges: **Restart to apply** (a new version is on disk) and **Update failed** (the last update did not go through). Each one has its own switch under **Notices** and takes no space otherwise. On failure the settings page explains it in plain words, and the raw error is kept in `~/.dsh/dsh-bottom-info-bar/update-log.jsonl` for troubleshooting.
178
180
 
179
181
  Updating itself only runs when the plugin is loaded from a profile (npm, GitHub address and the install script all qualify). A source checkout or a `link:` install is treated as read-only and left alone — update those by hand:
180
182
 
package/README.zh-CN.md CHANGED
@@ -167,14 +167,16 @@ cd dsh-bottom-info-bar
167
167
 
168
168
  ## 更新
169
169
 
170
- 插件会自己检查更新:启动时查一次 npm,之后最多每 15 分钟复查一次。更新方式在插件设置页的**版本与更新**里二选一:
170
+ 插件在每次 DSH 启动后不久检查一次 npm 上有没有新版本。更新方式在插件设置页的**版本与更新**里二选一:
171
171
 
172
172
  - **全自动更新**(默认):发现新版本就自动下载、校验、替换好,你只需要重启一次 DSH 就生效。
173
- - **手动更新**:只检查、不下载,你点「检查更新」时才执行。
173
+ - **手动更新**:只提示、不下载,你点「检查更新」时才执行。这个按钮只在选手动时出现。
174
174
 
175
- 替换前先校验文件完整性,任何一步没过就整批回滚,不会把正在用的版本弄坏;设置页里也随时可以点「回滚到上一版」。整个过程只改插件自己的文件,不碰依赖树,也不改 profile 的清单。
175
+ 检查只发生在启动那一次(装好也本来就要重启才生效,而重启本身就会触发下一次检查),所以设置页里会写明**上次检查**是什么时候,你不用猜它有没有在干活。
176
176
 
177
- 需要你动手时,信息栏会出现两枚短标记:**重启生效**(新版已装好,等重启)和**更新失败**(上次自动更新没成功)。两枚标记各自可以在「提醒信息」里关掉,平时不占位。
177
+ 替换前先校验文件完整性,任何一步没过就整批回滚,不会把正在用的版本弄坏。万一新版有问题,设置页会出现「回滚到上一版」并说明原因;确认没问题之后它就不占位了。整个过程只改插件自己的文件,不碰依赖树,也不改 profile 的清单。
178
+
179
+ 需要你动手时,信息栏会出现两枚短标记:**重启生效**(新版已装好,等重启)和**更新失败**(上次更新没成功)。两枚标记各自可以在「提醒信息」里关掉,平时不占位。更新失败时设置页会用一句人话讲清原因,原始报错留在 `~/.dsh/dsh-bottom-info-bar/update-log.jsonl` 里备查。
178
180
 
179
181
  自更新只在插件被正常装进 profile 时启用(npm、GitHub 地址、一键脚本三种安装方式都算)。如果你用的是本仓库的源码副本或 `link:` 安装,插件会把它当成只读副本、不去改动,这时按下面的方式手动更新:
180
182
 
package/docs/INSTALL.md CHANGED
@@ -82,12 +82,16 @@ dsh --profile web --dump-config | grep -A2 dsh-bottom-info-bar
82
82
 
83
83
  ## 更新版本
84
84
 
85
- 插件会自己检查更新:启动时查一次 npm,之后最多每 15 分钟复查一次。处理方式在插件设置页的**版本与更新**里二选一:
85
+ 插件在每次 DSH 启动后不久检查一次 npm 上有没有新版本。处理方式在插件设置页的**版本与更新**里二选一:
86
86
 
87
87
  - **全自动更新**(默认):自动下载 → 校验 → 替换插件自己的文件,然后你重启一次 DSH 就生效。
88
- - **手动更新**:只检查、不下载;点「检查更新」按钮才执行。
88
+ - **手动更新**:只提示、不下载;点「检查更新」按钮才执行(该按钮只在手动方式下出现)。
89
89
 
90
- 替换前会校验文件完整性,任何一步失败都整批回滚,不会破坏正在运行的那一份;设置页里另有「回滚到上一版」。整个过程只改插件包内文件,不动 profile 的 `package.json` / `pnpm-lock.yaml`,也不碰依赖树。
90
+ 设置页那一块分三层看:最上面是**状态**(结论 + 运行中版本 / 最新版本 / 上次检查时间),中间是**更新方式**二选一,最下面是**出问题时的办法** —— 「回滚到上一版」只在真需要时出现(新版已装好待重启,或上次更新失败),并写明为什么出现。更新失败时会用一句人话讲清原因,原始报错留在 `~/.dsh/dsh-bottom-info-bar/update-log.jsonl`。
91
+
92
+ 检查只发生在启动那一次:新版本装好本来就要重启才生效,而重启本身会触发下一次检查。所以「上次检查」的时间就是判断它有没有在干活的依据。
93
+
94
+ 替换前会校验文件完整性,任何一步失败都整批回滚,不会破坏正在运行的那一份。整个过程只改插件包内文件,不动 profile 的 `package.json` / `pnpm-lock.yaml`,也不碰依赖树。
91
95
 
92
96
  **前提**:自更新只在插件被当作 profile 插件装载时启用(npm、GitHub 地址、一键脚本三种安装方式都算)。源码副本与 `link:` 安装按只读处理,插件不会去改那份代码,需要按下面的方式手动更新。
93
97
 
package/lib/client.js CHANGED
@@ -59,7 +59,7 @@ const BIB_SET_NATIVE_MENU = bibSetNative('Menu');
59
59
  // 与上面同一条铁律:取不到时退回插件内等价实现,绝不把 undefined 交给 React.createElement。
60
60
  const BIB_SET_NATIVE_RISK_CONFIRM = bibSetNative('RiskConfirmation');
61
61
  const LOCALE_NAMESPACE = 'dsh-bottom-info-bar';
62
- const LOCALES = {"zh":{"meta.title":"底部信息栏","meta.description":"在输入框下方显示当前模型、余额和花费。","ui.requestTimedOut":"请求超时","ui.requestCanceled":"请求已取消","ui.couldNotParseResponse":"响应解析失败","ui.rpcFailed":"RPC 失败","ui.refreshFailed":"刷新失败","color.red":"红","color.green":"绿","color.blue":"蓝","color.purple":"紫","color.orange":"橙","color.neutral":"中性","mode.balance":"余额制","mode.subscription":"订阅制","mode.billing":"账单制","mode.native":"原生统计行","mode.common":"通用","ui.nativeStatsField":"原生统计行字段","ui.shownIn":"出现于","ui.pleaseTryAgainLater":"请稍后再试","ui.restoreDefaultColor":"恢复默认颜色","ui.infoBarSettings":"信息栏","ui.settingsAreTemporarilyUnavailable":"暂时无法读取设置","ui.loadingInfoBarSettings":"正在加载设置…","ui.couldNotLoadInfoBar":"无法读取信息栏设置:","ui.changesAppliedButCouldNot":"已应用,但无法保存到本地:","ui.unknownReason":"未知原因","ui.couldNotSave":"{errorPrefix}保存失败:{value}","ui.color":"“{value}”颜色:","ui.hasAnInvalidColorEnter":"“{value}”的颜色格式不正确,请输入 #RRGGBB(例如 #0044CC)。","ui.defaultColorsRestored":"已恢复默认颜色","ui.defaultLabelsRestored":"已恢复默认显示设置","ui.couldNotReset":"重置失败:{value}","ui.identifiesTheProviderAndModel":"显示当前会话使用的服务商和模型。","ui.show":"显示{label}","ui.clickToHide":"点击隐藏","ui.clickToShow":"点击显示","ui.presetColor":"{label}的预设颜色","ui.customColor":"{label}的自定义颜色","ui.customColorOpenColorPicker":"自定义颜色(打开系统取色器)","ui.customColorItem":"自定义…","ui.colorSwatchLabel":"{label}的颜色","ui.hexColor":"{label}的十六进制颜色","ui.expand":"展开","ui.collapse":"收起","ui.saving":"正在保存…","ui.processing":"正在处理…","ui.visibleFields":"要显示的信息","ui.timeDateTitle":"时间与日期","ui.timeDateDesc":"主时间和世界时间的时区与显示格式。","ui.customTextSectionDesc":"显示在信息栏中。","ui.resetConfirmTitle":"重置信息栏设置","ui.resetConfirmDescFields":"将恢复所有显示项;自定义颜色不受影响,改动会直接生效。","ui.resetConfirmDescColors":"将清除全部自定义颜色并恢复默认配色,字段显示开关不受影响。该操作会直接应用到信息栏。","ui.resetConfirmAcknowledge":"我了解这次重置不可撤销","ui.resetConfirmCancel":"取消","ui.resetConfirmConfirm":"确认重置","ui.resetLabels":"恢复显示","ui.resetColors":"恢复颜色","ui.resetRowTitle":"恢复默认","ui.resetRowDesc":"恢复显示项或颜色。","ui.customTextCount":"已输入 {value}/64 个字符","ui.couldNotDisplayInfoBar":"无法显示信息栏设置:{value}","ui.dataAndBilling":"账单数据","ui.dataAndBillingDesc":"导出或清除记录。","ui.exportBillingRecords":"导出账单","ui.exportBillingRecordsDesc":"导出为 CSV 或 JSON。","ui.exportBillingCsv":"导出 CSV","ui.exportBillingJson":"导出 JSON","ui.clearBillingRecords":"清除账单","ui.clearBillingRecordsDesc":"清除后无法恢复。","ui.clearBillingRecordsConfirm":"确定清除本插件保存的全部账单记录吗?建议先导出。清除后无法恢复,但不会影响设置或登录信息。","ui.exportedBillingRecords":"已导出 {count} 条账单记录({format})","ui.noBillingRecordsToExport":"目前没有可导出的账单记录。","ui.exportFailed":"导出失败:{value}","ui.exportIncomplete":"历史账单读取不完整:{value}。本次未导出,请稍后再试。","ui.clearFailed":"清除失败:{value}","ui.clearFailedWithoutDetails":"账单记录未能全部清除","ui.clearedBillingRecords":"已清除 {count} 条账单记录","ui.clearCanceled":"已取消","ui.exportNotSupported":"当前环境不支持下载文件。","ui.infoBar":"信息栏","ui.weekly":"周","ui.monthly":"月","ui.window":"窗口","ui.quotaDisplayUsed":"已用","ui.quotaDisplayRemaining":"剩余","ui.quotaDisplayModeTitle":"订阅窗口百分比方向","ui.quotaDisplayModeDesc":"订阅额度窗口按剩余或已用显示;告警阈值始终按剩余 20% 判定。","ui.windowUsedRemaining":"{label}窗口:已用 {usedPercent}%(剩余 {value}%)","ui.windowUsedRemainingResets":"{label}窗口 已用 {usedPercent}%(剩余 {value}%) · 重置 {value4}","ui.minimax":"MiniMax","ui.supportsImageInput":"支持图像输入。","ui.vision":"视觉","ui.unknown":"未知","ui.unknownModel":"未知模型","ui.modelSelectionPending":"正在读取当前模型","ui.modelCapabilityPending":"正在确认是否支持图像输入","ui.pluginVersion":"\n插件版本:{current}","ui.provider":"服务商:{provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"定价:峰谷价(工作日高峰 9-12、14-18 点;周末全天空闲)","ui.pricingFixed":"定价:固定价","ui.pricingNotListedUsingDefaults":"定价:暂未收录,不计算花费","ui.zhipu":"智谱","ui.xiaomiMiMo":"小米 MiMo","ui.commandCode":"Command Code","ui.subscription":"订阅","ui.cloudBilling":"云账单","ui.plan":"\n套餐:{plan}","ui.expiresLocalTime":"\n到期:{value}(本地时区)","ui.subscriptionServiceModel":"订阅服务:{serviceName}\n模型:{rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"暂时无法读取该服务商的余额。","ui.notSupported":"未适配","ui.notConfigured":"未配置 ","ui.notConfiguredConfigureItIn":"未配置 {credName}。请在\"设置 → 模型\"中填写。","ui.notConfiguredSettingsModels":"未配置 {credName}。请在设置 → 模型中填写。","ui.estimatedBalance":"估算余额:{symbol}{value}","ui.balance":"余额:{symbol}{value}","ui.balance.pushBalanceGroups":"余额","ui.low":"低","ui.estimated":"(估算)","ui.balanceIsTemporarilyUnavailableShowing":"余额暂不可用;正在显示上次数据并自动重试。","ui.couldNotLoadBalanceCheck":"余额获取失败。请检查网络和 API Key。","ui.balanceUnavailable":"余额获取失败","ui.beijingTime":"北京时间 ","ui.peakPrice":"高峰价","ui.offPeakPrice":"空闲价","ui.input":":输入 ¥","ui.mCachedInput":"/M · 缓存 ¥","ui.mOutput":"/M · 输出 ¥","ui.beijingTimeSwitchesTo":"北京时间 {atLabel} 切换为","ui.until":"距","ui.offPeak":"空闲","ui.peak":"高峰","ui.today":"今天 {symbol}{value}","ui.lastDays":"近一月 {symbol}{value}","ui.allTime":"全部 {symbol}{value}","ui.sessionIncludingSubagents":"本会话 {costTxt}(含子代理){value}","ui.session":"本会话","ui.spendIsTemporarilyUnavailableChat":"花费暂不可用;不会影响对话。","ui.spendUnavailable":"花费获取失败","ui.noSignInCredentialsFound":"未找到 {serviceName} 登录凭证。请重新授权。","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} 登录凭证已失效。请重新授权。","ui.deniedAccessReauthorizeOrTry":"{serviceName} 拒绝访问。请重新授权或稍后再试。","ui.rateLimitReachedPleaseTry":"{serviceName} 请求过于频繁。请稍后再试。","ui.timedOutCheckYourConnection":"{serviceName} 响应超时。请检查网络后再试。","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} 返回的数据暂时无法识别。请稍后再试。","ui.isTemporarilyUnavailableCheckYour":"{serviceName} 暂不可用。请检查网络后再试。","ui.subscriptionExpiresLocalTime":"订阅到期:{value}(本地时区)","ui.expires":"到期","ui.subscriptionSource":"订阅源:{value}(","ui.prepaidBalance":"可用余额","ui.availableBalanceLabel":"可用余额","ui.remainingCredits":"剩余额度","ui.availableBalance":"可用余额:{balTxt}","ui.availableCredits":"剩余 credits:{value}","ui.credits":"积分","ui.subscriptionSource.titleLines":"订阅源:{value}","ui.windowRemainingUsed":"{label}窗口:剩余 {value}%(已用 {usedPercent}%)","ui.resetsResetsIn":" · 重置 {value} · 距重置 {value2}","ui.windowRemainingUsedResets":"{label}窗口 剩余 {value}%(已用 {usedPercent}%) · 重置 {value4}","ui.resetsIn":"距重置","ui.configureItInSettingsModels":"。请在\"设置 → 模型\"中填写。","ui.deniedAccessTheTokenMay":"{serviceName} 拒绝访问:Token 可能缺少账单读取权限。","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} 账单暂不可用。请检查网络与权限后再试。","ui.billingSource":"账单源:{value}","ui.thisMonthSSpend":"本月花费:{symbol}{value}","ui.budgetUsed":"预算使用:{value}%","ui.dailyFreeQuotaRemaining":"每日免费额度剩余:{value}","ui.thisMonth":"本月","ui.thisMonthSUsage":"本月用量","ui.budget":"预算","ui.free":"免费","ui.resetsIn.pushBillingGroups":"{value} · 距重置 {value2}","ui.billingServiceModel":"账单服务:{serviceName}\n模型:{modelLabel}{versionLine}","ui.pricing":"定价","ui.spend":"花费","ui.mode":"模式","ui.subscriptionQuota":"订阅额度","ui.billing":"账单","ui.temporarilyUnavailableKeepingTheLast":"暂不可用;正在保留上次数据并自动重试。","ui.spendJournalSavedButThe":"账单流水已保存,但可直接查看的账单文件暂未更新:","ui.thisSpendRecordWasNot":"本次账单未保存,不会计入金额:","ui.ledgerUpdatePending":"账单待整理","ui.spendNotSaved":"账单未保存","ui.askYourAgentToUpdate":"发现插件新版本 {latest}。桌面端更新请在插件管理中完成。","ui.updateAvailable":"新版本提醒","ui.updateCommandCopied":"更新命令已复制","ui.versionAndUpdateTitle":"版本与更新","ui.versionAndUpdateDesc":"插件会自己把新版本替换到本机,重启 DSH 后生效。更新只替换插件自己的文件,账单记录与设置不受影响。","ui.versionUnavailable":"当前运行的 DSH 还没载入这一版插件的更新功能,所以这里暂时只显示这一行。重启 DSH 后,下面就会出现当前版本、更新方式与「检查更新」。","ui.versionRunning":"运行中版本 {version}","ui.versionLatest":"最新版本 {version}","ui.versionUnknown":"未知","ui.autoUpdateTitle":"更新方式","ui.autoUpdateDesc":"选「全自动更新」:启动后自动检查并下载新版本,重启 DSH 后生效。选「手动更新」:只检查,不下载,你点「检查更新」时才执行。","ui.updateModeAuto":"全自动更新","ui.updateModeManual":"手动更新","ui.autoUpdateOn":"已开启","ui.autoUpdateOff":"已关闭","ui.updateCheckNow":"检查更新","ui.updateChecking":"检查中…","ui.updateRetrying":"重试","ui.updateUpToDate":"已是最新版本。","ui.updateDisabled":"当前环境不支持自动更新(仅在插件被装载进 profile 时启用)。","ui.updatePendingRestart":"已更新到 {version},重启 DSH 后生效。","ui.updateInProgress":"发现新版本 {version},正在自动更新…","ui.updateAutoOffPending":"有新版本 {version},当前是手动更新方式。","ui.updateFailed":"上次自动更新失败:{error}","ui.updateRollback":"回滚到上一版","ui.updateRolledBack":"已回滚到上一版 {version}。","ui.updateHeld":"新版 {version} 已被你暂缓,不会自动装回来。","ui.updateAllowHeld":"允许更新到 {version}","ui.updateRestartBadge":"重启生效","ui.updateFailedBadge":"更新失败","ui.tools":"工具调用","ui.avgTTFT":"首 token 平均","ui.cacheHit":"缓存命中","ui.input.BottomInfoBar":"输入","ui.output":"输出","ui.savingView":"正在保存切换…","ui.clickToSwitchFullCompact":"单击切换 完整/简洁","ui.pressEnterOrSpaceFor":"按 Enter 或空格切换为简洁模式。","ui.pressEnterOrSpaceFor.BottomInfoBar":"按 Enter 或空格切换为完整模式。","host.hour":"5 小时","host.unknownProvider":"未知服务商","error.subscription.request-failed":"订阅额度请求未知异常","host.settingsFileCouldNotBe":"设置文件写入未完成","host.everydayQuestions":"日常问答","host.mediumCodingTask":"中等编码任务","host.longDocumentAnalysisCodeReview":"长文档分析/代码审查","host.largeProjectRefactorAcrossMultiple":"大工程重构(多轮)","host.subagentWorkflow":"子代理工作流","error.settings.save-failed":"settings.json 落盘失败:{value}","error.balance.credentials":"凭据读取失败","error.balance.not-configured":"未配置 {credential}","error.request.http":"请求失败(HTTP {status})","error.request.parse":"响应格式异常","error.subscription.not-connected":"未找到 ChatGPT 订阅登录凭证(~/.codex/auth.json),请安装 dsh-chatgpt-subscription 插件绑定","error.subscription.credentials-missing":"ChatGPT 订阅登录凭证缺少 id_token,请安装 dsh-chatgpt-subscription 插件重新绑定","error.subscription.opencode-not-configured":"未配置 OpenCode Go(OPENCODE_GO_API_KEY 或 opencode auth.json)","error.subscription.commandcode-not-configured":"未配置 Command Code(COMMAND_CODE_API_KEY、CMD_API_KEY 或 ~/.commandcode/auth.json)","error.subscription.commandcode-auth-failed":"Command Code 登录凭证已失效。请重新登录或更新 API Key","error.subscription.commandcode-unrecognized":"Command Code 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.zhipu":"智谱 {mapped}","host.zhipu.parseZaiQuota":"智谱 {value}{value2}","error.subscription.zhipu-not-configured":"未配置智谱 API Key(ZAI_API_KEY 或 ZAI_CODING_CN_API_KEY)","error.subscription.zhipu-auth-failed":"智谱 API 认证失败(密钥过期或不正确)","error.subscription.zhipu-unrecognized":"智谱返回了无法识别的额度格式(接口可能已变更),已保留上次数据","error.request.failed":"请求失败({value} {msg})","error.subscription.xiaomi-not-configured":"未配置小米 MiMo Token Plan 凭据({credName} 或 XIAOMI_API_KEY)","error.subscription.xiaomi-http":"请求失败(HTTP {value})","error.subscription.minimax-not-configured":"未配置 MiniMax 凭据(MINIMAX_API_KEY 或 MINIMAX_CN_API_KEY)。查询 Token Plan 必须使用 Subscription Key,按量 API Key 会被服务端拒绝","error.subscription.minimax-auth-failed":"MiniMax 拒绝了当前 API Key:查询 Token Plan 必须使用 Subscription Key(在 Token Plan 订阅页生成),按量 API Key 会被服务端拒绝(status_code=1004 / HTTP 401)","error.subscription.minimax-unrecognized":"MiniMax 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","error.billing.together-not-configured":"未配置 TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"本月真实账单(Together 官方 Usage API)","error.billing.fireworks-not-configured":"未配置 FIREWORKS_API_KEY","error.billing.fireworks-account":"账户解析失败(未取得 account_id)","host.actualBillForThisPeriod":"本周期真实账单(Fireworks Billing Summary)","host.actualUsageForThisPeriod":"本周期真实用量(billingUsage 回退端点,无金额)","error.billing.aws-not-configured":"未配置 AWS 凭据(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","error.billing.aws-http":"请求失败(HTTP {status}),可能缺少 ce:GetCostAndUsage 权限","host.actualMonthlyBillFromAWS":"AWS Cost Explorer 本月真实账单(账单延迟约 24 小时)","error.billing.cloudflare-not-configured":"未配置 CLOUDFLARE_API_KEY(需账号级 Token 并授予 Billing 读权限)","error.billing.cloudflare-account":"未配置 CLOUDFLARE_ACCOUNT_ID","error.billing.cloudflare-http":"请求失败(HTTP {status}),Token 需 Billing 读权限","host.actualMonthlyUsageFromThe":"Cloudflare Billable Usage API(Alpha),本月真实用量","error.billing.request-failed":"账单请求未知异常","host.spendSummaryFileMissingArchived":"账单汇总文件缺失:折叠期间的历史金额暂未计入显示(明细仍在冷归档 usage-archive/,可人工恢复)","host.spendLedgerCouldNotBe":"账单文件写入未完成","host.usageLedgerBusy":"当前仍有回答正在进行,完成后再清除账单记录","error.ledger.clear-failed":"账单记录清理未完成:{value}","error.ledger.snapshot-stale":"折叠汇总落盘失败:{value}","host.basedOnYourLastSessions":"基于你最近 {count} 次会话","host.estimatedFromSpendingOverThe":"基于过去 {SPEND_DAYS} 天消耗速度的估算","host.yourTypicalSession":"你的典型会话","host.patchMustIncludeFieldsOr":"patch 需包含 fields、colors、timeFormat、timeZones 或 customText 之一","host.timeFormatMustBeAnObject":"timeFormat 必须是对象","host.timeFieldMustBeABoolean":"时间格式字段必须是布尔值: {key}","host.timeZonesMustBeAnObject":"timeZones 必须是对象","host.timeZoneMustBeAValid":"时区必须是有效的 IANA 时区: {key}","host.customTextMustBeAString":"自定义文本必须是字符串","host.customTextTooLong":"自定义文本不能超过64字符","host.quotaDisplayModeInvalid":"订阅窗口百分比方向必须是 used 或 remaining","host.infoDensityMustBeFullOrCompact":"信息显示方式必须是完整或简洁","host.fieldsMustBeAnObject":"fields 必须是对象","host.unknownFieldId":"未知字段 id: {key}","host.fieldVisibilityMustBeA":"字段开关必须是布尔值: {key}","host.colorsMustBeAnObject":"colors 必须是对象","host.colorMustBeAPreset":"颜色只接受预设色名或 #RRGGBB: {key}","field.anchorGroup.label":"服务商与模型","field.anchorGroup.note":"显示当前会话使用的服务商和模型。","field.subServiceGroup.label":"订阅服务与模型","field.subServiceGroup.note":"显示订阅服务和当前模型或套餐。","field.billingServiceGroup.label":"账单服务与模型","field.billingServiceGroup.note":"显示账单服务和当前模型。","field.customText.label":"自定义文字","field.customText.note":"显示一段自定义文字,可用作备注或签名。","field.mainTime.label":"主时间","field.mainTime.note":"显示主时区的时间;时区与格式在「时间与日期」中调整。","field.worldTime.label":"世界时间","field.worldTime.note":"显示另一时区的时间;格式与主时间相同。","field.sessionCost.label":"本次会话花费","field.sessionCost.note":"显示本次会话的实际花费,包含子代理。","field.balance.note":"显示账户余额;余额过低时会标红。","field.period.label":"当前时段","field.period.note":"显示当前是高峰还是空闲时段。","field.countdown.label":"下次价格切换","field.countdown.note":"显示距离下次价格切换还有多久。","field.expiry.label":"订阅到期日","field.expiry.note":"服务商提供到期时间时显示。","field.subWindow5h.label":"5 小时额度","field.subWindow5h.note":"显示滚动 5 小时额度的剩余比例。","field.subWindowWeek.label":"每周额度","field.subWindowWeek.note":"显示每周额度的剩余比例。","field.subWindowMonth.label":"每月额度","field.subWindowMonth.note":"显示每月额度的剩余比例。","field.resetCountdown.label":"距额度重置","field.resetCountdown.note":"显示当前额度何时重置。","field.subBalance.label":"可用余额或剩余额度","field.subBalance.note":"按量账户显示可用余额;没有额度窗口的 credits 订阅显示剩余额度。","field.billingSpend.label":"本月用量","field.billingSpend.note":"显示本计费周期的实际用量或花费。","field.budget.label":"预算使用情况","field.budget.note":"服务商支持时显示已使用的预算比例。","field.freeQuota.label":"免费额度","field.freeQuota.note":"服务商明确提供时显示免费额度和重置时间。","field.turnsSteps.label":"轮次与步数","field.turnsSteps.note":"显示对话的轮次和步数,只在完整模式出现。","field.llmTime.label":"模型耗时","field.llmTime.note":"显示模型推理耗时,只在完整模式出现。","field.toolTime.label":"工具耗时","field.toolTime.note":"显示工具调用耗时,只在完整模式出现。","field.cacheHit.note":"显示提示词缓存命中率,只在完整模式出现。","field.tokensIO.label":"输入/输出 Token","field.tokensIO.note":"显示本次会话的输入和输出用量,只在完整模式出现。","field.contextUsage.label":"上下文用量","field.contextUsage.note":"显示上下文占用情况,只在完整模式出现。","ui.contextAria":"上下文已用 {percent}","ui.contextUsed":"上下文已用","ui.contextSystem":"系统提示词","ui.contextTools":"工具定义","ui.contextMessages":"对话消息","ui.contextFigures":"~{used} / {window}","number.thousand":"{value}K","number.million":"{value}M","field.unmapped.label":"暂不支持提示","field.unmapped.note":"当前服务商暂不支持余额查询时显示。","field.noKeyHint.label":"缺少密钥提示","field.noKeyHint.note":"缺少 API Key 时显示设置提示。","field.balanceError.label":"余额更新失败提示","field.balanceError.note":"余额更新失败时显示。","field.usageError.label":"用量更新失败提示","field.usageError.note":"花费暂时不可用时显示。","field.refreshFailure.label":"更新失败提示","field.refreshFailure.note":"任一数据更新失败时显示提醒。","field.persistWarning.label":"记录未保存提醒","field.persistWarning.note":"账单保存失败时显示提醒,建议保留。","field.updateNotice.label":"更新提醒","field.updateNotice.note":"有新版本可下载、或新版已下载待重启时显示短标记。","field.updateFailure.label":"更新失败提醒","field.updateFailure.note":"自动更新失败时显示短标记。","group.native":"原生信息","group.plugin":"插件信息","group.notice":"提醒信息","group.native.desc":"DeepSeek 原生底栏原有的字段,只在完整模式显示。开关只决定开和关,与模式不冲突。","group.plugin.desc":"本插件新增的字段(含接管过来的上下文圆环),简洁模式与完整模式都会显示。","group.notice.desc":"更新、失败等一次性提醒。可整组关掉,不影响上面的信息显示。","ui.listSeparator":"、","ui.sentenceEnd":"。","ui.fieldErrorPrefix":"「{label}」:","ui.turnCount":"{count} 轮","ui.turnCountPlural":"{count} 轮","ui.stepCount":"{count} 步","ui.stepCountPlural":"{count} 步","ui.mainTimeZone":"主时间时区","ui.worldTimeZone":"世界时间时区","ui.timeFormat":"时间格式","ui.year":"年","ui.month":"月","ui.day":"日","ui.hour":"时","ui.minute":"分","ui.second":"秒","ui.customTextTitle":"自定义文本","ui.customTextPlaceholder":"输入文字(最多 64 个字符)","ui.searchPlaceholder":"搜索内容…","ui.searchFieldsLabel":"搜索显示内容","ui.searchResultCount":"找到 {count} 项","ui.enabledFieldsCount":"已启用 {count} 项","ui.noSearchResults":"没有找到匹配内容","ui.timePreview":"预览:","ui.mainTime":"主时间","ui.worldTime":"世界时间","ui.customText":"自定义文本"},"en":{"meta.title":"Bottom Info Bar","meta.description":"Shows the current model, balance and spend under the composer.","ui.requestTimedOut":"Request timed out","ui.requestCanceled":"Request canceled","ui.couldNotParseResponse":"Could not parse response","ui.rpcFailed":"RPC failed","ui.refreshFailed":"Refresh failed","color.red":"Red","color.green":"Green","color.blue":"Blue","color.purple":"Purple","color.orange":"Orange","color.neutral":"Neutral","mode.balance":"Balance","mode.subscription":"Subscription","mode.billing":"Billing","mode.native":"Native stats","mode.common":"General","ui.nativeStatsField":"Native stats field","ui.shownIn":"Shown in: ","ui.pleaseTryAgainLater":"Please try again later","ui.restoreDefaultColor":"Restore default color","ui.infoBarSettings":"Info Bar","ui.settingsAreTemporarilyUnavailable":"Settings are temporarily unavailable","ui.loadingInfoBarSettings":"Loading settings…","ui.couldNotLoadInfoBar":"Could not load Info Bar settings: ","ui.changesAppliedButCouldNot":"Applied, but could not save locally: ","ui.unknownReason":"Unknown reason","ui.couldNotSave":"{errorPrefix}Could not save: {value}","ui.color":"\"{value}\" color: ","ui.hasAnInvalidColorEnter":"\"{value}\" has an invalid color. Enter #RRGGBB (for example, #0044CC).","ui.defaultColorsRestored":"Default colors restored","ui.defaultLabelsRestored":"Default display restored","ui.couldNotReset":"Could not reset: {value}","ui.identifiesTheProviderAndModel":"Shows the provider and model used in this conversation.","ui.show":"Show {label}","ui.clickToHide":"Click to hide","ui.clickToShow":"Click to show","ui.presetColor":"{label} preset color","ui.customColor":"{label} custom color","ui.customColorOpenColorPicker":"Custom color (open color picker)","ui.customColorItem":"Custom…","ui.colorSwatchLabel":"{label} color","ui.hexColor":"{label} hex color","ui.expand":"Expand","ui.collapse":"Collapse","ui.saving":"Saving…","ui.processing":"Working…","ui.visibleFields":"Information to show","ui.timeDateTitle":"Time and date","ui.timeDateDesc":"Time zones and display format for main and world time.","ui.customTextSectionDesc":"Shown in the Info Bar.","ui.resetConfirmTitle":"Reset Info Bar settings","ui.resetConfirmDescFields":"Restore all display choices. Custom colors are not affected and the change applies right away.","ui.resetConfirmDescColors":"All custom colors will be cleared and defaults restored. Field switches are not affected. This applies to the Info Bar right away.","ui.resetConfirmAcknowledge":"I understand this reset cannot be undone","ui.resetConfirmCancel":"Cancel","ui.resetConfirmConfirm":"Reset","ui.resetLabels":"Restore display","ui.resetColors":"Restore colors","ui.resetRowTitle":"Reset to defaults","ui.resetRowDesc":"Restore displayed items or colors.","ui.customTextCount":"{value}/64 characters","ui.couldNotDisplayInfoBar":"Could not display Info Bar settings: {value}","ui.dataAndBilling":"Billing data","ui.dataAndBillingDesc":"Export or clear records.","ui.exportBillingRecords":"Export billing","ui.exportBillingRecordsDesc":"Export as CSV or JSON.","ui.exportBillingCsv":"Export CSV","ui.exportBillingJson":"Export JSON","ui.clearBillingRecords":"Clear billing","ui.clearBillingRecordsDesc":"This cannot be undone.","ui.clearBillingRecordsConfirm":"Clear all billing records saved by this plugin? Export first. This cannot be undone, but settings and sign-in information will stay unchanged.","ui.exportedBillingRecords":"Exported {count} billing records ({format})","ui.noBillingRecordsToExport":"There are no billing records to export.","ui.exportFailed":"Export failed: {value}","ui.exportIncomplete":"Some historical billing records could not be read: {value}. Nothing was exported; try again later.","ui.clearFailed":"Could not clear billing records: {value}","ui.clearFailedWithoutDetails":"Billing records could not be fully cleared","ui.clearedBillingRecords":"Cleared {count} billing records","ui.clearCanceled":"Canceled","ui.exportNotSupported":"File downloads are not supported in this environment.","ui.infoBar":"Info Bar","ui.weekly":"Weekly","ui.monthly":"Monthly","ui.window":"Window","ui.quotaDisplayUsed":"Used","ui.quotaDisplayRemaining":"Remaining","ui.quotaDisplayModeTitle":"Subscription window percentage","ui.quotaDisplayModeDesc":"Show subscription quota windows as remaining or used. The warning threshold always follows the remaining 20% rule.","ui.windowUsedRemaining":"{label} window: used {usedPercent}% (remaining {value}%)","ui.windowUsedRemainingResets":"{label} window: used {usedPercent}% (remaining {value}%) · Resets {value4}","ui.minimax":"MiniMax","ui.supportsImageInput":"Supports image input.","ui.vision":"Vision","ui.unknown":"Unknown","ui.unknownModel":"Unknown model","ui.modelSelectionPending":"Reading current model","ui.modelCapabilityPending":"Checking image-input support","ui.pluginVersion":"\nPlugin version: {current}","ui.provider":"Provider: {provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"Pricing: peak/off-peak (Beijing time; weekday peaks 09:00-12:00 and 14:00-18:00; weekends off-peak)","ui.pricingFixed":"Pricing: fixed","ui.pricingNotListedUsingDefaults":"Pricing: not listed; spend is not calculated","ui.zhipu":"Zhipu","ui.xiaomiMiMo":"Xiaomi MiMo","ui.commandCode":"Command Code","ui.subscription":"Subscription","ui.cloudBilling":"Cloud billing","ui.plan":"\nPlan: {plan}","ui.expiresLocalTime":"\nExpires: {value} (local time)","ui.subscriptionServiceModel":"Subscription service: {serviceName}\nModel: {rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"This provider's balance is not available yet.","ui.notSupported":"Not supported","ui.notConfigured":"Not configured: ","ui.notConfiguredConfigureItIn":"Not configured: {credName}. Configure it in Settings → Models.","ui.notConfiguredSettingsModels":"{credName} is not configured. Add it in Settings → Models.","ui.estimatedBalance":"Estimated balance: {symbol}{value}","ui.balance":"Balance: {symbol}{value}","ui.balance.pushBalanceGroups":"Balance","ui.low":"Low","ui.estimated":"(estimated)","ui.balanceIsTemporarilyUnavailableShowing":"Balance is temporarily unavailable. Showing the last data and retrying automatically.","ui.couldNotLoadBalanceCheck":"Could not load balance. Check your connection and API key.","ui.balanceUnavailable":"Balance unavailable","ui.beijingTime":"Beijing time: ","ui.peakPrice":"Peak price","ui.offPeakPrice":"Off-peak price","ui.input":": input ¥","ui.mCachedInput":"/M · cached input ¥","ui.mOutput":"/M · output ¥","ui.beijingTimeSwitchesTo":"Beijing time: {atLabel} switches to ","ui.until":"Until ","ui.offPeak":"off-peak","ui.peak":"peak","ui.today":"Today {symbol}{value}","ui.lastDays":"Last 30 days {symbol}{value}","ui.allTime":"All time {symbol}{value}","ui.sessionIncludingSubagents":"Session {costTxt} (including subagents){value}","ui.session":"Session","ui.spendIsTemporarilyUnavailableChat":"Spend is temporarily unavailable. Chat is unaffected.","ui.spendUnavailable":"Spend unavailable","ui.noSignInCredentialsFound":"No sign-in credentials found for {serviceName}. Please reauthorize.","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} credentials have expired. Please reauthorize.","ui.deniedAccessReauthorizeOrTry":"{serviceName} denied access. Reauthorize or try again later.","ui.rateLimitReachedPleaseTry":"{serviceName} rate limit reached. Please try again later.","ui.timedOutCheckYourConnection":"{serviceName} timed out. Check your connection and try again.","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} returned an unrecognized response. Please try again later.","ui.isTemporarilyUnavailableCheckYour":"{serviceName} is temporarily unavailable. Check your connection and try again.","ui.subscriptionExpiresLocalTime":"Subscription expires: {value} (local time)","ui.expires":"Expires","ui.subscriptionSource":"Subscription source: {value} (","ui.prepaidBalance":"Available balance","ui.availableBalanceLabel":"Available balance","ui.remainingCredits":"Credits remaining","ui.availableBalance":"Available balance: {balTxt}","ui.availableCredits":"Credits remaining: {value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"Subscription source: {value}","ui.windowRemainingUsed":"{label} window: remaining {value}% (used {usedPercent}%)","ui.resetsResetsIn":" · Resets {value} · Resets in {value2}","ui.windowRemainingUsedResets":"{label} window: remaining {value}% (used {usedPercent}%) · Resets {value4}","ui.resetsIn":"Resets in","ui.configureItInSettingsModels":". Configure it in Settings → Models.","ui.deniedAccessTheTokenMay":"{serviceName} denied access: the token may lack billing read permissions.","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} billing is temporarily unavailable. Check your connection and permissions, then try again.","ui.billingSource":"Billing source: {value}","ui.thisMonthSSpend":"This month’s spend: {symbol}{value}","ui.budgetUsed":"Budget used: {value}%","ui.dailyFreeQuotaRemaining":"Daily free quota remaining: {value}","ui.thisMonth":"This month","ui.thisMonthSUsage":"This month’s usage","ui.budget":"Budget","ui.free":"Free","ui.resetsIn.pushBillingGroups":"{value} · Resets in {value2}","ui.billingServiceModel":"Billing service: {serviceName}\nModel: {modelLabel}{versionLine}","ui.pricing":"Pricing","ui.spend":"Spend","ui.mode":"Mode","ui.subscriptionQuota":"Subscription quota","ui.billing":"Billing","ui.temporarilyUnavailableKeepingTheLast":": temporarily unavailable. Keeping the last data and retrying automatically.","ui.spendJournalSavedButThe":"Spend journal saved, but the readable ledger has not been updated: ","ui.thisSpendRecordWasNot":"This spend record was not saved and will not be added to totals: ","ui.ledgerUpdatePending":"Ledger update pending","ui.spendNotSaved":"Spend not saved","ui.askYourAgentToUpdate":"Plugin version {latest} is available. Update it from plugin management on desktop.","ui.updateAvailable":"Update available","ui.updateCommandCopied":"Update command copied","ui.versionAndUpdateTitle":"Version and updates","ui.versionAndUpdateDesc":"The plugin replaces its own files with a new version; restart DSH to activate. Only the plugin's own files change, so billing records and settings are untouched.","ui.versionUnavailable":"The DSH currently running has not loaded this version of the plugin yet, so only this line can be shown here. Restart DSH and the running version, the update mode and the check button will appear.","ui.versionRunning":"Running version {version}","ui.versionLatest":"Latest version {version}","ui.versionUnknown":"Unknown","ui.autoUpdateTitle":"Update method","ui.autoUpdateDesc":"Automatic: checks and downloads new versions on startup, then applies after you restart DSH. Manual: only checks, and downloads when you press Check now.","ui.updateModeAuto":"Automatic","ui.updateModeManual":"Manual","ui.autoUpdateOn":"On","ui.autoUpdateOff":"Off","ui.updateCheckNow":"Check now","ui.updateChecking":"Checking…","ui.updateRetrying":"Retry","ui.updateUpToDate":"Up to date.","ui.updateDisabled":"Automatic updates are unavailable here (only enabled when the plugin is loaded as a profile plugin).","ui.updatePendingRestart":"Updated to {version}. Restart DSH to activate.","ui.updateInProgress":"Version {version} found; updating automatically…","ui.updateAutoOffPending":"Version {version} is available. Updates are set to manual.","ui.updateFailed":"Last automatic update failed: {error}","ui.updateRollback":"Roll back to previous version","ui.updateRolledBack":"Rolled back to the previous version {version}.","ui.updateHeld":"Version {version} is held back and will not be installed automatically.","ui.updateAllowHeld":"Allow updating to {version}","ui.updateRestartBadge":"Restart to apply","ui.updateFailedBadge":"Update failed","ui.tools":"Tools","ui.avgTTFT":"Avg. TTFT","ui.cacheHit":"Cache hit","ui.input.BottomInfoBar":"Input","ui.output":"Output","ui.savingView":"Saving view…","ui.clickToSwitchFullCompact":"Click to switch full/compact view","ui.pressEnterOrSpaceFor":"Press Enter or Space for compact view.","ui.pressEnterOrSpaceFor.BottomInfoBar":"Press Enter or Space for full view.","host.hour":"5-hour","host.unknownProvider":"Unknown provider","error.subscription.request-failed":"Unexpected subscription quota request failure","host.settingsFileCouldNotBe":"Settings file could not be saved","host.everydayQuestions":"Everyday questions","host.mediumCodingTask":"Medium coding task","host.longDocumentAnalysisCodeReview":"Long document analysis / code review","host.largeProjectRefactorAcrossMultiple":"Large project refactor across multiple turns","host.subagentWorkflow":"Subagent workflow","error.settings.save-failed":"Could not save settings.json: {value}","error.balance.credentials":"Could not read credentials","error.balance.not-configured":"Not configured: {credential}","error.request.http":"Request failed: HTTP {status}.","error.request.parse":"Unexpected response format","error.subscription.not-connected":"ChatGPT subscription is not connected: credentials not found in ~/.codex/auth.json. Install dsh-chatgpt-subscription and sign in.","error.subscription.credentials-missing":"ChatGPT subscription credentials are missing id_token. Install dsh-chatgpt-subscription and reauthorize.","error.subscription.opencode-not-configured":"OpenCode Go is not configured. Set OPENCODE_GO_API_KEY or use opencode auth.json.","error.subscription.commandcode-not-configured":"Command Code is not configured. Set COMMAND_CODE_API_KEY, CMD_API_KEY, or sign in with the Command Code CLI.","error.subscription.commandcode-auth-failed":"Command Code credentials were rejected. Sign in again or update the API key.","error.subscription.commandcode-unrecognized":"Command Code returned an unrecognized quota format; the previous snapshot was kept.","host.zhipu":"Zhipu {mapped}","host.zhipu.parseZaiQuota":"Zhipu {value}{value2}","error.subscription.zhipu-not-configured":"Zhipu API key is not configured. Set ZAI_API_KEY or ZAI_CODING_CN_API_KEY.","error.subscription.zhipu-auth-failed":"Zhipu API authentication failed: the key is expired or invalid.","error.subscription.zhipu-unrecognized":"Zhipu returned an unrecognized quota format (the API may have changed); the last known data is kept.","error.request.failed":"Request failed: {value} {msg}.","error.subscription.xiaomi-not-configured":"Xiaomi MiMo Token Plan credentials are not configured: {credName} or XIAOMI_API_KEY.","error.subscription.xiaomi-http":"Request failed: HTTP {value}.","error.subscription.minimax-not-configured":"MiniMax is not configured. Set MINIMAX_API_KEY or MINIMAX_CN_API_KEY. Token Plan queries require a Subscription Key; pay-as-you-go API keys are rejected by the server.","error.subscription.minimax-auth-failed":"MiniMax rejected the API key: Token Plan queries require a Subscription Key (generated on the Token Plan subscription page). Pay-as-you-go keys are rejected (status_code=1004 / HTTP 401).","error.subscription.minimax-unrecognized":"MiniMax returned an unrecognized quota format (the API may have changed); the last known data is kept.","error.billing.together-not-configured":"Not configured: TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"Actual monthly bill from the Together Usage API","error.billing.fireworks-not-configured":"Not configured: FIREWORKS_API_KEY","error.billing.fireworks-account":"Could not read account (account_id missing)","host.actualBillForThisPeriod":"Actual bill for this period (Fireworks Billing Summary)","host.actualUsageForThisPeriod":"Actual usage for this period (billingUsage fallback; no spend amount)","error.billing.aws-not-configured":"Not configured: AWS credentials (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","error.billing.aws-http":"Request failed: HTTP {status}. The token may lack ce:GetCostAndUsage permission","host.actualMonthlyBillFromAWS":"Actual monthly bill from AWS Cost Explorer (about 24 hours behind)","error.billing.cloudflare-not-configured":"Not configured: CLOUDFLARE_API_KEY (requires an account-level token with Billing read permission)","error.billing.cloudflare-account":"Not configured: CLOUDFLARE_ACCOUNT_ID","error.billing.cloudflare-http":"Request failed: HTTP {status}. The token requires Billing read permission","host.actualMonthlyUsageFromThe":"Actual monthly usage from the Cloudflare Billable Usage API (Alpha)","error.billing.request-failed":"Unexpected billing request failure","host.spendSummaryFileMissingArchived":"Spend summary file missing: archived amounts are not included in displayed totals. Details remain in usage-archive/ for manual recovery.","host.spendLedgerCouldNotBe":"Spend ledger could not be saved","host.usageLedgerBusy":"A response is still in progress. Clear billing records after it finishes.","error.ledger.clear-failed":"Billing records could not be fully cleared: {value}","error.ledger.snapshot-stale":"Could not save archived spend summaries: {value}","host.basedOnYourLastSessions":"Based on your last {count} sessions","host.estimatedFromSpendingOverThe":"Estimated from spending over the last {SPEND_DAYS} days","host.yourTypicalSession":"Your typical session","host.patchMustIncludeFieldsOr":"patch must include fields, colors, timeFormat, timeZones or customText","host.timeFormatMustBeAnObject":"timeFormat must be an object","host.timeFieldMustBeABoolean":"Time format field must be a boolean: {key}","host.timeZonesMustBeAnObject":"timeZones must be an object","host.timeZoneMustBeAValid":"Time zone must be a valid IANA zone: {key}","host.customTextMustBeAString":"Custom text must be a string","host.customTextTooLong":"Custom text must not exceed 64 characters","host.quotaDisplayModeInvalid":"Subscription window percentage mode must be \"used\" or \"remaining\"","host.infoDensityMustBeFullOrCompact":"Information display must be full or compact","host.fieldsMustBeAnObject":"fields must be an object","host.unknownFieldId":"Unknown field id: {key}","host.fieldVisibilityMustBeA":"Field visibility must be a boolean: {key}","host.colorsMustBeAnObject":"colors must be an object","host.colorMustBeAPreset":"Color must be a preset name or #RRGGBB: {key}","field.anchorGroup.label":"Provider and model","field.anchorGroup.note":"Shows the provider and model used in this conversation.","field.subServiceGroup.label":"Subscription service and model","field.subServiceGroup.note":"Shows the subscription service and current model or plan.","field.billingServiceGroup.label":"Billing service and model","field.billingServiceGroup.note":"Shows the billing service and current model.","field.customText.label":"Custom text","field.customText.note":"Shows custom text for a note or signature.","field.mainTime.label":"Main time","field.mainTime.note":"Shows the main time zone; adjust zone and format in Time and date.","field.worldTime.label":"World time","field.worldTime.note":"Shows another time zone; it shares the main time format.","field.sessionCost.label":"Session spend","field.sessionCost.note":"Shows actual spend for this session, including subagents.","field.balance.note":"Shows the account balance; a low balance appears in red.","field.period.label":"Current period","field.period.note":"Shows whether the current period is peak or off-peak.","field.countdown.label":"Next price change","field.countdown.note":"Shows the time until the next price change.","field.expiry.label":"Subscription expiry","field.expiry.note":"Shown when the provider supplies an expiry date.","field.subWindow5h.label":"5-hour quota","field.subWindow5h.note":"Shows the remaining rolling 5-hour quota.","field.subWindowWeek.label":"Weekly quota","field.subWindowWeek.note":"Shows the remaining weekly quota.","field.subWindowMonth.label":"Monthly quota","field.subWindowMonth.note":"Shows the remaining monthly quota.","field.resetCountdown.label":"Quota reset","field.resetCountdown.note":"Shows when the current quota resets.","field.subBalance.label":"Available balance or credits","field.subBalance.note":"Shows available balance for pay-as-you-go accounts, or remaining credits when a subscription has no visible quota window.","field.billingSpend.label":"Monthly usage","field.billingSpend.note":"Shows actual usage or spend for the current billing period.","field.budget.label":"Budget status","field.budget.note":"Shown when the provider reports a budget percentage.","field.freeQuota.label":"Free quota","field.freeQuota.note":"Shown when the provider reports a free allowance and reset time.","field.turnsSteps.label":"Turns and steps","field.turnsSteps.note":"Shows conversation turns and steps. Full mode only.","field.llmTime.label":"Model time","field.llmTime.note":"Shows model inference time. Full mode only.","field.toolTime.label":"Tool time","field.toolTime.note":"Shows tool execution time. Full mode only.","field.cacheHit.note":"Shows the prompt cache hit rate. Full mode only.","field.tokensIO.label":"Input / output tokens","field.tokensIO.note":"Shows this session’s input and output usage. Full mode only.","field.contextUsage.label":"Context usage","field.contextUsage.note":"Shows context usage. Full mode only.","ui.contextAria":"{percent} of context used","ui.contextUsed":"Context used","ui.contextSystem":"System prompt","ui.contextTools":"Tool definitions","ui.contextMessages":"Conversation messages","ui.contextFigures":"~{used} / {window}","number.thousand":"{value}K","number.million":"{value}M","field.unmapped.label":"Unsupported hint","field.unmapped.note":"Shown when balance lookup is not supported.","field.noKeyHint.label":"Missing key hint","field.noKeyHint.note":"Shown when an API key is missing.","field.balanceError.label":"Balance update error","field.balanceError.note":"Shown when balance update fails.","field.usageError.label":"Usage update error","field.usageError.note":"Shown when spend data is temporarily unavailable.","field.refreshFailure.label":"Update error","field.refreshFailure.note":"Shown when any data update fails.","field.persistWarning.label":"Unsaved record alert","field.persistWarning.note":"Shown when billing records cannot be saved; recommended.","field.updateNotice.label":"Update notice","field.updateNotice.note":"Shows a short marker when a new version is available or a downloaded version is waiting for a restart.","field.updateFailure.label":"Update failure notice","field.updateFailure.note":"Shows a short marker when the automatic update failed.","group.native":"Native information","group.plugin":"Plugin information","group.notice":"Notices","group.native.desc":"Fields the DeepSeek bar already had. Full mode only. The switch only turns a field on or off; it never changes the mode.","group.plugin.desc":"Fields this plugin adds, the adopted context ring included. Shown in both compact and full mode.","group.notice.desc":"One-off notices such as updates and failures. Turn the whole list off without touching the information above.","ui.listSeparator":", ","ui.sentenceEnd":".","ui.fieldErrorPrefix":"\"{label}\": ","ui.turnCount":"{count} turn","ui.turnCountPlural":"{count} turns","ui.stepCount":"{count} step","ui.stepCountPlural":"{count} steps","ui.mainTimeZone":"Main time zone","ui.worldTimeZone":"World time zone","ui.timeFormat":"Time format","ui.year":"Year","ui.month":"Month","ui.day":"Day","ui.hour":"Hour","ui.minute":"Minute","ui.second":"Second","ui.customTextTitle":"Custom text","ui.customTextPlaceholder":"Enter text (up to 64 characters)","ui.searchPlaceholder":"Search content…","ui.searchFieldsLabel":"Search visible content","ui.searchResultCount":"{count} found","ui.enabledFieldsCount":"{count} enabled","ui.noSearchResults":"No matching content","ui.timePreview":"Preview: ","ui.mainTime":"Main time","ui.worldTime":"World time","ui.customText":"Custom text"}};
62
+ const LOCALES = {"zh":{"meta.title":"底部信息栏","meta.description":"在输入框下方显示当前模型、余额和花费。","ui.requestTimedOut":"请求超时","ui.requestCanceled":"请求已取消","ui.couldNotParseResponse":"响应解析失败","ui.rpcFailed":"RPC 失败","ui.refreshFailed":"刷新失败","color.red":"红","color.green":"绿","color.blue":"蓝","color.purple":"紫","color.orange":"橙","color.neutral":"中性","mode.balance":"余额制","mode.subscription":"订阅制","mode.billing":"账单制","mode.native":"原生统计行","mode.common":"通用","ui.nativeStatsField":"原生统计行字段","ui.shownIn":"出现于","ui.pleaseTryAgainLater":"请稍后再试","ui.restoreDefaultColor":"恢复默认颜色","ui.infoBarSettings":"信息栏","ui.settingsAreTemporarilyUnavailable":"暂时无法读取设置","ui.loadingInfoBarSettings":"正在加载设置…","ui.couldNotLoadInfoBar":"无法读取信息栏设置:","ui.changesAppliedButCouldNot":"已应用,但无法保存到本地:","ui.unknownReason":"未知原因","ui.couldNotSave":"{errorPrefix}保存失败:{value}","ui.color":"“{value}”颜色:","ui.hasAnInvalidColorEnter":"“{value}”的颜色格式不正确,请输入 #RRGGBB(例如 #0044CC)。","ui.defaultColorsRestored":"已恢复默认颜色","ui.defaultLabelsRestored":"已恢复默认显示设置","ui.couldNotReset":"重置失败:{value}","ui.identifiesTheProviderAndModel":"显示当前会话使用的服务商和模型。","ui.show":"显示{label}","ui.clickToHide":"点击隐藏","ui.clickToShow":"点击显示","ui.presetColor":"{label}的预设颜色","ui.customColor":"{label}的自定义颜色","ui.customColorOpenColorPicker":"自定义颜色(打开系统取色器)","ui.customColorItem":"自定义…","ui.colorSwatchLabel":"{label}的颜色","ui.hexColor":"{label}的十六进制颜色","ui.expand":"展开","ui.collapse":"收起","ui.saving":"正在保存…","ui.processing":"正在处理…","ui.visibleFields":"要显示的信息","ui.timeDateTitle":"时间与日期","ui.timeDateDesc":"主时间和世界时间的时区与显示格式。","ui.customTextSectionDesc":"显示在信息栏中。","ui.resetConfirmTitle":"重置信息栏设置","ui.resetConfirmDescFields":"将恢复所有显示项;自定义颜色不受影响,改动会直接生效。","ui.resetConfirmDescColors":"将清除全部自定义颜色并恢复默认配色,字段显示开关不受影响。该操作会直接应用到信息栏。","ui.resetConfirmAcknowledge":"我了解这次重置不可撤销","ui.resetConfirmCancel":"取消","ui.resetConfirmConfirm":"确认重置","ui.resetLabels":"恢复显示","ui.resetColors":"恢复颜色","ui.resetRowTitle":"恢复默认","ui.resetRowDesc":"恢复显示项或颜色。","ui.customTextCount":"已输入 {value}/64 个字符","ui.couldNotDisplayInfoBar":"无法显示信息栏设置:{value}","ui.dataAndBilling":"账单数据","ui.dataAndBillingDesc":"导出或清除记录。","ui.exportBillingRecords":"导出账单","ui.exportBillingRecordsDesc":"导出为 CSV 或 JSON。","ui.exportBillingCsv":"导出 CSV","ui.exportBillingJson":"导出 JSON","ui.clearBillingRecords":"清除账单","ui.clearBillingRecordsDesc":"清除后无法恢复。","ui.clearBillingRecordsConfirm":"确定清除本插件保存的全部账单记录吗?建议先导出。清除后无法恢复,但不会影响设置或登录信息。","ui.exportedBillingRecords":"已导出 {count} 条账单记录({format})","ui.noBillingRecordsToExport":"目前没有可导出的账单记录。","ui.exportFailed":"导出失败:{value}","ui.exportIncomplete":"历史账单读取不完整:{value}。本次未导出,请稍后再试。","ui.clearFailed":"清除失败:{value}","ui.clearFailedWithoutDetails":"账单记录未能全部清除","ui.clearedBillingRecords":"已清除 {count} 条账单记录","ui.clearCanceled":"已取消","ui.exportNotSupported":"当前环境不支持下载文件。","ui.infoBar":"信息栏","ui.weekly":"周","ui.monthly":"月","ui.window":"窗口","ui.quotaDisplayUsed":"已用","ui.quotaDisplayRemaining":"剩余","ui.quotaDisplayModeTitle":"订阅窗口百分比方向","ui.quotaDisplayModeDesc":"订阅额度窗口按剩余或已用显示;告警阈值始终按剩余 20% 判定。","ui.windowUsedRemaining":"{label}窗口:已用 {usedPercent}%(剩余 {value}%)","ui.windowUsedRemainingResets":"{label}窗口 已用 {usedPercent}%(剩余 {value}%) · 重置 {value4}","ui.minimax":"MiniMax","ui.supportsImageInput":"支持图像输入。","ui.vision":"视觉","ui.unknown":"未知","ui.unknownModel":"未知模型","ui.modelSelectionPending":"正在读取当前模型","ui.modelCapabilityPending":"正在确认是否支持图像输入","ui.pluginVersion":"\n插件版本:{current}","ui.provider":"服务商:{provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"定价:峰谷价(工作日高峰 9-12、14-18 点;周末全天空闲)","ui.pricingFixed":"定价:固定价","ui.pricingNotListedUsingDefaults":"定价:暂未收录,不计算花费","ui.zhipu":"智谱","ui.xiaomiMiMo":"小米 MiMo","ui.commandCode":"Command Code","ui.subscription":"订阅","ui.cloudBilling":"云账单","ui.plan":"\n套餐:{plan}","ui.expiresLocalTime":"\n到期:{value}(本地时区)","ui.subscriptionServiceModel":"订阅服务:{serviceName}\n模型:{rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"暂时无法读取该服务商的余额。","ui.notSupported":"未适配","ui.notConfigured":"未配置 ","ui.notConfiguredConfigureItIn":"未配置 {credName}。请在\"设置 → 模型\"中填写。","ui.notConfiguredSettingsModels":"未配置 {credName}。请在设置 → 模型中填写。","ui.estimatedBalance":"估算余额:{symbol}{value}","ui.balance":"余额:{symbol}{value}","ui.balance.pushBalanceGroups":"余额","ui.low":"低","ui.estimated":"(估算)","ui.balanceIsTemporarilyUnavailableShowing":"余额暂不可用;正在显示上次数据并自动重试。","ui.couldNotLoadBalanceCheck":"余额获取失败。请检查网络和 API Key。","ui.balanceUnavailable":"余额获取失败","ui.beijingTime":"北京时间 ","ui.peakPrice":"高峰价","ui.offPeakPrice":"空闲价","ui.input":":输入 ¥","ui.mCachedInput":"/M · 缓存 ¥","ui.mOutput":"/M · 输出 ¥","ui.beijingTimeSwitchesTo":"北京时间 {atLabel} 切换为","ui.until":"距","ui.offPeak":"空闲","ui.peak":"高峰","ui.today":"今天 {symbol}{value}","ui.lastDays":"近一月 {symbol}{value}","ui.allTime":"全部 {symbol}{value}","ui.sessionIncludingSubagents":"本会话 {costTxt}(含子代理){value}","ui.session":"本会话","ui.spendIsTemporarilyUnavailableChat":"花费暂不可用;不会影响对话。","ui.spendUnavailable":"花费获取失败","ui.noSignInCredentialsFound":"未找到 {serviceName} 登录凭证。请重新授权。","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} 登录凭证已失效。请重新授权。","ui.deniedAccessReauthorizeOrTry":"{serviceName} 拒绝访问。请重新授权或稍后再试。","ui.rateLimitReachedPleaseTry":"{serviceName} 请求过于频繁。请稍后再试。","ui.timedOutCheckYourConnection":"{serviceName} 响应超时。请检查网络后再试。","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} 返回的数据暂时无法识别。请稍后再试。","ui.isTemporarilyUnavailableCheckYour":"{serviceName} 暂不可用。请检查网络后再试。","ui.subscriptionExpiresLocalTime":"订阅到期:{value}(本地时区)","ui.expires":"到期","ui.subscriptionSource":"订阅源:{value}(","ui.prepaidBalance":"可用余额","ui.availableBalanceLabel":"可用余额","ui.remainingCredits":"剩余额度","ui.availableBalance":"可用余额:{balTxt}","ui.availableCredits":"剩余 credits:{value}","ui.credits":"积分","ui.subscriptionSource.titleLines":"订阅源:{value}","ui.windowRemainingUsed":"{label}窗口:剩余 {value}%(已用 {usedPercent}%)","ui.resetsResetsIn":" · 重置 {value} · 距重置 {value2}","ui.windowRemainingUsedResets":"{label}窗口 剩余 {value}%(已用 {usedPercent}%) · 重置 {value4}","ui.resetsIn":"距重置","ui.configureItInSettingsModels":"。请在\"设置 → 模型\"中填写。","ui.deniedAccessTheTokenMay":"{serviceName} 拒绝访问:Token 可能缺少账单读取权限。","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} 账单暂不可用。请检查网络与权限后再试。","ui.billingSource":"账单源:{value}","ui.thisMonthSSpend":"本月花费:{symbol}{value}","ui.budgetUsed":"预算使用:{value}%","ui.dailyFreeQuotaRemaining":"每日免费额度剩余:{value}","ui.thisMonth":"本月","ui.thisMonthSUsage":"本月用量","ui.budget":"预算","ui.free":"免费","ui.resetsIn.pushBillingGroups":"{value} · 距重置 {value2}","ui.billingServiceModel":"账单服务:{serviceName}\n模型:{modelLabel}{versionLine}","ui.pricing":"定价","ui.spend":"花费","ui.mode":"模式","ui.subscriptionQuota":"订阅额度","ui.billing":"账单","ui.temporarilyUnavailableKeepingTheLast":"暂不可用;正在保留上次数据并自动重试。","ui.spendJournalSavedButThe":"账单流水已保存,但可直接查看的账单文件暂未更新:","ui.thisSpendRecordWasNot":"本次账单未保存,不会计入金额:","ui.ledgerUpdatePending":"账单待整理","ui.spendNotSaved":"账单未保存","ui.askYourAgentToUpdate":"发现插件新版本 {latest}。桌面端更新请在插件管理中完成。","ui.updateAvailable":"新版本提醒","ui.updateCommandCopied":"更新命令已复制","ui.versionAndUpdateTitle":"版本与更新","ui.versionAndUpdateDesc":"更新只替换插件自己的程序文件,账单记录和设置不受影响。","ui.versionUnavailable":"当前运行的 DSH 还没载入这一版插件的更新功能,所以这里暂时只显示这一行。重启 DSH 后,下面就会出现当前版本、更新方式与「检查更新」。","ui.versionRunning":"运行中版本 {version}","ui.versionLatest":"最新版本 {version}","ui.versionLastCheck":"上次检查:{time}","ui.versionUnknown":"未知","ui.timeJustNow":"刚刚","ui.timeMinutesAgo":"{n} 分钟前","ui.timeHoursAgo":"{n} 小时前","ui.timeDaysAgo":"{n} 天前","ui.autoUpdateTitle":"更新方式","ui.updateModeAutoDesc":"有新版本就自己下载好,你重启一次 DSH 就生效。","ui.updateModeManualDesc":"只提示不下载。你想装的时候点右边的「检查更新」。","ui.updateModeAuto":"全自动更新","ui.updateModeManual":"手动更新","ui.autoUpdateOn":"已开启","ui.autoUpdateOff":"已关闭","ui.updateCheckNow":"检查更新","ui.updateChecking":"检查中…","ui.updateRetrying":"重试","ui.updateUpToDate":"已是最新版本。","ui.updateDisabled":"当前环境不支持自动更新","ui.updateDisabledWhy":"自动更新只在插件装进 DSH 插件目录后可用。这一份是从源码或链接装进来的,升级请用原来的命令。","ui.updatePendingRestart":"已更新到 {version},重启 DSH 后生效。","ui.updateInProgress":"发现新版本 {version},正在自动更新…","ui.updateAutoOffPending":"有新版本 {version},当前是手动更新方式。","ui.updateFailed":"上次更新没成功","ui.updateFallbackWhy":"万一新版有问题,可以退回上一版。","ui.updateHoldWhy":"这个版本被你暂缓过,不会自己装回来。","ui.updateErrorIncompleteDownload":"更新包没下完整(网络中途断了),这次已放弃,你现在用的版本没受影响。","ui.updateErrorIntegrityMismatch":"更新包没通过安全校验,这次已放弃,你现在用的版本没受影响。","ui.updateErrorDownloadFailed":"连不上更新服务器,稍后再试。","ui.updateErrorTooLarge":"更新包大得反常,出于安全已放弃。","ui.updateErrorPayloadMismatch":"更新包的内容和版本号对不上,已放弃。","ui.updateErrorPayloadUnsafe":"更新包里出现了不该有的文件路径,已放弃。","ui.updateErrorCheckFailed":"检查更新没成功,可能是网络问题。","ui.updateErrorUnknown":"更新没成功,详细原因记在更新日志里。","ui.updateRollback":"回滚到上一版","ui.updateRolledBack":"已回滚到上一版 {version}。","ui.updateHeld":"新版 {version} 已被你暂缓,不会自动装回来。","ui.updateAllowHeld":"允许更新到 {version}","ui.updateRestartBadge":"重启生效","ui.updateFailedBadge":"更新失败","ui.tools":"工具调用","ui.avgTTFT":"首 token 平均","ui.cacheHit":"缓存命中","ui.input.BottomInfoBar":"输入","ui.output":"输出","ui.savingView":"正在保存切换…","ui.clickToSwitchFullCompact":"单击切换 完整/简洁","ui.pressEnterOrSpaceFor":"按 Enter 或空格切换为简洁模式。","ui.pressEnterOrSpaceFor.BottomInfoBar":"按 Enter 或空格切换为完整模式。","host.hour":"5 小时","host.unknownProvider":"未知服务商","error.subscription.request-failed":"订阅额度请求未知异常","host.settingsFileCouldNotBe":"设置文件写入未完成","host.everydayQuestions":"日常问答","host.mediumCodingTask":"中等编码任务","host.longDocumentAnalysisCodeReview":"长文档分析/代码审查","host.largeProjectRefactorAcrossMultiple":"大工程重构(多轮)","host.subagentWorkflow":"子代理工作流","error.settings.save-failed":"settings.json 落盘失败:{value}","error.balance.credentials":"凭据读取失败","error.balance.not-configured":"未配置 {credential}","error.request.http":"请求失败(HTTP {status})","error.request.parse":"响应格式异常","error.subscription.not-connected":"未找到 ChatGPT 订阅登录凭证(~/.codex/auth.json),请安装 dsh-chatgpt-subscription 插件绑定","error.subscription.credentials-missing":"ChatGPT 订阅登录凭证缺少 id_token,请安装 dsh-chatgpt-subscription 插件重新绑定","error.subscription.opencode-not-configured":"未配置 OpenCode Go(OPENCODE_GO_API_KEY 或 opencode auth.json)","error.subscription.commandcode-not-configured":"未配置 Command Code(COMMAND_CODE_API_KEY、CMD_API_KEY 或 ~/.commandcode/auth.json)","error.subscription.commandcode-auth-failed":"Command Code 登录凭证已失效。请重新登录或更新 API Key","error.subscription.commandcode-unrecognized":"Command Code 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","host.zhipu":"智谱 {mapped}","host.zhipu.parseZaiQuota":"智谱 {value}{value2}","error.subscription.zhipu-not-configured":"未配置智谱 API Key(ZAI_API_KEY 或 ZAI_CODING_CN_API_KEY)","error.subscription.zhipu-auth-failed":"智谱 API 认证失败(密钥过期或不正确)","error.subscription.zhipu-unrecognized":"智谱返回了无法识别的额度格式(接口可能已变更),已保留上次数据","error.request.failed":"请求失败({value} {msg})","error.subscription.xiaomi-not-configured":"未配置小米 MiMo Token Plan 凭据({credName} 或 XIAOMI_API_KEY)","error.subscription.xiaomi-http":"请求失败(HTTP {value})","error.subscription.minimax-not-configured":"未配置 MiniMax 凭据(MINIMAX_API_KEY 或 MINIMAX_CN_API_KEY)。查询 Token Plan 必须使用 Subscription Key,按量 API Key 会被服务端拒绝","error.subscription.minimax-auth-failed":"MiniMax 拒绝了当前 API Key:查询 Token Plan 必须使用 Subscription Key(在 Token Plan 订阅页生成),按量 API Key 会被服务端拒绝(status_code=1004 / HTTP 401)","error.subscription.minimax-unrecognized":"MiniMax 返回了无法识别的额度格式(接口可能已变更),已保留上次数据","error.billing.together-not-configured":"未配置 TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"本月真实账单(Together 官方 Usage API)","error.billing.fireworks-not-configured":"未配置 FIREWORKS_API_KEY","error.billing.fireworks-account":"账户解析失败(未取得 account_id)","host.actualBillForThisPeriod":"本周期真实账单(Fireworks Billing Summary)","host.actualUsageForThisPeriod":"本周期真实用量(billingUsage 回退端点,无金额)","error.billing.aws-not-configured":"未配置 AWS 凭据(AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","error.billing.aws-http":"请求失败(HTTP {status}),可能缺少 ce:GetCostAndUsage 权限","host.actualMonthlyBillFromAWS":"AWS Cost Explorer 本月真实账单(账单延迟约 24 小时)","error.billing.cloudflare-not-configured":"未配置 CLOUDFLARE_API_KEY(需账号级 Token 并授予 Billing 读权限)","error.billing.cloudflare-account":"未配置 CLOUDFLARE_ACCOUNT_ID","error.billing.cloudflare-http":"请求失败(HTTP {status}),Token 需 Billing 读权限","host.actualMonthlyUsageFromThe":"Cloudflare Billable Usage API(Alpha),本月真实用量","error.billing.request-failed":"账单请求未知异常","host.spendSummaryFileMissingArchived":"账单汇总文件缺失:折叠期间的历史金额暂未计入显示(明细仍在冷归档 usage-archive/,可人工恢复)","host.spendLedgerCouldNotBe":"账单文件写入未完成","host.usageLedgerBusy":"当前仍有回答正在进行,完成后再清除账单记录","error.ledger.clear-failed":"账单记录清理未完成:{value}","error.ledger.snapshot-stale":"折叠汇总落盘失败:{value}","host.basedOnYourLastSessions":"基于你最近 {count} 次会话","host.estimatedFromSpendingOverThe":"基于过去 {SPEND_DAYS} 天消耗速度的估算","host.yourTypicalSession":"你的典型会话","host.patchMustIncludeFieldsOr":"patch 需包含 fields、colors、timeFormat、timeZones 或 customText 之一","host.timeFormatMustBeAnObject":"timeFormat 必须是对象","host.timeFieldMustBeABoolean":"时间格式字段必须是布尔值: {key}","host.timeZonesMustBeAnObject":"timeZones 必须是对象","host.timeZoneMustBeAValid":"时区必须是有效的 IANA 时区: {key}","host.customTextMustBeAString":"自定义文本必须是字符串","host.customTextTooLong":"自定义文本不能超过64字符","host.quotaDisplayModeInvalid":"订阅窗口百分比方向必须是 used 或 remaining","host.infoDensityMustBeFullOrCompact":"信息显示方式必须是完整或简洁","host.fieldsMustBeAnObject":"fields 必须是对象","host.unknownFieldId":"未知字段 id: {key}","host.fieldVisibilityMustBeA":"字段开关必须是布尔值: {key}","host.colorsMustBeAnObject":"colors 必须是对象","host.colorMustBeAPreset":"颜色只接受预设色名或 #RRGGBB: {key}","field.anchorGroup.label":"服务商与模型","field.anchorGroup.note":"显示当前会话使用的服务商和模型。","field.subServiceGroup.label":"订阅服务与模型","field.subServiceGroup.note":"显示订阅服务和当前模型或套餐。","field.billingServiceGroup.label":"账单服务与模型","field.billingServiceGroup.note":"显示账单服务和当前模型。","field.customText.label":"自定义文字","field.customText.note":"显示一段自定义文字,可用作备注或签名。","field.mainTime.label":"主时间","field.mainTime.note":"显示主时区的时间;时区与格式在「时间与日期」中调整。","field.worldTime.label":"世界时间","field.worldTime.note":"显示另一时区的时间;格式与主时间相同。","field.sessionCost.label":"本次会话花费","field.sessionCost.note":"显示本次会话的实际花费,包含子代理。","field.balance.note":"显示账户余额;余额过低时会标红。","field.period.label":"当前时段","field.period.note":"显示当前是高峰还是空闲时段。","field.countdown.label":"下次价格切换","field.countdown.note":"显示距离下次价格切换还有多久。","field.expiry.label":"订阅到期日","field.expiry.note":"服务商提供到期时间时显示。","field.subWindow5h.label":"5 小时额度","field.subWindow5h.note":"显示滚动 5 小时额度的剩余比例。","field.subWindowWeek.label":"每周额度","field.subWindowWeek.note":"显示每周额度的剩余比例。","field.subWindowMonth.label":"每月额度","field.subWindowMonth.note":"显示每月额度的剩余比例。","field.resetCountdown.label":"距额度重置","field.resetCountdown.note":"显示当前额度何时重置。","field.subBalance.label":"可用余额或剩余额度","field.subBalance.note":"按量账户显示可用余额;没有额度窗口的 credits 订阅显示剩余额度。","field.billingSpend.label":"本月用量","field.billingSpend.note":"显示本计费周期的实际用量或花费。","field.budget.label":"预算使用情况","field.budget.note":"服务商支持时显示已使用的预算比例。","field.freeQuota.label":"免费额度","field.freeQuota.note":"服务商明确提供时显示免费额度和重置时间。","field.turnsSteps.label":"轮次与步数","field.turnsSteps.note":"显示对话的轮次和步数,只在完整模式出现。","field.llmTime.label":"模型耗时","field.llmTime.note":"显示模型推理耗时,只在完整模式出现。","field.toolTime.label":"工具耗时","field.toolTime.note":"显示工具调用耗时,只在完整模式出现。","field.cacheHit.note":"显示提示词缓存命中率,只在完整模式出现。","field.tokensIO.label":"输入/输出 Token","field.tokensIO.note":"显示本次会话的输入和输出用量,只在完整模式出现。","field.contextUsage.label":"上下文用量","field.contextUsage.note":"显示上下文占用情况,只在完整模式出现。","ui.contextAria":"上下文已用 {percent}","ui.contextUsed":"上下文已用","ui.contextSystem":"系统提示词","ui.contextTools":"工具定义","ui.contextMessages":"对话消息","ui.contextFigures":"~{used} / {window}","number.thousand":"{value}K","number.million":"{value}M","field.unmapped.label":"暂不支持提示","field.unmapped.note":"当前服务商暂不支持余额查询时显示。","field.noKeyHint.label":"缺少密钥提示","field.noKeyHint.note":"缺少 API Key 时显示设置提示。","field.balanceError.label":"余额更新失败提示","field.balanceError.note":"余额更新失败时显示。","field.usageError.label":"用量更新失败提示","field.usageError.note":"花费暂时不可用时显示。","field.refreshFailure.label":"更新失败提示","field.refreshFailure.note":"任一数据更新失败时显示提醒。","field.persistWarning.label":"记录未保存提醒","field.persistWarning.note":"账单保存失败时显示提醒,建议保留。","field.updateNotice.label":"更新提醒","field.updateNotice.note":"有新版本可下载、或新版已下载待重启时显示短标记。","field.updateFailure.label":"更新失败提醒","field.updateFailure.note":"自动更新失败时显示短标记。","group.native":"原生信息","group.plugin":"插件信息","group.notice":"提醒信息","group.native.desc":"DeepSeek 原生底栏原有的字段,只在完整模式显示。开关只决定开和关,与模式不冲突。","group.plugin.desc":"本插件新增的字段(含接管过来的上下文圆环),简洁模式与完整模式都会显示。","group.notice.desc":"更新、失败等一次性提醒。可整组关掉,不影响上面的信息显示。","ui.listSeparator":"、","ui.sentenceEnd":"。","ui.fieldErrorPrefix":"「{label}」:","ui.turnCount":"{count} 轮","ui.turnCountPlural":"{count} 轮","ui.stepCount":"{count} 步","ui.stepCountPlural":"{count} 步","ui.mainTimeZone":"主时间时区","ui.worldTimeZone":"世界时间时区","ui.timeFormat":"时间格式","ui.year":"年","ui.month":"月","ui.day":"日","ui.hour":"时","ui.minute":"分","ui.second":"秒","ui.customTextTitle":"自定义文本","ui.customTextPlaceholder":"输入文字(最多 64 个字符)","ui.searchPlaceholder":"搜索内容…","ui.searchFieldsLabel":"搜索显示内容","ui.searchResultCount":"找到 {count} 项","ui.enabledFieldsCount":"已启用 {count} 项","ui.noSearchResults":"没有找到匹配内容","ui.timePreview":"预览:","ui.mainTime":"主时间","ui.worldTime":"世界时间","ui.customText":"自定义文本"},"en":{"meta.title":"Bottom Info Bar","meta.description":"Shows the current model, balance and spend under the composer.","ui.requestTimedOut":"Request timed out","ui.requestCanceled":"Request canceled","ui.couldNotParseResponse":"Could not parse response","ui.rpcFailed":"RPC failed","ui.refreshFailed":"Refresh failed","color.red":"Red","color.green":"Green","color.blue":"Blue","color.purple":"Purple","color.orange":"Orange","color.neutral":"Neutral","mode.balance":"Balance","mode.subscription":"Subscription","mode.billing":"Billing","mode.native":"Native stats","mode.common":"General","ui.nativeStatsField":"Native stats field","ui.shownIn":"Shown in: ","ui.pleaseTryAgainLater":"Please try again later","ui.restoreDefaultColor":"Restore default color","ui.infoBarSettings":"Info Bar","ui.settingsAreTemporarilyUnavailable":"Settings are temporarily unavailable","ui.loadingInfoBarSettings":"Loading settings…","ui.couldNotLoadInfoBar":"Could not load Info Bar settings: ","ui.changesAppliedButCouldNot":"Applied, but could not save locally: ","ui.unknownReason":"Unknown reason","ui.couldNotSave":"{errorPrefix}Could not save: {value}","ui.color":"\"{value}\" color: ","ui.hasAnInvalidColorEnter":"\"{value}\" has an invalid color. Enter #RRGGBB (for example, #0044CC).","ui.defaultColorsRestored":"Default colors restored","ui.defaultLabelsRestored":"Default display restored","ui.couldNotReset":"Could not reset: {value}","ui.identifiesTheProviderAndModel":"Shows the provider and model used in this conversation.","ui.show":"Show {label}","ui.clickToHide":"Click to hide","ui.clickToShow":"Click to show","ui.presetColor":"{label} preset color","ui.customColor":"{label} custom color","ui.customColorOpenColorPicker":"Custom color (open color picker)","ui.customColorItem":"Custom…","ui.colorSwatchLabel":"{label} color","ui.hexColor":"{label} hex color","ui.expand":"Expand","ui.collapse":"Collapse","ui.saving":"Saving…","ui.processing":"Working…","ui.visibleFields":"Information to show","ui.timeDateTitle":"Time and date","ui.timeDateDesc":"Time zones and display format for main and world time.","ui.customTextSectionDesc":"Shown in the Info Bar.","ui.resetConfirmTitle":"Reset Info Bar settings","ui.resetConfirmDescFields":"Restore all display choices. Custom colors are not affected and the change applies right away.","ui.resetConfirmDescColors":"All custom colors will be cleared and defaults restored. Field switches are not affected. This applies to the Info Bar right away.","ui.resetConfirmAcknowledge":"I understand this reset cannot be undone","ui.resetConfirmCancel":"Cancel","ui.resetConfirmConfirm":"Reset","ui.resetLabels":"Restore display","ui.resetColors":"Restore colors","ui.resetRowTitle":"Reset to defaults","ui.resetRowDesc":"Restore displayed items or colors.","ui.customTextCount":"{value}/64 characters","ui.couldNotDisplayInfoBar":"Could not display Info Bar settings: {value}","ui.dataAndBilling":"Billing data","ui.dataAndBillingDesc":"Export or clear records.","ui.exportBillingRecords":"Export billing","ui.exportBillingRecordsDesc":"Export as CSV or JSON.","ui.exportBillingCsv":"Export CSV","ui.exportBillingJson":"Export JSON","ui.clearBillingRecords":"Clear billing","ui.clearBillingRecordsDesc":"This cannot be undone.","ui.clearBillingRecordsConfirm":"Clear all billing records saved by this plugin? Export first. This cannot be undone, but settings and sign-in information will stay unchanged.","ui.exportedBillingRecords":"Exported {count} billing records ({format})","ui.noBillingRecordsToExport":"There are no billing records to export.","ui.exportFailed":"Export failed: {value}","ui.exportIncomplete":"Some historical billing records could not be read: {value}. Nothing was exported; try again later.","ui.clearFailed":"Could not clear billing records: {value}","ui.clearFailedWithoutDetails":"Billing records could not be fully cleared","ui.clearedBillingRecords":"Cleared {count} billing records","ui.clearCanceled":"Canceled","ui.exportNotSupported":"File downloads are not supported in this environment.","ui.infoBar":"Info Bar","ui.weekly":"Weekly","ui.monthly":"Monthly","ui.window":"Window","ui.quotaDisplayUsed":"Used","ui.quotaDisplayRemaining":"Remaining","ui.quotaDisplayModeTitle":"Subscription window percentage","ui.quotaDisplayModeDesc":"Show subscription quota windows as remaining or used. The warning threshold always follows the remaining 20% rule.","ui.windowUsedRemaining":"{label} window: used {usedPercent}% (remaining {value}%)","ui.windowUsedRemainingResets":"{label} window: used {usedPercent}% (remaining {value}%) · Resets {value4}","ui.minimax":"MiniMax","ui.supportsImageInput":"Supports image input.","ui.vision":"Vision","ui.unknown":"Unknown","ui.unknownModel":"Unknown model","ui.modelSelectionPending":"Reading current model","ui.modelCapabilityPending":"Checking image-input support","ui.pluginVersion":"\nPlugin version: {current}","ui.provider":"Provider: {provLabel} {modelLabel}\n","ui.pricingPeakOffPeakBeijing":"Pricing: peak/off-peak (Beijing time; weekday peaks 09:00-12:00 and 14:00-18:00; weekends off-peak)","ui.pricingFixed":"Pricing: fixed","ui.pricingNotListedUsingDefaults":"Pricing: not listed; spend is not calculated","ui.zhipu":"Zhipu","ui.xiaomiMiMo":"Xiaomi MiMo","ui.commandCode":"Command Code","ui.subscription":"Subscription","ui.cloudBilling":"Cloud billing","ui.plan":"\nPlan: {plan}","ui.expiresLocalTime":"\nExpires: {value} (local time)","ui.subscriptionServiceModel":"Subscription service: {serviceName}\nModel: {rawModelLabel}{planLine}{expiryLine}{versionLine}","ui.balanceLookupIsNotYet":"This provider's balance is not available yet.","ui.notSupported":"Not supported","ui.notConfigured":"Not configured: ","ui.notConfiguredConfigureItIn":"Not configured: {credName}. Configure it in Settings → Models.","ui.notConfiguredSettingsModels":"{credName} is not configured. Add it in Settings → Models.","ui.estimatedBalance":"Estimated balance: {symbol}{value}","ui.balance":"Balance: {symbol}{value}","ui.balance.pushBalanceGroups":"Balance","ui.low":"Low","ui.estimated":"(estimated)","ui.balanceIsTemporarilyUnavailableShowing":"Balance is temporarily unavailable. Showing the last data and retrying automatically.","ui.couldNotLoadBalanceCheck":"Could not load balance. Check your connection and API key.","ui.balanceUnavailable":"Balance unavailable","ui.beijingTime":"Beijing time: ","ui.peakPrice":"Peak price","ui.offPeakPrice":"Off-peak price","ui.input":": input ¥","ui.mCachedInput":"/M · cached input ¥","ui.mOutput":"/M · output ¥","ui.beijingTimeSwitchesTo":"Beijing time: {atLabel} switches to ","ui.until":"Until ","ui.offPeak":"off-peak","ui.peak":"peak","ui.today":"Today {symbol}{value}","ui.lastDays":"Last 30 days {symbol}{value}","ui.allTime":"All time {symbol}{value}","ui.sessionIncludingSubagents":"Session {costTxt} (including subagents){value}","ui.session":"Session","ui.spendIsTemporarilyUnavailableChat":"Spend is temporarily unavailable. Chat is unaffected.","ui.spendUnavailable":"Spend unavailable","ui.noSignInCredentialsFound":"No sign-in credentials found for {serviceName}. Please reauthorize.","ui.credentialsHaveExpiredPleaseReauthorize":"{serviceName} credentials have expired. Please reauthorize.","ui.deniedAccessReauthorizeOrTry":"{serviceName} denied access. Reauthorize or try again later.","ui.rateLimitReachedPleaseTry":"{serviceName} rate limit reached. Please try again later.","ui.timedOutCheckYourConnection":"{serviceName} timed out. Check your connection and try again.","ui.returnedAnUnrecognizedResponsePlease":"{serviceName} returned an unrecognized response. Please try again later.","ui.isTemporarilyUnavailableCheckYour":"{serviceName} is temporarily unavailable. Check your connection and try again.","ui.subscriptionExpiresLocalTime":"Subscription expires: {value} (local time)","ui.expires":"Expires","ui.subscriptionSource":"Subscription source: {value} (","ui.prepaidBalance":"Available balance","ui.availableBalanceLabel":"Available balance","ui.remainingCredits":"Credits remaining","ui.availableBalance":"Available balance: {balTxt}","ui.availableCredits":"Credits remaining: {value}","ui.credits":"credits","ui.subscriptionSource.titleLines":"Subscription source: {value}","ui.windowRemainingUsed":"{label} window: remaining {value}% (used {usedPercent}%)","ui.resetsResetsIn":" · Resets {value} · Resets in {value2}","ui.windowRemainingUsedResets":"{label} window: remaining {value}% (used {usedPercent}%) · Resets {value4}","ui.resetsIn":"Resets in","ui.configureItInSettingsModels":". Configure it in Settings → Models.","ui.deniedAccessTheTokenMay":"{serviceName} denied access: the token may lack billing read permissions.","ui.billingIsTemporarilyUnavailableCheck":"{serviceName} billing is temporarily unavailable. Check your connection and permissions, then try again.","ui.billingSource":"Billing source: {value}","ui.thisMonthSSpend":"This month’s spend: {symbol}{value}","ui.budgetUsed":"Budget used: {value}%","ui.dailyFreeQuotaRemaining":"Daily free quota remaining: {value}","ui.thisMonth":"This month","ui.thisMonthSUsage":"This month’s usage","ui.budget":"Budget","ui.free":"Free","ui.resetsIn.pushBillingGroups":"{value} · Resets in {value2}","ui.billingServiceModel":"Billing service: {serviceName}\nModel: {modelLabel}{versionLine}","ui.pricing":"Pricing","ui.spend":"Spend","ui.mode":"Mode","ui.subscriptionQuota":"Subscription quota","ui.billing":"Billing","ui.temporarilyUnavailableKeepingTheLast":": temporarily unavailable. Keeping the last data and retrying automatically.","ui.spendJournalSavedButThe":"Spend journal saved, but the readable ledger has not been updated: ","ui.thisSpendRecordWasNot":"This spend record was not saved and will not be added to totals: ","ui.ledgerUpdatePending":"Ledger update pending","ui.spendNotSaved":"Spend not saved","ui.askYourAgentToUpdate":"Plugin version {latest} is available. Update it from plugin management on desktop.","ui.updateAvailable":"Update available","ui.updateCommandCopied":"Update command copied","ui.versionAndUpdateTitle":"Version and updates","ui.versionAndUpdateDesc":"Updates replace only the plugin's own files; billing records and settings are untouched.","ui.versionUnavailable":"The DSH currently running has not loaded this version of the plugin yet, so only this line can be shown here. Restart DSH and the running version, the update mode and the check button will appear.","ui.versionRunning":"Running version {version}","ui.versionLatest":"Latest version {version}","ui.versionLastCheck":"Last checked {time}","ui.versionUnknown":"Unknown","ui.timeJustNow":"just now","ui.timeMinutesAgo":"{n} min ago","ui.timeHoursAgo":"{n} h ago","ui.timeDaysAgo":"{n} d ago","ui.autoUpdateTitle":"Update method","ui.updateModeAutoDesc":"A new version is downloaded for you; restart DSH once to apply it.","ui.updateModeManualDesc":"Only checked, not downloaded. Press Check now when you want to install it.","ui.updateModeAuto":"Automatic","ui.updateModeManual":"Manual","ui.autoUpdateOn":"On","ui.autoUpdateOff":"Off","ui.updateCheckNow":"Check now","ui.updateChecking":"Checking…","ui.updateRetrying":"Retry","ui.updateUpToDate":"Up to date.","ui.updateDisabled":"Automatic updates are unavailable here","ui.updateDisabledWhy":"Automatic updates need the plugin installed into the DSH plugin directory. This copy came from a source checkout or a link, so use the original update command.","ui.updatePendingRestart":"Updated to {version}. Restart DSH to activate.","ui.updateInProgress":"Version {version} found; updating automatically…","ui.updateAutoOffPending":"Version {version} is available. Updates are set to manual.","ui.updateFailed":"The last update did not succeed","ui.updateFallbackWhy":"If the new version misbehaves, you can go back to the previous one.","ui.updateHoldWhy":"You held this version back, so it will not be installed automatically.","ui.updateErrorIncompleteDownload":"The download was incomplete (the connection dropped). This update was abandoned and the version you have is untouched.","ui.updateErrorIntegrityMismatch":"The update package failed its safety check, so it was abandoned. The version you have is untouched.","ui.updateErrorDownloadFailed":"Could not reach the update server. Try again later.","ui.updateErrorTooLarge":"The update package was unusually large, so it was abandoned.","ui.updateErrorPayloadMismatch":"The update package did not match its version number, so it was abandoned.","ui.updateErrorPayloadUnsafe":"The update package contained an unexpected file path, so it was abandoned.","ui.updateErrorCheckFailed":"The version check did not succeed, possibly a network problem.","ui.updateErrorUnknown":"The update did not succeed. Details are in the update log.","ui.updateRollback":"Roll back to previous version","ui.updateRolledBack":"Rolled back to the previous version {version}.","ui.updateHeld":"Version {version} is held back and will not be installed automatically.","ui.updateAllowHeld":"Allow updating to {version}","ui.updateRestartBadge":"Restart to apply","ui.updateFailedBadge":"Update failed","ui.tools":"Tools","ui.avgTTFT":"Avg. TTFT","ui.cacheHit":"Cache hit","ui.input.BottomInfoBar":"Input","ui.output":"Output","ui.savingView":"Saving view…","ui.clickToSwitchFullCompact":"Click to switch full/compact view","ui.pressEnterOrSpaceFor":"Press Enter or Space for compact view.","ui.pressEnterOrSpaceFor.BottomInfoBar":"Press Enter or Space for full view.","host.hour":"5-hour","host.unknownProvider":"Unknown provider","error.subscription.request-failed":"Unexpected subscription quota request failure","host.settingsFileCouldNotBe":"Settings file could not be saved","host.everydayQuestions":"Everyday questions","host.mediumCodingTask":"Medium coding task","host.longDocumentAnalysisCodeReview":"Long document analysis / code review","host.largeProjectRefactorAcrossMultiple":"Large project refactor across multiple turns","host.subagentWorkflow":"Subagent workflow","error.settings.save-failed":"Could not save settings.json: {value}","error.balance.credentials":"Could not read credentials","error.balance.not-configured":"Not configured: {credential}","error.request.http":"Request failed: HTTP {status}.","error.request.parse":"Unexpected response format","error.subscription.not-connected":"ChatGPT subscription is not connected: credentials not found in ~/.codex/auth.json. Install dsh-chatgpt-subscription and sign in.","error.subscription.credentials-missing":"ChatGPT subscription credentials are missing id_token. Install dsh-chatgpt-subscription and reauthorize.","error.subscription.opencode-not-configured":"OpenCode Go is not configured. Set OPENCODE_GO_API_KEY or use opencode auth.json.","error.subscription.commandcode-not-configured":"Command Code is not configured. Set COMMAND_CODE_API_KEY, CMD_API_KEY, or sign in with the Command Code CLI.","error.subscription.commandcode-auth-failed":"Command Code credentials were rejected. Sign in again or update the API key.","error.subscription.commandcode-unrecognized":"Command Code returned an unrecognized quota format; the previous snapshot was kept.","host.zhipu":"Zhipu {mapped}","host.zhipu.parseZaiQuota":"Zhipu {value}{value2}","error.subscription.zhipu-not-configured":"Zhipu API key is not configured. Set ZAI_API_KEY or ZAI_CODING_CN_API_KEY.","error.subscription.zhipu-auth-failed":"Zhipu API authentication failed: the key is expired or invalid.","error.subscription.zhipu-unrecognized":"Zhipu returned an unrecognized quota format (the API may have changed); the last known data is kept.","error.request.failed":"Request failed: {value} {msg}.","error.subscription.xiaomi-not-configured":"Xiaomi MiMo Token Plan credentials are not configured: {credName} or XIAOMI_API_KEY.","error.subscription.xiaomi-http":"Request failed: HTTP {value}.","error.subscription.minimax-not-configured":"MiniMax is not configured. Set MINIMAX_API_KEY or MINIMAX_CN_API_KEY. Token Plan queries require a Subscription Key; pay-as-you-go API keys are rejected by the server.","error.subscription.minimax-auth-failed":"MiniMax rejected the API key: Token Plan queries require a Subscription Key (generated on the Token Plan subscription page). Pay-as-you-go keys are rejected (status_code=1004 / HTTP 401).","error.subscription.minimax-unrecognized":"MiniMax returned an unrecognized quota format (the API may have changed); the last known data is kept.","error.billing.together-not-configured":"Not configured: TOGETHER_API_KEY","host.actualMonthlyBillFromThe":"Actual monthly bill from the Together Usage API","error.billing.fireworks-not-configured":"Not configured: FIREWORKS_API_KEY","error.billing.fireworks-account":"Could not read account (account_id missing)","host.actualBillForThisPeriod":"Actual bill for this period (Fireworks Billing Summary)","host.actualUsageForThisPeriod":"Actual usage for this period (billingUsage fallback; no spend amount)","error.billing.aws-not-configured":"Not configured: AWS credentials (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY)","error.billing.aws-http":"Request failed: HTTP {status}. The token may lack ce:GetCostAndUsage permission","host.actualMonthlyBillFromAWS":"Actual monthly bill from AWS Cost Explorer (about 24 hours behind)","error.billing.cloudflare-not-configured":"Not configured: CLOUDFLARE_API_KEY (requires an account-level token with Billing read permission)","error.billing.cloudflare-account":"Not configured: CLOUDFLARE_ACCOUNT_ID","error.billing.cloudflare-http":"Request failed: HTTP {status}. The token requires Billing read permission","host.actualMonthlyUsageFromThe":"Actual monthly usage from the Cloudflare Billable Usage API (Alpha)","error.billing.request-failed":"Unexpected billing request failure","host.spendSummaryFileMissingArchived":"Spend summary file missing: archived amounts are not included in displayed totals. Details remain in usage-archive/ for manual recovery.","host.spendLedgerCouldNotBe":"Spend ledger could not be saved","host.usageLedgerBusy":"A response is still in progress. Clear billing records after it finishes.","error.ledger.clear-failed":"Billing records could not be fully cleared: {value}","error.ledger.snapshot-stale":"Could not save archived spend summaries: {value}","host.basedOnYourLastSessions":"Based on your last {count} sessions","host.estimatedFromSpendingOverThe":"Estimated from spending over the last {SPEND_DAYS} days","host.yourTypicalSession":"Your typical session","host.patchMustIncludeFieldsOr":"patch must include fields, colors, timeFormat, timeZones or customText","host.timeFormatMustBeAnObject":"timeFormat must be an object","host.timeFieldMustBeABoolean":"Time format field must be a boolean: {key}","host.timeZonesMustBeAnObject":"timeZones must be an object","host.timeZoneMustBeAValid":"Time zone must be a valid IANA zone: {key}","host.customTextMustBeAString":"Custom text must be a string","host.customTextTooLong":"Custom text must not exceed 64 characters","host.quotaDisplayModeInvalid":"Subscription window percentage mode must be \"used\" or \"remaining\"","host.infoDensityMustBeFullOrCompact":"Information display must be full or compact","host.fieldsMustBeAnObject":"fields must be an object","host.unknownFieldId":"Unknown field id: {key}","host.fieldVisibilityMustBeA":"Field visibility must be a boolean: {key}","host.colorsMustBeAnObject":"colors must be an object","host.colorMustBeAPreset":"Color must be a preset name or #RRGGBB: {key}","field.anchorGroup.label":"Provider and model","field.anchorGroup.note":"Shows the provider and model used in this conversation.","field.subServiceGroup.label":"Subscription service and model","field.subServiceGroup.note":"Shows the subscription service and current model or plan.","field.billingServiceGroup.label":"Billing service and model","field.billingServiceGroup.note":"Shows the billing service and current model.","field.customText.label":"Custom text","field.customText.note":"Shows custom text for a note or signature.","field.mainTime.label":"Main time","field.mainTime.note":"Shows the main time zone; adjust zone and format in Time and date.","field.worldTime.label":"World time","field.worldTime.note":"Shows another time zone; it shares the main time format.","field.sessionCost.label":"Session spend","field.sessionCost.note":"Shows actual spend for this session, including subagents.","field.balance.note":"Shows the account balance; a low balance appears in red.","field.period.label":"Current period","field.period.note":"Shows whether the current period is peak or off-peak.","field.countdown.label":"Next price change","field.countdown.note":"Shows the time until the next price change.","field.expiry.label":"Subscription expiry","field.expiry.note":"Shown when the provider supplies an expiry date.","field.subWindow5h.label":"5-hour quota","field.subWindow5h.note":"Shows the remaining rolling 5-hour quota.","field.subWindowWeek.label":"Weekly quota","field.subWindowWeek.note":"Shows the remaining weekly quota.","field.subWindowMonth.label":"Monthly quota","field.subWindowMonth.note":"Shows the remaining monthly quota.","field.resetCountdown.label":"Quota reset","field.resetCountdown.note":"Shows when the current quota resets.","field.subBalance.label":"Available balance or credits","field.subBalance.note":"Shows available balance for pay-as-you-go accounts, or remaining credits when a subscription has no visible quota window.","field.billingSpend.label":"Monthly usage","field.billingSpend.note":"Shows actual usage or spend for the current billing period.","field.budget.label":"Budget status","field.budget.note":"Shown when the provider reports a budget percentage.","field.freeQuota.label":"Free quota","field.freeQuota.note":"Shown when the provider reports a free allowance and reset time.","field.turnsSteps.label":"Turns and steps","field.turnsSteps.note":"Shows conversation turns and steps. Full mode only.","field.llmTime.label":"Model time","field.llmTime.note":"Shows model inference time. Full mode only.","field.toolTime.label":"Tool time","field.toolTime.note":"Shows tool execution time. Full mode only.","field.cacheHit.note":"Shows the prompt cache hit rate. Full mode only.","field.tokensIO.label":"Input / output tokens","field.tokensIO.note":"Shows this session’s input and output usage. Full mode only.","field.contextUsage.label":"Context usage","field.contextUsage.note":"Shows context usage. Full mode only.","ui.contextAria":"{percent} of context used","ui.contextUsed":"Context used","ui.contextSystem":"System prompt","ui.contextTools":"Tool definitions","ui.contextMessages":"Conversation messages","ui.contextFigures":"~{used} / {window}","number.thousand":"{value}K","number.million":"{value}M","field.unmapped.label":"Unsupported hint","field.unmapped.note":"Shown when balance lookup is not supported.","field.noKeyHint.label":"Missing key hint","field.noKeyHint.note":"Shown when an API key is missing.","field.balanceError.label":"Balance update error","field.balanceError.note":"Shown when balance update fails.","field.usageError.label":"Usage update error","field.usageError.note":"Shown when spend data is temporarily unavailable.","field.refreshFailure.label":"Update error","field.refreshFailure.note":"Shown when any data update fails.","field.persistWarning.label":"Unsaved record alert","field.persistWarning.note":"Shown when billing records cannot be saved; recommended.","field.updateNotice.label":"Update notice","field.updateNotice.note":"Shows a short marker when a new version is available or a downloaded version is waiting for a restart.","field.updateFailure.label":"Update failure notice","field.updateFailure.note":"Shows a short marker when the automatic update failed.","group.native":"Native information","group.plugin":"Plugin information","group.notice":"Notices","group.native.desc":"Fields the DeepSeek bar already had. Full mode only. The switch only turns a field on or off; it never changes the mode.","group.plugin.desc":"Fields this plugin adds, the adopted context ring included. Shown in both compact and full mode.","group.notice.desc":"One-off notices such as updates and failures. Turn the whole list off without touching the information above.","ui.listSeparator":", ","ui.sentenceEnd":".","ui.fieldErrorPrefix":"\"{label}\": ","ui.turnCount":"{count} turn","ui.turnCountPlural":"{count} turns","ui.stepCount":"{count} step","ui.stepCountPlural":"{count} steps","ui.mainTimeZone":"Main time zone","ui.worldTimeZone":"World time zone","ui.timeFormat":"Time format","ui.year":"Year","ui.month":"Month","ui.day":"Day","ui.hour":"Hour","ui.minute":"Minute","ui.second":"Second","ui.customTextTitle":"Custom text","ui.customTextPlaceholder":"Enter text (up to 64 characters)","ui.searchPlaceholder":"Search content…","ui.searchFieldsLabel":"Search visible content","ui.searchResultCount":"{count} found","ui.enabledFieldsCount":"{count} enabled","ui.noSearchResults":"No matching content","ui.timePreview":"Preview: ","ui.mainTime":"Main time","ui.worldTime":"World time","ui.customText":"Custom text"}};
63
63
  let t;
64
64
  let localeService;
65
65
  // 文案取值(跟随宿主界面语言):优先 DSH 的 locale 服务;服务缺席、未授权或 bind 失效时
@@ -1965,6 +1965,38 @@ function bibSetPollingStop(id) {
1965
1965
  if (id === null || typeof window === 'undefined' || typeof window.clearInterval !== 'function') return;
1966
1966
  window.clearInterval(id);
1967
1967
  }
1968
+ // 相对时间:设置页每 15 秒轮询一次更新状态,所以「刚刚 / N 分钟前」会自己往前走,不需要额外定时器。
1969
+ // 拿不到时间戳(从未检查过、或当前环境不支持自更新)时返回 null,调用方直接不显示这一段。
1970
+ function bibSetRelativeTime(stamp) {
1971
+ if (typeof stamp !== 'number' || !Number.isFinite(stamp) || stamp <= 0) return null;
1972
+ const diff = Date.now() - stamp;
1973
+ if (diff < 60000) return t('ui.timeJustNow');
1974
+ const minutes = Math.floor(diff / 60000);
1975
+ if (minutes < 60) return t('ui.timeMinutesAgo', { n: minutes });
1976
+ const hours = Math.floor(minutes / 60);
1977
+ if (hours < 24) return t('ui.timeHoursAgo', { n: hours });
1978
+ return t('ui.timeDaysAgo', { n: Math.floor(hours / 24) });
1979
+ }
1980
+ // 更新失败原因:引擎把技术报错归一成稳定代号(见 self-update.js 的 describeUpdateError),这里翻成人话。
1981
+ // 认不出的代号回退成「原因已记进更新日志」,绝不把 "unexpected end of file" 这种英文报错甩给用户。
1982
+ const UPDATE_ERROR_COPY = {
1983
+ 'incomplete-download': 'ui.updateErrorIncompleteDownload',
1984
+ 'integrity-mismatch': 'ui.updateErrorIntegrityMismatch',
1985
+ 'download-failed': 'ui.updateErrorDownloadFailed',
1986
+ 'too-large': 'ui.updateErrorTooLarge',
1987
+ 'payload-mismatch': 'ui.updateErrorPayloadMismatch',
1988
+ 'payload-unsafe': 'ui.updateErrorPayloadUnsafe',
1989
+ 'check-failed': 'ui.updateErrorCheckFailed',
1990
+ };
1991
+ function updateErrorText(kind) {
1992
+ return t(UPDATE_ERROR_COPY[kind] || 'ui.updateErrorUnknown');
1993
+ }
1994
+ // 设置页「版本与更新」区。三层结构(2026-09-25 第三轮用户拍板「分三层:状态—设置—兜底」):
1995
+ // ① 状态:一句结论 + 一行事实(运行中 / 最新 / 上次检查)—— 只读,不掺操作;
1996
+ // ② 设置:唯一的决策项「更新方式」二选一,说明跟着选中项变,手动方式下旁边就是「检查更新」;
1997
+ // ③ 兜底:只在真需要处置时出现,并说明为什么会出现。
1998
+ // 为什么改成这样:三块内容性质不同(事实 / 决策 / 异常处置),原来平铺成三行掺在一起,
1999
+ // 同一套机制还在卡片说明、更新方式说明、状态行里讲了三遍 —— 用户原话「感觉就是乱加上去的」。
1968
2000
  function bibSetVersionSection(props) {
1969
2001
  const state = props.state;
1970
2002
  // 【2026-09-25 血案】宿主进程还没加载到这一版插件代码时(典型场景:包文件已被替换成新版,
@@ -1987,28 +2019,45 @@ function bibSetVersionSection(props) {
1987
2019
  React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.versionUnavailable'))))));
1988
2020
  }
1989
2021
  const busy = props.busy === true;
2022
+ const disabled = state.disabled === true;
2023
+ // 「手动更新」= 自动下载关掉了。方向必须按 `=== false` 判定:任何非 false 的值都算全自动。
2024
+ const manual = state.autoUpdate === false;
1990
2025
  const shown = function (value) { return typeof value === 'string' && value.length > 0 ? value : t('ui.versionUnknown'); };
1991
2026
  // 方向由 host 判定(见 getUpdateState):update = 磁盘已是新版待重启;rollback = 用户回滚过、
1992
2027
  // 磁盘比运行版本旧,同样只有重启才生效,但说法必须不同,否则用户会以为自己在跑新版。
1993
2028
  const restartDirection = state.restartDirection === 'update' || state.restartDirection === 'rollback'
1994
2029
  ? state.restartDirection : null;
1995
- // 回滚按钮在「刚替换完(含已重启)」或「有失败」时都给出 —— 它正是新版出问题时唯一的逃生门,
1996
- // 不能因为重启过一次就消失(pendingVersion 会保留到下次成功更新或回滚为止)。
1997
- const canRollback = !!state.pendingVersion || restartDirection !== null || !!state.lastError;
1998
2030
  // 回滚过的版本被暂缓:默认不再装回来,需要用户显式点「允许更新」才会覆盖。
1999
2031
  const held = typeof state.holdVersion === 'string' && state.holdVersion.length > 0
2000
2032
  && state.holdVersion === state.latest;
2033
+ // ③ 兜底的显示条件(2026-09-25 第三轮拍板「按需出现」):
2034
+ // 这里**刻意不再包含 pendingVersion**。它为了在回滚时定位备份而永不清除(见 self-update.js),
2035
+ // 于是只要成功更新过一次,「回滚到上一版」就永远挂在界面上 —— 这正是用户觉得最像「乱加」的一处。
2036
+ // 真正需要逃生门的窗口只有两个:新版已装好但还没重启(restartDirection === 'update',此时退回最省事),
2037
+ // 以及上次更新失败(lastError)。已经回滚过的(restartDirection === 'rollback')不再显示 ——
2038
+ // 那时引擎侧的 pendingVersion 已清空,再点一次也没有可回滚的对象。
2039
+ const canRollback = restartDirection === 'update' || !!state.lastError;
2040
+ const showFallback = canRollback || held;
2041
+ // ① 状态层:先给结论,再把事实摆在下面
2001
2042
  let statusText = t('ui.updateUpToDate');
2002
- if (state.disabled === true) statusText = t('ui.updateDisabled');
2043
+ if (disabled) statusText = t('ui.updateDisabled');
2003
2044
  else if (restartDirection === 'update') statusText = t('ui.updatePendingRestart', { version: shown(state.diskVersion) });
2004
2045
  else if (restartDirection === 'rollback') statusText = t('ui.updateRolledBack', { version: shown(state.diskVersion) });
2005
- else if (state.lastError) statusText = t('ui.updateFailed', { error: String(state.lastError) });
2046
+ else if (state.lastError) statusText = t('ui.updateFailed');
2006
2047
  else if (held) statusText = t('ui.updateHeld', { version: state.holdVersion });
2007
2048
  else if (state.available === true && typeof state.latest === 'string') {
2008
- statusText = state.autoUpdate === false
2049
+ statusText = manual
2009
2050
  ? t('ui.updateAutoOffPending', { version: state.latest })
2010
2051
  : t('ui.updateInProgress', { version: state.latest });
2011
2052
  }
2053
+ // 事实行:运行中 / 最新 / 上次检查时间。最后一项是这一轮新加的 —— 状态文件里一直记着它,
2054
+ // 界面却不显示,用户没法确认「它到底有没有在干活」(用户第三轮的原问题就是「实现了吗」)。
2055
+ const facts = [
2056
+ t('ui.versionRunning', { version: shown(state.runningVersion) }),
2057
+ t('ui.versionLatest', { version: shown(state.latest) }),
2058
+ ];
2059
+ const checkedAt = bibSetRelativeTime(state.lastCheckAt);
2060
+ if (checkedAt) facts.push(t('ui.versionLastCheck', { time: checkedAt }));
2012
2061
  return React.createElement('section', { className: 'bib-set-card', 'aria-labelledby': 'bib-set-version-title' },
2013
2062
  bibSetCardHeader({
2014
2063
  static: true,
@@ -2017,39 +2066,51 @@ function bibSetVersionSection(props) {
2017
2066
  description: t('ui.versionAndUpdateDesc'),
2018
2067
  }),
2019
2068
  React.createElement('div', { className: 'bib-set-data-actions' },
2069
+ // ① 状态:结论在上(大字),事实在下(小字)。
2020
2070
  React.createElement('div', { className: 'bib-set-data-row' },
2021
2071
  React.createElement('div', { className: 'bib-set-data-copy' },
2022
- React.createElement('p', { className: 'bib-set-data-title' }, t('ui.versionRunning', { version: shown(state.runningVersion) })),
2023
- React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.versionLatest', { version: shown(state.latest) })))),
2024
- React.createElement('div', { className: 'bib-set-data-row' },
2072
+ React.createElement('p', { className: 'bib-set-data-title' }, statusText),
2073
+ React.createElement('p', { className: 'bib-set-data-desc' }, disabled
2074
+ ? t('ui.versionRunning', { version: shown(state.runningVersion) }) + ' · ' + t('ui.updateDisabledWhy')
2075
+ : facts.join(' · ')))),
2076
+ // 失败原因单独一行讲人话:代号由 host 归一(历史数据里的原始报错同样能被翻译成代号)。
2077
+ state.lastError && !disabled ? React.createElement('div', { className: 'bib-set-data-row' },
2078
+ React.createElement('div', { className: 'bib-set-data-copy' },
2079
+ React.createElement('p', { className: 'bib-set-data-desc' }, updateErrorText(state.lastErrorKind || state.lastError)))) : null,
2080
+ // ② 更新方式:这一块唯一的设置项。说明跟着选中项走,不再把两种方式的说明并排摊开成一段话。
2081
+ // 当前环境不支持自更新时整块不渲染:摆出用不了的控件,只会让人以为是自己点错了地方。
2082
+ disabled ? null : React.createElement('div', { className: 'bib-set-data-row' },
2025
2083
  React.createElement('div', { className: 'bib-set-data-copy' },
2026
2084
  React.createElement('p', { className: 'bib-set-data-title' }, t('ui.autoUpdateTitle')),
2027
- React.createElement('p', { className: 'bib-set-data-desc' }, t('ui.autoUpdateDesc'))),
2085
+ React.createElement('p', { className: 'bib-set-data-desc' }, manual ? t('ui.updateModeManualDesc') : t('ui.updateModeAutoDesc'))),
2028
2086
  // 更新方式用两段式选择而不是开关:用户拍板「全自动更新 / 手动更新」是二选一,不是开与关。
2029
2087
  // 组件复用订阅窗口方向的两段式(同一套几何与对比度),必须以 createElement 创建(内部有 hooks)。
2030
2088
  React.createElement('div', { className: 'bib-set-data-button-group' },
2031
2089
  React.createElement(bibSetQuotaMode, {
2032
2090
  label: t('ui.autoUpdateTitle'),
2033
- value: state.autoUpdate === false ? 'manual' : 'auto',
2091
+ value: manual ? 'manual' : 'auto',
2034
2092
  // 取值归一必须显式给出:控件默认按「订阅窗口方向」归一,会把 auto/manual 折成 remaining,
2035
2093
  // 结果是两个子项都没选中(2026-09-25 由 tests/test-quota-display-mode.cjs 抓出)。
2036
2094
  normalize: function (value) { return value === 'manual' ? 'manual' : 'auto'; },
2037
2095
  onSelect: function (value) {
2038
- if (state.disabled === true || busy) return;
2096
+ if (busy) return;
2039
2097
  props.onToggleAuto(value !== 'manual');
2040
2098
  },
2041
2099
  options: [
2042
2100
  { value: 'auto', label: t('ui.updateModeAuto') },
2043
2101
  { value: 'manual', label: t('ui.updateModeManual') },
2044
2102
  ],
2045
- }))),
2046
- React.createElement('div', { className: 'bib-set-data-row' },
2103
+ }),
2104
+ // 「检查更新」只在手动方式下出现(2026-09-25 第三轮拍板「按需出现」):全自动时它做的是
2105
+ // 已经做完的事,点下去反而会重新下载一遍。
2106
+ manual ? bibSetButton({ disabled: busy, onClick: props.onCheck, children: busy ? t('ui.updateChecking') : t('ui.updateCheckNow') }) : null)),
2107
+ // ③ 兜底:平时不占位;出现时先说清为什么会出现,再给按钮。
2108
+ showFallback ? React.createElement('div', { className: 'bib-set-data-row' },
2047
2109
  React.createElement('div', { className: 'bib-set-data-copy' },
2048
- React.createElement('p', { className: 'bib-set-data-desc' }, statusText)),
2110
+ React.createElement('p', { className: 'bib-set-data-desc' }, canRollback ? t('ui.updateFallbackWhy') : t('ui.updateHoldWhy'))),
2049
2111
  React.createElement('div', { className: 'bib-set-data-button-group' },
2050
- bibSetButton({ disabled: busy, onClick: props.onCheck, children: busy ? t('ui.updateChecking') : t('ui.updateCheckNow') }),
2051
- held ? bibSetButton({ disabled: busy, onClick: props.onForce, children: t('ui.updateAllowHeld', { version: state.holdVersion }) }) : null,
2052
- canRollback ? bibSetButton({ disabled: busy, onClick: props.onRollback, children: t('ui.updateRollback') }) : null))));
2112
+ canRollback ? bibSetButton({ disabled: busy, onClick: props.onRollback, children: t('ui.updateRollback') }) : null,
2113
+ held ? bibSetButton({ disabled: busy, onClick: props.onForce, children: t('ui.updateAllowHeld', { version: state.holdVersion }) }) : null)) : null));
2053
2114
  }
2054
2115
 
2055
2116
  function bibSetDataCard(props) {
@@ -3664,7 +3725,7 @@ module.exports = {
3664
3725
  trailingErrorGroups.push(fieldSpan('updateFailure', 'updatefailed', React.createElement('span', {
3665
3726
  key: 'updateFailureBadge',
3666
3727
  className: 'bi-update-badge bi-update-badge--error',
3667
- title: t('ui.updateFailed', { error: String(updateStatus.lastError) }),
3728
+ title: t('ui.updateFailed') + ' · ' + updateErrorText(updateStatus.lastErrorKind || updateStatus.lastError),
3668
3729
  onClick: function (event) { event.stopPropagation(); },
3669
3730
  onKeyDown: function (event) { event.stopPropagation(); },
3670
3731
  }, t('ui.updateFailedBadge'))));
package/lib/index.js CHANGED
@@ -1703,7 +1703,7 @@ export default {
1703
1703
  const current = packageVersion()
1704
1704
  return {
1705
1705
  autoUpdate: false, pendingVersion: null, updatedAt: null, lastError: null, lastCheckAt: null,
1706
- runningVersion: current, diskVersion: current, updated: false, disabled: true,
1706
+ runningVersion: current, diskVersion: current, updated: false, disabled: true, lastErrorKind: null,
1707
1707
  }
1708
1708
  }
1709
1709
  // 会话谱系列表是冷安全读取,但仍可能触发持久化查询;短暂缓存避免信息栏轮询
package/lib/locales.js CHANGED
@@ -194,13 +194,19 @@ export const LOCALES = {
194
194
  "ui.updateAvailable": "新版本提醒",
195
195
  "ui.updateCommandCopied": "更新命令已复制",
196
196
  "ui.versionAndUpdateTitle": "版本与更新",
197
- "ui.versionAndUpdateDesc": "插件会自己把新版本替换到本机,重启 DSH 后生效。更新只替换插件自己的文件,账单记录与设置不受影响。",
197
+ "ui.versionAndUpdateDesc": "更新只替换插件自己的程序文件,账单记录和设置不受影响。",
198
198
  "ui.versionUnavailable": "当前运行的 DSH 还没载入这一版插件的更新功能,所以这里暂时只显示这一行。重启 DSH 后,下面就会出现当前版本、更新方式与「检查更新」。",
199
199
  "ui.versionRunning": "运行中版本 {version}",
200
200
  "ui.versionLatest": "最新版本 {version}",
201
+ "ui.versionLastCheck": "上次检查:{time}",
201
202
  "ui.versionUnknown": "未知",
203
+ "ui.timeJustNow": "刚刚",
204
+ "ui.timeMinutesAgo": "{n} 分钟前",
205
+ "ui.timeHoursAgo": "{n} 小时前",
206
+ "ui.timeDaysAgo": "{n} 天前",
202
207
  "ui.autoUpdateTitle": "更新方式",
203
- "ui.autoUpdateDesc": "选「全自动更新」:启动后自动检查并下载新版本,重启 DSH 后生效。选「手动更新」:只检查,不下载,你点「检查更新」时才执行。",
208
+ "ui.updateModeAutoDesc": "有新版本就自己下载好,你重启一次 DSH 就生效。",
209
+ "ui.updateModeManualDesc": "只提示不下载。你想装的时候点右边的「检查更新」。",
204
210
  "ui.updateModeAuto": "全自动更新",
205
211
  "ui.updateModeManual": "手动更新",
206
212
  "ui.autoUpdateOn": "已开启",
@@ -209,11 +215,22 @@ export const LOCALES = {
209
215
  "ui.updateChecking": "检查中…",
210
216
  "ui.updateRetrying": "重试",
211
217
  "ui.updateUpToDate": "已是最新版本。",
212
- "ui.updateDisabled": "当前环境不支持自动更新(仅在插件被装载进 profile 时启用)。",
218
+ "ui.updateDisabled": "当前环境不支持自动更新",
219
+ "ui.updateDisabledWhy": "自动更新只在插件装进 DSH 插件目录后可用。这一份是从源码或链接装进来的,升级请用原来的命令。",
213
220
  "ui.updatePendingRestart": "已更新到 {version},重启 DSH 后生效。",
214
221
  "ui.updateInProgress": "发现新版本 {version},正在自动更新…",
215
222
  "ui.updateAutoOffPending": "有新版本 {version},当前是手动更新方式。",
216
- "ui.updateFailed": "上次自动更新失败:{error}",
223
+ "ui.updateFailed": "上次更新没成功",
224
+ "ui.updateFallbackWhy": "万一新版有问题,可以退回上一版。",
225
+ "ui.updateHoldWhy": "这个版本被你暂缓过,不会自己装回来。",
226
+ "ui.updateErrorIncompleteDownload": "更新包没下完整(网络中途断了),这次已放弃,你现在用的版本没受影响。",
227
+ "ui.updateErrorIntegrityMismatch": "更新包没通过安全校验,这次已放弃,你现在用的版本没受影响。",
228
+ "ui.updateErrorDownloadFailed": "连不上更新服务器,稍后再试。",
229
+ "ui.updateErrorTooLarge": "更新包大得反常,出于安全已放弃。",
230
+ "ui.updateErrorPayloadMismatch": "更新包的内容和版本号对不上,已放弃。",
231
+ "ui.updateErrorPayloadUnsafe": "更新包里出现了不该有的文件路径,已放弃。",
232
+ "ui.updateErrorCheckFailed": "检查更新没成功,可能是网络问题。",
233
+ "ui.updateErrorUnknown": "更新没成功,详细原因记在更新日志里。",
217
234
  "ui.updateRollback": "回滚到上一版",
218
235
  "ui.updateRolledBack": "已回滚到上一版 {version}。",
219
236
  "ui.updateHeld": "新版 {version} 已被你暂缓,不会自动装回来。",
@@ -593,13 +610,19 @@ export const LOCALES = {
593
610
  "ui.updateAvailable": "Update available",
594
611
  "ui.updateCommandCopied": "Update command copied",
595
612
  "ui.versionAndUpdateTitle": "Version and updates",
596
- "ui.versionAndUpdateDesc": "The plugin replaces its own files with a new version; restart DSH to activate. Only the plugin's own files change, so billing records and settings are untouched.",
613
+ "ui.versionAndUpdateDesc": "Updates replace only the plugin's own files; billing records and settings are untouched.",
597
614
  "ui.versionUnavailable": "The DSH currently running has not loaded this version of the plugin yet, so only this line can be shown here. Restart DSH and the running version, the update mode and the check button will appear.",
598
615
  "ui.versionRunning": "Running version {version}",
599
616
  "ui.versionLatest": "Latest version {version}",
617
+ "ui.versionLastCheck": "Last checked {time}",
600
618
  "ui.versionUnknown": "Unknown",
619
+ "ui.timeJustNow": "just now",
620
+ "ui.timeMinutesAgo": "{n} min ago",
621
+ "ui.timeHoursAgo": "{n} h ago",
622
+ "ui.timeDaysAgo": "{n} d ago",
601
623
  "ui.autoUpdateTitle": "Update method",
602
- "ui.autoUpdateDesc": "Automatic: checks and downloads new versions on startup, then applies after you restart DSH. Manual: only checks, and downloads when you press Check now.",
624
+ "ui.updateModeAutoDesc": "A new version is downloaded for you; restart DSH once to apply it.",
625
+ "ui.updateModeManualDesc": "Only checked, not downloaded. Press Check now when you want to install it.",
603
626
  "ui.updateModeAuto": "Automatic",
604
627
  "ui.updateModeManual": "Manual",
605
628
  "ui.autoUpdateOn": "On",
@@ -608,11 +631,22 @@ export const LOCALES = {
608
631
  "ui.updateChecking": "Checking…",
609
632
  "ui.updateRetrying": "Retry",
610
633
  "ui.updateUpToDate": "Up to date.",
611
- "ui.updateDisabled": "Automatic updates are unavailable here (only enabled when the plugin is loaded as a profile plugin).",
634
+ "ui.updateDisabled": "Automatic updates are unavailable here",
635
+ "ui.updateDisabledWhy": "Automatic updates need the plugin installed into the DSH plugin directory. This copy came from a source checkout or a link, so use the original update command.",
612
636
  "ui.updatePendingRestart": "Updated to {version}. Restart DSH to activate.",
613
637
  "ui.updateInProgress": "Version {version} found; updating automatically…",
614
638
  "ui.updateAutoOffPending": "Version {version} is available. Updates are set to manual.",
615
- "ui.updateFailed": "Last automatic update failed: {error}",
639
+ "ui.updateFailed": "The last update did not succeed",
640
+ "ui.updateFallbackWhy": "If the new version misbehaves, you can go back to the previous one.",
641
+ "ui.updateHoldWhy": "You held this version back, so it will not be installed automatically.",
642
+ "ui.updateErrorIncompleteDownload": "The download was incomplete (the connection dropped). This update was abandoned and the version you have is untouched.",
643
+ "ui.updateErrorIntegrityMismatch": "The update package failed its safety check, so it was abandoned. The version you have is untouched.",
644
+ "ui.updateErrorDownloadFailed": "Could not reach the update server. Try again later.",
645
+ "ui.updateErrorTooLarge": "The update package was unusually large, so it was abandoned.",
646
+ "ui.updateErrorPayloadMismatch": "The update package did not match its version number, so it was abandoned.",
647
+ "ui.updateErrorPayloadUnsafe": "The update package contained an unexpected file path, so it was abandoned.",
648
+ "ui.updateErrorCheckFailed": "The version check did not succeed, possibly a network problem.",
649
+ "ui.updateErrorUnknown": "The update did not succeed. Details are in the update log.",
616
650
  "ui.updateRollback": "Roll back to previous version",
617
651
  "ui.updateRolledBack": "Rolled back to the previous version {version}.",
618
652
  "ui.updateHeld": "Version {version} is held back and will not be installed automatically.",
@@ -54,6 +54,34 @@ export function resolveSafePath(baseDir, relative) {
54
54
  return full
55
55
  }
56
56
 
57
+ // 失败原因归一(2026-09-25 第三轮加入):state.lastError 原来直接存技术报错(如
58
+ // "unexpected end of file"),界面只能把它原样甩给用户 —— 用户看到一行英文报错,既不懂也不知道
59
+ // 自己有没有风险。这里把报错映射成稳定代号,由界面翻译成人话;原始报错仍写进 update-log.jsonl
60
+ // 供排查(见 runOnce 的 catch)。
61
+ // 映射同时认「代号」与「原始报错」,因此历史 state 文件里存的老值也能被正确翻译。
62
+ // 顺序即优先级:更具体的模式必须排在更宽的模式前面,否则会被宽模式抢先命中
63
+ // (「download failed: too large」曾被 download-failed 抢走,2026-09-25 由新增的归一单测抓出)。
64
+ const UPDATE_ERROR_PATTERNS = [
65
+ ['incomplete-download', /unexpected end of file|invalid tar|incorrect header check|invalid distance|invalid checksum|too short|not in gzip/i],
66
+ ['integrity-mismatch', /integrity mismatch/i],
67
+ ['too-large', /too large/i],
68
+ ['download-failed', /download failed|empty body|fetch failed|abort/i],
69
+ ['payload-mismatch', /package name mismatch|version mismatch|has no package\.json|not valid JSON|not semver/i],
70
+ ['payload-unsafe', /path not allowed|path escapes|refusing to write outside/i],
71
+ ['check-failed', /check-failed/i],
72
+ ]
73
+ export const UPDATE_ERROR_KINDS = ['incomplete-download', 'integrity-mismatch', 'download-failed', 'too-large', 'payload-mismatch', 'payload-unsafe', 'check-failed', 'unknown']
74
+
75
+ export function describeUpdateError(value) {
76
+ const text = typeof value === 'string' ? value : (value && value.message) || ''
77
+ if (text.length === 0) return 'unknown'
78
+ if (UPDATE_ERROR_KINDS.includes(text)) return text
79
+ for (const [kind, pattern] of UPDATE_ERROR_PATTERNS) {
80
+ if (pattern.test(text)) return kind
81
+ }
82
+ return 'unknown'
83
+ }
84
+
57
85
  // 语义化版本比较:只接受 x.y.z(可带 v 前缀),返回 [major, minor, patch] 或 null。
58
86
  export function parseSemver(value) {
59
87
  const match = typeof value === 'string' && value.trim().match(/^v?(\d+)\.(\d+)\.(\d+)$/)
@@ -411,8 +439,11 @@ export function createSelfUpdater(options) {
411
439
  saveState()
412
440
  return Object.assign({}, state, { latest: latest.version, skipped: 'held' })
413
441
  }
414
- if (diskVersion() === latest.version && !manual) {
415
- // 磁盘已是新版:等用户重启,别重复下载
442
+ if (diskVersion() === latest.version) {
443
+ // 磁盘已是新版:等用户重启,别重复下载。
444
+ // 这里**不能**再挂 `&& !manual`:手动点「检查更新」只表示「用户在自动关掉时授权下载」,
445
+ // 不是「绕过防重」—— 原来那样写会让手动方式下连点几次就重复下载、重复替换、多留几份备份
446
+ // (2026-09-25 第三轮用户问「更新到底实现没有」时顺手查出来的)。
416
447
  state.pendingVersion = latest.version
417
448
  state.lastError = null
418
449
  saveState()
@@ -432,9 +463,11 @@ export function createSelfUpdater(options) {
432
463
  saveState()
433
464
  appendLog({ event: 'updated', version: latest.version })
434
465
  } catch (error) {
435
- state.lastError = error && error.message ? error.message : 'update-failed'
466
+ // 状态里只存代号(界面据此翻译成人话),原始报错写进日志供排查。
467
+ const raw = error && error.message ? error.message : 'update-failed'
468
+ state.lastError = describeUpdateError(raw)
436
469
  saveState()
437
- appendLog({ event: 'update-failed', version: latest.version, message: state.lastError })
470
+ appendLog({ event: 'update-failed', version: latest.version, kind: state.lastError, message: raw })
438
471
  }
439
472
  return Object.assign({}, state, { latest: latest.version })
440
473
  }
@@ -446,6 +479,9 @@ export function createSelfUpdater(options) {
446
479
  runningVersion,
447
480
  diskVersion: diskVersion(),
448
481
  updated: state.pendingVersion !== null && state.pendingVersion !== runningVersion,
482
+ // 失败原因额外给出代号:界面据此翻译成人话。历史 state 文件里存的是原始报错,
483
+ // 由 describeUpdateError 现场归一,因此老数据同样能显示成人话。
484
+ lastErrorKind: state.lastError ? describeUpdateError(state.lastError) : null,
449
485
  })
450
486
  },
451
487
  // 自动与手动共用一条流水线,并发调用复用同一次运行
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bottom-info-bar",
3
- "version": "1.19.2",
3
+ "version": "1.19.3",
4
4
  "description": "Shows the current model, balance and spend under the composer.",
5
5
  "keywords": [
6
6
  "deepseek",