dsh-deepseek-account 0.1.4 → 0.1.5

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.5 - 2026-08-30
4
+
5
+ - Group Codex five-hour and weekly quota data into compact visual blocks with stronger percentage hierarchy and secondary reset-time rows.
6
+ - Refresh the selected Provider once when the current conversation transitions from running to idle, without polling or duplicate idle refreshes.
7
+ - Replace the generic gear beside the DeepSeek account settings entry with the DeepSeek whale icon, restoring the original shell icon when the plugin unloads.
8
+
3
9
  ## 0.1.4 - 2026-08-30
4
10
 
5
11
  - Show the remaining Grok quota percentage with its current reset window.
package/README.en.md CHANGED
@@ -34,14 +34,14 @@ Requirements:
34
34
  Install the current stable version:
35
35
 
36
36
  ```sh
37
- dsh plugin --profile web add dsh-deepseek-account@0.1.4
37
+ dsh plugin --profile web add dsh-deepseek-account@0.1.5
38
38
  dsh web
39
39
  ```
40
40
 
41
41
  In the current conversation's Web UI:
42
42
 
43
- 1. The sidebar footer shows account information for the current Provider; click it to refresh.
44
- 2. The “DeepSeek account” settings page shows CNY/USD total, granted, and topped-up balances.
43
+ 1. The sidebar footer shows account information for the current Provider; click it to refresh, or let it refresh once automatically after each conversation turn finishes.
44
+ 2. The settings navigation uses the DeepSeek whale icon; the “DeepSeek account” page shows CNY/USD total, granted, and topped-up balances.
45
45
  3. “Sign in and top up” opens only `https://platform.deepseek.com/balance` in a new page.
46
46
 
47
47
  Continue to pin an exact version when upgrading so pre-release dependencies cannot drift:
@@ -61,7 +61,7 @@ dsh web
61
61
  | Showing the last verified balance | The current request failed and the plugin retained a marked stale snapshot | Check the network, retry later, and note the timestamp |
62
62
  | Quota source unavailable | The Grok/Codex plugin is absent, stopped, or exposes an incompatible local RPC | Check that plugin; DeepSeek remains unaffected |
63
63
 
64
- Automatic reads are cached for up to five minutes, manual refreshes for 30 seconds, and concurrent reads are coalesced into one request.
64
+ Automatic DeepSeek reads are cached for up to five minutes, forced refreshes for 30 seconds, and concurrent reads are coalesced into one request. After each conversation turn, the plugin observes the current session's running-to-idle edge and refreshes the current Provider without polling.
65
65
 
66
66
  ## Security design
67
67
 
package/README.md CHANGED
@@ -34,14 +34,14 @@ DeepSeek Harness 的独立账户插件:在一个侧栏入口中展示当前对
34
34
  安装当前稳定版本:
35
35
 
36
36
  ```sh
37
- dsh plugin --profile web add dsh-deepseek-account@0.1.4
37
+ dsh plugin --profile web add dsh-deepseek-account@0.1.5
38
38
  dsh web
39
39
  ```
40
40
 
41
41
  打开当前对话的 Web 界面后:
42
42
 
43
- 1. 侧栏底部会显示当前 Provider 的账户信息;点击可手动刷新。
44
- 2. 设置页的“DeepSeek 账户”可查看 CNY/USD 的总余额、赠送余额和充值余额。
43
+ 1. 侧栏底部会显示当前 Provider 的账户信息;点击可手动刷新,每轮对话结束后也会主动刷新一次。
44
+ 2. 设置导航使用 DeepSeek 鲸鱼图标;“DeepSeek 账户”页面可查看 CNY/USD 的总余额、赠送余额和充值余额。
45
45
  3. “登录并充值”只会在新页面打开 `https://platform.deepseek.com/balance`。
46
46
 
47
47
  升级时继续使用精确版本,避免预发布阶段的依赖漂移:
@@ -61,7 +61,7 @@ dsh web
61
61
  | 展示上一次成功读取的余额 | 当前请求失败,插件保留了已标记的旧快照 | 检查网络后重试,并留意数据时间 |
62
62
  | 额度来源不可用 | Grok/Codex 插件未安装、未运行或其本地 RPC 不兼容 | 检查对应插件;DeepSeek 功能不受影响 |
63
63
 
64
- 自动读取最多缓存 5 分钟,手动刷新最多每 30 秒发起一次请求;并发读取会合并为一次请求。
64
+ DeepSeek 自动读取最多缓存 5 分钟,强制刷新最多每 30 秒发起一次上游请求;并发读取会合并为一次请求。每轮对话结束时,插件会监听当前会话由运行变为空闲的边沿并刷新当前 Provider,不使用定时轮询。
65
65
 
66
66
  ## 安全设计
67
67
 
@@ -1,4 +1,4 @@
1
- /* global document, window */
1
+ /* global document, MutationObserver, window */
2
2
 
3
3
  window.__ModuleLoader__.load({
4
4
  id: "dsh-deepseek-account",
@@ -175,6 +175,26 @@ window.__ModuleLoader__.load({
175
175
  return provider
176
176
  }
177
177
 
178
+ function subscribeCurrentConversationCompletion(list, onComplete) {
179
+ const currentState = () => {
180
+ const snapshot = list.getSnapshot()
181
+ const sessionId = snapshot.current
182
+ return { sessionId, running: sessionId === undefined ? false : snapshot.byId?.[sessionId]?.running === true }
183
+ }
184
+ let previous = currentState()
185
+ return list.subscribe(() => {
186
+ const current = currentState()
187
+ if (current.sessionId !== undefined && current.sessionId === previous.sessionId && previous.running && !current.running) {
188
+ onComplete(current.sessionId)
189
+ }
190
+ previous = current
191
+ })
192
+ }
193
+
194
+ function useConversationCompletionRefresh(sessions, refresh) {
195
+ React.useEffect(() => subscribeCurrentConversationCompletion(sessions.list, () => { void refresh(true) }), [refresh, sessions])
196
+ }
197
+
178
198
  function accountProvider(selected) {
179
199
  return selected === "llm-grok" || selected === "grok" ? "grok" : selected === "dsh-codex" ? "codex" : "deepseek"
180
200
  }
@@ -219,9 +239,78 @@ window.__ModuleLoader__.load({
219
239
  }, React.createElement("path", { d: providerIconPaths[provider] ?? providerIconPaths.deepseek }))
220
240
  }
221
241
 
242
+ function mountSettingsNavIcon(button) {
243
+ const existing = button?.querySelector?.("[data-dsh-deepseek-account-nav-icon]")
244
+ if (existing !== null && existing !== undefined) return undefined
245
+ const original = button?.querySelector?.("svg")
246
+ if (original === null || original === undefined || typeof document.createElementNS !== "function") return undefined
247
+ const wasHidden = original.hidden === true
248
+ const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg")
249
+ icon.dataset.dshDeepseekAccountNavIcon = ""
250
+ icon.setAttribute("viewBox", "0 0 24 24")
251
+ icon.setAttribute("width", "16")
252
+ icon.setAttribute("height", "16")
253
+ icon.setAttribute("fill", "currentColor")
254
+ icon.setAttribute("fill-rule", "evenodd")
255
+ icon.setAttribute("focusable", "false")
256
+ icon.setAttribute("aria-hidden", "true")
257
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path")
258
+ path.setAttribute("d", providerIconPaths.deepseek)
259
+ icon.append(path)
260
+ original.hidden = true
261
+ original.after(icon)
262
+ let mounted = true
263
+ return () => {
264
+ if (!mounted) return
265
+ mounted = false
266
+ original.hidden = wasHidden
267
+ icon.remove()
268
+ }
269
+ }
270
+
271
+ function installSettingsNavIcon() {
272
+ if (typeof MutationObserver !== "function" || document.body?.querySelectorAll === undefined) return () => {}
273
+ const labels = new Set([dictionaries.zh.nav, dictionaries.en.nav])
274
+ const mounted = new Map()
275
+ const scan = () => {
276
+ for (const [button, cleanup] of mounted) {
277
+ if (button.isConnected === false) { cleanup(); mounted.delete(button) }
278
+ }
279
+ for (const button of document.body.querySelectorAll("button")) {
280
+ if (mounted.has(button)) continue
281
+ const matches = Array.from(button.querySelectorAll?.("span") ?? [])
282
+ .some((node) => labels.has(node.textContent?.trim()))
283
+ if (!matches) continue
284
+ const cleanup = mountSettingsNavIcon(button)
285
+ if (cleanup !== undefined) mounted.set(button, cleanup)
286
+ }
287
+ }
288
+ const observer = new MutationObserver(scan)
289
+ observer.observe(document.body, { childList: true, subtree: true })
290
+ scan()
291
+ return () => {
292
+ observer.disconnect()
293
+ for (const cleanup of mounted.values()) cleanup()
294
+ mounted.clear()
295
+ }
296
+ }
297
+
298
+ function SidebarDetails({ provider, state, presentation, t }) {
299
+ if (provider !== "codex" || state.status !== "ready") {
300
+ return React.createElement(React.Fragment, null, ...presentation.lines.map((line, index) =>
301
+ React.createElement("small", { key: `${provider}-${index}` }, line)))
302
+ }
303
+ return React.createElement("span", { className: "dsh-deepseek-account-quota-windows" }, ...state.windows.map((window) =>
304
+ React.createElement("span", { className: "dsh-deepseek-account-quota-window", key: window.kind },
305
+ React.createElement("span", { className: "dsh-deepseek-account-quota-period" }, t(window.kind === "five-hour" ? "fiveHour" : "weekly")),
306
+ React.createElement("strong", { className: "dsh-deepseek-account-quota-remaining" }, `${t("remaining")} ${percentText(window.remainingPercent)}%`),
307
+ React.createElement("small", { className: "dsh-deepseek-account-quota-reset" }, `${t("resetsAt")} ${resetTime(window.resetsAt)}`))))
308
+ }
309
+
222
310
  function SidebarBalance({ wide, ctx, client, t }) {
223
311
  const provider = useActiveAccountProvider(ctx)
224
312
  const { state, busy, refresh } = useAccount(client, provider)
313
+ useConversationCompletionRefresh(ctx.sessions, refresh)
225
314
  const currentState = state.provider === provider ? state : { provider, status: "loading" }
226
315
  const presentation = sidebarPresentation(provider, currentState, t)
227
316
  return React.createElement("button", {
@@ -231,7 +320,7 @@ window.__ModuleLoader__.load({
231
320
  ? React.createElement(React.Fragment, null,
232
321
  React.createElement("span", { className: "dsh-deepseek-account-symbol", "aria-hidden": "true" }, ProviderIcon({ provider })),
233
322
  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))),
323
+ SidebarDetails({ provider, state: currentState, presentation, t })),
235
324
  React.createElement("span", { "aria-hidden": "true" }, busy ? "…" : "↻"))
236
325
  : ProviderIcon({ provider }))
237
326
  }
@@ -265,7 +354,7 @@ window.__ModuleLoader__.load({
265
354
  const style = document.createElement("style")
266
355
  style.dataset.dshDeepseekAccount = ""
267
356
  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}"
357
+ 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}.dsh-deepseek-account-quota-windows{display:grid;gap:4px;margin-top:2px}.dsh-deepseek-account-quota-window{display:grid;min-width:0;padding:3px 5px;border-radius:5px;grid-template-columns:auto minmax(0,1fr);align-items:baseline;column-gap:6px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-interactive-bg-hover))}.dsh-deepseek-account-quota-period{font-size:11px;font-weight:650}.dsh-deepseek-account-quota-remaining{min-width:0;color:var(--dsw-alias-label-secondary);font-size:11px;font-weight:600}.dsh-deepseek-account-quota-reset{grid-column:1/-1;font-size:10px!important;line-height:1.25!important}"
269
358
  document.head.append(style)
