Repository navigation
Releases: 3155498108/workbuddy-one
Release list
v1.0.15:卡片标出「N 天未续期 · 建议先切过去打开一次」
v1.0.15:卡片直接标「N 天未续期 · 建议先切过去打开一次」
系列总览见 v1.0.10 说明,
workbuddy2api-panel/CHANGELOG.md有完整版。
上一版(v1.0.14)把这句判断办法只写在使用说明和 Release 里,界面上看不到。
这一版做成看得见的三处:
1. 卡片黄色标签
last_refresh_at 距今 ≥ 3 天 时,账号卡片多一个标签:
3 天未续期 · 建议先切过去打开一次
悬停给出完整原因:refresh token 是一次性的,每次续期都换新的、旧的立刻作废,
所以离到期还有几十天也可能已被别处(另一台电脑 / 手机 / 网页版)顶掉,
切过去仍会要求重新登录;建议先切过去打开一次让它续期(1 分钟),之后再切就顺畅。
2. 登录态标签的悬浮提示
「登录态还剩 N 天」那个标签,悬停永远能看到判断办法:
- 今天 / 1 天前续期 → 快照是新的,放心切
- 超过 3 天没续期 → 建议先切过去打开一次让它续期,再切回来
3. 切号时的提示
点「切换到此账号」后,idleDays ≥ 3 的号会多一句:
已经 4 天没续期,直接切过去大概率要求重新登录——建议先切过去打开一次
WorkBuddy 让它续期(1 分钟),之后再切就顺畅了
没到 3 天的号保持原来的「万一被要求重新登录,登一次就会重新计时」,不啰嗦。
为什么是 3 天
每天弹同一句等于没提示。3 天是「可能已被顶掉」和「还不烦人」之间的折中,而且和
标黄阈值(剩余 ≤ 7 天)互不重叠:一个看还剩多久,一个看多久没续——
后者才是掉登录的真因。
测试
TestCredNoteStaleAdvice:4 天前续期 → 含该句;2 天前续期 → 不含TestUIStaleHint:ui.html 必须含该文案、阈值idleDays >= 3、${staleTag}
确实插进卡片模板(写了不插等于没写)- 既有
TestCredNote/TestRoundDaysNoOffByOne/TestUIUsesRoundedDays仍通过
提示:你现在两个号都是今天 / 昨天续的期,所以那个黄色标签暂时不会出现——
它是条件显示的,等某个号 3 天没动才会自己冒出来。
v1.0.14:登录态剩余天数差一天(60 天显示成 59)
v1.0.14:登录态剩余天数差一天(60 天被显示成 59)
全系列(v1.0.3 → v1.0.14)的总览见
v1.0.10 说明,
workbuddy2api-panel/CHANGELOG.md有完整版。
结论:数值是对的,显示少了一天
直接读客户端真实凭证 auth\workbuddy-desktop.info 与管家快照:
| expiresAt(access) | refreshExpiresAt | lastRefreshTime | |
|---|---|---|---|
| Joker(当前登录) | 2026-11-29 09:09 | 2026-12-29 09:09 | 2026-10-30 09:09 |
| 林文海 | 2026-11-28 20:52 | 2026-12-28 20:52 | 2026-10-29 20:52 |
Joker 的 refresh token 是今天 09:09 续的、到 12-29 09:09,整 60 天。卡片读的
就是这个 refreshExpiresAt,但算天数时用了 Math.floor 截断:59.999… 天 → 59。
所以 59 天的来源没错,是取整错了。 顺手还发现两处过时:
- 上游续期窗口已经从 30 天改成 60 天(1.0.9 时代的注释和提示都写着 30 天)
- 卡片提示「切过去打开一次就重新续 30 天」也是旧的
改法
- 界面:
Math.floor→Math.round;窗口天数按数据算(到期 − 上次续期),不写死 30;
悬浮提示补「续期窗口 N 天」和「还剩很多天不代表不用重新登录」 - 管家侧:新增
roundDays()(四舍五入;不足一天按 1 天;已过期保持负数),
credNote/credWarnShort统一用它;切号提示变成
「登录态还剩 60 天(12-29 到期)」
顺便说清「还剩 N 天」到底代表什么
它是 refresh token 的硬到期时间,不是「切过去一定不用登录」。客户端的
refresh token 是一次性的,每次续期换新的、旧的立即作废——只要这个号在别处
(另一台电脑 / 手机 / 网页版)登录过,快照里那份就成废票,卡片还显示几十天,
切过去照样要重新登录。
真正该看的是卡片上「N 天前续期」这半句:
- 今天续期 / 1 天前 → 快照是新的,放心切
- 超过 3 天没续期 → 建议先切过去打开一次,让它续期(1.0.9 起切号后 8 秒 / 40 秒
会自动同步,运行期每 3 分钟同步一次)
测试
TestRoundDaysNoOffByOne:60 天−1 秒→60;59 天 23 小时→60;59 天 11 小时→59;
30 分钟→1;已过期→保持负数TestUIUsesRoundedDays:ui.html 必须含Math.round(leftMs / 86400000)、
不得含Math.floor(leftMs / 86400000)、不得写死「重新续 30 天」- 全量
go test(cmd/wbone / panel / scheduler)通过
使用说明
FAQ 9 重写为「卡片上的『登录态还剩 N 天』是怎么算的,准不准」:说明 60 天窗口、
天数只代表硬到期、以及用「N 天前续期」判断该不该切。
v1.0.13:猫猫旅行按「一天只能派一次」收紧补派
v1.0.13:猫猫旅行按「一天只能派一次」收紧补派 + 巡检失败快速重试
全系列(v1.0.3 → v1.0.13)的总览见
v1.0.10 说明,
workbuddy2api-panel/CHANGELOG.md有完整版。
这一版不是新功能,是把上一版的补派逻辑按真实规则改对。
前提:一天只有一次派出机会
09:08 查两个账号的真实状态:state=idle、daily_limit_reached=true、
record_id=0。今天那一次凌晨已经用掉了(00:36 / 00:47 各派一趟,到站已领奖),
猫在家等明天 00:00 重置——正常状态,不是漏派。
所以 v1.0.12 里「领完立刻再派」的说法是错的:9 点派 → 12 点到站 → 12 点领奖时,
今天的机会早就用掉了。
改法一:补派前先看今天的机会还在不在
daily_limit_reached 记的是「今天派过没有」,与领奖无关,所以领奖前那份状态
已经给出答案:
- 今天已派过(常态)→ 直接返回,不再多发一次
status请求,也不再打
re-depart skipped (daily limit reached after claim)这种容易被误读的日志 - 只有「昨天派出、今天凌晨到站」这种跨天情况 → 才把今天的唯一一次用掉
另外 travelClaim 改成返回 (claimed, err):到站却缺 record_id 这类异常按
skip 处理,不在异常态上再叠一个派出动作。
改法二:巡检整趟失败不再干等一个完整间隔
一天只有一次机会,错过代价大。原来失败要等 60 分钟,今早 09:01 就撞上了:
程序启动即补的那趟 buddy/info 直接 EOF(网络瞬断),没有短退避就会把今天的
这一次推到 10:01。
现在 RunTravelNowReport() 会区分「真的失败」和「正常 skip」(在途、当日已派满、
未知状态都算 skip,重试也没用)。有失败就退到 5 分钟重试,不改用户配的
60 分钟间隔,日志写明「巡检 N/M 个账号失败,5m0s 后重试」。
测试
- 状态机表新增「领奖前当日已派满 → 不重查」(断言
status只调 1 次) - 新增
TestTravelAutoRetriesSoonAfterFailure:间隔配 10s、退避 30ms,断言 300ms
内跑满 3 趟,且TravelAutoInterval()仍是 10s(短退避不改配置) - 新增
TestRunTravelNowReportCountsOnlyRealFailures:在途 = skip,failed=0 - 全量
go test(panel / scheduler / gwapi / upstream / cmd/wbone)通过
一句话总结现在的行为
每天一次,越早越好;到站就领,领完不用再等整点;程序重开会立刻补一趟;
断网了 5 分钟后重试,不会把一天一次的机会耗在一个坏时点上。
v1.0.12:修「无畏契约拼豆专家」任务做不了 / 猫猫旅行自动派出
v1.0.12:修「无畏契约拼豆专家」任务做不了 + 猫猫旅行自动派出
全系列(v1.0.3 → v1.0.12,共 10 个版本)的总览见
v1.0.10 说明,
workbuddy2api-panel/CHANGELOG.md里有完整版。
一、任务中心里「无畏契约拼豆专家」点完成报找不到专家
现场
WorkBuddy_Valorant(体验「无畏契约拼豆专家」)点完成,返回
「市场列表(20 条)里没找到「无畏契约拼豆专家」」,但任务本身还在有效期
(11-15 截止)。
根因
旧实现拉 MarketExpertList ——市场推荐的前 20 条——按中文名扫。这个专家
2026-10-20 上架时在推荐位上,过了推广期就掉出去了。
用真实凭证打上游核对(2026-10-30 01:00):
MarketExpertList(page_size=20, 无 keyword)
→ 20 条里没有任何「无畏契约」相关专家
MarketExpertSearch(keyword=无畏契约)
→ ex_xQHHD1u5Jkua 无畏契约拼豆专家(第一条)
任务还在、专家也还在,只是不在推荐位了。推广期一过,这个任务就必然失败。
改法
新增 findExpertByKeyword,走客户端搜索框那条路:
MarketExpertSearch按完整专家名搜,命中要求名字/职业含关键字;- 搜不到再按产品名(「无畏契约」)搜——专家改名也能命中;
- 搜索接口异常或零命中才回落推荐列表(老路径);
- 全都不行给可读错误(「可能已下架」),不再甩「20 条里没找到」。
真网核验:两个关键字都返回 ex_xQHHD1u5Jkua,走的是真实 headers + 信封解析。
二、猫猫旅行不会自己出门
现场
00:36 手动点「旅行巡检」后:林文海 的猫是 idle + daily_limit_reached=false,
Joker 的猫刚被派出去。
根因
travelOne 的设计是一趟一个动作:到站→领奖,在家→派出。
于是:21:00 那一趟把猫派出(当日次数打满)→ 约 00:00 到站 → 00:36 巡检时
「到站→领奖」这一趟就结束了,同一趟不会接着派 → 下一次固定时点是 09:00,
猫在家待 8 小时。领奖和派出被拆在两趟里,而两趟之间隔着整个固定时点间隔。
改法
- 领完立刻再派:
travelClaim返回是否成功,成功后重查状态,仍idle
且当日次数没用完就当场再派一趟(必须重查——claim 只回reward_credit,
判断不了当日上限)。 - 常驻巡检:新增
StartTravelAuto,默认每 60 分钟一趟,启动即补一趟。
固定时点负责「每天必跑」,巡检负责填「到站没人领 / 领完没人派」的空档。 - 可关:
schedule.travel_auto(缺省 true)、travel_auto_minutes
(缺省 60),设置页可见、保存即热生效;travel_enabled=false时巡检也不
启动(否则「完全停猫猫旅行」会被巡检偷偷推翻)。 - 看得见:任务中心顶部新增「猫猫旅行」卡片,逐账号显示猫名 / 状态
(在家待发 · 旅途中 · 已到站待领奖)/ 可领积分,附「立刻巡检」按钮。
测试
panel/expert_search_test.go4 个:推荐位没有但搜索命中(核心回归点)、
宽关键字命中、搜索挂了回落列表、两处都没有返回 nilscheduler:常驻巡检启动即跑 + 周期重复 + 热改 0 停止、interval=0不启动、
TravelHours返回副本;状态机表新增「领奖后立刻再派出」「当日已派满不补派」- 全量
go test(panel / scheduler / gwapi / upstream / cmd/wbone)通过
升级
设置页新增两项(常驻巡检 / 巡检间隔),默认已开,不用改配置。
config.json 里也可以手写:
"schedule": { "travel_auto": true, "travel_auto_minutes": 60 }v1.0.11:修 issue #1 双活切不回去 / 端口被占直接退出
修复外部用户报的 issue #1:两个账号都显示「正在使用」、切不回去,以及重开程序提示**「后台服务意外退出」**、重启电脑也无效。
全系列(v1.0.3 → v1.0.11)总览见 v1.0.10 说明顶部。
现场
当前登录账号1的情况下,切换账号2。2个账号都显示正在使用,无法切换回账号1,
没有切换账号的按钮。重新打开软件,提示后台服务意外退出,重启电脑无效。只有我重新在 WorkBuddy 客户端手动退出账号2,重新登录账号1之后,账号2 才会
出现切换账号的按钮。现在问题是,可以切换,无法切回。
其实是两条互相独立的故障赶在一起:切号卡死 + 端口被占。
根因一:切号只覆盖「目标账号有快照的线路」
auth/ 目录里可以有多条受管线路(workbuddy-desktop.info、workbuddy-desktop-v2.info 等)。
从 A 切到 B 时只写 B 有的那条,A 独有的线路原地不动、仍指向 A。
而旧版把「任一线路写的是这个账号」当成「这个账号正在使用」,于是 A、B 同时
「正在使用」;界面又对 active 的账号禁用按钮 —— 两个都 active 就是两个都点不动。
根因二:切换前的检查用同一套错误口径
旧版切换前会遍历所有线路,只要有一条写着目标账号就回「当前已经在这个账号上,
无需切换」。残留线路正好让 A 命中 —— 点「切回账号1」永远得到这句话。
这才是「可以切换,无法切回」的直接原因,只改界面的「正在使用」标注修不好它。
根因三:「后台服务意外退出」是端口冲突
界面和网关共用同一个 HTTP 服务。config.json 里的端口被别的程序占着时,
启动直接失败 → 弹框 → 退出。而占用者往往开机自启、比管家起得更早,所以
重启电脑也无效,用户看到的又是一个没信息量的框,不知道该关谁。
改了什么
- 当前账号只认一条权威线路:主客户端(
workbuddy-desktop)优先,没有主线路
才退回其它受管线路。界面显示和切换判定共用这一个口径,不会再打架。 - 切号时清理残留线路:目标账号没有快照的线路,切完后挪进
backup/。
挪而不删 —— 国内版 + 国际版并存是正常场景,删了会丢登录态;代价是那条
线路的客户端要重登一次,比整个切换界面卡死好。 - 切换前置检查改用权威口径:只要目标不是主线路上的那个账号,就真的切一次。
- 端口被占自动顺延:往后找最多 9 个空闲端口继续启动,页面顶部黄条写明
「端口 X 被占用,已改用 Y」。只换端口号、保留原主机部分(你写:7863是
有意开放局域网,不会悄悄收回回环),也不改你的config.json。 - 错误提示说人话:端口占用 / 配置坏了 / 没写权限,各给一句能照着做的中文,
原文一并保留。
升级后怎么用
- 已经卡在「两个都正在使用」的:升级到 1.0.11 后点一次「切换」就恢复,
不用再去客户端手动退出登录。 - 提示「后台服务意外退出」的:不会再退出了,直接看页面顶部黄条,按新端口访问。
- 已经在用 1.0.8 及以上版本的,可以直接在界面里点「检查更新」自动升级。
验证
- 新增 7 个单元测试(双活、切不回去、切后无残留、端口空闲/顺延/保留 0.0.0.0、
错误翻译),全量go test通过。 - 实机 e2e:先占住
127.0.0.1:17860再启动 exe → 自动改用 17861 并给出提示;
两条线路指向不同账号时只显示 1 个「正在使用」;点切换到目标账号后确实切过去,
backup/留下切前备份。修复前同一脚本会返回「当前已经在这个账号上」。
文件
WorkBuddyOne-v1.0.11-win64.zip(4,138,645 字节)- SHA256:
C6C325F3FD1322AEF9B32DA3BE364DF55DA328C3AE7CEABC06B91FE7C7D5B907
国内网络直连 GitHub 可能很慢,程序内置了加速源会自动切换;手动下载慢的话可用
https://gh-proxy.com/ 或 https://ghfast.top/ 前缀。
v1.0.11 详细说明
对外变化只有三处:切号不再卡死、端口被占不再退出、错误提示能看懂。以下为改动清单。
accounts.go
- 新增
activeLine():返回权威当前账号(主线路优先,退回排序第一条)。 - 新增
sameAccount():切换前置判定,与界面共用同一口径。 accounts()用activeLine()取代map[uid]任一命中。switchTo()增加收尾清理:目标账号无快照的线路挪进backup/<product>-stale-<时间>.info,
失败只记日志不回滚(已经切好了,不该因为收尾失败把用户退回原账号)。
main.go
- 新增
ensurePort(listen):顺延最多 9 个端口,WB2A_LISTEN覆盖,保留 host。 - 新增
portFreeOn(host, port);portFree(port)变成回环包装。 - 新增
explainGwErr(err):三类常见错误给中文处置建议。 - 启动时端口被占不再
return,改为换端口继续;网关异常弹框改用explainGwErr。 - 版本号 1.0.11,
rsrc.syso重生成。
api.go / ui.html
stateResp增加listen_note;账号页顶部黄条展示端口变更说明。
v1.0.10:修第一次启动闪退
10-21 与 10-24 两个工作日,一共发了 8 个版本。 每个版本都对应一个实际
遇到的故障,没有一个是"顺手改改"。下面是全系列一览 —— 按时间倒序看,就是一份
踩坑史。
全系列总览:v1.0.3 → v1.0.10
| 版本 | 一句话 | 类型 |
|---|---|---|
| v1.0.10 | 修「第一次启动闪退,第二次才行」—— Edge 首次初始化会换进程 | 修 bug |
| v1.0.9 | 显示登录态剩余天数,后台自动同步被轮换的 token | 新功能 |
| v1.0.8 | 修「更新完关了就不启动」—— 备份名撞 delete-pending + 换文件顺序反了;加国内镜像 | 修 bug |
| v1.0.7 | 修 403 限流(改零配额查版本),更新固定全自动 | 重构 |
| v1.0.6 | 修「从 1.0.3 起就更新不了」—— 20 秒总超时 + path.Base 不认 \ |
修 bug |
| v1.0.5 | 新活动任务自动跟上 + 更新加「自动安装」开关(后被 v1.0.7 删除) | 新功能 |
| v1.0.4 | 签到报 401 不再「假装成功」,可自愈一次 | 修 bug |
| v1.0.3 | 自动更新上线:查版本 → 下 zip → 换 exe → 接棒重启 | 新功能 |
三条主线
一、自动更新:从「有」到「真能用」(连续修了五个独立 bug)
v1.0.3 把功能做出来了,但之后好几天更新一直失败。原因不是单一 bug,而是
五个问题叠在一起,修掉一个才暴露下一个:
- 20 秒总超时(v1.0.6)—— 下载复用了查版本的 HTTP client,
Client.Timeout
计的是整个请求含读完 body,4 MB 包在慢网下必超时 path.Base只认/(v1.0.6)—— 旧版 Windows 压缩工具用\当 zip 条目
分隔符,包正常却被判成「安装包里没有 exe」- 403 配额(v1.0.7)—— 匿名调 GitHub API 按 IP 每小时 60 次,改用
/releases/latest的 302 拿版本号,零配额 - 备份名撞 delete-pending(v1.0.8)—— 固定用
.old会被上次更新残留的
「删除中」名字卡死,rename直接 Access is denied - 先关窗口后换文件(v1.0.8)—— 换文件一失败窗口已经没了,这就是
「更新完关了就不启动」的真相
还有个设计上的自我否定:v1.0.5 加的「自动安装」开关默认关,结果大部分人
永远不勾、永远停在老版本,而老版本的更新本身是坏的 —— 死循环。v1.0.7 直接删掉
开关,改成查到就装、装完自己重启。
二、登录态寿命(v1.0.9)
排查发现不是「太久没登录」:客户端的 refresh token 是一次性轮换的,每续期
一次就换新的、旧的立即作废;而它只把新 token 写进「活动」账号文件,不写我们的
快照。于是切走时存下的 token,切回来时早就失效了。
改法:后台每 3 分钟把活动账号的新 token 同步进快照,卡片上显示「登录态还剩 N 天」
和「X 天前续期」。
三、启动稳定性(v1.0.10,本版)
Edge 首次初始化新配置目录时会把自己 relaunch 一次,管家只盯着最初那个进程号,
一看它退出就判定「用户关了窗口」,亲手杀掉还开着的窗口。改成三重判据
(主进程 / Job 里的进程数 / 页面心跳),并修好关窗后 20 秒内再点图标没反应的副作用。
v1.0.10 详细说明
修「第一次启动会自己闪退,第二次启动才行」
这个 bug 不是玄学,日志里长得很规律:启动 → 1 秒后「窗口已显示」→ 3 秒后
「窗口已关闭,退出」→ 用户再点一次就正常。10-24 一天里出现六次,不是偶发。
根因
界面窗口是用 Edge 的 --app 模式打开的。Edge 第一次初始化一个全新的
user-data-dir 时,会把自己 relaunch 一次:旧主进程退出、新进程接管窗口。
管家当时只盯着 cmd.Start() 拿到的那个进程号,一看它退出就判定「用户把窗口
关了」,顺手把整棵进程树 kill 掉 —— 窗口其实好端端开着。于是窗口一闪就没了。
第二次点能好,是因为那时配置目录已经初始化完,Edge 不再重启自己。
证据:对比失败与成功那几次留下的 Edge 配置目录(data/browser-<pid>)。
| 失败 | 成功 | |
|---|---|---|
| 体积 | 14~19 MB | 39~130 MB |
| 未收尾的 journal | first_party_sets.db-journal、DIPS-wal 都在 |
干净 |
Local State(正常退出才写) |
没有 | 有 |
| 组件目录 | 缺 Crashpad、缺 FirstLaunchAfterInstallation | 齐全 |
journal 残留 + 没有 Local State = 进程是被强杀的,不是自己退的。而代码里唯一
会 kill 浏览器的地方,只有「以为窗口关了」这一条路径。
改法
三重判据,任意一条成立就认为窗口还开着:
- 当初拉起的主进程还活着;
- 主进程没了,但 Job 里还有别的进程(Edge relaunch 出来的新主进程/渲染进程)——
用QueryInformationJobObject数ActiveProcesses; - 页面 20 秒内发过心跳(界面每 3 秒发一次)。
三条全落空才退出。宁可晚几秒收摊,也不误杀。
配套:
- 启动后 10 秒宽限期(Edge 正在初始化,不做死亡判定);判死前连查 3 次。
- 收进程树改用
TerminateJobObject,relaunch 出来的新进程也一并收走,不留孤儿窗口。 - 修掉由此带来的副作用:关窗口后管家最多还要等心跳过期(20 秒)才退,这期间
再点图标会「点了没反应」。现在旧实例发现窗口真没了会主动退出让位,新窗口
2 秒左右就出来,不用点第二次。
验证
- 新增 7 个单元测试(
window_test.go),含「真建 Job、真塞两个活进程」验证
Win32 结构体布局没错。 - e2e(真实 exe):杀掉 Edge 主进程但留子进程 → 管家没自杀,日志连打「仍有心跳,
判定窗口仍在」,心跳过期后才正常退出。 - e2e:关窗后 0.4 秒再双击 → 旧实例让位 → 新进程接管 → 2 秒窗口出来。
升级
WorkBuddyOne.exe 直接覆盖到程序目录即可,profiles/、config.json 都不动。
或者打开管家点「检查更新」,它自己会换。
SHA256:E02DDC510DC8D80F3C27837189463D3DBD17D8E8DD6B0A7DCD63E864945994E4
v1.0.9 显示登录态剩余天数 + 自动同步被轮换的 token
本系列(v1.0.3 → v1.0.10,共 8 个版本)的完整更新总览见 v1.0.10。
这一版解决什么
「切换账号后还是要重新登录,是不是太久没登录了?」
不是。 查了本机登录态文件,两个账号都是当天刚续期、还剩 30 天。
真正的原因是客户端的 refresh token 一次性轮换:每续期一次就换新的,
旧的立即作废,而它只写当前活动的那个文件,不会写管家的快照。
所以手里那份可能早就成了废票——切过去自然被要求登录。
对比 backup/ 里的 8 份历史备份,refreshToken 密文 8 份全不一样,
这条结论是实测出来的,不是猜的。
改了什么
1. 后台自动同步登录态(真正让它保存更久)
管家每 3 分钟、启动时、切号后 8 秒和 40 秒,把客户端当前的登录态同步进
profiles/<uid>/。只在新于快照时才覆盖,绝不会拿一份失效的凭证冲掉有效快照
(客户端停在登录页时直接跳过)。
2. 每个账号显示还能撑多久
卡片上直接写「登录态还剩 N 天」,剩 7 天变黄、过期变红;
副行显示「X 天前续期」——因为轮换看的是最后使用时间,这才是真实倒计时。
鼠标悬停能看到具体到期时刻和上次续期时刻。
3. 切号时提前告知
切换成功的提示里会补一句「该账号上次续期是 N 天前,登录态还剩 M 天」,
不用等登录页弹出来才反应过来。
你需要知道的规则
- 客户端的 refresh token 是 30 天滚动:只要 30 天内在客户端里打开过这个账号
一次,它就一直是活的;超过 30 天没动,必须重新登录。 - 所以最省事的做法:每 2~3 周切过去打开一次,这些号就能一直保住。
卡片上的黄色标记就是提醒你该做这件事了。 - 管家只能给「当前正在用的账号」保鲜。停用的账号停在最后一次使用的时刻,
不会自己续期——software 解不开客户端的加密 token,也就没法替它续。 - 别在客户端里手动退出登录再登另一个号:那会直接覆盖登录态文件,
被切走的那个号就永久丢了。换号请用本程序。
顺带修的小问题
过期不到 1 小时时,整数除法会把它算成「还剩 0 天」,读起来像没过期。
现在会明说「已过期」或「不到 1 天」。
升级
1.0.9 直接覆盖 exe 即可(profiles/、config.json 都不动)。
已经在 1.0.6 及以上的,管家会自己更新到这一版。
v1.0.8 修更新后不重启 + 国内下载多源自动切换
本系列(v1.0.3 → v1.0.10,共 8 个版本)的完整更新总览见 v1.0.10。
这一版修的是「更新完它关闭了,就不再启动」
你本机的 data/app.log 把现场完整记下来了:
11:24:02 已下载 v1.0.7 安装包并取出程序
11:24:04 自动更新失败:无法移动当前程序:rename ... WorkBuddyOne.exe
→ WorkBuddyOne.exe.old: Access is denied
11:24:04 窗口已关闭,退出
意思是更新一次都没成功过,全卡在换文件那一步。两个 bug 互相放大:
① 备份名固定用 .old,撞上了「删除挂起」的名字
上一次更新会把正在运行的 exe 改名成 WorkBuddyOne.exe.old 再删掉。删是删了,但这个名字会一直挂在进程的映像引用上(delete-pending):目录里看不见它,可它既不能被创建、也不能作为替换的目标。于是下一次 rename 直接 Access is denied。
现在备份名是 <exe>.old-<pid>-<序号>,永远不会重名。
② 失败的顺序反了:先关窗口,再换文件
窗口一关,进程也跟着退出,而新版本根本没被启动过——这就是你看到的「关闭就不启动了」。
现在只有新版本真的启动成功才关窗口;失败时窗口原样留着,侧栏那一行变红并写出原因,data/app.log 里也有。
国内下载
- 查版本:直连和几个加速源并发问,取版本号最大的那个(公共反代会缓存旧页面,先到的未必最新),12 秒收口。
- 下载:顺序是「上次成功的源 → GitHub 直连 → 加速源」。一个源 12 秒拿不到响应头、或一个字节都没下到,就换下一个;换源会删掉残包重下(不同源的 Range 语义不一样,拼起来 CRC 必挂);同一个源已经下了一部分再断,则从断点续传。成功后把那个源记住,以后直接用,不用每次都在直连上白等。
- 加速源按实测速度排序(v1.0.7 包,首 1 MiB):
gh-proxy.com666 KB/s、ghfast.top416 KB/s、ghproxy.net92 KB/s;github.moeyy.xyz已失效,移除。
验证
- 新增 11 个单元测试:换源、换源必须替换残包、全源失败清残包、备份名 100 次不重名、多源取最大版本等;全量测试通过。
- 端到端复现了你的现场:把运行中的 exe 改名成
.old再删除(这个名字被进程占住、删不掉),在原位放回一份,然后让新版自己去更新——日志六项全中(已下载 / 已更新到 / 新版本已拉起 / 接棒启动 / 已清理备份),没有一条失败记录。旧代码在这一步必挂。
升级方式
低于 1.0.8 的版本,建议手动覆盖一次:下载 zip,把里面的 WorkBuddyOne.exe 解压到程序目录覆盖即可(profiles/、config.json 都不动,账号不用重登)。之后就能正常自动更新。
SHA256:6CC284C511EB644B21FA47E8E1438F5DF0BAF61E51C8C7611270369ABBACB0D1
v1.0.7 全自动更新 + 修 403 限流
本系列(v1.0.3 → v1.0.10,共 8 个版本)的完整更新总览见 v1.0.10。
v1.0.7 更新改成真·全自动,查版本不再走 GitHub API
1. 修「403 API rate limit exceeded」
GitHub 的匿名接口是按 IP 每小时 60 次,点几次「检查更新」就用完了,然后一直返回 403。
现在默认不碰 API:读 github.com/<repo>/releases/latest 的 302 跳转拿到版本号,再按固定命名规则拼出下载地址。这条路径没有配额限制,怎么点都不会限流。
代理把 302 拆成 200 页面时,会从正文里再找一次版本号。API 只在自定义 update_feed 或零配额路径失手时兜底——真撞上了也给中文提示(等一小时,或配 update_token),不再把 GitHub 的英文原文糊给你。
2. 移除「自动安装」开关,固定全自动
之前那个勾选默认是关的。结果大部分人永远不勾、永远停在老版本上,而老版本的更新本身又是坏的——死循环。
现在:查到新版本直接下载、替换、自己重启,不用点任何按钮。界面上不再有勾选,只留一个「检查更新」给你想手动催一下的时候用。
顺带修了个小逻辑:以前查到更新就记「今天查过了」,万一装失败(下载断了、进程被杀),要空等 24 小时才重试。现在只有确认「确实没有新版本」才记时间。
升级
1.0.6 及以下不会自动升到 1.0.7(1.0.6 的自动安装默认关,而且它查版本还可能撞 403),需要手动覆盖一次:
- 下载
WorkBuddyOne-v1.0.7-win64.zip - 解压,把
WorkBuddyOne.exe覆盖到程序目录 profiles/、config.json不动,账号不用重登
从 1.0.7 开始,以后就没有「手动」这一步了。
验证
- 零配额路径真实网络实测:拿到
v1.0.6,拼出的资产地址HEAD返回 302(说明拼对了) - 端到端:启动 1.0.7 → 假源提供 v1.0.8 → 不点任何按钮 → 6 秒内完成 检查→下载→换 exe→接棒→清理 .old,新进程正常服务
- 单元测试覆盖:302 提取 tag、代理返回 200 时从正文提取、403 中文提示、资产名拼接、界面不再有开关
SHA256:69DD3F372281A0E2FC4014658EB9585D2ACAB6400AF9F0390E7497A107260449
v1.0.6 修复更新失败(下载超时 / 安装包里没有 exe)
本系列(v1.0.3 → v1.0.10,共 8 个版本)的完整更新总览见 v1.0.10。
v1.0.6 修复「更新不了」
从 v1.0.3 起就更新失败的两个原因,都修了。
1. 下载被 20 秒总死线掐断
报错长这样:
更新失败: 下载中断: context deadline exceeded (Client.Timeout or context cancellation while reading body)
原因是下载复用了「查版本」那个 client,它设了 20 秒总超时。这个超时算的是整个请求
(含读完 body),不是「连不上就放弃」。安装包约 4 MB,国内走代理常见 100~300 KB/s,
要几十秒才下完,于是每次都在下到一半时被掐断。
现在下载用独立 client:
- 不设总时长,只判断「连续 25 秒一个新字节都没进来」才算卡死 —— 慢但活着就能下完;
- 断线自动续传(
Range从断点接),最多试 3 次,不用整包重来; - 下完核对字节数,对不上再来一轮,避免半包流到解包那一步;
- 按钮上显示下载百分比,一直在涨就说明在正常下载。
2. 「安装包里没有 WorkBuddyOne.exe」
包明明是好的。原因是用 path.Base 取文件名,而它只认 / 当分隔符;部分 Windows
压缩工具(旧版 Compress-Archive)打包时用 \,于是 dir\WorkBuddyOne.exe 被当成
「名字里带反斜杠的文件」,跟 WorkBuddyOne.exe 永远匹配不上。
现在:两种分隔符都认、大小写不敏感;包里只有一个 exe 时直接用它;真找不到时
报错会列出包内实际有哪些文件,方便判断拿到的是什么。
升级方式
低于 1.0.6 的版本没法自动升到 1.0.6(坏的就是更新本身),需要手动升级这一次:
- 下载下面的
WorkBuddyOne-v1.0.6-win64.zip - 解压,把
WorkBuddyOne.exe覆盖到程序目录 profiles/、config.json都不用动,账号不用重登
之后就能正常点「更新」了。
验证
- 12 个单元测试覆盖:停滞检测、慢速但存活(总时长超阈值但每块间隔小于阈值)、
断线续传、反斜杠包、大小写、多 exe 拒绝、真实包解包 - 本地假更新源端到端跑通「下载 → 解包 → 换 exe → 接棒启动」,正斜杠包与反斜杠包各跑一遍
- 真实 GitHub 链路实测:4,132,183 字节 / 7.8 秒 / 504 KB/s,正常完成
SHA256:A8576EA54345A545D0F6E024924368F62F0F77C3F80E7DF861FAD65B70C125C9