deepseek-harness-wallet 0.3.4 → 0.3.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
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.3.5 - 2026-08-27
6
+
7
+ - 修复 BigModel / Z.ai 中国区 Coding Plan 接口将 `TOKENS_LIMIT` 改名为 `CREDIT_LIMIT` 后被误判为“套餐接口返回格式已变化”的问题;兼容新旧字段,支持只有新额度字段的响应,并在接口提供时显示已用/总量、剩余量和重置时间。 / Fixed BigModel / Z.ai China Coding Plan responses being misclassified as “the plan response format changed” after `TOKENS_LIMIT` was renamed to `CREDIT_LIMIT`; both names are supported, credit-only responses are accepted, and available used/total, remaining, and reset values are shown when provided.
8
+ - 增加新旧字段共存时优先保留信息更完整的额度数据,并补充接口归一化与回归测试;当前 126/126 测试通过。 / Prefer the richer quota record when old and new names coexist, with normalization and regression coverage; 126/126 tests pass.
9
+
5
10
  ## 0.3.4 - 2026-08-25
6
11
 
7
12
  - 修复 DSH Desktop 重启后部分插件设置因 WebView origin/localStorage 命名空间变化而丢失的问题:显示内容、输入框标签、仅显示余额、标签比例、提醒、峰谷卡和永久删除偏好现在会即时写入浏览器并同步备份到本地 `wallet.json`;宿主已有值优先恢复,宿主缺失的旧设置才从浏览器迁移,避免旧标签页在重启后反向覆盖新选择。 / Fixed wallet preferences disappearing after a DSH Desktop restart when the WebView origin/localStorage namespace changes: display, composer-label, primary-value, scale, reminder, peak-card, and permanent-delete preferences now write locally and mirror into `wallet.json`; existing host values restore authoritatively, while browser values migrate only for host-missing keys so stale tabs cannot undo newer choices after restart (Fixes #31).
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  > A local-first companion that keeps account status, per-conversation usage, completion reminders, official recharge, flexible layout, and host-gated session controls beside the DSH composer.
16
16
 
17
- > **Version:** v0.3.4 is the current stable release on GitHub and npm.
17
+ > **Version:** v0.3.5 is the current stable release on GitHub and npm.
18
18
 
19
19
  > If DeepSeek Harness Control Center helps you, please consider leaving a ⭐ Star. Thank you!
20
20
 
@@ -73,13 +73,13 @@ Details: [compatibility](#browser-desktop-and-os-compatibility) · [data and tru
73
73
 
74
74
  ## Install
75
75
 
76
- From npm (stable v0.3.4):
76
+ From npm (stable v0.3.5):
77
77
 
78
78
  ```sh
79
79
  dsh plugin --profile web add deepseek-harness-wallet
80
80
  ```
81
81
 
82
- or from GitHub `main` (current v0.3.4 source):
82
+ or from GitHub `main` (current v0.3.5 source):
83
83
 
84
84
  ```sh
85
85
  dsh plugin --profile web add github:feibi-mochi/deepseek-harness-control-center
package/index.js CHANGED
@@ -33,7 +33,7 @@ const OFFICIAL_PROVIDER = 'deepseek-official'
33
33
  // DeepSeek's official paid-API bucket by the wrapper-provider alias control.
34
34
  const PLAN_PROVIDER_IDS = new Set(PLAN_ADAPTERS.map((adapter) => adapter.provider))
35
35
  const RECHARGE_URL = 'https://platform.deepseek.com/top_up'
36
- const PLUGIN_VERSION = '0.3.4'
36
+ const PLUGIN_VERSION = '0.3.5'
37
37
  const PRICING_SOURCE_URL = 'https://api-docs.deepseek.com/zh-cn/quick_start/pricing/'
38
38
  const PRICING_SYNC_INTERVAL_MS = 6 * 60 * 60_000
39
39
  const PRICING_SYNC_TIMEOUT_MS = 8_000
package/lib/client.js CHANGED
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
 
24
24
  var POLL_MS = 15000
25
25
  // Keep in lockstep with package.json; a test enforces the sync.
26
- var WALLET_VERSION = '0.3.4'
26
+ var WALLET_VERSION = '0.3.5'
27
27
  var CONFIRM_KEY = 'dsh-wallet-recharge-confirmed'
28
28
  var CHIP_LAYOUT_KEY = 'dshw-chip-layout-v4'
29
29
  var PANEL_POS_KEY = 'dshw-panel-pos-v1'
package/lib/plans.js CHANGED
@@ -8,8 +8,15 @@
8
8
 
9
9
  export const PLAN_CACHE_VERSION = 2
10
10
 
11
+ const TOKEN_LIMIT = Object.freeze({ id: 'tokens-5h', kind: 'tokens', window: '5h', amounts: false, reset: false })
12
+ const CREDIT_LIMIT = Object.freeze({ id: 'tokens-5h', kind: 'tokens', window: '5h', amounts: true, reset: true })
13
+
11
14
  const ZAI_LIMITS = Object.freeze({
12
- TOKENS_LIMIT: Object.freeze({ id: 'tokens-5h', kind: 'tokens', window: '5h' }),
15
+ // BigModel renamed this response type to CREDIT_LIMIT without changing
16
+ // the 5-hour model-credit semantics. Keep both names for old snapshots and
17
+ // new China-region responses.
18
+ TOKENS_LIMIT: TOKEN_LIMIT,
19
+ CREDIT_LIMIT,
13
20
  TIME_LIMIT: Object.freeze({ id: 'tools-month', kind: 'tools', window: 'month' }),
14
21
  })
15
22
 
@@ -81,9 +88,9 @@ function normalizedLimit(adapter, value, atMs) {
81
88
  if (value === null || typeof value !== 'object' || Array.isArray(value)) return null
82
89
  const descriptor = adapter.limitTypes[value.type]
83
90
  if (descriptor === undefined) return null
84
- const used = descriptor.kind === 'tools' ? finiteNonNegative(value.currentValue) : null
85
- const total = descriptor.kind === 'tools' ? finiteNonNegative(value.usage) : null
86
- const remaining = descriptor.kind === 'tools' ? finiteNonNegative(value.remaining) : null
91
+ const used = descriptor.amounts ? finiteNonNegative(value.currentValue) : descriptor.kind === 'tools' ? finiteNonNegative(value.currentValue) : null
92
+ const total = descriptor.amounts ? finiteNonNegative(value.usage) : descriptor.kind === 'tools' ? finiteNonNegative(value.usage) : null
93
+ const remaining = descriptor.amounts ? finiteNonNegative(value.remaining) : descriptor.kind === 'tools' ? finiteNonNegative(value.remaining) : null
87
94
  const percentages = percentagePair(value.percentage, used, total)
88
95
  return {
89
96
  id: descriptor.id,
@@ -93,18 +100,26 @@ function normalizedLimit(adapter, value, atMs) {
93
100
  used,
94
101
  total,
95
102
  remaining,
96
- resetAt: descriptor.kind === 'tools' ? resetTimestamp(value.nextResetTime, atMs) : null,
103
+ resetAt: descriptor.reset || descriptor.kind === 'tools' ? resetTimestamp(value.nextResetTime, atMs) : null,
97
104
  }
98
105
  }
99
106
 
107
+ function preferRicherLimit(next, current) {
108
+ if (current === undefined) return true
109
+ if (current.used === null && next.used !== null) return true
110
+ if (current.total === null && next.total !== null) return true
111
+ if (current.remaining === null && next.remaining !== null) return true
112
+ return current.resetAt === null && next.resetAt !== null
113
+ }
114
+
100
115
  function normalizeCachedLimit(value, atMs) {
101
116
  if (value === null || typeof value !== 'object' || Array.isArray(value)) return null
102
117
  if (!['tokens-5h', 'tools-month'].includes(value.id)) return null
103
118
  const kind = value.id === 'tokens-5h' ? 'tokens' : 'tools'
104
119
  const window = value.id === 'tokens-5h' ? '5h' : 'month'
105
- const used = kind === 'tools' ? finiteNonNegative(value.used) : null
106
- const total = kind === 'tools' ? finiteNonNegative(value.total) : null
107
- const remaining = kind === 'tools' ? finiteNonNegative(value.remaining) : null
120
+ const used = finiteNonNegative(value.used)
121
+ const total = finiteNonNegative(value.total)
122
+ const remaining = finiteNonNegative(value.remaining)
108
123
  const legacyUsedPercentage = value.usedPercentage ?? value.percentage
109
124
  const percentages = percentagePair(legacyUsedPercentage, used, total)
110
125
  return {
@@ -115,7 +130,7 @@ function normalizeCachedLimit(value, atMs) {
115
130
  used,
116
131
  total,
117
132
  remaining,
118
- resetAt: kind === 'tools' ? resetTimestamp(value.resetAt, atMs) : null,
133
+ resetAt: resetTimestamp(value.resetAt, atMs),
119
134
  }
120
135
  }
121
136
 
@@ -127,14 +142,12 @@ export function normalizePlanPayload(adapter, payload, atMs = Date.now()) {
127
142
  }
128
143
  const data = payload.data !== null && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : null
129
144
  if (data === null || !Array.isArray(data.limits)) throw new Error('invalid-plan-response')
130
- const limits = []
131
- const seen = new Set()
145
+ const limitsById = new Map()
132
146
  for (const item of data.limits) {
133
147
  const limit = normalizedLimit(adapter, item, atMs)
134
- if (limit === null || seen.has(limit.id)) continue
135
- seen.add(limit.id)
136
- limits.push(limit)
148
+ if (limit !== null && preferRicherLimit(limit, limitsById.get(limit.id))) limitsById.set(limit.id, limit)
137
149
  }
150
+ const limits = [...limitsById.values()]
138
151
  limits.sort((left, right) => left.id === 'tokens-5h' ? -1 : right.id === 'tokens-5h' ? 1 : left.id.localeCompare(right.id))
139
152
  if (limits.length === 0) throw new Error('invalid-plan-response')
140
153
  return {
@@ -156,14 +169,12 @@ export function normalizePlanSnapshotCache(value, atMs = Date.now()) {
156
169
  ? Math.trunc(raw.fetchedAt)
157
170
  : null
158
171
  if (fetchedAt === null || !Array.isArray(raw.limits)) continue
159
- const limits = []
160
- const seen = new Set()
172
+ const limitsById = new Map()
161
173
  for (const item of raw.limits) {
162
174
  const limit = normalizeCachedLimit(item, atMs)
163
- if (limit === null || seen.has(limit.id)) continue
164
- seen.add(limit.id)
165
- limits.push(limit)
175
+ if (limit !== null && preferRicherLimit(limit, limitsById.get(limit.id))) limitsById.set(limit.id, limit)
166
176
  }
177
+ const limits = [...limitsById.values()]
167
178
  if (limits.length === 0) continue
168
179
  limits.sort((left, right) => left.id === 'tokens-5h' ? -1 : right.id === 'tokens-5h' ? 1 : left.id.localeCompare(right.id))
169
180
  out[adapter.id] = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "deepseek-harness-wallet",
3
3
  "description": "Local-first DeepSeek Harness control center for balance monitoring, usage accounting, a 365-day local token ledger, Z.ai Coding Plan quota monitoring, official pricing sync, peak/off-peak status, encrypted multi-account switching, health checks, completion reminders, recharge, and host-gated session controls.",
4
- "version": "0.3.4",
4
+ "version": "0.3.5",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {