deepseek-harness-wallet 0.3.3 → 0.3.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 +6 -0
- package/README.md +3 -3
- package/index.js +53 -3
- package/lib/client.js +152 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.3.4 - 2026-08-25
|
|
6
|
+
|
|
7
|
+
- 修复 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).
|
|
8
|
+
- 新增受白名单和长度限制的 `/api/wallet/preferences` 读写接口,拒绝未知键和敏感字段;偏好沿用账本的主备恢复、失败锁写和退出前刷新机制。 / Added an allowlisted, length-bounded `/api/wallet/preferences` endpoint that rejects unknown or sensitive fields and inherits the ledger's backup recovery, fail-closed writes, and shutdown flush behavior.
|
|
9
|
+
- 新增宿主完整重载、Desktop 本地键丢失恢复、恶意字段拒绝和 keepalive 双写回归测试;当前 123/123 测试通过。 / Added host-reload persistence, missing-Desktop-key hydration, malicious-field rejection, and keepalive dual-write regressions; 123/123 tests pass.
|
|
10
|
+
|
|
5
11
|
## 0.3.3 - 2026-08-25
|
|
6
12
|
|
|
7
13
|
- 新增通用官方套餐适配器契约,并首批接入 Z.ai Coding Plan 全球区与中国区;每个来源固定绑定官方域名和独立的 DSH 凭据引用,浏览器不会收到 Key、接口路径或原始响应。 / Added a generic official subscription-plan adapter contract with initial Z.ai Coding Plan Global and China adapters; each source is pinned to its official domain and its own DSH credential reference, while the browser receives no key, endpoint path, or raw response.
|
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.
|
|
17
|
+
> **Version:** v0.3.4 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.
|
|
76
|
+
From npm (stable v0.3.4):
|
|
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.
|
|
82
|
+
or from GitHub `main` (current v0.3.4 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.
|
|
36
|
+
const PLUGIN_VERSION = '0.3.4'
|
|
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
|
|
@@ -49,7 +49,7 @@ const ACCOUNTS_CRYPTO_VERSION = 1
|
|
|
49
49
|
const CREDENTIAL_REF = 'DEEPSEEK_API_KEY'
|
|
50
50
|
const DEFAULT_THRESHOLD = 5
|
|
51
51
|
const BALANCE_REFRESH_MS = 60_000
|
|
52
|
-
const STORE_VERSION =
|
|
52
|
+
const STORE_VERSION = 5
|
|
53
53
|
const HISTORY_VERSION = 1
|
|
54
54
|
const HISTORY_RETENTION_DAYS = 365
|
|
55
55
|
const HISTORY_MAX_EVENTS = 20_000
|
|
@@ -59,6 +59,22 @@ const PLAN_REFRESH_MS = 5 * 60_000
|
|
|
59
59
|
const PLAN_STALE_MS = 15 * 60_000
|
|
60
60
|
const PLAN_REFRESH_TIMEOUT_MS = 12_000
|
|
61
61
|
const PLAN_RESPONSE_MAX_BYTES = 1_000_000
|
|
62
|
+
const UI_PREFERENCE_KEYS = new Set([
|
|
63
|
+
'dshw-chip-style-v1',
|
|
64
|
+
'dshw-chip-balance-only-v1',
|
|
65
|
+
'dshw-data-visibility-v1',
|
|
66
|
+
'dshw-chip-scale-v1',
|
|
67
|
+
'dshw-completion-notify-v1',
|
|
68
|
+
'dshw-low-blink-v1',
|
|
69
|
+
'dshw-peakring-v1',
|
|
70
|
+
'dshw-peak-orient-v1',
|
|
71
|
+
'dshw-peak-recharge-v1',
|
|
72
|
+
'dshw-peak-scale-v1',
|
|
73
|
+
'dshw-peak-dock-v1',
|
|
74
|
+
'dshw-peaknotify-v1',
|
|
75
|
+
'dshw-permanent-delete-v1',
|
|
76
|
+
])
|
|
77
|
+
const UI_PREFERENCE_VALUE_MAX = 500
|
|
62
78
|
|
|
63
79
|
// Beijing (UTC+8, no DST) weekday peak windows: 09:00-12:00 and 14:00-18:00.
|
|
64
80
|
// From 2026-08-23 00:00 Beijing, Saturday and Sunday are off-peak all day.
|
|
@@ -886,6 +902,17 @@ export function isOfficialProvider(provider, extraProviders) {
|
|
|
886
902
|
return Array.isArray(extraProviders) && extraProviders.includes(provider)
|
|
887
903
|
}
|
|
888
904
|
|
|
905
|
+
export function normalizeUiPreferences(value) {
|
|
906
|
+
const source = value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {}
|
|
907
|
+
const normalized = {}
|
|
908
|
+
for (const [key, entry] of Object.entries(source)) {
|
|
909
|
+
if (!UI_PREFERENCE_KEYS.has(key)) continue
|
|
910
|
+
if (typeof entry !== 'string' || entry.length > UI_PREFERENCE_VALUE_MAX || /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(entry)) continue
|
|
911
|
+
normalized[key] = entry
|
|
912
|
+
}
|
|
913
|
+
return normalized
|
|
914
|
+
}
|
|
915
|
+
|
|
889
916
|
function repairPlanProviderSessionUsage(sessions, historySource, atMs) {
|
|
890
917
|
const rawEvents = historySource !== null && typeof historySource === 'object' && !Array.isArray(historySource)
|
|
891
918
|
&& historySource.events !== null && typeof historySource.events === 'object' && !Array.isArray(historySource.events)
|
|
@@ -949,6 +976,7 @@ export function normalizeStoreData(value, atMs = Date.now()) {
|
|
|
949
976
|
officialProviders: normalizeProviderList(source.officialProviders),
|
|
950
977
|
knownProviders: normalizeProviderList(source.knownProviders),
|
|
951
978
|
plans: normalizePlanSnapshotCache(source.plans, atMs),
|
|
979
|
+
preferences: normalizeUiPreferences(source.preferences),
|
|
952
980
|
}
|
|
953
981
|
if (Object.hasOwn(source, 'history')) {
|
|
954
982
|
normalized.history = normalizeHistory(source.history, atMs)
|
|
@@ -964,7 +992,7 @@ let usageStorageRecovered = false
|
|
|
964
992
|
let usageStoreSkipBackupOnce = false
|
|
965
993
|
|
|
966
994
|
function emptyStoreData() {
|
|
967
|
-
return { version: STORE_VERSION, thresholds: { CNY: DEFAULT_THRESHOLD }, accountThresholds: {}, sessions: {}, officialProviders: [], knownProviders: [], plans: {}, history: emptyHistory() }
|
|
995
|
+
return { version: STORE_VERSION, thresholds: { CNY: DEFAULT_THRESHOLD }, accountThresholds: {}, sessions: {}, officialProviders: [], knownProviders: [], plans: {}, preferences: {}, history: emptyHistory() }
|
|
968
996
|
}
|
|
969
997
|
|
|
970
998
|
function readStoreFile(path) {
|
|
@@ -2025,6 +2053,27 @@ export function apply(ctx, config) {
|
|
|
2025
2053
|
return json(res, 200, { ok: true, pricing: pricingSnapshot() })
|
|
2026
2054
|
},
|
|
2027
2055
|
})
|
|
2056
|
+
const disposePreferences = ctx.webServer.register({
|
|
2057
|
+
kind: 'exact',
|
|
2058
|
+
path: '/api/wallet/preferences',
|
|
2059
|
+
handler: async (req, res) => {
|
|
2060
|
+
if (req.method === 'GET') return json(res, 200, { ok: true, entries: { ...store.preferences } })
|
|
2061
|
+
if (req.method !== 'POST') return json(res, 405, { ok: false, error: 'method-not-allowed' })
|
|
2062
|
+
if (usageStorageLocked) return json(res, 423, { ok: false, error: 'usage-storage-locked' })
|
|
2063
|
+
const body = await readBody(req)
|
|
2064
|
+
const entries = body && body.entries
|
|
2065
|
+
if (entries === null || typeof entries !== 'object' || Array.isArray(entries)) {
|
|
2066
|
+
return json(res, 400, { ok: false, error: 'entries object is required' })
|
|
2067
|
+
}
|
|
2068
|
+
const keys = Object.keys(entries)
|
|
2069
|
+
if (keys.length > UI_PREFERENCE_KEYS.size) return json(res, 400, { ok: false, error: 'invalid-preferences' })
|
|
2070
|
+
const patch = normalizeUiPreferences(entries)
|
|
2071
|
+
if (Object.keys(patch).length !== keys.length) return json(res, 400, { ok: false, error: 'invalid-preferences' })
|
|
2072
|
+
store.preferences = normalizeUiPreferences({ ...store.preferences, ...patch })
|
|
2073
|
+
scheduleSave(ctx.logger)
|
|
2074
|
+
return json(res, 200, { ok: true, entries: { ...store.preferences } })
|
|
2075
|
+
},
|
|
2076
|
+
})
|
|
2028
2077
|
const disposeThreshold = ctx.webServer.register({
|
|
2029
2078
|
kind: 'exact',
|
|
2030
2079
|
path: '/api/wallet/threshold',
|
|
@@ -2226,6 +2275,7 @@ historyEventCount = store.history && store.history.events ? Object.keys(store.hi
|
|
|
2226
2275
|
disposeSnapshot()
|
|
2227
2276
|
disposeHealth()
|
|
2228
2277
|
disposePricingRefresh()
|
|
2278
|
+
disposePreferences()
|
|
2229
2279
|
disposeThreshold()
|
|
2230
2280
|
disposeRefresh()
|
|
2231
2281
|
disposeClear()
|
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.
|
|
26
|
+
var WALLET_VERSION = '0.3.4'
|
|
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'
|
|
@@ -52,6 +52,12 @@ window.__ModuleLoader__.load({
|
|
|
52
52
|
var CHIP_EDGE_MARGIN = 2
|
|
53
53
|
var CHIP_EDGE_SNAP = 32
|
|
54
54
|
var CHIP_HOME_SNAP = 96
|
|
55
|
+
var BACKED_PREFERENCE_KEYS = new Set([
|
|
56
|
+
CHIP_STYLE_KEY, CHIP_BALANCE_ONLY_KEY, DATA_VISIBILITY_KEY, CHIP_SCALE_KEY,
|
|
57
|
+
NOTIFY_CONFIG_KEY, LOW_BLINK_KEY, PEAK_RING_KEY, PEAK_ORIENT_KEY,
|
|
58
|
+
PEAK_RECHARGE_KEY, PEAK_SCALE_KEY, PEAK_DOCK_KEY, PEAK_NOTIFY_KEY,
|
|
59
|
+
PERMANENT_DELETE_KEY
|
|
60
|
+
])
|
|
55
61
|
|
|
56
62
|
/**
|
|
57
63
|
* Platform-specific behavior lives here. Desktop wrappers may define
|
|
@@ -88,10 +94,12 @@ window.__ModuleLoader__.load({
|
|
|
88
94
|
try {
|
|
89
95
|
nativeStorage.setItem(key, value)
|
|
90
96
|
delete memoryOnly[key]
|
|
97
|
+
queuePersistentPreference(key, value)
|
|
91
98
|
return
|
|
92
99
|
} catch (e) { /* fall through to the memory-only marker */ }
|
|
93
100
|
}
|
|
94
101
|
memoryOnly[key] = true
|
|
102
|
+
queuePersistentPreference(key, value)
|
|
95
103
|
},
|
|
96
104
|
removeItem: function (key) {
|
|
97
105
|
delete memory[key]
|
|
@@ -293,6 +301,116 @@ window.__ModuleLoader__.load({
|
|
|
293
301
|
}
|
|
294
302
|
|
|
295
303
|
var compatibility = createCompatibilityAdapter(window)
|
|
304
|
+
var preferenceBackupQueue = Object.create(null)
|
|
305
|
+
var preferenceBackupTimer = null
|
|
306
|
+
var preferenceBackupRetries = 0
|
|
307
|
+
var preferenceHydrationPromise = null
|
|
308
|
+
var preferenceTouchedKeys = new Set()
|
|
309
|
+
|
|
310
|
+
function queuePersistentPreference(key, value) {
|
|
311
|
+
if (!BACKED_PREFERENCE_KEYS.has(key) || typeof value !== 'string') return
|
|
312
|
+
preferenceTouchedKeys.add(key)
|
|
313
|
+
preferenceBackupQueue[key] = value
|
|
314
|
+
preferenceBackupRetries = 0
|
|
315
|
+
if (preferenceBackupTimer === null) {
|
|
316
|
+
if (typeof window.setTimeout === 'function') preferenceBackupTimer = window.setTimeout(flushPersistentPreferences, 80)
|
|
317
|
+
else void flushPersistentPreferences()
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function flushPersistentPreferences() {
|
|
322
|
+
if (preferenceBackupTimer !== null) {
|
|
323
|
+
if (typeof window.clearTimeout === 'function') window.clearTimeout(preferenceBackupTimer)
|
|
324
|
+
preferenceBackupTimer = null
|
|
325
|
+
}
|
|
326
|
+
var entries = preferenceBackupQueue
|
|
327
|
+
preferenceBackupQueue = Object.create(null)
|
|
328
|
+
if (Object.keys(entries).length === 0 || typeof fetch !== 'function') return Promise.resolve(null)
|
|
329
|
+
return fetch('/api/wallet/preferences', {
|
|
330
|
+
method: 'POST',
|
|
331
|
+
headers: { 'content-type': 'application/json' },
|
|
332
|
+
body: JSON.stringify({ entries: entries }),
|
|
333
|
+
keepalive: true
|
|
334
|
+
}).then(function (response) {
|
|
335
|
+
if (!response || response.ok === false) throw new Error('preference-backup-failed')
|
|
336
|
+
preferenceBackupRetries = 0
|
|
337
|
+
return typeof response.json === 'function' ? response.json() : null
|
|
338
|
+
}).catch(function () {
|
|
339
|
+
Object.keys(entries).forEach(function (key) {
|
|
340
|
+
if (!Object.hasOwn(preferenceBackupQueue, key)) preferenceBackupQueue[key] = entries[key]
|
|
341
|
+
})
|
|
342
|
+
if (preferenceBackupRetries < 3 && preferenceBackupTimer === null && typeof window.setTimeout === 'function') {
|
|
343
|
+
preferenceBackupRetries += 1
|
|
344
|
+
preferenceBackupTimer = window.setTimeout(flushPersistentPreferences, preferenceBackupRetries * 750)
|
|
345
|
+
}
|
|
346
|
+
return null
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function localPreferenceEntries() {
|
|
351
|
+
var entries = {}
|
|
352
|
+
BACKED_PREFERENCE_KEYS.forEach(function (key) {
|
|
353
|
+
var value = compatibility.storage.getItem(key)
|
|
354
|
+
if (typeof value === 'string') entries[key] = value
|
|
355
|
+
})
|
|
356
|
+
return entries
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function canonicalPreferenceEntries(value) {
|
|
360
|
+
var entries = Object.assign({}, value || {})
|
|
361
|
+
if (entries[CHIP_STYLE_KEY] === 'text') {
|
|
362
|
+
entries[CHIP_STYLE_KEY] = 'standard'
|
|
363
|
+
if (!Object.hasOwn(entries, CHIP_BALANCE_ONLY_KEY)) entries[CHIP_BALANCE_ONLY_KEY] = 'true'
|
|
364
|
+
}
|
|
365
|
+
return entries
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function hydratePersistentPreferences() {
|
|
369
|
+
if (preferenceHydrationPromise !== null) return preferenceHydrationPromise
|
|
370
|
+
if (typeof fetch !== 'function') return Promise.resolve(null)
|
|
371
|
+
preferenceHydrationPromise = fetch('/api/wallet/preferences').then(function (response) {
|
|
372
|
+
if (!response || response.ok === false || typeof response.json !== 'function') throw new Error('preference-read-failed')
|
|
373
|
+
return response.json()
|
|
374
|
+
}).then(function (payload) {
|
|
375
|
+
var serverEntries = canonicalPreferenceEntries(payload && payload.ok && payload.entries && typeof payload.entries === 'object' ? payload.entries : {})
|
|
376
|
+
var rawLocalEntries = localPreferenceEntries()
|
|
377
|
+
var localEntries = canonicalPreferenceEntries(rawLocalEntries)
|
|
378
|
+
var merged = {}
|
|
379
|
+
Object.keys(serverEntries).forEach(function (key) {
|
|
380
|
+
if (BACKED_PREFERENCE_KEYS.has(key) && typeof serverEntries[key] === 'string') merged[key] = serverEntries[key]
|
|
381
|
+
})
|
|
382
|
+
Object.keys(localEntries).forEach(function (key) {
|
|
383
|
+
if (!Object.hasOwn(merged, key) || preferenceTouchedKeys.has(key)) merged[key] = localEntries[key]
|
|
384
|
+
})
|
|
385
|
+
Object.keys(merged).forEach(function (key) {
|
|
386
|
+
if (!Object.hasOwn(rawLocalEntries, key) || rawLocalEntries[key] !== merged[key]) compatibility.storage.setItem(key, merged[key])
|
|
387
|
+
else queuePersistentPreference(key, merged[key])
|
|
388
|
+
})
|
|
389
|
+
compatibility.dispatch(SETTINGS_EVENT)
|
|
390
|
+
compatibility.dispatch(NOTIFY_CONFIG_EVENT)
|
|
391
|
+
compatibility.dispatch(PEAK_RING_EVENT)
|
|
392
|
+
compatibility.dispatch(PERMANENT_DELETE_EVENT)
|
|
393
|
+
return merged
|
|
394
|
+
}).catch(function () {
|
|
395
|
+
preferenceHydrationPromise = null
|
|
396
|
+
return null
|
|
397
|
+
})
|
|
398
|
+
return preferenceHydrationPromise
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function installPersistentPreferenceSync() {
|
|
402
|
+
hydratePersistentPreferences()
|
|
403
|
+
var retries = typeof window.setTimeout === 'function' ? [1000, 4000].map(function (delay) {
|
|
404
|
+
return window.setTimeout(function () { if (preferenceHydrationPromise === null) hydratePersistentPreferences() }, delay)
|
|
405
|
+
}) : []
|
|
406
|
+
function flushOnHide() { void flushPersistentPreferences() }
|
|
407
|
+
if (typeof window.addEventListener === 'function') window.addEventListener('pagehide', flushOnHide)
|
|
408
|
+
return function () {
|
|
409
|
+
if (typeof window.clearTimeout === 'function') retries.forEach(function (timer) { window.clearTimeout(timer) })
|
|
410
|
+
if (typeof window.removeEventListener === 'function') window.removeEventListener('pagehide', flushOnHide)
|
|
411
|
+
void flushPersistentPreferences()
|
|
412
|
+
}
|
|
413
|
+
}
|
|
296
414
|
|
|
297
415
|
function openOfficialRecharge() {
|
|
298
416
|
var url = 'https://platform.deepseek.com/top_up'
|
|
@@ -1791,6 +1909,36 @@ window.__ModuleLoader__.load({
|
|
|
1791
1909
|
try { return compatibility.storage.getItem(PEAK_DOCK_KEY) || 'sidebar' } catch (e) { return 'sidebar' }
|
|
1792
1910
|
})
|
|
1793
1911
|
var settingsModelSelection = useCurrentModelSelection(props.sessionsService, props.modelDirectories, props.modelAware === true)
|
|
1912
|
+
React.useEffect(function () {
|
|
1913
|
+
if (typeof window.addEventListener !== 'function') return
|
|
1914
|
+
function syncPreferencesFromStorage() {
|
|
1915
|
+
try {
|
|
1916
|
+
var savedVisibility = compatibility.storage.getItem(DATA_VISIBILITY_KEY)
|
|
1917
|
+
setVisibility(savedVisibility === null ? normalizeDataVisibility(null) : normalizeDataVisibility(JSON.parse(savedVisibility)))
|
|
1918
|
+
} catch (e) { /* ignore */ }
|
|
1919
|
+
try { setChipStyle(normalizeChipStyle(compatibility.storage.getItem(CHIP_STYLE_KEY))) } catch (e) { /* ignore */ }
|
|
1920
|
+
try { setBalanceOnly(readBalanceOnly()) } catch (e) { /* ignore */ }
|
|
1921
|
+
try { setScale(normalizeChipScale(compatibility.storage.getItem(CHIP_SCALE_KEY))) } catch (e) { /* ignore */ }
|
|
1922
|
+
try {
|
|
1923
|
+
var notify = readNotifyConfig()
|
|
1924
|
+
setNotifyEnabled(notify.enabled)
|
|
1925
|
+
setNotifyTimeout(notify.timeout === 0 ? 'keep' : String(notify.timeout))
|
|
1926
|
+
} catch (e) { /* ignore */ }
|
|
1927
|
+
try { setLowBlinkEnabled(compatibility.storage.getItem(LOW_BLINK_KEY) !== 'false') } catch (e) { /* ignore */ }
|
|
1928
|
+
try { setPdEnabled(compatibility.storage.getItem(PERMANENT_DELETE_KEY) === 'true') } catch (e) { /* ignore */ }
|
|
1929
|
+
try { setRingEnabled(compatibility.storage.getItem(PEAK_RING_KEY) !== 'false') } catch (e) { /* ignore */ }
|
|
1930
|
+
try { setPeakNotifyEnabled(compatibility.storage.getItem(PEAK_NOTIFY_KEY) === 'true') } catch (e) { /* ignore */ }
|
|
1931
|
+
try { setPeakOrient(compatibility.storage.getItem(PEAK_ORIENT_KEY) || 'horizontal') } catch (e) { /* ignore */ }
|
|
1932
|
+
try { setPeakRecharge(compatibility.storage.getItem(PEAK_RECHARGE_KEY) !== 'false') } catch (e) { /* ignore */ }
|
|
1933
|
+
try {
|
|
1934
|
+
var savedPeakScale = Number.parseFloat(compatibility.storage.getItem(PEAK_SCALE_KEY))
|
|
1935
|
+
setPeakScale(Number.isFinite(savedPeakScale) ? Math.min(1.2, Math.max(1, savedPeakScale)) : 1)
|
|
1936
|
+
} catch (e) { /* ignore */ }
|
|
1937
|
+
try { setPeakDock(compatibility.storage.getItem(PEAK_DOCK_KEY) || 'sidebar') } catch (e) { /* ignore */ }
|
|
1938
|
+
}
|
|
1939
|
+
window.addEventListener(SETTINGS_EVENT, syncPreferencesFromStorage)
|
|
1940
|
+
return function () { window.removeEventListener(SETTINGS_EVENT, syncPreferencesFromStorage) }
|
|
1941
|
+
}, [])
|
|
1794
1942
|
useLayoutEffect(function () {
|
|
1795
1943
|
var node = settingsSectionRef.current
|
|
1796
1944
|
var dialog = node && typeof node.closest === 'function' ? node.closest('[role="dialog"]') : null
|
|
@@ -4968,6 +5116,7 @@ window.__ModuleLoader__.load({
|
|
|
4968
5116
|
ctx.inject(['sessions'], function (scope) {
|
|
4969
5117
|
scope.effect(function () { return installCompletionNotifier(scope) }, 'dsh-wallet: completion notifications')
|
|
4970
5118
|
scope.effect(function () { return installMaidModelMenuGuard() }, 'dsh-wallet: maid model-menu repaint guard')
|
|
5119
|
+
scope.effect(function () { return installPersistentPreferenceSync() }, 'dsh-wallet: durable preference sync')
|
|
4971
5120
|
})
|
|
4972
5121
|
ctx.inject(['slots', 'conversation', 'modelDirectories'], function (scope) {
|
|
4973
5122
|
scope.effect(function () {
|
|
@@ -5039,6 +5188,8 @@ window.__ModuleLoader__.load({
|
|
|
5039
5188
|
selectBalanceInfo: selectBalanceInfo,
|
|
5040
5189
|
balanceErrorText: balanceErrorText,
|
|
5041
5190
|
installCompletionNotifier: installCompletionNotifier,
|
|
5191
|
+
hydratePersistentPreferences: hydratePersistentPreferences,
|
|
5192
|
+
flushPersistentPreferences: flushPersistentPreferences,
|
|
5042
5193
|
normalizeDataVisibility: normalizeDataVisibility,
|
|
5043
5194
|
normalizeNotifyConfig: normalizeNotifyConfig,
|
|
5044
5195
|
normalizeChipLayout: normalizeChipLayout,
|
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
|
+
"version": "0.3.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|