magicrew-auth 0.3.7 → 0.3.8
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 +113 -0
- package/docs/internals.md +40 -11
- package/package.json +1 -1
- package/src/api.mjs +449 -409
- package/src/hooks/checkin.mjs +7 -0
- package/src/hooks/methods.mjs +243 -241
- package/src/keys.mjs +12 -1
- package/src/store.mjs +239 -220
- package/src/tasks.mjs +81 -63
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,118 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.8
|
|
4
|
+
|
|
5
|
+
**签到补上「每日登录积分」—— 国际站一直领不到,根因是它压根不在 onboarding 任务里**
|
|
6
|
+
|
|
7
|
+
症状:国际站账号点「立即签到」,永远回一句「新手福利 0 项已全部完成(当前积分 N)」,
|
|
8
|
+
积分一动不动;可这个号在网页上手动登录,却能拿到一笔「Daily Login Bonus +50」。
|
|
9
|
+
|
|
10
|
+
根因有两层:
|
|
11
|
+
|
|
12
|
+
1. 国际站的每日奖励走 `POST /api/v1/login-gift`,和「新手福利」
|
|
13
|
+
(`GET /api/v1/onboarding/tasks` + `PUT .../completion`)是**两套完全独立的机制**。
|
|
14
|
+
0.3.7 的 checkin 只实现了后者,`login-gift` 一次都没调过。
|
|
15
|
+
2. 国际站的 onboarding 接口对未登记账号恒返回 `{"participant_type":null,"tasks":[]}`。
|
|
16
|
+
`runCheckin` 把「0 项待办」当成「已全部完成」,于是 outcome 报 `done`、消息报
|
|
17
|
+
「0 项已全部完成」—— 看起来一切正常,实际上什么都没做。
|
|
18
|
+
|
|
19
|
+
修法:
|
|
20
|
+
|
|
21
|
+
- `src/api.mjs` 新增 `claimLoginGift(token, org, host)` —— `POST /api/v1/login-gift`,
|
|
22
|
+
无请求体,靠返回的 `granted` 区分「领到了」和「今天已经领过」
|
|
23
|
+
- `src/tasks.mjs` 的 `runCheckin` 改成两步:先领每日登录积分,再处理新手福利,
|
|
24
|
+
任一步失败不影响另一步;消息里分别报两块的结果
|
|
25
|
+
- 空任务列表不再算成功:如实报「本站无新手福利任务」,不再谎报「已全部完成」
|
|
26
|
+
|
|
27
|
+
实测(2026-10-27,三个账号):
|
|
28
|
+
|
|
29
|
+
| 站点 | 每日登录 | 当天重复调用 |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| 国际站 magicrew.ai | +50 | `granted:false`「今日已领取登录积分」 |
|
|
32
|
+
| 国内站 letsmagic.cn | **+200** | 同上 |
|
|
33
|
+
|
|
34
|
+
- 两站路径、方法、返回结构完全一致,只是金额不同
|
|
35
|
+
- **幂等**:当天已领返回 `granted:false`,HTTP 仍 200,不是错误 —— 可以每天无脑调
|
|
36
|
+
- 国内站同样在漏领:实测 1350 → 1550(+200),不是国际站独有的问题
|
|
37
|
+
- 奖励进的是当日 23:59:59 过期的额度包(`expire_type:1`),不累积
|
|
38
|
+
- 接口来源:网页登录过程抓包(CDP Network),`POST /api/v1/login-gift` 无请求体
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
**邀请码滚动修复:`store.mjs` 少了一行 import,这个功能从上线起就没工作过**
|
|
43
|
+
|
|
44
|
+
症状:每个新号注册,浏览器都被带到 `?invitation=<作者的码>`。用户自己的
|
|
45
|
+
`invite_count` 永远是 0 —— 也就是说每注册一个号,都在给作者送积分
|
|
46
|
+
(国际站 200/次,国内站 1,000/次)。
|
|
47
|
+
|
|
48
|
+
根因:`src/store.mjs` 的 `rollInvite()` 里调 `fetchOwnInviteCode()`,但文件头
|
|
49
|
+
**只 import 了 `node:fs` / `node:os` / `node:path`**,从来没 import 过这个函数。
|
|
50
|
+
于是每次调用都在那一行抛 `ReferenceError`,又被下面的空 `catch {}` 静默吞掉:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
async function rollInvite(siteId, jwt, org, host) {
|
|
54
|
+
try {
|
|
55
|
+
const own = await fetchOwnInviteCode(jwt, org, host) // ← ReferenceError
|
|
56
|
+
return writeInvite(siteId, own.code)
|
|
57
|
+
} catch {
|
|
58
|
+
return "" // ← 错误在这里消失,调用方只看到「没滚到」
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
结果 `magicrew-invite-<site>.json` 从来没被写过,`inviteToUse()` 每次都掉进
|
|
64
|
+
「没有记录 → 用作者码」这个分支。滚动机制形同虚设。
|
|
65
|
+
|
|
66
|
+
修法:
|
|
67
|
+
|
|
68
|
+
- `src/store.mjs` 补上 `import { fetchOwnInviteCode } from "./api.mjs"`
|
|
69
|
+
(`api.mjs` 不反向依赖 store,无循环依赖)
|
|
70
|
+
- `rollInvite` 加 6 秒超时(`Promise.race`),失败不再完全静默 ——
|
|
71
|
+
设 `MAGICREW_DEBUG=1` 时打一行错误,避免同类问题再次「安静地死掉」
|
|
72
|
+
- `src/hooks/methods.mjs` 三处调用从 `rollInvite(...).catch(() => {})`
|
|
73
|
+
改成 `await rollInvite(...)`:发射后不管的话,host 进程在 callback 之后被回收,
|
|
74
|
+
滚动一样会丢
|
|
75
|
+
- `src/hooks/checkin.mjs` 每天签到时顺带滚一次 —— 登录那一路万一被打断,
|
|
76
|
+
第二天还能补上
|
|
77
|
+
|
|
78
|
+
实测(2026-10-27,隔离 HOME 里验的,不污染真实配置):
|
|
79
|
+
|
|
80
|
+
| 站点 | 滚动前 | 滚动后 |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| 国际站 | `IV75F0ZKVKXG`(作者) | `IVF4A4H12KM8`(账号自己的) |
|
|
83
|
+
| 国内站 | `IVC8BAWYXRY1`(作者) | `IV1475HL797R`(账号自己的) |
|
|
84
|
+
|
|
85
|
+
> 附带更正 `src/api.mjs` 里一条过时的注释:原来写「网页版 JWT(`device_id` 空)
|
|
86
|
+
> 调 `/api/v1/invitations/info` 回 2155 用户不存在」。实测四个账号全是 354 字符的
|
|
87
|
+
> 网页 JWT、`device_id` 全为空,接口全部返回 200 并正常给出邀请码 ——
|
|
88
|
+
> 上游已经放宽了这个限制,滚动现在是通的,只是以前的 import 缺失挡住了它。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
**文档更正:checkin 从来没有「顺带续 key」,那条保活链路只存在于文档里**
|
|
93
|
+
|
|
94
|
+
`docs/internals.md` 第十四节和本文件 0.3.2 那条都写着「checkin 每天用 24 小时
|
|
95
|
+
提前量强制续一次 key,防止 refresh_token 闲置 7 天失效」。逐版本核对
|
|
96
|
+
0.3.0 / 0.3.2 / 0.3.7:**checkin 路径(`src/hooks/checkin.mjs` → `src/tasks.mjs`)
|
|
97
|
+
从未 import 过 `ensureKey`**,全仓 `leadMs` 只出现在 `src/keys.mjs` 自身。
|
|
98
|
+
|
|
99
|
+
它本来也不需要存在 —— 原来那段论证有个漏洞:
|
|
100
|
+
|
|
101
|
+
- 「一整天没有请求,key 会静静过期」不成立:续期不靠请求触发,是 magpie 按
|
|
102
|
+
`refreshLead`(10 分钟)主动调 `auth.refresh`。只要 magpie 在跑,有没有请求都会续
|
|
103
|
+
- 「挂在 checkin 上补洞」也补不了:真会让 refresh_token 闲置到 7 天失效的场景,
|
|
104
|
+
是 magpie 整整 7 天没开 —— 那时 checkin 同样不跑
|
|
105
|
+
|
|
106
|
+
**所以没有去补这个功能,而是把文档改回实况**:`src/hooks/refresh.mjs` 绕过了
|
|
107
|
+
`keys.mjs` 这个「唯一出口」(它直接调 `api.refreshGatewayKey(auth.refresh)`),
|
|
108
|
+
两处各持一份 refresh_token 会分叉。补上 `ensureKey` 会把 key-store 写起来、
|
|
109
|
+
进而抢轮换,结果是用户莫名被要求重新登录。真要补,得先把 `auth.refresh`
|
|
110
|
+
收敛进 `ensureKey` —— 那是另一件事,不在本 PR 范围。
|
|
111
|
+
|
|
112
|
+
顺带修正 `internals` 里过时的落盘路径:单槽 `magicrew-key-<siteId>.json`
|
|
113
|
+
→ 0.3.3 起的 `<siteId>__<account>` A/B 双槽。并在 `src/keys.mjs` 顶部注释里
|
|
114
|
+
标明「refresh 不经过本模块」这处未收敛的口子,避免后人照旧注释误判。
|
|
115
|
+
|
|
3
116
|
## 0.3.7
|
|
4
117
|
|
|
5
118
|
**登录先看本机浏览器:你在自己浏览器里登过,插件直接认,不再开窗让你重登**
|
package/docs/internals.md
CHANGED
|
@@ -680,31 +680,60 @@ mgw_8f9a…(18:14 又一次续期换的) → 网关请求 HTTP 200 正
|
|
|
680
680
|
不管吊销机制是什么,**只留一把、只有一个出口**这个设计依然成立 ——
|
|
681
681
|
理由从「否则互相吊销」改成「否则本地会分叉,说不清手里那把是不是服务端认的」。
|
|
682
682
|
|
|
683
|
-
### 保活链路(
|
|
683
|
+
### 保活链路(2026-10-27 逐版本核对后的实况)
|
|
684
684
|
|
|
685
685
|
```
|
|
686
|
-
每次请求前 ensureKey(c, getAuth)
|
|
686
|
+
每次请求前 ensureKey(c, getAuth) ← src/hooks/loader.mjs
|
|
687
687
|
├─ 文件里的 key 还能用(剩余 > 10 分钟)→ 直接用
|
|
688
688
|
└─ 不够了 → renewOnce(同站点串行)
|
|
689
689
|
├─ 有 refresh_token → PUT 换新的 → 覆盖落盘
|
|
690
690
|
└─ 没有 / 失败 → 退回 magpie 存的那把
|
|
691
691
|
|
|
692
|
-
|
|
693
|
-
└─
|
|
694
|
-
|
|
692
|
+
到期前 10 分钟 auth.refresh(auth) ← magpie 按 refreshLead 主动调度
|
|
693
|
+
└─ src/hooks/refresh.mjs 直接调 api.refreshGatewayKey(auth.refresh)
|
|
694
|
+
⚠ 不经过 keys.mjs,也不读 key-store
|
|
695
|
+
|
|
696
|
+
每天一次 checkin → runCheckin() ← 只领积分 / 任务,**不碰 key**
|
|
695
697
|
```
|
|
696
698
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
699
|
+
### ⚠ 更正(2026-10-27):checkin 并不续 key
|
|
700
|
+
|
|
701
|
+
早前这里写着「每天一次 `checkin` 里 `ensureKey(c, getAuth, { leadMs: 24h })`,
|
|
702
|
+
用 24 小时提前量保证每天真的续一次」。核对 0.3.0 / 0.3.2 / 0.3.7 三个版本:
|
|
703
|
+
**checkin 路径(`src/hooks/checkin.mjs` → `src/tasks.mjs`)从未 import 过 `ensureKey`**,
|
|
704
|
+
全仓 `leadMs` 只出现在 `src/keys.mjs` 自身。这条链路只存在于文档里。
|
|
705
|
+
|
|
706
|
+
而且它本来也不需要存在 —— 原来那段论证有个漏洞:
|
|
707
|
+
|
|
708
|
+
- **「一整天没有请求,key 会静静过期」不成立**:续期不靠请求触发,是 magpie 按
|
|
709
|
+
`refreshLead`(10 分钟)主动调 `auth.refresh`。只要 magpie 在跑,有没有请求都会续。
|
|
710
|
+
- **「挂在 checkin 上补洞」也补不了**:真会让 refresh_token 闲置到 7 天失效的场景,
|
|
711
|
+
是 magpie 整整 7 天没开 —— 那时 checkin 同样不跑。
|
|
700
712
|
|
|
701
|
-
|
|
713
|
+
所以 checkin 续 key 的实际收益是 0,而**补上它反而有害**:`src/hooks/refresh.mjs`
|
|
714
|
+
绕过了唯一出口(见 `src/keys.mjs` 顶部注释),两处各持一份 refresh_token 会分叉 ——
|
|
715
|
+
补上 `ensureKey` 就会把 key-store 写起来,进而抢轮换,结果是「用户莫名被要求重新登录」。
|
|
716
|
+
|
|
717
|
+
真要补这个能力,前提是先把 `auth.refresh` 收敛进 `ensureKey`。在那之前,
|
|
718
|
+
如实描述现状,比圆一个不存在的承诺安全。
|
|
719
|
+
|
|
720
|
+
**落盘**:`~/.cache/magpie/magicrew-key-<siteId>__<account>.json`(0.3.3 起按账号隔离、
|
|
721
|
+
且改成 A/B 双槽;早前这里写的是不带账号后缀的单槽格式 `{apiKey, refreshToken, expiresAt}`,
|
|
722
|
+
读的时候有 `normalize()` 负责迁移):
|
|
702
723
|
|
|
703
724
|
```json
|
|
704
|
-
{
|
|
725
|
+
{
|
|
726
|
+
"current": "b",
|
|
727
|
+
"refreshToken": "…",
|
|
728
|
+
"keys": {
|
|
729
|
+
"a": { "apiKey": "mgw_…", "expiresAt": 1793102896000 },
|
|
730
|
+
"b": { "apiKey": "mgw_…", "expiresAt": 1793109996000 }
|
|
731
|
+
},
|
|
732
|
+
"at": "2026-10-27T18:20:00.000Z"
|
|
733
|
+
}
|
|
705
734
|
```
|
|
706
735
|
|
|
707
|
-
|
|
736
|
+
`current` 指向正在用的那一槽,另一槽是上一次的、留作 403 时回退。
|
|
708
737
|
|
|
709
738
|
### 首次运行怎么自举
|
|
710
739
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magicrew-auth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "超级麦吉 / MagiCrew for magpie and OpenCode — one package, two providers: letsmagic.cn (国内) and magicrew.ai (international). Password or web-JWT sign-in, the account's own live model list, points balance, newcomer rewards, invite codes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|