dsh-workbuddy-xdpool 0.4.2 → 0.4.5

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.md CHANGED
@@ -1,181 +1,181 @@
1
- # DSH WorkBuddy XD Pool
2
-
3
- <p align="center">
4
- <a href="https://www.npmjs.com/package/dsh-workbuddy-xdpool"><img src="https://img.shields.io/npm/v/dsh-workbuddy-xdpool?style=flat-square&label=npm&color=cb3837" alt="npm version"></a>
5
- <a href="https://www.npmjs.com/package/dsh-workbuddy-xdpool"><img src="https://img.shields.io/npm/d18m/dsh-workbuddy-xdpool?style=flat-square&label=downloads&color=cb3837" alt="npm downloads"></a>
6
- <a href="LICENSE"><img src="https://img.shields.io/github/license/aosi526/dsh-workbuddy-xdpool?style=flat-square" alt="MIT license"></a>
7
- <a href="https://github.com/aosi526/dsh-workbuddy-xdpool/stargazers"><img src="https://img.shields.io/github/stars/aosi526/dsh-workbuddy-xdpool?style=flat-square" alt="GitHub stars"></a>
8
- </p>
9
-
10
- [English](./README.en.md) | 中文
11
-
12
- 将 WorkBuddy 桌面 App 里登录过的**所有账号**自动并入一个 **DeepSeek Harness 模型池**:无需任何手动配置,你在 WorkBuddy 桌面登录的每个账号都会成为一个池成员;某个账号被限流(429)时,请求会自动切换到下一个可用账号,实现多账号自动容错轮换。
13
-
14
- > 与单账号连接插件(如 dsh-workbuddy-connect)的区别:**XD Pool 把多账号当成常态**——它不挑账号、不做手动导入,而是把本机 WorkBuddy 桌面 App 的所有历史登录快照全部纳入一个共享池,用一个 `workbuddy-xdpool` provider 分组对外暴露,模型请求在池内自动 failover。
15
-
16
- **插件配置卡片(设置 → 插件 → DSH WorkBuddy XD Pool)**
17
-
18
- ![WorkBuddy 池设置卡片:国内版 / 国际版切换栏、池健康状态、账号面板、积分包与合计、每账号每日签到、模型管理(启用勾选 / 图片输入 / 上下文窗口)](assets/settings-card.png)
19
-
20
- **模型选择器(国内版 / 国际版 两个独立供应商分组;倍率直接拼进 model.name:DSH 0.1.2 composer 只读 name)**
21
-
22
- ![模型选择器:国内版与国际版各占一个分组,每个模型名后显示积分倍率与促销标签](assets/model-picker.png)
23
-
24
- **国内版 / 国际版 双供应商(各自独立账号、积分与模型,可同时使用)**
25
-
26
- ![切换国内版与国际版:未登录的一侧给出该区域的登录步骤](assets/region-tabs.png)
27
-
28
- ## 功能
29
-
30
- - **零配置开箱即用**:安装并启用后,WorkBuddy 桌面 App 里每个已登录账号都会在第一次被请求时自动发现、进入池中轮换。无需在插件里手动录入账号。
31
-
32
- - **自动容错轮换**:池维护每个账号的 `429` 冷却状态。当某个账号触发限流进入冷却,后续请求会跳过它、落到下一个健康账号;冷却结束自动恢复。所有账号同时冷却时请求才暂停。
33
-
34
- - **账号健康一目了然**:插件设置卡片显示池健康状态(N 账号 / X 冷却、当前会轮到哪个账号)、每个账号的令牌有效期与冷却倒计时。
35
-
36
- - **剩余积分实时可见**:卡片按账号展示积分包(`套餐名 · 剩余 / 总量`)与合计剩余(大字绿色高亮),跟随上游实时刷新。
37
-
38
- - **模型目录直接标注**:卡片列出当前在池内可用的模型,并标注积分倍率(如 `GLM-5.2 · x0.79`)、免费 / 限时免费 / 夜间折扣标签、图片输入能力与上下文窗口,倍率与标签跟随上游 `credits` / `tags` 实时更新。
39
-
40
- - **每日签到**:卡片在每个账号的积分区下方提供签到按钮,并显示连签天数、每日积分与里程碑额外奖励;一键领取该账号当日签到奖励。多账号可逐个领取,无需先切换账号。领取前会重新查询状态,**今日已领取的账号不会被重复领取**。CLI 亦提供 `checkin` 命令。
41
-
42
- - **国内版 / 国际版自动适配**:按账号凭据里的登录域名自动选择上游域名——国际版登录(`workbuddy.ai`)走 `www.workbuddy.ai`;国内版(默认)走 `copilot.tencent.com` 与 `www.codebuddy.cn`。同一池内可混用两种区域的账号,各自请求各自区域的上游。
43
-
44
- - **三种人工动作**:卡片与 CLI 都提供「重新检测账号」(重新扫描桌面登录快照,把新登录的账号并入池)、「清除所有冷却」(立即解除全部 429 冷却)与「每日签到」三个操作。
45
-
46
- ## 安装
47
-
48
- 前置:已安装并登录 WorkBuddy 桌面 App(插件复用 App 的登录状态;多账号 = 在桌面 App 里逐个登录/切换账号即可,每次登录都会被自动吸收进池)。已针对 DSH Desktop host `0.1.2` 兼容。
49
-
50
- > 与 host 兼容 `0.1.1-rc.2` / `0.1.2` 系:设置节安装会按 host 能力自动选择 `settings.installSection`(0.1.2-rc.1+)或自由函数(更早)。
51
-
52
- **方式一:从 npm 安装(推荐)**
53
-
54
- ```sh
55
- # dsh 不在 PATH 时,用 node ~/.dsh/profiles/node_modules/@deepseek-ai/dsh/lib/bin.js 代替 dsh
56
- dsh plugin --profile desktop add dsh-workbuddy-xdpool
57
- ```
58
-
59
- > 推荐 npm 安装:pnpm / npm 需要拉取的依赖只有插件自身(**约 1 个包,数秒完成**)。
60
- > 从 GitHub 源码安装会连带安装开发依赖(构建器、测试框架等数百个包),明显更慢。
61
-
62
- **方式二:从 GitHub 源码安装**
63
-
64
- ```sh
65
- dsh plugin --profile desktop add github:aosi526/dsh-workbuddy-xdpool
66
- ```
67
-
68
- **方式三:手动注册 bundle**
69
-
70
- ```sh
71
- # 1) 安装包(npm 或 GitHub 任选其一)
72
- dsh plugin --profile desktop add dsh-workbuddy-xdpool
73
-
74
- # 2) 注册 bundle:编辑 ~/.dsh/profiles/desktop/package.json,
75
- # 在 "dsh" → "profile" → "bundles" 数组末尾追加 "dsh-workbuddy-xdpool"
76
-
77
- # 3) 重启 DSH Desktop
78
- ```
79
-
80
- **本地构建**(开发者):
81
-
82
- ```sh
83
- pnpm install
84
- pnpm build # 产出 lib/index.js + lib/index.d.ts + lib/bin.js + lib/client.js
85
- pnpm test # 26 项测试(自动容错轮换、按(账号,模型)冷却、签到守卫)
86
- pnpm typecheck # 宿主侧
87
- pnpm typecheck:client # 客户端
88
- ```
89
-
90
- > **构建产物已随仓库提交**(`lib/` 不再 gitignore)。因此从 GitHub 安装时不需要任何安装期脚本,不会触发 pnpm 的「构建脚本被拦截,请放行」提示。**改动 `src/` 后请重新 `pnpm build` 并把 `lib/` 一并提交**,否则用户拿到的是旧产物。
91
-
92
- > 注意:`pnpm install` 需用 pnpm 11(`npx pnpm@11`),必要时加 `--config.confirmModulesPurge=false --config.minimumReleaseAge=0`(pnpm 11 默认的 `minimumReleaseAge` 供应链年龄策略会拦截刚发布的 rc 包)。
93
-
94
- 装好后:模型选择器里会出现 **WorkBuddy XD Pool** 分组;设置 → 插件 → **DSH WorkBuddy XD Pool** 卡片可查看池健康、各账号令牌/积分/签到/冷却,以及「重新检测账号」「清除所有冷却」按钮与每个账号的签到按钮。
95
-
96
- 插件在 Web / TUI profile 下同样可用(`--profile web` / `--profile dsh-tui`)。
97
-
98
- ## 命令行
99
-
100
- 统一用 `dsh plugin --profile desktop exec dsh-workbuddy-xdpool <子命令>` 调用:
101
-
102
- ```sh
103
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool status # 池账号数/冷却 + shim 状态(--credits 查积分、--json 机器可读、--rates 看倍率)
104
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts # 已发现账号(--json)
105
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool doctor # 诊断发现/冷却/上游连通性
106
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool reset # 立即清除所有 429 冷却
107
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin # 查询每个账号今日签到状态(--json 机器可读)
108
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin all
109
- # 领取所有账号今日签到奖励;也可传账号标签只领一个
110
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool login # 引导如何在桌面再加一个账号入池
111
- ```
112
-
113
- ## 池里怎么多账号?
114
-
115
- 池走**自动发现**:WorkBuddy 桌面 App 每次登录都会在本机留下一个带令牌的历史快照,XD Pool 扫描这些快照,把每个账号都吸收入池。因此多账号 = 在 WorkBuddy 桌面 App 里逐个登录 / 切换账号即可,之后点卡片「重新检测账号」或重启 DSH,新账号自动成为池成员。
116
-
117
- 若你想对桌面 App 之外的某个登录做**显式快照**(例如临时固定某个账号再验证),也可手动导入:
118
-
119
- ```sh
120
- # 在 WorkBuddy 桌面 App 登录账号后(key 自己起名):
121
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool import myKey
122
- # 查看/删除已导入快照:
123
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts
124
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool remove myKey
125
- ```
126
-
127
- 导入快照以 key 的 **MD5 前 8 位**命名落在 `~/.dsh/.workbuddy-xdpool/`(key 本身记在文件里),中文、带 `/`、带空格的 key 都安全;长期使用靠 refresh token 自动续期,失效则回到桌面重新登录后 `import <key> --force` 覆盖。
128
-
129
- ## 配置
130
-
131
- 池的有效配置经插件设置节(`settings.workbuddy-xdpool`)读取,模型设置页可改,改动即时生效:
132
-
133
- | 字段 | 说明 | 默认 |
134
- | --- | --- | --- |
135
- | `authFile` | 覆盖 WorkBuddy 桌面 auth 文件路径(跨平台探测异常时用,等价于 `WORKBUDDY_AUTH_FILE`) | 自动探测 |
136
- | `cooldownMs` | 单账号 429 冷却时长(毫秒) | `60000` |
137
-
138
- 也可直接写在 `~/.dsh/settings.yaml`:
139
-
140
- ```yaml
141
- workbuddy-xdpool:
142
- cooldownMs: 120000
143
- ```
144
-
145
- ## 架构
146
-
147
- - **宿主侧**(`src/`,DSH 主进程内):
148
- - `index.ts` —— 注册 `workbuddy-xdpool` provider、`workbuddy-xdpool` 设置节(`settings.installSection`)、4 条同源路由(状态 / 重新检测 / 清除冷却 / 签到)、账号发现与模型目录播种。
149
- - `accounts.ts` —— `WorkBuddyAccountPool`:读本机 WorkBuddy 桌面 auth 快照、429 冷却、round-robin failover 与 token 刷新。
150
- - `catalog.ts` / `upstream.ts` —— 上游模型目录(含每模型积分倍率、免费/图片能力标签)、积分查询与每日签到的上游客户端(按凭据域名自动切换国内 / 国际版域名)。
151
- - `web-status.ts` / `status-paths.ts` —— 卡片消费的同源状态文档与路由;签到是本插件唯一的写操作,按「POST + 回环来源 + 显式 accountId + 领取前二次确认」四重守卫。
152
- - `bin.ts` —— 上述 CLI。
153
- - **客户端**(`src/client/`,浏览器卡片,经 `dsh.client` 由宿主加载):折叠卡片外壳沿用宿主内置卡的 `dsm-plugin-card*` 样式语言(`--dsw-alias-*` 主题变量),内容用 `dsm-workbuddy-xdpool-*` 前缀,绝不污染宿主其它卡片;命名空间 `settings.workbuddy-xdpool`。
154
- - **构建**:`tsdown` 产出 `lib/index.js`(宿主入口)+ `lib/index.d.ts`(类型)+ `lib/bin.js`(CLI)+ `lib/client.js`(CJS,`window.__ModuleLoader__.load` 包裹的浏览器 bundle)。四个产物均随仓库提交,因此安装时不需要任何构建脚本。
155
-
156
- ## 已知限制
157
-
158
- - **仅使用本机桌面 App 的账号**:池不会、也无法替你发起 WorkBuddy 的登录/扫码(token 由 WorkBuddy 桌面 App 自己的腾讯 SSO 登录铸造并设备绑定)。加池账号 = 在 WorkBuddy 桌面 App 里登录/切换,XD Pool 自动吸收。
159
- - 依赖 WorkBuddy 客户端接口(非官方开放 API),WorkBuddy 更新后插件可能需要随之调整;若某账号 refresh token 失效,回到桌面重新登录即可。
160
- - 若 Windows 与 Linux 用户名不同且 Windows 环境变量未传入 WSL,请用 `WORKBUDDY_AUTH_FILE` 或配置节的 `authFile` 指定实际位置。
161
-
162
- ## 免责声明
163
-
164
- - 本项目**仅供个人学习和研究使用**,仅驱动使用者自己的 WorkBuddy 账号在本机调用,请勿用于商业用途或超出个人合理使用的场景。
165
- - 使用者需遵守 WorkBuddy 的服务条款;因使用本项目产生的任何后果(包括但不限于账号被限制、额度被清空、服务中断),由使用者自行承担。
166
- - 本项目作者不对任何因使用或滥用本项目产生的直接或间接损失负责。
167
- - 本项目与腾讯、WorkBuddy、DeepSeek 均无关联,未获其授权或认可;文中出现的名称仅用于描述兼容关系,其商标权利归各自所有。
168
-
169
- ## 致谢
170
-
171
- 本项目的实现参考了以下已公开的项目,并按其许可证要求保留版权声明。参考方向为**设计思路与既有结论**,代码为独立实现;关键模块在源文件头部注释中也标注了所参考的项目与模式:
172
-
173
- - [corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)(MIT)—— 设置节注册(`settings.installSection`)与 DSH 插件结构、客户端卡片加载机制、桌面端凭据刷新与 loopback shim 加固的核心参照;本项目沿用其「宿主通过 installSection 挂卡片」的打通路径。
174
- - [dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)(MIT,Copyright (c) 2026 LaoDing)—— `dsm-plugin-card*` 卡片样式语言与 `--dsw-alias-*` 主题变量的参照实现;**每日签到**(`/v2/billing/meter/checkin-activity-status` 与 `/v2/billing/meter/daily-checkin`)、积分包聚合口径(月度周期套餐 / 一次性礼包区分)与国内 / 国际版按 `domain` 选择上游域名的做法,参考了该项目已验证的接口形态。
175
- - [Sliverkiss/workbuddy2api](https://github.com/Sliverkiss/workbuddy2api)(MIT)—— WorkBuddy 上游协议(`copilot.tencent.com` 的 wire behavior)与积分接口的参照实现。
176
-
177
- 以上项目的版权归各自作者所有。本项目采用**参考设计思路 + 独立实现**的方式,未整体复制任何参考项目的源码。若标注有遗漏或不当之处,欢迎提交 issue 指正。
178
-
179
- ## 许可证
180
-
181
- [MIT](./LICENSE)
1
+ # DSH WorkBuddy XD Pool
2
+
3
+ <p align="center">
4
+ <a href="https://www.npmjs.com/package/dsh-workbuddy-xdpool"><img src="https://img.shields.io/npm/v/dsh-workbuddy-xdpool?style=flat-square&label=npm&color=cb3837" alt="npm version"></a>
5
+ <a href="https://www.npmjs.com/package/dsh-workbuddy-xdpool"><img src="https://img.shields.io/npm/d18m/dsh-workbuddy-xdpool?style=flat-square&label=downloads&color=cb3837" alt="npm downloads"></a>
6
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/XDTrees/dsh-workbuddy-xdpool?style=flat-square" alt="MIT license"></a>
7
+ <a href="https://github.com/XDTrees/dsh-workbuddy-xdpool/stargazers"><img src="https://img.shields.io/github/stars/XDTrees/dsh-workbuddy-xdpool?style=flat-square" alt="GitHub stars"></a>
8
+ </p>
9
+
10
+ [English](./README.en.md) | 中文
11
+
12
+ 将 WorkBuddy 桌面 App 里登录过的**所有账号**自动并入一个 **DeepSeek Harness 模型池**:无需任何手动配置,你在 WorkBuddy 桌面登录的每个账号都会成为一个池成员;某个账号被限流(429)时,请求会自动切换到下一个可用账号,实现多账号自动容错轮换。
13
+
14
+ > 与单账号连接插件(如 dsh-workbuddy-connect)的区别:**XD Pool 把多账号当成常态**——它不挑账号、不做手动导入,而是把本机 WorkBuddy 桌面 App 的所有历史登录快照全部纳入一个共享池,用一个 `workbuddy-xdpool` provider 分组对外暴露,模型请求在池内自动 failover。
15
+
16
+ **插件配置卡片(设置 → 插件 → DSH WorkBuddy XD Pool)**
17
+
18
+ ![WorkBuddy 池设置卡片:国内版 / 国际版切换栏、池健康状态、账号面板、积分包与合计、每账号每日签到、模型管理(启用勾选 / 图片输入 / 上下文窗口)](assets/settings-card.png)
19
+
20
+ **模型选择器(国内版 / 国际版 两个独立供应商分组;倍率直接拼进 model.name:DSH 0.1.2 composer 只读 name)**
21
+
22
+ ![模型选择器:国内版与国际版各占一个分组,每个模型名后显示积分倍率与促销标签](assets/model-picker.png)
23
+
24
+ **国内版 / 国际版 双供应商(各自独立账号、积分与模型,可同时使用)**
25
+
26
+ ![切换国内版与国际版:未登录的一侧给出该区域的登录步骤](assets/region-tabs.png)
27
+
28
+ ## 功能
29
+
30
+ - **零配置开箱即用**:安装并启用后,WorkBuddy 桌面 App 里每个已登录账号都会在第一次被请求时自动发现、进入池中轮换。无需在插件里手动录入账号。
31
+
32
+ - **自动容错轮换**:池维护每个账号的 `429` 冷却状态。当某个账号触发限流进入冷却,后续请求会跳过它、落到下一个健康账号;冷却结束自动恢复。所有账号同时冷却时请求才暂停。
33
+
34
+ - **账号健康一目了然**:插件设置卡片显示池健康状态(N 账号 / X 冷却、当前会轮到哪个账号)、每个账号的令牌有效期与冷却倒计时。
35
+
36
+ - **剩余积分实时可见**:卡片按账号展示积分包(`套餐名 · 剩余 / 总量`)与合计剩余(大字绿色高亮),跟随上游实时刷新。
37
+
38
+ - **模型目录直接标注**:卡片列出当前在池内可用的模型,并标注积分倍率(如 `GLM-5.2 · x0.79`)、免费 / 限时免费 / 夜间折扣标签、图片输入能力与上下文窗口,倍率与标签跟随上游 `credits` / `tags` 实时更新。
39
+
40
+ - **每日签到**:卡片在每个账号的积分区下方提供签到按钮,并显示连签天数、每日积分与里程碑额外奖励;一键领取该账号当日签到奖励。多账号可逐个领取,无需先切换账号。领取前会重新查询状态,**今日已领取的账号不会被重复领取**。CLI 亦提供 `checkin` 命令。
41
+
42
+ - **国内版 / 国际版自动适配**:按账号凭据里的登录域名自动选择上游域名——国际版登录(`workbuddy.ai`)走 `www.workbuddy.ai`;国内版(默认)走 `copilot.tencent.com` 与 `www.codebuddy.cn`。同一池内可混用两种区域的账号,各自请求各自区域的上游。
43
+
44
+ - **三种人工动作**:卡片与 CLI 都提供「重新检测账号」(重新扫描桌面登录快照,把新登录的账号并入池)、「清除所有冷却」(立即解除全部 429 冷却)与「每日签到」三个操作。
45
+
46
+ ## 安装
47
+
48
+ 前置:已安装并登录 WorkBuddy 桌面 App(插件复用 App 的登录状态;多账号 = 在桌面 App 里逐个登录/切换账号即可,每次登录都会被自动吸收进池)。已针对 DSH Desktop host `0.1.2` 兼容。
49
+
50
+ > 与 host 兼容 `0.1.1-rc.2` / `0.1.2` 系:设置节安装会按 host 能力自动选择 `settings.installSection`(0.1.2-rc.1+)或自由函数(更早)。
51
+
52
+ **方式一:从 npm 安装(推荐)**
53
+
54
+ ```sh
55
+ # dsh 不在 PATH 时,用 node ~/.dsh/profiles/node_modules/@deepseek-ai/dsh/lib/bin.js 代替 dsh
56
+ dsh plugin --profile desktop add dsh-workbuddy-xdpool
57
+ ```
58
+
59
+ > 推荐 npm 安装:pnpm / npm 需要拉取的依赖只有插件自身(**约 1 个包,数秒完成**)。
60
+ > 从 GitHub 源码安装会连带安装开发依赖(构建器、测试框架等数百个包),明显更慢。
61
+
62
+ **方式二:从 GitHub 源码安装**
63
+
64
+ ```sh
65
+ dsh plugin --profile desktop add github:XDTrees/dsh-workbuddy-xdpool
66
+ ```
67
+
68
+ **方式三:手动注册 bundle**
69
+
70
+ ```sh
71
+ # 1) 安装包(npm 或 GitHub 任选其一)
72
+ dsh plugin --profile desktop add dsh-workbuddy-xdpool
73
+
74
+ # 2) 注册 bundle:编辑 ~/.dsh/profiles/desktop/package.json,
75
+ # 在 "dsh" → "profile" → "bundles" 数组末尾追加 "dsh-workbuddy-xdpool"
76
+
77
+ # 3) 重启 DSH Desktop
78
+ ```
79
+
80
+ **本地构建**(开发者):
81
+
82
+ ```sh
83
+ pnpm install
84
+ pnpm build # 产出 lib/index.js + lib/index.d.ts + lib/bin.js + lib/client.js
85
+ pnpm test # 26 项测试(自动容错轮换、按(账号,模型)冷却、签到守卫)
86
+ pnpm typecheck # 宿主侧
87
+ pnpm typecheck:client # 客户端
88
+ ```
89
+
90
+ > **构建产物已随仓库提交**(`lib/` 不再 gitignore)。因此从 GitHub 安装时不需要任何安装期脚本,不会触发 pnpm 的「构建脚本被拦截,请放行」提示。**改动 `src/` 后请重新 `pnpm build` 并把 `lib/` 一并提交**,否则用户拿到的是旧产物。
91
+
92
+ > 注意:`pnpm install` 需用 pnpm 11(`npx pnpm@11`),必要时加 `--config.confirmModulesPurge=false --config.minimumReleaseAge=0`(pnpm 11 默认的 `minimumReleaseAge` 供应链年龄策略会拦截刚发布的 rc 包)。
93
+
94
+ 装好后:模型选择器里会出现 **WorkBuddy XD Pool** 分组;设置 → 插件 → **DSH WorkBuddy XD Pool** 卡片可查看池健康、各账号令牌/积分/签到/冷却,以及「重新检测账号」「清除所有冷却」按钮与每个账号的签到按钮。
95
+
96
+ 插件在 Web / TUI profile 下同样可用(`--profile web` / `--profile dsh-tui`)。
97
+
98
+ ## 命令行
99
+
100
+ 统一用 `dsh plugin --profile desktop exec dsh-workbuddy-xdpool <子命令>` 调用:
101
+
102
+ ```sh
103
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool status # 池账号数/冷却 + shim 状态(--credits 查积分、--json 机器可读、--rates 看倍率)
104
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts # 已发现账号(--json)
105
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool doctor # 诊断发现/冷却/上游连通性
106
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool reset # 立即清除所有 429 冷却
107
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin # 查询每个账号今日签到状态(--json 机器可读)
108
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin all
109
+ # 领取所有账号今日签到奖励;也可传账号标签只领一个
110
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool login # 引导如何在桌面再加一个账号入池
111
+ ```
112
+
113
+ ## 池里怎么多账号?
114
+
115
+ 池走**自动发现**:WorkBuddy 桌面 App 每次登录都会在本机留下一个带令牌的历史快照,XD Pool 扫描这些快照,把每个账号都吸收入池。因此多账号 = 在 WorkBuddy 桌面 App 里逐个登录 / 切换账号即可,之后点卡片「重新检测账号」或重启 DSH,新账号自动成为池成员。
116
+
117
+ 若你想对桌面 App 之外的某个登录做**显式快照**(例如临时固定某个账号再验证),也可手动导入:
118
+
119
+ ```sh
120
+ # 在 WorkBuddy 桌面 App 登录账号后(key 自己起名):
121
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool import myKey
122
+ # 查看/删除已导入快照:
123
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts
124
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool remove myKey
125
+ ```
126
+
127
+ 导入快照以 key 的 **MD5 前 8 位**命名落在 `~/.dsh/.workbuddy-xdpool/`(key 本身记在文件里),中文、带 `/`、带空格的 key 都安全;长期使用靠 refresh token 自动续期,失效则回到桌面重新登录后 `import <key> --force` 覆盖。
128
+
129
+ ## 配置
130
+
131
+ 池的有效配置经插件设置节(`settings.workbuddy-xdpool`)读取,模型设置页可改,改动即时生效:
132
+
133
+ | 字段 | 说明 | 默认 |
134
+ | --- | --- | --- |
135
+ | `authFile` | 覆盖 WorkBuddy 桌面 auth 文件路径(跨平台探测异常时用,等价于 `WORKBUDDY_AUTH_FILE`) | 自动探测 |
136
+ | `cooldownMs` | 单账号 429 冷却时长(毫秒) | `60000` |
137
+
138
+ 也可直接写在 `~/.dsh/settings.yaml`:
139
+
140
+ ```yaml
141
+ workbuddy-xdpool:
142
+ cooldownMs: 120000
143
+ ```
144
+
145
+ ## 架构
146
+
147
+ - **宿主侧**(`src/`,DSH 主进程内):
148
+ - `index.ts` —— 注册 `workbuddy-xdpool` provider、`workbuddy-xdpool` 设置节(`settings.installSection`)、4 条同源路由(状态 / 重新检测 / 清除冷却 / 签到)、账号发现与模型目录播种。
149
+ - `accounts.ts` —— `WorkBuddyAccountPool`:读本机 WorkBuddy 桌面 auth 快照、429 冷却、round-robin failover 与 token 刷新。
150
+ - `catalog.ts` / `upstream.ts` —— 上游模型目录(含每模型积分倍率、免费/图片能力标签)、积分查询与每日签到的上游客户端(按凭据域名自动切换国内 / 国际版域名)。
151
+ - `web-status.ts` / `status-paths.ts` —— 卡片消费的同源状态文档与路由;签到是本插件唯一的写操作,按「POST + 回环来源 + 显式 accountId + 领取前二次确认」四重守卫。
152
+ - `bin.ts` —— 上述 CLI。
153
+ - **客户端**(`src/client/`,浏览器卡片,经 `dsh.client` 由宿主加载):折叠卡片外壳沿用宿主内置卡的 `dsm-plugin-card*` 样式语言(`--dsw-alias-*` 主题变量),内容用 `dsm-workbuddy-xdpool-*` 前缀,绝不污染宿主其它卡片;命名空间 `settings.workbuddy-xdpool`。
154
+ - **构建**:`tsdown` 产出 `lib/index.js`(宿主入口)+ `lib/index.d.ts`(类型)+ `lib/bin.js`(CLI)+ `lib/client.js`(CJS,`window.__ModuleLoader__.load` 包裹的浏览器 bundle)。四个产物均随仓库提交,因此安装时不需要任何构建脚本。
155
+
156
+ ## 已知限制
157
+
158
+ - **仅使用本机桌面 App 的账号**:池不会、也无法替你发起 WorkBuddy 的登录/扫码(token 由 WorkBuddy 桌面 App 自己的腾讯 SSO 登录铸造并设备绑定)。加池账号 = 在 WorkBuddy 桌面 App 里登录/切换,XD Pool 自动吸收。
159
+ - 依赖 WorkBuddy 客户端接口(非官方开放 API),WorkBuddy 更新后插件可能需要随之调整;若某账号 refresh token 失效,回到桌面重新登录即可。
160
+ - 若 Windows 与 Linux 用户名不同且 Windows 环境变量未传入 WSL,请用 `WORKBUDDY_AUTH_FILE` 或配置节的 `authFile` 指定实际位置。
161
+
162
+ ## 免责声明
163
+
164
+ - 本项目**仅供个人学习和研究使用**,仅驱动使用者自己的 WorkBuddy 账号在本机调用,请勿用于商业用途或超出个人合理使用的场景。
165
+ - 使用者需遵守 WorkBuddy 的服务条款;因使用本项目产生的任何后果(包括但不限于账号被限制、额度被清空、服务中断),由使用者自行承担。
166
+ - 本项目作者不对任何因使用或滥用本项目产生的直接或间接损失负责。
167
+ - 本项目与腾讯、WorkBuddy、DeepSeek 均无关联,未获其授权或认可;文中出现的名称仅用于描述兼容关系,其商标权利归各自所有。
168
+
169
+ ## 致谢
170
+
171
+ 本项目的实现参考了以下已公开的项目,并按其许可证要求保留版权声明。参考方向为**设计思路与既有结论**,代码为独立实现;关键模块在源文件头部注释中也标注了所参考的项目与模式:
172
+
173
+ - [corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)(MIT)—— 设置节注册(`settings.installSection`)与 DSH 插件结构、客户端卡片加载机制、桌面端凭据刷新与 loopback shim 加固的核心参照;本项目沿用其「宿主通过 installSection 挂卡片」的打通路径。
174
+ - [dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy)(MIT,Copyright (c) 2026 LaoDing)—— `dsm-plugin-card*` 卡片样式语言与 `--dsw-alias-*` 主题变量的参照实现;**每日签到**(`/v2/billing/meter/checkin-activity-status` 与 `/v2/billing/meter/daily-checkin`)、积分包聚合口径(月度周期套餐 / 一次性礼包区分)与国内 / 国际版按 `domain` 选择上游域名的做法,参考了该项目已验证的接口形态。
175
+ - [Sliverkiss/workbuddy2api](https://github.com/Sliverkiss/workbuddy2api)(MIT)—— WorkBuddy 上游协议(`copilot.tencent.com` 的 wire behavior)与积分接口的参照实现。
176
+
177
+ 以上项目的版权归各自作者所有。本项目采用**参考设计思路 + 独立实现**的方式,未整体复制任何参考项目的源码。若标注有遗漏或不当之处,欢迎提交 issue 指正。
178
+
179
+ ## 许可证
180
+
181
+ [MIT](./LICENSE)
package/lib/bin.js CHANGED
@@ -1154,13 +1154,30 @@ function formatRates(status) {
1154
1154
  }
1155
1155
  return lines.join("\n");
1156
1156
  }
1157
+ //#endregion
1158
+ //#region src/index.ts
1159
+ /**
1160
+ * One region's model-selection schema.
1161
+ *
1162
+ * Every field is optional on purpose: an absent field keeps its documented
1163
+ * meaning ("all enabled" / "follow the upstream image flag" / "no cap"), and a
1164
+ * region that has never been saved stays absent so `applyConfigFromSource` can
1165
+ * fall back to the legacy flat keys.
1166
+ */
1167
+ const modelSelectionSchema = z.object({
1168
+ enabledModelIds: z.array(z.string()).description("Model ids enabled in this region's picker (absent = all)"),
1169
+ imageModelIds: z.array(z.string()).description("Model ids accepting image input in this region (absent = follow upstream)"),
1170
+ contextBudgets: z.dict(z.number().step(1).min(1)).description("Per-model context-window override for this region")
1171
+ });
1157
1172
  z.object({
1158
1173
  authFile: z.string().description("WorkBuddy desktop auth file (defaults to the app own location)"),
1159
1174
  cooldownMs: z.number().step(1).min(1e3).default(6e4).description("Rate-limit cooldown per account, in milliseconds"),
1160
1175
  distribution: z.union(["priority", "round-robin"]).default("priority").description("How requests are spread: priority (drain one) or round-robin"),
1161
- enabledModelIds: z.array(z.string()).default([]).description("Model ids enabled in the picker (empty = all)"),
1162
- imageModelIds: z.array(z.string()).default([]).description("Model ids accepting image input (empty = follow upstream)"),
1163
- contextBudgets: z.dict(z.number().step(1).min(1)).default({}).description("Per-model context-window override, keyed by model id")
1176
+ enabledModelIds: z.array(z.string()).default([]).description("Legacy shared model-id list; used by a region that has no per-region selection yet"),
1177
+ imageModelIds: z.array(z.string()).default([]).description("Legacy shared image-id list; used by a region that has no per-region selection yet"),
1178
+ contextBudgets: z.dict(z.number().step(1).min(1)).default({}).description("Legacy shared context budgets; used by a region with no per-region selection yet"),
1179
+ modelSelectionCn: modelSelectionSchema.description("Model selection for the domestic gateway"),
1180
+ modelSelectionGlobal: modelSelectionSchema.description("Model selection for the international gateway")
1164
1181
  });
1165
1182
  /** Assemble the runtime objects without registering anything. */
1166
1183
  /**
package/lib/client.js CHANGED
@@ -379,7 +379,19 @@ window.__ModuleLoader__.load({
379
379
  * is touched the draft takes over and is what the Save button posts. Discard
380
380
  * drops it back to the copy the server last reported.
381
381
  */
382
- const [draft, setDraft] = (0, react.useState)(void 0);
382
+ const [draftByRegion, setDraftByRegion] = (0, react.useState)({});
383
+ /**
384
+ * The draft for the tab on screen. Keyed by region: the two gateways have
385
+ * different rosters, so edits made on one tab must not leak into the other
386
+ * when the user switches tabs (or saves).
387
+ */
388
+ const draft = draftByRegion[activeRegion];
389
+ const setDraft = (next) => {
390
+ setDraftByRegion((prev) => ({
391
+ ...prev,
392
+ [activeRegion]: next
393
+ }));
394
+ };
383
395
  const [savingModels, setSavingModels] = (0, react.useState)(false);
384
396
  const mounted = (0, react.useRef)(true);
385
397
  (0, react.useEffect)(() => {
@@ -600,9 +612,12 @@ window.__ModuleLoader__.load({
600
612
  const imageModelIds = Object.entries(draft).filter(([, e]) => e.images).map(([id]) => id);
601
613
  const contextBudgets = {};
602
614
  for (const [id, entry] of Object.entries(draft)) if (entry.budget !== void 0) contextBudgets[id] = entry.budget;
603
- await write.call(settingsScope, "enabledModelIds", enabledModelIds);
604
- await write.call(settingsScope, "imageModelIds", imageModelIds);
605
- await write.call(settingsScope, "contextBudgets", contextBudgets);
615
+ const key = activeRegion === "cn" ? "modelSelectionCn" : "modelSelectionGlobal";
616
+ await write.call(settingsScope, key, {
617
+ enabledModelIds,
618
+ imageModelIds,
619
+ contextBudgets
620
+ });
606
621
  setDraft(void 0);
607
622
  if (mounted.current) setFlash(t?.("row.modelsSaved") ?? "Saved");
608
623
  } catch (cause) {
package/lib/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { Api, Model } from "@earendil-works/pi-ai";
3
3
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
4
- import { Context } from "@deepseek-ai/cordis";
4
+ import { Context, Context as Context$1 } from "@deepseek-ai/cordis";
5
5
  import { SettingsNamespace } from "@deepseek-ai/dsh-settings";
6
6
  //#region src/upstream.d.ts
7
7
  /** Upstream failure classes the shim maps onto distinct HTTP answers. */
@@ -445,6 +445,8 @@ export declare const WORKBUDDY_POOL_PROVIDER = "workbuddy-xdpool";
445
445
  interface WorkBuddyAdapterOptions {
446
446
  shim: WorkBuddyShim;
447
447
  catalog: WorkBuddyCatalog;
448
+ /** Plugin context; the pi-ai adapter reads `attachments`/`fs` from it. */
449
+ ctx: Context$1;
448
450
  providerId?: string;
449
451
  displayName?: string;
450
452
  }
@@ -741,9 +743,25 @@ export interface Config {
741
743
  * user cap a model without touching the catalog.
742
744
  */
743
745
  contextBudgets?: Record<string, number>;
746
+ /**
747
+ * Per-region model selection. The two gateways advertise different rosters, so
748
+ * one shared list would let a save on one tab silently rewrite the other tab's
749
+ * picker. Each region owns its own copy; a region with no entry falls back to
750
+ * the legacy flat keys above, so an upgrade keeps the list already in use.
751
+ */
752
+ modelSelectionCn?: ModelSelectionConfig;
753
+ modelSelectionGlobal?: ModelSelectionConfig;
754
+ }
755
+ /** One region's saved model selection. */
756
+ export interface ModelSelectionConfig {
757
+ enabledModelIds?: string[];
758
+ imageModelIds?: string[];
759
+ contextBudgets?: Record<string, number>;
744
760
  }
745
761
  /** Upper bound the card offers as the "default" context window, in tokens. */
746
762
  export declare const DEFAULT_CONTEXT_BUDGET = 200000;
763
+ /** Settings key holding one region's saved selection. */
764
+ export declare const modelSelectionKeyFor: (region: 'cn' | 'global') => string;
747
765
  /**
748
766
  * Plugin configuration schema.
749
767
  *
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@ import { readFile, readdir } from "node:fs/promises";
5
5
  import { homedir } from "node:os";
6
6
  import { createProvider } from "@earendil-works/pi-ai";
7
7
  import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
8
- import { resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
8
+ import { resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
9
9
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
10
10
  import { createServer } from "node:http";
11
11
  import { Readable } from "node:stream";
@@ -1344,7 +1344,9 @@ function createWorkBuddyAdapter(options) {
1344
1344
  adapter: new PiAiAdapter({
1345
1345
  profiles: () => profiles,
1346
1346
  auth: INERT_AUTH,
1347
- resolveApiKey: async () => shim.token()
1347
+ resolveApiKey: async () => shim.token(),
1348
+ resolveAttachments: () => options.ctx.get("attachments"),
1349
+ resolveImageAccess: (attachments, ref) => resolveImageAttachmentAccess(attachments, (hostPath) => options.ctx.get("fs")?.processPathFromHostPath(hostPath), ref)
1348
1350
  }),
1349
1351
  buildModels,
1350
1352
  invalidate: () => {
@@ -2023,11 +2025,14 @@ function registerPoolStatusRoute(ctx, deps) {
2023
2025
  if (!loopbackOrigin(req)) return json(res, 403, { error: "origin-not-trusted" });
2024
2026
  if (deps.saveSelection === void 0) return json(res, 503, { error: "settings service unavailable; model selection cannot be saved" });
2025
2027
  try {
2026
- const selection = parseSelection(await readJsonBody(req));
2028
+ const body = await readJsonBody(req);
2029
+ const selection = parseSelection(body);
2027
2030
  if (selection === void 0) return json(res, 400, { error: "invalid selection payload" });
2028
- await deps.saveSelection(selection);
2031
+ const region = body["region"] === "global" ? "global" : "cn";
2032
+ await deps.saveSelection(region, selection);
2029
2033
  json(res, 200, {
2030
2034
  ok: true,
2035
+ region,
2031
2036
  selection
2032
2037
  });
2033
2038
  } catch (error) {
@@ -2067,6 +2072,21 @@ const WORKBUDDY_POOL_SETTINGS_NS = "workbuddy-xdpool";
2067
2072
  /** Upper bound the card offers as the "default" context window, in tokens. */
2068
2073
  const DEFAULT_CONTEXT_BUDGET = 2e5;
2069
2074
  /**
2075
+ * One region's model-selection schema.
2076
+ *
2077
+ * Every field is optional on purpose: an absent field keeps its documented
2078
+ * meaning ("all enabled" / "follow the upstream image flag" / "no cap"), and a
2079
+ * region that has never been saved stays absent so `applyConfigFromSource` can
2080
+ * fall back to the legacy flat keys.
2081
+ */
2082
+ const modelSelectionSchema = z.object({
2083
+ enabledModelIds: z.array(z.string()).description("Model ids enabled in this region's picker (absent = all)"),
2084
+ imageModelIds: z.array(z.string()).description("Model ids accepting image input in this region (absent = follow upstream)"),
2085
+ contextBudgets: z.dict(z.number().step(1).min(1)).description("Per-model context-window override for this region")
2086
+ });
2087
+ /** Settings key holding one region's saved selection. */
2088
+ const modelSelectionKeyFor = (region) => region === "cn" ? "modelSelectionCn" : "modelSelectionGlobal";
2089
+ /**
2070
2090
  * Plugin configuration schema.
2071
2091
  *
2072
2092
  * Mirrors the shape the settings section stores. Every field carries a default
@@ -2080,9 +2100,11 @@ const Config = z.object({
2080
2100
  authFile: z.string().description("WorkBuddy desktop auth file (defaults to the app own location)"),
2081
2101
  cooldownMs: z.number().step(1).min(1e3).default(6e4).description("Rate-limit cooldown per account, in milliseconds"),
2082
2102
  distribution: z.union(["priority", "round-robin"]).default("priority").description("How requests are spread: priority (drain one) or round-robin"),
2083
- enabledModelIds: z.array(z.string()).default([]).description("Model ids enabled in the picker (empty = all)"),
2084
- imageModelIds: z.array(z.string()).default([]).description("Model ids accepting image input (empty = follow upstream)"),
2085
- contextBudgets: z.dict(z.number().step(1).min(1)).default({}).description("Per-model context-window override, keyed by model id")
2103
+ enabledModelIds: z.array(z.string()).default([]).description("Legacy shared model-id list; used by a region that has no per-region selection yet"),
2104
+ imageModelIds: z.array(z.string()).default([]).description("Legacy shared image-id list; used by a region that has no per-region selection yet"),
2105
+ contextBudgets: z.dict(z.number().step(1).min(1)).default({}).description("Legacy shared context budgets; used by a region with no per-region selection yet"),
2106
+ modelSelectionCn: modelSelectionSchema.description("Model selection for the domestic gateway"),
2107
+ modelSelectionGlobal: modelSelectionSchema.description("Model selection for the international gateway")
2086
2108
  });
2087
2109
  /** Live API, published for the CLI. */
2088
2110
  let api;
@@ -2147,19 +2169,19 @@ function apply(ctx, config = {}) {
2147
2169
  }
2148
2170
  };
2149
2171
  const applyConfigFromSource = () => {
2150
- const { authFile, cooldownMs, distribution, enabledModelIds, imageModelIds, contextBudgets } = current();
2172
+ const { authFile, cooldownMs, distribution, enabledModelIds, imageModelIds, contextBudgets, modelSelectionCn, modelSelectionGlobal } = current();
2151
2173
  core.pool.applyConfig({
2152
2174
  ...authFile === void 0 ? {} : { authDirs: [dirname(authFile)] },
2153
2175
  ...cooldownMs === void 0 ? {} : { cooldownMs },
2154
2176
  distribution: distribution ?? "priority"
2155
2177
  });
2156
- const selection = {
2178
+ const legacySelection = {
2157
2179
  ...enabledModelIds === void 0 ? {} : { enabledModelIds },
2158
2180
  ...imageModelIds === void 0 ? {} : { imageModelIds },
2159
2181
  ...contextBudgets === void 0 ? {} : { contextBudgets }
2160
2182
  };
2161
- core.catalogs.cn.applySelection(selection);
2162
- core.catalogs.global.applySelection(selection);
2183
+ core.catalogs.cn.applySelection(modelSelectionCn ?? legacySelection);
2184
+ core.catalogs.global.applySelection(modelSelectionGlobal ?? legacySelection);
2163
2185
  };
2164
2186
  const settingsService = ctx.settings;
2165
2187
  if (typeof settingsService.installSection === "function") settingsService.installSection(ctx, WORKBUDDY_POOL_SETTINGS_NS, Config, config, sectionHooks);
@@ -2218,10 +2240,12 @@ function apply(ctx, config = {}) {
2218
2240
  catalogs: core.catalogs,
2219
2241
  client: core.client,
2220
2242
  shim: () => shimInfo("cn"),
2221
- saveSelection: (selection) => {
2222
- setSetting("enabledModelIds", selection.enabledModelIds);
2223
- setSetting("imageModelIds", selection.imageModelIds);
2224
- setSetting("contextBudgets", selection.contextBudgets);
2243
+ saveSelection: (region, selection) => {
2244
+ setSetting(modelSelectionKeyFor(region), {
2245
+ ...selection.enabledModelIds === void 0 ? {} : { enabledModelIds: [...selection.enabledModelIds] },
2246
+ ...selection.imageModelIds === void 0 ? {} : { imageModelIds: [...selection.imageModelIds] },
2247
+ ...selection.contextBudgets === void 0 ? {} : { contextBudgets: { ...selection.contextBudgets } }
2248
+ });
2225
2249
  }
2226
2250
  }));
2227
2251
  api = {
@@ -2253,12 +2277,14 @@ function apply(ctx, config = {}) {
2253
2277
  try {
2254
2278
  const adaptersByRegion = {
2255
2279
  cn: createWorkBuddyAdapter({
2280
+ ctx,
2256
2281
  shim: shims.cn,
2257
2282
  catalog: core.catalogs.cn,
2258
2283
  providerId: POOL_PROVIDER_BY_REGION.cn,
2259
2284
  displayName: POOL_NAME_BY_REGION.cn
2260
2285
  }),
2261
2286
  global: createWorkBuddyAdapter({
2287
+ ctx,
2262
2288
  shim: shims.global,
2263
2289
  catalog: core.catalogs.global,
2264
2290
  providerId: POOL_PROVIDER_BY_REGION.global,
@@ -2350,4 +2376,4 @@ function apply(ctx, config = {}) {
2350
2376
  });
2351
2377
  }
2352
2378
  //#endregion
2353
- export { Config, DEFAULT_CONTEXT_BUDGET, FALLBACK_WORKBUDDY_MODELS, POOL_CHECKIN_PATH, POOL_MODELS_SAVE_PATH, POOL_RESCAN_PATH, POOL_RESET_COOLDOWN_PATH, POOL_STATUS_PATH, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_LIVE_FILENAME, WORKBUDDY_POOL_PROVIDER, WORKBUDDY_POOL_SETTINGS_NS, WorkBuddyAccountPool, WorkBuddyCatalog, WorkBuddyUpstreamClient, apply, buildStatus, candidateAuthDirs, classifyUpstreamError, createCore, createWorkBuddyAdapter, createWorkBuddyShim, currentApi, defaultDesktopAuthDirs, formatRates, formatStatus, inject, name, parseRateLimitReset, parseWorkBuddyAuth, setApi, workbuddyAccountId };
2379
+ export { Config, DEFAULT_CONTEXT_BUDGET, FALLBACK_WORKBUDDY_MODELS, POOL_CHECKIN_PATH, POOL_MODELS_SAVE_PATH, POOL_RESCAN_PATH, POOL_RESET_COOLDOWN_PATH, POOL_STATUS_PATH, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_LIVE_FILENAME, WORKBUDDY_POOL_PROVIDER, WORKBUDDY_POOL_SETTINGS_NS, WorkBuddyAccountPool, WorkBuddyCatalog, WorkBuddyUpstreamClient, apply, buildStatus, candidateAuthDirs, classifyUpstreamError, createCore, createWorkBuddyAdapter, createWorkBuddyShim, currentApi, defaultDesktopAuthDirs, formatRates, formatStatus, inject, modelSelectionKeyFor, name, parseRateLimitReset, parseWorkBuddyAuth, setApi, workbuddyAccountId };