dsh-workbuddy-connect 0.2.5 → 0.3.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/README.en.md +21 -3
- package/README.md +24 -3
- package/lib/bin.js +1 -1
- package/lib/client.js +92 -3
- package/lib/{host-heartbeat-1MO2dZrM.js → host-heartbeat-CM7AW7vZ.js} +324 -30
- package/lib/index.d.ts +92 -9
- package/lib/index.js +170 -18
- package/package.json +29 -30
package/README.en.md
CHANGED
|
@@ -10,7 +10,13 @@ Brings every model in the WorkBuddy desktop app (GLM-5.3, GLM-5.2, DeepSeek-V4-P
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
-
- **Image input**:
|
|
13
|
+
- **Image input**: most models accept images — paste or drop one straight into the conversation (GLM-5.3-Flash, GLM-5.2, the DeepSeek-V4 series, and more); the few text-only models (e.g. GLM-5.1) clearly say so.
|
|
14
|
+
|
|
15
|
+
- **Thinking effort**: the model picker lets you switch the thinking effort on models that support it — GLM-5.3 offers low / high / xhigh and GLM-5.3-Flash offers low / high / max. Models without the option cannot be adjusted and use WorkBuddy's default.
|
|
16
|
+
|
|
17
|
+
- **Promo badges**: promo badges (`限时免费`, `夜间折扣`) ride the model name itself (e.g. `Hy4 preview · x0.00 · 限时免费`), visible wherever you pick a model; the status card also collects currently-discounted models. Per the WorkBuddy service data, synced each time DSH starts.
|
|
18
|
+
|
|
19
|
+
- **Rate**: every model name carries its credits multiplier (e.g. `GLM-5.2 · x0.79`, `Hy3 · x0.00`) in both the `/model` popup and the composer's model dropdown. The rate is display-only and never affects requests.
|
|
14
20
|
|
|
15
21
|
- **Info at a glance**: Settings → Plugins → DSH WorkBuddy Connect card
|
|
16
22
|
|
|
@@ -24,6 +30,16 @@ Expand the card to see the account, token validity, and remaining credit.
|
|
|
24
30
|
|
|
25
31
|
Prerequisite: the WorkBuddy desktop app is installed and signed in (the plugin reuses the app's sign-in state and follows account switches automatically).
|
|
26
32
|
|
|
33
|
+
**Match the plugin version to your DSH core** — a mismatched combination fails to start DSH:
|
|
34
|
+
|
|
35
|
+
| Plugin | Required DSH core | Desktop app |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| **0.3.0+** | `0.1.2-rc.1` or newer | `2.0.5`+ recommended |
|
|
38
|
+
| **0.2.6** | `0.1.1-rc.2` (older line) | `2.0.3` / `2.0.4` |
|
|
39
|
+
|
|
40
|
+
- On DSH `0.1.2-rc.1` or newer, just install the latest: `dsh plugin --profile web add dsh-workbuddy-connect`
|
|
41
|
+
- Still on DSH `0.1.1-rc.2`? Stay on the older release: `dsh plugin --profile web add dsh-workbuddy-connect@0.2.6`
|
|
42
|
+
|
|
27
43
|
The plugin runs under all three DSH interfaces: **Web**, **Desktop**, and **TUI**. Pick the install command that matches the profile you use.
|
|
28
44
|
|
|
29
45
|
```sh
|
|
@@ -48,7 +64,9 @@ dsh plugin --profile dsh-tui add dsh-workbuddy-connect
|
|
|
48
64
|
dsh --profile dsh-tui
|
|
49
65
|
```
|
|
50
66
|
|
|
51
|
-
>
|
|
67
|
+
> **TUI users should stay on 0.2.6**: in our testing, installing this plugin 0.3.0 on the TUI profile crashes startup with `events is not iterable` — the terminal UI package (`@deepseek-harness-tui/dsh-tui`) has not been adapted to the new DSH core yet (the fix is committed upstream, unreleased). We recommend TUI users keep DSH `0.1.1-rc.2` and plugin `0.2.6` for now, and upgrade to 0.3.0 once the terminal UI ships an adapted release.
|
|
68
|
+
|
|
69
|
+
> Note: the `dsh-tui` profile requires pnpm 11 to install packages (a different pnpm on PATH fails with `ERR_PNPM_UNEXPECTED_STORE` — use `npx pnpm@11`).
|
|
52
70
|
|
|
53
71
|
After installing, switch to a WorkBuddy model in the model picker of the interface you chose. On Web, the settings card (Settings → Plugins → DSH WorkBuddy Connect) shows the account, token validity, and remaining credit; on TUI, configure `authFile` in `/settings`.
|
|
54
72
|
|
|
@@ -58,7 +76,7 @@ After installing, switch to a WorkBuddy model in the model picker of the interfa
|
|
|
58
76
|
|
|
59
77
|
## Known limitations
|
|
60
78
|
|
|
61
|
-
- Verified on macOS with the DSH Web / Desktop
|
|
79
|
+
- Verified on macOS with the DSH Web / Desktop profiles (`0.1.2-rc.1`+, Node 22+); TUI pending a terminal-UI release adapted to 0.1.2 (see the Install section). Windows probes Local and Roaming AppData in order; WSL first reads credentials from the mounted Windows user profile. If the Windows and Linux user names differ and Windows environment variables are not forwarded into WSL, point `WORKBUDDY_AUTH_FILE` at the actual file.
|
|
62
80
|
- Relies on WorkBuddy client interfaces (not a public API); the plugin may need updates as WorkBuddy changes.
|
|
63
81
|
|
|
64
82
|
## Disclaimer
|
package/README.md
CHANGED
|
@@ -15,7 +15,16 @@
|
|
|
15
15
|

|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
-
|
|
18
|
+
- **图片输入**:大部分模型支持发图,在对话里直接粘贴或拖入图片即可(GLM-5.3-Flash、GLM-5.2、DeepSeek-V4 系列等);少数只支持文字的模型(如 GLM-5.1)会明确提示不支持。
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
- **思考强度**:模型选择器里可为支持的模型切换思考强度,例如 GLM-5.3 可选 low / high / xhigh,GLM-5.3-Flash 可选 low / high / max;没有出现选项的模型不支持调整,使用 WorkBuddy 的默认档位。
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
- **徽章展示**:促销徽章(限时免费、夜间折扣)直接跟在模型名后面(如 `Hy4 preview · x0.00 · 限时免费`),选模型时一眼可见;设置卡片里也会汇总当前有优惠的模型。以 WorkBuddy 服务端的数据为准,每次启动 DSH 时同步。
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
- **费率比例**:模型选择列表里每个模型名后直接显示积分倍率(如 `GLM-5.2 · x0.79`、`Hy3 · x0.00`),`/model` 弹窗与输入框的模型下拉都能看到。倍率只是显示,不影响实际请求。
|
|
19
28
|
|
|
20
29
|
|
|
21
30
|
- **信息查看**:设置 → 插件 → DSH WorkBuddy Connect 卡片
|
|
@@ -31,6 +40,16 @@
|
|
|
31
40
|
|
|
32
41
|
前置:已安装并登录 WorkBuddy 桌面 App(插件复用 App 的登录状态,账号切换自动跟随)。
|
|
33
42
|
|
|
43
|
+
**版本对应(重要)**:本插件与 DSH 核心版本一一对应,不可混用——不匹配的组合会导致 DSH 启动失败:
|
|
44
|
+
|
|
45
|
+
| 插件版本 | 要求的 DSH 核心 | 桌面 App |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| **0.3.0+** | `0.1.2-rc.1` 及以上 | 建议 `2.0.5`+ |
|
|
48
|
+
| **0.2.6** | `0.1.1-rc.2`(旧线) | `2.0.3` / `2.0.4` |
|
|
49
|
+
|
|
50
|
+
- DSH `0.1.2-rc.1` 及以上的用户,正常安装最新版即可:`dsh plugin --profile web add dsh-workbuddy-connect`
|
|
51
|
+
- 还在用 DSH `0.1.1-rc.2` 的用户,请安装旧版本并停留在 `0.2.6`:`dsh plugin --profile web add dsh-workbuddy-connect@0.2.6`
|
|
52
|
+
|
|
34
53
|
插件在三种 DSH 界面下均可运行:**Web**、**Desktop**、**TUI**。根据你使用的 profile 选对应命令安装。
|
|
35
54
|
|
|
36
55
|
```sh
|
|
@@ -55,7 +74,9 @@ dsh plugin --profile dsh-tui add dsh-workbuddy-connect
|
|
|
55
74
|
dsh --profile dsh-tui
|
|
56
75
|
```
|
|
57
76
|
|
|
58
|
-
>
|
|
77
|
+
> **TUI 用户请先留在 0.2.6**:实测在 TUI 上安装本插件 0.3.0 会导致启动崩溃(报 `events is not iterable`),原因是终端界面插件 `@deepseek-harness-tui/dsh-tui` 还没适配 DSH 新核心(修复已提交,尚未发版)。建议 TUI 用户暂时继续使用 DSH `0.1.1-rc.2` 和本插件 `0.2.6`,等终端界面插件发布适配版本后再升级 0.3.0。
|
|
78
|
+
|
|
79
|
+
> 提示:`dsh-tui` profile 需用 pnpm 11 安装(PATH 里是其他版本会报 `ERR_PNPM_UNEXPECTED_STORE`,用 `npx pnpm@11` 即可)。
|
|
59
80
|
|
|
60
81
|
安装后,在对应界面的模型选择器里切换到 WorkBuddy 模型即可使用;Web 下设置卡片(设置 → 插件 → DSH WorkBuddy Connect)可查看账号信息、令牌有效期与剩余积分,TUI 下可在 `/settings` 里配置 `authFile`。
|
|
61
82
|
|
|
@@ -65,7 +86,7 @@ dsh --profile dsh-tui
|
|
|
65
86
|
|
|
66
87
|
## 已知限制
|
|
67
88
|
|
|
68
|
-
- 在 macOS 的 DSH Web / Desktop
|
|
89
|
+
- 在 macOS 的 DSH Web / Desktop profile(`0.1.2-rc.1`+、Node 22+)下验证通过;TUI 待终端界面插件发布 0.1.2 适配版后验证(见安装章节说明)。Windows 会依次探测 Local 与 Roaming AppData;WSL 会优先从挂载的 Windows 用户目录读取登录凭据。若 Windows 与 Linux 用户名不同且 Windows 环境变量未传入 WSL,请通过 `WORKBUDDY_AUTH_FILE` 指定实际位置。
|
|
69
90
|
- 依赖 WorkBuddy 客户端接口(非官方开放 API),WorkBuddy 更新后插件可能需要随之调整。
|
|
70
91
|
|
|
71
92
|
## 免责声明
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { S as workbuddyOwnAuthPath, a as readHostHeartbeat, c as WORKBUDDY_CONNECT_VERSION, l as WorkBuddyUpstreamClient, m as FALLBACK_WORKBUDDY_MODELS, o as workbuddyHostHeartbeatPath, r as isHeartbeatProcessAlive, v as WorkBuddyCredentialStore } from "./host-heartbeat-CM7AW7vZ.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
|
@@ -124,6 +124,36 @@ window.__ModuleLoader__.load({
|
|
|
124
124
|
lineHeight: "20px",
|
|
125
125
|
color: "var(--dsw-alias-label-secondary)"
|
|
126
126
|
};
|
|
127
|
+
const modelBadgeStyle = {
|
|
128
|
+
display: "flex",
|
|
129
|
+
alignItems: "center",
|
|
130
|
+
gap: 6,
|
|
131
|
+
flexWrap: "wrap"
|
|
132
|
+
};
|
|
133
|
+
const modelOfferStyle = {
|
|
134
|
+
display: "flex",
|
|
135
|
+
flexDirection: "column",
|
|
136
|
+
gap: 2
|
|
137
|
+
};
|
|
138
|
+
const modelRateStyle = {
|
|
139
|
+
fontSize: 12,
|
|
140
|
+
lineHeight: "18px",
|
|
141
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
142
|
+
};
|
|
143
|
+
const modelBadgeChipStyle = {
|
|
144
|
+
padding: "1px 8px",
|
|
145
|
+
borderRadius: 999,
|
|
146
|
+
fontSize: 11,
|
|
147
|
+
lineHeight: "18px",
|
|
148
|
+
background: "var(--dsw-alias-state-success-subtle, rgba(34, 160, 107, 0.12))",
|
|
149
|
+
color: "var(--dsw-alias-state-success-primary, #22a06b)"
|
|
150
|
+
};
|
|
151
|
+
/** Localize an upstream promotional badge label, with an unknown-badge fallback. */
|
|
152
|
+
function modelBadgeLabel(badge, t) {
|
|
153
|
+
if (badge === "限时免费") return t("badgeLimitedFree");
|
|
154
|
+
if (badge === "夜间折扣") return t("badgeNightDiscount");
|
|
155
|
+
return badge;
|
|
156
|
+
}
|
|
127
157
|
const progressTrackStyle = {
|
|
128
158
|
height: 8,
|
|
129
159
|
overflow: "hidden",
|
|
@@ -187,6 +217,34 @@ window.__ModuleLoader__.load({
|
|
|
187
217
|
]
|
|
188
218
|
});
|
|
189
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* One model offer row: name, promotional badges, and the billing rate.
|
|
222
|
+
*
|
|
223
|
+
* The rate sits under the name rather than beside it because the row already
|
|
224
|
+
* spends its horizontal budget on badges; stacking keeps long model names and
|
|
225
|
+
* several badges from squeezing the rate into an ellipsis.
|
|
226
|
+
*/
|
|
227
|
+
function ModelOfferRow({ model, t }) {
|
|
228
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
229
|
+
style: modelOfferStyle,
|
|
230
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
231
|
+
style: quotaLabelStyle,
|
|
232
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
233
|
+
style: modelBadgeStyle,
|
|
234
|
+
children: [model.badges?.map((badge) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
235
|
+
style: modelBadgeChipStyle,
|
|
236
|
+
children: modelBadgeLabel(badge, t)
|
|
237
|
+
}, badge)), model.free === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
238
|
+
style: modelBadgeChipStyle,
|
|
239
|
+
children: t("freeModel")
|
|
240
|
+
}) : null]
|
|
241
|
+
})]
|
|
242
|
+
}), model.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
243
|
+
style: modelRateStyle,
|
|
244
|
+
children: t("rate", { rate: model.credits })
|
|
245
|
+
})]
|
|
246
|
+
});
|
|
247
|
+
}
|
|
190
248
|
/** Render WorkBuddy sign-in state and credit as one expandable card. */
|
|
191
249
|
function WorkBuddyPluginCard({ t }) {
|
|
192
250
|
if (t === void 0) throw new Error("WorkBuddy plugin card requires its translation function");
|
|
@@ -329,6 +387,16 @@ window.__ModuleLoader__.load({
|
|
|
329
387
|
status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
330
388
|
style: errorStyle,
|
|
331
389
|
children: t("creditsError", { message: status.creditsError })
|
|
390
|
+
}),
|
|
391
|
+
status.models === void 0 || status.models.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
392
|
+
style: quotaListStyle,
|
|
393
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
394
|
+
style: quotaTitleStyle,
|
|
395
|
+
children: t("modelsHeading")
|
|
396
|
+
}), status.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelOfferRow, {
|
|
397
|
+
model,
|
|
398
|
+
t
|
|
399
|
+
}, model.id))]
|
|
332
400
|
})
|
|
333
401
|
] }) : null,
|
|
334
402
|
status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -365,7 +433,12 @@ window.__ModuleLoader__.load({
|
|
|
365
433
|
refresh: "Refresh",
|
|
366
434
|
refreshing: "Refreshing…",
|
|
367
435
|
requestFailed: "Request failed",
|
|
368
|
-
accountHeading: "Account"
|
|
436
|
+
accountHeading: "Account",
|
|
437
|
+
modelsHeading: "Model offers",
|
|
438
|
+
freeModel: "Free",
|
|
439
|
+
badgeLimitedFree: "Limited-time free",
|
|
440
|
+
badgeNightDiscount: "Night discount",
|
|
441
|
+
rate: "{rate} credits per message"
|
|
369
442
|
};
|
|
370
443
|
const zh = {
|
|
371
444
|
title: "DSH WorkBuddy Connect",
|
|
@@ -386,13 +459,29 @@ window.__ModuleLoader__.load({
|
|
|
386
459
|
refresh: "刷新",
|
|
387
460
|
refreshing: "正在刷新…",
|
|
388
461
|
requestFailed: "请求失败",
|
|
389
|
-
accountHeading: "账号"
|
|
462
|
+
accountHeading: "账号",
|
|
463
|
+
modelsHeading: "模型优惠",
|
|
464
|
+
freeModel: "免费",
|
|
465
|
+
badgeLimitedFree: "限时免费",
|
|
466
|
+
badgeNightDiscount: "夜间折扣",
|
|
467
|
+
rate: "{rate} 积分/次"
|
|
390
468
|
};
|
|
391
469
|
//#endregion
|
|
392
470
|
//#region src/client/index.tsx
|
|
393
471
|
/** Stable browser-plugin name. */
|
|
394
472
|
const name = "dsh-workbuddy-connect-client";
|
|
395
|
-
/**
|
|
473
|
+
/**
|
|
474
|
+
* Client services required by the Plugin configuration contribution.
|
|
475
|
+
*
|
|
476
|
+
* DSH 0.1.2 removed `@deepseek-ai/dsh-client-runtime` (the package that used to
|
|
477
|
+
* hold the browser `ClientContext` alias and the `slots` service). The services
|
|
478
|
+
* this card relies on now come from narrower packages: the `slots` registry
|
|
479
|
+
* moved to `@deepseek-ai/dsh-client-ui-renderer`, `locale` stayed in
|
|
480
|
+
* `@deepseek-ai/dsh-client-locale`, and the `settings.plugin.item` slot is
|
|
481
|
+
* declared by `@deepseek-ai/dsh-client-ui-settings-plugins`. All three are
|
|
482
|
+
* named in the package's `dsh.client.inject` list, so cordis has activated
|
|
483
|
+
* them before this plugin's fiber starts.
|
|
484
|
+
*/
|
|
396
485
|
const inject = ["slots", "locale"];
|
|
397
486
|
/**
|
|
398
487
|
* Register card copy and the WorkBuddy card under Plugin configuration.
|
|
@@ -314,10 +314,16 @@ var WorkBuddyCredentialStore = class {
|
|
|
314
314
|
//#endregion
|
|
315
315
|
//#region src/catalog.ts
|
|
316
316
|
/**
|
|
317
|
-
* Static CLI models observed on the CN endpoint (
|
|
318
|
-
*
|
|
319
|
-
* replaces this list at startup; it exists so the provider
|
|
320
|
-
* usable catalog even while the first fetch is in flight or
|
|
317
|
+
* Static CLI models observed on the CN endpoint (re-verified against the live
|
|
318
|
+
* catalog 2026-09-01, including the thinking-effort and billing metadata). The
|
|
319
|
+
* upstream refresh replaces this list at startup; it exists so the provider
|
|
320
|
+
* registers with a usable catalog even while the first fetch is in flight or
|
|
321
|
+
* offline.
|
|
322
|
+
*
|
|
323
|
+
* The list tracks the `cli` agent's model roster exactly: the 15 models the
|
|
324
|
+
* desktop CLI offers. Reasoning metadata is taken verbatim from the live
|
|
325
|
+
* endpoint — each model's supported effort set and whether thinking can be
|
|
326
|
+
* disabled — and the `free` flag follows the upstream `x0.00` credits marker.
|
|
321
327
|
*/
|
|
322
328
|
const FALLBACK_WORKBUDDY_MODELS = [
|
|
323
329
|
{
|
|
@@ -325,77 +331,267 @@ const FALLBACK_WORKBUDDY_MODELS = [
|
|
|
325
331
|
name: "Auto",
|
|
326
332
|
contextWindow: 168e3,
|
|
327
333
|
maxTokens: 32e3,
|
|
328
|
-
supportsImages: true
|
|
334
|
+
supportsImages: true,
|
|
335
|
+
reasoning: {
|
|
336
|
+
supports: true,
|
|
337
|
+
onlyReasoning: true,
|
|
338
|
+
defaultEffort: "high",
|
|
339
|
+
canDisableThinking: false
|
|
340
|
+
},
|
|
341
|
+
billing: { free: false }
|
|
329
342
|
},
|
|
330
343
|
{
|
|
331
344
|
id: "hy3",
|
|
332
345
|
name: "Hy3",
|
|
333
346
|
contextWindow: 192e3,
|
|
334
347
|
maxTokens: 64e3,
|
|
335
|
-
supportsImages: true
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
348
|
+
supportsImages: true,
|
|
349
|
+
reasoning: {
|
|
350
|
+
supports: true,
|
|
351
|
+
onlyReasoning: true,
|
|
352
|
+
defaultEffort: "high",
|
|
353
|
+
canDisableThinking: false
|
|
354
|
+
},
|
|
355
|
+
billing: {
|
|
356
|
+
credits: "x0.00",
|
|
357
|
+
badges: ["限时免费"],
|
|
358
|
+
free: true
|
|
359
|
+
}
|
|
343
360
|
},
|
|
344
361
|
{
|
|
345
362
|
id: "glm-5.2",
|
|
346
363
|
name: "GLM-5.2",
|
|
347
364
|
contextWindow: 1e6,
|
|
348
365
|
maxTokens: 48e3,
|
|
349
|
-
supportsImages: true
|
|
366
|
+
supportsImages: true,
|
|
367
|
+
reasoning: {
|
|
368
|
+
supports: true,
|
|
369
|
+
onlyReasoning: true,
|
|
370
|
+
defaultEffort: "medium",
|
|
371
|
+
canDisableThinking: false
|
|
372
|
+
},
|
|
373
|
+
billing: {
|
|
374
|
+
credits: "x0.79 credits",
|
|
375
|
+
badges: ["夜间折扣"],
|
|
376
|
+
free: false
|
|
377
|
+
}
|
|
350
378
|
},
|
|
351
379
|
{
|
|
352
380
|
id: "glm-5.1",
|
|
353
381
|
name: "GLM-5.1",
|
|
354
382
|
contextWindow: 2e5,
|
|
355
383
|
maxTokens: 48e3,
|
|
356
|
-
supportsImages: false
|
|
384
|
+
supportsImages: false,
|
|
385
|
+
reasoning: {
|
|
386
|
+
supports: true,
|
|
387
|
+
onlyReasoning: true,
|
|
388
|
+
defaultEffort: "medium",
|
|
389
|
+
canDisableThinking: false
|
|
390
|
+
},
|
|
391
|
+
billing: {
|
|
392
|
+
credits: "x0.79 credits",
|
|
393
|
+
free: false
|
|
394
|
+
}
|
|
357
395
|
},
|
|
358
396
|
{
|
|
359
|
-
id: "
|
|
360
|
-
name: "
|
|
361
|
-
contextWindow:
|
|
362
|
-
maxTokens:
|
|
363
|
-
supportsImages: true
|
|
397
|
+
id: "glm-5v-turbo",
|
|
398
|
+
name: "GLM-5v-Turbo",
|
|
399
|
+
contextWindow: 2e5,
|
|
400
|
+
maxTokens: 64e3,
|
|
401
|
+
supportsImages: true,
|
|
402
|
+
reasoning: {
|
|
403
|
+
supports: true,
|
|
404
|
+
onlyReasoning: true,
|
|
405
|
+
defaultEffort: "medium",
|
|
406
|
+
canDisableThinking: false
|
|
407
|
+
},
|
|
408
|
+
billing: {
|
|
409
|
+
credits: "x0.71 credits",
|
|
410
|
+
free: false
|
|
411
|
+
}
|
|
364
412
|
},
|
|
365
413
|
{
|
|
366
414
|
id: "kimi-k3-1",
|
|
367
415
|
name: "Kimi-K3",
|
|
368
416
|
contextWindow: 1e6,
|
|
369
417
|
maxTokens: 32e3,
|
|
370
|
-
supportsImages: true
|
|
418
|
+
supportsImages: true,
|
|
419
|
+
reasoning: {
|
|
420
|
+
supports: true,
|
|
421
|
+
onlyReasoning: true,
|
|
422
|
+
defaultEffort: "medium",
|
|
423
|
+
canDisableThinking: false
|
|
424
|
+
},
|
|
425
|
+
billing: {
|
|
426
|
+
credits: "x1.62 credits",
|
|
427
|
+
free: false
|
|
428
|
+
}
|
|
371
429
|
},
|
|
372
430
|
{
|
|
373
431
|
id: "kimi-k2.7",
|
|
374
432
|
name: "Kimi-K2.7-Code",
|
|
375
433
|
contextWindow: 256e3,
|
|
376
434
|
maxTokens: 32e3,
|
|
377
|
-
supportsImages: true
|
|
435
|
+
supportsImages: true,
|
|
436
|
+
reasoning: {
|
|
437
|
+
supports: true,
|
|
438
|
+
onlyReasoning: true,
|
|
439
|
+
defaultEffort: "medium",
|
|
440
|
+
canDisableThinking: false
|
|
441
|
+
},
|
|
442
|
+
billing: {
|
|
443
|
+
credits: "x0.57 credits",
|
|
444
|
+
free: false
|
|
445
|
+
}
|
|
378
446
|
},
|
|
379
447
|
{
|
|
380
448
|
id: "kimi-k2.6",
|
|
381
449
|
name: "Kimi-K2.6",
|
|
382
450
|
contextWindow: 256e3,
|
|
383
451
|
maxTokens: 32e3,
|
|
384
|
-
supportsImages: true
|
|
452
|
+
supportsImages: true,
|
|
453
|
+
reasoning: {
|
|
454
|
+
supports: true,
|
|
455
|
+
onlyReasoning: true,
|
|
456
|
+
defaultEffort: "medium",
|
|
457
|
+
canDisableThinking: false
|
|
458
|
+
},
|
|
459
|
+
billing: {
|
|
460
|
+
credits: "x0.52 credits",
|
|
461
|
+
free: false
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "minimax-m3",
|
|
466
|
+
name: "MiniMax-M3",
|
|
467
|
+
contextWindow: 512e3,
|
|
468
|
+
maxTokens: 128e3,
|
|
469
|
+
supportsImages: true,
|
|
470
|
+
reasoning: {
|
|
471
|
+
supports: true,
|
|
472
|
+
onlyReasoning: true,
|
|
473
|
+
defaultEffort: "medium",
|
|
474
|
+
canDisableThinking: false
|
|
475
|
+
},
|
|
476
|
+
billing: {
|
|
477
|
+
credits: "x0.25 credits",
|
|
478
|
+
free: false
|
|
479
|
+
}
|
|
385
480
|
},
|
|
386
481
|
{
|
|
387
482
|
id: "deepseek-v4-flash",
|
|
388
483
|
name: "Deepseek-V4-Flash",
|
|
389
484
|
contextWindow: 1e6,
|
|
390
485
|
maxTokens: 5e4,
|
|
391
|
-
supportsImages: true
|
|
486
|
+
supportsImages: true,
|
|
487
|
+
reasoning: {
|
|
488
|
+
supports: true,
|
|
489
|
+
onlyReasoning: true,
|
|
490
|
+
defaultEffort: "high",
|
|
491
|
+
canDisableThinking: false
|
|
492
|
+
},
|
|
493
|
+
billing: {
|
|
494
|
+
credits: "x0.17 credits",
|
|
495
|
+
free: false
|
|
496
|
+
}
|
|
392
497
|
},
|
|
393
498
|
{
|
|
394
499
|
id: "deepseek-v4-pro",
|
|
395
500
|
name: "Deepseek-V4-Pro",
|
|
396
501
|
contextWindow: 1e6,
|
|
397
502
|
maxTokens: 5e4,
|
|
398
|
-
supportsImages: true
|
|
503
|
+
supportsImages: true,
|
|
504
|
+
reasoning: {
|
|
505
|
+
supports: true,
|
|
506
|
+
onlyReasoning: true,
|
|
507
|
+
defaultEffort: "high",
|
|
508
|
+
canDisableThinking: false
|
|
509
|
+
},
|
|
510
|
+
billing: {
|
|
511
|
+
credits: "x0.51 credits",
|
|
512
|
+
free: false
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
id: "hy4-preview",
|
|
517
|
+
name: "Hy4 preview",
|
|
518
|
+
contextWindow: 1e6,
|
|
519
|
+
maxTokens: 64e3,
|
|
520
|
+
supportsImages: true,
|
|
521
|
+
reasoning: {
|
|
522
|
+
supports: true,
|
|
523
|
+
onlyReasoning: true,
|
|
524
|
+
supportedEfforts: ["high"],
|
|
525
|
+
defaultEffort: "high",
|
|
526
|
+
canDisableThinking: false
|
|
527
|
+
},
|
|
528
|
+
billing: {
|
|
529
|
+
credits: "x0.00",
|
|
530
|
+
badges: ["限时免费"],
|
|
531
|
+
free: true
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
id: "hy3-x",
|
|
536
|
+
name: "Hy3",
|
|
537
|
+
contextWindow: 192e3,
|
|
538
|
+
maxTokens: 64e3,
|
|
539
|
+
supportsImages: true,
|
|
540
|
+
reasoning: {
|
|
541
|
+
supports: true,
|
|
542
|
+
onlyReasoning: true,
|
|
543
|
+
supportedEfforts: ["low", "high"],
|
|
544
|
+
defaultEffort: "high",
|
|
545
|
+
canDisableThinking: false
|
|
546
|
+
},
|
|
547
|
+
billing: {
|
|
548
|
+
credits: "x0.05",
|
|
549
|
+
free: false
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: "glm-5.3",
|
|
554
|
+
name: "GLM-5.3",
|
|
555
|
+
contextWindow: 1e6,
|
|
556
|
+
maxTokens: 48e3,
|
|
557
|
+
supportsImages: true,
|
|
558
|
+
reasoning: {
|
|
559
|
+
supports: true,
|
|
560
|
+
onlyReasoning: true,
|
|
561
|
+
supportedEfforts: [
|
|
562
|
+
"low",
|
|
563
|
+
"high",
|
|
564
|
+
"xhigh"
|
|
565
|
+
],
|
|
566
|
+
defaultEffort: "high",
|
|
567
|
+
canDisableThinking: true
|
|
568
|
+
},
|
|
569
|
+
billing: {
|
|
570
|
+
credits: "x0.79",
|
|
571
|
+
free: false
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
id: "glm-5.3-flash",
|
|
576
|
+
name: "GLM-5.3-Flash",
|
|
577
|
+
contextWindow: 1e6,
|
|
578
|
+
maxTokens: 32e3,
|
|
579
|
+
supportsImages: true,
|
|
580
|
+
reasoning: {
|
|
581
|
+
supports: true,
|
|
582
|
+
onlyReasoning: true,
|
|
583
|
+
supportedEfforts: [
|
|
584
|
+
"low",
|
|
585
|
+
"high",
|
|
586
|
+
"max"
|
|
587
|
+
],
|
|
588
|
+
defaultEffort: "high",
|
|
589
|
+
canDisableThinking: true
|
|
590
|
+
},
|
|
591
|
+
billing: {
|
|
592
|
+
credits: "x0.06",
|
|
593
|
+
free: false
|
|
594
|
+
}
|
|
399
595
|
}
|
|
400
596
|
];
|
|
401
597
|
/** Mutable catalog shared by the shim's `/v1/models` and the adapter. */
|
|
@@ -436,6 +632,84 @@ const HARD_CREDIT_MARKERS = [
|
|
|
436
632
|
"额度用尽",
|
|
437
633
|
"没有积分"
|
|
438
634
|
];
|
|
635
|
+
/** The concrete effort spellings WorkBuddy exposes on the wire. */
|
|
636
|
+
const EFFORT_VALUES = [
|
|
637
|
+
"low",
|
|
638
|
+
"medium",
|
|
639
|
+
"high",
|
|
640
|
+
"xhigh",
|
|
641
|
+
"max"
|
|
642
|
+
];
|
|
643
|
+
/** Promotional badge keys the upstream tags carry, minus their color suffix. */
|
|
644
|
+
const BADGE_PREFIX = "badge:";
|
|
645
|
+
/** Parse the upstream `reasoning` object into {@link WorkBuddyModelReasoning}. */
|
|
646
|
+
function resolveUpstreamReasoning(wrapped) {
|
|
647
|
+
const supports = wrapped["supportsReasoning"] === true;
|
|
648
|
+
const onlyReasoning = wrapped["onlyReasoning"] === true;
|
|
649
|
+
const rawReasoning = wrapped["reasoning"];
|
|
650
|
+
let supportedEfforts;
|
|
651
|
+
let defaultEffort;
|
|
652
|
+
let canDisableThinking = true;
|
|
653
|
+
if (typeof rawReasoning === "object" && rawReasoning !== null && !Array.isArray(rawReasoning)) {
|
|
654
|
+
const reasoning = rawReasoning;
|
|
655
|
+
const rawEfforts = reasoning["supportedEfforts"];
|
|
656
|
+
if (Array.isArray(rawEfforts)) {
|
|
657
|
+
const efforts = rawEfforts.filter((value) => typeof value === "string" && EFFORT_VALUES.includes(value));
|
|
658
|
+
if (efforts.length > 0) supportedEfforts = efforts;
|
|
659
|
+
}
|
|
660
|
+
if (typeof reasoning["defaultEffort"] === "string" && EFFORT_VALUES.includes(reasoning["defaultEffort"])) defaultEffort = reasoning["defaultEffort"];
|
|
661
|
+
else if (typeof reasoning["effort"] === "string" && EFFORT_VALUES.includes(reasoning["effort"])) defaultEffort = reasoning["effort"];
|
|
662
|
+
canDisableThinking = reasoning["canDisableThinking"] === true;
|
|
663
|
+
}
|
|
664
|
+
return { reasoning: {
|
|
665
|
+
supports,
|
|
666
|
+
onlyReasoning,
|
|
667
|
+
...supportedEfforts === void 0 ? {} : { supportedEfforts },
|
|
668
|
+
...defaultEffort === void 0 ? {} : { defaultEffort },
|
|
669
|
+
canDisableThinking
|
|
670
|
+
} };
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Reduce an upstream credits string to its language-neutral display form.
|
|
674
|
+
*
|
|
675
|
+
* The host LLM seam carries this text to the browser, and the host has no
|
|
676
|
+
* locale service — whatever string is produced here is shown verbatim in every
|
|
677
|
+
* UI language. The upstream is inconsistent in a way that matters: some catalog
|
|
678
|
+
* rows report a bare multiplier (`x0.79`) and others append a unit word
|
|
679
|
+
* (`x0.79 credits`), and the unit word would pin the display to English.
|
|
680
|
+
* Dropping a trailing `credits` (case-insensitive, singular or plural) yields
|
|
681
|
+
* the one spelling that reads identically in every language.
|
|
682
|
+
*
|
|
683
|
+
* @param credits - raw upstream credits string, e.g. `"x0.79 credits"`.
|
|
684
|
+
* @returns the bare multiplier, or undefined when nothing displayable remains.
|
|
685
|
+
*/
|
|
686
|
+
function normalizeCredits(credits) {
|
|
687
|
+
if (credits === void 0) return void 0;
|
|
688
|
+
const trimmed = credits.trim();
|
|
689
|
+
if (trimmed === "") return void 0;
|
|
690
|
+
if (/^credits?$/iu.test(trimmed)) return void 0;
|
|
691
|
+
const bare = trimmed.replace(/\s+credits?$/iu, "").trim();
|
|
692
|
+
return bare === "" ? void 0 : bare;
|
|
693
|
+
}
|
|
694
|
+
/** Parse the upstream `tags` / `credits` fields into billing metadata. */
|
|
695
|
+
function resolveUpstreamBilling(wrapped) {
|
|
696
|
+
const rawCredits = wrapped["credits"];
|
|
697
|
+
const credits = typeof rawCredits === "string" && rawCredits.trim() !== "" ? rawCredits.trim() : void 0;
|
|
698
|
+
const badges = [];
|
|
699
|
+
const rawTags = wrapped["tags"];
|
|
700
|
+
if (Array.isArray(rawTags)) for (const tag of rawTags) {
|
|
701
|
+
if (typeof tag !== "string") continue;
|
|
702
|
+
if (!tag.toLowerCase().startsWith(BADGE_PREFIX)) continue;
|
|
703
|
+
const label = tag.slice(6).split(":")[0] ?? tag.slice(6);
|
|
704
|
+
if (label !== "") badges.push(label);
|
|
705
|
+
}
|
|
706
|
+
const free = credits !== void 0 && /^x?0\.0+$/u.test(credits);
|
|
707
|
+
return { billing: {
|
|
708
|
+
...credits === void 0 ? {} : { credits },
|
|
709
|
+
...badges.length === 0 ? {} : { badges },
|
|
710
|
+
free
|
|
711
|
+
} };
|
|
712
|
+
}
|
|
439
713
|
/** Session-invalidation markers that mean "sign in again in the WorkBuddy app". */
|
|
440
714
|
const SESSION_DEAD_MARKERS = ["Offline user session not found", "12153"];
|
|
441
715
|
/** Classify an upstream failure from its HTTP status and body excerpt. */
|
|
@@ -513,8 +787,15 @@ function billingHeaders(credential) {
|
|
|
513
787
|
}
|
|
514
788
|
/**
|
|
515
789
|
* Normalize an OpenAI chat-completions body for the WorkBuddy upstream:
|
|
516
|
-
* force `stream: true` (the upstream rejects non-streaming)
|
|
517
|
-
* `tool_choice` (the upstream's field is a string; object forms return 400)
|
|
790
|
+
* force `stream: true` (the upstream rejects non-streaming), flatten
|
|
791
|
+
* `tool_choice` (the upstream's field is a string; object forms return 400),
|
|
792
|
+
* and rewrite `developer` messages as `system`.
|
|
793
|
+
*
|
|
794
|
+
* The `developer` rewrite is load-bearing: pi-ai emits the system prompt as
|
|
795
|
+
* `role: "developer"` (the OpenAI convention it adopted), but the WorkBuddy
|
|
796
|
+
* upstream rejects that role with HTTP 400 code 11128 ("Illegal API
|
|
797
|
+
* invocation from an unapproved channel"). Rewriting to `system` is the
|
|
798
|
+
* compatible spelling the upstream accepts.
|
|
518
799
|
*/
|
|
519
800
|
function prepareChatBody(source) {
|
|
520
801
|
let body;
|
|
@@ -526,9 +807,20 @@ function prepareChatBody(source) {
|
|
|
526
807
|
if (typeof body !== "object" || body === null || Array.isArray(body)) return source;
|
|
527
808
|
const obj = body;
|
|
528
809
|
obj["stream"] = true;
|
|
810
|
+
normalizeDeveloperRole(obj);
|
|
529
811
|
normalizeToolChoice(obj);
|
|
530
812
|
return JSON.stringify(obj);
|
|
531
813
|
}
|
|
814
|
+
/** Rewrite `role: "developer"` messages to `role: "system"` (upstream rejects developer). */
|
|
815
|
+
function normalizeDeveloperRole(obj) {
|
|
816
|
+
const messages = obj["messages"];
|
|
817
|
+
if (!Array.isArray(messages)) return;
|
|
818
|
+
for (const message of messages) {
|
|
819
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) continue;
|
|
820
|
+
const wrapped = message;
|
|
821
|
+
if (wrapped["role"] === "developer") wrapped["role"] = "system";
|
|
822
|
+
}
|
|
823
|
+
}
|
|
532
824
|
/** Rewrite OpenAI `tool_choice` spellings into the upstream's string form. */
|
|
533
825
|
function normalizeToolChoice(obj) {
|
|
534
826
|
const suppress = () => {
|
|
@@ -679,7 +971,9 @@ var WorkBuddyUpstreamClient = class {
|
|
|
679
971
|
name: typeof wrapped["name"] === "string" && wrapped["name"] !== "" ? wrapped["name"] : id,
|
|
680
972
|
contextWindow: input,
|
|
681
973
|
maxTokens: output,
|
|
682
|
-
supportsImages: wrapped["supportsImages"] === true && wrapped["disabledMultimodal"] !== true
|
|
974
|
+
supportsImages: wrapped["supportsImages"] === true && wrapped["disabledMultimodal"] !== true,
|
|
975
|
+
...resolveUpstreamReasoning(wrapped),
|
|
976
|
+
...resolveUpstreamBilling(wrapped)
|
|
683
977
|
});
|
|
684
978
|
}
|
|
685
979
|
const models = cliIds.map((id) => byId.get(id)).filter((model) => model !== void 0);
|
|
@@ -747,7 +1041,7 @@ var WorkBuddyUpstreamClient = class {
|
|
|
747
1041
|
};
|
|
748
1042
|
//#endregion
|
|
749
1043
|
//#region src/version.ts
|
|
750
|
-
const WORKBUDDY_CONNECT_VERSION = "0.
|
|
1044
|
+
const WORKBUDDY_CONNECT_VERSION = "0.3.0";
|
|
751
1045
|
//#endregion
|
|
752
1046
|
//#region src/host-heartbeat.ts
|
|
753
1047
|
/**
|
|
@@ -890,4 +1184,4 @@ function isHeartbeatProcessAlive(heartbeat) {
|
|
|
890
1184
|
return startAtMs <= heartbeat.registeredAt;
|
|
891
1185
|
}
|
|
892
1186
|
//#endregion
|
|
893
|
-
export {
|
|
1187
|
+
export { workbuddyOwnAuthPath as S, WORKBUDDY_AUTH_FILE_ENV as _, readHostHeartbeat as a, defaultDesktopAuthPath as b, WORKBUDDY_CONNECT_VERSION as c, normalizeCredits as d, prepareChatBody as f, WORKBUDDY_AUTH_FILENAME as g, WorkBuddyCatalog as h, processStartTimeMs as i, WorkBuddyUpstreamClient as l, FALLBACK_WORKBUDDY_MODELS as m, clearHostHeartbeat as n, workbuddyHostHeartbeatPath as o, regionOf as p, isHeartbeatProcessAlive as r, writeHostHeartbeat as s, WORKBUDDY_HOST_HEARTBEAT_FILENAME as t, classifyUpstreamError as u, WorkBuddyCredentialStore as v, parseWorkBuddyAuth as x, defaultDesktopAuthCandidates as y };
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
3
3
|
import { Context } from "@deepseek-ai/cordis";
|
|
4
|
+
import { SettingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
5
|
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
5
6
|
//#region src/upstream.d.ts
|
|
6
7
|
/** WorkBuddy region selected by the credential's login domain. */
|
|
@@ -19,6 +20,49 @@ interface WorkBuddyUpstreamModel {
|
|
|
19
20
|
* admits an image the provider then rejects after the message is durable.
|
|
20
21
|
*/
|
|
21
22
|
supportsImages: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Reasoning metadata the upstream catalog declares per model. The wire
|
|
25
|
+
* effort values (`low`, `medium`, `high`, `xhigh`, `max`) map directly onto
|
|
26
|
+
* pi-ai's thinking levels, and the supported set decides which levels the
|
|
27
|
+
* DSH model selector offers.
|
|
28
|
+
*/
|
|
29
|
+
reasoning?: WorkBuddyModelReasoning;
|
|
30
|
+
/**
|
|
31
|
+
* Billing convenience metadata: the credits multiplier string the upstream
|
|
32
|
+
* reports (e.g. `"x0.00"` for free) and promotional badges like
|
|
33
|
+
* `badge:限时免费:#FF0000` or `badge:夜间折扣:#1E90FF`.
|
|
34
|
+
*
|
|
35
|
+
* The multiplier reaches the browser through the host LLM seam, which has no
|
|
36
|
+
* locale service, so {@link normalizeCredits} trims it to a
|
|
37
|
+
* language-neutral display form (`x0.79`) that reads the same in every UI
|
|
38
|
+
* language. The raw upstream string (which may spell `x0.79 credits`) stays
|
|
39
|
+
* on {@link WorkBuddyModelBilling.credits} for diagnostics.
|
|
40
|
+
*/
|
|
41
|
+
billing?: WorkBuddyModelBilling;
|
|
42
|
+
}
|
|
43
|
+
/** Reasoning metadata the upstream catalog declares for one model. */
|
|
44
|
+
interface WorkBuddyModelReasoning {
|
|
45
|
+
/** Whether the model does any reasoning at all (upstream `supportsReasoning`). */
|
|
46
|
+
supports: boolean;
|
|
47
|
+
/** Whether the model can only think (upstream `onlyReasoning`). */
|
|
48
|
+
onlyReasoning: boolean;
|
|
49
|
+
/** Selectable effort values; absent means the model has no explicit set. */
|
|
50
|
+
supportedEfforts?: readonly WorkBuddyEffort[];
|
|
51
|
+
/** Default effort the upstream uses when none is chosen. */
|
|
52
|
+
defaultEffort?: WorkBuddyEffort;
|
|
53
|
+
/** Whether thinking can be switched off; false means it is always on. */
|
|
54
|
+
canDisableThinking: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** The concrete effort spellings WorkBuddy exposes on the wire. */
|
|
57
|
+
type WorkBuddyEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
|
|
58
|
+
/** Billing convenience metadata reported for one model. */
|
|
59
|
+
interface WorkBuddyModelBilling {
|
|
60
|
+
/** Credits multiplier, e.g. `"x0.00"` (free) or `"x0.79"`. */
|
|
61
|
+
credits?: string;
|
|
62
|
+
/** Promotional tags, e.g. `"限时免费"`, `"夜间折扣"`. */
|
|
63
|
+
badges?: readonly string[];
|
|
64
|
+
/** Whether the model is currently free (`x0.00` credits). */
|
|
65
|
+
free: boolean;
|
|
22
66
|
}
|
|
23
67
|
/** One billing package and its remaining credit. */
|
|
24
68
|
interface WorkBuddyCreditAccount {
|
|
@@ -48,14 +92,36 @@ type WorkBuddyChatResult = {
|
|
|
48
92
|
kind: UpstreamErrorKind;
|
|
49
93
|
message: string;
|
|
50
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Reduce an upstream credits string to its language-neutral display form.
|
|
97
|
+
*
|
|
98
|
+
* The host LLM seam carries this text to the browser, and the host has no
|
|
99
|
+
* locale service — whatever string is produced here is shown verbatim in every
|
|
100
|
+
* UI language. The upstream is inconsistent in a way that matters: some catalog
|
|
101
|
+
* rows report a bare multiplier (`x0.79`) and others append a unit word
|
|
102
|
+
* (`x0.79 credits`), and the unit word would pin the display to English.
|
|
103
|
+
* Dropping a trailing `credits` (case-insensitive, singular or plural) yields
|
|
104
|
+
* the one spelling that reads identically in every language.
|
|
105
|
+
*
|
|
106
|
+
* @param credits - raw upstream credits string, e.g. `"x0.79 credits"`.
|
|
107
|
+
* @returns the bare multiplier, or undefined when nothing displayable remains.
|
|
108
|
+
*/
|
|
109
|
+
declare function normalizeCredits(credits: string | undefined): string | undefined;
|
|
51
110
|
/** Classify an upstream failure from its HTTP status and body excerpt. */
|
|
52
111
|
declare function classifyUpstreamError(status: number, body: string): UpstreamErrorKind;
|
|
53
112
|
/** Region for a login domain; an empty domain means CN (matching upstream tooling). */
|
|
54
113
|
declare function regionOf(domain: string): WorkBuddyRegion;
|
|
55
114
|
/**
|
|
56
115
|
* Normalize an OpenAI chat-completions body for the WorkBuddy upstream:
|
|
57
|
-
* force `stream: true` (the upstream rejects non-streaming)
|
|
58
|
-
* `tool_choice` (the upstream's field is a string; object forms return 400)
|
|
116
|
+
* force `stream: true` (the upstream rejects non-streaming), flatten
|
|
117
|
+
* `tool_choice` (the upstream's field is a string; object forms return 400),
|
|
118
|
+
* and rewrite `developer` messages as `system`.
|
|
119
|
+
*
|
|
120
|
+
* The `developer` rewrite is load-bearing: pi-ai emits the system prompt as
|
|
121
|
+
* `role: "developer"` (the OpenAI convention it adopted), but the WorkBuddy
|
|
122
|
+
* upstream rejects that role with HTTP 400 code 11128 ("Illegal API
|
|
123
|
+
* invocation from an unapproved channel"). Rewriting to `system` is the
|
|
124
|
+
* compatible spelling the upstream accepts.
|
|
59
125
|
*/
|
|
60
126
|
declare function prepareChatBody(source: string): string;
|
|
61
127
|
/**
|
|
@@ -190,10 +256,16 @@ declare class WorkBuddyCredentialStore {
|
|
|
190
256
|
/** One model entry the adapter exposes. */
|
|
191
257
|
type WorkBuddyModelInfo = WorkBuddyUpstreamModel;
|
|
192
258
|
/**
|
|
193
|
-
* Static CLI models observed on the CN endpoint (
|
|
194
|
-
*
|
|
195
|
-
* replaces this list at startup; it exists so the provider
|
|
196
|
-
* usable catalog even while the first fetch is in flight or
|
|
259
|
+
* Static CLI models observed on the CN endpoint (re-verified against the live
|
|
260
|
+
* catalog 2026-09-01, including the thinking-effort and billing metadata). The
|
|
261
|
+
* upstream refresh replaces this list at startup; it exists so the provider
|
|
262
|
+
* registers with a usable catalog even while the first fetch is in flight or
|
|
263
|
+
* offline.
|
|
264
|
+
*
|
|
265
|
+
* The list tracks the `cli` agent's model roster exactly: the 15 models the
|
|
266
|
+
* desktop CLI offers. Reasoning metadata is taken verbatim from the live
|
|
267
|
+
* endpoint — each model's supported effort set and whether thinking can be
|
|
268
|
+
* disabled — and the `free` flag follows the upstream `x0.00` credits marker.
|
|
197
269
|
*/
|
|
198
270
|
declare const FALLBACK_WORKBUDDY_MODELS: readonly WorkBuddyModelInfo[];
|
|
199
271
|
/** Mutable catalog shared by the shim's `/v1/models` and the adapter. */
|
|
@@ -336,8 +408,19 @@ declare function isHeartbeatProcessAlive(heartbeat: WorkBuddyHostHeartbeat): boo
|
|
|
336
408
|
declare const name = "llm-workbuddy";
|
|
337
409
|
/** The model registry required before the provider can register. */
|
|
338
410
|
declare const inject: string[];
|
|
339
|
-
/**
|
|
340
|
-
|
|
411
|
+
/**
|
|
412
|
+
* Settings namespace owning the configuration card.
|
|
413
|
+
*
|
|
414
|
+
* DSH 0.1.2 dropped the `settingsNamespace()` branding function: a namespace is
|
|
415
|
+
* now a nominal string, validated by the type system where it is used rather
|
|
416
|
+
* than at runtime by a function call. The brand is compile-time only, so this
|
|
417
|
+
* stays the plain string it always was — every comparison, descriptor lookup,
|
|
418
|
+
* and `dsh` config file still sees `'workbuddy'`. It is cast once here so the
|
|
419
|
+
* public constant carries the seam's type without pulling the brand helper
|
|
420
|
+
* into this package (upstream DSH plugins, `dsh-llm-pi-ai` included, pass
|
|
421
|
+
* their namespaces as plain string literals).
|
|
422
|
+
*/
|
|
423
|
+
declare const WORKBUDDY_SETTINGS_NS: SettingsNamespace;
|
|
341
424
|
/** Plugin configuration. */
|
|
342
425
|
interface Config {
|
|
343
426
|
/** Explicit WorkBuddy desktop auth-file path, overriding env and platform defaults. */
|
|
@@ -352,4 +435,4 @@ declare const Config: z<Config>;
|
|
|
352
435
|
*/
|
|
353
436
|
declare function apply(ctx: Context, config: Config): void;
|
|
354
437
|
//#endregion
|
|
355
|
-
export { Config, FALLBACK_WORKBUDDY_MODELS, type UpstreamErrorKind, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, type WorkBuddyAdapter, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCredits, type WorkBuddyHostHeartbeat, type WorkBuddyModelInfo, type WorkBuddyRefreshOutcome, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, inject, isHeartbeatProcessAlive, name, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath };
|
|
438
|
+
export { Config, FALLBACK_WORKBUDDY_MODELS, type UpstreamErrorKind, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, type WorkBuddyAdapter, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCredits, type WorkBuddyEffort, type WorkBuddyHostHeartbeat, type WorkBuddyModelBilling, type WorkBuddyModelInfo, type WorkBuddyModelReasoning, type WorkBuddyRefreshOutcome, type WorkBuddyShim, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, inject, isHeartbeatProcessAlive, name, normalizeCredits, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath };
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { S as workbuddyOwnAuthPath, _ as WORKBUDDY_AUTH_FILE_ENV, a as readHostHeartbeat, b as defaultDesktopAuthPath, d as normalizeCredits, f as prepareChatBody, g as WORKBUDDY_AUTH_FILENAME, h as WorkBuddyCatalog, i as processStartTimeMs, l as WorkBuddyUpstreamClient, m as FALLBACK_WORKBUDDY_MODELS, n as clearHostHeartbeat, o as workbuddyHostHeartbeatPath, p as regionOf, r as isHeartbeatProcessAlive, s as writeHostHeartbeat, t as WORKBUDDY_HOST_HEARTBEAT_FILENAME, u as classifyUpstreamError, v as WorkBuddyCredentialStore, x as parseWorkBuddyAuth, y as defaultDesktopAuthCandidates } from "./host-heartbeat-CM7AW7vZ.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
4
3
|
import { createProvider } from "@earendil-works/pi-ai";
|
|
5
4
|
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
6
5
|
import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
@@ -62,6 +61,84 @@ const NO_COST = {
|
|
|
62
61
|
cacheRead: 0,
|
|
63
62
|
cacheWrite: 0
|
|
64
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* The suffix appended to a model's display name so its billing rate is visible
|
|
66
|
+
* wherever the name is shown.
|
|
67
|
+
*
|
|
68
|
+
* The separator is a middle dot rather than a hyphen or colon: model names
|
|
69
|
+
* already contain hyphens (`GLM-5.3-Flash`, `Deepseek-V4-Flash`), so a hyphen
|
|
70
|
+
* separator would be ambiguous about where the name ends and the rate begins.
|
|
71
|
+
*/
|
|
72
|
+
const RATE_SEPARATOR = " · ";
|
|
73
|
+
/**
|
|
74
|
+
* Append the billing rate to one model's display name.
|
|
75
|
+
*
|
|
76
|
+
* The rate AND the declared promo badges ride the *name* alone: since DSH
|
|
77
|
+
* 0.1.2 the composer's model seat (`ModelSelect`) renders `model.name` only —
|
|
78
|
+
* `description` is no longer read there at all (the 0.1.1-era client rendered
|
|
79
|
+
* it, which is why the badges used to be visible in the seat). The `/model`
|
|
80
|
+
* popup renders the name too, so a separate `description` copy would either
|
|
81
|
+
* duplicate (rate) or vanish (badges) depending on client generation.
|
|
82
|
+
*
|
|
83
|
+
* This is display-only and cannot affect routing: the wire request is built
|
|
84
|
+
* from `model.id` (pi-ai's completions API sets `model: model.id`), the
|
|
85
|
+
* selection a picker submits is `{provider, model: id, reasoningEffort}`, and
|
|
86
|
+
* `dsh-llm` validates `name` as a non-empty string without comparing its
|
|
87
|
+
* contents. Nothing in the host resolves a model *by* name.
|
|
88
|
+
*/
|
|
89
|
+
/**
|
|
90
|
+
* The catalog display suffix: the billing rate followed by the declared promo
|
|
91
|
+
* badges (`限时免费`, `夜间折扣`), or undefined when the row carries neither.
|
|
92
|
+
* The badge labels are the upstream's own spellings and the host seam has no
|
|
93
|
+
* locale service, so non-Chinese UIs see them verbatim — accepted until the
|
|
94
|
+
* picker grows a localized badge slot.
|
|
95
|
+
*/
|
|
96
|
+
function displaySuffix(info) {
|
|
97
|
+
const parts = [normalizeCredits(info.billing?.credits), ...info.billing?.badges ?? []].filter((part) => part !== void 0 && part !== "");
|
|
98
|
+
return parts.length === 0 ? void 0 : parts.join(" · ");
|
|
99
|
+
}
|
|
100
|
+
/** Append the catalog display suffix to one model's display name. */
|
|
101
|
+
function withCatalogDisplay(name, info) {
|
|
102
|
+
const suffix = displaySuffix(info);
|
|
103
|
+
return suffix === void 0 ? name : `${name}${RATE_SEPARATOR}${suffix}`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve a WorkBuddy model's reasoning capability into pi-ai's
|
|
107
|
+
* `thinkingLevelMap` (every level pinned to its wire spelling or `null` for
|
|
108
|
+
* unsupported), mirroring `dsh-llm-pi-ai`'s own `resolveModelReasoning`.
|
|
109
|
+
*
|
|
110
|
+
* Declared sets only: a thinking control is offered exactly when the upstream
|
|
111
|
+
* catalog declares a `supportedEfforts` list, and it offers exactly the
|
|
112
|
+
* declared values. Rows without a list (the older `{effort, summary}` shape)
|
|
113
|
+
* get no control at all — their selectable set is client-side knowledge the
|
|
114
|
+
* catalog does not carry (the desktop app differs per model there: GLM-5.2
|
|
115
|
+
* gets a thinking control while MiniMax-M3 and Kimi-K2.6 do not, though their
|
|
116
|
+
* catalog rows are identical), and another implementation against the same
|
|
117
|
+
* upstream (workbuddy2api) gates on the declared set and downgrades
|
|
118
|
+
* out-of-set values rather than passing them through, so sending an
|
|
119
|
+
* undeclared value risks a 400. Such models never carry `reasoning_effort`
|
|
120
|
+
* on the wire; the upstream applies its own default.
|
|
121
|
+
* `off` is offered only when the model explicitly reports thinking can be
|
|
122
|
+
* disabled (`canDisableThinking === true`).
|
|
123
|
+
*/
|
|
124
|
+
function reasoningFields(info) {
|
|
125
|
+
const reasoning = info.reasoning;
|
|
126
|
+
if (reasoning === void 0 || reasoning.supports !== true) return { reasoning: false };
|
|
127
|
+
const efforts = reasoning.supportedEfforts;
|
|
128
|
+
if (efforts === void 0 || efforts.length === 0) return { reasoning: false };
|
|
129
|
+
return {
|
|
130
|
+
reasoning: true,
|
|
131
|
+
thinkingLevelMap: {
|
|
132
|
+
off: reasoning.canDisableThinking === true ? "off" : null,
|
|
133
|
+
minimal: null,
|
|
134
|
+
low: efforts.includes("low") ? "low" : null,
|
|
135
|
+
medium: efforts.includes("medium") ? "medium" : null,
|
|
136
|
+
high: efforts.includes("high") ? "high" : null,
|
|
137
|
+
xhigh: efforts.includes("xhigh") ? "xhigh" : null,
|
|
138
|
+
max: efforts.includes("max") ? "max" : null
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
65
142
|
/** Build one pi-ai model descriptor pointing at the loopback shim. */
|
|
66
143
|
function toPiModel(info, baseUrl) {
|
|
67
144
|
return {
|
|
@@ -71,6 +148,7 @@ function toPiModel(info, baseUrl) {
|
|
|
71
148
|
provider: WORKBUDDY_PROVIDER,
|
|
72
149
|
baseUrl,
|
|
73
150
|
input: info.supportsImages === true ? ["text", "image"] : ["text"],
|
|
151
|
+
...reasoningFields(info),
|
|
74
152
|
cost: NO_COST,
|
|
75
153
|
contextWindow: info.contextWindow,
|
|
76
154
|
maxTokens: info.maxTokens
|
|
@@ -117,7 +195,7 @@ function createWorkBuddyAdapter(options) {
|
|
|
117
195
|
};
|
|
118
196
|
let profiles = /* @__PURE__ */ new Map([[WORKBUDDY_PROVIDER, profile]]);
|
|
119
197
|
return {
|
|
120
|
-
adapter: new
|
|
198
|
+
adapter: new WorkBuddyPiAiAdapter(catalog, {
|
|
121
199
|
profiles: () => profiles,
|
|
122
200
|
auth: INERT_AUTH,
|
|
123
201
|
resolveApiKey: async () => shim.token(),
|
|
@@ -128,6 +206,52 @@ function createWorkBuddyAdapter(options) {
|
|
|
128
206
|
}
|
|
129
207
|
};
|
|
130
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* The WorkBuddy route's adapter: `PiAiAdapter` with the billing rate folded
|
|
211
|
+
* into the catalog answers it returns to the DSH model pickers.
|
|
212
|
+
*
|
|
213
|
+
* `PiAiAdapter.listModels()` and `.resolveModel()` build their answers straight
|
|
214
|
+
* from the pi-ai descriptors, which carry no billing fact, so the rate is
|
|
215
|
+
* layered on here by looking the model up in the live catalog. Both overrides
|
|
216
|
+
* delegate to `super` and then rewrite only the display fields, so streaming,
|
|
217
|
+
* capability resolution, and effort mapping stay exactly as `dsh-llm-pi-ai`
|
|
218
|
+
* implements them.
|
|
219
|
+
*
|
|
220
|
+
* A model missing from the catalog (an id the shim would serve but the last
|
|
221
|
+
* upstream refresh did not list) falls through with its name untouched rather
|
|
222
|
+
* than being dropped: catalog membership is advisory, and the seam tolerates
|
|
223
|
+
* serving an unlisted id.
|
|
224
|
+
*/
|
|
225
|
+
var WorkBuddyPiAiAdapter = class extends PiAiAdapter {
|
|
226
|
+
catalog;
|
|
227
|
+
constructor(catalog, options) {
|
|
228
|
+
super(options);
|
|
229
|
+
this.catalog = catalog;
|
|
230
|
+
}
|
|
231
|
+
/** Catalog entry for one model id, or undefined when the catalog omits it. */
|
|
232
|
+
infoFor(model) {
|
|
233
|
+
return this.catalog.current().find((entry) => entry.id === model);
|
|
234
|
+
}
|
|
235
|
+
async listModels(provider) {
|
|
236
|
+
return (await super.listModels(provider)).map((model) => {
|
|
237
|
+
const info = this.infoFor(model.id);
|
|
238
|
+
if (info === void 0) return model;
|
|
239
|
+
return {
|
|
240
|
+
...model,
|
|
241
|
+
name: withCatalogDisplay(model.name, info)
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
async resolveModel(provider, model, signal) {
|
|
246
|
+
const resolved = await super.resolveModel(provider, model, signal);
|
|
247
|
+
const info = this.infoFor(model);
|
|
248
|
+
if (info === void 0) return resolved;
|
|
249
|
+
return {
|
|
250
|
+
...resolved,
|
|
251
|
+
name: withCatalogDisplay(resolved.name, info)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
};
|
|
131
255
|
//#endregion
|
|
132
256
|
//#region src/shim.ts
|
|
133
257
|
/**
|
|
@@ -402,22 +526,36 @@ async function workBuddyWebStatus(deps) {
|
|
|
402
526
|
...authStatus.source === void 0 ? {} : { source: authStatus.source },
|
|
403
527
|
...authStatus.expiresAtMs === void 0 ? {} : { expiresAt: authStatus.expiresAtMs }
|
|
404
528
|
};
|
|
529
|
+
const modelsField = deps.models().filter((model) => model.billing?.free === true || (model.billing?.badges?.length ?? 0) > 0).map((model) => {
|
|
530
|
+
const rate = normalizeCredits(model.billing?.credits);
|
|
531
|
+
return {
|
|
532
|
+
id: model.id,
|
|
533
|
+
name: model.name,
|
|
534
|
+
...model.billing?.free === true ? { free: true } : {},
|
|
535
|
+
...model.billing?.badges !== void 0 && model.billing.badges.length > 0 ? { badges: model.billing.badges } : {},
|
|
536
|
+
...rate === void 0 ? {} : { credits: rate }
|
|
537
|
+
};
|
|
538
|
+
});
|
|
539
|
+
const statusWithModels = modelsField.length > 0 ? {
|
|
540
|
+
...status,
|
|
541
|
+
models: modelsField
|
|
542
|
+
} : status;
|
|
405
543
|
try {
|
|
406
544
|
const credential = await deps.store.current();
|
|
407
545
|
if (credential !== void 0) {
|
|
408
546
|
const credits = await deps.client.fetchCredits(credential);
|
|
409
547
|
return {
|
|
410
|
-
...
|
|
548
|
+
...statusWithModels,
|
|
411
549
|
credits
|
|
412
550
|
};
|
|
413
551
|
}
|
|
414
552
|
} catch (error) {
|
|
415
553
|
return {
|
|
416
|
-
...
|
|
554
|
+
...statusWithModels,
|
|
417
555
|
creditsError: safeMessage(error)
|
|
418
556
|
};
|
|
419
557
|
}
|
|
420
|
-
return
|
|
558
|
+
return statusWithModels;
|
|
421
559
|
}
|
|
422
560
|
/** Mount the GET status route on an optional webServer context. */
|
|
423
561
|
function registerWorkBuddyStatusRoute(ctx, deps) {
|
|
@@ -452,8 +590,19 @@ function registerWorkBuddyStatusRoute(ctx, deps) {
|
|
|
452
590
|
const name = "llm-workbuddy";
|
|
453
591
|
/** The model registry required before the provider can register. */
|
|
454
592
|
const inject = ["llm"];
|
|
455
|
-
/**
|
|
456
|
-
|
|
593
|
+
/**
|
|
594
|
+
* Settings namespace owning the configuration card.
|
|
595
|
+
*
|
|
596
|
+
* DSH 0.1.2 dropped the `settingsNamespace()` branding function: a namespace is
|
|
597
|
+
* now a nominal string, validated by the type system where it is used rather
|
|
598
|
+
* than at runtime by a function call. The brand is compile-time only, so this
|
|
599
|
+
* stays the plain string it always was — every comparison, descriptor lookup,
|
|
600
|
+
* and `dsh` config file still sees `'workbuddy'`. It is cast once here so the
|
|
601
|
+
* public constant carries the seam's type without pulling the brand helper
|
|
602
|
+
* into this package (upstream DSH plugins, `dsh-llm-pi-ai` included, pass
|
|
603
|
+
* their namespaces as plain string literals).
|
|
604
|
+
*/
|
|
605
|
+
const WORKBUDDY_SETTINGS_NS = "workbuddy";
|
|
457
606
|
const Config = z.object({ authFile: z.string().description("WorkBuddy desktop auth file (defaults to the app's own location)") });
|
|
458
607
|
/**
|
|
459
608
|
* Start the loopback endpoint, register the `workbuddy` provider, and
|
|
@@ -476,17 +625,20 @@ function apply(ctx, config) {
|
|
|
476
625
|
});
|
|
477
626
|
ctx.inject(["webServer"], (webCtx) => registerWorkBuddyStatusRoute(webCtx, {
|
|
478
627
|
store,
|
|
479
|
-
client
|
|
628
|
+
client,
|
|
629
|
+
models: () => catalog.current()
|
|
480
630
|
}));
|
|
481
631
|
let current = () => config;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
632
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
633
|
+
settingsCtx.settings.installSection(ctx, WORKBUDDY_SETTINGS_NS, Config, config, {
|
|
634
|
+
setSource(source) {
|
|
635
|
+
current = source;
|
|
636
|
+
},
|
|
637
|
+
onChange() {
|
|
638
|
+
const next = current().authFile;
|
|
639
|
+
store.setDesktopPath(next);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
490
642
|
});
|
|
491
643
|
let stopped = false;
|
|
492
644
|
ctx.effect(() => () => {
|
|
@@ -553,4 +705,4 @@ function apply(ctx, config) {
|
|
|
553
705
|
});
|
|
554
706
|
}
|
|
555
707
|
//#endregion
|
|
556
|
-
export { Config, FALLBACK_WORKBUDDY_MODELS, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WorkBuddyCatalog, WorkBuddyCredentialStore, WorkBuddyUpstreamClient, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, inject, isHeartbeatProcessAlive, name, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath };
|
|
708
|
+
export { Config, FALLBACK_WORKBUDDY_MODELS, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WorkBuddyCatalog, WorkBuddyCredentialStore, WorkBuddyUpstreamClient, apply, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthPath, inject, isHeartbeatProcessAlive, name, normalizeCredits, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath };
|
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.
|
|
5
|
+
"version": "0.3.0",
|
|
6
6
|
"author": "corrinehu",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|
|
@@ -56,8 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"client": {
|
|
58
58
|
"inject": [
|
|
59
|
-
"@deepseek-ai/dsh-client-
|
|
60
|
-
"@deepseek-ai/dsh-client-ui-slots",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
61
60
|
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
62
61
|
"@deepseek-ai/dsh-client-locale"
|
|
63
62
|
],
|
|
@@ -65,39 +64,39 @@
|
|
|
65
64
|
}
|
|
66
65
|
},
|
|
67
66
|
"peerDependencies": {
|
|
68
|
-
"@earendil-works/pi-ai": "0.
|
|
69
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
70
|
-
"@deepseek-ai/dsh-atomic-write": "^0.1.
|
|
71
|
-
"@deepseek-ai/dsh-attachment": "^0.1.
|
|
72
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.
|
|
73
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
74
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
75
|
-
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.
|
|
76
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
77
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
67
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
68
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
69
|
+
"@deepseek-ai/dsh-atomic-write": "^0.1.2-rc.1",
|
|
70
|
+
"@deepseek-ai/dsh-attachment": "^0.1.2-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.2-rc.1",
|
|
72
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1",
|
|
73
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
74
|
+
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-rc.1",
|
|
75
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
|
|
76
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
78
77
|
"react": "^18.2.0"
|
|
79
78
|
},
|
|
80
79
|
"devDependencies": {
|
|
81
|
-
"@earendil-works/pi-ai": "0.
|
|
82
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
83
|
-
"@deepseek-ai/dsh-atomic-write": "^0.1.
|
|
84
|
-
"@deepseek-ai/dsh-attachment": "^0.1.
|
|
85
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
86
|
-
"@deepseek-ai/dsh-client-
|
|
87
|
-
"@deepseek-ai/dsh-client-ui-
|
|
88
|
-
"@deepseek-ai/dsh-
|
|
89
|
-
"@deepseek-ai/dsh-
|
|
90
|
-
"@deepseek-ai/dsh-
|
|
91
|
-
"@deepseek-ai/dsh-
|
|
92
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/
|
|
95
|
-
"@deepseek-ai/schemastery": "3.18.1",
|
|
80
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
81
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
82
|
+
"@deepseek-ai/dsh-atomic-write": "^0.1.2-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-attachment": "^0.1.2-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-credentials": "^0.1.2-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.2-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-llm-pi-ai": "^0.1.2-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
|
|
93
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
96
94
|
"@types/node": "^22.20.0",
|
|
97
95
|
"@types/react": "~18.3.1",
|
|
98
96
|
"react": "^18.2.0",
|
|
99
97
|
"tsdown": "^0.22.2",
|
|
100
98
|
"typescript": "^5.9.3",
|
|
101
|
-
"vitest": "^4.1.8"
|
|
99
|
+
"vitest": "^4.1.8",
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-rc.1"
|
|
102
101
|
}
|
|
103
|
-
}
|
|
102
|
+
}
|