deepseek-harness-wallet 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,6 +1,13 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project are documented here.
3
+ All notable changes to this project are documented in this file.
4
+
5
+ ## 0.1.5 - 2026-08-18
6
+
7
+ - 修复输入框内的余额标签被固定压缩成 44px 极简显示的问题,现在按实际可用空间在完整 / 紧凑 / 极简三档间自动切换。 / Fixed the composer chip always collapsing to its 44px compact value; it now switches between full, fit, and compact layouts by measured space.
8
+ - 修复缩放比例不等于 100% 时被误判为空间不足的问题。 / Fixed compact mode being mis-triggered whenever the scale was not 100%.
9
+ - 修复芯片拖到输入框旁悬浮时被其他面板遮挡的问题。 / Fixed docked chips being covered by neighbouring host panels.
10
+ - 输入框内缩放上限调整为 120%,悬浮 / 侧边停靠时仍可到 125%。 / The scale slider is capped at 120% in the composer row; floating and side docks keep the full 75–125% range.
4
11
 
5
12
  ## 0.1.4 - 2026-08-17
6
13
 
package/README.md CHANGED
@@ -1,26 +1,18 @@
1
1
  # DeepSeek Harness Control Center
2
2
 
3
- ### DeepSeek Harness monitoring, alerts, recharge, and session control center · DeepSeek Harness 监控、提醒、充值与会话控制中心
3
+ [![npm version](https://img.shields.io/npm/v/deepseek-harness-wallet?label=npm&color=5965d8)](https://www.npmjs.com/package/deepseek-harness-wallet)
4
+ [![GitHub release](https://img.shields.io/github/v/release/feibi-mochi/deepseek-harness-control-center?label=release&color=5965d8)](https://github.com/feibi-mochi/deepseek-harness-control-center/releases)
5
+ [![CI](https://github.com/feibi-mochi/deepseek-harness-control-center/actions/workflows/validate.yml/badge.svg)](https://github.com/feibi-mochi/deepseek-harness-control-center/actions/workflows/validate.yml)
6
+ [![DeepSeek Harness](https://img.shields.io/badge/DeepSeek%20Harness-0.1.0--rc.6-4aa3ff)](https://github.com/deepseek-ai/DeepSeek-Harness)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-3b7a57)](./LICENSE)
4
8
 
5
- [English](./README.md) · [简体中文](https://github.com/feibi-mochi/deepseek-harness-control-center/blob/main/docs/i18n/README.zh-CN.md)
9
+ **DeepSeek Harness monitoring, alerts, recharge, and session control center.**
6
10
 
7
- **Version:** 0.1.4 · **DeepSeek Harness:** 0.1.0-rc.6 · **License:** MIT
11
+ `Balance ¥5.89 · Session ¥0.72 · Official 18.8M | Third-party 800K · ↗ Recharge`
8
12
 
9
- **A local-first monitoring and control companion for DeepSeek Harness Web.**
13
+ [English](./README.md) · [简体中文](https://github.com/feibi-mochi/deepseek-harness-control-center/blob/main/docs/i18n/README.zh-CN.md) · [Install](#install) · [Compatibility](#browser-desktop-and-os-compatibility) · [Changelog](./CHANGELOG.md)
10
14
 
11
- DeepSeek Harness can keep several conversations and model providers active at once, but the information needed to manage them is normally scattered: the official account balance is on one page, token usage is buried in stream events, a long-running conversation may finish in a background tab, and session cleanup is separate again. DeepSeek Harness Control Center brings those signals and controls into one small surface beside the composer while retaining `deepseek-harness-wallet` as its installation and compatibility identifier. It is designed to answer three everyday questions without interrupting the conversation: **How much official balance remains? What has this conversation used? Does anything need my attention?**
12
-
13
- The resident chip combines the live DeepSeek balance, the open conversation's estimated official spend, official token usage, third-party token totals, and the official recharge shortcut. Accounting is isolated twice—by conversation and by provider—so one conversation cannot borrow another conversation's counters, and third-party traffic cannot become DeepSeek spend. Official cost is accumulated when each usage event arrives using the price active at that moment; later peak/off-peak changes therefore do not rewrite historical estimates. The detail panel expands this summary into per-currency balance rows, token splits, session spend, refresh controls, and a configurable CNY low-balance threshold.
14
-
15
- Monitoring is paired with deliberate actions. A low balance changes the wallet's visual state and emits one alert until the balance recovers. The recharge shortcut always targets DeepSeek's fixed official top-up URL, and the first use shows the destination before leaving Harness. Completion reminders cover a different kind of waiting: they can remain until manually dismissed or close after a selected delay, combine simultaneous completions into a queue, coordinate across tabs, and open the relevant conversation when clicked. If a system notification cannot be shown, the same event falls back to an in-page notice instead of disappearing silently.
16
-
17
- The interface is meant to live with the user's workspace rather than occupy it. The chip may remain in the composer, move freely, preview and snap to supported page edges or regions, switch between compact horizontal and vertical arrangements, scale from the control panel, or minimize directly to a movable dot. If the composer becomes too narrow for the full home chip, it keeps a clickable balance/token value instead of overflowing underneath the model selector. Official and third-party data can be shown independently while preventing an empty all-hidden state. The details panel itself is draggable and remembers its last position, and saved positions are fitted back into the viewport when the window changes.
18
-
19
- Session controls keep two very different operations separate. **Clear current-session wallet data** removes only this plugin's balance-derived spend and token counters for the open conversation; it does not delete the conversation. **Permanent session deletion** is an optional host integration, not a deletion engine supplied by this npm package. It appears only when the current DSH host advertises a real deletion path, remains opt-in, and still requires confirmation; unsupported hosts keep the setting disabled. This boundary prevents a decorative switch from promising an operation the surrounding application cannot perform.
20
-
21
- The trust model is intentionally narrow. The plugin registers no model tools, injects no prompt text, and adds no model-token overhead. Its host-side accounting is kept in the local DSH storage, while layout and reminder preferences use browser-compatible local storage. The existing DSH credential is used only for the official balance request, and recharge navigation is fixed to the official DeepSeek domain. The plugin does not collect payment details, estimate third-party money values without a price source, or merge balances across currencies.
22
-
23
- Compatibility is based on capabilities rather than operating-system names. A normal DSH Web environment uses standards-based browser behavior; restricted Electron, Tauri, or other desktop shells can provide a small adapter for notifications, storage, and external links without changing accounting or UI logic. Optional features degrade visibly when a capability is absent. This keeps the project useful as a broader Harness monitoring and session-control center while leaving room for future provider adapters, history views, and other management features without changing the established npm package identity.
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.
24
16
 
25
17
  > If DeepSeek Harness Control Center helps you, please consider leaving a ⭐ Star. Thank you!
26
18
 
@@ -41,6 +33,24 @@ Compatibility is based on capabilities rather than operating-system names. A nor
41
33
  - **Theme-native UI** — built entirely on `--dsw-alias-*` theme variables, so light and dark themes both render correctly; the panel closes when you click outside and flips open-direction near screen edges.
42
34
  - **Clear current-session wallet data** — one button clears only the open conversation's token/cost records; it does not delete the conversation, and every other conversation is untouched.
43
35
 
36
+ ## Project overview
37
+
38
+ ### One place for the signals that matter
39
+
40
+ DeepSeek Harness can keep several conversations and model providers active at once, but balance, usage, background-task status, and session actions normally live in different places. Control Center brings the information worth checking repeatedly beside the composer, so the current workflow can answer three questions at a glance: **How much official balance remains? What has this conversation used? Does anything need attention?**
41
+
42
+ ### Present when needed, quiet when not
43
+
44
+ The project is designed around quick reading and in-context action rather than another full-page dashboard. Its compact surface expands only when needed, adapts to the available space, and leaves layout and reminder behavior under the user's control. Accounting remains separated by conversation and provider, while wallet-data cleanup and permanent session deletion remain intentionally different operations.
45
+
46
+ ### Extensible without hiding the boundaries
47
+
48
+ The npm package handles monitoring and interface behavior; optional host powers are enabled only when DSH actually provides them. That capability-based boundary keeps unsupported actions visibly unavailable and gives browsers or desktop wrappers a small, reviewable adaptation surface. Future providers and controls can therefore be added without changing the established `deepseek-harness-wallet` package identity or silently expanding what the plugin is trusted to do.
49
+
50
+ > **Want permanent session deletion?** It cannot be enabled by configuring the plugin alone. Give the [integration guide](./integrations/dsh-session-delete/README.md) and [Agent adaptation prompt](./integrations/dsh-session-delete/AGENT_PROMPT.md) to an Agent with access to the buildable DSH source. The control-panel switch becomes available only after the host implementation is built, tested, and advertises the capability.
51
+
52
+ Details: [compatibility](#browser-desktop-and-os-compatibility) · [data and trust](#data--trust) · [pricing](#pricing-timeline)
53
+
44
54
  ## Install
45
55
 
46
56
  From npm:
package/lib/client.js CHANGED
@@ -250,7 +250,8 @@ window.__ModuleLoader__.load({
250
250
 
251
251
  var css = [
252
252
  '.dshw_anchor{display:inline-flex;align-items:center;min-height:22px;min-width:0;vertical-align:middle}',
253
- '.dshw_anchorHome{container-name:dshw-home;container-type:inline-size;overflow:hidden;min-width:44px}',
253
+ '.dshw_anchorHome{overflow:hidden;min-width:44px}',
254
+ '.dshw_chipLift{z-index:80!important}',
254
255
  '.dshw_anchorHome>.dshw_chip{box-sizing:border-box;max-width:100%;min-width:0;overflow:hidden}',
255
256
  '.dshw_chip{border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));background:transparent;height:22px;color:var(--dsw-alias-label-primary,#1f2328);white-space:nowrap;border-radius:999px;align-items:stretch;font-size:12px;line-height:1;display:inline-flex;position:relative;touch-action:none;user-select:none;cursor:grab}',
256
257
  '.dshw_chip:hover,.dshw_chip:focus-within{border-color:var(--dsw-alias-brand-primary,#4aa3ff)}',
@@ -307,8 +308,13 @@ window.__ModuleLoader__.load({
307
308
  '.dshw_notice{box-sizing:border-box;width:100%;display:flex;align-items:flex-start;gap:8px;padding:10px 10px 10px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.15));border-radius:9px;background:var(--dsw-alias-bg-overlay,#fff);color:var(--dsw-alias-label-primary,#1f2328);box-shadow:0 6px 20px rgba(0,0,0,.2);font-size:12px;line-height:1.45;pointer-events:auto;cursor:pointer}',
308
309
  '.dshw_noticeCopy{min-width:0;display:flex;flex:1;flex-direction:column;gap:2px}.dshw_noticeCopy span{white-space:pre-line;overflow-wrap:anywhere}',
309
310
  '.dshw_noticeClose{flex:none;width:24px;height:24px;border:0;border-radius:5px;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer}.dshw_noticeClose:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06))}',
310
- '@container dshw-home (max-width:90px){.dshw_anchorHome .dshw_chip{width:100%}.dshw_anchorHome .dshw_chipMain{box-sizing:border-box;width:100%;min-width:0;overflow:hidden;justify-content:center;padding:0 3px}.dshw_anchorHome .dshw_chipMain>span{display:none}.dshw_anchorHome .dshw_chipMain>.dshw_homePrimary{display:inline;min-width:0;overflow:hidden;text-overflow:ellipsis}.dshw_anchorHome .dshw_homePrimaryLabel{display:none}.dshw_anchorHome .dshw_recharge{display:none}}',
311
- '@supports not (container-type:inline-size){@media (max-width:640px){.dshw_anchorHome .dshw_chip{width:100%}.dshw_anchorHome .dshw_chipMain{box-sizing:border-box;width:100%;min-width:0;overflow:hidden;justify-content:center;padding:0 3px}.dshw_anchorHome .dshw_chipMain>span{display:none}.dshw_anchorHome .dshw_chipMain>.dshw_homePrimary{display:inline;min-width:0;overflow:hidden;text-overflow:ellipsis}.dshw_anchorHome .dshw_homePrimaryLabel{display:none}.dshw_anchorHome .dshw_recharge{display:none}}}',
311
+ // Home-fit and compact modes are toggled by measured space (see the
312
+ // home-space effect in WalletChip), not by container queries:
313
+ // container-type:inline-size on a content-sized flex item detaches its
314
+ // width from the chip, so flex layouts resolve it to min-width (44px)
315
+ // even when the row has room.
316
+ '.dshw_anchorHome.dshw_fit .dshw_chipMain>span:not(.dshw_homePrimary){display:none}',
317
+ '.dshw_anchorHome.dshw_compact .dshw_chip{width:100%}.dshw_anchorHome.dshw_compact .dshw_chipMain{box-sizing:border-box;width:100%;min-width:0;overflow:hidden;justify-content:center;padding:0 3px}.dshw_anchorHome.dshw_compact .dshw_chipMain>span{display:none}.dshw_anchorHome.dshw_compact .dshw_chipMain>.dshw_homePrimary{display:inline;min-width:0;overflow:hidden;text-overflow:ellipsis}.dshw_anchorHome.dshw_compact .dshw_homePrimaryLabel{display:none}.dshw_anchorHome.dshw_compact .dshw_recharge{display:none}',
312
318
  '@media (max-width:420px){.dshw_chipDocked:not(.dshw_chipVertical){max-width:calc(100vw - 4px);font-size:10px}.dshw_chipDocked:not(.dshw_chipVertical) .dshw_chipMain{gap:3px;padding:0 4px}.dshw_chipDocked:not(.dshw_chipVertical) .dshw_recharge{padding:0 4px}}'
313
319
  ].join('')
314
320
 
@@ -687,6 +693,18 @@ window.__ModuleLoader__.load({
687
693
  return node || null
688
694
  }
689
695
 
696
+ function isStackingContext(style) {
697
+ if (!style) return false
698
+ if (style.position === 'fixed' || style.position === 'sticky') return true
699
+ if (style.zIndex !== 'auto' && style.position !== 'static') return true
700
+ if (style.transform !== 'none' || style.filter !== 'none' || style.perspective !== 'none') return true
701
+ if (style.willChange === 'transform' || style.willChange === 'filter' || style.willChange === 'opacity' || style.willChange === 'z-index') return true
702
+ if (style.contain && style.contain !== 'none') return true
703
+ if (style.isolation === 'isolate') return true
704
+ if (style.opacity !== '1') return true
705
+ return false
706
+ }
707
+
690
708
  function findComposerRect(node, viewportWidth, viewportHeight) {
691
709
  var composer = findComposerNode(node, viewportWidth, viewportHeight)
692
710
  return composer && typeof composer.getBoundingClientRect === 'function' ? composer.getBoundingClientRect() : null
@@ -799,6 +817,8 @@ window.__ModuleLoader__.load({
799
817
  var [chipScale, setChipScale] = React.useState(function () {
800
818
  try { return normalizeChipScale(compatibility.storage.getItem(CHIP_SCALE_KEY)) } catch (e) { return 1 }
801
819
  })
820
+ var [homeMode, setHomeMode] = React.useState('full')
821
+ var chipLiftHostRef = React.useRef(null)
802
822
  var [dataVisibility, setDataVisibility] = React.useState(function () {
803
823
  try {
804
824
  var savedVisibility = compatibility.storage.getItem(DATA_VISIBILITY_KEY)
@@ -855,6 +875,8 @@ window.__ModuleLoader__.load({
855
875
 
856
876
  function saveChipScale(next) {
857
877
  next = normalizeChipScale(next)
878
+ var cap = chipLayoutRef.current.dock === 'home' ? 1.2 : 1.25
879
+ if (next > cap) next = cap
858
880
  chipScaleRef.current = next
859
881
  setChipScale(next)
860
882
  try { compatibility.storage.setItem(CHIP_SCALE_KEY, String(next)) } catch (e) { /* ignore */ }
@@ -1211,6 +1233,35 @@ window.__ModuleLoader__.load({
1211
1233
  if (event.preventDefault) event.preventDefault()
1212
1234
  }
1213
1235
 
1236
+ // A docked chip is position:fixed, but it still renders inside the host
1237
+ // composer subtree; hosts commonly wrap that subtree in a low stacking
1238
+ // context (dsh uses z-index:1), which caps the chip under neighbouring
1239
+ // top-level panels. The node must stay in place so React's delegated
1240
+ // events keep working, so instead lift the outermost trapping ancestor
1241
+ // context while any dock mode is active.
1242
+ useLayoutEffect(function () {
1243
+ if (chipLayout.dock === 'home') return
1244
+ var node = chipAnchorRef.current
1245
+ if (node === null || typeof window === 'undefined' || !window.getComputedStyle) return
1246
+ var outermost = null
1247
+ while (node && node !== document.body) {
1248
+ if (isStackingContext(window.getComputedStyle(node))) outermost = node
1249
+ node = node.parentElement
1250
+ }
1251
+ chipLiftHostRef.current = outermost
1252
+ if (outermost && outermost.classList) outermost.classList.add('dshw_chipLift')
1253
+ return function () {
1254
+ if (outermost && outermost.classList) outermost.classList.remove('dshw_chipLift')
1255
+ if (chipLiftHostRef.current === outermost) chipLiftHostRef.current = null
1256
+ }
1257
+ }, [chipLayout.dock])
1258
+
1259
+ // A stored 125% from a floating/side dock must come back down when the
1260
+ // chip returns to the space-constrained home row.
1261
+ React.useEffect(function () {
1262
+ if (chipLayout.dock === 'home' && chipScaleRef.current > 1.2) saveChipScale(1.2)
1263
+ }, [chipLayout.dock])
1264
+
1214
1265
  useLayoutEffect(function () {
1215
1266
  var node = chipRef.current
1216
1267
  if (node === null) return
@@ -1218,6 +1269,55 @@ window.__ModuleLoader__.load({
1218
1269
  var measured = { width: rect.width, height: rect.height, scale: chipScaleRef.current }
1219
1270
  if (node.classList && node.classList.contains('dshw_chipVertical')) chipSizeRef.current.vertical = measured
1220
1271
  else chipSizeRef.current.horizontal = measured
1272
+ }, [chipLayout.dock, chipScale, data, showOfficial, showThird, homeMode])
1273
+
1274
+ useLayoutEffect(function () {
1275
+ if (chipLayout.dock !== 'home') return
1276
+ var anchor = chipAnchorRef.current
1277
+ var chip = chipRef.current
1278
+ if (!anchor || !chip) return
1279
+
1280
+ function measureHomeMode() {
1281
+ var current = chipAnchorRef.current
1282
+ var chipNode = chipRef.current
1283
+ if (!current || !chipNode) return null
1284
+ // Try each tier in place and let the composer row's own flex pressure
1285
+ // accept or squeeze it: a tier fits when the chip's allocated width
1286
+ // matches its content width. All readings come from the chip itself
1287
+ // so the scale slider's zoom factor cancels out; the anchor's
1288
+ // parent slot wrapper can be display:contents, so measuring
1289
+ // ancestors is not reliable.
1290
+ var compact = current.classList.contains('dshw_compact')
1291
+ current.classList.remove('dshw_compact')
1292
+ current.classList.remove('dshw_fit')
1293
+ var mode
1294
+ if (chipNode.clientWidth >= chipNode.scrollWidth - 1) {
1295
+ mode = 'full'
1296
+ } else {
1297
+ current.classList.add('dshw_fit')
1298
+ mode = chipNode.clientWidth >= chipNode.scrollWidth - 1 ? 'fit' : 'compact'
1299
+ current.classList.remove('dshw_fit')
1300
+ }
1301
+ if (compact) current.classList.add('dshw_compact')
1302
+ return mode
1303
+ }
1304
+
1305
+ function evaluateHomeSpace() {
1306
+ var next = measureHomeMode()
1307
+ if (next) setHomeMode(next)
1308
+ }
1309
+
1310
+ evaluateHomeSpace()
1311
+ window.addEventListener('resize', evaluateHomeSpace)
1312
+ var observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(evaluateHomeSpace) : null
1313
+ if (observer) {
1314
+ observer.observe(anchor)
1315
+ observer.observe(chip)
1316
+ }
1317
+ return function () {
1318
+ window.removeEventListener('resize', evaluateHomeSpace)
1319
+ if (observer) observer.disconnect()
1320
+ }
1221
1321
  }, [chipLayout.dock, chipScale, data, showOfficial, showThird])
1222
1322
 
1223
1323
  useLayoutEffect(function () {
@@ -1377,6 +1477,9 @@ window.__ModuleLoader__.load({
1377
1477
  var thirdTokens = third.tokens ? totalTokens(third.tokens) : 0
1378
1478
  var low = showOfficial && snapshot.lowBalance === true
1379
1479
  var chipDock = chipLayout.dock
1480
+ // The home toolbar row cannot fit the widest scale on narrow windows, so
1481
+ // cap the slider there; floating and side docks keep the full range.
1482
+ var scaleMaxPercent = chipDock === 'home' ? 120 : 125
1380
1483
  var chipVertical = chipDock === 'left' || chipDock === 'right' || chipDock === 'content-left'
1381
1484
  var chipClass = low ? 'dshw_chip dshw_chipLow' : 'dshw_chip'
1382
1485
  var cssZoom = compatibility.supportsCssZoom()
@@ -1455,7 +1558,7 @@ window.__ModuleLoader__.load({
1455
1558
  onClick: onRechargeClick
1456
1559
  }, '\u2197\u5145') : null)
1457
1560
 
1458
- var chipHost = React.createElement('span', { ref: chipAnchorRef, className: chipDock === 'home' ? 'dshw_anchor dshw_anchorHome' : 'dshw_anchor' }, chip)
1561
+ var chipHost = React.createElement('span', { ref: chipAnchorRef, className: chipDock === 'home' ? 'dshw_anchor dshw_anchorHome' + (homeMode === 'compact' ? ' dshw_compact' : homeMode === 'fit' ? ' dshw_fit' : '') : 'dshw_anchor' }, chip)
1459
1562
  var snapPreviewElement = snapPreview ? React.createElement('div', {
1460
1563
  className: snapPreview.vertical ? 'dshw_snapPreview dshw_snapPreviewVertical' : 'dshw_snapPreview',
1461
1564
  style: { left: snapPreview.x, top: snapPreview.y, width: snapPreview.width, height: snapPreview.height },
@@ -1531,7 +1634,7 @@ window.__ModuleLoader__.load({
1531
1634
  className: 'dshw_scaleInput',
1532
1635
  type: 'range',
1533
1636
  min: '75',
1534
- max: '125',
1637
+ max: String(scaleMaxPercent),
1535
1638
  step: '5',
1536
1639
  value: String(Math.round(chipScale * 100)),
1537
1640
  'aria-label': '钱包芯片比例',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "deepseek-harness-wallet",
3
3
  "description": "Local-first account monitoring, usage accounting, official recharge, completion reminders, flexible layout, and host-gated session controls for DeepSeek Harness.",
4
- "version": "0.1.4",
4
+ "version": "0.1.5",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {