dsh-connect-workbuddy 4.3.0 → 4.3.1

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,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.3.1 (2026-10-11)
4
+
5
+ ### Fixes
6
+
7
+ - **对话框积分读数现在关得掉:设置命名空间改为「跟随宿主」而非「猜名字」。**(issue #46)
8
+ - **现象**:设置卡片里取消勾选「在对话框显示 WBCN 积分」,勾选框随即显示为已关闭、`cordis.patch.yml` 里也确实落了 `regions.cn.showCreditsInMainUi: false`——**写入侧正常**——但切到该 provider 的会话,输入框左侧**仍然渲染** `WB CN · <余额>`。
9
+ - **根因(两段)**:① `src/client/index.tsx` 的命名空间解析只跑一次,且恰逢 describe 镜像未就绪(镜像由异步 `ctx.remote.settings.describe()` 填充)。旧代码把「镜像为空」当成「声明的名字一定对」,绑定 `forms.get('workbuddy')` 且此后永不复查;而 0.2.0 的设置命名空间**就是 Loader entry id**(`dsh-connect-workbuddy`),`ConfigFormController.derive()` 又是**精确匹配**,于是这个猜出来的表单永久停在 `{status:"unavailable", value:undefined}`。② `regionCreditsShownOf` 是 opt-out(`!== false` 即「显示」),`undefined` 于是被判为显示,读数照常渲染。**卡片本身显示正确**,因为 `WorkBuddyCard` 的 `creditsShownOn` 是 Host 优先——4.0.0 新增的读数 gate 没有继承这条规则。
10
+ - **修法一(治本)**:改用宿主现成范式 `configForms.whileServed([...])`(内部 `mirror.subscribe` + 立即 `sync`),把命名空间候选表 `WORKBUDDY_SETTINGS_NAMESPACE_CANDIDATES` 按**真实 entry id 优先、声明名最后**排序,并从 `served` 集合里现场挑选。**晚绑定必须重新注册**:渲染器按 entry 的**对象身份**永久缓存 inject 结果(`rootInjectCache` / `cachedRootInject` 里的 `if (!props) { props = runInject(...) }`),已注册 entry 不会再求值——所以旧注册被 dispose、新注册被创建。副作用是宿主不再服务该命名空间时绑定会被自动撤回。
11
+ - **修法二(治复发)**:让 gate 与卡片同源。Host 的 usage 载荷本就带 `showCreditsInMainUi`,现在它**压过**设置 scope(`composerCreditsRegionOf` 新增 `hostValue` 参数,`ComposerCredits` 在拿到文档后按它隐藏,并停掉 5 分钟轮询)。非布尔值(含 `error` 变体与旧文档)一律视为「宿主没说」,仍由 scope 定夺——这样镜像陈旧或未绑定时都不会再让读数复活。
12
+ - 回归测试 `tests/issue46-namespace-binding.spec.tsx` 14 条,覆盖:镜像后到时的重绑(原缺陷)、候选表顺序、`include:` 包裹的 entry id、声明名绝不被绑定、宿主停服时撤回、只开一个 watch;并钉住「死 scope 读作显示」这一现象本身与 Host 优先级。渲染层新增三条(Host 报关闭则隐藏、Host 报开启则显示、无该字段则沿用 scope)。**变异验证**:去掉 Host 优先级判断 → 1 红;把声明名排到候选表首位 → 3 红。
13
+
3
14
  ## 4.3.0 (2026-10-11)
4
15
 
5
16
  > **升级前请读这一条(行为变更)**:账号列表里的「删除」在 4.3.0 起是**「暂时隐藏」**——删掉后立即从列表消失,也不会被自动轮询带回来,但它是**可找回**的:点「重新检测账号」会把**所有**隐藏的账号一次性全部找回(不是只找回你刚删的那个),重新登录或导入同一账号也会清除该账号的隐藏记录。如果你的习惯是「删掉就永远不再看到它」,请在升级前确认这一语义可以接受。桌面 App 自身的登录**始终不被改动**——删除只是「本插件不再列出它」。上一版(4.2.0)不存在隐藏记录,因此**没有历史状态需要迁移**。
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, G as WORKBUDDY_APP_EXECUTABLE_ENV, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, Z as findWorkbuddyAppExecutableWithSource, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-jNlsUMZO.js";
2
+ import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, G as WORKBUDDY_APP_EXECUTABLE_ENV, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, Z as findWorkbuddyAppExecutableWithSource, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-BQkmibKu.js";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/bin.ts
package/lib/client.js CHANGED
@@ -309,6 +309,23 @@ window.__ModuleLoader__.load({
309
309
  * fallback and must not import Node-only host code.
310
310
  */
311
311
  const WORKBUDDY_SETTINGS_NS = "workbuddy";
312
+ /**
313
+ * Every namespace spelling the browser half follows when looking for the
314
+ * settings form the Host actually serves, in preference order.
315
+ *
316
+ * `describe()` keys a form by its Loader entry id, and the profile patch picks
317
+ * that id — so the plugin must ASK rather than assume. The observed spellings
318
+ * are the bare package name, the `include:`-wrapped id the live Desktop host
319
+ * uses, and the declared name as the last resort. `configForms.whileServed`
320
+ * follows all of them and re-runs when the mirror arrives, which is what keeps
321
+ * the binding correct on a host whose mirror populates after `apply` (the
322
+ * 4.3.0 readout defect, issue #46).
323
+ */
324
+ const WORKBUDDY_SETTINGS_NAMESPACE_CANDIDATES = [
325
+ "dsh-connect-workbuddy",
326
+ "include:dsh-connect-workbuddy",
327
+ WORKBUDDY_SETTINGS_NS
328
+ ];
312
329
  //#endregion
313
330
  //#region src/client/account-selection.ts
314
331
  /**
@@ -4563,6 +4580,17 @@ window.__ModuleLoader__.load({
4563
4580
  if (usage.status !== "signed-in") return void 0;
4564
4581
  return usage.credits?.total;
4565
4582
  }
4583
+ /**
4584
+ * The Host's per-region readout switch off a usage document, when it carries one.
4585
+ *
4586
+ * The `error` variant has no data at all, so it can only ever answer `undefined`
4587
+ * — "the Host did not say", which leaves the settings scope in charge.
4588
+ */
4589
+ function hostCreditsShownOf(usage) {
4590
+ if (usage === void 0 || usage.status === "error") return void 0;
4591
+ const value = usage.showCreditsInMainUi;
4592
+ return typeof value === "boolean" ? value : void 0;
4593
+ }
4566
4594
  /** Group the digits so a four-figure balance stays readable in a narrow row. */
4567
4595
  function formatCredits(value) {
4568
4596
  return value.toLocaleString("en-US", { maximumFractionDigits: 0 });
@@ -4649,8 +4677,9 @@ window.__ModuleLoader__.load({
4649
4677
  if (Array.isArray(data.accounts)) setAccountCredits(data.accounts);
4650
4678
  } catch {}
4651
4679
  }, [activeRegion]);
4680
+ const hostHidden = hostCreditsShownOf(usage) === false;
4652
4681
  (0, react.useEffect)(() => {
4653
- if (owned === void 0) return void 0;
4682
+ if (owned === void 0 || hostHidden) return void 0;
4654
4683
  let cancelled = false;
4655
4684
  let timer;
4656
4685
  const controller = new AbortController();
@@ -4665,7 +4694,11 @@ window.__ModuleLoader__.load({
4665
4694
  if (timer !== void 0) window.clearInterval(timer);
4666
4695
  controller.abort();
4667
4696
  };
4668
- }, [owned, fetchUsage]);
4697
+ }, [
4698
+ owned,
4699
+ hostHidden,
4700
+ fetchUsage
4701
+ ]);
4669
4702
  (0, react.useEffect)(() => {
4670
4703
  if (!open || activeRegion === void 0) return void 0;
4671
4704
  const controller = new AbortController();
@@ -4714,6 +4747,22 @@ window.__ModuleLoader__.load({
4714
4747
  }
4715
4748
  };
4716
4749
  if (owned === void 0) return null;
4750
+ /**
4751
+ * The Host's own answer for this region, when the document carries one.
4752
+ *
4753
+ * The Host is the AUTHORITY on whether this readout is wanted, exactly as the
4754
+ * card's `creditsShownOn` treats it — and here it is also the fix for a defect
4755
+ * shipped in 4.3.0 (issue #46): the gate's decision rests on the settings
4756
+ * scope, which on a host whose describe mirror lands late was bound to a
4757
+ * namespace nothing serves. Every read of that scope is then `undefined`, and
4758
+ * the opt-out rule (`!== false`) reads "shown" forever — so unticking the
4759
+ * checkbox wrote correctly and changed nothing on screen.
4760
+ *
4761
+ * Checked AFTER the fetch rather than before it because the answer only
4762
+ * arrives with the document. The cost is one usage read for a readout the user
4763
+ * may have hidden, against a persistent wrong number on screen.
4764
+ */
4765
+ if (hostCreditsShownOf(usage) === false) return null;
4717
4766
  const signedIn = usage?.status === "signed-in" ? usage : void 0;
4718
4767
  const total = signedIn === void 0 ? void 0 : creditsOf(signedIn);
4719
4768
  const signedOut = usage !== void 0 && usage.status === "signed-out";
@@ -4900,11 +4949,22 @@ window.__ModuleLoader__.load({
4900
4949
  * The region's own switches are applied HERE rather than inside the component,
4901
4950
  * so a region that is switched off (or whose provider is off) starts no fetch
4902
4951
  * loop at all.
4952
+ *
4953
+ * `hostValue` is the Host's own `showCreditsInMainUi` answer for this region,
4954
+ * when a usage document carrying it is already in hand. It OUTRANKS the settings
4955
+ * scope, exactly as the card's `creditsShownOn` does and for the same measured
4956
+ * reason: the browser's settings mirror is documented as stale for this
4957
+ * namespace on the affected deployments, and — the shipped 4.3.0 defect (issue
4958
+ * #46) — the scope can be bound to a namespace nothing serves, where every read
4959
+ * is `undefined` and the opt-out rule then reads "shown" forever. The Host is
4960
+ * the authority; the scope is the fallback for a Host line that predates the
4961
+ * field.
4903
4962
  */
4904
- function composerCreditsRegionOf(projection, scope) {
4963
+ function composerCreditsRegionOf(projection, scope, hostValue) {
4905
4964
  const provider = selectedProviderOf(projection);
4906
4965
  const region = provider === void 0 ? void 0 : WORKBUDDY_COMPOSER_PROVIDERS[provider];
4907
4966
  if (region === void 0) return void 0;
4967
+ if (typeof hostValue === "boolean") return hostValue ? region : void 0;
4908
4968
  const value = scope === void 0 ? void 0 : unwrapVolatileDeep(scope.getSnapshot().value);
4909
4969
  if (!regionCreditsShownOf(value, region)) return void 0;
4910
4970
  if (!regionEnabledOf(value, region)) return void 0;
@@ -5510,17 +5570,10 @@ window.__ModuleLoader__.load({
5510
5570
  const softGet = (name) => ctx.get(name);
5511
5571
  let settingsScope;
5512
5572
  const forms = softGet("configForms");
5513
- if (forms !== void 0) {
5514
- let ns = WORKBUDDY_SETTINGS_NS;
5515
- try {
5516
- const served = (forms.describe().getSnapshot().view?.namespaces ?? []).find((entry) => entry.ns === "workbuddy" || /workbuddy/i.test(entry.ns));
5517
- if (served !== void 0) ns = served.ns;
5518
- } catch {}
5519
- settingsScope = forms.get(ns);
5520
- }
5573
+ /** Register one card slot; returns its disposer, or undefined if it failed. */
5521
5574
  const registerCard = (slotName, key) => {
5522
5575
  try {
5523
- ctx.slots.inject(slotName, () => ctx.slots.register({
5576
+ return ctx.slots.inject(slotName, () => ctx.slots.register({
5524
5577
  name: slotName,
5525
5578
  key,
5526
5579
  priority: 30,
@@ -5531,10 +5584,9 @@ window.__ModuleLoader__.load({
5531
5584
  }, WorkBuddyCard));
5532
5585
  } catch (error) {
5533
5586
  console.error(`[dsh-connect-workbuddy] card slot "${slotName}" failed to register (host provider unaffected):`, error);
5587
+ return;
5534
5588
  }
5535
5589
  };
5536
- registerCard("plugins.bundle.config", "dsh-connect-workbuddy");
5537
- registerCard("plugins.row.config", "dsh-connect-workbuddy#dsh-connect-workbuddy");
5538
5590
  /**
5539
5591
  * Credit readout in the composer tool row (`conversation.input.left`, a
5540
5592
  * session-scope list slot).
@@ -5555,7 +5607,7 @@ window.__ModuleLoader__.load({
5555
5607
  */
5556
5608
  const registerComposerCredits = () => {
5557
5609
  try {
5558
- ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
5610
+ return ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
5559
5611
  name: "conversation.input.left",
5560
5612
  id: "dsh-connect-workbuddy-composer-credits",
5561
5613
  order: 4,
@@ -5567,9 +5619,84 @@ window.__ModuleLoader__.load({
5567
5619
  }, ComposerCreditsGate));
5568
5620
  } catch (error) {
5569
5621
  console.error("[dsh-connect-workbuddy] composer credits slot failed to register (host provider unaffected):", error);
5622
+ return;
5623
+ }
5624
+ };
5625
+ /**
5626
+ * Every slot this plugin contributes, and how to (re)create them.
5627
+ *
5628
+ * Kept as one list because the binding of `settingsScope` and the slot
5629
+ * registrations must move TOGETHER: see {@link bindSettingsScope} for why a
5630
+ * late-arriving namespace has to produce fresh registrations rather than a
5631
+ * mutated closure.
5632
+ */
5633
+ /**
5634
+ * Disposers of the slots registered for the CURRENT namespace binding.
5635
+ *
5636
+ * Declared before {@link bindSettingsScope} because that function closes
5637
+ * over it and reassigns it; the binding is only armed at the end of `apply`,
5638
+ * so this is initialised by then either way — hoisting it keeps that
5639
+ * ordering a non-requirement rather than a trap.
5640
+ */
5641
+ let liveDisposers = [];
5642
+ const registerAllSlots = () => [
5643
+ registerCard("plugins.bundle.config", "dsh-connect-workbuddy"),
5644
+ registerCard("plugins.row.config", "dsh-connect-workbuddy#dsh-connect-workbuddy"),
5645
+ registerComposerCredits()
5646
+ ].filter((disposer) => disposer !== void 0);
5647
+ /**
5648
+ * Bind `settingsScope` to whichever namespace the Host really serves, and
5649
+ * RE-REGISTER every slot when that answer changes.
5650
+ *
5651
+ * Why this is not a one-shot read at startup — the shipped 4.3.0 defect
5652
+ * (issue #46): the mirror is filled by an async `remote.settings.describe()`,
5653
+ * so at `apply` time `view` is usually still absent. The old code read that
5654
+ * absence as "the declared name must be right", bound
5655
+ * `forms.get('workbuddy')`, and never looked again. `ConfigFormController.
5656
+ * derive()` matches the namespace EXACTLY, so the guessed form stayed
5657
+ * `unavailable` forever; the composer gate — reading only that scope —
5658
+ * judged the opt-out switch `undefined !== false` and kept rendering the
5659
+ * readout. Unticking the box wrote `false` correctly and changed nothing on
5660
+ * screen.
5661
+ *
5662
+ * `whileServed` is the Host's own idiom for this (`mirror.subscribe` plus an
5663
+ * immediate sync). It is strictly better than re-reading the snapshot by
5664
+ * hand because it ALSO withdraws the binding if the Host stops serving our
5665
+ * namespace, and it hands `register` the set of namespaces actually served
5666
+ * — so the choice is made from live data instead of a captured guess.
5667
+ *
5668
+ * `register` runs on the unserved→served EDGE, not on every mirror update
5669
+ * (see the service's `sync`), so the disposal below is not a churn loop: it
5670
+ * clears whatever a previous edge left registered. That replacement is NOT
5671
+ * optional, because the renderer caches each entry's inject result by object
5672
+ * identity (`rootInjectCache` / `cachedRootInject` in
5673
+ * `dsh-client-ui-renderer`: `if (!props) { props = runInject(...) }`), so an
5674
+ * entry registered before the mirror landed is never re-evaluated. Late
5675
+ * binding can therefore only work if the OLD registrations are disposed and
5676
+ * NEW ones created — which is what this callback does.
5677
+ */
5678
+ const bindSettingsScope = () => {
5679
+ if (forms === void 0 || typeof forms.whileServed !== "function") {
5680
+ settingsScope = forms?.get(WORKBUDDY_SETTINGS_NS);
5681
+ liveDisposers = registerAllSlots();
5682
+ return;
5570
5683
  }
5684
+ ctx.effect(() => forms.whileServed(WORKBUDDY_SETTINGS_NAMESPACE_CANDIDATES, (served) => {
5685
+ const ns = WORKBUDDY_SETTINGS_NAMESPACE_CANDIDATES.find((candidate) => served.has(candidate)) ?? [...served].find((entry) => /workbuddy/i.test(entry));
5686
+ settingsScope = ns === void 0 ? void 0 : forms.get(ns);
5687
+ for (const dispose of liveDisposers) try {
5688
+ dispose();
5689
+ } catch {}
5690
+ liveDisposers = registerAllSlots();
5691
+ return () => {
5692
+ for (const dispose of liveDisposers) try {
5693
+ dispose();
5694
+ } catch {}
5695
+ liveDisposers = [];
5696
+ };
5697
+ }), "dsh-connect-workbuddy: settings namespace binding");
5571
5698
  };
5572
- registerComposerCredits();
5699
+ bindSettingsScope();
5573
5700
  } catch (error) {
5574
5701
  console.error("[dsh-connect-workbuddy] client card failed to load (host provider unaffected):", error);
5575
5702
  }
@@ -3439,7 +3439,7 @@ function generationOf(version) {
3439
3439
  //#endregion
3440
3440
  //#region src/version.ts
3441
3441
  /** The npm package version this build was produced from. */
3442
- const WORKBUDDY_CONNECT_VERSION = "4.3.0";
3442
+ const WORKBUDDY_CONNECT_VERSION = "4.3.1";
3443
3443
  //#endregion
3444
3444
  //#region src/host-heartbeat.ts
3445
3445
  /**
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as isWorkbuddyBundle, A as workbuddyAccountId, B as parseCreditMultiplier, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as WORKBUDDY_APP_EXECUTABLE_ENV, H as parseUpstreamModel, I as classifyUpstreamError, J as deriveAtRestKeyId, K as clearAtRestKeyCache, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as isEncryptedFieldWrapper, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as prepareChatBody, V as parseReasoning, W as regionOf, X as findWorkbuddyAppExecutable, Y as fetchAtRestKeyPayload, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, et as macosBundleExecutable, f as WorkBuddyCatalog, g as ENCRYPTED_CREDENTIAL_CODE, h as fallbackModelsFor, i as processStartTimeMs, it as workbuddyAppExecutableCandidates, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, m as deriveCatalog, n as clearHostHeartbeat, nt as openEncryptedField, o as workbuddyHostHeartbeatPath, p as applyContextBudgets, q as deriveAtRestKey, r as isHeartbeatProcessAlive, rt as readAtRestKey, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as macosNestedAppBundles, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-jNlsUMZO.js";
1
+ import { $ as isWorkbuddyBundle, A as workbuddyAccountId, B as parseCreditMultiplier, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as WORKBUDDY_APP_EXECUTABLE_ENV, H as parseUpstreamModel, I as classifyUpstreamError, J as deriveAtRestKeyId, K as clearAtRestKeyCache, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as isEncryptedFieldWrapper, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as prepareChatBody, V as parseReasoning, W as regionOf, X as findWorkbuddyAppExecutable, Y as fetchAtRestKeyPayload, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, et as macosBundleExecutable, f as WorkBuddyCatalog, g as ENCRYPTED_CREDENTIAL_CODE, h as fallbackModelsFor, i as processStartTimeMs, it as workbuddyAppExecutableCandidates, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, m as deriveCatalog, n as clearHostHeartbeat, nt as openEncryptedField, o as workbuddyHostHeartbeatPath, p as applyContextBudgets, q as deriveAtRestKey, r as isHeartbeatProcessAlive, rt as readAtRestKey, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as macosNestedAppBundles, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as knownRegionOf } from "./host-heartbeat-BQkmibKu.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-connect-workbuddy",
3
3
  "displayName": "DSH Connect WorkBuddy",
4
4
  "description": "把本机登录的 WorkBuddy 模型接入 DeepSeek Harness,并提供只读的积分概览与模型管理。",
5
- "version": "4.3.0",
5
+ "version": "4.3.1",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",