dsh-workbuddy-bridge 0.3.1 → 0.5.0

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/NOTICE CHANGED
@@ -12,4 +12,4 @@
12
12
  ## 本仓维护者
13
13
 
14
14
  - Copyright (c) 2026 zlZayn
15
- - 仓库:<https://github.com/zlZayn/dsh-workbuddy-bridge>
15
+ - 仓库:<https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge>
package/README.md CHANGED
@@ -2,23 +2,25 @@
2
2
  <img src="icon.svg" alt="dsh-workbuddy-bridge" width="80" height="80">
3
3
  </p>
4
4
 
5
- <p align="center">
6
- <h1 align="center">dsh-workbuddy-bridge</h1>
7
- </p>
5
+ <h1 align="center">dsh-workbuddy-bridge</h1>
8
6
 
9
7
  <div align="center">
10
8
  <p><strong>把 WorkBuddy 桌面 App 的模型接进 DSH 对话窗口</strong></p>
11
9
  <p><em>WorkBuddy desktop models in DeepSeek Harness</em></p>
12
10
 
13
11
  <p>
12
+ <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat&logo=deepseek&logoColor=white" alt="由 dsh 驱动"></a>
13
+ </p>
14
+
15
+ <p>
16
+ <a href="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/actions/workflows/ci.yml"><img src="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
14
17
  <a href="https://www.npmjs.com/package/dsh-workbuddy-bridge"><img src="https://img.shields.io/npm/v/dsh-workbuddy-bridge?style=flat" alt="npm"></a>
15
- <a href="https://github.com/zlZayn/dsh-workbuddy-bridge/actions/workflows/ci.yml"><img src="https://github.com/zlZayn/dsh-workbuddy-bridge/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
16
- <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DeepSeek%20Harness-Plugin-4176E6?style=flat" alt="DeepSeek Harness Plugin"></a>
17
- <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT 许可证"></a>
18
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat" alt="MIT 许可证"></a>
19
+ <a href="https://www.npmjs.com/package/dsh-workbuddy-bridge"><img src="https://img.shields.io/badge/SLSA-Build%20L2-green?logo=slsa&style=flat" alt="SLSA Build L2"></a>
18
20
  </p>
19
21
 
20
22
  <p>
21
- 各版本改动 → <a href="https://github.com/zlZayn/dsh-workbuddy-bridge/releases">Releases</a>(与 npm 版本一一对应)
23
+ 各版本改动 → <a href="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/releases">Releases</a>(与 npm 版本一一对应)
22
24
  </p>
23
25
 
24
26
  <p>
@@ -38,7 +40,7 @@
38
40
  <p align="center">
39
41
  <img src="assets/model-picker.png" alt="WorkBuddy 模型出现在 DSH 模型选择器中" width="300">
40
42
  <br>
41
- <em>国内版在模型选择器中自成一组,模型名后直接跟积分倍率与促销徽章;装国际版则出现「WorkBuddy AI」组。</em>
43
+ <em>中文界面截图:国内版在模型选择器中自成一组,模型名后直接跟积分倍率与促销徽章;装国际版则出现「WorkBuddy AI」组。</em>
42
44
  </p>
43
45
 
44
46
  ## 能力
@@ -46,8 +48,10 @@
46
48
  - **开箱即用** —— 装完启用,模型分组自己出现。
47
49
  - **两版隔离** —— 国内版与国际版各自分组,模型、账号、积分互不混用,只跟随各自 App 的登录状态。
48
50
  - **推理档位** —— 上游声明了档位的模型直接可选;没声明的可在插件页**手动检测**。
49
- 灯泡表示「这个模型能切档位」,不是「检测过」——已声明的模型也显示灯泡,只是不需要检测。
50
- - **积分可见** —— 每条回复下方显示本次消耗,模型选择器旁常驻剩余积分。观测不到就不显示。
51
+ - **推理档位** —— 上游声明了档位的模型直接可选;没声明的可在插件页**手动检测**。
52
+ 灯泡表示能切档位;上游已声明的模型也亮着,只是不需要检测。
53
+ 能切档位的模型会带一个**默认档位**(上游声明了就按声明,否则取 `high`),因此选择器里不再出现「Default」这一项。
54
+ - **积分可见** —— 每条回复下方显示本次消耗,模型选择器旁常驻剩余积分。拿不到数字时不显示。
51
55
  - **模型显隐** —— 卡片「模型」标签里勾选哪些模型出现在选择器里,按登录账号分别保存。
52
56
  - **图片输入** —— 多数模型可直接粘贴或拖入图片;纯文字模型会明确说明。
53
57
  - **三种界面** —— Web / Desktop / TUI;仅 TUI 无手动检测。
@@ -55,13 +59,13 @@
55
59
  <p align="center">
56
60
  <img src="assets/composer-row.png" alt="输入区那一行:币图标与余额、推理档位灯泡、模型名,从左到右一行" width="560">
57
61
  <br>
