magicrew-auth 0.3.7 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,208 +1,208 @@
1
- # 超级麦吉 / MagiCrew for magpie
2
-
3
- **一个包,两个供应商:国内的 GLM / DeepSeek / Qwen / Kimi,国际再加 Claude / GPT / Gemini —— 原样走它的 OpenAI 兼容网关。**
4
-
5
- 超级麦吉(`letsmagic.cn`)和 MagiCrew(`magicrew.ai`)是同一产品的两套云。这个插件把它们各接成一个独立供应商 —— 在 magpie 的供应商列表、用量概览、模型目录里各占一行,不聚合、不互相覆盖登录态。
6
-
7
- ```
8
- 国内 超级麦吉 www.letsmagic.cn GLM / DeepSeek / Qwen / Kimi / MiniMax / MiMo / 豆包
9
- 国际 MagiCrew www.magicrew.ai 上面全部,再加 Claude / GPT / Gemini
10
- 协议 OpenAI Chat Completions —— 原生,不是翻译出来的
11
- 计费 积分(预充值,不是月度配额)
12
- 凭证 登录一次 30 天全自动:api_key 两小时自续,refresh_token 自滚
13
- ```
14
-
15
- > 上面那排是 2026-10-27 从两站 `GET /api/v1/flows/models` 拉下来核对的。清单会随订阅档位和平台上下架变 —— 所以插件里**没有一张写死的模型清单**,账户能选什么就显示什么。
16
-
17
- ---
18
-
19
- ## 功能
20
-
21
- - **两个供应商分得开** —— 国内、国际各一行,图标、名字、模型、积分、登录态全部独立
22
- - **本机浏览器登过就直接认** —— 你平时那个 Edge / Chrome / Brave 里登过 magicrew,点登录时插件读它的 localStorage 接上:不开窗口、不用重登
23
- - **模型跟着账号走** —— 每次登录后按 `/api/v1/flows/models` 实时拉,账户当前能选什么就展示什么,生图和 embedding 不会混进来
24
- - **积分算得准** —— 余额读的是 `/api/v1/quota/points/total`;空号就显示空,不会被优先派活
25
- - **登录一次管 30 天** —— 网页 JWT 顶 30 天,`mgw_` api_key 每 2 小时自续,refresh_token 每次续期时自滚
26
- - **点一下,全自动** —— 插件自己拉起浏览器停在官方登录页,你在里面登(手机号 / 密码 / Google / 邮箱验证码 / 飞书 / 钉钉都行),登录完成那一刻它自己把凭据读走,不用回来点任何东西
27
- - **登录方式还有两条备选** —— 手机号+密码走 API;或者直接粘 JWT + 组织码
28
- - **新手奖励自动领** —— 国内站 8 项里 7 项 PUT 即到账,每天跑一次幂等
29
- - **邀请码自动滚** —— 第一次注册带作者的码,之后每次登录都记住「上一个登录账号」的码,拿到新的就覆盖旧的
30
-
31
- ---
32
-
33
- ## 模型
34
-
35
- **国内的 GLM、DeepSeek、Qwen、Kimi、MiniMax、MiMo、豆包;国际站再加 Claude、GPT、Gemini 三家。**
36
-
37
- 登录后按账号实时拉 `/api/v1/flows/models`,账户能选什么就显示什么 —— 下表只列各家**最新**和**热门**的那几款:
38
-
39
- | 发行商 | 最新 / 热门 | 上下文 | 国内 | 国际 |
40
- |---|---|---|---|---|
41
- | GLM(智谱) | `glm-5.3` · `glm-5.3-flash` | 1M · 256K | ✓ | ✓ |
42
- | DeepSeek | `deepseek-v4.1-flash` | 1M | ✓ | ✓ |
43
- | DeepSeek | `deepseek-v4-pro` | 128K | — | ✓ |
44
- | Qwen(阿里) | `qwen3.8-max` · `qwen3.7-plus` | 1M | ✓ | ✓ |
45
- | Kimi(月之暗面) | `kimi-k3.1-pro` | 1M | ✓ | ✓ |
46
- | MiniMax | `minimax-m3` · `minimax-m3.1-flash` | 1M | ✓ | ✓ |
47
- | MiMo(小米) | `mimo-v2.6-pro` · `mimo-v2.6-flash` | 1M | ✓ | ✓ |
48
- | 豆包 Seed(字节) | `doubao-seed-2.0-pro` · `doubao-seed-2.0-code` | 256K | ✓ | ✓ |
49
- | Claude(Anthropic) | `claude-haiku-5.5` | 1M | — | ✓ |
50
- | GPT(OpenAI) | `gpt-6-luna` · `gpt-5.4-mini` | 1.05M · 400K | — | ✓ |
51
- | Gemini(Google) | `gemini-3.7-flash` | 1M | — | ✓ |
52
-
53
- 全量(含生图、向量、各家历史版本)不进 README —— 装好以后模型选择器里看到的才是账号真实的全部。
54
-
55
- 用的是 `/api/v1/flows/models`(网页模型选择器里眼睛能点到的那些),不是 `/v1/models` —— 后者混着 VIP 锁定、生图、embedding,塞进来用户会选到根本调不动的模型。
56
-
57
- 上下文按三档取:magpie GUI 里手选 > 模型库(`~/.cache/magpie/models.json`)> 兜底 1M。这两个接口都不返回 `context_length`;早先一律填 256K,把 `deepseek-v4.1-flash` 真实 1M 的窗口砍成四分之一,agent 反而更早拒绝 —— 根因与取舍见 `docs/internals.md` 第十六节。
58
-
59
- 推理模型会吃 `max_tokens`:`glm-5.3-flash` 给 64 个 token 时 `reasoning_content` 就吃满、`content` 是空串。插件把低于 512 的 `max_tokens` 抬到 512。
60
-
61
- 两站不是简单叠加:国际站多出 Claude / GPT / Gemini 三家,另有豆包 Seed 1.8 / 2.0-mini / seed-code、Kimi K2.6 / K2.7-code / K3、GLM 5.2、Qwen3.7-max 这些国内没有的档位。
62
-
63
- ---
64
-
65
- ## 新手奖励
66
-
67
- 国内站 8 项任务合计 500 积分,7 项 PUT 一次就完成(实测 1240 → 1540,+300,六项当场入账)。只有「完成第一次 AI 工作」那项服务端认死必须真的跑过一次,PUT 回 `9303`。
68
-
69
- 国际站 `/api/v1/onboarding/tasks` 现在返回空列表 —— 接口在,这个账号没东西可领。
70
-
71
- 每天跑一次,幂等。
72
-
73
- ---
74
-
75
- ## 邀请码
76
-
77
- 只一条规则,不需要你选任何东西:
78
-
79
- ```
80
- ① 固定位置有记录 → 用记录里那个
81
- ② 没有记录 → 用作者的码
82
- ③ 登录成功后 → 查这个账号自己的码 → 覆盖写回
83
- ```
84
-
85
- 效果:第一次注册带作者的码,之后每次都用「上一个登录成功的账号」的码,一路滚下去。
86
-
87
- 固定位置:
88
-
89
- ```
90
- ~/.cache/magpie/magicrew-invite-<站点 id>.json {"code":"...","at":"..."}
91
- ```
92
-
93
- > ③ 不是每次都能成。`/api/v1/invitations/info` 只认带 `device_id` 的 JWT —— 同一个账号同一时刻签的两把,差别就在这一个字段:桌面客户端 438 字符(`device_id` = 64 位 hex)能拿到码,网页版 350 字符(`device_id` 空)回 `2155 用户不存在`。
94
- > 密码登录走 API、带 device 指纹,滚得动;浏览器登录拿到的是网页那把,滚不动 —— 那就保留旧值,不报错、不挡登录。
95
-
96
- 奖励口径(上游前端常量,2026-10-27):
97
-
98
- | | 每次注册 | 每次订阅 | 邀请注册封顶 | 总额封顶 |
99
- |---|---|---|---|---|
100
- | 国内 | 1,000 | 3,000 | 60,000 | 300,000 |
101
- | 国际 | 200 | 500 | 10,000 | 30,000 |
102
-
103
- ---
104
-
105
- ## 安装
106
-
107
- 在 magpie 的 **插件 → 发现** 搜 `magicrew`,点**安装**(npm 上的包名是 `magicrew-auth`)。
108
-
109
- 装完选站点,然后**点第一个「浏览器登录」**。它先看你平时用的那个浏览器:
110
-
111
- **① 那边已经登录过 magicrew** → 插件直接读它的 localStorage 接上,**不开窗口、不用再登一次**。
112
- 凭据就明文躺在 `Local Storage/leveldb` 里(Edge / Chrome / Brave / Chromium 都行)。
113
-
114
- **② 没登录过,或者这个账号已经加过** → 插件自己拉起一个窗口:
115
-
116
- 1. 浏览器窗口弹出,停在官方登录页
117
- 2. 用你平时的方式登进去(手机号 / 密码 / Google / 邮箱验证码 / 飞书 / 钉钉都行)
118
- 3. **登录完成那一刻它自己就接上了** —— 不用回来点任何按钮,不用开 DevTools
119
-
120
- 原理:插件不是调用系统浏览器,而是自己起一个 Edge / Chrome(独立 profile +
121
- `--remote-debugging-port`),所以登录成功后能用 CDP 在页面上下文里把它自己的
122
- localStorage 读走,拿到 JWT 和组织码,签完密钥就关窗。
123
-
124
- **每次登录都是一个新的 profile,用完就删。** 这是必须的:共用一个 profile 的话,
125
- 添加第二个账号时窗口一打开,页面里已经躺着上一个账号的凭据 —— 插件会以为登录完成、
126
- 立刻关窗,而 magpie 拿到的还是旧账号(同账号 → 替换,不新增)。
127
-
128
- > **为什么两条路都要?** magicrew 没有 OAuth 回调通道 —— 登录类型只有 13 种、全是「拿凭据换 token」,没有设备码、没有插件授权、没有一次性 ticket。凭据只可能落在两个地方:你自己浏览器的 localStorage(插件读文件就能拿到,所以优先),或者插件自己起的那个窗口(读不到时才需要)。
129
- >
130
- > 本机没装 Chrome / Edge 时自动降级:改用系统浏览器打开,登录完把凭证送进剪贴板,插件从剪贴板读。
131
-
132
- 不想碰 Console 的话,另外两条:
133
-
134
- - **密码登录** —— 手机号 + 密码 + 区号,走 API,全程在 magpie 里
135
- - **登录态(JWT + 组织码)** —— 手填两个字段:
136
-
137
- ```
138
- DevTools → Application → Local Storage → https://www.magicrew.ai
139
- MAGIC:user:authorization → JSON,取 value = JWT
140
- MAGIC:user:organizationCode → JSON,取 value = 组织码(12 位小写字母数字)
141
- ```
142
-
143
- > 组织码**不能从 JWT 推**。JWT 里只有 `cus.tenant_id`(18 位数字),组织码是另一套短串。缺了它 `/api/v1/*` 回 `2182 用户所在组织不存在`,给错了回 `3035 登录失败`。
144
-
145
- ### 命令行
146
-
147
- ```bash
148
- # npm(推荐:magpie 按 version 检测更新,有新版本会提示)
149
- magpie plugin add magicrew-auth
150
-
151
- # 从仓库源码(拿最新提交,不走 npm;更新按 commit,要手动 update)
152
- magpie plugin add github:MYCF711/magpie-magicrew-auth
153
- ```
154
-
155
- > **两条路的卡片标题不一样**:npm 装 → 卡片标题就是包名 `magicrew-auth`;git 装 → 取 `package.json` 的 `name`,也就是 **`Magicrew`**。功能一样,只是标题。
156
- > 从旧包名 `magpie-magicrew-auth` 过来的,先 `magpie plugin rm magpie-magicrew-auth` 再装。
157
-
158
- 装完确认:
159
-
160
- ```bash
161
- magpie plugin list
162
- # magicrew-auth on
163
- # magicrew (超级麦吉, 27 models) signed in as ...
164
- # magicrew-intl (MagiCrew, 49 models) signed in as ...
165
- ```
166
-
167
- 两个供应商各一行 —— 这就是「不聚合」的样子。
168
-
169
- ---
170
-
171
- ## 常见问题
172
-
173
- **两个站要装两次吗?** 不用。一个包导出两个插件,装一次两个供应商都在,各自登录。
174
-
175
- **我在自己浏览器里登了 magicrew,插件能自动认出来吗?** 能。点「浏览器登录」时插件先去本机 Edge / Chrome / Brave / Chromium 的 localStorage 里找**这个站点**的登录态,找到就直接接上,不弹窗口。读不到、或者这个账号已经加过,才会开窗口让你登新的。
176
-
177
- **添加第二个账号时,网页闪一下就关了、magpie 里也没多出账号?** 0.3.5 及更早会这样:登录浏览器复用同一个 profile,第二个窗口一打开就读到上一个账号的凭据,于是立刻「登录成功」并关窗 —— 加到的是已有账号。0.3.6 起每次登录用一个一次性 profile(关窗即删),不会再串号。
178
-
179
- **npm 上叫 `magicrew-auth`,为什么卡片显示 `Magicrew`?** 卡片标题取包内 `package.json` 的 `name`(`Magicrew`),而 npm 拒绝含大写字母的新包名。发布时用 `publish-npm.mjs` 临时换成 `magicrew-auth`、发完还原 —— 所以 npm 页面是小写包名,git 源装出来是 `Magicrew`。功能完全一样。
180
-
181
- **为什么模型列表会变?** 因为清单是从 `/api/v1/flows/models` 实时拉的。订阅档位变了、平台方上下架变了,清单就跟着变 —— 插件里没有需要维护的模型表。
182
-
183
- **国际站 `/v1/chat/completions` 报 500,22 字节的纯文本?** 那是缺 `organization-code` 请求头。国内站不需要,国际站强制。插件按站点自动带。
184
-
185
- **`glm-5.3-flash` 回复是空的?** 推理 token 吃满了 `max_tokens`。插件已经把低于 512 的抬到 512;还是空就把 `max_tokens` 再调大。
186
-
187
- **Android / macOS 也支持吗?** 插件只调 HTTP 接口,跟客户端无关。设备上能登录,这里就能用。
188
-
189
- ---
190
-
191
- ## 参考源
192
-
193
- | 来源 | 用在哪 |
194
- |---|---|
195
- | [dtyq/magic](https://github.com/dtyq/magic)(Apache 2.0 + 附加限制) | 接口契约:`types/login.ts` 的 `invite_code`、`LoginService.ts` 关于「邀请码在创建组织时消费」的注释、`constants/api.ts` 的错误码 |
196
- | [@magpie-community/opencode-workbuddy-auth](https://github.com/magpie-community/plugins/tree/main/packages/workbuddy)(MIT) | 「一个包导出多个 `XxxAuthPlugin`」的写法:每个站点一个具名导出,各成一个供应商 |
197
- | [@magpie-community/opencode-qoder-auth](https://github.com/magpie-community/plugins/tree/main/packages/qoder)(MIT) | 同上,四站点四导出的对照例 |
198
- | [magpie-plugin-template](https://github.com/MYCF711/magpie-plugin-template)(MIT) | README / CHANGELOG / NOTICE 的骨架与硬规则 |
199
-
200
- 本插件不复制上游代码,只调它的公开 HTTP 接口。完整版权声明见 [`NOTICE.md`](./NOTICE.md)。
201
-
202
- ---
203
-
204
- > 更新记录:[`CHANGELOG.md`](./CHANGELOG.md)
205
-
206
- ## 许可
207
-
208
- MIT。
1
+ # 超级麦吉 / MagiCrew for magpie
2
+
3
+ **一个包,两个供应商:国内的 GLM / DeepSeek / Qwen / Kimi,国际再加 Claude / GPT / Gemini —— 原样走它的 OpenAI 兼容网关。**
4
+
5
+ 超级麦吉(`letsmagic.cn`)和 MagiCrew(`magicrew.ai`)是同一产品的两套云。这个插件把它们各接成一个独立供应商 —— 在 magpie 的供应商列表、用量概览、模型目录里各占一行,不聚合、不互相覆盖登录态。
6
+
7
+ ```
8
+ 国内 超级麦吉 www.letsmagic.cn GLM / DeepSeek / Qwen / Kimi / MiniMax / MiMo / 豆包
9
+ 国际 MagiCrew www.magicrew.ai 上面全部,再加 Claude / GPT / Gemini
10
+ 协议 OpenAI Chat Completions —— 原生,不是翻译出来的
11
+ 计费 积分(预充值,不是月度配额)
12
+ 凭证 登录一次 30 天全自动:api_key 两小时自续,refresh_token 自滚
13
+ ```
14
+
15
+ > 上面那排是 2026-10-27 从两站 `GET /api/v1/flows/models` 拉下来核对的。清单会随订阅档位和平台上下架变 —— 所以插件里**没有一张写死的模型清单**,账户能选什么就显示什么。
16
+
17
+ ---
18
+
19
+ ## 功能
20
+
21
+ - **两个供应商分得开** —— 国内、国际各一行,图标、名字、模型、积分、登录态全部独立
22
+ - **本机浏览器登过就直接认** —— 你平时那个 Edge / Chrome / Brave 里登过 magicrew,点登录时插件读它的 localStorage 接上:不开窗口、不用重登
23
+ - **模型跟着账号走** —— 每次登录后按 `/api/v1/flows/models` 实时拉,账户当前能选什么就展示什么,生图和 embedding 不会混进来
24
+ - **积分算得准** —— 余额读的是 `/api/v1/quota/points/total`;空号就显示空,不会被优先派活
25
+ - **登录一次管 30 天** —— 网页 JWT 顶 30 天,`mgw_` api_key 每 2 小时自续,refresh_token 每次续期时自滚
26
+ - **点一下,全自动** —— 插件自己拉起浏览器停在官方登录页,你在里面登(手机号 / 密码 / Google / 邮箱验证码 / 飞书 / 钉钉都行),登录完成那一刻它自己把凭据读走,不用回来点任何东西
27
+ - **登录方式还有两条备选** —— 手机号+密码走 API;或者直接粘 JWT + 组织码
28
+ - **新手奖励自动领** —— 国内站 8 项里 7 项 PUT 即到账,每天跑一次幂等
29
+ - **邀请码自动滚** —— 第一次注册带作者的码,之后每次登录都记住「上一个登录账号」的码,拿到新的就覆盖旧的
30
+
31
+ ---
32
+
33
+ ## 模型
34
+
35
+ **国内的 GLM、DeepSeek、Qwen、Kimi、MiniMax、MiMo、豆包;国际站再加 Claude、GPT、Gemini 三家。**
36
+
37
+ 登录后按账号实时拉 `/api/v1/flows/models`,账户能选什么就显示什么 —— 下表只列各家**最新**和**热门**的那几款:
38
+
39
+ | 发行商 | 最新 / 热门 | 上下文 | 国内 | 国际 |
40
+ |---|---|---|---|---|
41
+ | GLM(智谱) | `glm-5.3` · `glm-5.3-flash` | 1M · 256K | ✓ | ✓ |
42
+ | DeepSeek | `deepseek-v4.1-flash` | 1M | ✓ | ✓ |
43
+ | DeepSeek | `deepseek-v4-pro` | 128K | — | ✓ |
44
+ | Qwen(阿里) | `qwen3.8-max` · `qwen3.7-plus` | 1M | ✓ | ✓ |
45
+ | Kimi(月之暗面) | `kimi-k3.1-pro` | 1M | ✓ | ✓ |
46
+ | MiniMax | `minimax-m3` · `minimax-m3.1-flash` | 1M | ✓ | ✓ |
47
+ | MiMo(小米) | `mimo-v2.6-pro` · `mimo-v2.6-flash` | 1M | ✓ | ✓ |
48
+ | 豆包 Seed(字节) | `doubao-seed-2.0-pro` · `doubao-seed-2.0-code` | 256K | ✓ | ✓ |
49
+ | Claude(Anthropic) | `claude-haiku-5.5` | 1M | — | ✓ |
50
+ | GPT(OpenAI) | `gpt-6-luna` · `gpt-5.4-mini` | 1.05M · 400K | — | ✓ |
51
+ | Gemini(Google) | `gemini-3.7-flash` | 1M | — | ✓ |
52
+
53
+ 全量(含生图、向量、各家历史版本)不进 README —— 装好以后模型选择器里看到的才是账号真实的全部。
54
+
55
+ 用的是 `/api/v1/flows/models`(网页模型选择器里眼睛能点到的那些),不是 `/v1/models` —— 后者混着 VIP 锁定、生图、embedding,塞进来用户会选到根本调不动的模型。
56
+
57
+ 上下文按三档取:magpie GUI 里手选 > 模型库(`~/.cache/magpie/models.json`)> 兜底 1M。这两个接口都不返回 `context_length`;早先一律填 256K,把 `deepseek-v4.1-flash` 真实 1M 的窗口砍成四分之一,agent 反而更早拒绝 —— 根因与取舍见 `docs/internals.md` 第十六节。
58
+
59
+ 推理模型会吃 `max_tokens`:`glm-5.3-flash` 给 64 个 token 时 `reasoning_content` 就吃满、`content` 是空串。插件把低于 512 的 `max_tokens` 抬到 512。
60
+
61
+ 两站不是简单叠加:国际站多出 Claude / GPT / Gemini 三家,另有豆包 Seed 1.8 / 2.0-mini / seed-code、Kimi K2.6 / K2.7-code / K3、GLM 5.2、Qwen3.7-max 这些国内没有的档位。
62
+
63
+ ---
64
+
65
+ ## 新手奖励
66
+
67
+ 国内站 8 项任务合计 500 积分,7 项 PUT 一次就完成(实测 1240 → 1540,+300,六项当场入账)。只有「完成第一次 AI 工作」那项服务端认死必须真的跑过一次,PUT 回 `9303`。
68
+
69
+ 国际站 `/api/v1/onboarding/tasks` 现在返回空列表 —— 接口在,这个账号没东西可领。
70
+
71
+ 每天跑一次,幂等。
72
+
73
+ ---
74
+
75
+ ## 邀请码
76
+
77
+ 只一条规则,不需要你选任何东西:
78
+
79
+ ```
80
+ ① 固定位置有记录 → 用记录里那个
81
+ ② 没有记录 → 用作者的码
82
+ ③ 登录成功后 → 查这个账号自己的码 → 覆盖写回
83
+ ```
84
+
85
+ 效果:第一次注册带作者的码,之后每次都用「上一个登录成功的账号」的码,一路滚下去。
86
+
87
+ 固定位置:
88
+
89
+ ```
90
+ ~/.cache/magpie/magicrew-invite-<站点 id>.json {"code":"...","at":"..."}
91
+ ```
92
+
93
+ > ③ 不是每次都能成。`/api/v1/invitations/info` 只认带 `device_id` 的 JWT —— 同一个账号同一时刻签的两把,差别就在这一个字段:桌面客户端 438 字符(`device_id` = 64 位 hex)能拿到码,网页版 350 字符(`device_id` 空)回 `2155 用户不存在`。
94
+ > 密码登录走 API、带 device 指纹,滚得动;浏览器登录拿到的是网页那把,滚不动 —— 那就保留旧值,不报错、不挡登录。
95
+
96
+ 奖励口径(上游前端常量,2026-10-27):
97
+
98
+ | | 每次注册 | 每次订阅 | 邀请注册封顶 | 总额封顶 |
99
+ |---|---|---|---|---|
100
+ | 国内 | 1,000 | 3,000 | 60,000 | 300,000 |
101
+ | 国际 | 200 | 500 | 10,000 | 30,000 |
102
+
103
+ ---
104
+
105
+ ## 安装
106
+
107
+ 在 magpie 的 **插件 → 发现** 搜 `magicrew`,点**安装**(npm 上的包名是 `magicrew-auth`)。
108
+
109
+ 装完选站点,然后**点第一个「浏览器登录」**。它先看你平时用的那个浏览器:
110
+
111
+ **① 那边已经登录过 magicrew** → 插件直接读它的 localStorage 接上,**不开窗口、不用再登一次**。
112
+ 凭据就明文躺在 `Local Storage/leveldb` 里(Edge / Chrome / Brave / Chromium 都行)。
113
+
114
+ **② 没登录过,或者这个账号已经加过** → 插件自己拉起一个窗口:
115
+
116
+ 1. 浏览器窗口弹出,停在官方登录页
117
+ 2. 用你平时的方式登进去(手机号 / 密码 / Google / 邮箱验证码 / 飞书 / 钉钉都行)
118
+ 3. **登录完成那一刻它自己就接上了** —— 不用回来点任何按钮,不用开 DevTools
119
+
120
+ 原理:插件不是调用系统浏览器,而是自己起一个 Edge / Chrome(独立 profile +
121
+ `--remote-debugging-port`),所以登录成功后能用 CDP 在页面上下文里把它自己的
122
+ localStorage 读走,拿到 JWT 和组织码,签完密钥就关窗。
123
+
124
+ **每次登录都是一个新的 profile,用完就删。** 这是必须的:共用一个 profile 的话,
125
+ 添加第二个账号时窗口一打开,页面里已经躺着上一个账号的凭据 —— 插件会以为登录完成、
126
+ 立刻关窗,而 magpie 拿到的还是旧账号(同账号 → 替换,不新增)。
127
+
128
+ > **为什么两条路都要?** magicrew 没有 OAuth 回调通道 —— 登录类型只有 13 种、全是「拿凭据换 token」,没有设备码、没有插件授权、没有一次性 ticket。凭据只可能落在两个地方:你自己浏览器的 localStorage(插件读文件就能拿到,所以优先),或者插件自己起的那个窗口(读不到时才需要)。
129
+ >
130
+ > 本机没装 Chrome / Edge 时自动降级:改用系统浏览器打开,登录完把凭证送进剪贴板,插件从剪贴板读。
131
+
132
+ 不想碰 Console 的话,另外两条:
133
+
134
+ - **密码登录** —— 手机号 + 密码 + 区号,走 API,全程在 magpie 里
135
+ - **登录态(JWT + 组织码)** —— 手填两个字段:
136
+
137
+ ```
138
+ DevTools → Application → Local Storage → https://www.magicrew.ai
139
+ MAGIC:user:authorization → JSON,取 value = JWT
140
+ MAGIC:user:organizationCode → JSON,取 value = 组织码(12 位小写字母数字)
141
+ ```
142
+
143
+ > 组织码**不能从 JWT 推**。JWT 里只有 `cus.tenant_id`(18 位数字),组织码是另一套短串。缺了它 `/api/v1/*` 回 `2182 用户所在组织不存在`,给错了回 `3035 登录失败`。
144
+
145
+ ### 命令行
146
+
147
+ ```bash
148
+ # npm(推荐:magpie 按 version 检测更新,有新版本会提示)
149
+ magpie plugin add magicrew-auth
150
+
151
+ # 从仓库源码(拿最新提交,不走 npm;更新按 commit,要手动 update)
152
+ magpie plugin add github:MYCF711/magpie-magicrew-auth
153
+ ```
154
+
155
+ > **两条路的卡片标题不一样**:npm 装 → 卡片标题就是包名 `magicrew-auth`;git 装 → 取 `package.json` 的 `name`,也就是 **`Magicrew`**。功能一样,只是标题。
156
+ > 从旧包名 `magpie-magicrew-auth` 过来的,先 `magpie plugin rm magpie-magicrew-auth` 再装。
157
+
158
+ 装完确认:
159
+
160
+ ```bash
161
+ magpie plugin list
162
+ # magicrew-auth on
163
+ # magicrew (超级麦吉, 27 models) signed in as ...
164
+ # magicrew-intl (MagiCrew, 49 models) signed in as ...
165
+ ```
166
+
167
+ 两个供应商各一行 —— 这就是「不聚合」的样子。
168
+
169
+ ---
170
+
171
+ ## 常见问题
172
+
173
+ **两个站要装两次吗?** 不用。一个包导出两个插件,装一次两个供应商都在,各自登录。
174
+
175
+ **我在自己浏览器里登了 magicrew,插件能自动认出来吗?** 能。点「浏览器登录」时插件先去本机 Edge / Chrome / Brave / Chromium 的 localStorage 里找**这个站点**的登录态,找到就直接接上,不弹窗口。读不到、或者这个账号已经加过,才会开窗口让你登新的。
176
+
177
+ **添加第二个账号时,网页闪一下就关了、magpie 里也没多出账号?** 0.3.5 及更早会这样:登录浏览器复用同一个 profile,第二个窗口一打开就读到上一个账号的凭据,于是立刻「登录成功」并关窗 —— 加到的是已有账号。0.3.6 起每次登录用一个一次性 profile(关窗即删),不会再串号。
178
+
179
+ **npm 上叫 `magicrew-auth`,为什么卡片显示 `Magicrew`?** 卡片标题取包内 `package.json` 的 `name`(`Magicrew`),而 npm 拒绝含大写字母的新包名。发布时用 `publish-npm.mjs` 临时换成 `magicrew-auth`、发完还原 —— 所以 npm 页面是小写包名,git 源装出来是 `Magicrew`。功能完全一样。
180
+
181
+ **为什么模型列表会变?** 因为清单是从 `/api/v1/flows/models` 实时拉的。订阅档位变了、平台方上下架变了,清单就跟着变 —— 插件里没有需要维护的模型表。
182
+
183
+ **国际站 `/v1/chat/completions` 报 500,22 字节的纯文本?** 那是缺 `organization-code` 请求头。国内站不需要,国际站强制。插件按站点自动带。
184
+
185
+ **`glm-5.3-flash` 回复是空的?** 推理 token 吃满了 `max_tokens`。插件已经把低于 512 的抬到 512;还是空就把 `max_tokens` 再调大。
186
+
187
+ **Android / macOS 也支持吗?** 插件只调 HTTP 接口,跟客户端无关。设备上能登录,这里就能用。
188
+
189
+ ---
190
+
191
+ ## 参考源
192
+
193
+ | 来源 | 用在哪 |
194
+ |---|---|
195
+ | [dtyq/magic](https://github.com/dtyq/magic)(Apache 2.0 + 附加限制) | 接口契约:`types/login.ts` 的 `invite_code`、`LoginService.ts` 关于「邀请码在创建组织时消费」的注释、`constants/api.ts` 的错误码 |
196
+ | [@magpie-community/opencode-workbuddy-auth](https://github.com/magpie-community/plugins/tree/main/packages/workbuddy)(MIT) | 「一个包导出多个 `XxxAuthPlugin`」的写法:每个站点一个具名导出,各成一个供应商 |
197
+ | [@magpie-community/opencode-qoder-auth](https://github.com/magpie-community/plugins/tree/main/packages/qoder)(MIT) | 同上,四站点四导出的对照例 |
198
+ | [magpie-plugin-template](https://github.com/MYCF711/magpie-plugin-template)(MIT) | README / CHANGELOG / NOTICE 的骨架与硬规则 |
199
+
200
+ 本插件不复制上游代码,只调它的公开 HTTP 接口。完整版权声明见 [`NOTICE.md`](./NOTICE.md)。
201
+
202
+ ---
203
+
204
+ > 更新记录:[`CHANGELOG.md`](./CHANGELOG.md)
205
+
206
+ ## 许可
207
+
208
+ MIT。
@@ -0,0 +1,68 @@
1
+ # 事故记录 2026-10-28 · 添加第二个账号却刷新了第一个的凭证
2
+
3
+ > 状态:**已修复并验证**(插件 0.3.12 / magpie 已重启)
4
+ > 待办:本文件内容等 Hindsight 后端(192.168.1.6:8888)恢复后灌入记忆库
5
+
6
+ ## 症状
7
+
8
+ 在 magpie 里点「添加新账号」:
9
+
10
+ - 第一次:正常,成功加进账号 A
11
+ - 第二次:**不开窗口,一秒内就成功**,但 magpie 里没多出账号,A 的凭证被刷新了
12
+
13
+ ## 根因
14
+
15
+ 不是仓库代码的问题 —— **magpie 里装的插件版本太老(0.3.5)**。
16
+
17
+ | 事实 | 证据 |
18
+ |---|---|
19
+ | magpie 加载的是 Magicrew 0.3.5 | `~/.config/magpie/plugins/node_modules/Magicrew/package.json` version=0.3.5;`bun.lock` 锁 `#825416b` |
20
+ | 0.3.5 用**固定复用 profile** | 0.3.5 `src/browser-login.mjs:25` → `join(homedir(), ".cache", "magpie", "magicrew-login-profile")`,注释原文「profile 目录**不删**,下次还能复用登录态」 |
21
+ | 该目录里躺着上一个账号的凭据 | 读 `.../magicrew-login-profile/Default/Local Storage/leveldb` → origin `www.letsmagic.cn`,org=`66p0q56qo47j` |
22
+ | 第二次打开时立刻被判「已登录」 | 同一 profile → `waitForCreds` 第一轮(1.5 s)就读到残留 → 判定成功 → 关窗 |
23
+ | magpie 侧按同身份处理 → 替换而非新增 | `providers-state.json` 的 `accountNames` 只有 `h97gkbu8bruj` 一条 |
24
+
25
+ 链路:**复用 profile 残留凭据 → 立刻判成功 → 提交同 uid → magpie 替换旧账号 → 用户看到「直接刷新凭证」**。
26
+
27
+ ## 代码里早在 0.3.6 就修了(提交 6ca3255)
28
+
29
+ - `newProfileDir()`:每次登录一个新目录,结束即删
30
+ - `sweepProfiles()`:清残留,且**必删**历史复用目录 `magicrew-login-profile`
31
+ - `waitForCreds(…, { baseline })`:窗口打开时就存在的凭据不算登录成功
32
+ - `~/.cache/magpie/magicrew-orgs-<site>.json`:加过的 org 名单,挡住「本机浏览器里那把属于已加过账号」时仍自动接上
33
+
34
+ ## 这次做了什么
35
+
36
+ ```bash
37
+ # 1. 备份 magpie 插件清单(可回滚)
38
+ cd ~/.config/magpie/plugins
39
+ cp package.json package.json.bak-before-magicrew-012-20261028-003807
40
+ cp bun.lock bun.lock.bak-before-magicrew-012-20261028-003807
41
+
42
+ # 2. 升级(关键:plugin add 不重解析 commit,必须 update)
43
+ cd /e/Downloads && ./magpie-windows-amd64.exe plugin update # → Magicrew 0.3.12
44
+
45
+ # 3. 旧的复用 profile 移出 sweep 范围
46
+ mv ~/.cache/magpie/magicrew-login-profile ~/.cache/magicrew-profile-backup-20261028-004617
47
+
48
+ # 4. 预灌已知 org 名单(本机真实 Edge 里也有 h97gkbu8bruj 的登录态,不挡会被自动接上)
49
+ # magicrew: h97gkbu8bruj, 7fli52t2tc2b, 66p0q56qo47j, orgcode67890
50
+ # magicrew-intl: 67u4oe6fbenj, orgcode67890
51
+
52
+ # 5. 重启 magpie 加载新代码
53
+ powershell -NoProfile -Command "Stop-Process -Id <pid> -Force; Start-Process 'E:\Downloads\magpie-windows-amd64.exe' -ArgumentList 'gui','plugins'"
54
+ curl.exe -s -o /dev/null -w "%{http_code}\n" --max-time 10 http://127.0.0.1:3425/v1/models # 200
55
+ ```
56
+
57
+ ## 验证结果
58
+
59
+ - `magpie plugin list` → `(Magicrew 0.3.12)`,账号仍在:`h97gkbu8bruj`(超级麦吉)、`1451895195@qq.com`(MagiCrew)
60
+ - `bun.lock` → `Magicrew@github:MYCF711/magpie-magicrew-auth#970a3ea`
61
+ - `readLocalCreds("www.letsmagic.cn").org = h97gkbu8bruj`,在 `readKnownOrgs("magicrew")` 里 → **点添加不会再自动接上旧账号**
62
+ - 模型列表已重新拉取:国内 27 / 国际 49
63
+
64
+ ## 三条可复用判据
65
+
66
+ 1. **`magpie plugin add <git-url>` 不会升级已装插件** —— bun.lock 锁着 `#<sha>`,add 只是确认依赖存在;升级必须 `magpie plugin update`。
67
+ 2. **`~/.cache/magpie/plugin-host/*.js` 没生成新文件 = 插件代码没重载** —— 改完必须重启 magpie 进程。
68
+ 3. **一次性 profile 的代价要讲清楚**:独立 user-data-dir 没有日常浏览器的 cookie,所以每次「添加账号」都要在弹窗里重登一次;只有「本机浏览器里登好的、且不在 orgs 名单里的」才会自动接上。
package/docs/internals.md CHANGED
@@ -680,31 +680,102 @@ mgw_8f9a…(18:14 又一次续期换的) → 网关请求 HTTP 200 正
680
680
  不管吊销机制是什么,**只留一把、只有一个出口**这个设计依然成立 ——
681
681
  理由从「否则互相吊销」改成「否则本地会分叉,说不清手里那把是不是服务端认的」。
682
682
 
683
- ### 保活链路(0.3.2 起)
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
- 每天一次 checkin 里 ensureKey(c, getAuth, { leadMs: 24h })
693
- └─ 用 24 小时提前量 → 保证每天真的续一次
694
- refresh_token 因此一直新鲜,不会因闲置 7 天失效
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**
697
+ ```
698
+
699
+ ### checkin 现在做四件事(0.3.11 起)
700
+
701
+ ```
702
+ ① POST /api/v1/login-gift 领每日登录积分(国内 +200、国际 +50;当天已领回 granted:false,幂等)
703
+ ② 首任务 / 首对话没做过 → 走 Socket.IO 发一条消息(纯 Node,不开浏览器)
704
+ 国内站:onboarding 的 first_ai_work 还是 PENDING → 建话题 + 发一条,轮询等 COMPLETED
705
+ 国际站:onboarding 恒返回空任务 → 改看「有没有工作台项目」,一个都没有才发
706
+ ③ 其余新手福利 → PUT /api/v1/onboarding/tasks/<code>/completion
707
+ ④ 报积分变化
708
+ ```
709
+
710
+ ② 是唯一有外部副作用的动作:会建项目 / 话题、真花一次模型积分(挑最便宜的模型)。
711
+
712
+ **它是每个账号一辈子只做一次的事,不是每天的事**(0.3.12 改正)。真正的触发点是
713
+ **登录成功那一刻**(`src/hooks/methods.mjs` 三条登录路径都会 `kickoffFirstTask`,
714
+ 发射后不管、不阻塞登录);签到只是兜底 —— 万一登录那次没跑成(进程被杀、网络抖),
715
+ 下一次签到接着做。
716
+
717
+ 闸门落在 `~/.cache/magpie/magicrew-autochat-<siteId>.json`
718
+ (`siteId` 取 `magicrew` / `magicrew-intl`),内容是:
719
+
720
+ ```json
721
+ { "<org>": { "at": "ISO", "status": "sent|failed|gave-up", "attempts": 1,
722
+ "topicId": "…", "model": "Qwen3.5 Flash", "reason": "…" } }
695
723
  ```
696
724
 
697
- **为什么 checkin 要强制续**:api_key 只活 2 小时,`ensureKey` 的默认提前量是 10 分钟 ——
698
- 一整天没有请求的话,key 会静静过期;更麻烦的是 refresh_token 只活 7 天且**用一次才轮换**,
699
- 长期不用就会闲置到期。挂在 checkin 上(magpie 保证每账号每天跑一次)正好补这个洞。
725
+ 规则:
700
726
 
701
- **落盘**:`~/.cache/magpie/magicrew-key-<siteId>.json`,只保留最新一把:
727
+ | 情况 | 写什么 | 之后还发不发 |
728
+ |---|---|---|
729
+ | 消息发出去了 | `status: "sent"` | **永远不发**(换天、重登、重启都一样) |
730
+ | 真去发了但失败 | `status: "failed"`,`attempts++` | 发,最多 3 次 |
731
+ | 连失败 3 次 | `status: "gave-up"` | 不发(别让永远失败的号每次撞一遍) |
732
+ | 还没有工作台 / 项目 | 不写 | 发,下次再来(只读接口,成本低) |
733
+ | 服务端本来就是 COMPLETED | `status: "sent"` + `reason` | 不发 |
734
+
735
+ 老版本(0.3.11)记的是 `{ [org]: "YYYY-MM-DD" }` 字符串日期 —— 读的时候按「已发过」
736
+ 归一化,所以升级上来不会补发一遍。
737
+
738
+ 怎么发的(协议帧、三个 id 从哪来、为什么不用浏览器)见 `src/chat-task.mjs` 顶部注释。
739
+ key 依然一个都不碰。
740
+
741
+ ### ⚠ 更正(2026-10-27):checkin 并不续 key
742
+
743
+ 早前这里写着「每天一次 `checkin` 里 `ensureKey(c, getAuth, { leadMs: 24h })`,
744
+ 用 24 小时提前量保证每天真的续一次」。核对 0.3.0 / 0.3.2 / 0.3.7 三个版本:
745
+ **checkin 路径(`src/hooks/checkin.mjs` → `src/tasks.mjs`)从未 import 过 `ensureKey`**,
746
+ 全仓 `leadMs` 只出现在 `src/keys.mjs` 自身。这条链路只存在于文档里。
747
+
748
+ 而且它本来也不需要存在 —— 原来那段论证有个漏洞:
749
+
750
+ - **「一整天没有请求,key 会静静过期」不成立**:续期不靠请求触发,是 magpie 按
751
+ `refreshLead`(10 分钟)主动调 `auth.refresh`。只要 magpie 在跑,有没有请求都会续。
752
+ - **「挂在 checkin 上补洞」也补不了**:真会让 refresh_token 闲置到 7 天失效的场景,
753
+ 是 magpie 整整 7 天没开 —— 那时 checkin 同样不跑。
754
+
755
+ 所以 checkin 续 key 的实际收益是 0,而**补上它反而有害**:`src/hooks/refresh.mjs`
756
+ 绕过了唯一出口(见 `src/keys.mjs` 顶部注释),两处各持一份 refresh_token 会分叉 ——
757
+ 补上 `ensureKey` 就会把 key-store 写起来,进而抢轮换,结果是「用户莫名被要求重新登录」。
758
+
759
+ 真要补这个能力,前提是先把 `auth.refresh` 收敛进 `ensureKey`。在那之前,
760
+ 如实描述现状,比圆一个不存在的承诺安全。
761
+
762
+ **落盘**:`~/.cache/magpie/magicrew-key-<siteId>__<account>.json`(0.3.3 起按账号隔离、
763
+ 且改成 A/B 双槽;早前这里写的是不带账号后缀的单槽格式 `{apiKey, refreshToken, expiresAt}`,
764
+ 读的时候有 `normalize()` 负责迁移):
702
765
 
703
766
  ```json
704
- { "apiKey": "mgw_…", "refreshToken": "…", "expiresAt": 1793102896000, "at": "2026-10-27T18:20:00.000Z" }
767
+ {
768
+ "current": "b",
769
+ "refreshToken": "…",
770
+ "keys": {
771
+ "a": { "apiKey": "mgw_…", "expiresAt": 1793102896000 },
772
+ "b": { "apiKey": "mgw_…", "expiresAt": 1793109996000 }
773
+ },
774
+ "at": "2026-10-27T18:20:00.000Z"
775
+ }
705
776
  ```
706
777
 
707
- 旧的一旦被覆盖就删了 —— 服务端那边它也已经失效,留着只会让下一个读的人用错。
778
+ `current` 指向正在用的那一槽,另一槽是上一次的、留作 403 时回退。
708
779
 
709
780
  ### 首次运行怎么自举
710
781