Skip to content

Releases: 3155498108/workbuddy-one

v1.0.15:卡片标出「N 天未续期 · 建议先切过去打开一次」

Choose a tag to compare

@3155498108 3155498108 released this 30 Oct 01:28

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)

Choose a tag to compare

@3155498108 3155498108 released this 30 Oct 01:19

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:猫猫旅行按「一天只能派一次」收紧补派

Choose a tag to compare

@3155498108 3155498108 released this 30 Oct 01:14

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:修「无畏契约拼豆专家」任务做不了 / 猫猫旅行自动派出

Choose a tag to compare

@3155498108 3155498108 released this 29 Oct 17:00

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,走客户端搜索框那条路:

  1. MarketExpertSearch 按完整专家名搜,命中要求名字/职业含关键字;
  2. 搜不到再按产品名(「无畏契约」)搜——专家改名也能命中;
  3. 搜索接口异常或零命中才回落推荐列表(老路径);
  4. 全都不行给可读错误(「可能已下架」),不再甩「20 条里没找到」。

真网核验:两个关键字都返回 ex_xQHHD1u5Jkua,走的是真实 headers + 信封解析。

二、猫猫旅行不会自己出门

现场

00:36 手动点「旅行巡检」后:林文海 的猫是 idle + daily_limit_reached=false,
Joker 的猫刚被派出去。

根因

travelOne 的设计是一趟一个动作:到站→领奖,在家→派出。

于是:21:00 那一趟把猫派出(当日次数打满)→ 约 00:00 到站 → 00:36 巡检时
「到站→领奖」这一趟就结束了,同一趟不会接着派 → 下一次固定时点是 09:00,
猫在家待 8 小时。领奖和派出被拆在两趟里,而两趟之间隔着整个固定时点间隔。

改法

  1. 领完立刻再派:travelClaim 返回是否成功,成功后重查状态,仍 idle
    且当日次数没用完就当场再派一趟(必须重查——claim 只回 reward_credit,
    判断不了当日上限)。
  2. 常驻巡检:新增 StartTravelAuto,默认每 60 分钟一趟,启动即补一趟。
    固定时点负责「每天必跑」,巡检负责填「到站没人领 / 领完没人派」的空档。
  3. 可关:schedule.travel_auto(缺省 true)、travel_auto_minutes
    (缺省 60),设置页可见、保存即热生效;travel_enabled=false 时巡检也不
    启动(否则「完全停猫猫旅行」会被巡检偷偷推翻)。
  4. 看得见:任务中心顶部新增「猫猫旅行」卡片,逐账号显示猫名 / 状态
    (在家待发 · 旅途中 · 已到站待领奖)/ 可领积分,附「立刻巡检」按钮。

测试

  • panel/expert_search_test.go 4 个:推荐位没有但搜索命中(核心回归点)、
    宽关键字命中、搜索挂了回落列表、两处都没有返回 nil
  • scheduler:常驻巡检启动即跑 + 周期重复 + 热改 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 双活切不回去 / 端口被占直接退出

Choose a tag to compare

@3155498108 3155498108 released this 28 Oct 11:13

修复外部用户报的 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:修第一次启动闪退

Choose a tag to compare

@3155498108 3155498108 released this 24 Oct 04:46

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,而是
五个问题叠在一起,修掉一个才暴露下一个:

  1. 20 秒总超时(v1.0.6)—— 下载复用了查版本的 HTTP client,Client.Timeout
    计的是整个请求含读完 body,4 MB 包在慢网下必超时
  2. path.Base 只认 /(v1.0.6)—— 旧版 Windows 压缩工具用 \ 当 zip 条目
    分隔符,包正常却被判成「安装包里没有 exe」
  3. 403 配额(v1.0.7)—— 匿名调 GitHub API 按 IP 每小时 60 次,改用
    /releases/latest 的 302 拿版本号,零配额
  4. 备份名撞 delete-pending(v1.0.8)—— 固定用 .old 会被上次更新残留的
    「删除中」名字卡死,rename 直接 Access is denied
  5. 先关窗口后换文件(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 浏览器的地方,只有「以为窗口关了」这一条路径。

改法

三重判据,任意一条成立就认为窗口还开着:

  1. 当初拉起的主进程还活着;
  2. 主进程没了,但 Job 里还有别的进程(Edge relaunch 出来的新主进程/渲染进程)——
    用 QueryInformationJobObject 数 ActiveProcesses;
  3. 页面 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

Choose a tag to compare

@3155498108 3155498108 released this 24 Oct 04:19

本系列(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 修更新后不重启 + 国内下载多源自动切换

Choose a tag to compare

@3155498108 3155498108 released this 24 Oct 03:44

本系列(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.com 666 KB/s、ghfast.top 416 KB/s、ghproxy.net 92 KB/s;github.moeyy.xyz 已失效,移除。

验证

  • 新增 11 个单元测试:换源、换源必须替换残包、全源失败清残包、备份名 100 次不重名、多源取最大版本等;全量测试通过。
  • 端到端复现了你的现场:把运行中的 exe 改名成 .old 再删除(这个名字被进程占住、删不掉),在原位放回一份,然后让新版自己去更新——日志六项全中(已下载 / 已更新到 / 新版本已拉起 / 接棒启动 / 已清理备份),没有一条失败记录。旧代码在这一步必挂。

升级方式

低于 1.0.8 的版本,建议手动覆盖一次:下载 zip,把里面的 WorkBuddyOne.exe 解压到程序目录覆盖即可(profiles/、config.json 都不动,账号不用重登)。之后就能正常自动更新。

SHA256:6CC284C511EB644B21FA47E8E1438F5DF0BAF61E51C8C7611270369ABBACB0D1

v1.0.7 全自动更新 + 修 403 限流

Choose a tag to compare

@3155498108 3155498108 released this 24 Oct 03:16

本系列(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),需要手动覆盖一次:

  1. 下载 WorkBuddyOne-v1.0.7-win64.zip
  2. 解压,把 WorkBuddyOne.exe 覆盖到程序目录
  3. 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)

Choose a tag to compare

@3155498108 3155498108 released this 23 Oct 17:07

本系列(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(坏的就是更新本身),需要手动升级这一次:

  1. 下载下面的 WorkBuddyOne-v1.0.6-win64.zip
  2. 解压,把 WorkBuddyOne.exe 覆盖到程序目录
  3. profiles/、config.json 都不用动,账号不用重登

之后就能正常点「更新」了。

验证

  • 12 个单元测试覆盖:停滞检测、慢速但存活(总时长超阈值但每块间隔小于阈值)、
    断线续传、反斜杠包、大小写、多 exe 拒绝、真实包解包
  • 本地假更新源端到端跑通「下载 → 解包 → 换 exe → 接棒启动」,正斜杠包与反斜杠包各跑一遍
  • 真实 GitHub 链路实测:4,132,183 字节 / 7.8 秒 / 504 KB/s,正常完成

SHA256:A8576EA54345A545D0F6E024924368F62F0F77C3F80E7DF861FAD65B70C125C9