58
- <em>对话窗口里每天看到的样子:币图标与余额、推理档位灯泡,再往右是模型名——都排在同一行。</em>
62
+ <em>中文界面截图:对话窗口里每天看到的样子:币图标与余额、推理档位灯泡,再往右是模型名——都排在同一行。</em>
59
63
  </p>
60
64
 
61
65
  <p align="center">
62
66
  <img src="assets/plugin-page.png" alt="插件详情页:配置表单与两张状态卡片" width="480">
63
67
  <br>
64
- <em>插件页:上面是配置表单,下面两张卡片各跟随自己 App 的登录状态。</em>
68
+ <em>中文界面截图:插件页:上面是配置表单,下面两张卡片各跟随自己 App 的登录状态。</em>
65
69
  </p>
66
70
 
67
71
  ## 安装
@@ -73,18 +77,21 @@ dsh plugin --profile web add dsh-workbuddy-bridge
73
77
  dsh web
74
78
  ```
75
79
 
76
- Desktop 与 TUI 把 `--profile web` 换成 `desktop` / `dsh-tui`(TUI 需终端插件
77
- `@deepseek-harness-tui/dsh-tui` ≥ `0.10.0-beta.5`,详见其文档)。
80
+ Desktop 与 TUI 把 `--profile web` 换成 `desktop` / `dsh-tui`(TUI 需较新版本的终端插件
81
+ `@deepseek-harness-tui/dsh-tui`,版本要求见其文档)。
78
82
 
79
83
  也可以从源码安装:
80
84
 
81
85
  ```sh
82
- git clone https://github.com/zlZayn/dsh-workbuddy-bridge.git
86
+ git clone https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge.git
83
87
  cd dsh-workbuddy-bridge
84
88
  pnpm install && pnpm build
85
89
  dsh plugin --profile web add "$PWD"
