dsh-workbuddy-connect 0.7.8 → 0.7.9
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/README.en.md +13 -11
- package/README.md +13 -11
- package/lib/bin.js +1 -1
- package/lib/client.js +22 -3
- package/lib/{host-heartbeat-sjotAEdb.js → host-heartbeat-SJaZ33tR.js} +1 -1
- package/lib/index.js +395 -58
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -13,7 +13,7 @@ Brings the models available in your signed-in WorkBuddy / WorkBuddy AI desktop a
|
|
|
13
13
|
```text
|
|
14
14
|
Please install and verify the DSH (DeepSeek Harness) plugin dsh-workbuddy-connect for me (project: https://github.com/corrinehu/dsh-workbuddy-connect).
|
|
15
15
|
Start by reading the project README and confirming which interface I actually use (Web / Desktop / TUI) and its DSH core version — for the Desktop app, the bundled core, never the standalone CLI's version.
|
|
16
|
-
Then install from the official npm package per the version mapping (0.2.1-rc.2 -> 0.7.
|
|
16
|
+
Then install from the official npm package per the version mapping (0.2.1-rc.2 -> 0.7.9, 0.2.1-rc.1 -> 0.7.7, 0.2.0-rc.2 -> 0.7.6, rc.1 -> 0.7.0, 0.1.5-0.1.7 -> 0.6.5), preferring a locally supported non-market channel; on some Desktop distributions (the official Electron app in particular) the profile is app-managed and rules vary by distribution — check mine first, then explore its compliant install route, but never bypass its validation.
|
|
17
17
|
Ask for my consent and make backups before changing any local files or configuration.
|
|
18
18
|
After installing, verify the WorkBuddy group appears in the model picker and walk me through one short conversation.
|
|
19
19
|
If the Desktop install still fails generation validation (e.g. the @types/retry false positive), report it honestly, reference upstream dataelement/dsh-desktop#688, and suggest switching to the standalone Web / TUI — do not modify dependency packages or bypass the check.
|
|
@@ -23,7 +23,7 @@ Core-to-plugin quick reference (full table in "Compatibility and release history
|
|
|
23
23
|
|
|
24
24
|
| DSH core | Recommended plugin |
|
|
25
25
|
|---|---|
|
|
26
|
-
| 0.2.1-rc.2 | 0.7.
|
|
26
|
+
| 0.2.1-rc.2 | 0.7.9 (npm latest) |
|
|
27
27
|
| 0.2.1-rc.1 | 0.7.7 |
|
|
28
28
|
| 0.2.0-rc.2 | 0.7.6 |
|
|
29
29
|
| 0.2.0-rc.1 | 0.7.0 |
|
|
@@ -33,8 +33,8 @@ Core-to-plugin quick reference (full table in "Compatibility and release history
|
|
|
33
33
|
<summary>Manual install (command line, Web / TUI only)</summary>
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
# Web (pin per your actual core — example: 0.2.1-rc.2 -> 0.7.
|
|
37
|
-
dsh plugin --profile web add dsh-workbuddy-connect@0.7.
|
|
36
|
+
# Web (pin per your actual core — example: 0.2.1-rc.2 -> 0.7.9; or install from source via github:corrinehu/dsh-workbuddy-connect)
|
|
37
|
+
dsh plugin --profile web add dsh-workbuddy-connect@0.7.9
|
|
38
38
|
dsh web
|
|
39
39
|
|
|
40
40
|
# TUI (example for a 0.1.x core; that profile needs pnpm 11, e.g. npx pnpm@11)
|
|
@@ -84,7 +84,7 @@ The same UI works unchanged inside the DSH 0.1.5 settings cards:
|
|
|
84
84
|
|
|
85
85
|

|
|
86
86
|
|
|
87
|
-
The expanded card has three tabs: Status shows the account, token validity, total credit, catalog source, and reasoning-level detection; Context lists each model's context window, and the international version's "use the upstream's largest declared context window" switch lives here (on by default, so DSH sizes context compression to the largest declared window; turn it off to follow the upstream default instead, and the preference persists across restarts); Details
|
|
87
|
+
The expanded card has three tabs: Status shows the account, token validity, total credit, catalog source, and reasoning-level detection; Context lists each model's context window, and the international version's "use the upstream's largest declared context window" switch lives here (on by default, so DSH sizes context compression to the largest declared window; turn it off to follow the upstream default instead, and the preference persists across restarts); Details combines credit packages with the same name and shows model offers. The CN and international versions each get their own card.
|
|
88
88
|
|
|
89
89
|

|
|
90
90
|
|
|
@@ -119,7 +119,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
119
119
|
- **International-version environments not yet covered**: on Windows / WSL / Linux no reliable source for the international app's version has been located yet, so the saved value or the built-in default is used. On macOS, real-shim checks covered complete GPT-family replies, tool calls, and continued turns.
|
|
120
120
|
- **Behaviour change with no credentials**: a version whose app was never signed in — and that left no plugin-owned copy — no longer shows a model group. The CN version used to display a built-in fallback list, but every model on it failed when selected.
|
|
121
121
|
- **The enterprise credit path currently covers the CN product only**: the international enterprise billing interface is unverified, so those accounts still read through the personal endpoint pending measurement. The enterprise branch could not be tested locally (the development machine holds a personal account); it was implemented from the official app's interface contract, and reports from enterprise users are welcome.
|
|
122
|
-
- **Settings entries vary with the DSH version**:
|
|
122
|
+
- **Settings entries vary with the DSH version**: older services use `installSection`; newer ones use SettingsForms. On DSH 0.2.1-rc.2, 0.7.9 restores the international edition's maximum-context switch, enabled by default. Turning it off uses the upstream default window, and the preference survives restarts. The switch appears only once this plugin's writable settings entry is ready; while waiting, the maximum window remains the default.
|
|
123
123
|
- Relies on WorkBuddy client interfaces (not a public API); the plugin may need updates as WorkBuddy changes.
|
|
124
124
|
|
|
125
125
|
## Compatibility and release history
|
|
@@ -128,7 +128,7 @@ The plugin version must match the DSH core — a mismatched combination fails to
|
|
|
128
128
|
|
|
129
129
|
| DSH core | Recommended plugin | Notes |
|
|
130
130
|
|---|---|---|
|
|
131
|
-
| `0.2.1-rc.2` | `0.7.
|
|
131
|
+
| `0.2.1-rc.2` | `0.7.9` | Targets this core only; `0.2.0-rc.2` and `0.2.1-rc.2` differ. Check the core bundled in your Desktop app. |
|
|
132
132
|
| `0.2.1-rc.1` | `0.7.7` | The core moved to `@earendil-works/pi-ai` 1.3.0, so the plugin bundles 1.3.0 too — a leftover 0.87.1 overrides entry must be removed (see the pi-ai note below). |
|
|
133
133
|
| `0.2.0-rc.2` | `0.7.6` | Previous core line; fixes enterprise catalog gaps and international chat requests blocked by the upstream channel check. Avoid 0.7.2 with its registry regression. |
|
|
134
134
|
| `0.2.0-rc.1` | `0.7.0` | 0.7.1 drops rc.1 support; stay on this version. |
|
|
@@ -138,6 +138,7 @@ The plugin version must match the DSH core — a mismatched combination fails to
|
|
|
138
138
|
|
|
139
139
|
Release history (full records at [releases](https://github.com/corrinehu/dsh-workbuddy-connect/releases)):
|
|
140
140
|
|
|
141
|
+
- **0.7.9**: restores the international edition's maximum-context switch on DSH 0.2.1-rc.2, including delayed initialization and persistent settings (PR [#172](https://github.com/corrinehu/dsh-workbuddy-connect/pull/172), by @1Dreamer666); avoids fetch-blocked local ports that could cause international-edition 502 errors (PR [#164](https://github.com/corrinehu/dsh-workbuddy-connect/pull/164), by @Xiong-Ziyi); combines credit packages with the same name to reduce repeated entries.
|
|
141
142
|
- **0.7.8**: adapts to DSH 0.2.1-rc.2; bundles the official atomic-write helper instead of relying on host module exposure; refreshes the China catalog snapshot and distinguishes Hy3 / Hy3-X; improves probe result labels and fixes the credit-refresh placement and tooltip. Thanks to @chenpiyanghappy for PR [#156](https://github.com/corrinehu/dsh-workbuddy-connect/pull/156).
|
|
142
143
|
- **0.7.7**: adapts to the DSH 0.2.1-rc.1 core: peers narrow to 0.2.1-rc.1 and the plugin-bundled `@earendil-works/pi-ai` moves to 1.3.0 with it (a profile-level leftover 0.87.1 overrides entry must be removed, or the plugin fails to load).
|
|
143
144
|
- **0.7.6**: fixes enterprise accounts missing `glm-5.3-flashx` from the model catalog ([#123](https://github.com/corrinehu/dsh-workbuddy-connect/issues/123)), and fixes international chat requests being blocked by the upstream channel check ([#139](https://github.com/corrinehu/dsh-workbuddy-connect/issues/139) / [#142](https://github.com/corrinehu/dsh-workbuddy-connect/issues/142)).
|
|
@@ -149,9 +150,9 @@ Release history (full records at [releases](https://github.com/corrinehu/dsh-wor
|
|
|
149
150
|
- **0.7.0**: adapts to DSH 0.2.0's settings-service rework and drops 0.1.x support.
|
|
150
151
|
- **0.6.5**: final release of the 0.1.x line.
|
|
151
152
|
|
|
152
|
-
Pinned-version install example: `dsh plugin --profile web add dsh-workbuddy-connect@0.7.
|
|
153
|
+
Pinned-version install example: `dsh plugin --profile web add dsh-workbuddy-connect@0.7.9`.
|
|
153
154
|
|
|
154
|
-
**pi-ai versions are per core line and must not be mixed**: the 0.2.1-rc.1 / rc.2 core lines (plugin 0.7.7 / 0.7.8 respectively) moved to `@earendil-works/pi-ai` 1.3.0 (bundled by the plugin), so a profile-level leftover `overrides: {'@earendil-works/pi-ai': '0.87.1'}` **must be removed** or the plugin fails to load; the 0.2.0-rc.2 core line (plugin 0.7.6) still requires `^0.87.1` — on that line, **do not** upgrade the profile-level pi-ai to 1.x, and do not act on third-party plugin managers' "updatable" prompts, which do not check peer constraints ([#86](https://github.com/corrinehu/dsh-workbuddy-connect/issues/86)).
|
|
155
|
+
**pi-ai versions are per core line and must not be mixed**: the 0.2.1-rc.1 / rc.2 core lines (plugin 0.7.7 / 0.7.8–0.7.9 respectively) moved to `@earendil-works/pi-ai` 1.3.0 (bundled by the plugin), so a profile-level leftover `overrides: {'@earendil-works/pi-ai': '0.87.1'}` **must be removed** or the plugin fails to load; the 0.2.0-rc.2 core line (plugin 0.7.6) still requires `^0.87.1` — on that line, **do not** upgrade the profile-level pi-ai to 1.x, and do not act on third-party plugin managers' "updatable" prompts, which do not check peer constraints ([#86](https://github.com/corrinehu/dsh-workbuddy-connect/issues/86)).
|
|
155
156
|
|
|
156
157
|
## Disclaimer
|
|
157
158
|
|
|
@@ -162,10 +163,11 @@ Pinned-version install example: `dsh plugin --profile web add dsh-workbuddy-conn
|
|
|
162
163
|
|
|
163
164
|
## Related Projects / Community Ecosystem
|
|
164
165
|
|
|
165
|
-
- **[
|
|
166
|
+
- **[dsh-buddy-checkin](https://github.com/corrinehu/dsh-buddy-checkin)**: a daily check-in plugin for the WorkBuddy China edition, maintained by this project's author.
|
|
167
|
+
- **[dsh-workbuddy-switcher](https://github.com/ddGuo/dsh-workbuddy-switcher)**: a community-maintained WorkBuddy account management and switching plugin.
|
|
166
168
|
- **[hana-workbuddy-connect](https://github.com/miaoyongxu/hana-workbuddy-connect)**: a community-maintained unofficial adapter project that reuses parts of the `dsh-workbuddy-connect` implementation to bring WorkBuddy / WorkBuddy AI models to HanaAgent.
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
See each project's documentation for supported versions and installation requirements. Except for `dsh-buddy-checkin`, these projects are independently maintained by third parties. Listing them here is not an official endorsement or a stability or compatibility commitment for this plugin's internal interfaces.
|
|
169
171
|
|
|
170
172
|
## Acknowledgements
|
|
171
173
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
```text
|
|
14
14
|
请帮我安装并验证 DSH(DeepSeek Harness)插件 dsh-workbuddy-connect(项目地址:https://github.com/corrinehu/dsh-workbuddy-connect):
|
|
15
15
|
先阅读项目 README,确认我实际使用的界面(Web / Desktop / TUI)与 DSH 内核版本,桌面版以 App 内置核心为准,不要用独立 CLI 的版本代替;
|
|
16
|
-
再按版本对应关系从 npm 正式包安装(0.2.1-rc.2→0.7.
|
|
16
|
+
再按版本对应关系从 npm 正式包安装(0.2.1-rc.2→0.7.9、0.2.1-rc.1→0.7.7、0.2.0-rc.2→0.7.6、rc.1→0.7.0、0.1.5–0.1.7→0.6.5),优先走本机受支持的非插件市场渠道;部分 Desktop 发行版(尤其官方 Electron 版)的 profile 由 App 管理,各发行版规则未必相同,先确认我所用的发行版,再探查其合规的安装方式,但不得绕过其校验;
|
|
17
17
|
任何本地文件或配置改动,先征得我同意并做好备份;
|
|
18
18
|
装完后验证模型选择器里出现 WorkBuddy 分组,并陪我完成一次简短对话;
|
|
19
19
|
若 Desktop 安装仍因 generation 校验失败(如 @types/retry 误报),请如实报告、参照上游 dataelement/dsh-desktop#688,并建议我改用独立的 Web / TUI,不要修改依赖包或绕过校验。
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
| DSH 内核 | 推荐插件版 |
|
|
25
25
|
|---|---|
|
|
26
|
-
| 0.2.1-rc.2 | 0.7.
|
|
26
|
+
| 0.2.1-rc.2 | 0.7.9(npm latest) |
|
|
27
27
|
| 0.2.1-rc.1 | 0.7.7 |
|
|
28
28
|
| 0.2.0-rc.2 | 0.7.6 |
|
|
29
29
|
| 0.2.0-rc.1 | 0.7.0 |
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
<summary>手动安装(命令行,仅 Web / TUI)</summary>
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
# Web(按实际内核固定版本,示例为 0.2.1-rc.2 → 0.7.
|
|
37
|
-
dsh plugin --profile web add dsh-workbuddy-connect@0.7.
|
|
36
|
+
# Web(按实际内核固定版本,示例为 0.2.1-rc.2 → 0.7.9;也可用 github:corrinehu/dsh-workbuddy-connect 从源码安装)
|
|
37
|
+
dsh plugin --profile web add dsh-workbuddy-connect@0.7.9
|
|
38
38
|
dsh web
|
|
39
39
|
|
|
40
40
|
# TUI(示例为 0.1.x 内核;该 profile 需用 pnpm 11 安装,如 npx pnpm@11)
|
|
@@ -84,7 +84,7 @@ dsh --profile dsh-tui
|
|
|
84
84
|
|
|
85
85
|

|
|
86
86
|
|
|
87
|
-
卡片展开后分「状态 / 上下文 / 明细」三个标签:状态页展示账号、令牌有效期、合计积分、模型列表来源与推理档位检测;上下文页列出各模型上下文窗口,国际版可在这里关闭「使用上游声明的最大上下文窗口」(默认开启,DSH
|
|
87
|
+
卡片展开后分「状态 / 上下文 / 明细」三个标签:状态页展示账号、令牌有效期、合计积分、模型列表来源与推理档位检测;上下文页列出各模型上下文窗口,国际版可在这里关闭「使用上游声明的最大上下文窗口」(默认开启,DSH 按上游最大窗口安排上下文压缩;关闭后改用上游默认窗口,偏好持久化);明细页合并展示同名积分包的余量与模型优惠。国内版与国际版各有一张自己的卡片。
|
|
88
88
|
|
|
89
89
|

|
|
90
90
|
|
|
@@ -120,7 +120,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
120
120
|
- **国际版仍未覆盖的环境**:Windows / WSL / Linux 下国际版 App 的版本读取尚未找到可靠来源,会退回最近保存的版本或内置值。macOS 上已通过真实 shim 验证 GPT 系完整回复、工具调用与续轮。
|
|
121
121
|
- **无凭据时的行为变化**:某版 App 从未登录、也没留下插件自留副本时,该版模型分组不再显示。此前国内版会显示一份内置兜底列表,但那些模型选了必然报错。
|
|
122
122
|
- **企业账号积分目前仅覆盖国内版**:国际版企业账号的计费接口尚未验证,仍按个人版接口读取;待有实测结论后再扩展。企业账号分支在本机无法自测(开发机为个人账号),依据官方 App 的接口契约实现,欢迎企业账号用户反馈实测结果。
|
|
123
|
-
- **设置入口随 DSH
|
|
123
|
+
- **设置入口随 DSH 版本而异**:旧版设置服务使用 `installSection`,新版使用 SettingsForms。0.7.9 在 DSH 0.2.1-rc.2 上恢复国际版「使用上游声明的最大上下文窗口」开关,默认开启;关闭后使用上游默认窗口,设置在重启后保留。只有本插件的可写设置入口就绪后才显示开关,等待期间仍使用默认的最大窗口。
|
|
124
124
|
- 依赖 WorkBuddy 客户端接口(非官方开放 API),WorkBuddy 更新后插件可能需要随之调整。
|
|
125
125
|
|
|
126
126
|
## 版本兼容与历史版本
|
|
@@ -129,7 +129,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
129
129
|
|
|
130
130
|
| DSH 内核 | 推荐插件版 | 注意事项 |
|
|
131
131
|
|---|---|---|
|
|
132
|
-
| `0.2.1-rc.2` | `0.7.
|
|
132
|
+
| `0.2.1-rc.2` | `0.7.9` | 仅适配这一内核;`0.2.0-rc.2` 与 `0.2.1-rc.2` 不同,请核对 App 内置版本。 |
|
|
133
133
|
| `0.2.1-rc.1` | `0.7.7` | 内核升级到 `@earendil-works/pi-ai` 1.3.0,插件同步自带 1.3.0,历史遗留的 0.87.1 overrides 必须移除(见下方 pi-ai 说明)。 |
|
|
134
134
|
| `0.2.0-rc.2` | `0.7.6` | 上一代内核线;修复企业账号模型目录缺项与国际版对话被渠道校验拦截,勿停留在有注册表回归的 0.7.2。 |
|
|
135
135
|
| `0.2.0-rc.1` | `0.7.0` | 0.7.1 起不再支持 rc.1,请停留本版。 |
|
|
@@ -139,6 +139,7 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
139
139
|
|
|
140
140
|
历次更新(完整记录见 [releases](https://github.com/corrinehu/dsh-workbuddy-connect/releases)):
|
|
141
141
|
|
|
142
|
+
- **0.7.9**:恢复 DSH 0.2.1-rc.2 上国际版最大上下文窗口开关,修复延迟初始化及设置持久化(PR [#172](https://github.com/corrinehu/dsh-workbuddy-connect/pull/172),by @1Dreamer666);避免本地服务绑定到 fetch 禁用端口导致国际版 502(PR [#164](https://github.com/corrinehu/dsh-workbuddy-connect/pull/164),by @Xiong-Ziyi);同名积分包合并展示,减少重复条目。
|
|
142
143
|
- **0.7.8**:适配 DSH 0.2.1-rc.2;将官方原子写入工具打包到插件,避免依赖宿主暴露该模块;更新国内模型目录快照,区分 Hy3 / Hy3-X;优化推理检测结果展示,调整积分刷新按钮位置并修正悬停提示。感谢 @chenpiyanghappy 的 PR [#156](https://github.com/corrinehu/dsh-workbuddy-connect/pull/156)。
|
|
143
144
|
- **0.7.7**:适配 DSH 0.2.1-rc.1 内核:peer 收窄到 0.2.1-rc.1,插件自带的 `@earendil-works/pi-ai` 随内核升级到 1.3.0(profile 级历史遗留的 0.87.1 overrides 必须移除,否则插件无法加载);修复国际版与国内版推理档位检测停留「检测未完成」——上游把档位拒绝码改拼为 `invalid_parameter_value`([#143](https://github.com/corrinehu/dsh-workbuddy-connect/issues/143) / [#148](https://github.com/corrinehu/dsh-workbuddy-connect/issues/148),PR [#145](https://github.com/corrinehu/dsh-workbuddy-connect/pull/145));修复中文 Windows 控制台下注册表缺键诊断因 GBK 编码被误判(zh-CN 实机验证)。
|
|
144
145
|
- **0.7.6**:修复企业账号模型目录缺少 `glm-5.3-flashx`([#123](https://github.com/corrinehu/dsh-workbuddy-connect/issues/123)),修复国际版对话请求被上游渠道校验拦截([#139](https://github.com/corrinehu/dsh-workbuddy-connect/issues/139) / [#142](https://github.com/corrinehu/dsh-workbuddy-connect/issues/142))。
|
|
@@ -150,9 +151,9 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
150
151
|
- **0.7.0**:适配 DSH 0.2.0 的设置服务改造,不再支持 0.1.x。
|
|
151
152
|
- **0.6.5**:0.1.x 线最终版本。
|
|
152
153
|
|
|
153
|
-
固定版本安装示例:`dsh plugin --profile web add dsh-workbuddy-connect@0.7.
|
|
154
|
+
固定版本安装示例:`dsh plugin --profile web add dsh-workbuddy-connect@0.7.9`。
|
|
154
155
|
|
|
155
|
-
**pi-ai 版本按内核线区分,不能混**:0.2.1-rc.1 / rc.2 内核线(分别对应插件 0.7.7 / 0.7.8)已升级到 `@earendil-works/pi-ai` 1.3.0(由插件自带),profile 级历史遗留的 `overrides: {'@earendil-works/pi-ai': '0.87.1'}` **必须移除**,否则插件会因加载到旧一代 pi-ai 而启动失败;0.2.0-rc.2 内核线(插件 0.7.6)仍要求 `^0.87.1`,在这条线上**不要**把 profile 级的 pi-ai 升到 1.x,第三方插件管理器的「可更新」提示不校验 peer 约束、不能照做([#86](https://github.com/corrinehu/dsh-workbuddy-connect/issues/86))。
|
|
156
|
+
**pi-ai 版本按内核线区分,不能混**:0.2.1-rc.1 / rc.2 内核线(分别对应插件 0.7.7 / 0.7.8–0.7.9)已升级到 `@earendil-works/pi-ai` 1.3.0(由插件自带),profile 级历史遗留的 `overrides: {'@earendil-works/pi-ai': '0.87.1'}` **必须移除**,否则插件会因加载到旧一代 pi-ai 而启动失败;0.2.0-rc.2 内核线(插件 0.7.6)仍要求 `^0.87.1`,在这条线上**不要**把 profile 级的 pi-ai 升到 1.x,第三方插件管理器的「可更新」提示不校验 peer 约束、不能照做([#86](https://github.com/corrinehu/dsh-workbuddy-connect/issues/86))。
|
|
156
157
|
|
|
157
158
|
## 免责声明
|
|
158
159
|
|
|
@@ -163,10 +164,11 @@ dsh plugin --profile web exec dsh-workbuddy-connect doctor --provider workbuddy-
|
|
|
163
164
|
|
|
164
165
|
## 相关项目 / 社区生态
|
|
165
166
|
|
|
166
|
-
- **[
|
|
167
|
+
- **[dsh-buddy-checkin](https://github.com/corrinehu/dsh-buddy-checkin)**:本项目作者维护的 WorkBuddy 国内版每日签到插件。
|
|
168
|
+
- **[dsh-workbuddy-switcher](https://github.com/ddGuo/dsh-workbuddy-switcher)**:社区维护的 WorkBuddy 账号管理与切换插件。
|
|
167
169
|
- **[hana-workbuddy-connect](https://github.com/miaoyongxu/hana-workbuddy-connect)**:社区维护的非官方适配项目,复用 `dsh-workbuddy-connect` 的部分实现,为 HanaAgent 接入 WorkBuddy / WorkBuddy AI 模型。
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
适用版本与安装要求请参阅各项目说明。除 `dsh-buddy-checkin` 外,以上项目均由第三方独立维护;收录不代表官方背书,也不构成本插件内部接口的稳定性或兼容性承诺。
|
|
170
172
|
|
|
171
173
|
## 致谢
|
|
172
174
|
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as WORKBUDDY_VARIANTS, O as WorkBuddyUpstreamClient, Q as resolveAppVersion, S as CN_VARIANT, a as readHostHeartbeat, b as atRestKeyProviderFor, c as WORKBUDDY_CONNECT_VERSION, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, w as variantFor } from "./host-heartbeat-
|
|
2
|
+
import { C as WORKBUDDY_VARIANTS, O as WorkBuddyUpstreamClient, Q as resolveAppVersion, S as CN_VARIANT, a as readHostHeartbeat, b as atRestKeyProviderFor, c as WORKBUDDY_CONNECT_VERSION, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, u as FALLBACK_WORKBUDDY_MODELS, w as variantFor } from "./host-heartbeat-SJaZ33tR.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
|
@@ -691,6 +691,25 @@ window.__ModuleLoader__.load({
|
|
|
691
691
|
})]
|
|
692
692
|
});
|
|
693
693
|
}
|
|
694
|
+
/** Combine same-name packages for display; expiry data stays per package upstream. */
|
|
695
|
+
function groupCreditPackages(accounts) {
|
|
696
|
+
const groups = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const account of accounts) {
|
|
698
|
+
const group = groups.get(account.packageName);
|
|
699
|
+
if (group === void 0) groups.set(account.packageName, {
|
|
700
|
+
packageName: account.packageName,
|
|
701
|
+
remain: account.remain,
|
|
702
|
+
size: account.size,
|
|
703
|
+
...account.unlimited === true ? { unlimited: true } : {}
|
|
704
|
+
});
|
|
705
|
+
else {
|
|
706
|
+
group.remain += account.remain;
|
|
707
|
+
group.size = group.size > 0 && account.size > 0 ? group.size + account.size : 0;
|
|
708
|
+
if (account.unlimited === true) group.unlimited = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return [...groups.values()];
|
|
712
|
+
}
|
|
694
713
|
/**
|
|
695
714
|
* One billing package as a labeled progress bar.
|
|
696
715
|
*
|
|
@@ -1489,13 +1508,13 @@ window.__ModuleLoader__.load({
|
|
|
1489
1508
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1490
1509
|
style: quotaTitleStyle,
|
|
1491
1510
|
children: t("creditsDetailHeading")
|
|
1492
|
-
}), status.credits.accounts.filter((account) => account.packageName === "enterprise" || account.remain > 0 || account.unlimited === true).map((account
|
|
1511
|
+
}), groupCreditPackages(status.credits.accounts).filter((account) => account.packageName === "enterprise" || account.remain > 0 || account.unlimited === true).map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreditBar, {
|
|
1493
1512
|
label: account.packageName === "enterprise" ? t("packageEnterprise") : account.packageName,
|
|
1494
1513
|
remain: account.remain,
|
|
1495
1514
|
size: account.size,
|
|
1496
1515
|
unlimited: account.unlimited,
|
|
1497
1516
|
t
|
|
1498
|
-
},
|
|
1517
|
+
}, account.packageName))]
|
|
1499
1518
|
}), status.models === void 0 || status.models.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1500
1519
|
style: quotaListStyle,
|
|
1501
1520
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -2828,7 +2847,7 @@ window.__ModuleLoader__.load({
|
|
|
2828
2847
|
};
|
|
2829
2848
|
//#endregion
|
|
2830
2849
|
//#region src/version.ts
|
|
2831
|
-
const WORKBUDDY_CONNECT_VERSION = "0.7.
|
|
2850
|
+
const WORKBUDDY_CONNECT_VERSION = "0.7.9";
|
|
2832
2851
|
//#endregion
|
|
2833
2852
|
//#region src/client/WorkBuddyConfigPage.tsx
|
|
2834
2853
|
/**
|
|
@@ -4542,7 +4542,7 @@ var WorkBuddyCatalog = class {
|
|
|
4542
4542
|
};
|
|
4543
4543
|
//#endregion
|
|
4544
4544
|
//#region src/version.ts
|
|
4545
|
-
const WORKBUDDY_CONNECT_VERSION = "0.7.
|
|
4545
|
+
const WORKBUDDY_CONNECT_VERSION = "0.7.9";
|
|
4546
4546
|
//#endregion
|
|
4547
4547
|
//#region src/host-heartbeat.ts
|
|
4548
4548
|
/**
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as validAppVersion, A as extractDisplayErrorMessage, B as randomSentinel, C as WORKBUDDY_VARIANTS, D as WORKBUDDY_UPDATE_PATH, F as prepareCnChatBody, G as readCliVersion, H as FALLBACK_CN_APP_VERSION, I as prepareInternationalChatBody, J as WORKBUDDY_APP_VERSION_FILENAME, K as resolveChatIdentity, L as regionOf, M as normalizeCredits, N as parseModelCatalog, O as WorkBuddyUpstreamClient, P as prepareChatBody, Q as resolveAppVersion, R as PROBE_EFFORT_CANDIDATES, S as CN_VARIANT, U as chatUserAgent, V as CN_APP_VERSION_FILENAME, W as fallbackChatIdentity, X as installedAppVersion, Y as appUserAgent, Z as readBundleVersion, _ as desktopAuthCandidatesFor, a as readHostHeartbeat, b as atRestKeyProviderFor, c as WORKBUDDY_CONNECT_VERSION, d as WorkBuddyCatalog, f as WORKBUDDY_AUTH_FILENAME, g as defaultDesktopAuthPath, h as defaultDesktopAuthCandidates, i as processStartTimeMs, j as modelWithCurrentPromotion, k as classifyUpstreamError, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as WORKBUDDY_AUTH_FILE_ENV, q as validCliVersion, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS, v as parseWorkBuddyAuth, w as variantFor, x as AI_VARIANT, y as workbuddyOwnAuthPath, z as probeModel } from "./host-heartbeat-
|
|
1
|
+
import { $ as validAppVersion, A as extractDisplayErrorMessage, B as randomSentinel, C as WORKBUDDY_VARIANTS, D as WORKBUDDY_UPDATE_PATH, F as prepareCnChatBody, G as readCliVersion, H as FALLBACK_CN_APP_VERSION, I as prepareInternationalChatBody, J as WORKBUDDY_APP_VERSION_FILENAME, K as resolveChatIdentity, L as regionOf, M as normalizeCredits, N as parseModelCatalog, O as WorkBuddyUpstreamClient, P as prepareChatBody, Q as resolveAppVersion, R as PROBE_EFFORT_CANDIDATES, S as CN_VARIANT, U as chatUserAgent, V as CN_APP_VERSION_FILENAME, W as fallbackChatIdentity, X as installedAppVersion, Y as appUserAgent, Z as readBundleVersion, _ as desktopAuthCandidatesFor, a as readHostHeartbeat, b as atRestKeyProviderFor, c as WORKBUDDY_CONNECT_VERSION, d as WorkBuddyCatalog, f as WORKBUDDY_AUTH_FILENAME, g as defaultDesktopAuthPath, h as defaultDesktopAuthCandidates, i as processStartTimeMs, j as modelWithCurrentPromotion, k as classifyUpstreamError, l as FALLBACK_WORKBUDDY_AI_MODELS, m as WorkBuddyCredentialStore, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as WORKBUDDY_AUTH_FILE_ENV, q as validCliVersion, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as FALLBACK_WORKBUDDY_MODELS, v as parseWorkBuddyAuth, w as variantFor, x as AI_VARIANT, y as workbuddyOwnAuthPath, z as probeModel } from "./host-heartbeat-SJaZ33tR.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -577,13 +577,6 @@ var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
|
|
|
577
577
|
};
|
|
578
578
|
//#endregion
|
|
579
579
|
//#region src/loopback.ts
|
|
580
|
-
/**
|
|
581
|
-
* Shared loopback gates for the plugin's local HTTP surfaces: the loopback
|
|
582
|
-
* shim and the same-origin web-status route. Both are only ever meant to be
|
|
583
|
-
* addressed through the machine's loopback interface.
|
|
584
|
-
*
|
|
585
|
-
* @module dsh-workbuddy-connect/loopback
|
|
586
|
-
*/
|
|
587
580
|
/** Loopback hostnames a local plugin surface may be addressed by. */
|
|
588
581
|
const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
|
|
589
582
|
"127.0.0.1",
|
|
@@ -623,6 +616,163 @@ function originIsLoopback(origin) {
|
|
|
623
616
|
return false;
|
|
624
617
|
}
|
|
625
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Ports fetch refuses to connect to, per the fetch standard's port blocking
|
|
621
|
+
* table (https://fetch.spec.whatwg.org/#port-blocking). Kept in sync with the
|
|
622
|
+
* `badPorts` table undici ships: only the entries a host's ephemeral range can
|
|
623
|
+
* actually reach matter, but carrying the whole list costs nothing and keeps
|
|
624
|
+
* the check honest on machines with a customised range.
|
|
625
|
+
*/
|
|
626
|
+
const FETCH_BLOCKED_PORTS = /* @__PURE__ */ new Set([
|
|
627
|
+
1,
|
|
628
|
+
7,
|
|
629
|
+
9,
|
|
630
|
+
11,
|
|
631
|
+
13,
|
|
632
|
+
15,
|
|
633
|
+
17,
|
|
634
|
+
19,
|
|
635
|
+
20,
|
|
636
|
+
21,
|
|
637
|
+
22,
|
|
638
|
+
23,
|
|
639
|
+
25,
|
|
640
|
+
37,
|
|
641
|
+
42,
|
|
642
|
+
43,
|
|
643
|
+
53,
|
|
644
|
+
69,
|
|
645
|
+
77,
|
|
646
|
+
79,
|
|
647
|
+
87,
|
|
648
|
+
95,
|
|
649
|
+
101,
|
|
650
|
+
102,
|
|
651
|
+
103,
|
|
652
|
+
104,
|
|
653
|
+
109,
|
|
654
|
+
110,
|
|
655
|
+
111,
|
|
656
|
+
113,
|
|
657
|
+
115,
|
|
658
|
+
117,
|
|
659
|
+
119,
|
|
660
|
+
123,
|
|
661
|
+
135,
|
|
662
|
+
137,
|
|
663
|
+
139,
|
|
664
|
+
143,
|
|
665
|
+
161,
|
|
666
|
+
179,
|
|
667
|
+
389,
|
|
668
|
+
427,
|
|
669
|
+
465,
|
|
670
|
+
512,
|
|
671
|
+
513,
|
|
672
|
+
514,
|
|
673
|
+
515,
|
|
674
|
+
526,
|
|
675
|
+
530,
|
|
676
|
+
531,
|
|
677
|
+
532,
|
|
678
|
+
540,
|
|
679
|
+
548,
|
|
680
|
+
554,
|
|
681
|
+
556,
|
|
682
|
+
563,
|
|
683
|
+
587,
|
|
684
|
+
601,
|
|
685
|
+
636,
|
|
686
|
+
989,
|
|
687
|
+
990,
|
|
688
|
+
993,
|
|
689
|
+
995,
|
|
690
|
+
1719,
|
|
691
|
+
1720,
|
|
692
|
+
1723,
|
|
693
|
+
2049,
|
|
694
|
+
3659,
|
|
695
|
+
4045,
|
|
696
|
+
4190,
|
|
697
|
+
5060,
|
|
698
|
+
5061,
|
|
699
|
+
6e3,
|
|
700
|
+
6566,
|
|
701
|
+
6665,
|
|
702
|
+
6666,
|
|
703
|
+
6667,
|
|
704
|
+
6668,
|
|
705
|
+
6669,
|
|
706
|
+
6679,
|
|
707
|
+
6697,
|
|
708
|
+
10080
|
|
709
|
+
]);
|
|
710
|
+
/** Whether fetch (and therefore pi-ai) refuses to connect to `port`. */
|
|
711
|
+
function isFetchBlockedPort(port) {
|
|
712
|
+
return FETCH_BLOCKED_PORTS.has(port);
|
|
713
|
+
}
|
|
714
|
+
/** Bind attempts allowed before giving up, including the first. */
|
|
715
|
+
const DEFAULT_MAX_ATTEMPTS = 16;
|
|
716
|
+
/**
|
|
717
|
+
* Bind `server` to an ephemeral port fetch will actually connect to, and
|
|
718
|
+
* resolve with the address it ended up on.
|
|
719
|
+
*
|
|
720
|
+
* `listen(0)` lets the OS hand out any free ephemeral port, and a handful of
|
|
721
|
+
* the ports it can hand out are on the fetch standard's bad-port list: fetch
|
|
722
|
+
* refuses to connect to them and fails with `TypeError: fetch failed` before a
|
|
723
|
+
* socket is opened. The shim is only ever reached through fetch (pi-ai calls
|
|
724
|
+
* it), so settling on one of those ports leaves the plugin with an endpoint
|
|
725
|
+
* that answers raw HTTP and rejects every real request. The port is not
|
|
726
|
+
* negotiable at request time, so the only fix is to bind again.
|
|
727
|
+
*
|
|
728
|
+
* A blocked port is closed and rebound; a listen error is fatal, as it was
|
|
729
|
+
* before, because nothing about picking another port would fix it.
|
|
730
|
+
*/
|
|
731
|
+
async function bindToUsablePort(server, options) {
|
|
732
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS);
|
|
733
|
+
const isPortBlocked = options.isPortBlocked ?? isFetchBlockedPort;
|
|
734
|
+
let lastBlockedPort = 0;
|
|
735
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
736
|
+
const address = await listenOnEphemeralPort(server, options.host);
|
|
737
|
+
if (!isPortBlocked(address.port)) return address;
|
|
738
|
+
lastBlockedPort = address.port;
|
|
739
|
+
options.onBlockedPort?.(address.port, attempt);
|
|
740
|
+
await closeQuietly(server);
|
|
741
|
+
}
|
|
742
|
+
throw new Error(`could not bind a fetch-reachable loopback port after ${maxAttempts} attempts (last blocked port: ${lastBlockedPort})`);
|
|
743
|
+
}
|
|
744
|
+
/** One `listen(0)` on `host`, resolving with the address the OS handed out. */
|
|
745
|
+
function listenOnEphemeralPort(server, host) {
|
|
746
|
+
return new Promise((resolve, reject) => {
|
|
747
|
+
const cleanup = () => {
|
|
748
|
+
server.off("listening", onListening);
|
|
749
|
+
server.off("error", onError);
|
|
750
|
+
};
|
|
751
|
+
const onListening = () => {
|
|
752
|
+
cleanup();
|
|
753
|
+
const address = server.address();
|
|
754
|
+
if (address === null || typeof address === "string") {
|
|
755
|
+
reject(/* @__PURE__ */ new Error("workbuddy shim has no listening address"));
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
resolve(address);
|
|
759
|
+
};
|
|
760
|
+
const onError = (error) => {
|
|
761
|
+
cleanup();
|
|
762
|
+
reject(error);
|
|
763
|
+
};
|
|
764
|
+
server.once("listening", onListening);
|
|
765
|
+
server.once("error", onError);
|
|
766
|
+
server.listen(0, host);
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
/** Close between attempts; a listener that already closed is not an error here. */
|
|
770
|
+
function closeQuietly(server) {
|
|
771
|
+
return new Promise((resolve) => {
|
|
772
|
+
server.closeAllConnections();
|
|
773
|
+
server.close(() => resolve());
|
|
774
|
+
});
|
|
775
|
+
}
|
|
626
776
|
//#endregion
|
|
627
777
|
//#region src/shim.ts
|
|
628
778
|
/**
|
|
@@ -713,11 +863,12 @@ function createWorkBuddyShim(options) {
|
|
|
713
863
|
const server = createServer((req, res) => {
|
|
714
864
|
handle(req, res);
|
|
715
865
|
});
|
|
716
|
-
const ready =
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
866
|
+
const ready = bindToUsablePort(server, {
|
|
867
|
+
host: "127.0.0.1",
|
|
868
|
+
onBlockedPort: (port, attempt) => {
|
|
869
|
+
logger?.warn(`dsh-workbuddy-connect: port ${port} is blocked for fetch; rebinding (attempt ${attempt})`);
|
|
870
|
+
}
|
|
871
|
+
}).then(() => void 0);
|
|
721
872
|
const baseUrl = () => {
|
|
722
873
|
const address = server.address();
|
|
723
874
|
if (address === null || typeof address === "string") throw new Error("workbuddy shim has no listening address");
|
|
@@ -1862,6 +2013,38 @@ function legacySettingsOf(settings) {
|
|
|
1862
2013
|
if (typeof candidate.update !== "function") return void 0;
|
|
1863
2014
|
return settings;
|
|
1864
2015
|
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Narrow one host settings service to the modern DSH 0.2.x SettingsForms API, or
|
|
2018
|
+
* `undefined` when this host's generation does not carry it.
|
|
2019
|
+
*/
|
|
2020
|
+
function modernSettingsOf(settings) {
|
|
2021
|
+
if (typeof settings !== "object" || settings === null) return void 0;
|
|
2022
|
+
const candidate = settings;
|
|
2023
|
+
if (typeof candidate.installSection === "function") return void 0;
|
|
2024
|
+
if (typeof candidate.describe !== "function") return void 0;
|
|
2025
|
+
if (typeof candidate.update !== "function") return void 0;
|
|
2026
|
+
return settings;
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Resolve this plugin's own profile entry id from SettingsForms descriptors.
|
|
2030
|
+
*
|
|
2031
|
+
* The real `describe()` (dsh-settings 0.2.1-rc.2, `SettingsForms.describe`)
|
|
2032
|
+
* hands out *serialized* form schemas (`volatileForm(schema).toJSON()`) and
|
|
2033
|
+
* no fiber reference, so neither a `desc.schema === Config` identity check
|
|
2034
|
+
* nor a fiber comparison can ever succeed — identity has to come from the
|
|
2035
|
+
* entry id the loader attached to this plugin's own fiber
|
|
2036
|
+
* (`ctx.fiber.entry.options.id`; the tree-qualified `entry.id` includes
|
|
2037
|
+
* parent prefixes that SettingsForms does not use). The package-name literal is the only fallback, for hosts where the
|
|
2038
|
+
* plugin was loaded programmatically and carries no entry: entry ids are
|
|
2039
|
+
* unique per composition, so the literal can only name this plugin. Neither
|
|
2040
|
+
* route matching an entry that is served answers `undefined` — never a
|
|
2041
|
+
* guess, because a wrong guess silently writes another plugin's
|
|
2042
|
+
* configuration.
|
|
2043
|
+
*/
|
|
2044
|
+
function resolveModernSettingsEntryId(descriptors, ownEntryId) {
|
|
2045
|
+
if (ownEntryId !== void 0) return descriptors.find((row) => row.ns === ownEntryId)?.ns;
|
|
2046
|
+
return descriptors.find((row) => row.ns === "dsh-workbuddy-connect")?.ns;
|
|
2047
|
+
}
|
|
1865
2048
|
//#endregion
|
|
1866
2049
|
//#region src/index.ts
|
|
1867
2050
|
/** Stable Cordis plugin name. */
|
|
@@ -1940,7 +2123,7 @@ const AUTH_FILE_FIELD = z.string().description("WorkBuddy desktop auth file (def
|
|
|
1940
2123
|
const AUTH_FILE_AI_FIELD = z.string().description("WorkBuddy AI desktop auth file (defaults to the app's own location)");
|
|
1941
2124
|
/** Probe authorization (shared by the plugin schema and the CN section). */
|
|
1942
2125
|
const PROBE_CONSENT_FIELD = z.boolean().default(false).description("Authorize reasoning-effort probes (each probe sends real requests that may consume credit)");
|
|
1943
|
-
const MAXIMUM_CONTEXT_WINDOW_FIELD = z.boolean().default(true).description("Use the largest context window declared by WorkBuddy AI when alternatives are available (on by default)");
|
|
2126
|
+
const MAXIMUM_CONTEXT_WINDOW_FIELD = z.boolean().default(true).volatile().description("Use the largest context window declared by WorkBuddy AI when alternatives are available (on by default)");
|
|
1944
2127
|
const Config = z.object({
|
|
1945
2128
|
authFile: AUTH_FILE_FIELD,
|
|
1946
2129
|
authFileAI: AUTH_FILE_AI_FIELD,
|
|
@@ -1948,6 +2131,17 @@ const Config = z.object({
|
|
|
1948
2131
|
useMaximumContextWindow: MAXIMUM_CONTEXT_WINDOW_FIELD
|
|
1949
2132
|
});
|
|
1950
2133
|
/**
|
|
2134
|
+
* Read the context-window preference off a resolved Config. Fields marked
|
|
2135
|
+
* `.volatile()` materialize as cosmokit volatile containers (`{ get() }`) so
|
|
2136
|
+
* the host can live-update them without remounting the plugin; plain hosts
|
|
2137
|
+
* and values set through this plugin are plain booleans. Both spellings
|
|
2138
|
+
* answer here.
|
|
2139
|
+
*/
|
|
2140
|
+
function plainConfigBoolean(value) {
|
|
2141
|
+
if (value !== null && typeof value === "object" && typeof value.get === "function") return plainConfigBoolean(value.get());
|
|
2142
|
+
return value === void 0 ? void 0 : value === true;
|
|
2143
|
+
}
|
|
2144
|
+
/**
|
|
1951
2145
|
* The CN side's settings section: only the fields that side edits.
|
|
1952
2146
|
*
|
|
1953
2147
|
* The section's namespace is what serves these fields to `settings.yaml` and
|
|
@@ -2009,7 +2203,7 @@ function createVariantRuntime(config, variant, current, identityOf, accountOf, k
|
|
|
2009
2203
|
});
|
|
2010
2204
|
const fallback = fallbackFor(variant);
|
|
2011
2205
|
const catalog = new WorkBuddyCatalog(fallback);
|
|
2012
|
-
if (variant.id !== CN_VARIANT.id) catalog.setUseMaximumContextWindow(config.useMaximumContextWindow === true);
|
|
2206
|
+
if (variant.id !== CN_VARIANT.id) catalog.setUseMaximumContextWindow(plainConfigBoolean(config.useMaximumContextWindow) === true);
|
|
2013
2207
|
catalog.setVisible(false);
|
|
2014
2208
|
const probeStore = new WorkBuddyProbeStore({
|
|
2015
2209
|
pluginVersion: WORKBUDDY_CONNECT_VERSION,
|
|
@@ -2196,6 +2390,8 @@ function apply(ctx, config) {
|
|
|
2196
2390
|
* checkbox that could not be saved.
|
|
2197
2391
|
*/
|
|
2198
2392
|
let legacySettingsAvailable = false;
|
|
2393
|
+
/** Whether modern DSH 0.2.x SettingsForms service is available to persist settings. */
|
|
2394
|
+
let settingsAvailable = false;
|
|
2199
2395
|
/**
|
|
2200
2396
|
* Point a variant at an account identity, invalidating whatever the previous
|
|
2201
2397
|
* one left behind.
|
|
@@ -2275,7 +2471,7 @@ function apply(ctx, config) {
|
|
|
2275
2471
|
disabled: runtime.visibilityStore.disabled(account)
|
|
2276
2472
|
};
|
|
2277
2473
|
},
|
|
2278
|
-
...runtime.variant.id === CN_VARIANT.id ? {} : { useMaximumContextWindow: () => legacySettingsAvailable ? current().useMaximumContextWindow === true : void 0 }
|
|
2474
|
+
...runtime.variant.id === CN_VARIANT.id ? {} : { useMaximumContextWindow: () => legacySettingsAvailable || settingsAvailable ? plainConfigBoolean(current().useMaximumContextWindow) === true : void 0 }
|
|
2279
2475
|
});
|
|
2280
2476
|
registerWorkBuddyProbeRoute(webCtx, {
|
|
2281
2477
|
path: runtime.variant.probePath,
|
|
@@ -2350,50 +2546,191 @@ function apply(ctx, config) {
|
|
|
2350
2546
|
});
|
|
2351
2547
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
2352
2548
|
const legacy = legacySettingsOf(settingsCtx.settings);
|
|
2353
|
-
if (legacy
|
|
2354
|
-
|
|
2549
|
+
if (legacy !== void 0) {
|
|
2550
|
+
legacySettingsAvailable = true;
|
|
2551
|
+
/** Section sources; each falls back to its own slice when its side unloads. */
|
|
2552
|
+
const sources = {
|
|
2553
|
+
cn: () => config,
|
|
2554
|
+
ai: () => config
|
|
2555
|
+
};
|
|
2556
|
+
/** Merge both sections into the whole config the rest of the plugin reads. */
|
|
2557
|
+
const merged = () => {
|
|
2558
|
+
const aiMaximum = plainConfigBoolean(sources.ai().useMaximumContextWindow);
|
|
2559
|
+
return {
|
|
2560
|
+
...sources.cn().authFile === void 0 ? {} : { authFile: sources.cn().authFile },
|
|
2561
|
+
...sources.cn().probeConsent === void 0 ? {} : { probeConsent: sources.cn().probeConsent },
|
|
2562
|
+
...sources.ai().authFileAI === void 0 ? {} : { authFileAI: sources.ai().authFileAI },
|
|
2563
|
+
...aiMaximum === void 0 ? {} : { useMaximumContextWindow: aiMaximum }
|
|
2564
|
+
};
|
|
2565
|
+
};
|
|
2566
|
+
const applyMaximumContextWindow = (next) => {
|
|
2567
|
+
const runtime = runtimes.find((candidate) => candidate.variant.id !== CN_VARIANT.id);
|
|
2568
|
+
if (runtime?.catalog.setUseMaximumContextWindow(next.useMaximumContextWindow === true)) runtime.invalidate();
|
|
2569
|
+
};
|
|
2570
|
+
const repointStores = () => {
|
|
2571
|
+
const next = merged();
|
|
2572
|
+
applyMaximumContextWindow(next);
|
|
2573
|
+
for (const runtime of runtimes) runtime.store.setDesktopPath(configuredAuthFile(next, runtime.variant));
|
|
2574
|
+
};
|
|
2575
|
+
legacy.installSection(ctx, WORKBUDDY_SETTINGS_NS, CN_SECTION, config, {
|
|
2576
|
+
setSource(source) {
|
|
2577
|
+
sources.cn = source;
|
|
2578
|
+
current = merged;
|
|
2579
|
+
},
|
|
2580
|
+
onChange: repointStores
|
|
2581
|
+
});
|
|
2582
|
+
legacy.installSection(ctx, WORKBUDDY_AI_SETTINGS_NS, AI_SECTION, config, {
|
|
2583
|
+
setSource(source) {
|
|
2584
|
+
sources.ai = source;
|
|
2585
|
+
current = merged;
|
|
2586
|
+
},
|
|
2587
|
+
onChange: repointStores
|
|
2588
|
+
});
|
|
2589
|
+
setMaximumContextWindow = async (enabled) => {
|
|
2590
|
+
await legacy.update(WORKBUDDY_AI_SETTINGS_NS, { useMaximumContextWindow: enabled });
|
|
2591
|
+
return { state: "updated" };
|
|
2592
|
+
};
|
|
2355
2593
|
return;
|
|
2356
2594
|
}
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2595
|
+
const modern = modernSettingsOf(settingsCtx.settings);
|
|
2596
|
+
if (modern !== void 0) {
|
|
2597
|
+
/**
|
|
2598
|
+
* The live preference as the host's own Config reports it.
|
|
2599
|
+
*
|
|
2600
|
+
* The forms layer is the single source of truth on a 0.2.x host: a write
|
|
2601
|
+
* lands in the profile document and comes back through `describe()`, so
|
|
2602
|
+
* nothing has to be mirrored in memory here (and a mirror is exactly how
|
|
2603
|
+
* runtime and saved state drift apart). The persisted default already
|
|
2604
|
+
* applies — the preference field defaults to `true` and the composition
|
|
2605
|
+
* handed this plugin its resolved `config`.
|
|
2606
|
+
*/
|
|
2607
|
+
let liveConfig = {
|
|
2608
|
+
...config,
|
|
2609
|
+
useMaximumContextWindow: plainConfigBoolean(config.useMaximumContextWindow) ?? true
|
|
2610
|
+
};
|
|
2611
|
+
/** Whether {@link liveConfig} came from a settings descriptor (see below). */
|
|
2612
|
+
let liveConfigBound = false;
|
|
2613
|
+
/**
|
|
2614
|
+
* Read this plugin's own entry out of the host's forms.
|
|
2615
|
+
*
|
|
2616
|
+
* The namespace on a 0.2.x host is the *profile entry id* — not the
|
|
2617
|
+
* `SettingsNamespace` the 0.1.x sections were installed under — so it is
|
|
2618
|
+
* whatever the user named this plugin in the profile composition.
|
|
2619
|
+
*
|
|
2620
|
+
* Identity cannot come from the descriptors themselves: `describe()`
|
|
2621
|
+
* hands out *serialized* forms (`volatileForm(schema).toJSON()`), never
|
|
2622
|
+
* the live Config object, and its rows carry no fiber — so
|
|
2623
|
+
* `desc.schema === Config` and fiber comparisons are always false
|
|
2624
|
+
* against the real service. The loader instead keys entries by id and
|
|
2625
|
+
* attaches them to the fibers it runs (`ctx.fiber.entry.options.id`, unlike the tree-qualified `entry.id`), which is
|
|
2626
|
+
* what {@link resolveModernSettingsEntryId} reads; the package-name
|
|
2627
|
+
* literal stays as the only fallback (entry ids are unique per
|
|
2628
|
+
* composition, so it can only name this plugin). Anything unmatched
|
|
2629
|
+
* answers `undefined` — a wrong guess would silently write another
|
|
2630
|
+
* plugin's configuration.
|
|
2631
|
+
*/
|
|
2632
|
+
const settingsEntryId = () => {
|
|
2633
|
+
try {
|
|
2634
|
+
const entry = ctx.fiber?.entry;
|
|
2635
|
+
return resolveModernSettingsEntryId(modern.describe(), entry?.options?.id ?? entry?.id);
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
ctx.logger.warn("dsh-workbuddy-connect: reading this plugin's settings entry failed: %s", error);
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
};
|
|
2641
|
+
/**
|
|
2642
|
+
* Apply whatever the settings document now says.
|
|
2643
|
+
*
|
|
2644
|
+
* The value read is the descriptor's `value` — the projection of the live
|
|
2645
|
+
* fiber config, i.e. what the loader actually resolved — rather than
|
|
2646
|
+
* `user`, which names only the profile's own section and would miss an
|
|
2647
|
+
* inherited or default value that is nonetheless in effect.
|
|
2648
|
+
*/
|
|
2649
|
+
const applyModernConfig = () => {
|
|
2650
|
+
if (!liveConfigBound) return;
|
|
2651
|
+
const runtime = runtimes.find((candidate) => candidate.variant.id !== CN_VARIANT.id);
|
|
2652
|
+
if (runtime?.catalog.setUseMaximumContextWindow(plainConfigBoolean(liveConfig.useMaximumContextWindow) === true)) runtime.invalidate();
|
|
2653
|
+
};
|
|
2654
|
+
setMaximumContextWindow = async (enabled) => {
|
|
2655
|
+
const ns = settingsEntryId();
|
|
2656
|
+
if (ns === void 0) return {
|
|
2657
|
+
state: "failed",
|
|
2658
|
+
reason: "no configurable settings entry for this plugin"
|
|
2659
|
+
};
|
|
2660
|
+
try {
|
|
2661
|
+
await modern.update(ns, { useMaximumContextWindow: enabled });
|
|
2662
|
+
if ((modern.describe().find((entry) => entry.ns === ns)?.value)?.useMaximumContextWindow !== enabled) return {
|
|
2663
|
+
state: "failed",
|
|
2664
|
+
reason: "the settings document did not accept the context-window preference"
|
|
2665
|
+
};
|
|
2666
|
+
} catch (error) {
|
|
2667
|
+
return {
|
|
2668
|
+
state: "failed",
|
|
2669
|
+
reason: error instanceof Error ? error.message.slice(0, 300) : String(error)
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
liveConfig = {
|
|
2673
|
+
...liveConfig,
|
|
2674
|
+
useMaximumContextWindow: enabled
|
|
2675
|
+
};
|
|
2676
|
+
current = () => liveConfig;
|
|
2677
|
+
applyModernConfig();
|
|
2678
|
+
return { state: "updated" };
|
|
2679
|
+
};
|
|
2680
|
+
let retry;
|
|
2681
|
+
let retryIndex = 0;
|
|
2682
|
+
let bindingStopped = false;
|
|
2683
|
+
const retryDelays = [
|
|
2684
|
+
250,
|
|
2685
|
+
500,
|
|
2686
|
+
1e3,
|
|
2687
|
+
2e3,
|
|
2688
|
+
4e3,
|
|
2689
|
+
8e3
|
|
2690
|
+
];
|
|
2691
|
+
const clearRetry = () => {
|
|
2692
|
+
clearTimeout(retry);
|
|
2693
|
+
retry = void 0;
|
|
2694
|
+
};
|
|
2695
|
+
const bindModernConfig = () => {
|
|
2696
|
+
if (bindingStopped) return;
|
|
2697
|
+
try {
|
|
2698
|
+
const ns = settingsEntryId();
|
|
2699
|
+
const entry = ns === void 0 ? void 0 : modern.describe().find((desc) => desc.ns === ns);
|
|
2700
|
+
settingsAvailable = (entry?.schema === void 0 ? void 0 : new z(entry.schema))?.dict?.useMaximumContextWindow?.type === "boolean";
|
|
2701
|
+
if (settingsAvailable) {
|
|
2702
|
+
const stored = (entry?.value)?.useMaximumContextWindow;
|
|
2703
|
+
liveConfig = {
|
|
2704
|
+
...liveConfig,
|
|
2705
|
+
useMaximumContextWindow: plainConfigBoolean(stored) ?? plainConfigBoolean(liveConfig.useMaximumContextWindow) ?? true
|
|
2706
|
+
};
|
|
2707
|
+
liveConfigBound = true;
|
|
2708
|
+
current = () => liveConfig;
|
|
2709
|
+
applyModernConfig();
|
|
2710
|
+
clearRetry();
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
} catch (error) {
|
|
2714
|
+
settingsAvailable = false;
|
|
2715
|
+
ctx.logger.warn("dsh-workbuddy-connect: reading the stored context-window preference failed: %s", error);
|
|
2716
|
+
}
|
|
2717
|
+
if (retry === void 0 && retryIndex < retryDelays.length) retry = setTimeout(() => {
|
|
2718
|
+
retry = void 0;
|
|
2719
|
+
bindModernConfig();
|
|
2720
|
+
}, retryDelays[retryIndex++]);
|
|
2721
|
+
};
|
|
2722
|
+
settingsCtx.effect(() => () => {
|
|
2723
|
+
bindingStopped = true;
|
|
2724
|
+
clearRetry();
|
|
2725
|
+
settingsAvailable = false;
|
|
2726
|
+
});
|
|
2727
|
+
settingsCtx.effect(() => ctx.on("settings/document-updated", () => {
|
|
2728
|
+
bindModernConfig();
|
|
2729
|
+
}));
|
|
2730
|
+
bindModernConfig();
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
ctx.logger.warn("dsh-workbuddy-connect: host settings service has no installSection or SettingsForms API; per-variant settings and the maximum-context preference are unavailable");
|
|
2397
2734
|
});
|
|
2398
2735
|
ctx.effect(() => () => {
|
|
2399
2736
|
stopped = true;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-workbuddy-connect",
|
|
3
3
|
"displayName": "DSH WorkBuddy Connect",
|
|
4
4
|
"description": "将 WorkBuddy 桌面 App 包含的模型自动接入 DeepSeek Harness — bring WorkBuddy desktop app models into DeepSeek Harness with zero configuration.",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.9",
|
|
6
6
|
"author": "corrinehu",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|