dsh-workbuddy-xdpool 1.2.0 → 1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,53 @@
4
4
 
5
5
  版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
6
6
 
7
+ ## 1.3.0 (2026-09-23)
8
+
9
+ 上一版把八类任务链接了进去,这一版修的是**「接进去了但你看不见」**的问题——功能在跑,界面上却没显示,等于白做。
10
+
11
+ ### 猫猫旅行一天只跑一趟,等于坏了一半
12
+
13
+ 旅行的流程是「派出去 → 回来领奖」,需要**两趟**。原来默认时段只配了 12:00 一个点,意味着猫早上出去之后,**要等到第二天**才有人管它。
14
+
15
+ 改成 **09:00 + 21:00 两趟**(早上派出去,晚上回来领)。同时把每个任务的「今天跑过了」判断从**按日期**改成**按时间点**——按日期判断会把晚上那趟直接吞掉,这个守卫跟多时点本身是冲突的。单时点任务(签到、上报、任务奖励、连登)行为不变,同一小时内重复检查仍然不会重复跑。
16
+
17
+ > 参考项目的默认值就是 `[9, 21]`,注释里写的理由是「一趟派出 + 一趟领奖闭环」。
18
+
19
+ ### 卡片现在告诉你「具体领到了什么」
20
+
21
+ 原来每个任务只显示「日期 · 账号数」,回答不了「我最在意的那个任务领到了没」。
22
+
23
+ 现在会**列出这一轮实际领到的奖励名**:
24
+
25
+ - 任务奖励 → 列出台任务标题(如「体验设计创意模式」「召唤 3 次专家团」)
26
+ - 每日签到 → `签到 +100`
27
+ - 猫猫旅行 → `猫猫旅行 +10`
28
+
29
+ 连登奖励还没到解锁天数时,会显示**还差几天**(如 `7d in 4d`)——所有档位都是未解锁属于正常状态,但只写「未解锁」会让人以为坏了。
30
+
31
+ ### 修掉「猫猫旅行永远不显示」
32
+
33
+ 有个真 bug:积分自动化面板里只显示签到 / 上报 / 任务 / 连登四行,**猫猫旅行那行永远不出现**,尽管它确实在跑。
34
+
35
+ 根因是那行任务的判断逻辑里,**列表本身就是缺的**——界面渲染一行一个任务,而列表里没有 travel,分支里也没有,类型定义里同样没有。这类「加了功能忘了加列表」的失败模式是**静默的**:任务照跑,用户看不见,也不会报错。
36
+
37
+ 除了补齐三处,还加了个**防回归测试**:卡片的任务列表必须和调度器的任务列表逐项相等、顺序一致。这个不变量之前没人守。
38
+
39
+ ### 其它
40
+
41
+ - 行内文案「未排程 / 尚未执行」改成「当天不跑 / 还没跑过」,原来的说法不好懂。
42
+ - 补上中英两份缺失的「猫猫旅行」标签(之前那行显示的是原始 key)。
43
+ - README 重写(中英双语):按「多账号池」和「积分自动化」两块组织,把 13 个任务名列出来、实测数据写进去,并把几个踩坑结论翻译成大白话。
44
+ - 截图更新:卡片全貌换成含自动化面板的新版,另加一张自动化面板特写。
45
+ - 测试从 134 增至 139。
46
+
47
+ ### 已知限制(未变)
48
+
49
+ - `Expert_Philanthropy`(公益专家)需要真实捐款,不做。
50
+ - `black_cat`(夜猫子)只在 23:00–08:00 计分且无积分奖励,不做。
51
+ - `Model_chat_GLM5.2` 与 `wb_wechat_oa_subscribe_task` 留待下个版本。
52
+ - `Expert_team_use_3` 偶发卡在 2/3(怀疑单账号当日额度,跨天恢复,未验证;新账号不受影响)。
53
+
7
54
  ## 1.2.0 (2026-09-23)
8
55
 
9
56
  把成长中心剩下的**八类客户端任务**也接进自动化。这些任务在任务中心里写的是「升级桌面客户端后点击某个入口」,听起来只有人坐在真实客户端前才能完成;实际上计分器看的是**行为事件流**,只要事件形状对、指纹对,任务就会涨进度。这一轮把每条链都在真实账号上跑通并回读确认了计分。
package/README.en.md CHANGED
@@ -9,36 +9,83 @@
9
9
 
10
10
  English | [中文](./README.md)
11
11
 
12
- Merge **every WorkBuddy account** you have ever signed into on this machine into a single **DeepSeek Harness model pool**. No manual setup: each account you sign in on the WorkBuddy desktop app automatically becomes a pool member, and when one account gets rate-limited (HTTP 429), requests automatically fail over to the next healthy account.
12
+ Every account you've signed into the WorkBuddy desktop app, merged into one DSH model pool. However many accounts you've used, that's how many the pool holds — no forms, no manual import.
13
13
 
14
- > Unlike single-account connectors (e.g. `dsh-workbuddy-connect`), XD Pool treats multi-account as the norm — it never picks or imports an account by hand. It scans every historical sign-in snapshot left by the WorkBuddy desktop app, merges them all into one shared pool, and exposes them as a single `workbuddy-xdpool` provider group whose requests auto-fail-over across members.
14
+ An account gets rate-limited or runs out of credits? The request just moves to the next one that works. You keep asking; it keeps handling the rest.
15
15
 
16
- **Settings card (Settings → Plugins → DSH WorkBuddy XD Pool)**
16
+ **It also throws in a full points-automation suite**: check-ins, task rewards, streak redemption, the buddy trip — all the chores you'd otherwise click through every day, done in the background.
17
17
 
18
- ![WorkBuddy pool settings card: domestic/international tab strip, pool health, per-account panels, credit packages and totals, per-account daily check-in, and model management (enable, image input, context window)](assets/settings-card.png)
18
+ > **How this differs from the single-account connectors**: plugins like `dsh-workbuddy-connect` handle one account at a time. XD Pool treats **multiple accounts as the normal case** — it doesn't pick favourites or ask you to import anything. Every sign-in snapshot on this machine goes into one shared pool, exposed as a single `workbuddy-xdpool` provider group, with automatic failover inside.
19
19
 
20
- **Model picker (domestic and international appear as two separate supplier groups; the rate multiplier is baked into model.name because the DSH 0.1.2 composer only reads name)**
20
+ ## Screenshots
21
21
 
22
- ![Model picker: the domestic and international suppliers each form their own group, with the credit rate and promo badge shown next to every model name](assets/model-picker.png)
22
+ **The plugin card (Settings → Plugins → DSH WorkBuddy XD Pool)**
23
23
 
24
- **Domestic / international dual suppliers (each with its own accounts, credits and models, usable at the same time)**
24
+ ![WorkBuddy pool card: CN/global tabs, pool health, account panels with credit packages, per-account check-in, the points-automation panel, and the model directory](assets/settings-card.png)
25
25
 