86
90
  ```
87
91
 
92
+ ⚠️ 上面那步 `pnpm install` 不能省:按路径挂载时依赖要由仓库自己装好(见[本地开发装法](docs/PUBLISHING.md)),
93
+ 漏装则插件显示「未运行」(`ERR_MODULE_NOT_FOUND`)。
94
+
88
95
  ## 插件页
89
96
 
90
97
  侧边栏 **插件(Plugins)** → 已安装 → 点 **DSH WorkBuddy Bridge**。配置区在描述下面,保存即生效:
@@ -95,19 +102,19 @@ dsh plugin --profile web add "$PWD"
95
102
  <p align="center">
96
103
  <img src="assets/models-tab.png" alt="模型标签:显隐、窗口、倍率" width="480">
97
104
  <br>
98
- <em>「模型」标签:勾选哪些模型出现在选择器里,同时看窗口与倍率。</em>
105
+ <em>中文界面截图:「模型」标签:勾选哪些模型出现在选择器里,同时看窗口与倍率。</em>
99
106
  </p>
100
107
 
101
108
  <p align="center">
102
109
  <img src="assets/detection-tab.png" alt="检测标签:每个推理模型一行" width="300">
103
110
  <img src="assets/credits-tab.png" alt="积分标签:总余额与各套餐余量条" width="300">
104
111
  <br>
105
- <em>左「检测」:每个推理模型一行,点按钮确认它接受哪些推理档位;右「积分」:总余额,下面是各套餐的余量条。</em>
112
+ <em>中文界面截图:左「检测」:每个推理模型一行,点按钮确认它接受哪些推理档位;右「积分」:总余额,下面是各套餐的余量条。</em>
106
113
  </p>
107
114
 
108
115
  ## 为什么是手动检测
109
116
 
110
- 上游不保证档位信息可靠——实测有的模型**接受** `reasoning_effort` 却忽略未知值。
117
+ WorkBuddy 接口不保证档位信息可靠——有的模型**接受** `reasoning_effort` 却忽略未知值。
111
118
  所以插件不猜:按需对单个模型发少量真实请求确认;结果只表示**上游当前接受该档位**,
112
119
  不承诺它改变推理行为。已声明档位的模型跳过这一步,直接用声明值。
113
120
  机制与取舍见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
@@ -115,12 +122,12 @@ dsh plugin --profile web add "$PWD"
115
122
  <p align="center">
116
123
  <img src="assets/reasoning-levels-declared.png" alt="推理档位控件:上游已声明档位、检测按钮禁用" width="300">
117
124
  <br>
118
- <em>已声明档位的模型:灯泡点亮、档位直接可选,检测按钮禁用。</em>
125
+ <em>中文界面截图:已声明档位的模型:灯泡点亮、档位直接可选,检测按钮禁用。</em>
119
126
  </p>
120
127
 
121
128
  ## 兼容与限制
122
129
 
123
- 无凭据的分组不显示。宿主版本下限、平台支持、目录来源与已知缺口
130
+ 无凭据的分组不显示。宿主版本下限、平台支持、模型清单从哪来与已知缺口
124
131
  → [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md)。
125
132
 
126
133
  设计取舍 → [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md);发版流程 → [docs/PUBLISHING.md](docs/PUBLISHING.md);
@@ -128,7 +135,7 @@ dsh plugin --profile web add "$PWD"
128
135
 
129
136
  ## 免责声明
130
137
 
131
- - 本项目**仅供个人学习和研究使用**,仅驱动使用者自己的 WorkBuddy 账号在本机调用,请勿用于商业用途或超出个人合理使用的场景。
138
+ - 本项目**仅供个人学习和研究使用**,仅驱动使用者自己的 WorkBuddy 账号在本机调用,请勿用于商业用途。
132
139
  - 使用者需遵守 WorkBuddy 的服务条款;因使用本项目产生的任何后果,由使用者自行承担。
133
140
  - 本项目与腾讯、WorkBuddy、DeepSeek 均无关联,未获其授权或认可;文中出现的名称仅用于描述兼容关系,其商标权利归各自所有。
134
141
 
@@ -143,4 +150,4 @@ dsh plugin --profile web add "$PWD"
143
150
 
144
151
  ## 贡献
145
152
 
146
- 流程与本地钩子 → [CONTRIBUTING.md](CONTRIBUTING.md)。缺陷与需求走仓库 Issues。
153
+ 缺陷与需求走仓库 Issues。
package/README_en.md CHANGED
@@ -2,22 +2,25 @@
2
2
  <img src="icon.svg" alt="dsh-workbuddy-bridge" width="80" height="80">
3
3
  </p>
4
4
 
5
- <p align="center">
6
- <h1 align="center">dsh-workbuddy-bridge</h1>
7
- </p>
5
+ <h1 align="center">dsh-workbuddy-bridge</h1>
8
6
 
9
7
  <div align="center">
10
8
  <p><strong>WorkBuddy desktop models in DeepSeek Harness</strong></p>
9
+ <p><em>把 WorkBuddy 桌面 App 的模型接进 DSH 对话窗口</em></p>
10
+
11
+ <p>
12
+ <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat&logo=deepseek&logoColor=white" alt="powered by dsh"></a>
13
+ </p>
11
14
 
12
15
  <p>
16
+ <a href="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/actions/workflows/ci.yml"><img src="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
13
17
  <a href="https://www.npmjs.com/package/dsh-workbuddy-bridge"><img src="https://img.shields.io/npm/v/dsh-workbuddy-bridge?style=flat" alt="npm"></a>
14
- <a href="https://github.com/zlZayn/dsh-workbuddy-bridge/actions/workflows/ci.yml"><img src="https://github.com/zlZayn/dsh-workbuddy-bridge/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
15
- <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DeepSeek%20Harness-Plugin-4176E6?style=flat" alt="DeepSeek Harness Plugin"></a>
16
- <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="MIT License"></a>
18
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat" alt="MIT License"></a>
19
+ <a href="https://www.npmjs.com/package/dsh-workbuddy-bridge"><img src="https://img.shields.io/badge/SLSA-Build%20L2-green?logo=slsa&style=flat" alt="SLSA Build L2"></a>
17
20
  </p>
18
21
 
19
22
  <p>
20
- Per-version changes → <a href="https://github.com/zlZayn/dsh-workbuddy-bridge/releases">Releases</a> (one-to-one with npm versions)
23
+ Per-version changes → <a href="https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/releases">Releases</a> (one-to-one with npm versions)
21
24
  </p>
22
25
 
23
26
  <p>
@@ -29,8 +32,7 @@
29
32
 
30
33
  > [!NOTE]
31
34
  > **Credentials never leave this machine**: sign-in state is read from the WorkBuddy desktop app's own
32
- > credential file and kept as one encrypted local copy; the browser half never sees a token, and none
33
- > ever appears in the UI.
35
+ > credential file and kept as one encrypted local copy; the browser half never sees a token, and none ever appears in the UI.
34
36
 
35
37
  The CN **WorkBuddy** and the international **WorkBuddy AI** coexist: install one app and its model group
36
38
  appears; install both and the two groups sit side by side, each with its own account and credit. The
@@ -39,7 +41,7 @@ models appear directly in the DSH model picker — no separate provider to confi
39
41
  <p align="center">
40
42
  <img src="assets/model-picker.png" alt="WorkBuddy models in the DSH model picker" width="300">
41
43
  <br>
42
- <em>The CN version forms its own group in the model picker, each model carrying its credit rate and promo
44
+ <em>Chinese UI screenshot: The CN version forms its own group in the model picker, each model carrying its credit rate and promo
43
45
  badge; installing the international version adds a "WorkBuddy AI" group the same way.</em>
44
46
  </p>
45
47
 
@@ -49,10 +51,11 @@ models appear directly in the DSH model picker — no separate provider to confi
49
51
  - **Two isolated versions** — "WorkBuddy" (CN) and "WorkBuddy AI" (international) each form their own group;
50
52
  models, accounts and credit never mix, and each follows only its own app's sign-in.
51
53
  - **Reasoning levels** — models that declare levels expose them directly; the rest can be **checked manually**
52
- on the plugin page. The bulb means "this model can switch levels", not "it has been detected" — a declared
53
- model shows the bulb too, it just has nothing to detect.
54
+ on the plugin page. The bulb means "this model can switch levels"; a declared model shows it too, it just
55
+ has nothing to detect. A switchable model also carries a **default level** (the upstream's own declaration
56
+ when it has one, otherwise `high`), so the picker no longer offers "Default".
54
57
  - **Credit in view** — each reply shows what it consumed, and remaining credit sits beside the model picker.
55
- Nothing renders when a figure cannot be observed.
58
+ Nothing renders when a figure is unavailable.
56
59
  - **Model visibility** — tick which models appear in the picker on the card's "Models" tab, saved per
57
60
  signed-in account.
58
61
  - **Image input** — most models accept pasted or dropped images; text-only models say so explicitly.
@@ -61,14 +64,14 @@ models appear directly in the DSH model picker — no separate provider to confi
61
64
  <p align="center">
62
65
  <img src="assets/composer-row.png" alt="The composer row: the coin and balance, the reasoning-level bulb and the model name in one line" width="560">
63
66
  <br>
64
- <em>The everyday view in the conversation window: the coin and its balance, the reasoning-level bulb, then the
67
+ <em>Chinese UI screenshot: The everyday view in the conversation window: the coin and its balance, the reasoning-level bulb, then the
65
68
  model name — one row, left to right.</em>
66
69
  </p>
67
70
 
68
71
  <p align="center">
69
72
  <img src="assets/plugin-page.png" alt="Plugin detail page: the configuration form and the two status cards" width="480">
70
73
  <br>
71
- <em>The plugin page: the configuration form on top, two cards below, each following its own app's sign-in.</em>
74
+ <em>Chinese UI screenshot: The plugin page: the configuration form on top, two cards below, each following its own app's sign-in.</em>
72
75
  </p>
73
76
 
74
77
  ## Install
@@ -81,18 +84,22 @@ dsh plugin --profile web add dsh-workbuddy-bridge
81
84
  dsh web
82
85
  ```
