dsh-deepseek-account 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4 - 2026-08-30
4
+
5
+ - Show the remaining Grok quota percentage with its current reset window.
6
+ - Show both Codex five-hour and weekly remaining percentages and reset times on separate, wrapping sidebar lines.
7
+ - Replace the generic sidebar letters with provider-specific Grok, ChatGPT/OpenAI, and DeepSeek icons.
8
+
3
9
  ## 0.1.3 - 2026-08-30
4
10
 
5
11
  - Keep the sidebar mounted while switching from a ready Provider to DeepSeek, an unknown Provider fallback, or Codex, instead of rendering the previous Provider's incompatible state shape.
package/README.en.md CHANGED
@@ -14,14 +14,14 @@ An independent account plugin for DeepSeek Harness. It uses one sidebar entry to
14
14
 
15
15
  | Current Provider | Sidebar content | Data source | Provider plugin required |
16
16
  | --- | --- | --- | --- |
17
- | DeepSeek | Balance for the configured API key | Official DeepSeek `/user/balance` | No |
18
- | Grok | Quota reset time | Existing local RPC from the Grok plugin | Yes |
19
- | Codex | Five-hour and weekly reset times | Existing local RPC from the Codex plugin | Yes |
20
- | Unknown custom Provider | Falls back to the DeepSeek balance | Official DeepSeek `/user/balance` | No |
17
+ | DeepSeek | DeepSeek icon and balance for the configured API key | Official DeepSeek `/user/balance` | No |
18
+ | Grok | Grok icon, remaining percentage, current period, and reset time | Existing local RPC from the Grok plugin | Yes |
19
+ | Codex | ChatGPT icon plus remaining percentages and reset times for the five-hour and weekly windows | Existing local RPC from the Codex plugin | Yes |
20
+ | Unknown custom Provider | Falls back to the DeepSeek icon and balance | Official DeepSeek `/user/balance` | No |
21
21
 
22
22
  The sidebar always shows exactly one item for the Provider selected by the current conversation. Grok and Codex are optional data sources; this plugin does not take over their authentication, models, settings, or other behavior. If an optional plugin is missing, its quota source is shown as unavailable while DeepSeek balance and top-up remain independent.
23
23
 
24
- Only DeepSeek has a top-up entry. Grok and Codex show reset times only; this plugin neither shows nor infers remaining quota.
24
+ Only DeepSeek has a top-up entry. Grok uses the remaining percentage supplied by its optional plugin, falling back to the supplied used percentage when needed. Codex converts the used percentages supplied by its optional plugin into remaining percentages. The plugin retains only the projected percentages and reset windows needed for display, not the upstream usage objects.
25
25
 
26
26
  ## Installation
27
27
 
@@ -34,7 +34,7 @@ Requirements:
34
34
  Install the current stable version:
35
35
 
36
36
  ```sh
37
- dsh plugin --profile web add dsh-deepseek-account@0.1.3
37
+ dsh plugin --profile web add dsh-deepseek-account@0.1.4
38
38
  dsh web
39
39
  ```
40
40
 
package/README.md CHANGED
@@ -14,14 +14,14 @@ DeepSeek Harness 的独立账户插件:在一个侧栏入口中展示当前对
14
14
 
15
15
  | 当前 Provider | 侧栏显示 | 数据来源 | 是否需要对应插件 |
16
16
  | --- | --- | --- | --- |
17
- | DeepSeek | API Key 所属账户余额 | DeepSeek 官方 `/user/balance` | 否 |
18
- | Grok | 额度重置时间 | Grok 插件已有的本地 RPC | 是 |
19
- | Codex | 5 小时和每周额度重置时间 | Codex 插件已有的本地 RPC | 是 |
20
- | 无法识别的自定义 Provider | 回退显示 DeepSeek 余额 | DeepSeek 官方 `/user/balance` | 否 |
17
+ | DeepSeek | DeepSeek 图标、API Key 所属账户余额 | DeepSeek 官方 `/user/balance` | 否 |
18
+ | Grok | Grok 图标、剩余额度百分比、当前周期与重置时间 | Grok 插件已有的本地 RPC | 是 |
19
+ | Codex | ChatGPT 图标、5 小时和每周的剩余额度百分比与重置时间 | Codex 插件已有的本地 RPC | 是 |
20
+ | 无法识别的自定义 Provider | 回退显示 DeepSeek 图标和余额 | DeepSeek 官方 `/user/balance` | 否 |
21
21
 