26
- ![Switching between the domestic and international tabs: the side with no sign-in lists the steps for that region](assets/region-tabs.png)
26
+ **The points-automation panel**
27
+
28
+ ![Points automation: the master switch, a Run now button, each job's last run with the tasks it actually collected, and the streak countdown](assets/automation-panel.png)
29
+
30
+ **Model picker (CN and global as two separate groups)**
31
+
32
+ ![Model picker: the CN and global groups each list their own models with credit multipliers and promo tags](assets/model-picker.png)
33
+
34
+ **CN / global dual provider (separate accounts, credits and models, usable at the same time)**
35
+
36
+ ![Switching between CN and global; the side that is not signed in shows that region's sign-in steps](assets/region-tabs.png)
27
37
 
28
38
  ## Features
29
39
 
30
- - **Zero-config**: install, enable, done. Every account signed into the WorkBuddy desktop app is auto-discovered into the rotation pool on first use.
31
- - **Automatic failover**: the pool tracks each account's `429` cooldown. A cooling account is skipped in favor of the next healthy one; cooldowns expire automatically. Requests pause only when every account is cooling at once.
32
- - **Pool health at a glance**: the settings card shows pool health (N accounts / X cooling, which account is next), each account's token expiry, and cooldown countdowns.
33
- - **Live remaining credits**: per-account credit packages (`package · remain / size`) and a big green total, refreshed from upstream in real time.
34
- - **Annotated model catalog**: the card lists pool models with their credit multiplier (e.g. `GLM-5.2 · x0.79`), free / limited-free / night-discount tags, image-input capability, and context window — kept live from upstream `credits` / `tags`.
35
- - **Daily check-in**: below each account's credits the card offers a check-in button showing the current streak, the per-day credit, and any milestone bonus. Collect an account's reward with one click — several accounts can be collected in turn, with no need to switch the pool's preferred account first. The status is re-read before claiming, so an account that already collected today is **never double-collected**. Also available as the `checkin` CLI command.
36
- - **CN / global region auto-detection**: the upstream origin is picked per account from its login domain — a global sign-in (`workbuddy.ai`) uses `www.workbuddy.ai`, while a CN sign-in (the default) uses `copilot.tencent.com` and `www.codebuddy.cn`. Both regions can coexist in one pool, each account talking to its own region, with rotation and failover working across both.
37
- - **Three manual actions**: re-detect desktop sign-ins, clear all cooldowns, and daily check-in — available both on the card and via the CLI.
40
+ ### 1. The multi-account pool
41
+
42
+ - **Zero config**: install it, turn it on, done. Every account signed into the WorkBuddy desktop app is discovered and pooled automatically — nothing to enter by hand.
43
+
44
+ - **Automatic failover**: the pool tracks each account's rate-limit state. Hit a 429 and that account cools down while later requests land on the next healthy one; it rejoins automatically once the cooldown ends. Requests only wait when every account is cooling.
45
+
46
+ - **Pool health at a glance**: the card shows how many accounts there are, how many are cooling, which one is next, plus each account's token expiry and cooldown countdown.
47
+
48
+ - **Live credit balances**: per-account credit packages (`package · remaining / total`) with a large total, refreshed from upstream as it changes.
49
+
50
+ - **An annotated model catalog**: pool models with their credit multiplier (e.g. `GLM-5.2 · x0.79`), free / limited-free / night-discount tags, image-input support and context window — all tracking upstream live.
51
+
52
+ - **CN / global auto-detection**: the upstream origin is picked per account from its login domain — a global sign-in (`workbuddy.ai`) goes to `www.workbuddy.ai`, CN (the default) to `copilot.tencent.com`. Both kinds can live in the same pool.
53
+
54
+ - **Manual controls when you want them**: re-detect accounts, clear all cooldowns, and daily check-in — on the card and in the CLI.
55
+
56
+ ### 2. Points automation
57
+
58
+ One switch on the card, and the plugin works through your growth-centre routine for you. **On by default, and you can hit "Run now" to force a pass whenever you like.**
59
+
60
+ Five jobs:
61
+
62
+ | Job | Default time | What it does |
63
+ | --- | --- | --- |
64
+ | Daily check-in | 09:00 | Collect today's check-in reward |
65
+ | Activity report | 10:00 | Report activity so the growth centre counts the day |
66
+ | Task rewards | 11:00 | Enrol in open tasks and collect every reward that's ready |
67
+ | Streak bonus | 12:00 | Redeem streak tiers and spend the lottery draws |
68
+ | Buddy trip | 09:00 · 21:00 | Send the cat out, collect it when it's back |
69
+
70
+ **The task-rewards pass covers 13 board tasks**, including the ones that read as "somebody has to sit at the desktop and click these menus":
71
+
72
+ > Browse inspiration · Use 5 templates · Try a hot skill · Apply the "Peacekeeper Elite" theme · Read the library · Summon 5 experts · Summon 3 expert teams · Try the Tencent Lighthouse expert · Open any assistant app · Enter the QQ teacher assistant · Create a design canvas · Create a scheduled task · Chat with the cat
73
+
74
+ Measured across four accounts, one pass collected **3300 credits and 135 energy**.
75
+
76
+ **The card tells you what a pass actually collected** — it lists the task names rather than reporting a bare count. The streak countdown is spelled out too ("7d in 4d"), so a locked tier doesn't look like a failure.
77
+
78
+ **"Earned today"** breaks the day down by source — task rewards, check-in, streak, buddy trip — and resets at midnight.
79
+
80
+ **Credit floor per account**: set a minimum balance and an account below it stops being given work, so you can keep some credits in reserve.
81
+
82
+ > **The global region doesn't show this panel** — that gateway has no growth system at all.
38
83
 
39
84
  ## Install
40
85
 
41
- Prerequisite: WorkBuddy desktop app installed and signed in (the plugin reuses the app's sign-in state; adding accounts = signing in / switching accounts in the desktop app — each is absorbed into the pool automatically). Tested against DSH Desktop host `0.1.2`; compatible with `0.1.1-rc.2` / `0.1.2` (the settings-section install picks `settings.installSection` on `0.1.2-rc.1+`, or the older free function earlier).
86
+ Prerequisite: the WorkBuddy desktop app installed and signed in (the plugin reuses the sign-in state it stores locally). Multi-account = sign in and out a few times in the desktop app; each sign-in is absorbed into the pool. Built against DSH Desktop host `0.1.2`.
87
+
88
+ > Compatible with the `0.1.1-rc.2` / `0.1.2` host line: the settings section installs via `settings.installSection` (0.1.2-rc.1+) or the earlier free-function form, whichever the host supports.
42
89
 
43
90
  **Option A — install from npm (recommended)**
44
91
 
@@ -48,15 +95,15 @@ dsh plugin --profile desktop add dsh-workbuddy-xdpool
48
95
  ```
49
96
 
50
97
  > npm is the fast path: the only dependency pulled in is the plugin itself (**roughly 1 package, a few seconds**).
51
- > Installing from GitHub source also installs the dev toolchain (bundler, test runner, hundreds of packages), which is markedly slower.
98
+ > Installing from the GitHub source drags in the dev dependencies (bundler, test runner, hundreds of packages) and is much slower.
52
99
 
53
- **Option B — install from GitHub source**
100
+ **Option B — install from the GitHub source**
54
101
 
55
102
  ```sh
56
103
  dsh plugin --profile desktop add github:XDTrees/dsh-workbuddy-xdpool
57
104
  ```
58
105
 
59
- **Option C — manual bundle registration**
106
+ **Option C — register the bundle by hand**
60
107
 
61
108
  ```sh
62
109
  # 1) install the package (npm or GitHub)
@@ -68,26 +115,28 @@ dsh plugin --profile desktop add dsh-workbuddy-xdpool
68
115
  # 3) restart DSH Desktop
69
116
  ```
70
117
 
71
- **Build locally** (developers):
118
+ **Building locally (developers)**
72
119
 
73
120
  ```sh
74
121
  pnpm install
75
- pnpm build # outputs lib/index.js + lib/index.d.ts + lib/bin.js + lib/client.js
76
- pnpm test # 26 tests (failover rotation, per-(account,model) cooldowns, check-in guards)
77
- pnpm typecheck # host side
78
- pnpm typecheck:client # client side
122
+ pnpm build # produces lib/index.js + lib/index.d.ts + lib/bin.js + lib/client.js
123
+ pnpm test # 138 tests
124
+ pnpm typecheck # host side
125
+ pnpm typecheck:client # client side
79
126
  ```
80
127
 
81
- > `pnpm install` needs pnpm 11 (`npx pnpm@11`); add `--config.confirmModulesPurge=false --config.minimumReleaseAge=0` if the supply-chain age policy blocks freshly published rc packages.
128
+ > **The build output is committed** (`lib/` is not gitignored), so a GitHub install needs no install-time script and never trips pnpm's "build scripts were blocked" prompt. **After changing `src/`, re-run `pnpm build` and commit `lib/` too** — otherwise users get the old build.
129
+
130
+ > Note: `pnpm install` wants pnpm 11 (`npx pnpm@11`), and may need `--config.confirmModulesPurge=false --config.minimumReleaseAge=0` — pnpm 11's default `minimumReleaseAge` supply-chain policy blocks just-published rc packages.
82
131
 
83
- > **The built output is committed** (`lib/` is no longer ignored). A GitHub install therefore runs
84
- > no install-time script and never hits pnpm's "build scripts are blocked" prompt. **After changing
85
- > `src/`, re-run `pnpm build` and commit `lib/` too**, or users would receive the old build.
132
+ Once installed: a **WorkBuddy XD Pool** group appears in the model picker, and Settings → Plugins → **DSH WorkBuddy XD Pool** shows pool health, each account's token / credits / check-in / cooldown state, the "Detect accounts again" and "Clear all cooldowns" buttons, and a check-in button per account.
86
133
 
87
- After install: a **WorkBuddy XD Pool** group appears in the model picker; Settings → Plugins → **DSH WorkBuddy XD Pool** card shows pool health, per-account tokens/credits/check-in/cooldowns, plus "Detect accounts again" and "Clear all cooldowns" buttons. Works on Web / TUI profiles too (`--profile web` / `--profile dsh-tui`).
134
+ It also works under the Web / TUI profiles (`--profile web` / `--profile dsh-tui`).
88
135
 
89
136
  ## CLI
90
137
 
138
+ All commands run through `dsh plugin --profile desktop exec dsh-workbuddy-xdpool <subcommand>`:
139
+
91
140
  ```sh
92
141
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool status # pool accounts/cooldown + shim state (--credits, --json, --rates)
93
142
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts # discovered accounts (--json)
@@ -131,32 +180,50 @@ workbuddy-xdpool:
131
180
 
132
181
  ## Architecture
133
182
 
134
- - **Host side** (`src/`): registers the `workbuddy-xdpool` provider, the `workbuddy-xdpool` settings section (`settings.installSection`), four same-origin routes (status / re-scan / clear cooldowns / check-in), and account discovery + catalog seeding. The upstream client picks the CN or global origin per credential from its login domain.
135
- - **Client** (`src/client/`): the browser card loaded via `dsh.client`; the collapsible shell reuses the host's `dsm-plugin-card*` style language (`--dsw-alias-*` theme tokens), with content classes namespaced `dsm-workbuddy-xdpool-*`. Check-in is the plugin's only mutating route: POST-only, loopback-origin-only, an explicit per-account `accountId`, and a pre-claim status re-check.
136
- - **Build**: `tsdown` produces `lib/index.js` (host entry) + `lib/index.d.ts` (types) + `lib/bin.js` (CLI) + `lib/client.js` (CJS browser bundle wrapped in `window.__ModuleLoader__.load`). All four are committed, so an install needs no build-time script.
183
+ - **Host side** (`src/`, inside the DSH main process)
184
+ - `index.ts` — registers the `workbuddy-xdpool` provider, the `workbuddy-xdpool` settings section, the same-origin routes (status / re-scan / clear cooldowns / check-in / automation run / credit reserve), plus account discovery and catalog seeding.
185
+ - `accounts.ts` — `WorkBuddyAccountPool`: reads the desktop auth snapshots, 429 cooldowns, rotation and token refresh; account disabling and credit reserves live here too.
186
+ - `scheduler.ts` — the points-automation scheduler: runs five jobs on local time points (check-in / report / tasks / streak / travel), persists the daily earnings ledger, and backs the card's "Run now" button with the same code path.
187
+ - `task-events.ts` — builds the 13 task event chains. Each chain is plain data plus the fingerprint channel it must go out on (desktop or web); the tasks that need a REAL conversation (skill, experts) open one here to get the server-side id.
188
+ - `catalog.ts` / `upstream.ts` — the upstream client: model catalog (with per-model multipliers and free / image tags), credits, check-in, and every automation endpoint, switching CN/global by credential domain.
189
+ - `web-status.ts` / `status-paths.ts` — the same-origin status document and routes the card reads. Mutations are gated on POST + loopback origin + an explicit `accountId`.
190
+ - `bin.ts` — the CLI above.
191
+ - **Client** (`src/client/`, the browser card loaded via `dsh.client`)
192
+ The collapsible shell reuses the host's `dsm-plugin-card*` style language (`--dsw-alias-*` theme tokens); content classes are namespaced `dsm-workbuddy-xdpool-*`, and copy lives under the `settings.workbuddy-xdpool` namespace.
193
+ - **Build**
194
+ `tsdown` produces `lib/index.js` (host entry) + `lib/index.d.ts` (types) + `lib/bin.js` (CLI) + `lib/client.js` (CJS browser bundle wrapped in `window.__ModuleLoader__.load`). All four are committed, so an install needs no build-time script.
137
195
 
138
196
  ## Known limitations
139
197
 
140
- - **Only accounts on this machine**: the pool cannot and will not perform WorkBuddy sign-in / QR auth for you (tokens are minted by the WorkBuddy desktop app's own Tencent SSO and are device-bound). Add accounts by signing in on the desktop app.
141
- - Depends on WorkBuddy client endpoints (not an official public API); may need updates when WorkBuddy changes.
142
- - If Windows/Linux usernames differ and Windows env vars aren't forwarded into WSL, point `WORKBUDDY_AUTH_FILE` or the config `authFile` at the real location.
198
+ - **Only accounts on THIS machine's desktop app**: the pool cannot — and will not — sign you in or scan a QR code (tokens are minted by the WorkBuddy desktop app's own Tencent SSO and bound to the device). Adding an account = signing in on the desktop app; XD Pool absorbs it.
199
+
200
+ - **A few automation tasks are deliberately not implemented**
201
+ - `Expert_Philanthropy` (charity expert): requires a real donation; there is no way around it.
202
+ - `black_cat` (night owl): only scores between 23:00 and 08:00, and pays **no credits** — all-nighters for nothing.
203
+ - `Model_chat_GLM5.2` (chat with a specific model) and `wb_wechat_oa_subscribe_task` (follow the official account): the path is mapped out, deferred to the next release.
204
+
205
+ - **`Expert_team_use_3` occasionally sticks at 2/3**: on one account, after two expert teams had been used that day, the third would not register (using a different, unused team didn't help either). Suspected per-account daily quota that resets the next day, unconfirmed. **Fresh accounts are unaffected** (measured 0/3 → 3/3).
206
+
207
+ - **Depends on WorkBuddy's client APIs** (not an official public API), so a WorkBuddy update may require a matching plugin update. If an account's refresh token lapses, sign in again on the desktop.
208
+
209
+ - If your Windows and Linux usernames differ and the Windows environment variable doesn't reach WSL, point `WORKBUDDY_AUTH_FILE` (or `authFile`) at the real location.
143
210
 
144
211
  ## Disclaimer
145
212
 
146
- - For **personal study and research only** — drives your own WorkBuddy accounts on your own machine. Do not use commercially or beyond reasonable personal use.
147
- - You are responsible for complying with WorkBuddy's terms of service; any consequences (account limits, emptied quotas, outages) are your own.
148
- - The authors are not liable for any direct or indirect loss from using or misusing this project.
149
- - This project is not affiliated with, endorsed by, or authorized by Tencent, WorkBuddy, or DeepSeek; all names belong to their respective owners.
213
+ - This project is **for personal study and research only**. It drives only your own WorkBuddy accounts from your own machine — do not use it commercially or beyond reasonable personal use.
214
+ - You are responsible for complying with WorkBuddy's terms of service. Any consequences of using this project (including but not limited to account restrictions, cleared balances, or service interruption) are yours to bear.
215
+ - The authors accept no liability for any direct or indirect loss arising from the use or misuse of this project.
216
+ - This project is not affiliated with, authorised by, or endorsed by Tencent, WorkBuddy, or DeepSeek. Product names appear only to describe compatibility; trademarks belong to their respective owners.
150
217
 
151
218
  ## Acknowledgments
152
219
 
153
- This project was built with reference to the following public projects, whose copyright notices are retained as their licenses require. The reference is to **design ideas and established findings**; the code is an independent implementation, and the modules that draw on an existing pattern say so in their file headers:
220
+ This project draws on the following public projects and keeps their copyright notices as their licences require. What was taken is **design approach and established findings**; the code is an independent implementation. The modules that lean on a specific source say so in their file headers:
154
221
 
155
- - [corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect) (MIT) — the core reference for settings-section registration (`settings.installSection`) and the DSH plugin structure, client-card loading, desktop credential refresh, and loopback shim hardening.
156
- - [dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy) (MIT, Copyright (c) 2026 LaoDing) — the reference for the `dsm-plugin-card*` card style language and `--dsw-alias-*` theme tokens; the **daily check-in** endpoints (`/v2/billing/meter/checkin-activity-status` and `/v2/billing/meter/daily-checkin`), the credit-package aggregation rules (monthly-cycle vs one-off gift), and picking the upstream origin per credential domain for CN/global follow interface shapes that project had already validated.
157
- - [Sliverkiss/workbuddy2api](https://github.com/Sliverkiss/workbuddy2api) (MIT) — reference implementation of the upstream WorkBuddy protocol (`copilot.tencent.com` wire behavior) and credits endpoints.
222
+ - [corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect) (MIT) — the primary reference for settings-section registration (`settings.installSection`) and the DSH plugin structure, the client card loading mechanism, and desktop credential refresh plus loopback-shim hardening. This project follows its "host mounts the card via installSection" path.
223
+ - [dingminhua/dsh-connect-workbuddy](https://github.com/dingminhua/dsh-connect-workbuddy) (MIT, Copyright (c) 2026 LaoDing) — the reference implementation for the `dsm-plugin-card*` card style language and `--dsw-alias-*` theme tokens; **daily check-in** (`/v2/billing/meter/checkin-activity-status` and `/v2/billing/meter/daily-checkin`), the credit-package aggregation rules (monthly vs one-off packs), and picking the upstream origin per credential `domain` all follow interface shapes that project verified.
224
+ - [Sliverkiss/workbuddy2api](https://github.com/Sliverkiss/workbuddy2api) (MIT) — the reference for the WorkBuddy upstream protocol (`copilot.tencent.com` wire behaviour) and the credits APIs; **points automation** (growth-centre check-in, activity report, streak redemption, the buddy-trip state machine, the 13 task event chains, and the expert summon chains) was ported wholesale from that project's Go reverse-engineering, including the scoring criteria and measured conclusions behind every event chain.
158
225
 
159
- All copyrights belong to their respective authors. This project takes the **reference-the-design, implement-independently** approach and does not wholesale copy any reference project's source. If an attribution is missing or wrong, please open an issue.
226
+ All copyright in the above belongs to their respective authors. This project uses a **reference-the-approach, implement-independently** method and does not copy any reference project's source wholesale. If anything is mis-attributed or missing, please open an issue.
160
227
 
161
228
  ## License
162
229
 
package/README.md CHANGED
@@ -9,15 +9,25 @@
9
9
 
10
10
  [English](./README.en.md) | 中文
11
11
 
12
- 将 WorkBuddy 桌面 App 里登录过的**所有账号**自动并入一个 **DeepSeek Harness 模型池**:无需任何手动配置,你在 WorkBuddy 桌面登录的每个账号都会成为一个池成员;某个账号被限流(429)时,请求会自动切换到下一个可用账号,实现多账号自动容错轮换。
12
+ 把 WorkBuddy 桌面 App 里登录过的**所有账号**,合成一个 DSH 模型池。你在 WorkBuddy 里登过几个号,池里就有几个号——不用手动填表,不用一个个录入。
13
13
 
14
- > 与单账号连接插件(如 dsh-workbuddy-connect)的区别:**XD Pool 把多账号当成常态**——它不挑账号、不做手动导入,而是把本机 WorkBuddy 桌面 App 的所有历史登录快照全部纳入一个共享池,用一个 `workbuddy-xdpool` provider 分组对外暴露,模型请求在池内自动 failover。
14
+ 哪个号被限流了、积分用光了,请求自动换到下一个能用的号上去。你只管问,剩下的它自己处理。
15
+
16
+ **另外附送一整套「积分自动化」**:签到、领任务奖励、连登兑换、开猫猫旅行……这些每天都要手动点一遍的活儿,插件在后台替你干完了。
17
+
18
+ > **和单账号连接插件的区别**:`dsh-workbuddy-connect` 那类插件是「一次一个账号」。XD Pool 把**多账号当默认情况**——不挑号、不手动导入,本机 WorkBuddy 所有历史登录快照全部收进一个共享池,对外用一个 `workbuddy-xdpool` provider 分组,池内自动容错。
19
+
20
+ ## 先看几张图
15
21
 
16
22
  **插件配置卡片(设置 → 插件 → DSH WorkBuddy XD Pool)**
17
23
 
18
- ![WorkBuddy 池设置卡片:国内版 / 国际版切换栏、池健康状态、账号面板、积分包与合计、每账号每日签到、模型管理(启用勾选 / 图片输入 / 上下文窗口)](assets/settings-card.png)
24
+ ![WorkBuddy 池设置卡片:国内版 / 国际版切换栏、池健康状态、账号面板、积分包与合计、每账号每日签到、积分自动化面板、模型管理](assets/settings-card.png)
19
25
 
20
- **模型选择器(国内版 / 国际版 两个独立供应商分组;倍率直接拼进 model.name:DSH 0.1.2 composer 只读 name)**
26
+ **积分自动化面板**
27
+
28
+ ![积分自动化:总开关、立即运行按钮、每个任务的上次执行时间与当轮具体领取到的任务清单、连登解锁倒计时](assets/automation-panel.png)
29
+
30
+ **模型选择器(国内版 / 国际版 两个独立分组)**
21
31
 
22
32
  ![模型选择器:国内版与国际版各占一个分组,每个模型名后显示积分倍率与促销标签](assets/model-picker.png)
23
33
 
@@ -27,27 +37,55 @@
27
37
 
28
38
  ## 功能
29
39
 
30
- - **零配置开箱即用**:安装并启用后,WorkBuddy 桌面 App 里每个已登录账号都会在第一次被请求时自动发现、进入池中轮换。无需在插件里手动录入账号。
40
+ ### 一、多账号池
41
+
42
+ - **零配置**:装上、打开,就完事了。WorkBuddy 桌面 App 里每个登过的账号都会被自动发现并入池,不需要在插件里录任何东西。
31
43
 
32
- - **自动容错轮换**:池维护每个账号的 `429` 冷却状态。当某个账号触发限流进入冷却,后续请求会跳过它、落到下一个健康账号;冷却结束自动恢复。所有账号同时冷却时请求才暂停。
44
+ - **自动容错换号**:池子记着每个账号的限流状态。某个号触发 429 就冷却它,后面的请求自动落到下一个健康的号上;冷却结束自动恢复。所有号都在冷却时,请求才排队等待。
33
45
 
34
- - **账号健康一目了然**:插件设置卡片显示池健康状态(N 账号 / X 冷却、当前会轮到哪个账号)、每个账号的令牌有效期与冷却倒计时。
46
+ - **池健康一眼看清**:卡片显示「几个账号 / 几个在冷却」「下一个会轮到谁」,以及每个账号的令牌有效期和冷却倒计时。
35
47
 
36
- - **剩余积分实时可见**:卡片按账号展示积分包(`套餐名 · 剩余 / 总量`)与合计剩余(大字绿色高亮),跟随上游实时刷新。
48
+ - **剩余积分实时可见**:按账号列出积分包(`套餐名 · 剩余 / 总量`)和大字合计,跟着上游实时刷新。
37
49
 
38
- - **模型目录直接标注**:卡片列出当前在池内可用的模型,并标注积分倍率(如 `GLM-5.2 · x0.79`)、免费 / 限时免费 / 夜间折扣标签、图片输入能力与上下文窗口,倍率与标签跟随上游 `credits` / `tags` 实时更新。
50
+ - **模型目录带标注**:列出池里能用的模型,附带积分倍率(如 `GLM-5.2 · x0.79`)、免费 / 限时免费 / 夜间折扣标签、能否收图和上下文窗口大小,全部跟随上游实时更新。
39
51
 
40
- - **每日签到**:卡片在每个账号的积分区下方提供签到按钮,并显示连签天数、每日积分与里程碑额外奖励;一键领取该账号当日签到奖励。多账号可逐个领取,无需先切换账号。领取前会重新查询状态,**今日已领取的账号不会被重复领取**。CLI 亦提供 `checkin` 命令。
52
+ - **国内版 / 国际版自动适配**:按账号凭据里的登录域名自动选上游——国际版登录(`workbuddy.ai`)走 `www.workbuddy.ai`,国内版(默认)走 `copilot.tencent.com`。两种账号可以混在同一个池里。
41
53
 
42
- - **国内版 / 国际版自动适配**:按账号凭据里的登录域名自动选择上游域名——国际版登录(`workbuddy.ai`)走 `www.workbuddy.ai`;国内版(默认)走 `copilot.tencent.com` 与 `www.codebuddy.cn`。同一池内可混用两种区域的账号,各自请求各自区域的上游。
54
+ - **想手动做点啥也行**:卡片和命令行都提供「重新检测账号」「清除所有冷却」「每日签到」三个操作。
55
+
56
+ ### 二、积分自动化
57
+
58
+ 卡片上一个开关,插件在后台把你成长中心的日常动作全做掉。**默认全自动,也可以随时点「立即运行」手动跑一轮。**
59
+
60
+ 它管这五件事:
61
+
62
+ | 任务 | 默认时间 | 干什么 |
63
+ | --- | --- | --- |
64
+ | 每日签到 | 09:00 | 领取当天签到奖励 |
65
+ | 活跃上报 | 10:00 | 报个到,让成长中心知道你还在用 |
66
+ | 任务奖励 | 11:00 | 把任务中心的奖励该报名的报名、该领的领掉 |
67
+ | 连登奖励 | 12:00 | 连登档位兑换 + 抽奖 |
68
+ | 猫猫旅行 | 09:00 · 21:00 | 把猫派出去、回来领奖励 |
43
69
 
44
- - **三种人工动作**:卡片与 CLI 都提供「重新检测账号」(重新扫描桌面登录快照,把新登录的账号并入池)、「清除所有冷却」(立即解除全部 429 冷却)与「每日签到」三个操作。
70
+ **「任务奖励」这一项覆盖了 13 个任务**,包括那些看起来非得本人坐在电脑前点鼠标才能完成的:
71
+
72
+ > 探索优秀灵感 · 使用 5 个模板 · 尝鲜热门技能 · 换「和平精英」主题 · 逛资料库 · 召唤 5 次专家 · 召唤 3 次专家团 · 体验腾讯轻量云专家 · 进入任一小助手应用 · 进入企鹅教师助手 · 设计创意模式建画布 · 创建定时任务 · 猫猫对话
73
+
74
+ 实测一轮跑下来,四个账号合计领到 **3300 积分 + 135 能量**。
75
+
76
+ **卡片上会告诉你这一轮具体领到了什么**——不是只写「领了 8 个任务」,而是把任务名字列出来。连登还差几天解锁也会写清楚(比如「还差 4 天解锁 7 天档」),免得你以为是坏了。
77
+
78
+ **关于「今日收益」**:卡片按来源分四行显示——任务奖励、签到、连登、旅行。今天赚了多少一眼看到,跨天自动清零。
79
+
80
+ **关于保留积分**:可以给每个账号设一个积分下限,余额低于这个数的账号就不派活了,省着点用。
81
+
82
+ > **国际版不显示这个面板**——那边的网关压根没有积分系统。
45
83
 
46
84
  ## 安装
47
85
 
48
- 前置:已安装并登录 WorkBuddy 桌面 App(插件复用 App 的登录状态;多账号 = 在桌面 App 里逐个登录/切换账号即可,每次登录都会被自动吸收进池)。已针对 DSH Desktop host `0.1.2` 兼容。
86
+ 前置条件:装好 WorkBuddy 桌面 App 并登录(插件复用它存在本机的登录状态)。多账号 = 在桌面 App 里换着登几次,每次登录都会被自动吸进池里。已针对 DSH Desktop host `0.1.2` 适配。
49
87
 
50
- > 与 host 兼容 `0.1.1-rc.2` / `0.1.2` 系:设置节安装会按 host 能力自动选择 `settings.installSection`(0.1.2-rc.1+)或自由函数(更早)。
88
+ > 兼容 host `0.1.1-rc.2` / `0.1.2` 系列:安装设置节时会按 host 能力自动选择 `settings.installSection`(0.1.2-rc.1+)或更早的自由函数写法。
51
89
 
52
90
  **方式一:从 npm 安装(推荐)**
53
91
 
@@ -56,8 +94,8 @@
56
94
  dsh plugin --profile desktop add dsh-workbuddy-xdpool
57
95
  ```
58
96
 
59
- > 推荐 npm 安装:pnpm / npm 需要拉取的依赖只有插件自身(**约 1 个包,数秒完成**)。
60
- > 从 GitHub 源码安装会连带安装开发依赖(构建器、测试框架等数百个包),明显更慢。
97
+ > 推荐 npm:需要拉取的依赖只有插件自己(**约 1 个包,几秒搞定**)。
98
+ > 从 GitHub 源码装会连带装开发依赖(构建器、测试框架等几百个包),慢得多。
61
99
 
62
100
  **方式二:从 GitHub 源码安装**
63
101
 
@@ -68,74 +106,74 @@ dsh plugin --profile desktop add github:XDTrees/dsh-workbuddy-xdpool
68
106
  **方式三:手动注册 bundle**
69
107
 
70
108
  ```sh
71
- # 1) 安装包(npm 或 GitHub 任选其一)
109
+ # 1) 先装包(npm 或 GitHub 任选)
72
110
  dsh plugin --profile desktop add dsh-workbuddy-xdpool
73
111
 
74
112
  # 2) 注册 bundle:编辑 ~/.dsh/profiles/desktop/package.json,
75
- # 在 "dsh" → "profile" → "bundles" 数组末尾追加 "dsh-workbuddy-xdpool"
113
+ # 在 "dsh" → "profile" → "bundles" 数组末尾加上 "dsh-workbuddy-xdpool"
76
114
 
77
115
  # 3) 重启 DSH Desktop
78
116
  ```
79
117
 
80
- **本地构建**(开发者):
118
+ **本地构建(开发者)**
81
119
 
82
120
  ```sh
83
121
  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 # 客户端
122
+ pnpm build # 产出 lib/index.js + lib/index.d.ts + lib/bin.js + lib/client.js
123
+ pnpm test # 138 项测试
124
+ pnpm typecheck # 宿主侧类型检查
125
+ pnpm typecheck:client # 客户端类型检查
88
126
  ```
89
127
 
90
- > **构建产物已随仓库提交**(`lib/` 不再 gitignore)。因此从 GitHub 安装时不需要任何安装期脚本,不会触发 pnpm 的「构建脚本被拦截,请放行」提示。**改动 `src/` 后请重新 `pnpm build` 并把 `lib/` 一并提交**,否则用户拿到的是旧产物。
128
+ > **构建产物已随仓库提交**(`lib/` 不在 gitignore 里),所以从 GitHub 装不需要任何安装期脚本,也不会弹 pnpm 那句「构建脚本被拦截,请放行」。**改了 `src/` 记得重新 `pnpm build` 并把 `lib/` 一起提交**,否则用户拿到的是旧产物。
91
129
 
92
- > 注意:`pnpm install` 需用 pnpm 11(`npx pnpm@11`),必要时加 `--config.confirmModulesPurge=false --config.minimumReleaseAge=0`(pnpm 11 默认的 `minimumReleaseAge` 供应链年龄策略会拦截刚发布的 rc 包)。
130
+ > 注意:`pnpm install` 要用 pnpm 11(`npx pnpm@11`),必要时加 `--config.confirmModulesPurge=false --config.minimumReleaseAge=0`——pnpm 11 默认的 `minimumReleaseAge` 供应链年龄策略会拦下刚发布的 rc 包。
93
131
 
94
- 装好后:模型选择器里会出现 **WorkBuddy XD Pool** 分组;设置 → 插件 → **DSH WorkBuddy XD Pool** 卡片可查看池健康、各账号令牌/积分/签到/冷却,以及「重新检测账号」「清除所有冷却」按钮与每个账号的签到按钮。
132
+ 装完以后:模型选择器里会出现 **WorkBuddy XD Pool** 分组;设置 → 插件 → **DSH WorkBuddy XD Pool** 能看池健康、各账号的令牌 / 积分 / 签到 / 冷却状态,还有「重新检测账号」「清除所有冷却」按钮和每个账号的签到按钮。
95
133
 
96
- 插件在 Web / TUI profile 下同样可用(`--profile web` / `--profile dsh-tui`)。
134
+ Web / TUI profile 也能用(`--profile web` / `--profile dsh-tui`)。
97
135
 
98
136
  ## 命令行
99
137
 
100
138
  统一用 `dsh plugin --profile desktop exec dsh-workbuddy-xdpool <子命令>` 调用:
101
139
 
102
140
  ```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 机器可读)
141
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool status # 池里几个号 / 几个冷却 + shim 状态(--credits 查积分、--json 机器可读、--rates 看倍率)
142
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts # 已发现的账号(--json)
143
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool doctor # 诊断发现 / 冷却 / 上游连通性
144
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool reset # 立刻清掉所有 429 冷却
145
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin # 查每个账号今天签到了没(--json 机器可读)
108
146
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool checkin all
109
- # 领取所有账号今日签到奖励;也可传账号标签只领一个
110
- dsh plugin --profile desktop exec dsh-workbuddy-xdpool login # 引导如何在桌面再加一个账号入池
147
+ # 领掉所有账号今天的签到奖励;也可以只传一个账号标签领单个
148
+ dsh plugin --profile desktop exec dsh-workbuddy-xdpool login # 教你怎么在桌面再加一个号进池
111
149
  ```
112
150
 
113
151
  ## 池里怎么多账号?
114
152
 
115
- 池走**自动发现**:WorkBuddy 桌面 App 每次登录都会在本机留下一个带令牌的历史快照,XD Pool 扫描这些快照,把每个账号都吸收入池。因此多账号 = 在 WorkBuddy 桌面 App 里逐个登录 / 切换账号即可,之后点卡片「重新检测账号」或重启 DSH,新账号自动成为池成员。
153
+ 池子走**自动发现**:WorkBuddy 桌面 App 每次登录都会在本机留一个带令牌的历史快照,XD Pool 扫描这些快照,把每个账号都收进池。所以多账号 = 在桌面 App 里换着登几次,之后点卡片上的「重新检测账号」或者重启 DSH,新号就自动进池了。
116
154
 
117
- 若你想对桌面 App 之外的某个登录做**显式快照**(例如临时固定某个账号再验证),也可手动导入:
155
+ 如果你想给桌面 App 之外的一次登录做个**显式快照**(比如临时固定某个号做验证),也可以手动导入:
118
156
 
119
157
  ```sh
120
158
  # 在 WorkBuddy 桌面 App 登录账号后(key 自己起名):
121
159
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool import myKey
122
- # 查看/删除已导入快照:
160
+ # 查看 / 删除已导入的快照:
123
161
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool accounts
124
162
  dsh plugin --profile desktop exec dsh-workbuddy-xdpool remove myKey
125
163
  ```
126
164
 
127
- 导入快照以 key 的 **MD5 前 8 位**命名落在 `~/.dsh/.workbuddy-xdpool/`(key 本身记在文件里),中文、带 `/`、带空格的 key 都安全;长期使用靠 refresh token 自动续期,失效则回到桌面重新登录后 `import <key> --force` 覆盖。
165
+ 导入的快照以 key 的 **MD5 前 8 位**命名,放在 `~/.dsh/.workbuddy-xdpool/`(key 本身记在文件里),中文、带 `/`、带空格的 key 都安全。长期使用靠 refresh token 自动续期;失效了就回桌面重新登录,再 `import <key> --force` 覆盖。
128
166
 
129
167
  ## 配置
130
168
 
131
- 池的有效配置经插件设置节(`settings.workbuddy-xdpool`)读取,模型设置页可改,改动即时生效:
169
+ 池的配置走插件设置节(`settings.workbuddy-xdpool`),模型设置页里能改,改完立即生效:
132
170
 
133
171
  | 字段 | 说明 | 默认 |
134
172
  | --- | --- | --- |
135
- | `authFile` | 覆盖 WorkBuddy 桌面 auth 文件路径(跨平台探测异常时用,等价于 `WORKBUDDY_AUTH_FILE`) | 自动探测 |
173
+ | `authFile` | 覆盖 WorkBuddy 桌面 auth 文件路径(跨平台自动探测出问题时用,等同于 `WORKBUDDY_AUTH_FILE`) | 自动探测 |
136
174
  | `cooldownMs` | 单账号 429 冷却时长(毫秒) | `60000` |
137
175
 
138
- 也可直接写在 `~/.dsh/settings.yaml`:
176
+ 也可以直接写进 `~/.dsh/settings.yaml`:
139
177
 
140
178
  ```yaml
141
179
  workbuddy-xdpool:
@@ -144,20 +182,33 @@ workbuddy-xdpool:
144
182
 
145
183
  ## 架构
146
184
 
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)。四个产物均随仓库提交,因此安装时不需要任何构建脚本。
185
+ - **宿主侧**(`src/`,跑在 DSH 主进程里)
186
+ - `index.ts` —— 注册 `workbuddy-xdpool` provider、`workbuddy-xdpool` 设置节、各条同源路由(状态 / 重新检测 / 清除冷却 / 签到 / 自动化运行 / 保留积分),以及账号发现与模型目录播种。
187
+ - `accounts.ts` —— `WorkBuddyAccountPool`:读本机 WorkBuddy 桌面 auth 快照、429 冷却、轮换与 token 刷新;账号停用与积分保留也在这里。
188
+ - `scheduler.ts` —— 积分自动化的调度器:按本地时点跑五个任务(签到 / 上报 / 任务 / 连登 / 旅行),每日收益账本落盘,手动「立即运行」走的是同一套逻辑。
189
+ - `task-events.ts` —— 13 条任务事件链的构造。每条链都是纯数据,外加它该走哪个指纹通道(桌面 / 网页);需要真实会话的任务(技能、专家)在这里先开一次真对话,拿到服务端 id。
190
+ - `catalog.ts` / `upstream.ts` —— 上游模型目录(含每模型积分倍率、免费 / 图片能力标签)、积分查询、签到、自动化各接口的上游客户端(按凭据域名自动切国内外)。
191
+ - `web-status.ts` / `status-paths.ts` —— 卡片读的同源状态文档与路由。写操作按「POST + 回环来源 + 显式 accountId」把关。
192
+ - `bin.ts` —— 上面那套 CLI。
193
+ - **客户端**(`src/client/`,浏览器卡片,经 `dsh.client` 由宿主加载)
194
+ 折叠卡片外壳沿用宿主内置卡的 `dsm-plugin-card*` 样式语言(`--dsw-alias-*` 主题变量),内容类名统一 `dsm-workbuddy-xdpool-*` 前缀,不污染宿主其它卡片;文案命名空间 `settings.workbuddy-xdpool`。
195
+ - **构建**
196
+ `tsdown` 产出 `lib/index.js`(宿主入口)+ `lib/index.d.ts`(类型)+ `lib/bin.js`(CLI)+ `lib/client.js`(CJS,用 `window.__ModuleLoader__.load` 包起来的浏览器 bundle)。四个产物都随仓库提交,所以安装时不需要任何构建脚本。
155
197
 
156
198
  ## 已知限制
157
199
 
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` 指定实际位置。
200
+ - **只能用本机桌面 App 里的账号**:池不会、也没法替你发起 WorkBuddy 的登录或扫码(token 由 WorkBuddy 桌面 App 自己的腾讯 SSO 铸造并和设备绑定)。加号 = 在桌面 App 里登录 / 切换,XD Pool 自动吸收。
201
+
202
+ - **自动化里有几个任务刻意没做**
203
+ - `Expert_Philanthropy`(公益专家):需要真捐钱,绕不过去。
204
+ - `black_cat`(夜猫子):只在 23:00–08:00 计分,而且**没有积分奖励**——熬夜白熬。
205
+ - `Model_chat_GLM5.2`(指定模型对话)与 `wb_wechat_oa_subscribe_task`(关注公众号):路径已经摸清,留到下个版本。
206
+
207
+ - **`Expert_team_use_3` 偶发卡在 2/3**:有个号当天用掉两次专家团后,第三次怎么发都不涨(换个没用过的专家团也不涨)。怀疑该任务对单账号有当日额度、跨天恢复,但没验证。**新号不受影响**(实测 0/3 → 3/3)。
208
+
209
+ - **依赖 WorkBuddy 客户端接口**(不是官方开放 API),WorkBuddy 更新后插件可能得跟着调。某个账号的 refresh token 失效时,回桌面重新登录即可。
210
+
211
+ - 如果 Windows 和 Linux 用户名不同、且 Windows 环境变量没传进 WSL,请用 `WORKBUDDY_AUTH_FILE` 或配置里的 `authFile` 指定实际位置。
161
212
 
162
213
  ## 免责声明
163
214
 
@@ -172,7 +223,7 @@ workbuddy-xdpool:
172
223
 
173
224
  - [corrinehu/dsh-workbuddy-connect](https://github.com/corrinehu/dsh-workbuddy-connect)(MIT)—— 设置节注册(`settings.installSection`)与 DSH 插件结构、客户端卡片加载机制、桌面端凭据刷新与 loopback shim 加固的核心参照;本项目沿用其「宿主通过 installSection 挂卡片」的打通路径。
174
225
  - [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)与积分接口的参照实现。
226
+ - [Sliverkiss/workbuddy2api](https://github.com/Sliverkiss/workbuddy2api)(MIT)—— WorkBuddy 上游协议(`copilot.tencent.com` 的 wire behavior)与积分接口的参照实现;**积分自动化**(成长中心签到 / 活跃上报 / 连登兑换 / 猫猫旅行状态机 / 13 条任务事件链 / 专家召唤链)整体照该项目的 Go 版逆向实现移植,每条事件链的判据与实测结论都来自它。
176
227
 
177
228
  以上项目的版权归各自作者所有。本项目采用**参考设计思路 + 独立实现**的方式,未整体复制任何参考项目的源码。若标注有遗漏或不当之处,欢迎提交 issue 指正。
178
229
 
Binary file
Binary file
package/lib/bin.js CHANGED
@@ -2714,6 +2714,16 @@ function dayKey(date) {
2714
2714
  return `${date.getFullYear()}-${month}-${day}`;
2715
2715
  }
2716
2716
  /**
2717
+ * The scheduled SLOT `date` falls in, as `YYYY-MM-DDTHH`.
2718
+ *
2719
+ * The per-job run guard keys on this instead of the date, so a job configured
2720
+ * for several hours runs in each of them while a second tick inside the same
2721
+ * hour is still refused.
2722
+ */
2723
+ function slotKey(date) {
2724
+ return `${dayKey(date)}T${String(date.getHours()).padStart(2, "0")}`;
2725
+ }
2726
+ /**
2717
2727
  * Whether `now`'s local hour is one of `hours`.
2718
2728
  *
2719
2729
  * The reference panel computes a `nextFire` instant and sleeps until it; this
@@ -2818,7 +2828,7 @@ var WorkBuddyScheduler = class {
2818
2828
  this.reportHours = options.reportHours ?? [10];
2819
2829
  this.taskHours = options.taskHours ?? [11];
2820
2830
  this.streakHours = options.streakHours ?? [12];
2821
- this.travelHours = options.travelHours ?? [12];
2831
+ this.travelHours = options.travelHours ?? [9, 21];
2822
2832
  }
2823
2833
  /** Apply a new configuration; safe to call while running. */
2824
2834
  /**
@@ -2896,7 +2906,7 @@ var WorkBuddyScheduler = class {
2896
2906
  async runNow(kind, force = false) {
2897
2907
  const today = dayKey(this.now());
2898
2908
  const state = this.states[kind];
2899
- if (!force && state.lastRunDate === today) return { ...state };
2909
+ if (!force && state.lastRunSlot === slotKey(this.now())) return { ...state };
2900
2910
  this.busy = true;
2901
2911
  try {
2902
2912
  await this.runJob(kind, today);
@@ -2991,10 +3001,11 @@ var WorkBuddyScheduler = class {
2991
3001
  this.busy = true;
2992
3002
  try {
2993
3003
  const now = this.now();
3004
+ const slot = slotKey(now);
2994
3005
  const today = dayKey(now);
2995
3006
  for (const kind of JOB_KINDS) {
2996
3007
  if (this.stopped) return;
2997
- if (this.states[kind].lastRunDate === today) continue;
3008
+ if (this.states[kind].lastRunSlot === slot) continue;
2998
3009
  if (!isFireHour(now, this.hoursOf(kind))) continue;
2999
3010
  await this.runJob(kind, today);
3000
3011
  }
@@ -3092,6 +3103,8 @@ var WorkBuddyScheduler = class {
3092
3103
  let credit = 0;
3093
3104
  let energy = 0;
3094
3105
  let claimed = 0;
3106
+ const detail = [];
3107
+ let progressNote;
3095
3108
  const accounts = this.accountsInOrder();
3096
3109
  if (kind === "tasks") await this.sendEventChains(accounts);
3097
3110
  for (let index = 0; index < accounts.length; index++) {
@@ -3104,7 +3117,10 @@ var WorkBuddyScheduler = class {
3104
3117
  try {
3105
3118
  const claim = await this.client.claimDailyCheckin(account.credential);
3106
3119
  this.recordEarnings(account.id, today, { checkinCredit: claim.credit });
3107
- if (claim.credit > 0) this.logger.info?.(`automation checkin ${account.label}: +${claim.credit} credit`);
3120
+ if (claim.credit > 0) {
3121
+ if (claim.credit > 0) detail.push(`签到 +${claim.credit}`);
3122
+ this.logger.info?.(`automation checkin ${account.label}: +${claim.credit} credit`);
3123
+ }
3108
3124
  } catch (error) {
3109
3125
  if (!isAlreadyCheckin(error)) throw error;
3110
3126
  this.logger.info?.(`automation checkin ${account.label}: already done today`);
@@ -3120,6 +3136,7 @@ var WorkBuddyScheduler = class {
3120
3136
  credit += result.credit;
3121
3137
  energy += result.energy;
3122
3138
  claimed += result.claimed;
3139
+ detail.push(...result.titles);
3123
3140
  this.claimableSeen += result.claimableCount;
3124
3141
  this.recordEarnings(account.id, today, {
3125
3142
  credit: result.credit,
@@ -3129,9 +3146,11 @@ var WorkBuddyScheduler = class {
3129
3146
  this.logger.info?.(`automation tasks ${account.label}: ${result.claimed} claimed (+${result.credit} credit, +${result.energy} energy, ${result.claimableCount} claimable seen)`);
3130
3147
  break;
3131
3148
  }
3132
- case "streak":
3133
- await this.redeemStreak(account);
3149
+ case "streak": {
3150
+ const progress = await this.redeemStreak(account);
3151
+ if (progress !== void 0) progressNote = progress;
3134
3152
  break;
3153
+ }
3135
3154
  case "travel": await this.runTravel(account);
3136
3155
  }
3137
3156
  ok++;
@@ -3142,6 +3161,7 @@ var WorkBuddyScheduler = class {
3142
3161
  if (index < accounts.length - 1 && this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
3143
3162
  }
3144
3163
  const state = this.states[kind];
3164
+ state.lastRunSlot = slotKey(this.now());
3145
3165
  state.lastRunDate = today;
3146
3166
  state.lastRunAtMs = this.now().getTime();
3147
3167
  state.ok = ok;
@@ -3150,6 +3170,8 @@ var WorkBuddyScheduler = class {
3150
3170
  state.energy = energy;
3151
3171
  state.claimed = claimed;
3152
3172
  state.message = this.summarise(kind, ok, failed, claimed, credit, energy);
3173
+ state.detail = detail;
3174
+ state.progress = progressNote;
3153
3175
  this.logger.info?.(`automation ${accountWord}: ${state.message}`);
3154
3176
  }
3155
3177
  /** Compose the one-line summary shown on the card. */
@@ -3352,6 +3374,7 @@ var WorkBuddyScheduler = class {
3352
3374
  let credit = 0;
3353
3375
  let energy = 0;
3354
3376
  let claimed = 0;
3377
+ const titles = [];
3355
3378
  for (const task of claimable) {
3356
3379
  if (this.stopped) break;
3357
3380
  const reward = await this.client.claimTaskReward(credential, task.taskCode);
@@ -3359,6 +3382,7 @@ var WorkBuddyScheduler = class {
3359
3382
  energy += reward.energy;
3360
3383
  if (reward.credit > 0 || reward.energy > 0) {
3361
3384
  claimed++;
3385
+ titles.push(task.title);
3362
3386
  this.logger.info?.(`automation claim ${account.label}: ${task.title} +${reward.credit}c +${reward.energy}e`);
3363
3387
  } else this.logger.info?.(`automation claim ${account.label}: ${task.taskCode} already claimed`);
3364
3388
  if (this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
@@ -3367,7 +3391,8 @@ var WorkBuddyScheduler = class {
3367
3391
  claimed,
3368
3392
  credit,
3369
3393
  energy,
3370
- claimableCount: claimable.length
3394
+ claimableCount: claimable.length,
3395
+ titles
3371
3396
  };
3372
3397
  }
3373
3398
  /**
@@ -3407,6 +3432,11 @@ var WorkBuddyScheduler = class {
3407
3432
  }
3408
3433
  if (this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
3409
3434
  }
3435
+ const pendingTier = status.tiers.find((tier) => tier.status === "locked");
3436
+ if (pendingTier !== void 0) {
3437
+ const remaining = Math.max(0, pendingTier.days - status.days);
3438
+ return remaining > 0 ? `${pendingTier.tier} in ${remaining}d` : `${pendingTier.tier} ready`;
3439
+ }
3410
3440
  }
3411
3441
  /**
3412
3442
  * One trip through the buddy travel loop for an account.
package/lib/client.js CHANGED
@@ -275,6 +275,10 @@ window.__ModuleLoader__.load({
275
275
  .dsm-workbuddy-xdpool-auto-job-name{flex:0 0 72px;color:var(--dsw-alias-label-primary,#e8e8ea)}
276
276
  .dsm-workbuddy-xdpool-auto-job-when{flex:1 1 auto;font-variant-numeric:tabular-nums}
277
277
  .dsm-workbuddy-xdpool-auto-job-last{flex:0 0 auto;text-align:right;font-variant-numeric:tabular-nums}
278
+ /* The job detail wraps to its own full-width line: it is a list of reward
279
+ titles, which needs the whole row rather than a cramped right-hand slot. */
280
+ .dsm-workbuddy-xdpool-auto-job-detail{flex:1 1 100%;color:var(--dsw-alias-label-secondary,#b8bcc4);line-height:1.5;word-break:break-word}
281
+ .dsm-workbuddy-xdpool-auto-job-note{flex:0 0 auto;color:var(--dsw-alias-label-secondary,#b8bcc4)}
278
282
  .dsm-workbuddy-xdpool-auto-run{flex:0 0 auto;padding:2px 8px;font-size:10px;line-height:16px}
279
283
  /* Reserved credits: one inline number field per account. */
280
284
  .dsm-workbuddy-xdpool-reserve{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-top:8px;padding-top:8px;border-top:1px dashed var(--dsw-alias-border-l2,#36373b);font-size:11px;color:var(--dsw-alias-label-dimmed,#8a97b5)}
@@ -362,10 +366,11 @@ window.__ModuleLoader__.load({
362
366
  const AUTOMATION_POLL_MS = 2e3;
363
367
  const AUTOMATION_POLL_ATTEMPTS = 90;
364
368
  const AUTOMATION_JOBS = [
369
+ "checkin",
365
370
  "report",
366
371
  "tasks",
367
- "checkin",
368
- "streak"
372
+ "streak",
373
+ "travel"
369
374
  ];
370
375
  /** Read one job's configured hours off the status document. */
371
376
  function automationHours(status, kind) {
@@ -376,6 +381,7 @@ window.__ModuleLoader__.load({
376
381
  case "tasks": return automation.taskHours;
377
382
  case "checkin": return automation.checkinHours;
378
383
  case "streak": return automation.streakHours;
384
+ case "travel": return automation.travelHours;
379
385
  }
380
386
  }
381
387
  /** Read one job's last-run record off the status document. */
@@ -1132,6 +1138,14 @@ window.__ModuleLoader__.load({
1132
1138
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1133
1139
  className: "dsm-workbuddy-xdpool-auto-job-last",
1134
1140
  children: job?.lastRunDate === void 0 ? t?.("row.autoNever") ?? "not run yet" : `${job.lastRunDate} · ${job.ok}${job.failed > 0 ? `/${job.failed}` : ""}`
1141
+ }),
1142
+ job?.progress === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1143
+ className: "dsm-workbuddy-xdpool-auto-job-note",
1144
+ children: job.progress
1145
+ }),
1146
+ job?.detail === void 0 || job.detail.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1147
+ className: "dsm-workbuddy-xdpool-auto-job-detail",
1148
+ children: job.detail.join(" · ")
1135
1149
  })
1136
1150
  ]
1137
1151
  }, kind);
@@ -1767,13 +1781,14 @@ window.__ModuleLoader__.load({
1767
1781
  "row.autoOn": "On",
1768
1782
  "row.autoOff": "Off",
1769
1783
  "row.autoBusy": "Saving…",
1770
- "row.autoHintOn": "Reports activity, claims task rewards and checks in once a day.",
1784
+ "row.autoHintOn": "Checks in, reports activity, claims task and streak rewards, and runs the buddy trip — every day.",
1771
1785
  "row.autoHintOff": "Off: no background requests are made for you.",
1772
1786
  "row.autoJob_report": "Activity report",
1773
1787
  "row.autoJob_tasks": "Task rewards",
1774
1788
  "row.autoJob_checkin": "Daily check-in",
1775
1789
  "row.autoJob_streak": "Streak bonus",
1776
- "row.autoHourNone": "not scheduled",
1790
+ "row.autoJob_travel": "Buddy trip",
1791
+ "row.autoHourNone": "skipped today",
1777
1792
  "row.autoNever": "not run yet",
1778
1793
  "row.autoRun": "Run now",
1779
1794
  "row.autoRunning": "Running…",
@@ -1895,14 +1910,15 @@ window.__ModuleLoader__.load({
1895
1910
  "row.autoOn": "已开启",
1896
1911
  "row.autoOff": "已关闭",
1897
1912
  "row.autoBusy": "保存中…",
1898
- "row.autoHintOn": "每天自动活跃上报、领取任务奖励并签到。",
1913
+ "row.autoHintOn": "每天自动签到、上报活跃、领取任务奖励与连登奖励,并照看猫猫旅行。",
1899
1914
  "row.autoHintOff": "已关闭:不会替你发起任何后台请求。",
1900
1915
  "row.autoJob_report": "活跃上报",
1901
1916
  "row.autoJob_tasks": "任务奖励",
1902
1917
  "row.autoJob_checkin": "每日签到",
1903
1918
  "row.autoJob_streak": "连登奖励",
1904
- "row.autoHourNone": "未排程",
1905
- "row.autoNever": "尚未执行",
1919
+ "row.autoJob_travel": "猫猫旅行",
1920
+ "row.autoHourNone": "当天不跑",
1921
+ "row.autoNever": "还没跑过",
1906
1922
  "row.autoRun": "立即运行",
1907
1923
  "row.autoRunning": "运行中…",
1908
1924
  "row.autoRan": "完成:{count} 个账号正常,{failed} 个失败",
package/lib/index.d.ts CHANGED
@@ -1151,6 +1151,16 @@ interface SchedulerLogger {
1151
1151
  interface AutomationJobState {
1152
1152
  /** `YYYY-MM-DD` of the last completed run, or undefined if it never ran. */
1153
1153
  lastRunDate?: string;
1154
+ /**
1155
+ * The scheduled SLOT of the last run, as `YYYY-MM-DDTHH`.
1156
+ *
1157
+ * The tick de-duplicates on this rather than on the date: keying on the date
1158
+ * alone caps every job at one run a day, which is wrong for a job with two
1159
+ * time points — blocking the cat loop's second pass would leave the cat out
1160
+ * until tomorrow. Per slot a job runs once in each configured hour, while a
1161
+ * repeat tick inside the same hour is still refused.
1162
+ */
1163
+ lastRunSlot?: string;
1154
1164
  /** Epoch ms of the last completed run. */
1155
1165
  lastRunAtMs?: number;
1156
1166
  /** Accounts that completed without throwing. */
@@ -1164,6 +1174,23 @@ interface AutomationJobState {
1164
1174
  /** Tasks claimed by the task job on the last run. */
1165
1175
  claimed: number;
1166
1176
  /** Human-readable summary of the last run. */
1177
+ /**
1178
+ * What this run actually did, in the words of the task board.
1179
+ *
1180
+ * `message` is a count ("3 accounts, 5 tasks claimed"); this is the list a
1181
+ * person can check off — the reward titles the pass collected. A row showing
1182
+ * only a bare number cannot answer "did it do the thing I care about", which
1183
+ * is the question the panel exists to answer.
1184
+ */
1185
+ detail?: readonly string[];
1186
+ /**
1187
+ * A pending milestone worth naming, when there is one.
1188
+ *
1189
+ * Streak tiers are why this exists: every tier reads `locked` until enough
1190
+ * consecutive days accumulate, and "locked" on its own reads as "broken"
1191
+ * rather than "come back in four days".
1192
+ */
1193
+ progress?: string;
1167
1194
  message?: string;
1168
1195
  }
1169
1196
  /**
@@ -1794,6 +1821,15 @@ interface PoolWebAutomationJob {
1794
1821
  claimed: number;
1795
1822
  /** One-line summary of the last run. */
1796
1823
  message?: string;
1824
+ /**
1825
+ * What the last run actually did, in the words of the task board.
1826
+ *
1827
+ * `message` is a count; this is the list a person can check off, which is
1828
+ * what turns a row from "it ran" into "it did the things I care about".
1829
+ */
1830
+ detail?: readonly string[];
1831
+ /** A pending milestone worth naming, e.g. the next streak tier countdown. */
1832
+ progress?: string;
1797
1833
  }
1798
1834
  /**
1799
1835
  * Automation block on the status document.
@@ -1812,6 +1848,7 @@ interface PoolWebAutomation {
1812
1848
  reportHours: readonly number[];
1813
1849
  taskHours: readonly number[];
1814
1850
  streakHours: readonly number[];
1851
+ travelHours: readonly number[];
1815
1852
  jobs: {
1816
1853
  checkin: PoolWebAutomationJob;
1817
1854
  report: PoolWebAutomationJob;
package/lib/index.js CHANGED
@@ -3031,6 +3031,16 @@ function dayKey(date) {
3031
3031
  return `${date.getFullYear()}-${month}-${day}`;
3032
3032
  }
3033
3033
  /**
3034
+ * The scheduled SLOT `date` falls in, as `YYYY-MM-DDTHH`.
3035
+ *
3036
+ * The per-job run guard keys on this instead of the date, so a job configured
3037
+ * for several hours runs in each of them while a second tick inside the same
3038
+ * hour is still refused.
3039
+ */
3040
+ function slotKey(date) {
3041
+ return `${dayKey(date)}T${String(date.getHours()).padStart(2, "0")}`;
3042
+ }
3043
+ /**
3034
3044
  * Whether `now`'s local hour is one of `hours`.
3035
3045
  *
3036
3046
  * The reference panel computes a `nextFire` instant and sleeps until it; this
@@ -3135,7 +3145,7 @@ var WorkBuddyScheduler = class {
3135
3145
  this.reportHours = options.reportHours ?? [10];
3136
3146
  this.taskHours = options.taskHours ?? [11];
3137
3147
  this.streakHours = options.streakHours ?? [12];
3138
- this.travelHours = options.travelHours ?? [12];
3148
+ this.travelHours = options.travelHours ?? [9, 21];
3139
3149
  }
3140
3150
  /** Apply a new configuration; safe to call while running. */
3141
3151
  /**
@@ -3213,7 +3223,7 @@ var WorkBuddyScheduler = class {
3213
3223
  async runNow(kind, force = false) {
3214
3224
  const today = dayKey(this.now());
3215
3225
  const state = this.states[kind];
3216
- if (!force && state.lastRunDate === today) return { ...state };
3226
+ if (!force && state.lastRunSlot === slotKey(this.now())) return { ...state };
3217
3227
  this.busy = true;
3218
3228
  try {
3219
3229
  await this.runJob(kind, today);
@@ -3308,10 +3318,11 @@ var WorkBuddyScheduler = class {
3308
3318
  this.busy = true;
3309
3319
  try {
3310
3320
  const now = this.now();
3321
+ const slot = slotKey(now);
3311
3322
  const today = dayKey(now);
3312
3323
  for (const kind of JOB_KINDS) {
3313
3324
  if (this.stopped) return;
3314
- if (this.states[kind].lastRunDate === today) continue;
3325
+ if (this.states[kind].lastRunSlot === slot) continue;
3315
3326
  if (!isFireHour(now, this.hoursOf(kind))) continue;
3316
3327
  await this.runJob(kind, today);
3317
3328
  }
@@ -3409,6 +3420,8 @@ var WorkBuddyScheduler = class {
3409
3420
  let credit = 0;
3410
3421
  let energy = 0;
3411
3422
  let claimed = 0;
3423
+ const detail = [];
3424
+ let progressNote;
3412
3425
  const accounts = this.accountsInOrder();
3413
3426
  if (kind === "tasks") await this.sendEventChains(accounts);
3414
3427
  for (let index = 0; index < accounts.length; index++) {
@@ -3421,7 +3434,10 @@ var WorkBuddyScheduler = class {
3421
3434
  try {
3422
3435
  const claim = await this.client.claimDailyCheckin(account.credential);
3423
3436
  this.recordEarnings(account.id, today, { checkinCredit: claim.credit });
3424
- if (claim.credit > 0) this.logger.info?.(`automation checkin ${account.label}: +${claim.credit} credit`);
3437
+ if (claim.credit > 0) {
3438
+ if (claim.credit > 0) detail.push(`签到 +${claim.credit}`);
3439
+ this.logger.info?.(`automation checkin ${account.label}: +${claim.credit} credit`);
3440
+ }
3425
3441
  } catch (error) {
3426
3442
  if (!isAlreadyCheckin(error)) throw error;
3427
3443
  this.logger.info?.(`automation checkin ${account.label}: already done today`);
@@ -3437,6 +3453,7 @@ var WorkBuddyScheduler = class {
3437
3453
  credit += result.credit;
3438
3454
  energy += result.energy;
3439
3455
  claimed += result.claimed;
3456
+ detail.push(...result.titles);
3440
3457
  this.claimableSeen += result.claimableCount;
3441
3458
  this.recordEarnings(account.id, today, {
3442
3459
  credit: result.credit,
@@ -3446,9 +3463,11 @@ var WorkBuddyScheduler = class {
3446
3463
  this.logger.info?.(`automation tasks ${account.label}: ${result.claimed} claimed (+${result.credit} credit, +${result.energy} energy, ${result.claimableCount} claimable seen)`);
3447
3464
  break;
3448
3465
  }
3449
- case "streak":
3450
- await this.redeemStreak(account);
3466
+ case "streak": {
3467
+ const progress = await this.redeemStreak(account);
3468
+ if (progress !== void 0) progressNote = progress;
3451
3469
  break;
3470
+ }
3452
3471
  case "travel": await this.runTravel(account);
3453
3472
  }
3454
3473
  ok++;
@@ -3459,6 +3478,7 @@ var WorkBuddyScheduler = class {
3459
3478
  if (index < accounts.length - 1 && this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
3460
3479
  }
3461
3480
  const state = this.states[kind];
3481
+ state.lastRunSlot = slotKey(this.now());
3462
3482
  state.lastRunDate = today;
3463
3483
  state.lastRunAtMs = this.now().getTime();
3464
3484
  state.ok = ok;
@@ -3467,6 +3487,8 @@ var WorkBuddyScheduler = class {
3467
3487
  state.energy = energy;
3468
3488
  state.claimed = claimed;
3469
3489
  state.message = this.summarise(kind, ok, failed, claimed, credit, energy);
3490
+ state.detail = detail;
3491
+ state.progress = progressNote;
3470
3492
  this.logger.info?.(`automation ${accountWord}: ${state.message}`);
3471
3493
  }
3472
3494
  /** Compose the one-line summary shown on the card. */
@@ -3669,6 +3691,7 @@ var WorkBuddyScheduler = class {
3669
3691
  let credit = 0;
3670
3692
  let energy = 0;
3671
3693
  let claimed = 0;
3694
+ const titles = [];
3672
3695
  for (const task of claimable) {
3673
3696
  if (this.stopped) break;
3674
3697
  const reward = await this.client.claimTaskReward(credential, task.taskCode);
@@ -3676,6 +3699,7 @@ var WorkBuddyScheduler = class {
3676
3699
  energy += reward.energy;
3677
3700
  if (reward.credit > 0 || reward.energy > 0) {
3678
3701
  claimed++;
3702
+ titles.push(task.title);
3679
3703
  this.logger.info?.(`automation claim ${account.label}: ${task.title} +${reward.credit}c +${reward.energy}e`);
3680
3704
  } else this.logger.info?.(`automation claim ${account.label}: ${task.taskCode} already claimed`);
3681
3705
  if (this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
@@ -3684,7 +3708,8 @@ var WorkBuddyScheduler = class {
3684
3708
  claimed,
3685
3709
  credit,
3686
3710
  energy,
3687
- claimableCount: claimable.length
3711
+ claimableCount: claimable.length,
3712
+ titles
3688
3713
  };
3689
3714
  }
3690
3715
  /**
@@ -3724,6 +3749,11 @@ var WorkBuddyScheduler = class {
3724
3749
  }
3725
3750
  if (this.delayMs > 0 && !this.stopped) await sleep(this.delayMs);
3726
3751
  }
3752
+ const pendingTier = status.tiers.find((tier) => tier.status === "locked");
3753
+ if (pendingTier !== void 0) {
3754
+ const remaining = Math.max(0, pendingTier.days - status.days);
3755
+ return remaining > 0 ? `${pendingTier.tier} in ${remaining}d` : `${pendingTier.tier} ready`;
3756
+ }
3727
3757
  }
3728
3758
  /**
3729
3759
  * One trip through the buddy travel loop for an account.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-workbuddy-xdpool",
3
3
  "displayName": "DSH WorkBuddy XD Pool",
4
4
  "description": "Merge every locally signed-in WorkBuddy account into DeepSeek Harness as one auto-failing-over model pool (multi-account rotation, live credits, daily check-in and model catalog).",
5
- "version": "1.2.0",
5
+ "version": "1.3.0",
6
6
  "license": "MIT",
7
7
  "author": "XDTrees",
8
8
  "homepage": "https://github.com/XDTrees/dsh-workbuddy-xdpool#readme",