dsh-connect-workbuddy 3.4.4 → 3.4.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 +20 -0
- package/lib/bin.js +1 -1
- package/lib/{host-heartbeat-DTuwptkM.js → host-heartbeat-CTigiLJK.js} +19 -4
- package/lib/index.js +18 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.4.5 (2026-10-04)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
- **保存设置不再把「本该用实时目录」的模型槽写空,正在用的模型不会凭空消失(issue #32)。**
|
|
8
|
+
- **现象**:在 `regions` 从未成功写入过的 profile 上(如 issue #31 所述的环境),第一次保存(哪怕只改了一处偏好)之后,模型选择器里**正在使用的默认模型突然不见了**,其他只在上游目录里、不在内置静态名单里的模型也一起消失,且**没有任何报错**。
|
|
9
|
+
- **根因**(两半,缺一不可):
|
|
10
|
+
1. **写半边**——`__save` 的 `merged` 以宿主解析值为种子。当 `regions` 还是空对象时,`{"cn":{"imageModelIds":[]}}` 这样的载荷会落下**一个没有 `lastCatalog` 的 cn 槽**。槽本身合法(schema 里 `lastCatalog` 默认就是 `[]`),但它缺了这份目录。
|
|
11
|
+
2. **读半边**——`configuredModels` / `displayModels` 把 roster 解析成两步:`state.lastCatalog?.length ? lastCatalog : 静态名单`。「槽存在但没有目录」和「从未配置过」于是被当成同一件事,都退回**内置的 12 条静态名单**;而 `applySelection` 又把这份静态名单 `catalog.set()` 进**实时目录**——实时发现注册的 17 条就此被覆盖。正在用的 `space-bunny` 只在实时目录里、不在静态名单里,当场消失。
|
|
12
|
+
- **修法**:
|
|
13
|
+
- 写半边:`__save` 在合并之后、落盘之前,对本次载荷**真正触及**的区域(只碰 `incoming` 里出现过的键,不动调用方没提到的兄弟区域),若该槽缺目录(`lastCatalog` 缺失或为空数组),就用 `displayModels` 取到的**实时名册**补上。用 `displayModels` 而非上游往返:保存不该依赖网络,也不该因为上游不可用而失败。调用方显式传来的数组(含非空数组)仍然优先,实时名册为空则维持原状、不写空数组。
|
|
14
|
+
- 读半边:roster 解析改成三步 `lastCatalog → 该区域当前已注册的实时目录 → 静态名单`。静态名单退回它本来该待的位置——上游离线、什么都发现不了时的最后兜底。`applySelection` 在 `set()` **之前**读一次当前目录,避免观察到自己正要写的值。
|
|
15
|
+
- 这样「槽存在但缺目录」会**降到实时目录**,永远不会越过它掉到静态名单。
|
|
16
|
+
|
|
17
|
+
- **未显式设置预算的模型不再被强制压到 200K,1M 模型的真实窗口得以保留(issue #33)。**
|
|
18
|
+
- **现象**:上游目录里 17 个模型有 15 个被改报成 200000——`space-bunny`、`deepseek-v4.1-flash`、`deepseek-v4-pro`、`glm-5.3`、`glm-5.3-flash`、`glm-5.2`、`kimi-k3-1`、`kimi-k2.8-preview` 的 1000000、`hy4-preview` 的 960000、`minimax-m3` 的 512000、`auto`/`kimi-k2.6`/`kimi-k2.7` 的 256000 全部变成 200000。
|
|
19
|
+
- **根因**:`applyContextBudgets` 里 `budgets[model.id] ?? 200_000` 的默认值——**未设置预算被当成「设成 200K」**。这条默认值在 CHANGELOG 里给出的理由是「让 DSH 提前压缩以避开上游节流」,但同一份 CHANGELOG 又自述实测节流线是 **2 万~3 万 token 且单次请求即触顶**:200K 比那条线高一个数量级,压到 200K 并没有把压缩提前到安全区。200K 在静态目录里也无出处(`auto` 是 168000、`hy3` 是 192000、`default-model` 是 176000、`primary-model` 是 272000);插件自己的静态目录还把 `glm-5.3` 等声明为 1000000,与钳制自相矛盾。
|
|
20
|
+
- **修法**:`applyContextBudgets` 不再有默认值——**只做上限**:未设置预算时保留模型的原生窗口,设置了就用 `Math.min(原生, 预算)`。`contextBudgets` 的语义恢复成「用户设的上限」,而不是一个隐式的全局改写。(`toWebModel` 里那份重复的 `?? 200_000` 也一并去掉,改为调用同一个函数,避免卡片与运行时目录各说各话。)
|
|
21
|
+
- 想继续压缩的用户仍然可以显式设置 `regions.<region>.contextBudgets`;卡片上原本只在原生窗口 > 200K 时才出现的「DSH 上下文预算」选择器,现在因为不再默认钳制而更有意义。
|
|
22
|
+
|
|
3
23
|
## 3.4.4 (2026-10-03)
|
|
4
24
|
|
|
5
25
|
### Features
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as workbuddyOwnAuthPath, C as defaultDesktopAuthDirs, D as legacyWorkbuddyOwnAuthPath, P as WorkBuddyUpstreamClient, S as defaultDesktopAuthCandidates, U as WORKBUDDY_APP_EXECUTABLE_ENV, Y as findWorkbuddyAppExecutableWithSource, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, 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-CTigiLJK.js";
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
//#region src/bin.ts
|
|
@@ -2556,11 +2556,26 @@ const FALLBACK_WORKBUDDY_MODELS_GLOBAL = [
|
|
|
2556
2556
|
function fallbackModelsFor(region) {
|
|
2557
2557
|
return region === "global" ? FALLBACK_WORKBUDDY_MODELS_GLOBAL : FALLBACK_WORKBUDDY_MODELS;
|
|
2558
2558
|
}
|
|
2559
|
-
/**
|
|
2559
|
+
/**
|
|
2560
|
+
* Apply the saved local DSH budget. A budget is an UPPER LIMIT the user sets,
|
|
2561
|
+
* never a default: a model without an explicit budget keeps its own window, and
|
|
2562
|
+
* a budget can only ever LOWER a window (`Math.min`), so a value above the
|
|
2563
|
+
* native one is not an error, just a no-op.
|
|
2564
|
+
*
|
|
2565
|
+
* There used to be a `?? 200_000` default here (issue #33), on the rationale
|
|
2566
|
+
* that a lower advertised window makes DSH compact sooner and so keeps a long
|
|
2567
|
+
* session under the upstream throttle. That rationale does not hold: the
|
|
2568
|
+
* measured throttle fires on a SINGLE request past 20k-30k input tokens (the
|
|
2569
|
+
* CHANGELOG's own wording calls 200K "远超上游 ~20–30k 的节流线"), so clamping to
|
|
2570
|
+
* 200K bought none of the safety it claimed while costing every >200K model its
|
|
2571
|
+
* real window — and contradicting the static roster in this very file, which
|
|
2572
|
+
* declares those models as 1M. A user who wants the conservative behaviour sets
|
|
2573
|
+
* a budget explicitly; the card offers that per model.
|
|
2574
|
+
*/
|
|
2560
2575
|
function applyContextBudgets(catalog, budgets = {}) {
|
|
2561
2576
|
return catalog.map((model) => ({
|
|
2562
2577
|
...model,
|
|
2563
|
-
contextWindow: model.
|
|
2578
|
+
contextWindow: budgets[model.id] === void 0 ? model.contextWindow : Math.min(model.contextWindow, budgets[model.id])
|
|
2564
2579
|
}));
|
|
2565
2580
|
}
|
|
2566
2581
|
function deriveCatalog(catalog, enabled, budgets = {}) {
|
|
@@ -2791,7 +2806,7 @@ function generationOf(version) {
|
|
|
2791
2806
|
//#endregion
|
|
2792
2807
|
//#region src/version.ts
|
|
2793
2808
|
/** The npm package version this build was produced from. */
|
|
2794
|
-
const WORKBUDDY_CONNECT_VERSION = "3.4.
|
|
2809
|
+
const WORKBUDDY_CONNECT_VERSION = "3.4.5";
|
|
2795
2810
|
//#endregion
|
|
2796
2811
|
//#region src/host-heartbeat.ts
|
|
2797
2812
|
/**
|
|
@@ -2949,4 +2964,4 @@ async function clearHostHeartbeat() {
|
|
|
2949
2964
|
await rm(workbuddyHostHeartbeatPath(), { force: true });
|
|
2950
2965
|
}
|
|
2951
2966
|
//#endregion
|
|
2952
|
-
export {
|
|
2967
|
+
export { macosNestedAppBundles as $, workbuddyOwnAuthPath as A, parseUpstreamModel as B, defaultDesktopAuthDirs as C, legacyWorkbuddyOwnAuthPath as D, isEncryptedCredentialError as E, classifyUpstreamError as F, deriveAtRestKey as G, regionOf as H, declaredTools as I, findWorkbuddyAppExecutable as J, deriveAtRestKeyId as K, isCredentialRejectedError as L, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as M, WorkBuddyCredentialRejectedError as N, parseWorkBuddyAuth as O, WorkBuddyUpstreamClient as P, macosBundleExecutable as Q, parseCreditMultiplier as R, defaultDesktopAuthCandidates as S, hasEncryptedCredentialFields as T, WORKBUDDY_APP_EXECUTABLE_ENV as U, prepareChatBody as V, clearAtRestKeyCache as W, isEncryptedFieldWrapper as X, findWorkbuddyAppExecutableWithSource as Y, isWorkbuddyBundle as Z, WORKBUDDY_AUTH_FILENAME as _, readHostHeartbeat as a, WorkBuddyEncryptedCredentialError as b, WORKBUDDY_CONNECT_VERSION as c, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, openEncryptedField as et, WorkBuddyCatalog as f, ENCRYPTED_CREDENTIAL_CODE as g, fallbackModelsFor as h, processStartTimeMs as i, CREDENTIAL_REJECTED_CODE as j, workbuddyAccountId as k, piAiRuntimeInfo as l, deriveCatalog as m, clearHostHeartbeat as n, workbuddyAppExecutableCandidates as nt, workbuddyHostHeartbeatPath as o, applyContextBudgets as p, fetchAtRestKeyPayload as q, isHeartbeatProcessAlive as r, writeHostHeartbeat as s, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, readAtRestKey as tt, FALLBACK_WORKBUDDY_MODELS as u, WORKBUDDY_AUTH_FILE_ENV as v, defaultDesktopAuthPath as w, authFileName as x, WorkBuddyCredentialStore as y, parseReasoning as z };
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as macosNestedAppBundles, A as workbuddyOwnAuthPath, B as parseUpstreamModel, C as defaultDesktopAuthDirs, D as legacyWorkbuddyOwnAuthPath, E as isEncryptedCredentialError, F as classifyUpstreamError, G as deriveAtRestKey, H as regionOf, I as declaredTools, J as findWorkbuddyAppExecutable, K as deriveAtRestKeyId, L as isCredentialRejectedError, M as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, N as WorkBuddyCredentialRejectedError, O as parseWorkBuddyAuth, P as WorkBuddyUpstreamClient, Q as macosBundleExecutable, R as parseCreditMultiplier, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as WORKBUDDY_APP_EXECUTABLE_ENV, V as prepareChatBody, W as clearAtRestKeyCache, X as isEncryptedFieldWrapper, Z as isWorkbuddyBundle, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, et as openEncryptedField, f as WorkBuddyCatalog, g as ENCRYPTED_CREDENTIAL_CODE, h as fallbackModelsFor, i as processStartTimeMs, j as CREDENTIAL_REJECTED_CODE, k as workbuddyAccountId, l as piAiRuntimeInfo, m as deriveCatalog, n as clearHostHeartbeat, nt as workbuddyAppExecutableCandidates, o as workbuddyHostHeartbeatPath, p as applyContextBudgets, q as fetchAtRestKeyPayload, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as readAtRestKey, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, z as parseReasoning } from "./host-heartbeat-CTigiLJK.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";
|
|
@@ -3093,10 +3093,11 @@ function toCredits(answer) {
|
|
|
3093
3093
|
}
|
|
3094
3094
|
/** Project a model into the card's row, dropping empty optional fields. */
|
|
3095
3095
|
function toWebModel(model, budgets) {
|
|
3096
|
+
const budgeted = applyContextBudgets([model], budgets)[0] ?? model;
|
|
3096
3097
|
return {
|
|
3097
3098
|
id: model.id,
|
|
3098
3099
|
name: model.name,
|
|
3099
|
-
contextWindow:
|
|
3100
|
+
contextWindow: budgeted.contextWindow,
|
|
3100
3101
|
nativeContextWindow: model.contextWindow,
|
|
3101
3102
|
maxTokens: model.maxTokens,
|
|
3102
3103
|
...model.creditMultiplier === void 0 ? {} : { creditMultiplier: model.creditMultiplier },
|
|
@@ -3551,6 +3552,14 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
|
|
|
3551
3552
|
...slot
|
|
3552
3553
|
} : slot;
|
|
3553
3554
|
}
|
|
3555
|
+
if (field === "regions") for (const key of Object.keys(incoming)) {
|
|
3556
|
+
const slot = merged[key];
|
|
3557
|
+
if (!isPlainRecord(slot) || key !== "cn" && key !== "global") continue;
|
|
3558
|
+
if (Array.isArray(slot.lastCatalog) && slot.lastCatalog.length > 0) continue;
|
|
3559
|
+
const live = deps.displayModels(key);
|
|
3560
|
+
if (live.length === 0) continue;
|
|
3561
|
+
slot.lastCatalog = live;
|
|
3562
|
+
}
|
|
3554
3563
|
await settings.mutate(row.ns, [{
|
|
3555
3564
|
op: "set",
|
|
3556
3565
|
path: [field],
|
|
@@ -3926,13 +3935,16 @@ function apply(ctx, config) {
|
|
|
3926
3935
|
...model,
|
|
3927
3936
|
...images.has(model.id) ? { multimodal: true } : { multimodal: false }
|
|
3928
3937
|
}));
|
|
3929
|
-
const configuredModels = (value, region) => {
|
|
3938
|
+
const configuredModels = (value, region, liveModels = []) => {
|
|
3930
3939
|
const state = regionStateOf(value, region);
|
|
3931
|
-
|
|
3940
|
+
const roster = state.lastCatalog?.length ? state.lastCatalog : liveModels.length ? liveModels : fallbackModelsFor(region);
|
|
3941
|
+
return withImageSelection(deriveCatalog(roster, new Set(state.enabledModelIds ?? []), state.contextBudgets ?? {}), new Set(state.imageModelIds ?? []));
|
|
3932
3942
|
};
|
|
3933
3943
|
const displayModels = (value, region) => {
|
|
3934
3944
|
const state = regionStateOf(value, region);
|
|
3935
|
-
|
|
3945
|
+
if (state.lastCatalog?.length) return state.lastCatalog;
|
|
3946
|
+
const live = stacks[region]?.catalog?.current();
|
|
3947
|
+
return live?.length ? live : fallbackModelsFor(region);
|
|
3936
3948
|
};
|
|
3937
3949
|
let current = () => config;
|
|
3938
3950
|
let invalidateCatalog = () => {};
|
|
@@ -3955,7 +3967,7 @@ function apply(ctx, config) {
|
|
|
3955
3967
|
for (const region of REGION_KEYS) {
|
|
3956
3968
|
stacks[region].store.setDesktopPath(authFile);
|
|
3957
3969
|
stacks[region].store.selectAccount(effectiveAccountFor(region, value));
|
|
3958
|
-
stacks[region].catalog.set(configuredModels(value, region));
|
|
3970
|
+
stacks[region].catalog.set(configuredModels(value, region, stacks[region].catalog.current()));
|
|
3959
3971
|
}
|
|
3960
3972
|
invalidateCatalog();
|
|
3961
3973
|
syncRegionRegistration(value);
|
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": "3.4.
|
|
5
|
+
"version": "3.4.5",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|