83
86
 
84
- For Desktop and TUI swap `--profile web` for `desktop` / `dsh-tui` (the TUI needs the terminal plugin
85
- `@deepseek-harness-tui/dsh-tui` ≥ `0.10.0-beta.5` — see its docs).
87
+ For Desktop and TUI swap `--profile web` for `desktop` / `dsh-tui` (the TUI needs a recent version of
88
+ the terminal plugin `@deepseek-harness-tui/dsh-tui` — see its docs).
86
89
 
87
90
  Or from source:
88
91
 
89
92
  ```sh
90
- git clone https://github.com/zlZayn/dsh-workbuddy-bridge.git
93
+ git clone https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge.git
91
94
  cd dsh-workbuddy-bridge
92
95
  pnpm install && pnpm build
93
96
  dsh plugin --profile web add "$PWD"
94
97
  ```
95
98
 
99
+ ⚠️ The `pnpm install` above is not optional: under a path install the repository has to supply
100
+ its own dependencies (see [local development setup](docs/PUBLISHING.md)) — skip it and the
101
+ plugin shows as "not running" (`ERR_MODULE_NOT_FOUND`).
102
+
96
103
  ## The plugin page
97
104
 
98
105
  Sidebar **Plugins** → installed → click **DSH WorkBuddy Bridge**. The configuration area sits right under
@@ -104,20 +111,20 @@ the description and takes effect on save:
104
111
  <p align="center">
105
112
  <img src="assets/models-tab.png" alt="Models tab: visibility, windows, rates" width="480">
106
113
  <br>
107
- <em>The Models tab: tick which models appear in the picker, and read each one's window and rate.</em>
114
+ <em>Chinese UI screenshot: The Models tab: tick which models appear in the picker, and read each one's window and rate.</em>
108
115
  </p>
109
116
 
110
117
  <p align="center">
111
118
  <img src="assets/detection-tab.png" alt="Detection tab: one row per reasoning model" width="300">
112
119
  <img src="assets/credits-tab.png" alt="Credits tab: the total balance and per-package allowances" width="300">
113
120
  <br>
114
- <em>Left, "Detection": one row per reasoning model — press the button to confirm which reasoning levels it
121
+ <em>Chinese UI screenshots: Left, "Detection": one row per reasoning model — press the button to confirm which reasoning levels it
115
122
  accepts. Right, "Credits": the total balance, each package's allowance as a row below.</em>
116
123
  </p>
117
124
 
118
125
  ## Why detection is manual
119
126
 
120
- The upstream does not guarantee that level information is reliable — some models were observed to
127
+ The WorkBuddy API does not guarantee that level information is reliable — some models were observed to
121
128
  **accept** `reasoning_effort` while silently ignoring unknown values. So the plugin does not guess: it
