dsh-connect-workbuddy 5.8.3 → 5.8.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.8.4 (2026-10-24)
4
+
5
+ > 修复版本:热重载一次之后设置卡片上一半按钮永久 405 的那一格(issue #86)。根因不是「宿主在跑旧代码」——是 `benefit/claim` 路由的 disposer 从未被调用,泄漏的路由让**下一次**热重载在它身上抛错,于是排在它后面的 8 条路由一条都没注册上。**请求路径、provider 注册、凭据、计费与换号规则一律未变**;改动是 cleanup 列表里的一行。
6
+
7
+ ### Fixes
8
+
9
+ - **`benefit/claim` 路由的 disposer 从未被调用,热重载一次之后卡片上一半按钮永久 405 空响应(issue #86 的真正触发条件)。**
10
+ - **症状**:设置卡里点「一键签到」「一键测试」「删除」「扫码登录」「保存」等按钮,报「批量操作接口没有应答(HTTP 405,无响应体)」,或扫码登录报 `登录失败: Failed to execute 'json' on 'Response': Unexpected end of JSON input`。模型本身照常能用——坏掉的只有卡片上的这些接口。
11
+ - **判定**:插件每个 handler 都回 JSON,**包括它自己的 405**(`json(res, 405, {error:'method not allowed'})`,带 `Content-Type` 与 body);宿主 HTTP 层回的 405 **没有 body**。所以「405 + 空 body」= 该路径在宿主路由表里根本不存在。
12
+ - **根因**:`registerWorkBuddyStatusRoute()` 里 13 条路由都写成 `const disposeXxx = ctx.webServer.register({...})`,**唯独 `benefit/claim` 那条声明了 `disposeBenefitClaim` 却没把它放进 cleanup 列表**(`tsc` 不报错:`noUnusedLocals` 未开)。宿主 `WebServer.register()` 对同一 `(kind, path)` 第二次注册**直接抛错**(`webserver: duplicate exact route "…"`,已核 `@deepseek-ai/dsh-host-webserver/lib/index.js:328`),且路由只在 disposer 运行时才从表里移除。于是:第一次挂载 → `benefit/claim` 泄漏;热重载再挂 → 按注册顺序走到它撞上重复路径 → 抛错 → 整个 `ctx.effect` 回调中断 → 排在它后面的 8 条路由(`models/refresh`、`probe`、`tps`、`pool`、`__save`、`oauth`、`transfer`、`cli-live`)一次都没注册上。
13
+ - **这解释了 issue #86 里「具体触发条件未复现」的那一格**:触发条件不是启动时序,是**热重载过一次**——只有第二次挂载才会撞上第一次的泄漏。所以「重启即恢复」只是把泄漏的那一份清掉,下次热重载再复发,这正是它看起来像「偶发」的原因。
14
+ - **修法**:把 `disposeBenefitClaim()` 补进 cleanup 列表,与其余 12 条对称。一处一行。并在 cleanup 列表处补注释说明「每条注册都必须在此出现」,因为这个缺陷的代价不是多一个 handler,而是让下一次挂载丢掉一半路由。
15
+ - **验证**:新增 3 条回归用例(`tests/web-status.spec.ts` → `registerWorkBuddyStatusRoute re-mount (issue #86)`),用一个**按宿主真实语义**的 `webServer` 替身(同路径第二次 `register` 抛错、只有 disposer 才移除)——宽松的替身会带着 bug 一起通过,所以这条属性必须照抄。变异验证:注释掉 `disposeBenefitClaim()` → 2 条变红并复现同一句 `webserver: duplicate exact route "/plugins/dsh-connect-workbuddy/benefit/claim"`。
16
+ - **代价**:无。请求路径、provider 注册、凭据、计费与换号规则一律未变。
17
+
3
18
  ## 5.8.3 (2026-10-24)
4
19
 
5
20
  > 体验版本:把 #132/#133 那几项「只能手改 `cordis.patch.yml` 才生效」的开关搬进插件卡片,用户可以直接勾选,不用再编辑 profile 里的 YAML、也不用知道那个文件在哪。**请求路径、默认字节、计费行为均未改动**:四个开关的默认值与采样时机与 5.8.0 完全一致(三个布尔默认关闭),这次只新增了一条写入它们的 UI 路径。
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as WorkBuddyCredentialStore, Ct as WORKBUDDY_APP_EXECUTABLE_ENV, D as defaultDesktopAuthDirs, E as defaultDesktopAuthCandidates, I as workbuddyOwnAuthPath, Lt as hostPeersInfo, M as legacyWorkbuddyOwnAuthPath, S as WORKBUDDY_AUTH_FILE_ENV, V as WorkBuddyUpstreamClient, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as rosterSeenPath, f as piAiRuntimeInfo, kt as findWorkbuddyAppExecutableWithSource, m as FALLBACK_WORKBUDDY_MODELS_GLOBAL, o as workbuddyHostHeartbeatPath, p as FALLBACK_WORKBUDDY_MODELS, r as isHeartbeatProcessAlive, u as describeRosterSeenPayload } from "./host-heartbeat-DzfWuVsm.js";
2
+ import { C as WorkBuddyCredentialStore, Ct as WORKBUDDY_APP_EXECUTABLE_ENV, D as defaultDesktopAuthDirs, E as defaultDesktopAuthCandidates, I as workbuddyOwnAuthPath, Lt as hostPeersInfo, M as legacyWorkbuddyOwnAuthPath, S as WORKBUDDY_AUTH_FILE_ENV, V as WorkBuddyUpstreamClient, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as rosterSeenPath, f as piAiRuntimeInfo, kt as findWorkbuddyAppExecutableWithSource, m as FALLBACK_WORKBUDDY_MODELS_GLOBAL, o as workbuddyHostHeartbeatPath, p as FALLBACK_WORKBUDDY_MODELS, r as isHeartbeatProcessAlive, u as describeRosterSeenPayload } from "./host-heartbeat-CAX0oKQT.js";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { realpathSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
@@ -6940,7 +6940,7 @@ function describeRosterSeenPayload(raw) {
6940
6940
  //#endregion
6941
6941
  //#region src/version.ts
6942
6942
  /** The npm package version this build was produced from. */
6943
- const WORKBUDDY_CONNECT_VERSION = "5.8.3";
6943
+ const WORKBUDDY_CONNECT_VERSION = "5.8.4";
6944
6944
  //#endregion
6945
6945
  //#region src/host-heartbeat.ts
6946
6946
  /**
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as parseCreditMultiplier, A as isEncryptedCredentialError, At as isEncryptedFieldWrapper, B as WorkBuddyCredentialRejectedError, Bt as writeFileAtomic, C as WorkBuddyCredentialStore, Ct as WORKBUDDY_APP_EXECUTABLE_ENV, D as defaultDesktopAuthDirs, Dt as fetchAtRestKeyPayload, E as defaultDesktopAuthCandidates, Et as deriveAtRestKeyId, F as workbuddyAccountId, Ft as readAtRestKey, G as forceNeutralHead, H as channelGateMemberOf, I as workbuddyOwnAuthPath, It as workbuddyAppExecutableCandidates, J as isCredentialRejectedError, K as isContextTooLongBody, L as CONTEXT_OVERFLOW_MARKER, M as legacyWorkbuddyOwnAuthPath, Mt as macosBundleExecutable, N as parseWorkBuddyAuth, Nt as macosNestedAppBundles, O as defaultDesktopAuthPath, Ot as findWorkbuddyAppExecutable, P as preferredAccountName, Pt as openEncryptedField, Q as openingEvidenceOfBody, R as CREDENTIAL_REJECTED_CODE, Rt as resolveDshHome, S as WORKBUDDY_AUTH_FILE_ENV, St as nativeModalityOf, T as authFileName, Tt as deriveAtRestKey, U as classifyUpstreamError, V as WorkBuddyUpstreamClient, W as declaredTools, X as knownRegionOf, Y as isFirstMessageGateBody, Z as modelIdOfBody, _ as deriveCatalog, _t as pickImageModel, a as readHostHeartbeat, at as requestEvidenceOf, b as ENCRYPTED_CREDENTIAL_CODE, bt as NATIVE_MODALITY_BY_MODEL_ID, c as WORKBUDDY_CONNECT_VERSION, ct as REFUSES_OFF_MODEL_IDS, dt as offDefaultFor, et as parseReasoning, f as piAiRuntimeInfo, ft as offRefused, g as applyContextBudgets, gt as WorkBuddyImageError, h as WorkBuddyCatalog, ht as DEFAULT_IMAGE_SIZE, i as processStartTimeMs, it as regionOf, j as isVaultAccountId, jt as isWorkbuddyBundle, k as hasEncryptedCredentialFields, l as createFileRosterSeenStore, lt as defaultOffModelIds, m as FALLBACK_WORKBUDDY_MODELS_GLOBAL, mt as withEffectiveOff, n as clearHostHeartbeat, nt as perturbChannelFingerprints, o as workbuddyHostHeartbeatPath, ot as rewriteSessionTitleModel, p as FALLBACK_WORKBUDDY_MODELS, pt as recordRefusal, q as isConversationIdGateBody, r as isHeartbeatProcessAlive, rt as prepareChatBody, s as writeHostHeartbeat, st as attachBusyLevels, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as parseUpstreamModel, ut as lastRefusalFor, v as fallbackModelsFor, vt as parseQueueAnswer, w as WorkBuddyEncryptedCredentialError, wt as clearAtRestKeyCache, x as WORKBUDDY_AUTH_FILENAME, xt as imageDefaultFor, y as withoutRouters, yt as queueNoticeText, z as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, zt as withFileLock } from "./host-heartbeat-DzfWuVsm.js";
1
+ import { $ as parseCreditMultiplier, A as isEncryptedCredentialError, At as isEncryptedFieldWrapper, B as WorkBuddyCredentialRejectedError, Bt as writeFileAtomic, C as WorkBuddyCredentialStore, Ct as WORKBUDDY_APP_EXECUTABLE_ENV, D as defaultDesktopAuthDirs, Dt as fetchAtRestKeyPayload, E as defaultDesktopAuthCandidates, Et as deriveAtRestKeyId, F as workbuddyAccountId, Ft as readAtRestKey, G as forceNeutralHead, H as channelGateMemberOf, I as workbuddyOwnAuthPath, It as workbuddyAppExecutableCandidates, J as isCredentialRejectedError, K as isContextTooLongBody, L as CONTEXT_OVERFLOW_MARKER, M as legacyWorkbuddyOwnAuthPath, Mt as macosBundleExecutable, N as parseWorkBuddyAuth, Nt as macosNestedAppBundles, O as defaultDesktopAuthPath, Ot as findWorkbuddyAppExecutable, P as preferredAccountName, Pt as openEncryptedField, Q as openingEvidenceOfBody, R as CREDENTIAL_REJECTED_CODE, Rt as resolveDshHome, S as WORKBUDDY_AUTH_FILE_ENV, St as nativeModalityOf, T as authFileName, Tt as deriveAtRestKey, U as classifyUpstreamError, V as WorkBuddyUpstreamClient, W as declaredTools, X as knownRegionOf, Y as isFirstMessageGateBody, Z as modelIdOfBody, _ as deriveCatalog, _t as pickImageModel, a as readHostHeartbeat, at as requestEvidenceOf, b as ENCRYPTED_CREDENTIAL_CODE, bt as NATIVE_MODALITY_BY_MODEL_ID, c as WORKBUDDY_CONNECT_VERSION, ct as REFUSES_OFF_MODEL_IDS, dt as offDefaultFor, et as parseReasoning, f as piAiRuntimeInfo, ft as offRefused, g as applyContextBudgets, gt as WorkBuddyImageError, h as WorkBuddyCatalog, ht as DEFAULT_IMAGE_SIZE, i as processStartTimeMs, it as regionOf, j as isVaultAccountId, jt as isWorkbuddyBundle, k as hasEncryptedCredentialFields, l as createFileRosterSeenStore, lt as defaultOffModelIds, m as FALLBACK_WORKBUDDY_MODELS_GLOBAL, mt as withEffectiveOff, n as clearHostHeartbeat, nt as perturbChannelFingerprints, o as workbuddyHostHeartbeatPath, ot as rewriteSessionTitleModel, p as FALLBACK_WORKBUDDY_MODELS, pt as recordRefusal, q as isConversationIdGateBody, r as isHeartbeatProcessAlive, rt as prepareChatBody, s as writeHostHeartbeat, st as attachBusyLevels, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as parseUpstreamModel, ut as lastRefusalFor, v as fallbackModelsFor, vt as parseQueueAnswer, w as WorkBuddyEncryptedCredentialError, wt as clearAtRestKeyCache, x as WORKBUDDY_AUTH_FILENAME, xt as imageDefaultFor, y as withoutRouters, yt as queueNoticeText, z as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, zt as withFileLock } from "./host-heartbeat-CAX0oKQT.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { appendFile, mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
@@ -9203,7 +9203,7 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
9203
9203
  }
9204
9204
  }
9205
9205
  });
9206
- ctx.webServer.register({
9206
+ const disposeBenefitClaim = ctx.webServer.register({
9207
9207
  kind: "exact",
9208
9208
  path: WORKBUDDY_BENEFIT_CLAIM_PATH,
9209
9209
  handler: async (req, res) => {
@@ -9630,6 +9630,7 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
9630
9630
  }
9631
9631
  });
9632
9632
  return () => {
9633
+ disposeBenefitClaim();
9633
9634
  disposeRefresh();
9634
9635
  disposeCheckin();
9635
9636
  disposeAccounts();
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": "5.8.3",
5
+ "version": "5.8.4",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",