dsh-connect-workbuddy 5.0.0 → 5.0.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,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.0.1 (2026-10-22)
4
+
5
+ > 版本号是 **patch**:改卡面上的一个字符串(标题)、去掉模型列表的一条 CSS 规则与两行路由项,以及几处描述它们的文档。没有接口或配置结构的变更——但如果你的配置里存着 `auto` / `default-model`,升级后它不再出现在列表里。
6
+
7
+ ### Fixes
8
+
9
+ - **国际区的 `Auto` 行也从模型列表里去掉了** —— 它和国内区的 `auto` 是**同一个东西的两个 id**:都是该区的默认路由(`isDefault: true`、都叫 `Auto`),也都是各自名册里**唯一不声明积分倍率**的那一行(实测 2026-10-22:国内 20 个里 19 个标了价,国际 28 个里 27 个)。国内区的 `auto` 早就被过滤了,国际区的 `default-model` 一直漏着——一个插件无法标价的行,就是它无法向你交代成本的行。
10
+ - **判据不是名字**:`fast-model` / `balanced-model` / `primary-model` 也是别名,但它们都声明了真实倍率(x0.34 / x0.59 / x3.31),所以保留。丢掉它们会平白拿走三个用户真正会选的模型。
11
+ - **顺带修掉一个会把它带回来的漏洞**:`keepIds` 分支(让用户正在用的模型不被刷新撤掉,issue #32)原本不过滤路由,于是**旧配置里 `enabledModelIds` 还留着 `auto` 的人,每次刷新都会把这行重新加回来**——而且取消勾选也去不掉。现在两条路径都过滤。
12
+ - **在读取点过滤,而不是迁移已保存的配置**:`lastCatalog` 是配置里带着的历史快照,卡片读的就是它。只改刷新逻辑的话,升级后这一行会继续显示到下次刷新为止,看起来就像「没修好」。现在 `withoutRouters()` 在 `displayModels` 与 `configuredModels` 两处读取时过滤,存进去的值不动——升级后立刻消失,而不是等一次刷新。
13
+
14
+ - **模型页不再有内部滚动条,所有模型一次全部呈现** —— 设置卡片「模型」栏的列表此前带 `max-height: 520px` + `overflow-y: auto`,20 多个模型只显示七行左右,其余藏在一条需要先发现、再拖动的滚动条后面;而面板本身已经在滚动,于是是两条滚动条叠在一起。
15
+ - **`overflow: hidden` 保留**,而且是必要的:圆角由这个带边框的盒子绘制,不裁剪的话首行与末行会把直角画到圆角上。只是现在盒子的高度就等于内容高度,不会再裁掉任何东西。
16
+ - **5.0.0 的描述写错了**,本版一并更正:当时记的是「模型表自己内滚」,与最终代码不符。
17
+
18
+ - **插件卡片的标题改回包名 `dsh-connect-workbuddy`** —— 5.0.0 里补 `locale/{en,zh}.json` 时,`meta.title` 写成了「WorkBuddy 模型接入」/「WorkBuddy Model Access」,于是卡片标题从裸包名变成了那个名字。这个插件的身份就是包名——仓库、npm 页面、issue 里都是这个叫法——所以改回去。
19
+ - **显式写出包名,而不是删掉 `meta.title` 留空**。两种做法渲染出的字完全一样(宿主 `localizedText` 的 fallback 就是 `manifest.name`),但留空之后文件里没有任何地方记录「标题应当等于包名」这件事,下一次「给它起个好听点的名字」看起来就会像一次免费改进,而不是一次回退。`tests/locale-meta.spec.ts` 现在钉住它。
20
+ - **简介不受影响**:仍由 `locale/zh.json` / `locale/en.json` 各自的 `meta.description` 提供,中英分离如初。
21
+
22
+ ### Docs
23
+
24
+ - `docs/DESIGN.md` 更正一处过时说法(原文写「本仓库的 `package.json.description` 是英文」,那是 PR 原本的写法,合并时按「保持中文」处理了);并补上第 4 条机制事实:`meta.title` 就写成包名,以及为什么写出来而不是留空。
25
+ - `CHANGELOG` 的 5.0.0 条目同步改成包名口径。
26
+ - 顺带说明 `package.json` 里那个 `DSH Connect WorkBuddy`(`displayName`)**不参与任何渲染**——宿主标题的回退是 `manifest.name`,不是它。保留只是历史遗留。
27
+
28
+ ### Tests
29
+
30
+ - `tests/locale-meta.spec.ts` 那条从「标题**不**等于包名」改为「两种语言的标题**都**等于包名」,并把理由写进用例:这是一个决定,不是一个默认值。
31
+ - `tests/client-host-contract.spec.ts` 新增 1 条守卫:`.dsm-workbuddy-model-list` 的规则里**不得**出现 `max-height` 或 `overflow-y`,同时**必须**保留 `overflow:hidden`(只断言「没有上限」会让人以后把裁剪也顺手删掉,而那是圆角所依赖的)。
32
+ - `tests/catalog.spec.ts` 新增 4 条 `withoutRouters` 用例:过滤旧快照里的路由、CN 的 `auto` 与国��的 `default-model` 都被认出、`fast`/`balanced`/`primary` 不受牵连、不修改入参;并把「两个区的 fallback 名册都不含各自的路由」从只查 `auto` 扩到两个 id。
33
+ - `tests/upstream.spec.ts` 新增 4 条 `selectCliModels` 用例:两个区各自的 `auto` 都被丢掉、**旧配置里仍写着 `auto` 时不会借 `keepIds` 复活**、真正的「已下架但你在用」的模型仍然被保住(issue #32 那一半不能被这次过滤破坏)。
34
+ - `tests/native-modality.spec.ts` 的路由用例扩成两个 id,并明确 `fast`/`balanced`/`primary`/`deep` 不算路由。
35
+ - 全量 `npx vitest run` = **107 文件 / 1760 测试全绿**;`tsc` 两个 project 均 exit 0;`npm run build` 成功。
36
+
3
37
  ## 5.0.0 (2026-10-21)
4
38
 
5
39
  > 版本号是 **major**:接口与配置项没有破坏性变更(顶层配置字段一个不少,也没有导出被删),但这一版把「花不花钱」的决定权**第一次交给了模型**。在此之前,本插件每一次花钱都跟一个人工动作绑在一起:一条聊天请求是你发的,模型行的「测试」按钮是你点的。5.0.0 新增的 `workbuddy_draw_image` 是模型自己决定调的——约 5.71 积分一张,而宿主没有「调用前先确认」的机制(`needsApproval` / `toolApproval` 在 `app.asar` 里都不存在)。
@@ -18,7 +52,7 @@
18
52
  - **设置卡片按「账号 / 模型 / 诊断」分栏** —— 这张卡片原来是一条 3538 像素的长页,把两类提交语义完全不同的事情混排在一根竖线上:账号改动点一下立刻生效,目录改动是草稿、必须按「保存」,而用户在第 1 屏勾完模型后要滚三屏才能找到那个按钮。
19
53
  - **三栏**:**账号**(地区说明、积分开关、凭据类错误、账号池的切换/签到/导入导出/日志)、**模型**(模型表、保存/放弃)、**诊断**(只读事实表:区域、上游域名、当前服务的账号名、登录有效期、目录模型数、已启用数、上游最近一次错误的原文)。地区的「国内版 / 国际版」两个 tab 原样保留在最上面,它是这三栏共同的作用域。
20
54
  - **切栏不重新拉、不丢草稿**:非活动栏用 `hidden` 留在页面里而不是卸载,所以改了几个勾、切去看一眼余额、再切回来的改动还在,账号池的日志也不重建。`hidden` 同时把非活动栏移出无障碍树,`role="tabpanel"` 的语义成立(用 CSS `display:none` 反而会被后续样式覆盖)。
21
- - **模型表自己内滚**(`max-height: 520px`),栏高不再由模型条数决定。
55
+ - 模型表**全部展开,不设内部滚动**。(5.0.0 原本写成 `max-height: 520px` 内滚,5.0.2 改掉:见该版说明。)
22
56
  - **签出态不画分栏**:没有登录时这张卡片就是一屏说明,套三个空 tab 只会把唯一的说明藏起来。
23
57
  - **诊断栏没有任何控件**,它存在的唯一理由是能被整段贴进问题反馈:账号显示**名字**而不是 id(测试里专门断言不出现 id),上游错误是原文引用、不翻译不概括。
24
58
 
@@ -32,7 +66,7 @@
32
66
  - **`locale/en.json` 是锚点**:读取端遍历的是「英文资源所在目录」,没有它 `zh.json` 永远不会被读——而且不报错。
33
67
  - **`exports` 缺 `"./locale/*.json"` 通配 = 静默失败**:解析抛 `ERR_PACKAGE_PATH_NOT_EXPORTED`,读取端把它连同其它失败一起吞掉。
34
68
  - **`files` 必须带裸目录 `locale`**,否则发布出去的 tarball 里没有它自己那份文案。
35
- - 现在中文界面显示「WorkBuddy 模型接入」+中文简介,英文界面显示「WorkBuddy Model Access」+英文简介。`package.json` 的 `description` **保持中文**:它同时是 npm 包页面的简介、GitHub 的仓库简介,和「万一 locale 读不到」的最后兜底——英文文案住在 `locale/en.json` 里,那才是能覆盖兜底的那一份。
69
+ - 现在中文界面读 `locale/zh.json`、英文界面读 `locale/en.json`,两边各有自己的简介。**标题就是包名 `dsh-connect-workbuddy`**(两处 `meta.title` 都这么写):这个插件的身份就是那个包名,仓库、npm 页面与 issue 里都是这个叫法;写出来而不是留空,是因为留空也会经 `localizedText` 的 `fallback` 渲染出同样的字,但文件里就没有任何地方记录「这是决定」。`package.json` 的 `description` **保持中文**:它同时是 npm 包页面的简介、GitHub 的仓库简介,和「万一 locale 读不到」的最后兜底——英文文案住在 `locale/en.json` 里,那才是能覆盖兜底的那一份。
36
70
 
37
71
  - **新增生图工具 `workbuddy_draw_image`(国内区,默认关闭,需显式授权)** —— 把 WorkBuddy 官方 CLI 的文生图能力暴露成一个 DSH agent 工具,模型可以按用户的描述直接画一张图。
38
72
  - **为什么它是 opt-in,而其他功能不是**:这是本插件唯一「由模型决定花钱」的能力。聊天请求是你发的,「测试」按钮是你点的,而工具调用是模型自己发起的——约 5.71 积分一张,宿主又没有逐次确认的机制。所以开关归你:`regions.cn.imageToolEnabled`,默认 `false`,关着时工具不注册。这与 3.0.0 给账号池的处理是同一个道理(能改变账单落点的功能必须显式打开)。
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, Ot as hostPeersInfo, S as defaultDesktopAuthCandidates, a as readHostHeartbeat, bt as findWorkbuddyAppExecutableWithSource, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, j as workbuddyOwnAuthPath, l as piAiRuntimeInfo, mt as WORKBUDDY_APP_EXECUTABLE_ENV, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, v as WORKBUDDY_AUTH_FILE_ENV, y as WorkBuddyCredentialStore } from "./host-heartbeat-D313ENPV.js";
2
+ import { C as defaultDesktopAuthCandidates, I as WorkBuddyUpstreamClient, M as workbuddyOwnAuthPath, a as readHostHeartbeat, b as WorkBuddyCredentialStore, c as WORKBUDDY_CONNECT_VERSION, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, ht as WORKBUDDY_APP_EXECUTABLE_ENV, k as legacyWorkbuddyOwnAuthPath, kt as hostPeersInfo, l as piAiRuntimeInfo, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, w as defaultDesktopAuthDirs, xt as findWorkbuddyAppExecutableWithSource, y as WORKBUDDY_AUTH_FILE_ENV } from "./host-heartbeat-DtMzOwZr.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
@@ -1556,17 +1556,30 @@ window.__ModuleLoader__.load({
1556
1556
  /**
1557
1557
  * Ids the upstream lists in the model roster that are ROUTERS, not models.
1558
1558
  *
1559
- * `auto` (CN) is the one observed today: the gateway resolves it to whichever
1560
- * model it considers the best fit (measured: CN `auto` → `hy4-preview`), so its
1561
- * price, context window and behaviour are decided at request time by something
1562
- * else. Offering it as a row advertises a model whose terms the plugin cannot
1563
- * state, and it duplicates a real row already in the same roster.
1559
+ * Two of them are known, one per region, and they are the SAME concept under
1560
+ * two ids:
1561
+ *
1562
+ * - `auto` (CN gateway) — the gateway resolves it to whichever model it
1563
+ * considers the best fit (measured: CN `auto` → `hy4-preview`), and its own
1564
+ * description says so outright: 「平衡效果与速度。自动为每个任务匹配最优模型,
1565
+ * 积分倍率随之浮动」 ("picks the best model per task; the credit multiplier
1566
+ * floats accordingly").
1567
+ * - `default-model` (international gateway) — identical role (name "Auto",
1568
+ * `isDefault: true`, the region's default pick), also the ONLY entry in that
1569
+ * roster that declares no `credits` at all.
1570
+ *
1571
+ * The evidence for treating both as routers is the shared, self-describing
1572
+ * shape, not the name: measured 2026-10-22 against both live gateways, these
1573
+ * two entries are the ONLY ones whose `credits` field is absent/empty (CN 19
1574
+ * of 20 priced, global 27 of 28), and the ones carrying `isDefault: true`.
1575
+ * A row the plugin cannot price is a row whose cost it cannot state, and the
1576
+ * router duplicates a real row already in the same roster.
1564
1577
  *
1565
1578
  * Kept as an explicit table rather than a heuristic: a router is an upstream
1566
1579
  * concept with no declared flag, and guessing from the name would eventually
1567
1580
  * hide a real model. Add an id here only after the same kind of evidence.
1568
1581
  */
1569
- const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto"]);
1582
+ const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto", "default-model"]);
1570
1583
  /** Whether this id is an upstream router rather than a model. */
1571
1584
  function isRouterModelId(modelId) {
1572
1585
  return ROUTER_MODEL_IDS.has(modelId);
@@ -4231,14 +4244,22 @@ window.__ModuleLoader__.load({
4231
4244
  an error banner, because the fact it states is normal product behaviour
4232
4245
  rather than a failure. Weight is the only signal it needs. */
4233
4246
  .dsm-workbuddy-free-quota-warn{font-weight:600}
4234
- /* The rows scroll INSIDE the box once the list outgrows it: the section owns a
4235
- fixed share of the card instead of setting its height from the roster's
4236
- length. Without this the list was the card's height all by itself — 20-odd
4237
- models, each carrying a budget field, a reasoning picker and an image toggle.
4238
- The rounded corners survive because the scroll container is the bordered box
4239
- itself, so the first and last row are clipped by the same radius the border
4240
- already draws. */
4241
- .dsm-workbuddy-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden;max-height:520px;overflow-y:auto}
4247
+ /* Every model is laid out in full — NO inner scroll box.
4248
+ *
4249
+ * The previous rule capped this at max-height 520px with overflow-y auto, so a
4250
+ * 20-model roster showed about seven rows and hid the rest behind a scrollbar
4251
+ * the user had to discover and then drag inside a panel that already scrolls.
4252
+ * A model picker whose whole job is "let me see and tick models" should not
4253
+ * have two scrollbars stacked on top of each other.
4254
+ *
4255
+ * overflow:hidden stays, and it is load-bearing rather than vestigial: the
4256
+ * rounded corners are drawn by this bordered box, so without clipping the first
4257
+ * and last row would paint square corners over it. Nothing is CLIPPED by it any
4258
+ * more, because the box is now exactly as tall as its contents.
4259
+ *
4260
+ * NOTE: no backticks in CSS comments inside this template literal — one would
4261
+ * terminate the string early and silently drop every rule after it. */
4262
+ .dsm-workbuddy-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden}
4242
4263
  .dsm-workbuddy-model{display:grid;grid-template-columns:minmax(0,1fr);gap:7px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#232529);transition:opacity .16s}
4243
4264
  .dsm-workbuddy-model-disabled{opacity:.55}
4244
4265
  .dsm-workbuddy-model+.dsm-workbuddy-model{border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
@@ -985,17 +985,30 @@ const NATIVE_MODALITY_BY_MODEL_ID = {
985
985
  /**
986
986
  * Ids the upstream lists in the model roster that are ROUTERS, not models.
987
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.
988
+ * Two of them are known, one per region, and they are the SAME concept under
989
+ * two ids:
990
+ *
991
+ * - `auto` (CN gateway) — the gateway resolves it to whichever model it
992
+ * considers the best fit (measured: CN `auto` → `hy4-preview`), and its own
993
+ * description says so outright: 「平衡效果与速度。自动为每个任务匹配最优模型,
994
+ * 积分倍率随之浮动」 ("picks the best model per task; the credit multiplier
995
+ * floats accordingly").
996
+ * - `default-model` (international gateway) — identical role (name "Auto",
997
+ * `isDefault: true`, the region's default pick), also the ONLY entry in that
998
+ * roster that declares no `credits` at all.
999
+ *
1000
+ * The evidence for treating both as routers is the shared, self-describing
1001
+ * shape, not the name: measured 2026-10-22 against both live gateways, these
1002
+ * two entries are the ONLY ones whose `credits` field is absent/empty (CN 19
1003
+ * of 20 priced, global 27 of 28), and the ones carrying `isDefault: true`.
1004
+ * A row the plugin cannot price is a row whose cost it cannot state, and the
1005
+ * router duplicates a real row already in the same roster.
993
1006
  *
994
1007
  * Kept as an explicit table rather than a heuristic: a router is an upstream
995
1008
  * concept with no declared flag, and guessing from the name would eventually
996
1009
  * hide a real model. Add an id here only after the same kind of evidence.
997
1010
  */
998
- const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto"]);
1011
+ const ROUTER_MODEL_IDS = /* @__PURE__ */ new Set(["auto", "default-model"]);
999
1012
  /** Whether this id is an upstream router rather than a model. */
1000
1013
  function isRouterModelId(modelId) {
1001
1014
  return ROUTER_MODEL_IDS.has(modelId);
@@ -3233,7 +3246,7 @@ function selectCliModels(rawModels, agents, keepIds = []) {
3233
3246
  }
3234
3247
  const ids = cliIds !== void 0 && cliIds.length > 0 ? cliIds : [...byId.keys()];
3235
3248
  const models = ids.filter((id) => !isRouterModelId(id)).map((id) => byId.get(id)).filter((model) => model !== void 0);
3236
- const kept = keepIds.filter((id) => !ids.includes(id)).map((id) => byId.get(id)).filter((model) => model !== void 0);
3249
+ const kept = keepIds.filter((id) => !ids.includes(id) && !isRouterModelId(id)).map((id) => byId.get(id)).filter((model) => model !== void 0);
3237
3250
  const resolved = [...models, ...kept];
3238
3251
  if (resolved.length === 0) throw new Error("workbuddy model catalog resolved to an empty list");
3239
3252
  return resolved;
@@ -5229,6 +5242,22 @@ var WorkBuddyCredentialStore = class {
5229
5242
  //#endregion
5230
5243
  //#region src/catalog.ts
5231
5244
  /**
5245
+ * WorkBuddy model catalog: a static fallback list, replaced by the upstream's
5246
+ * dynamic catalog once it loads, and filtered by the user's explicit selection.
5247
+ *
5248
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
5249
+ * — 「上次刷新的完整目录(lastCatalog)与用户勾选分离,运行时目录由两者
5250
+ * 推导」的模型来自该项目。它让卡片永远展示最新目录而不是陈旧快照,
5251
+ * 并让上游刷新成为草稿操作(用户点保存才生效)。
5252
+ * 静态 fallback 目录的做法来自
5253
+ * corrinehu/dsh-workbuddy-connect(MIT):上游不可用时 provider 不为空。
5254
+ * 改动:WorkBuddy 直接用各模型的 `maxInputTokens` 声明实际上下文窗口;
5255
+ * 上游没有独立的长上下文开关或第二个模型 id,因此不会虚构 `@1m` 变体。
5256
+ * 本目录额外承载上游给出的积分倍率、多模态与推理档位。
5257
+ *
5258
+ * @module dsh-connect-workbuddy/catalog
5259
+ */
5260
+ /**
5232
5261
  * Static CLI models captured from the CN endpoint (2026-08-30). The upstream
5233
5262
  * refresh replaces this list at startup; it exists so the provider registers
5234
5263
  * with a usable catalog even while the first fetch is in flight or offline.
@@ -5316,13 +5345,6 @@ const FALLBACK_WORKBUDDY_MODELS = [
5316
5345
  * usable before the first fetch lands. Order and rates mirror the upstream.
5317
5346
  */
5318
5347
  const FALLBACK_WORKBUDDY_MODELS_GLOBAL = [
5319
- {
5320
- id: "default-model",
5321
- name: "Auto",
5322
- contextWindow: 176e3,
5323
- maxTokens: 24e3,
5324
- creditMultiplier: .79
5325
- },
5326
5348
  {
5327
5349
  id: "fast-model",
5328
5350
  name: "Fast",
@@ -5466,6 +5488,21 @@ function fallbackModelsFor(region) {
5466
5488
  return region === "global" ? FALLBACK_WORKBUDDY_MODELS_GLOBAL : FALLBACK_WORKBUDDY_MODELS;
5467
5489
  }
5468
5490
  /**
5491
+ * Drop the upstream's ROUTER rows from a roster that came from settings.
5492
+ *
5493
+ * The static lists and `selectCliModels` both refuse routers already, but
5494
+ * `lastCatalog` is a SNAPSHOT the user's profile carries from an earlier
5495
+ * version — and every read of it (the card's list, the runtime catalog, the
5496
+ * provider's advertisement) would otherwise keep showing the row long after the
5497
+ * code stopped offering it. Filtering at the READ point instead of migrating the
5498
+ * saved value is deliberate: the stored slot stays exactly what the last refresh
5499
+ * wrote, so nothing is silently rewritten on load, and the row disappears the
5500
+ * moment the new code runs rather than at the next refresh.
5501
+ */
5502
+ function withoutRouters(models) {
5503
+ return models.filter((model) => !isRouterModelId(model.id));
5504
+ }
5505
+ /**
5469
5506
  * Apply the saved local DSH budget. A budget is an UPPER LIMIT the user sets,
5470
5507
  * never a default: a model without an explicit budget keeps its own window, and
5471
5508
  * a budget can only ever LOWER a window (`Math.min`), so a value above the
@@ -5762,7 +5799,7 @@ function generationOf(version) {
5762
5799
  //#endregion
5763
5800
  //#region src/version.ts
5764
5801
  /** The npm package version this build was produced from. */
5765
- const WORKBUDDY_CONNECT_VERSION = "5.0.0";
5802
+ const WORKBUDDY_CONNECT_VERSION = "5.0.1";
5766
5803
  //#endregion
5767
5804
  //#region src/host-heartbeat.ts
5768
5805
  /**
@@ -5920,4 +5957,4 @@ async function clearHostHeartbeat() {
5920
5957
  await rm(workbuddyHostHeartbeatPath(), { force: true });
5921
5958
  }
5922
5959
  //#endregion
5923
- export { REFUSES_OFF_MODEL_IDS as $, workbuddyAccountId as A, withFileLock as At, isCredentialRejectedError as B, defaultDesktopAuthDirs as C, macosBundleExecutable as Ct, isVaultAccountId as D, workbuddyAppExecutableCandidates as Dt, isEncryptedCredentialError as E, readAtRestKey as Et, WorkBuddyUpstreamClient as F, parseCreditMultiplier as G, knownRegionOf as H, classifyUpstreamError as I, perturbChannelFingerprints as J, parseReasoning as K, declaredTools as L, CREDENTIAL_REJECTED_CODE as M, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as N, legacyWorkbuddyOwnAuthPath as O, hostPeersInfo as Ot, WorkBuddyCredentialRejectedError as P, busyDisplaySuffix as Q, forceNeutralHead as R, defaultDesktopAuthCandidates as S, isWorkbuddyBundle as St, hasEncryptedCredentialFields as T, openEncryptedField as Tt, modelIdOfBody as U, isFirstMessageGateBody as V, openingEvidenceOfBody as W, regionOf as X, prepareChatBody as Y, attachBusyLevels as Z, WORKBUDDY_AUTH_FILENAME as _, deriveAtRestKeyId as _t, readHostHeartbeat as a, withEffectiveOff as at, WorkBuddyEncryptedCredentialError as b, findWorkbuddyAppExecutableWithSource as bt, WORKBUDDY_CONNECT_VERSION as c, pickImageModel as ct, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, NATIVE_MODALITY_BY_MODEL_ID as dt, defaultOffModelIds as et, WorkBuddyCatalog as f, imageDefaultFor as ft, ENCRYPTED_CREDENTIAL_CODE as g, deriveAtRestKey as gt, fallbackModelsFor as h, clearAtRestKeyCache as ht, processStartTimeMs as i, recordRefusal as it, workbuddyOwnAuthPath as j, writeFileAtomic as jt, parseWorkBuddyAuth as k, resolveDshHome as kt, piAiRuntimeInfo as l, parseQueueAnswer as lt, deriveCatalog as m, WORKBUDDY_APP_EXECUTABLE_ENV as mt, clearHostHeartbeat as n, offDefaultFor as nt, workbuddyHostHeartbeatPath as o, DEFAULT_IMAGE_SIZE as ot, applyContextBudgets as p, nativeModalityOf as pt, parseUpstreamModel as q, isHeartbeatProcessAlive as r, offRefused as rt, writeHostHeartbeat as s, WorkBuddyImageError as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, lastRefusalFor as tt, FALLBACK_WORKBUDDY_MODELS as u, queueNoticeText as ut, WORKBUDDY_AUTH_FILE_ENV as v, fetchAtRestKeyPayload as vt, defaultDesktopAuthPath as w, macosNestedAppBundles as wt, authFileName as x, isEncryptedFieldWrapper as xt, WorkBuddyCredentialStore as y, findWorkbuddyAppExecutable as yt, isConversationIdGateBody as z };
5960
+ export { busyDisplaySuffix as $, parseWorkBuddyAuth as A, resolveDshHome as At, isConversationIdGateBody as B, defaultDesktopAuthCandidates as C, isWorkbuddyBundle as Ct, isEncryptedCredentialError as D, readAtRestKey as Dt, hasEncryptedCredentialFields as E, openEncryptedField as Et, WorkBuddyCredentialRejectedError as F, openingEvidenceOfBody as G, isFirstMessageGateBody as H, WorkBuddyUpstreamClient as I, parseUpstreamModel as J, parseCreditMultiplier as K, classifyUpstreamError as L, workbuddyOwnAuthPath as M, writeFileAtomic as Mt, CREDENTIAL_REJECTED_CODE as N, isVaultAccountId as O, workbuddyAppExecutableCandidates as Ot, WORKBUDDY_FALLBACK_SYSTEM_PROMPT as P, attachBusyLevels as Q, declaredTools as R, authFileName as S, isEncryptedFieldWrapper as St, defaultDesktopAuthPath as T, macosNestedAppBundles as Tt, knownRegionOf as U, isCredentialRejectedError as V, modelIdOfBody as W, prepareChatBody as X, perturbChannelFingerprints as Y, regionOf as Z, ENCRYPTED_CREDENTIAL_CODE as _, deriveAtRestKey as _t, readHostHeartbeat as a, recordRefusal as at, WorkBuddyCredentialStore as b, findWorkbuddyAppExecutable as bt, WORKBUDDY_CONNECT_VERSION as c, WorkBuddyImageError as ct, FALLBACK_WORKBUDDY_MODELS_GLOBAL as d, queueNoticeText as dt, REFUSES_OFF_MODEL_IDS as et, WorkBuddyCatalog as f, NATIVE_MODALITY_BY_MODEL_ID as ft, withoutRouters as g, clearAtRestKeyCache as gt, fallbackModelsFor as h, WORKBUDDY_APP_EXECUTABLE_ENV as ht, processStartTimeMs as i, offRefused as it, workbuddyAccountId as j, withFileLock as jt, legacyWorkbuddyOwnAuthPath as k, hostPeersInfo as kt, piAiRuntimeInfo as l, pickImageModel as lt, deriveCatalog as m, nativeModalityOf as mt, clearHostHeartbeat as n, lastRefusalFor as nt, workbuddyHostHeartbeatPath as o, withEffectiveOff as ot, applyContextBudgets as p, imageDefaultFor as pt, parseReasoning as q, isHeartbeatProcessAlive as r, offDefaultFor as rt, writeHostHeartbeat as s, DEFAULT_IMAGE_SIZE as st, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, defaultOffModelIds as tt, FALLBACK_WORKBUDDY_MODELS as u, parseQueueAnswer as ut, WORKBUDDY_AUTH_FILENAME as v, deriveAtRestKeyId as vt, defaultDesktopAuthDirs as w, macosBundleExecutable as wt, WorkBuddyEncryptedCredentialError as x, findWorkbuddyAppExecutableWithSource as xt, WORKBUDDY_AUTH_FILE_ENV as y, fetchAtRestKeyPayload as yt, forceNeutralHead as z };
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as REFUSES_OFF_MODEL_IDS, A as workbuddyAccountId, At as withFileLock, B as isCredentialRejectedError, C as defaultDesktopAuthDirs, Ct as macosBundleExecutable, D as isVaultAccountId, Dt as workbuddyAppExecutableCandidates, E as isEncryptedCredentialError, Et as readAtRestKey, F as WorkBuddyUpstreamClient, G as parseCreditMultiplier, H as knownRegionOf, I as classifyUpstreamError, J as perturbChannelFingerprints, K as parseReasoning, L as declaredTools, M as CREDENTIAL_REJECTED_CODE, N as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, O as legacyWorkbuddyOwnAuthPath, P as WorkBuddyCredentialRejectedError, Q as busyDisplaySuffix, R as forceNeutralHead, S as defaultDesktopAuthCandidates, St as isWorkbuddyBundle, T as hasEncryptedCredentialFields, Tt as openEncryptedField, U as modelIdOfBody, V as isFirstMessageGateBody, W as openingEvidenceOfBody, X as regionOf, Y as prepareChatBody, Z as attachBusyLevels, _ as WORKBUDDY_AUTH_FILENAME, _t as deriveAtRestKeyId, a as readHostHeartbeat, at as withEffectiveOff, b as WorkBuddyEncryptedCredentialError, c as WORKBUDDY_CONNECT_VERSION, ct as pickImageModel, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as NATIVE_MODALITY_BY_MODEL_ID, et as defaultOffModelIds, f as WorkBuddyCatalog, ft as imageDefaultFor, g as ENCRYPTED_CREDENTIAL_CODE, gt as deriveAtRestKey, h as fallbackModelsFor, ht as clearAtRestKeyCache, i as processStartTimeMs, it as recordRefusal, j as workbuddyOwnAuthPath, jt as writeFileAtomic, k as parseWorkBuddyAuth, kt as resolveDshHome, l as piAiRuntimeInfo, lt as parseQueueAnswer, m as deriveCatalog, mt as WORKBUDDY_APP_EXECUTABLE_ENV, n as clearHostHeartbeat, nt as offDefaultFor, o as workbuddyHostHeartbeatPath, ot as DEFAULT_IMAGE_SIZE, p as applyContextBudgets, pt as nativeModalityOf, q as parseUpstreamModel, r as isHeartbeatProcessAlive, rt as offRefused, s as writeHostHeartbeat, st as WorkBuddyImageError, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as lastRefusalFor, u as FALLBACK_WORKBUDDY_MODELS, ut as queueNoticeText, v as WORKBUDDY_AUTH_FILE_ENV, vt as fetchAtRestKeyPayload, w as defaultDesktopAuthPath, wt as macosNestedAppBundles, x as authFileName, xt as isEncryptedFieldWrapper, y as WorkBuddyCredentialStore, yt as findWorkbuddyAppExecutable, z as isConversationIdGateBody } from "./host-heartbeat-D313ENPV.js";
1
+ import { $ as busyDisplaySuffix, A as parseWorkBuddyAuth, At as resolveDshHome, B as isConversationIdGateBody, C as defaultDesktopAuthCandidates, Ct as isWorkbuddyBundle, D as isEncryptedCredentialError, Dt as readAtRestKey, E as hasEncryptedCredentialFields, Et as openEncryptedField, F as WorkBuddyCredentialRejectedError, G as openingEvidenceOfBody, H as isFirstMessageGateBody, I as WorkBuddyUpstreamClient, J as parseUpstreamModel, K as parseCreditMultiplier, L as classifyUpstreamError, M as workbuddyOwnAuthPath, Mt as writeFileAtomic, N as CREDENTIAL_REJECTED_CODE, O as isVaultAccountId, Ot as workbuddyAppExecutableCandidates, P as WORKBUDDY_FALLBACK_SYSTEM_PROMPT, Q as attachBusyLevels, R as declaredTools, S as authFileName, St as isEncryptedFieldWrapper, T as defaultDesktopAuthPath, Tt as macosNestedAppBundles, U as knownRegionOf, V as isCredentialRejectedError, W as modelIdOfBody, X as prepareChatBody, Y as perturbChannelFingerprints, Z as regionOf, _ as ENCRYPTED_CREDENTIAL_CODE, _t as deriveAtRestKey, a as readHostHeartbeat, at as recordRefusal, b as WorkBuddyCredentialStore, bt as findWorkbuddyAppExecutable, c as WORKBUDDY_CONNECT_VERSION, ct as WorkBuddyImageError, d as FALLBACK_WORKBUDDY_MODELS_GLOBAL, dt as queueNoticeText, et as REFUSES_OFF_MODEL_IDS, f as WorkBuddyCatalog, ft as NATIVE_MODALITY_BY_MODEL_ID, g as withoutRouters, gt as clearAtRestKeyCache, h as fallbackModelsFor, ht as WORKBUDDY_APP_EXECUTABLE_ENV, i as processStartTimeMs, it as offRefused, j as workbuddyAccountId, jt as withFileLock, k as legacyWorkbuddyOwnAuthPath, l as piAiRuntimeInfo, lt as pickImageModel, m as deriveCatalog, mt as nativeModalityOf, n as clearHostHeartbeat, nt as lastRefusalFor, o as workbuddyHostHeartbeatPath, ot as withEffectiveOff, p as applyContextBudgets, pt as imageDefaultFor, q as parseReasoning, r as isHeartbeatProcessAlive, rt as offDefaultFor, s as writeHostHeartbeat, st as DEFAULT_IMAGE_SIZE, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, tt as defaultOffModelIds, u as FALLBACK_WORKBUDDY_MODELS, ut as parseQueueAnswer, v as WORKBUDDY_AUTH_FILENAME, vt as deriveAtRestKeyId, w as defaultDesktopAuthDirs, wt as macosBundleExecutable, x as WorkBuddyEncryptedCredentialError, y as WORKBUDDY_AUTH_FILE_ENV, yt as fetchAtRestKeyPayload, z as forceNeutralHead } from "./host-heartbeat-DtMzOwZr.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";
@@ -673,7 +673,10 @@ const NO_REASONING_EFFORTS = { efforts: [{
673
673
  * catalog has six of them (`default`, `glm-5.0`, `glm-4.7`, `glm-4.6`,
674
674
  * `hunyuan-chat`, `hunyuan-image-alpha`), the live global one two
675
675
  * (`default-model`, `deep-model`), and EVERY entry of both static fallback lists
676
- * — the inherited `high` is not a tier that model has. `resolveReasoningLevel`
676
+ * — the inherited `high` is not a tier that model has. (`default-model` is the
677
+ * international `auto` router and is not offered as a row since 5.0.1; the
678
+ * measurement is kept verbatim because the defect it documents was observed
679
+ * against the full roster.) `resolveReasoningLevel`
677
680
  * then throws `UNSUPPORTED_REASONING_EFFORT` before the request is even built,
678
681
  * so the model is unusable locally: no wire call, no upstream error, nothing to
679
682
  * retry. Introduced by v4.11.0, which made the profile default explicit; before
@@ -6889,7 +6892,7 @@ function apply(ctx, config) {
6889
6892
  }));
6890
6893
  const configuredModels = (value, region, liveModels = []) => {
6891
6894
  const state = regionStateOf(value, region);
6892
- const roster = state.lastCatalog?.length ? state.lastCatalog : liveModels.length ? liveModels : fallbackModelsFor(region);
6895
+ const roster = state.lastCatalog?.length ? withoutRouters(state.lastCatalog) : liveModels.length ? liveModels : fallbackModelsFor(region);
6893
6896
  const declared = withImageSelection(deriveCatalog(roster, new Set(state.enabledModelIds ?? []), state.contextBudgets ?? {}), new Set(state.imageModelIds ?? []));
6894
6897
  return withEffectiveOff(declared, new Set(savedOffModelIds(state, declared))).map((model) => model.reasoning === void 0 || lastRefusalFor(model.id) === void 0 ? model : {
6895
6898
  ...model,
@@ -6911,7 +6914,7 @@ function apply(ctx, config) {
6911
6914
  const savedOffModelIds = (state, roster) => state.offModelIds ?? defaultOffModelIds(roster);
6912
6915
  const displayModels = (value, region) => {
6913
6916
  const state = regionStateOf(value, region);
6914
- if (state.lastCatalog?.length) return state.lastCatalog;
6917
+ if (state.lastCatalog?.length) return withoutRouters(state.lastCatalog);
6915
6918
  const live = stacks[region]?.catalog?.current();
6916
6919
  return live?.length ? live : fallbackModelsFor(region);
6917
6920
  };
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "WorkBuddy Model Access",
3
+ "title": "dsh-connect-workbuddy",
4
4
  "description": "Connects locally signed-in WorkBuddy models, CN and international alike, to DSH, reading each model's credit multiplier from the upstream catalog on every refresh, with per-model image input toggles, account switching backed by an auto-failing-over pool, manual daily check-in, and read-only credit overviews shown as compact pills on the card and in the composer."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "WorkBuddy 模型接入",
3
+ "title": "dsh-connect-workbuddy",
4
4
  "description": "将本机登录的 WorkBuddy 模型(国内版与国际版)接入 DSH,每次刷新按上游目录自动读取每个模型的积分倍率,支持逐模型图片输入开关、多账号切换与账号池自动换号,并提供每日签到、卡片与输入框里的胶囊积分读数等只读积分概览。"
5
5
  }
6
6
  }
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.0.0",
5
+ "version": "5.0.1",
6
6
  "author": "dingminhua",
7
7
  "copyright": "Copyright (c) 2026 LaoDing",
8
8
  "license": "MIT",