122
129
  confirms one model at a time with a few real requests on demand, and a result only means **the upstream
123
130
  currently accepts that level** — it does not promise any change in behaviour. Models that declare their
@@ -127,13 +134,13 @@ Mechanism and trade-offs in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
127
134
  <p align="center">
128
135
  <img src="assets/reasoning-levels-declared.png" alt="The reasoning-level control: levels declared upstream, detect disabled" width="300">
129
136
  <br>
130
- <em>A model with declared levels: the bulb is lit, the levels are directly selectable, and detection is disabled.</em>
137
+ <em>Chinese UI screenshot: A model with declared levels: the bulb is lit, the levels are directly selectable, and detection is disabled.</em>
131
138
  </p>
132
139
 
133
140
  ## Compatibility and limits
134
141
 
135
- A group without credentials does not appear. The host version floor, platform support, catalog sources and
136
- known gaps
142
+ A group without credentials does not appear. The host version floor, platform support, where the model
143
+ catalog comes from and known gaps
137
144
  → [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
138
145
 
139
146
  Design notes → [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md); release process → [docs/PUBLISHING.md](docs/PUBLISHING.md);
@@ -142,7 +149,7 @@ maintainer map → [AGENTS.md](AGENTS.md).
142
149
  ## Disclaimer
143
150
 
144
151
  - This project is **for personal study and research only**; it drives your own WorkBuddy account on your own
145
- machine. Do not use it commercially or beyond reasonable personal use.
152
+ machine. Do not use it commercially.
146
153
  - You must comply with WorkBuddy's terms of service; any consequences are your own responsibility.
147
154
  - Not affiliated with, nor endorsed by, Tencent, WorkBuddy, or DeepSeek; names appear only to describe
148
155
  compatibility, and trademarks belong to their owners.
@@ -158,4 +165,4 @@ maintainer map → [AGENTS.md](AGENTS.md).
158
165
 
159
166
  ## Contributing
160
167
 
161
- Process and local hooks → [CONTRIBUTING.md](CONTRIBUTING.md). Bugs and requests go to the repo Issues.
168
+ Bugs and requests go to the repo Issues.
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { C as variantFor, D as WorkBuddyUpstreamClient, S as WORKBUDDY_VARIANTS, Y as resolveAppVersion, a as readHostHeartbeat, c as WORKBUDDY_BRIDGE_VERSION, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, x as CN_VARIANT } from "./heartbeat-DihKwwFX.js";
2
+ import { C as variantFor, D as WorkBuddyUpstreamClient, S as WORKBUDDY_VARIANTS, Y as resolveAppVersion, a as readHostHeartbeat, c as WORKBUDDY_BRIDGE_VERSION, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, x as CN_VARIANT } from "./heartbeat-BpW2mSgI.js";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/cli/bin.ts
@@ -3688,7 +3688,7 @@ var WorkBuddyCatalog = class {
3688
3688
  };
3689
3689
  //#endregion
3690
3690
  //#region src/version.ts
3691
- const WORKBUDDY_BRIDGE_VERSION = "0.3.1";
3691
+ const WORKBUDDY_BRIDGE_VERSION = "0.5.0";
3692
3692
  //#endregion
3693
3693
  //#region src/web/heartbeat.ts
3694
3694
  /**
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as parseModelCatalog, B as chatUserAgent, C as variantFor, D as WorkBuddyUpstreamClient, F as probeModel, G as WORKBUDDY_APP_VERSION_FILENAME, H as readCliVersion, I as randomSentinel, J as readBundleVersion, K as appUserAgent, L as classifyUpstreamError, M as prepareInternationalChatBody, N as regionOf, O as modelWithCurrentPromotion, P as PROBE_EFFORT_CANDIDATES, R as CN_APP_VERSION_FILENAME, S as WORKBUDDY_VARIANTS, U as resolveChatIdentity, V as fallbackChatIdentity, W as validCliVersion, X as validAppVersion, Y as resolveAppVersion, _ as desktopAuthCandidatesFor, a as readHostHeartbeat, b as AI_VARIANT, c as WORKBUDDY_BRIDGE_VERSION, d as WorkBuddyCatalog, f as WORKBUDDY_AUTH_FILENAME, g as defaultDesktopAuthPath, h as defaultDesktopAuthCandidates, i as processStartTimeMs, j as prepareChatBody, k as normalizeCredits, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as WORKBUDDY_AUTH_FILE_ENV, q as installedAppVersion, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS, v as parseWorkBuddyAuth, w as WORKBUDDY_CREDIT_PATH, x as CN_VARIANT, y as workbuddyOwnAuthPath, z as FALLBACK_CN_APP_VERSION } from "./heartbeat-DihKwwFX.js";
1
+ import { A as parseModelCatalog, B as chatUserAgent, C as variantFor, D as WorkBuddyUpstreamClient, F as probeModel, G as WORKBUDDY_APP_VERSION_FILENAME, H as readCliVersion, I as randomSentinel, J as readBundleVersion, K as appUserAgent, L as classifyUpstreamError, M as prepareInternationalChatBody, N as regionOf, O as modelWithCurrentPromotion, P as PROBE_EFFORT_CANDIDATES, R as CN_APP_VERSION_FILENAME, S as WORKBUDDY_VARIANTS, U as resolveChatIdentity, V as fallbackChatIdentity, W as validCliVersion, X as validAppVersion, Y as resolveAppVersion, _ as desktopAuthCandidatesFor, a as readHostHeartbeat, b as AI_VARIANT, c as WORKBUDDY_BRIDGE_VERSION, d as WorkBuddyCatalog, f as WORKBUDDY_AUTH_FILENAME, g as defaultDesktopAuthPath, h as defaultDesktopAuthCandidates, i as processStartTimeMs, j as prepareChatBody, k as normalizeCredits, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as WORKBUDDY_AUTH_FILE_ENV, q as installedAppVersion, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS, v as parseWorkBuddyAuth, w as WORKBUDDY_CREDIT_PATH, x as CN_VARIANT, y as workbuddyOwnAuthPath, z as FALLBACK_CN_APP_VERSION } from "./heartbeat-BpW2mSgI.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
@@ -6,7 +6,7 @@ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
6
6
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
7
7
  import { createProvider } from "@earendil-works/pi-ai";
8
8
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
9
- import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
9
+ import { ReasoningEffortId, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
10
10
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
11
11
  import { createServer } from "node:http";
12
12
  import { Readable } from "node:stream";
@@ -303,6 +303,39 @@ function resolveEfforts(info, observed) {
303
303
  canDisable: false
304
304
  };
305
305
  }
306
+ /**
307
+ * The level a switchable model defaults to, or undefined when it has no switch.
308
+ *
309
+ * **Why this is not simply the upstream's `defaultEffort`.** The host's model
310
+ * picker adds a "Default" item — "follow whatever the provider does" — exactly
311
+ * when a model reports no default; reporting one is what removes that item. The
312
+ * upstream's own answer is used whenever it names a level the offered set
313
+ * contains, because that is the only defensible source: it is what the provider
314
+ * itself would do with no `reasoning_effort` on the wire.
315
+ *
316
+ * `high` backs it up otherwise, and the asymmetry is deliberate. A row can
317
+ * declare a set while naming no default, and a stale `defaultEffort` can name a
318
+ * level a narrower declared set no longer offers (`defaultEffort: 'low'` beside
319
+ * `supportedEfforts: ['high']`). Both leave the level unpinned, and the chosen
320
+ * behaviour is to pin it anyway: a wrong-but-explicit level is a one-click fix
321
+ * in the picker, while a silent "follow the provider" is not even visible.
322
+ *
323
+ * The fallback therefore never strays outside the offered set. A model whose set
324
+ * excludes `high` falls back to its first offered level — naming a level the
325
+ * model cannot accept would make every request that omits an effort fail
326
+ * `UNSUPPORTED_REASONING_EFFORT`, which is the failure the `reasoning-merge`
327
+ * specs exist to prevent.
328
+ *
329
+ * @param info - the catalog row, whose `reasoning.defaultEffort` may name one.
330
+ * @param offered - the offered levels, in order; the answer is always a member.
331
+ * @returns a member of `offered`, or undefined when nothing is offered.
332
+ */
333
+ function resolveDefaultEffort(info, offered) {
334
+ if (offered.length === 0) return void 0;
335
+ const declared = info.reasoning?.defaultEffort;
336
+ if (declared !== void 0 && offered.includes(declared)) return declared;
337
+ return offered.includes("high") ? "high" : offered[0];
338
+ }
306
339
  //#endregion
307
340
  //#region src/llm/adapter.ts
308
341
  /**
@@ -498,7 +531,7 @@ function createWorkBuddyAdapter(options) {
498
531
  };
499
532
  let profiles = /* @__PURE__ */ new Map([[providerId, profile]]);
500
533
  return {
501
- adapter: new WorkBuddyPiAiAdapter(catalog, hidden ?? (() => []), {
534
+ adapter: new WorkBuddyPiAiAdapter(catalog, hidden ?? (() => []), observe ?? (() => void 0), {
502
535
  profiles: () => profiles,
503
536
  auth: INERT_AUTH,
504
537
  resolveApiKey: async () => shim.token(),
@@ -520,6 +553,14 @@ function createWorkBuddyAdapter(options) {
520
553
  * capability resolution, and effort mapping stay exactly as `dsh-llm-pi-ai`
521
554
  * implements them.
522
555
  *
556
+ * `resolveModel()` and `prepareCall()` additionally answer the *default*
557
+ * thinking level. `dsh-llm-pi-ai` can only report a default from a whole-route
558
+ * profile field (`profile.reasoning`), which cannot express one level per
559
+ * model, so the per-model answer has to be attached here. Two call sites are
560
+ * required rather than one: the picker reads `resolveModel()`, while the
561
+ * request path reads `prepareCall()`, and a default present on only one of them
562
+ * would show the user a level the wire does not actually send.
563
+ *
523
564
  * A model missing from the catalog (an id the shim would serve but the last
524
565
  * upstream refresh did not list) falls through with its name untouched rather
525
566
  * than being dropped: catalog membership is advisory, and the seam tolerates
@@ -528,15 +569,48 @@ function createWorkBuddyAdapter(options) {
528
569
  var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
529
570
  catalog;
530
571
  hidden;
531
- constructor(catalog, hidden, options) {
572
+ observe;
573
+ constructor(catalog, hidden, observe, options) {
532
574
  super(options);
533
575
  this.catalog = catalog;
534
576
  this.hidden = hidden;
577
+ this.observe = observe;
535
578
  }
536
579
  /** Catalog entry for one model id, or undefined when the catalog omits it. */
537
580
  infoFor(model) {
538
581
  return this.catalog.current().find((entry) => entry.id === model);
539
582
  }
583
+ /**
584
+ * The default level for one model, or undefined when it has no switch.
585
+ *
586
+ * Derived from the *same* switch the picker is built from — `resolveEfforts`
587
+ * with the same observation the adapter passes to `reasoningFields` — so the
588
+ * default can never name a level this model does not offer.
589
+ */
590
+ defaultEffortFor(model) {
591
+ const info = this.infoFor(model);
592
+ if (info === void 0) return void 0;
593
+ const effort = resolveDefaultEffort(info, resolveEfforts(info, this.observe(model)).efforts);
594
+ return effort === void 0 ? void 0 : ReasoningEffortId(effort);
595
+ }
596
+ /**
597
+ * Attach the per-model default to an already-resolved model answer.
598
+ *
599
+ * Written as a rewrite of `reasoning` rather than a spread of the whole
600
+ * answer: `reasoning.defaultEffort` must stay absent — not `undefined` — on a
601
+ * model with no switch, because the host reads absence as "offer Default".
602
+ */
603
+ withDefaultEffort(resolved) {
604
+ const effort = this.defaultEffortFor(resolved.id);
605
+ if (effort === void 0 || resolved.reasoning === void 0) return resolved;
606
+ return {
607
+ ...resolved,
608
+ reasoning: {
609
+ ...resolved.reasoning,
610
+ defaultEffort: effort
611
+ }
612
+ };
613
+ }
540
614
  async listModels(provider) {
541
615
  const models = await super.listModels(provider);
542
616
  const hidden = new Set(this.hidden());
@@ -553,11 +627,18 @@ var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
553
627
  async resolveModel(provider, model, signal) {
554
628
  const resolved = await super.resolveModel(provider, model, signal);
555
629
  const info = this.infoFor(model);
556
- if (info === void 0) return resolved;
557
- return {
630
+ const named = info === void 0 ? resolved : {
558
631
  ...resolved,
559
632
  name: withCatalogDisplay(resolved.name, info)
560
633
  };
634
+ return this.withDefaultEffort(named);
635
+ }
636
+ async prepareCall(provider, model, signal) {
637
+ const prepared = await super.prepareCall(provider, model, signal);
638
+ return {
639
+ ...prepared,
640
+ model: this.withDefaultEffort(prepared.model)
641
+ };
561
642
  }
562
643
  };
563
644
  //#endregion
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-workbuddy-bridge",
3
3
  "displayName": "DSH WorkBuddy Bridge",
4
4
  "description": "将 WorkBuddy 桌面 App 包含的模型自动接入 DeepSeek Harness — bring WorkBuddy desktop app models into DeepSeek Harness with zero configuration.",
5
- "version": "0.3.1",
5
+ "version": "0.5.0",
6
6
  "author": "zlZayn",
7
7
  "keywords": [
8
8
  "dsh",
@@ -16,7 +16,7 @@
16
16
  "type": "module",
17
17
  "engines": {
18
18
  "node": "^22.19.0 || >=24.0.0",
19
- "dsh": ">=0.1.7-rc.2"
19
+ "dsh": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1"
20
20
  },
21
21
  "icon": "./icon.svg",
22
22
  "main": "lib/index.js",
@@ -50,10 +50,10 @@
50
50
  "license": "MIT",
51
51
  "repository": {
52
52
  "type": "git",
53
- "url": "git+https://github.com/zlZayn/dsh-workbuddy-bridge.git"
53
+ "url": "git+https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge.git"
54
54
  },
55
- "homepage": "https://github.com/zlZayn/dsh-workbuddy-bridge#readme",
56
- "bugs": "https://github.com/zlZayn/dsh-workbuddy-bridge/issues",
55
+ "homepage": "https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge#readme",
56
+ "bugs": "https://github.com/dsh-plugin-lab/dsh-workbuddy-bridge/issues",
57
57
  "dsh": {
58
58
  "bundle": {
59
59
  "patch": "./cordis.patch.yml"
@@ -75,52 +75,52 @@
75
75
  },
76
76
  "peerDependencies": {
77
77
  "@deepseek-ai/cordis": "^4.0.4",
78
- "@deepseek-ai/dsh-api-remotes": ">=0.1.7-rc.2",
79
- "@deepseek-ai/dsh-atomic-write": ">=0.1.7-rc.2",
80
- "@deepseek-ai/dsh-attachment": ">=0.1.7-rc.2",
81
- "@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.2",
82
- "@deepseek-ai/dsh-client-store": ">=0.1.7-rc.2",
83
- "@deepseek-ai/dsh-client-ui-chat": ">=0.1.7-rc.2",
84
- "@deepseek-ai/dsh-client-ui-plugin-manager": ">=0.1.7-rc.2",
85
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.2",
86
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-rc.2",
87
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.2",
88
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.2",
89
- "@deepseek-ai/dsh-home-paths": ">=0.1.7-rc.2",
90
- "@deepseek-ai/dsh-host-webserver": ">=0.1.7-rc.2",
91
- "@deepseek-ai/dsh-llm": ">=0.1.7-rc.2",
92
- "@deepseek-ai/dsh-llm-pi-ai": ">=0.1.7-rc.2",
93
- "@deepseek-ai/dsh-settings": ">=0.1.7-rc.2",
78
+ "@deepseek-ai/dsh-api-remotes": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
79
+ "@deepseek-ai/dsh-atomic-write": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
80
+ "@deepseek-ai/dsh-attachment": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
81
+ "@deepseek-ai/dsh-client-locale": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
82
+ "@deepseek-ai/dsh-client-store": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
83
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
84
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
85
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
86
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
87
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
88
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
89
+ "@deepseek-ai/dsh-home-paths": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
90
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
91
+ "@deepseek-ai/dsh-llm": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
92
+ "@deepseek-ai/dsh-llm-pi-ai": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
93
+ "@deepseek-ai/dsh-settings": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
94
94
  "@deepseek-ai/schemastery": "^3.18.4",
95
- "@earendil-works/pi-ai": "^0.85.1",
95
+ "@earendil-works/pi-ai": "^1.1.0",
96
96
  "react": "^18.2.0",
97
97
  "react-dom": "^18.2.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@deepseek-ai/cordis": "4.0.4",
101
- "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.2",
102
- "@deepseek-ai/dsh-atomic-write": "0.2.0-rc.2",
103
- "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
104
- "@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
105
- "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
106
- "@deepseek-ai/dsh-client-ui-chat": "0.2.0-rc.2",
107
- "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.2",
108
- "@deepseek-ai/dsh-client-ui-model-selection": "0.2.0-rc.2",
109
- "@deepseek-ai/dsh-client-ui-plugin-manager": "0.2.0-rc.2",
110
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
111
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
112
- "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
113
- "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
114
- "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
115
- "@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
116
- "@deepseek-ai/dsh-host-webserver": "0.2.0-rc.2",
117
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
118
- "@deepseek-ai/dsh-llm-pi-ai": "0.2.0-rc.2",
119
- "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
120
- "@deepseek-ai/dsh-util-code-language": "0.2.0-rc.2",
121
- "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
122
- "@deepseek-ai/schemastery": "3.18.4",
123
- "@earendil-works/pi-ai": "^0.87.1",
101
+ "@deepseek-ai/dsh-api-remotes": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
102
+ "@deepseek-ai/dsh-atomic-write": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
103
+ "@deepseek-ai/dsh-attachment": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
104
+ "@deepseek-ai/dsh-client-locale": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
105
+ "@deepseek-ai/dsh-client-store": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
106
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
107
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
108
+ "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
109
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
110
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
111
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
112
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
113
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
114
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
115
+ "@deepseek-ai/dsh-home-paths": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
116
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
117
+ "@deepseek-ai/dsh-llm": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
118
+ "@deepseek-ai/dsh-llm-pi-ai": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
119
+ "@deepseek-ai/dsh-settings": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
120
+ "@deepseek-ai/dsh-util-code-language": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
121
+ "@deepseek-ai/dsh-util-workspace-path": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
122
+ "@deepseek-ai/schemastery": "3.18.5",
123
+ "@earendil-works/pi-ai": "^1.1.0",
124
124
  "@eslint/js": "^10.0.1",
125
125
  "@shikijs/langs": "^4.4.3",
126
126
  "@types/node": "^22.20.0",
@@ -135,7 +135,7 @@
135
135
  "eslint-config-prettier": "^10.1.8",
136
136
  "globals": "^17.12.0",
137
137
  "immer": "^11.1.18",
138
- "katex": "^0.16.47",
138
+ "katex": "^0.18.2",
139
139
  "lightningcss": "^1.33.0",
140
140
  "mdast-util-from-markdown": "^2.0.3",
141
141
  "mdast-util-gfm": "^3.1.0",