270
359
  return () => style.remove()
271
360
  }
@@ -274,6 +363,7 @@ window.__ModuleLoader__.load({
274
363
  function apply(ctx) {
275
364
  ctx.effect(() => ctx.locale.register(namespace, dictionaries), "deepseek-account: dictionaries")
276
365
  ctx.effect(() => installStyle(), "deepseek-account: styles")
366
+ ctx.effect(() => installSettingsNavIcon(), "deepseek-account: settings navigation icon")
277
367
  const t = ctx.locale.bind(namespace)
278
368
  const client = createAccountClient(ctx.connection)
279
369
  ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
@@ -293,6 +383,8 @@ window.__ModuleLoader__.load({
293
383
  module.exports.safeCodexUsage = safeCodexUsage
294
384
  module.exports.safeGrokQuota = safeGrokQuota
295
385
  module.exports.safeSnapshot = safeSnapshot
386
+ module.exports.installSettingsNavIcon = installSettingsNavIcon
387
+ module.exports.subscribeCurrentConversationCompletion = subscribeCurrentConversationCompletion
296
388
  return module.exports
297
389
  },
298
390
  })
@@ -18,6 +18,8 @@ browser account client ──> sidebar + DeepSeek settings
18
18
  │
19
19
  ├── optional /grok-auth RPC ──> remaining % + current reset window
20
20
  └── optional /dsh-codex RPC ──> remaining % + 5-hour/weekly reset windows
21
+
22
+ current session list ── running true→false ──> one forced account refresh
21
23
  ```
22
24
 
23
25
  充值是独立路径:浏览器只打开固定的 `https://platform.deepseek.com/balance`。插件不参与该网页的登录、Cookie 或付款请求。
@@ -58,10 +60,16 @@ await balance.read({ force? })
58
60
  - 调用 DeepSeek、Grok 或 Codex 的本地 RPC;
59
61
  - 再次校验所有跨 RPC 数据;
60
62
  - 注册侧栏与设置页;
63
+ - 监听当前会话的运行结束边沿,并刷新一次当前 Provider;
64
+ - 在 Harness 尚未提供 settings section 图标接口时,只对中英文“DeepSeek 账户”导航行挂载可清理的鲸鱼图标;
61
65
  - 注册中英文文案和局部样式。
62
66
 
63
67
  客户端只依赖投影后的账户状态。`scripts/build.mjs` 会复制确定性产物,并拒绝包含 `DEEPSEEK_API_KEY`、credential resolution 或 Authorization header 特征的客户端文件。
64
68
 
69
+ 完成刷新只观察 `ctx.sessions.list` 已公开的 `current` 与 `running` 状态。同一会话仅在 `true → false` 时触发;初始空闲、重复空闲通知和切换到另一个空闲会话都不能触发。不要为此增加定时轮询或读取完整对话日志。
70
+
71
+ 设置导航图标 adapter 只匹配插件拥有的中英文 label,保留并隐藏 Harness 原图标,重复 DOM 通知不得重复挂载;插件卸载时必须断开观察并恢复原图标。Harness 将来提供正式 section 图标接口后,应删除这个临时 adapter。
72
+
65
73
  ### Optional Provider adapters
66
74
 
67
75
  Grok 的 `/grok-auth` 和 Codex 的 `/dsh-codex` 是外部、可选 seam。它们不是本插件拥有的接口,因此:
@@ -0,0 +1,58 @@
1
+ ## 中文
2
+
3
+ `0.1.5` 优化 Codex 额度可读性,在每轮对话结束后主动刷新当前 Provider,并让 DeepSeek 账户设置项使用对应的鲸鱼图标。
4
+
5
+ ### 主要变化
6
+
7
+ - Codex 的 5 小时和每周额度改为两个紧凑信息块:窗口与剩余百分比位于主行,重置时间位于次级行。
8
+ - 当前会话从运行变为空闲时,主动刷新一次当前 Provider;初始空闲、重复空闲通知和会话切换不会误触发,也不使用轮询。
9
+ - 将“DeepSeek 账户”设置导航的通用齿轮替换为 DeepSeek 鲸鱼图标;重复渲染不会重复插入,插件卸载时恢复 Harness 原图标。
10
+
11
+ ### 兼容性与安全边界
12
+
13
+ - 图标适配只匹配本插件拥有的中英文设置 label,不修改其他设置项。
14
+ - 本版本不接入 DeepSeek 网页登录、用量分析或插件内充值,也不读取网页 Cookie、账户密码或支付数据。
15
+ - Grok/Codex 仍是可选数据源,其登录、模型、设置与 RPC 行为不变。
16
+
17
+ ### 验证限制
18
+
19
+ - 自动化测试覆盖配额分层渲染、Provider 图标、设置导航图标的挂载/去重/恢复,以及单次对话完成刷新。
20
+ - 未执行真实 Grok/Codex 账户读取、真实 DeepSeek 余额请求、网页登录或付款;真实 Harness GUI 视觉验收仍需安装后确认。
21
+
22
+ 安装:
23
+
24
+ ```sh
25
+ dsh plugin --profile web add dsh-deepseek-account@0.1.5
26
+ dsh web
27
+ ```
28
+
29
+ <details>
30
+ <summary>English release notes</summary>
31
+
32
+ `0.1.5` improves Codex quota readability, refreshes the current Provider after each completed conversation turn, and gives the DeepSeek account settings entry its matching whale icon.
33
+
34
+ ### Changes
35
+
36
+ - Codex five-hour and weekly quotas render as compact blocks, with the window and remaining percentage on the primary row and reset time on a secondary row.
37
+ - A current-session running-to-idle edge refreshes the selected Provider once. Initial idle state, duplicate idle notifications, and session switches do not trigger false refreshes; no polling is used.
38
+ - The generic settings gear beside “DeepSeek account” is replaced with the DeepSeek whale icon. Re-renders do not duplicate it, and plugin cleanup restores the Harness icon.
39
+
40
+ ### Compatibility and security boundaries
41
+
42
+ - The icon adapter matches only this plugin's Chinese and English settings labels and does not modify other entries.
43
+ - This release does not add DeepSeek web login, usage analytics, or in-plugin payment, and it does not read web cookies, passwords, or payment data.
44
+ - Grok and Codex remain optional data sources; their authentication, models, settings, and RPC behavior are unchanged.
45
+
46
+ ### Verification limits
47
+
48
+ - Automated tests cover quota hierarchy, Provider icons, settings-icon mount/deduplication/restoration, and one refresh per completed conversation turn.
49
+ - No live Grok/Codex quota read, DeepSeek balance request, web login, or payment was performed. Live Harness GUI visual acceptance remains pending installation.
50
+
51
+ Install:
52
+
53
+ ```sh
54
+ dsh plugin --profile web add dsh-deepseek-account@0.1.5
55
+ dsh web
56
+ ```
57
+
58
+ </details>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-deepseek-account",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "DeepSeek account balance and official top-up entry for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "license": "MIT",