dsh-connect-workbuddy 4.14.0 → 4.14.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 +25 -0
- package/lib/bin.js +1 -1
- package/lib/client.js +65 -49
- package/lib/{host-heartbeat-B1I95fKS.js → host-heartbeat-D1WnqQY2.js} +105 -12
- package/lib/index.d.ts +20 -0
- package/lib/index.js +1 -50
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.14.1 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
> 版本号是 **patch**:没有接口、配置项或请求结构的变更,发往上游的请求体**一个字节都没变**。本版只改「哪些上游条目算作模型」这一处判断,以及它带出的一个目录退化路径。
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- **不再把上游的 `auto` 路由器当成模型列出来**
|
|
10
|
+
- **它不是模型**:国内区 `cli` 名单里的 `auto` 是一个**路由决策**,网关在请求时把它解析成它认为合适的那一个(实测 → `hy4-preview`)。所以它的价格、上下文窗口与行为都由别人在请求时决定,插件无法如实描述;把它列成一行,用户看到的将是一行「倍率未知、行为不定」的条目。
|
|
11
|
+
- **它还制造重复**:同一份名单里本来就有它会被解析到的那个真实模型(`hy4-preview`),于是选择器出现两行指向同一个上游模型、其中一行没有倍率。
|
|
12
|
+
- **改在名单构建的单一位置**(`selectCliModels`),因此卡片、模型选择器、探针与发现路由看到的是同一份结果,不会出现「卡片上没有、选择器里还有」。
|
|
13
|
+
- **用显式表而不是名字启发式**:新增 `ROUTER_MODEL_IDS` / `isRouterModelId`(`src/native-modality.ts`),与既有的 `nativeModalityOf(...) === 'router'` 共用同一个来源。路由是上游的概念、没有声明字段,按名字猜迟早会误伤真实模型。
|
|
14
|
+
- **静态兜底同步移除**:`FALLBACK_WORKBUDDY_MODELS` 里也有 `auto`。只改实时名单的话,它会在离线/首帧路径上复活——那是这类不一致里最难察觉的一种。
|
|
15
|
+
- **空名单守卫挪到并入保留模型之后**:否则名单只剩 router 时,守卫会在合并前抛错,把「上游改了 router」放大成「这个区域一个模型都没有」。现在「上游名单为空、但用户仍有在跑的真实模型」会正常降级为后者。
|
|
16
|
+
|
|
17
|
+
- **勾选与目录零交集时退回整个目录,而不是返回空**
|
|
18
|
+
- 真实触发路径正是一位「只勾过 `auto`」的用户:4.14.0 里他还能勾选它,本版不再提供之后,他的 `enabledModelIds` 与目录的交集为空,运行时目录会变成空的——那是 issue #32 的故障形态(选择器突然没有模型,且没有任何报错)。
|
|
19
|
+
- `deriveCatalog` 现在只在「非空勾选 ∩ 目录 = ∅」时退回整个目录。用户勾选表达的是「要一些模型开着」,不是「一个都不要」;空目录仍然返回空,这条只处理「勾选不再匹配任何东西」。
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
|
|
23
|
+
- `tests/upstream.spec.ts` 新增 2 条:**router 绝不作为模型出现**(名单里 `auto` 与 `hy4-preview` 同时存在时,结果只有后者);**名单只剩 router 时保留模型仍然生效**——这条专门钉住「守卫在合并前还是合并后」这个坑,守卫放错位置它会红。
|
|
24
|
+
- `tests/catalog.spec.ts` 新增/改写:两区静态兜底都不含 `auto`;勾选只命中 router 时退回整个目录而不是空。
|
|
25
|
+
- 既有两条把 `auto` 当素材的用例改用真实模型(`hy3`),避免测试本身依赖刚被删掉的行为。
|
|
26
|
+
- **真实上游核验**(只读 GET,不消耗积分):国内区 `cli` 名单 20 → **19** 条,`auto` 已不在其中;其余 9 枚活动徽标一个不少,与 WorkBuddy 客户端逐条一致。
|
|
27
|
+
|
|
3
28
|
## 4.14.0 (2026-10-21)
|
|
4
29
|
|
|
5
30
|
> 版本号是 **minor**:有用户可见的功能与行为变更(模型行新增上游活动徽标;国内区改以 App 身份读产品配置,因此目录与客户端一致)。发往上游的**聊天**请求体、路由 id、凭据、计费与换号规则一律未变——变的只有**读取模型目录**那一次 GET 的 User-Agent。
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL,
|
|
2
|
+
import { C as defaultDesktopAuthDirs, F as WorkBuddyUpstreamClient, O as legacyWorkbuddyOwnAuthPath, S as defaultDesktopAuthCandidates, _t as hostPeersInfo, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, lt as findWorkbuddyAppExecutableWithSource, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, rt as WORKBUDDY_APP_EXECUTABLE_ENV, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-D1WnqQY2.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
|
@@ -1501,6 +1501,71 @@ window.__ModuleLoader__.load({
|
|
|
1501
1501
|
return t("row.poolProbeDaysAgo", { count: Math.round(age / DAY_MS) });
|
|
1502
1502
|
}
|
|
1503
1503
|
//#endregion
|
|
1504
|
+
//#region src/native-modality.ts
|
|
1505
|
+
/**
|
|
1506
|
+
* Reviewed vendor classifications, keyed by EXACT model id (never a prefix).
|
|
1507
|
+
* Ids absent from this record are `unknown` — see {@link nativeModalityOf}.
|
|
1508
|
+
*/
|
|
1509
|
+
const NATIVE_MODALITY_BY_MODEL_ID = {
|
|
1510
|
+
"deepseek-v4-pro": "text",
|
|
1511
|
+
"glm-5.1": "text",
|
|
1512
|
+
"glm-5.2": "text",
|
|
1513
|
+
"glm-5.3": "text",
|
|
1514
|
+
"hy3": "text",
|
|
1515
|
+
"hy4-preview": "text",
|
|
1516
|
+
"deepseek-v4.1-flash": "multimodal",
|
|
1517
|
+
"glm-5.3-flash": "multimodal",
|
|
1518
|
+
"glm-5v-turbo": "multimodal",
|
|
1519
|
+
"kimi-k2.6": "multimodal",
|
|
1520
|
+
"kimi-k2.7": "multimodal",
|
|
1521
|
+
"kimi-k3-1": "multimodal",
|
|
1522
|
+
"minimax-m3": "multimodal"
|
|
1523
|
+
};
|
|
1524
|
+
/**
|
|
1525
|
+
* Ids the upstream lists in the model roster that are ROUTERS, not models.
|
|
1526
|
+
*
|
|
1527
|
+
* `auto` (CN) is the one observed today: the gateway resolves it to whichever
|
|
1528
|
+
* model it considers the best fit (measured: CN `auto` → `hy4-preview`), so its
|
|
1529
|
+
* price, context window and behaviour are decided at request time by something
|
|
1530
|
+
* else. Offering it as a row advertises a model whose terms the plugin cannot
|
|
1531
|
+
* state, and it duplicates a real row already in the same roster.
|
|
1532
|
+
*
|
|
1533
|
+
* Kept as an explicit table rather than a heuristic: a router is an upstream
|
|
1534
|
+
* concept with no declared flag, and guessing from the name would eventually
|
|
1535
|
+
* hide a real model. Add an id here only after the same kind of evidence.
|
|
1536
|
+
*/
|
|
1537
|
+
const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto"]);
|
|
1538
|
+
/** Whether this id is an upstream router rather than a model. */
|
|
1539
|
+
function isRouterModelId(modelId) {
|
|
1540
|
+
return ROUTER_MODEL_IDS.has(modelId);
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Native modality of one model id. Exact match only: a new model never
|
|
1544
|
+
* inherits a classification from its family, its display name, or the
|
|
1545
|
+
* platform's image flag.
|
|
1546
|
+
*/
|
|
1547
|
+
function nativeModalityOf(modelId) {
|
|
1548
|
+
if (isRouterModelId(modelId)) return "router";
|
|
1549
|
+
return NATIVE_MODALITY_BY_MODEL_ID[modelId] ?? "unknown";
|
|
1550
|
+
}
|
|
1551
|
+
/**
|
|
1552
|
+
* Whether a model refresh should PRE-CHECK this model's image box.
|
|
1553
|
+
*
|
|
1554
|
+
* Only a documented `multimodal` is pre-checked. `text` is documented as
|
|
1555
|
+
* unable to read images, and `unknown` is deliberately left unchecked rather
|
|
1556
|
+
* than guessed — the whole reason this table exists is that the platform flag
|
|
1557
|
+
* guessed wrong. A platform `supportsImages: false` still vetoes, so a
|
|
1558
|
+
* documented-multimodal id can never be pre-checked against the platform's
|
|
1559
|
+
* explicit "no".
|
|
1560
|
+
*
|
|
1561
|
+
* The user can always tick a box by hand; this decides a DEFAULT only, and the
|
|
1562
|
+
* effective runtime flag remains the saved `imageModelIds`.
|
|
1563
|
+
*/
|
|
1564
|
+
function imageDefaultFor(info) {
|
|
1565
|
+
if (info.supportsImages === false) return false;
|
|
1566
|
+
return NATIVE_MODALITY_BY_MODEL_ID[info.id] === "multimodal";
|
|
1567
|
+
}
|
|
1568
|
+
//#endregion
|
|
1504
1569
|
//#region src/promo.ts
|
|
1505
1570
|
/**
|
|
1506
1571
|
* `'HH:mm'` → minutes since midnight, or undefined for anything else.
|
|
@@ -3735,55 +3800,6 @@ window.__ModuleLoader__.load({
|
|
|
3735
3800
|
});
|
|
3736
3801
|
}
|
|
3737
3802
|
//#endregion
|
|
3738
|
-
//#region src/native-modality.ts
|
|
3739
|
-
/**
|
|
3740
|
-
* Reviewed vendor classifications, keyed by EXACT model id (never a prefix).
|
|
3741
|
-
* Ids absent from this record are `unknown` — see {@link nativeModalityOf}.
|
|
3742
|
-
*/
|
|
3743
|
-
const NATIVE_MODALITY_BY_MODEL_ID = {
|
|
3744
|
-
"deepseek-v4-pro": "text",
|
|
3745
|
-
"glm-5.1": "text",
|
|
3746
|
-
"glm-5.2": "text",
|
|
3747
|
-
"glm-5.3": "text",
|
|
3748
|
-
"hy3": "text",
|
|
3749
|
-
"hy4-preview": "text",
|
|
3750
|
-
"deepseek-v4.1-flash": "multimodal",
|
|
3751
|
-
"glm-5.3-flash": "multimodal",
|
|
3752
|
-
"glm-5v-turbo": "multimodal",
|
|
3753
|
-
"kimi-k2.6": "multimodal",
|
|
3754
|
-
"kimi-k2.7": "multimodal",
|
|
3755
|
-
"kimi-k3-1": "multimodal",
|
|
3756
|
-
"minimax-m3": "multimodal"
|
|
3757
|
-
};
|
|
3758
|
-
/** The `auto` entry is a router that picks a model, not a model itself. */
|
|
3759
|
-
const ROUTER_MODEL_ID = "auto";
|
|
3760
|
-
/**
|
|
3761
|
-
* Native modality of one model id. Exact match only: a new model never
|
|
3762
|
-
* inherits a classification from its family, its display name, or the
|
|
3763
|
-
* platform's image flag.
|
|
3764
|
-
*/
|
|
3765
|
-
function nativeModalityOf(modelId) {
|
|
3766
|
-
if (modelId === ROUTER_MODEL_ID) return "router";
|
|
3767
|
-
return NATIVE_MODALITY_BY_MODEL_ID[modelId] ?? "unknown";
|
|
3768
|
-
}
|
|
3769
|
-
/**
|
|
3770
|
-
* Whether a model refresh should PRE-CHECK this model's image box.
|
|
3771
|
-
*
|
|
3772
|
-
* Only a documented `multimodal` is pre-checked. `text` is documented as
|
|
3773
|
-
* unable to read images, and `unknown` is deliberately left unchecked rather
|
|
3774
|
-
* than guessed — the whole reason this table exists is that the platform flag
|
|
3775
|
-
* guessed wrong. A platform `supportsImages: false` still vetoes, so a
|
|
3776
|
-
* documented-multimodal id can never be pre-checked against the platform's
|
|
3777
|
-
* explicit "no".
|
|
3778
|
-
*
|
|
3779
|
-
* The user can always tick a box by hand; this decides a DEFAULT only, and the
|
|
3780
|
-
* effective runtime flag remains the saved `imageModelIds`.
|
|
3781
|
-
*/
|
|
3782
|
-
function imageDefaultFor(info) {
|
|
3783
|
-
if (info.supportsImages === false) return false;
|
|
3784
|
-
return NATIVE_MODALITY_BY_MODEL_ID[info.id] === "multimodal";
|
|
3785
|
-
}
|
|
3786
|
-
//#endregion
|
|
3787
3803
|
//#region src/client/promo-badge.ts
|
|
3788
3804
|
/**
|
|
3789
3805
|
* The UI's language, as the browser reports it.
|
|
@@ -962,6 +962,71 @@ function clearAtRestKeyCache() {
|
|
|
962
962
|
cachedAppExecutable = void 0;
|
|
963
963
|
}
|
|
964
964
|
//#endregion
|
|
965
|
+
//#region src/native-modality.ts
|
|
966
|
+
/**
|
|
967
|
+
* Reviewed vendor classifications, keyed by EXACT model id (never a prefix).
|
|
968
|
+
* Ids absent from this record are `unknown` — see {@link nativeModalityOf}.
|
|
969
|
+
*/
|
|
970
|
+
const NATIVE_MODALITY_BY_MODEL_ID = {
|
|
971
|
+
"deepseek-v4-pro": "text",
|
|
972
|
+
"glm-5.1": "text",
|
|
973
|
+
"glm-5.2": "text",
|
|
974
|
+
"glm-5.3": "text",
|
|
975
|
+
"hy3": "text",
|
|
976
|
+
"hy4-preview": "text",
|
|
977
|
+
"deepseek-v4.1-flash": "multimodal",
|
|
978
|
+
"glm-5.3-flash": "multimodal",
|
|
979
|
+
"glm-5v-turbo": "multimodal",
|
|
980
|
+
"kimi-k2.6": "multimodal",
|
|
981
|
+
"kimi-k2.7": "multimodal",
|
|
982
|
+
"kimi-k3-1": "multimodal",
|
|
983
|
+
"minimax-m3": "multimodal"
|
|
984
|
+
};
|
|
985
|
+
/**
|
|
986
|
+
* Ids the upstream lists in the model roster that are ROUTERS, not models.
|
|
987
|
+
*
|
|
988
|
+
* `auto` (CN) is the one observed today: the gateway resolves it to whichever
|
|
989
|
+
* model it considers the best fit (measured: CN `auto` → `hy4-preview`), so its
|
|
990
|
+
* price, context window and behaviour are decided at request time by something
|
|
991
|
+
* else. Offering it as a row advertises a model whose terms the plugin cannot
|
|
992
|
+
* state, and it duplicates a real row already in the same roster.
|
|
993
|
+
*
|
|
994
|
+
* Kept as an explicit table rather than a heuristic: a router is an upstream
|
|
995
|
+
* concept with no declared flag, and guessing from the name would eventually
|
|
996
|
+
* hide a real model. Add an id here only after the same kind of evidence.
|
|
997
|
+
*/
|
|
998
|
+
const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto"]);
|
|
999
|
+
/** Whether this id is an upstream router rather than a model. */
|
|
1000
|
+
function isRouterModelId(modelId) {
|
|
1001
|
+
return ROUTER_MODEL_IDS.has(modelId);
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Native modality of one model id. Exact match only: a new model never
|
|
1005
|
+
* inherits a classification from its family, its display name, or the
|
|
1006
|
+
* platform's image flag.
|
|
1007
|
+
*/
|
|
1008
|
+
function nativeModalityOf(modelId) {
|
|
1009
|
+
if (isRouterModelId(modelId)) return "router";
|
|
1010
|
+
return NATIVE_MODALITY_BY_MODEL_ID[modelId] ?? "unknown";
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Whether a model refresh should PRE-CHECK this model's image box.
|
|
1014
|
+
*
|
|
1015
|
+
* Only a documented `multimodal` is pre-checked. `text` is documented as
|
|
1016
|
+
* unable to read images, and `unknown` is deliberately left unchecked rather
|
|
1017
|
+
* than guessed — the whole reason this table exists is that the platform flag
|
|
1018
|
+
* guessed wrong. A platform `supportsImages: false` still vetoes, so a
|
|
1019
|
+
* documented-multimodal id can never be pre-checked against the platform's
|
|
1020
|
+
* explicit "no".
|
|
1021
|
+
*
|
|
1022
|
+
* The user can always tick a box by hand; this decides a DEFAULT only, and the
|
|
1023
|
+
* effective runtime flag remains the saved `imageModelIds`.
|
|
1024
|
+
*/
|
|
1025
|
+
function imageDefaultFor(info) {
|
|
1026
|
+
if (info.supportsImages === false) return false;
|
|
1027
|
+
return NATIVE_MODALITY_BY_MODEL_ID[info.id] === "multimodal";
|
|
1028
|
+
}
|
|
1029
|
+
//#endregion
|
|
965
1030
|
//#region src/off-thinking.ts
|
|
966
1031
|
/**
|
|
967
1032
|
* Ids observed to REFUSE `reasoning_effort: "off"` with HTTP 400 despite
|
|
@@ -2328,6 +2393,18 @@ function parseUpstreamModel(value) {
|
|
|
2328
2393
|
* is exposed rather than nothing: the roster is an upstream detail that may
|
|
2329
2394
|
* change, and an empty answer would silently disarm the provider.
|
|
2330
2395
|
*
|
|
2396
|
+
* ROUTERS ARE NOT MODELS. The upstream lists `auto` (CN) and its global
|
|
2397
|
+
* equivalents alongside real models, but those entries are ROUTING decisions,
|
|
2398
|
+
* not models: the gateway resolves them to whichever model it thinks fits, so
|
|
2399
|
+
* a row for one would offer a name whose price, context window and behaviour
|
|
2400
|
+
* are all decided at request time by something else. Selected this way it also
|
|
2401
|
+
* duplicates a real row — upstream resolves CN `auto` to `hy4-preview`, which
|
|
2402
|
+
* is already in the same roster. They are dropped here, at the single place
|
|
2403
|
+
* the roster is built, so the card, the picker, the probe and the discovery
|
|
2404
|
+
* route all agree. A user who already had one enabled keeps it through
|
|
2405
|
+
* `keepIds` below: this removes the OFFER, never a running model (issue #32's
|
|
2406
|
+
* failure mode).
|
|
2407
|
+
*
|
|
2331
2408
|
* `keepIds` is the user's own selection riding along with the refresh. The
|
|
2332
2409
|
* upstream roster may drop a model the user is actually running — the app
|
|
2333
2410
|
* channel's `cli` roster dropped `minimax-m2.7` on 2026-10-21 while the SAME
|
|
@@ -2351,10 +2428,11 @@ function selectCliModels(rawModels, agents, keepIds = []) {
|
|
|
2351
2428
|
}
|
|
2352
2429
|
}
|
|
2353
2430
|
const ids = cliIds !== void 0 && cliIds.length > 0 ? cliIds : [...byId.keys()];
|
|
2354
|
-
const models = ids.map((id) => byId.get(id)).filter((model) => model !== void 0);
|
|
2355
|
-
if (models.length === 0) throw new Error("workbuddy model catalog resolved to an empty list");
|
|
2431
|
+
const models = ids.filter((id) => !isRouterModelId(id)).map((id) => byId.get(id)).filter((model) => model !== void 0);
|
|
2356
2432
|
const kept = keepIds.filter((id) => !ids.includes(id)).map((id) => byId.get(id)).filter((model) => model !== void 0);
|
|
2357
|
-
|
|
2433
|
+
const resolved = [...models, ...kept];
|
|
2434
|
+
if (resolved.length === 0) throw new Error("workbuddy model catalog resolved to an empty list");
|
|
2435
|
+
return resolved;
|
|
2358
2436
|
}
|
|
2359
2437
|
/**
|
|
2360
2438
|
* The roster plus the promotion badges the SAME document carries.
|
|
@@ -4255,14 +4333,14 @@ var WorkBuddyCredentialStore = class {
|
|
|
4255
4333
|
* Static CLI models captured from the CN endpoint (2026-08-30). The upstream
|
|
4256
4334
|
* refresh replaces this list at startup; it exists so the provider registers
|
|
4257
4335
|
* with a usable catalog even while the first fetch is in flight or offline.
|
|
4336
|
+
*
|
|
4337
|
+
* The upstream's `auto` ROUTER is deliberately absent, matching what
|
|
4338
|
+
* `selectCliModels` now does to the live roster: it resolves to a different
|
|
4339
|
+
* model per request, so it is not a row this plugin can describe. Keeping it
|
|
4340
|
+
* here would have reintroduced it on the offline/第一帧 path only, which is the
|
|
4341
|
+
* hardest kind of inconsistency to notice.
|
|
4258
4342
|
*/
|
|
4259
4343
|
const FALLBACK_WORKBUDDY_MODELS = [
|
|
4260
|
-
{
|
|
4261
|
-
id: "auto",
|
|
4262
|
-
name: "Auto",
|
|
4263
|
-
contextWindow: 168e3,
|
|
4264
|
-
maxTokens: 32e3
|
|
4265
|
-
},
|
|
4266
4344
|
{
|
|
4267
4345
|
id: "hy3",
|
|
4268
4346
|
name: "Hy3",
|
|
@@ -4510,8 +4588,23 @@ function applyContextBudgets(catalog, budgets = {}) {
|
|
|
4510
4588
|
contextWindow: budgets[model.id] === void 0 ? model.contextWindow : Math.min(model.contextWindow, budgets[model.id])
|
|
4511
4589
|
}));
|
|
4512
4590
|
}
|
|
4591
|
+
/**
|
|
4592
|
+
* Derive the runtime catalog from the last-refreshed directory plus the user's
|
|
4593
|
+
* selection.
|
|
4594
|
+
*
|
|
4595
|
+
* An empty selection means "never configured" and serves the whole directory.
|
|
4596
|
+
* But a NON-empty selection that intersects the directory in nothing must NOT
|
|
4597
|
+
* serve nothing: that is the issue #32 failure mode (the picker empties out),
|
|
4598
|
+
* and it now has a realistic trigger — a user whose only ticked model was the
|
|
4599
|
+
* upstream's `auto` router, which this version stops offering. Such a selection
|
|
4600
|
+
* degrades to the whole directory rather than to silence: the user asked for
|
|
4601
|
+
* "some models to be on", and the alternative is a region with no models at
|
|
4602
|
+
* all. An empty directory still yields an empty result; the guard is only about
|
|
4603
|
+
* a selection that no longer matches anything.
|
|
4604
|
+
*/
|
|
4513
4605
|
function deriveCatalog(catalog, enabled, budgets = {}) {
|
|
4514
|
-
|
|
4606
|
+
const selected = enabled.size === 0 ? catalog : catalog.filter((model) => enabled.has(model.id));
|
|
4607
|
+
return applyContextBudgets(selected.length === 0 && enabled.size > 0 ? catalog : selected, budgets);
|
|
4515
4608
|
}
|
|
4516
4609
|
/**
|
|
4517
4610
|
* Mutable catalog shared by the shim's `/v1/models` and the adapter.
|
|
@@ -4770,7 +4863,7 @@ function generationOf(version) {
|
|
|
4770
4863
|
//#endregion
|
|
4771
4864
|
//#region src/version.ts
|
|
4772
4865
|
/** The npm package version this build was produced from. */
|
|
4773
|
-
const WORKBUDDY_CONNECT_VERSION = "4.14.
|
|
4866
|
+
const WORKBUDDY_CONNECT_VERSION = "4.14.1";
|
|
4774
4867
|
//#endregion
|
|
4775
4868
|
//#region src/host-heartbeat.ts
|
|
4776
4869
|
/**
|
|
@@ -4928,4 +5021,4 @@ async function clearHostHeartbeat() {
|
|
|
4928
5021
|
await rm(workbuddyHostHeartbeatPath(), { force: true });
|
|
4929
5022
|
}
|
|
4930
5023
|
//#endregion
|
|
4931
|
-
export { withEffectiveOff as $, workbuddyAccountId as A, modelIdOfBody as B, defaultDesktopAuthDirs as C, isVaultAccountId as D, isEncryptedCredentialError as E, WorkBuddyUpstreamClient as F, regionOf as G, parseReasoning as H, classifyUpstreamError as I, defaultOffModelIds as J, systemOpeningsOfBody as K, declaredTools as L, CREDENTIAL_REJECTED_CODE as M, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as N, legacyWorkbuddyOwnAuthPath as O, WorkBuddyCredentialRejectedError as P, recordRefusal as Q, isCredentialRejectedError as R, defaultDesktopAuthCandidates as S, hasEncryptedCredentialFields as T, parseUpstreamModel as U, parseCreditMultiplier as V, prepareChatBody as W, offDefaultFor as X, lastRefusalFor as Y, offRefused as Z, WORKBUDDY_AUTH_FILENAME as _,
|
|
5024
|
+
export { withEffectiveOff as $, workbuddyAccountId as A, modelIdOfBody as B, defaultDesktopAuthDirs as C, isVaultAccountId as D, isEncryptedCredentialError as E, WorkBuddyUpstreamClient as F, regionOf as G, parseReasoning as H, classifyUpstreamError as I, defaultOffModelIds as J, systemOpeningsOfBody as K, declaredTools as L, CREDENTIAL_REJECTED_CODE as M, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as N, legacyWorkbuddyOwnAuthPath as O, WorkBuddyCredentialRejectedError as P, recordRefusal as Q, isCredentialRejectedError as R, defaultDesktopAuthCandidates as S, hasEncryptedCredentialFields as T, parseUpstreamModel as U, parseCreditMultiplier as V, prepareChatBody as W, offDefaultFor as X, lastRefusalFor as Y, offRefused as Z, WORKBUDDY_AUTH_FILENAME as _, hostPeersInfo as _t, readHostHeartbeat as a, deriveAtRestKey as at, WorkBuddyEncryptedCredentialError as b, writeFileAtomic as bt, WORKBUDDY_CONNECT_VERSION as c, findWorkbuddyAppExecutable as ct, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, isWorkbuddyBundle as dt, NATIVE_MODALITY_BY_MODEL_ID as et, WorkBuddyCatalog as f, macosBundleExecutable as ft, ENCRYPTED_CREDENTIAL_CODE as g, workbuddyAppExecutableCandidates as gt, fallbackModelsFor as h, readAtRestKey as ht, processStartTimeMs as i, clearAtRestKeyCache as it, workbuddyOwnAuthPath as j, parseWorkBuddyAuth as k, piAiRuntimeInfo as l, findWorkbuddyAppExecutableWithSource as lt, deriveCatalog as m, openEncryptedField as mt, clearHostHeartbeat as n, nativeModalityOf as nt, workbuddyHostHeartbeatPath as o, deriveAtRestKeyId as ot, applyContextBudgets as p, macosNestedAppBundles as pt, REFUSES_OFF_MODEL_IDS as q, isHeartbeatProcessAlive as r, WORKBUDDY_APP_EXECUTABLE_ENV as rt, writeHostHeartbeat as s, fetchAtRestKeyPayload as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, imageDefaultFor as tt, FALLBACK_WORKBUDDY_MODELS as u, isEncryptedFieldWrapper as ut, WORKBUDDY_AUTH_FILE_ENV as v, resolveDshHome as vt, defaultDesktopAuthPath as w, authFileName as x, WorkBuddyCredentialStore as y, withFileLock as yt, knownRegionOf as z };
|
package/lib/index.d.ts
CHANGED
|
@@ -1286,6 +1286,12 @@ type WorkBuddyModelInfo = WorkBuddyUpstreamModel;
|
|
|
1286
1286
|
* Static CLI models captured from the CN endpoint (2026-08-30). The upstream
|
|
1287
1287
|
* refresh replaces this list at startup; it exists so the provider registers
|
|
1288
1288
|
* with a usable catalog even while the first fetch is in flight or offline.
|
|
1289
|
+
*
|
|
1290
|
+
* The upstream's `auto` ROUTER is deliberately absent, matching what
|
|
1291
|
+
* `selectCliModels` now does to the live roster: it resolves to a different
|
|
1292
|
+
* model per request, so it is not a row this plugin can describe. Keeping it
|
|
1293
|
+
* here would have reintroduced it on the offline/第一帧 path only, which is the
|
|
1294
|
+
* hardest kind of inconsistency to notice.
|
|
1289
1295
|
*/
|
|
1290
1296
|
declare const FALLBACK_WORKBUDDY_MODELS: readonly WorkBuddyModelInfo[];
|
|
1291
1297
|
/**
|
|
@@ -1312,6 +1318,20 @@ declare function fallbackModelsFor(region: 'cn' | 'global'): readonly WorkBuddyM
|
|
|
1312
1318
|
* never been configured must still serve models rather than nothing.
|
|
1313
1319
|
*/
|
|
1314
1320
|
type WorkBuddyContextBudget = number;
|
|
1321
|
+
/**
|
|
1322
|
+
* Derive the runtime catalog from the last-refreshed directory plus the user's
|
|
1323
|
+
* selection.
|
|
1324
|
+
*
|
|
1325
|
+
* An empty selection means "never configured" and serves the whole directory.
|
|
1326
|
+
* But a NON-empty selection that intersects the directory in nothing must NOT
|
|
1327
|
+
* serve nothing: that is the issue #32 failure mode (the picker empties out),
|
|
1328
|
+
* and it now has a realistic trigger — a user whose only ticked model was the
|
|
1329
|
+
* upstream's `auto` router, which this version stops offering. Such a selection
|
|
1330
|
+
* degrades to the whole directory rather than to silence: the user asked for
|
|
1331
|
+
* "some models to be on", and the alternative is a region with no models at
|
|
1332
|
+
* all. An empty directory still yields an empty result; the guard is only about
|
|
1333
|
+
* a selection that no longer matches anything.
|
|
1334
|
+
*/
|
|
1315
1335
|
declare function deriveCatalog(catalog: readonly WorkBuddyModelInfo[], enabled: ReadonlySet<string>, budgets?: Readonly<Record<string, WorkBuddyContextBudget | undefined>>): WorkBuddyModelInfo[];
|
|
1316
1336
|
/**
|
|
1317
1337
|
* Mutable catalog shared by the shim's `/v1/models` and the adapter.
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as withEffectiveOff, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as defaultOffModelIds, K as systemOpeningsOfBody, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as recordRefusal, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as offDefaultFor, Y as lastRefusalFor, Z as offRefused, _ as WORKBUDDY_AUTH_FILENAME,
|
|
1
|
+
import { $ as withEffectiveOff, A as workbuddyAccountId, B as modelIdOfBody, C as defaultDesktopAuthDirs, D as isVaultAccountId, E as isEncryptedCredentialError, F as WorkBuddyUpstreamClient, G as regionOf, H as parseReasoning, I as classifyUpstreamError, J as defaultOffModelIds, K as systemOpeningsOfBody, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as recordRefusal, R as isCredentialRejectedError, S as defaultDesktopAuthCandidates, T as hasEncryptedCredentialFields, U as parseUpstreamModel, V as parseCreditMultiplier, W as prepareChatBody, X as offDefaultFor, Y as lastRefusalFor, Z as offRefused, _ as WORKBUDDY_AUTH_FILENAME, a as readHostHeartbeat, at as deriveAtRestKey, b as WorkBuddyEncryptedCredentialError, bt as writeFileAtomic, c as WORKBUDDY_CONNECT_VERSION, ct as findWorkbuddyAppExecutable, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as isWorkbuddyBundle, et as NATIVE_MODALITY_BY_MODEL_ID, f as WorkBuddyCatalog, ft as macosBundleExecutable, g as ENCRYPTED_CREDENTIAL_CODE, gt as workbuddyAppExecutableCandidates, h as fallbackModelsFor, ht as readAtRestKey, i as processStartTimeMs, it as clearAtRestKeyCache, j as workbuddyOwnAuthPath, k as parseWorkBuddyAuth, l as piAiRuntimeInfo, m as deriveCatalog, mt as openEncryptedField, n as clearHostHeartbeat, nt as nativeModalityOf, o as workbuddyHostHeartbeatPath, ot as deriveAtRestKeyId, p as applyContextBudgets, pt as macosNestedAppBundles, q as REFUSES_OFF_MODEL_IDS, r as isHeartbeatProcessAlive, rt as WORKBUDDY_APP_EXECUTABLE_ENV, s as writeHostHeartbeat, st as fetchAtRestKeyPayload, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as imageDefaultFor, u as FALLBACK_WORKBUDDY_MODELS, ut as isEncryptedFieldWrapper, v as WORKBUDDY_AUTH_FILE_ENV, vt as resolveDshHome, w as defaultDesktopAuthPath, x as authFileName, y as WorkBuddyCredentialStore, yt as withFileLock, z as knownRegionOf } from "./host-heartbeat-D1WnqQY2.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -6173,55 +6173,6 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
|
|
|
6173
6173
|
}, "dsh-connect-workbuddy: Web status route");
|
|
6174
6174
|
}
|
|
6175
6175
|
//#endregion
|
|
6176
|
-
//#region src/native-modality.ts
|
|
6177
|
-
/**
|
|
6178
|
-
* Reviewed vendor classifications, keyed by EXACT model id (never a prefix).
|
|
6179
|
-
* Ids absent from this record are `unknown` — see {@link nativeModalityOf}.
|
|
6180
|
-
*/
|
|
6181
|
-
const NATIVE_MODALITY_BY_MODEL_ID = {
|
|
6182
|
-
"deepseek-v4-pro": "text",
|
|
6183
|
-
"glm-5.1": "text",
|
|
6184
|
-
"glm-5.2": "text",
|
|
6185
|
-
"glm-5.3": "text",
|
|
6186
|
-
"hy3": "text",
|
|
6187
|
-
"hy4-preview": "text",
|
|
6188
|
-
"deepseek-v4.1-flash": "multimodal",
|
|
6189
|
-
"glm-5.3-flash": "multimodal",
|
|
6190
|
-
"glm-5v-turbo": "multimodal",
|
|
6191
|
-
"kimi-k2.6": "multimodal",
|
|
6192
|
-
"kimi-k2.7": "multimodal",
|
|
6193
|
-
"kimi-k3-1": "multimodal",
|
|
6194
|
-
"minimax-m3": "multimodal"
|
|
6195
|
-
};
|
|
6196
|
-
/** The `auto` entry is a router that picks a model, not a model itself. */
|
|
6197
|
-
const ROUTER_MODEL_ID = "auto";
|
|
6198
|
-
/**
|
|
6199
|
-
* Native modality of one model id. Exact match only: a new model never
|
|
6200
|
-
* inherits a classification from its family, its display name, or the
|
|
6201
|
-
* platform's image flag.
|
|
6202
|
-
*/
|
|
6203
|
-
function nativeModalityOf(modelId) {
|
|
6204
|
-
if (modelId === ROUTER_MODEL_ID) return "router";
|
|
6205
|
-
return NATIVE_MODALITY_BY_MODEL_ID[modelId] ?? "unknown";
|
|
6206
|
-
}
|
|
6207
|
-
/**
|
|
6208
|
-
* Whether a model refresh should PRE-CHECK this model's image box.
|
|
6209
|
-
*
|
|
6210
|
-
* Only a documented `multimodal` is pre-checked. `text` is documented as
|
|
6211
|
-
* unable to read images, and `unknown` is deliberately left unchecked rather
|
|
6212
|
-
* than guessed — the whole reason this table exists is that the platform flag
|
|
6213
|
-
* guessed wrong. A platform `supportsImages: false` still vetoes, so a
|
|
6214
|
-
* documented-multimodal id can never be pre-checked against the platform's
|
|
6215
|
-
* explicit "no".
|
|
6216
|
-
*
|
|
6217
|
-
* The user can always tick a box by hand; this decides a DEFAULT only, and the
|
|
6218
|
-
* effective runtime flag remains the saved `imageModelIds`.
|
|
6219
|
-
*/
|
|
6220
|
-
function imageDefaultFor(info) {
|
|
6221
|
-
if (info.supportsImages === false) return false;
|
|
6222
|
-
return NATIVE_MODALITY_BY_MODEL_ID[info.id] === "multimodal";
|
|
6223
|
-
}
|
|
6224
|
-
//#endregion
|
|
6225
6176
|
//#region src/index.ts
|
|
6226
6177
|
/** Stable Cordis plugin name. */
|
|
6227
6178
|
const name = "dsh-connect-workbuddy";
|
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.14.
|
|
5
|
+
"version": "4.14.1",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|