22
22
  侧栏始终只显示当前对话所选 Provider 的一项信息。Grok 和 Codex 只是可选数据源;本插件不接管它们的登录、模型、设置或其他能力。缺少可选插件时,对应额度显示“来源不可用”,DeepSeek 余额和充值仍可独立使用。
23
23
 
24
- 只有 DeepSeek 提供充值入口。Grok 和 Codex 只显示重置时间,不显示或推断剩余额度。
24
+ 只有 DeepSeek 提供充值入口。Grok 直接采用可选插件返回的剩余百分比,缺失时可由其已用百分比换算;Codex 将可选插件返回的已用百分比换算成剩余百分比。插件只保留显示所需的百分比和重置窗口,不保留上游原始用量对象。
25
25
 
26
26
  ## 安装
27
27
 
@@ -34,7 +34,7 @@ DeepSeek Harness 的独立账户插件:在一个侧栏入口中展示当前对
34
34
  安装当前稳定版本:
35
35
 
36
36
  ```sh
37
- dsh plugin --profile web add dsh-deepseek-account@0.1.3
37
+ dsh plugin --profile web add dsh-deepseek-account@0.1.4
38
38
  dsh web
39
39
  ```
40
40
 
@@ -1,3 +1,5 @@
1
1
  # Third-Party Notices
2
2
 
3
3
  The runtime package declares DeepSeek Harness modules as peer dependencies and does not copy their source code or assets. Those modules are distributed by their respective authors under their published licenses.
4
+
5
+ The inline DeepSeek, Grok, and OpenAI identification icons in `client.js` are adapted from [Lobe Icons](https://github.com/lobehub/lobe-icons), distributed under the MIT License. DeepSeek, Grok, ChatGPT, OpenAI, and their respective logos and marks belong to their owners. Their use here identifies the currently selected Provider and does not imply endorsement or affiliation.
@@ -10,6 +10,11 @@ window.__ModuleLoader__.load({
10
10
  const grokChannel = "/grok-auth"
11
11
  const codexChannel = "/dsh-codex"
12
12
  const topUpUrl = "https://platform.deepseek.com/balance"
13
+ const providerIconPaths = Object.freeze({
14
+ grok: "M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815",
15
+ codex: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z",
16
+ deepseek: "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z",
17
+ })
13
18
  const dictionaries = {
14
19
  zh: {
15
20
  nav: "DeepSeek 账户", title: "DeepSeek 账户", description: "查看当前 API Key 所属账户余额,并前往 DeepSeek 官方平台充值。",
@@ -17,7 +22,7 @@ window.__ModuleLoader__.load({
17
22
  topUp: "登录并充值", officialNote: "登录和付款仅在 DeepSeek 官方网页完成;插件不读取网页 Cookie,也不会代为提交付款。",
18
23
  updated: "数据更新时间", loading: "正在读取余额…", unavailable: "暂时无法读取余额。", unconfigured: "请先在模型设置中配置 DeepSeek API Key。",
19
24
  unauthorized: "当前 API Key 无效或已失效。", rateLimited: "查询过于频繁,请稍后重试。", stale: "当前展示上一次成功读取的余额。",
20
- grokQuota: "Grok 额度", codexQuota: "Codex 额度", fiveHour: "5 小时", weekly: "每周", resetsAt: "重置", resetUnknown: "重置时间不可用", sourceUnavailable: "额度来源不可用",
25
+ grokQuota: "Grok 额度", codexQuota: "Codex 额度", fiveHour: "5 小时", weekly: "每周", monthly: "每月", currentPeriod: "当前周期", remaining: "剩余", resetsAt: "重置", resetUnknown: "重置时间不可用", sourceUnavailable: "额度来源不可用",
21
26
  },
22
27
  en: {
23
28
  nav: "DeepSeek account", title: "DeepSeek account", description: "View the balance of the account associated with the current API key and open the official DeepSeek Platform to top up.",
@@ -25,7 +30,7 @@ window.__ModuleLoader__.load({
25
30
  topUp: "Sign in and top up", officialNote: "Sign-in and payment happen only on the official DeepSeek website. The plugin never reads web cookies or submits payment.",
26
31
  updated: "Updated", loading: "Reading balance…", unavailable: "The balance is temporarily unavailable.", unconfigured: "Configure a DeepSeek API key in model settings first.",
27
32
  unauthorized: "The current API key was rejected.", rateLimited: "Too many balance requests. Try again later.", stale: "Showing the last successfully verified balance.",
28
- grokQuota: "Grok quota", codexQuota: "Codex quota", fiveHour: "5-hour", weekly: "Weekly", resetsAt: "Resets", resetUnknown: "Reset time unavailable", sourceUnavailable: "Quota source unavailable",
33
+ grokQuota: "Grok quota", codexQuota: "Codex quota", fiveHour: "5-hour", weekly: "Weekly", monthly: "Monthly", currentPeriod: "Current period", remaining: "Remaining", resetsAt: "Resets", resetUnknown: "Reset time unavailable", sourceUnavailable: "Quota source unavailable",
29
34
  },
30
35
  }
31
36
 
@@ -60,11 +65,14 @@ window.__ModuleLoader__.load({
60
65
  const quota = result.value.dashboard.quota
61
66
  if (!plainRecord(quota) || quota.state !== "ready") return undefined
62
67
  const periodKind = quota.periodKind === "weekly" || quota.periodKind === "monthly" ? quota.periodKind : "current"
68
+ const remainingPercent = projectedRemainingPercent(quota.remainingPercent, quota.usedPercent)
69
+ if ((quota.remainingPercent !== undefined || quota.usedPercent !== undefined) && remainingPercent === undefined) return undefined
63
70
  if (quota.resetsAt !== undefined && !validTime(quota.resetsAt)) return undefined
64
71
  return {
65
72
  provider: "grok",
66
73
  status: "ready",
67
74
  periodKind,
75
+ ...(remainingPercent === undefined ? {} : { remainingPercent }),
68
76
  ...(quota.resetsAt === undefined ? {} : { resetsAt: quota.resetsAt }),
69
77
  }
70
78
  }
@@ -86,7 +94,7 @@ window.__ModuleLoader__.load({
86
94
  if (!plainRecord(value) || typeof value.usedPercent !== "number" || !Number.isFinite(value.usedPercent) || value.usedPercent < 0 || value.usedPercent > 100) return undefined
87
95
  const kind = value.windowDurationMins === 300 ? "five-hour" : value.windowDurationMins === 10_080 ? "weekly" : undefined
88
96
  if (kind === undefined || !validTimestamp(value.resetsAt)) return undefined
89
- return { kind, resetsAt: value.resetsAt }
97
+ return { kind, remainingPercent: projectedRemainingPercent(undefined, value.usedPercent), resetsAt: value.resetsAt }
90
98
  }
91
99
 
92
100
  function safeSnapshot(value) {
@@ -176,18 +184,39 @@ window.__ModuleLoader__.load({
176
184
  function money(balance) { return balance === undefined ? "—" : `${balance.currency === "CNY" ? "¥" : "$"}${balance.total}` }
177
185
 
178
186
  function resetTime(value) { return value === undefined ? undefined : new Date(value).toLocaleString() }
187
+ function percentText(value) { return String(Math.round(value * 10) / 10) }
179
188
 
180
189
  function sidebarPresentation(provider, state, t) {
181
190
  if (provider === "grok") {
182
- const reset = state.status === "ready" ? resetTime(state.resetsAt) : undefined
183
- return { label: t("grokQuota"), symbol: "G", summary: reset === undefined ? t(state.status === "loading" ? "loading" : "sourceUnavailable") : `${t("resetsAt")} ${reset}` }
191
+ if (state.status !== "ready") return { label: t("grokQuota"), lines: [t(state.status === "loading" ? "loading" : "sourceUnavailable")] }
192
+ const periodLabel = t(state.periodKind === "weekly" ? "weekly" : state.periodKind === "monthly" ? "monthly" : "currentPeriod")
193
+ const reset = resetTime(state.resetsAt)
194
+ return {
195
+ label: t("grokQuota"),
196
+ lines: [
197
+ state.remainingPercent === undefined ? t("sourceUnavailable") : `${t("remaining")} ${percentText(state.remainingPercent)}%`,
198
+ `${periodLabel} · ${reset === undefined ? t("resetUnknown") : `${t("resetsAt")} ${reset}`}`,
199
+ ],
200
+ }
184
201
  }
185
202
  if (provider === "codex") {
186
203
  const windows = state.status === "ready" ? state.windows : []
187
- const summary = windows.map((window) => `${t(window.kind === "five-hour" ? "fiveHour" : "weekly")} ${t("resetsAt")} ${resetTime(window.resetsAt)}`).join(" · ")
188
- return { label: t("codexQuota"), symbol: "C", summary: summary || t(state.status === "loading" ? "loading" : "sourceUnavailable") }
204
+ const lines = windows.map((window) => `${t(window.kind === "five-hour" ? "fiveHour" : "weekly")} · ${t("remaining")} ${percentText(window.remainingPercent)}% · ${t("resetsAt")} ${resetTime(window.resetsAt)}`)
205
+ return { label: t("codexQuota"), lines: lines.length === 0 ? [t(state.status === "loading" ? "loading" : "sourceUnavailable")] : lines }
189
206
  }
190
- return { label: t("balance"), symbol: "¥", summary: money(mainBalance(shownSnapshot(state))) }
207
+ return { label: t("balance"), lines: [money(mainBalance(shownSnapshot(state)))] }
208
+ }
209
+
210
+ function ProviderIcon({ provider }) {
211
+ return React.createElement("svg", {
212
+ className: "dsh-deepseek-account-provider-icon",
213
+ "data-provider-icon": provider,
214
+ viewBox: "0 0 24 24",
215
+ fill: "currentColor",
216
+ fillRule: "evenodd",
217
+ focusable: "false",
218
+ "aria-hidden": "true",
219
+ }, React.createElement("path", { d: providerIconPaths[provider] ?? providerIconPaths.deepseek }))
191
220
  }
192
221
 
193
222
  function SidebarBalance({ wide, ctx, client, t }) {
@@ -197,13 +226,14 @@ window.__ModuleLoader__.load({
197
226
  const presentation = sidebarPresentation(provider, currentState, t)
198
227
  return React.createElement("button", {
199
228
  type: "button", className: wide ? "dsh-deepseek-account-card" : "dsh-deepseek-account-rail",
200
- disabled: busy, onClick: () => void refresh(true), "aria-label": `${presentation.label} ${presentation.summary}`,
229
+ disabled: busy, onClick: () => void refresh(true), "aria-label": `${presentation.label} ${presentation.lines.join(" · ")}`,
201
230
  }, wide
202
231
  ? React.createElement(React.Fragment, null,
203
- React.createElement("span", { className: "dsh-deepseek-account-symbol", "aria-hidden": "true" }, presentation.symbol),
204
- React.createElement("span", { className: "dsh-deepseek-account-copy" }, React.createElement("span", null, presentation.label), React.createElement("small", null, presentation.summary)),
232
+ React.createElement("span", { className: "dsh-deepseek-account-symbol", "aria-hidden": "true" }, ProviderIcon({ provider })),
233
+ React.createElement("span", { className: "dsh-deepseek-account-copy" }, React.createElement("span", null, presentation.label),
234
+ ...presentation.lines.map((line, index) => React.createElement("small", { key: `${provider}-${index}` }, line))),
205
235
  React.createElement("span", { "aria-hidden": "true" }, busy ? "…" : "↻"))
206
- : React.createElement("span", null, presentation.symbol))
236
+ : ProviderIcon({ provider }))
207
237
  }
208
238
 
209
239
  function AccountSettings({ client, t }) {
@@ -235,6 +265,7 @@ window.__ModuleLoader__.load({
235
265
  const style = document.createElement("style")
236
266
  style.dataset.dshDeepseekAccount = ""
237
267
  style.textContent = ".dsh-deepseek-account-card{display:grid;width:100%;min-height:48px;margin:3px 0;padding:4px 3px;border:0;border-radius:8px;grid-template-columns:22px minmax(0,1fr) 16px;align-items:center;gap:10px;color:inherit;background:transparent;cursor:pointer;font:inherit;text-align:left}.dsh-deepseek-account-card:hover,.dsh-deepseek-account-rail:hover{background:var(--dsw-alias-interactive-bg-hover)}.dsh-deepseek-account-symbol{font-weight:650;text-align:center}.dsh-deepseek-account-copy{display:flex;min-width:0;flex-direction:column}.dsh-deepseek-account-copy small{overflow:hidden;color:var(--dsw-alias-label-tertiary);font-size:11px;text-overflow:ellipsis;white-space:nowrap}.dsh-deepseek-account-rail{display:grid;width:36px;height:36px;margin:4px auto;border:0;border-radius:8px;place-items:center;color:inherit;background:transparent;cursor:pointer;font-size:10px;font-weight:650}.dsh-deepseek-account-page{box-sizing:border-box;width:min(860px,100%);padding:8px 4px 40px;color:var(--dsw-alias-label-primary)}.dsh-deepseek-account-page h2{margin:0}.dsh-deepseek-account-description{color:var(--dsw-alias-label-secondary)}.dsh-deepseek-account-panel{margin-top:14px;padding:18px;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-layer-1)}.dsh-deepseek-account-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.dsh-deepseek-account-head h3{margin:0}.dsh-deepseek-account-head button,.dsh-deepseek-account-topup{min-height:34px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:0 13px;color:inherit;background:transparent;font:inherit}.dsh-deepseek-account-balances{display:grid;margin-top:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.dsh-deepseek-account-balance{display:flex;flex-direction:column;gap:5px;padding:14px;border-radius:12px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-interactive-bg-hover))}.dsh-deepseek-account-balance strong{font-size:22px}.dsh-deepseek-account-balance span,.dsh-deepseek-account-panel p,.dsh-deepseek-account-panel small{color:var(--dsw-alias-label-secondary);font-size:12px}.dsh-deepseek-account-warning{color:var(--dsw-alias-state-warn-label)!important}.dsh-deepseek-account-topup{display:inline-flex;align-items:center;color:#fff;text-decoration:none;background:var(--dsw-alias-state-business-primary)}"
268
+ style.textContent += ".dsh-deepseek-account-provider-icon{display:block;width:18px;height:18px;margin:auto}.dsh-deepseek-account-copy{gap:2px}.dsh-deepseek-account-copy small{overflow:visible;line-height:1.35;overflow-wrap:anywhere;text-overflow:clip;white-space:normal}"
238
269
  document.head.append(style)
239
270
  return () => style.remove()
240
271
  }
@@ -270,3 +301,7 @@ function plainRecord(value) { return typeof value === "object" && value !== null
270
301
  function validDecimal(value) { return typeof value === "string" && value.length <= 64 && /^\d+(?:\.\d+)?$/u.test(value) }
271
302
  function validTime(value) { return typeof value === "string" && Number.isFinite(Date.parse(value)) }
272
303
  function validTimestamp(value) { return Number.isSafeInteger(value) && value >= 0 && Number.isFinite(new Date(value).getTime()) }
304
+ function projectedRemainingPercent(remainingPercent, usedPercent) {
305
+ const value = remainingPercent === undefined ? (typeof usedPercent === "number" ? 100 - usedPercent : undefined) : remainingPercent
306
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 100 ? value : undefined
307
+ }
@@ -16,8 +16,8 @@ loopback RPC /dsh-deepseek-account
16
16
  ▼
17
17
  browser account client ──> sidebar + DeepSeek settings
18
18
  │
19
- ├── optional /grok-auth RPC ──> reset time only
20
- └── optional /dsh-codex RPC ──> 5-hour/weekly reset times only
19
+ ├── optional /grok-auth RPC ──> remaining % + current reset window
20
+ └── optional /dsh-codex RPC ──> remaining % + 5-hour/weekly reset windows
21
21
  ```
22
22
 
23
23
  充值是独立路径:浏览器只打开固定的 `https://platform.deepseek.com/balance`。插件不参与该网页的登录、Cookie 或付款请求。
@@ -67,7 +67,7 @@ await balance.read({ force? })
67
67
  Grok 的 `/grok-auth` 和 Codex 的 `/dsh-codex` 是外部、可选 seam。它们不是本插件拥有的接口,因此:
68
68
 
69
69
  - 调用失败必须降级为 `unavailable`;
70
- - 只读取显示所需的重置窗口,不保留用量百分比或其他账户数据;
70
+ - 只投影显示所需的剩余百分比与重置窗口,不保留上游原始用量对象或其他账户数据;
71
71
  - 不以 npm 依赖或 Host 注入的方式接管对应插件;
72
72
  - 外部 RPC 变化时,通过投影函数和契约测试适配,不能让变化扩散到 UI。
73
73
 
@@ -79,6 +79,8 @@ Grok 的 `/grok-auth` 和 Codex 的 `/dsh-codex` 是外部、可选 seam。它
79
79
  - `dsh-codex` → Codex;
80
80
  - 其他值(包括未知自定义 Provider)→ DeepSeek。
81
81
 
82
+ 侧栏图标使用同一映射:Grok 标识、ChatGPT/OpenAI Blossom、DeepSeek 鲸鱼;图形内联在客户端产物中并继承当前主题颜色,不发起额外网络请求。
83
+
82
84
  改变默认回退属于用户可见兼容性变化,必须先开 Issue,并同步 README、界面文案、测试、CHANGELOG 和发布说明。
83
85
 
84
86
  ## Test surface
@@ -0,0 +1,72 @@
1
+ ## 中文
2
+
3
+ `0.1.4` 补全 Grok 与 Codex 的剩余额度展示,拆分 Codex 的 5 小时和每周窗口,并让侧栏图标随当前 Provider 切换。
4
+
5
+ ### 额度展示
6
+
7
+ - Grok 显示剩余额度百分比、当前周期和重置时间;优先使用 Grok 插件返回的剩余百分比,必要时由已用百分比换算。
8
+ - Codex 将已有 RPC 的已用百分比换算成剩余百分比,分别完整显示 5 小时与每周窗口及各自重置时间。
9
+ - 额度窗口改为独立、可换行的侧栏文本,不再因单行省略而隐藏后半段数据。
10
+
11
+ ### Provider 图标
12
+
13
+ - 选择 Grok 时显示 Grok 图标。
14
+ - 选择 Codex 时显示 ChatGPT/OpenAI 图标。
15
+ - 选择 DeepSeek 或无法识别的自定义 Provider 时显示 DeepSeek 图标。
16
+ - 图标内联于客户端产物、继承当前主题颜色,不会增加网络请求或运行时依赖。
17
+
18
+ ### 兼容性与安全边界
19
+
20
+ - Grok/Codex 仍是可选插件;缺失或 RPC 不兼容时仅降级为“额度来源不可用”。
21
+ - 本插件不改变 Grok/Codex 的登录、模型、设置或 RPC 行为,只保留侧栏显示所需的剩余百分比和重置窗口。
22
+ - DeepSeek 余额、未知 Provider 回退和官方充值入口保持不变。
23
+
24
+ ### 验证限制
25
+
26
+ - 自动化验证覆盖额度字段投影、Grok/Codex 分窗口渲染、Provider 图标映射、长文本换行、Provider 切换和包内容。
27
+ - 未执行真实 Grok/Codex 账户额度读取或真实 Harness GUI 验收;未执行真实 DeepSeek 余额查询或付款。
28
+
29
+ 安装:
30
+
31
+ ```sh
32
+ dsh plugin --profile web add dsh-deepseek-account@0.1.4
33
+ dsh web
34
+ ```
35
+
36
+ <details>
37
+ <summary>English release notes</summary>
38
+
39
+ `0.1.4` adds complete remaining-quota presentation for Grok and Codex, separates the Codex five-hour and weekly windows, and switches the sidebar icon with the current Provider.
40
+
41
+ ### Quota presentation
42
+
43
+ - Grok shows the remaining percentage, current period, and reset time. It prefers the remaining percentage returned by the Grok plugin and derives it from the used percentage only when needed.
44
+ - Codex converts the used percentages from its existing RPC into remaining percentages and shows the five-hour and weekly windows with their respective reset times.
45
+ - Quota windows now render as separate wrapping sidebar lines instead of losing trailing data to single-line ellipsis.
46
+
47
+ ### Provider icons
48
+
49
+ - Grok selections use the Grok icon.
50
+ - Codex selections use the ChatGPT/OpenAI icon.
51
+ - DeepSeek and unknown custom Provider fallbacks use the DeepSeek icon.
52
+ - Icons are embedded in the client artifact, inherit the current theme color, and add no network request or runtime dependency.
53
+
54
+ ### Compatibility and security boundaries
55
+
56
+ - Grok and Codex remain optional. Missing or incompatible RPCs degrade only to “quota source unavailable.”
57
+ - This plugin does not change Grok/Codex authentication, models, settings, or RPC behavior. It retains only the remaining percentages and reset windows required for display.
58
+ - DeepSeek balance, unknown-Provider fallback, and the official top-up entry remain unchanged.
59
+
60
+ ### Verification limits
61
+
62
+ - Automated checks cover quota projection, separate Grok/Codex window rendering, Provider icon mapping, long-text wrapping, Provider switches, and package contents.
63
+ - No live Grok/Codex quota read or live Harness GUI acceptance was performed. No real DeepSeek balance query or payment was performed.
64
+
65
+ Install:
66
+
67
+ ```sh
68
+ dsh plugin --profile web add dsh-deepseek-account@0.1.4
69
+ dsh web
70
+ ```
71
+
72
+ </details>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-deepseek-account",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "DeepSeek account balance and official top-up entry for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "license": "MIT",