Skip to content

registry: add magpie-providers-hub (10 providers, third-party dsh-providers-hub adapter) - #502

Merged
yetone merged 1 commit into
magpie-community:mainfrom
Gyl1219:add-providers-hub
Oct 30, 2026
Merged

yetone merged 1 commit into
magpie-community:mainfrom
Gyl1219:add-providers-hub

Conversation

@Gyl1219

@Gyl1219 Gyl1219 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

给 registry.json 加一条。

{ "package": "magpie-providers-hub", "name": "Providers Hub",
  "providers": ["hub-astudio","hub-accio","hub-lobsterai","hub-magicrew","hub-magicrew-intl"],
  "community": true,
  "summary": { "en": "…", "zh": "…" } }

这个包

npm — https://www.npmjs.com/package/magpie-providers-hub (0.4.7,2026-10-30 已发布)
源码 — https://github.com/Gyl1219/magpie-providers-hub

它是 iJetLi/dsh-providers-hub(MIT)的第三方 magpie 适配,README / UPSTREAM.md / NOTICE.md 三处声明归属;运行时不依赖 DSH(index.mjs 与 src/ 只 import node: 内置模块和彼此)。

一个插件,五条供应商:每个渠道是自己的 provider id(hub-<渠道>),于是可以各自登录、各自看额度、各自单独关掉。这是拆成多条的原因 —— magpie 的 failover 和开关都是 provider 粒度,聚合成一条就都做不到。

关于发布账号(先交代,省得你去查)

npm 上是 gyl1219 发布的,GitHub 源码在 Gyl1219 名下,主 npm 账号是 gyl0511。原因是主账号目前在 npm 的 72 小时安全暂停里(recovery-code 触发,2026-11-02 06:31 解除),期间不能发布;为了不耽误这条 PR 才用备用账号发布。owner add gyl0511 会在暂停解除后补上(这颗分阶段 token 没有账号读权限,CLI 做不了,我跟进)。包本身的维护权与源码归属都是同一个作者,不是转手包。

三条审核会问的

1. provider id 与代码一致。
node -e "import('magpie-providers-hub').then(m => console.log(m.DEFAULT_PROVIDER_IDS))" 打出的就是上面那五个。id 全部 ASCII 是故意的:它会进模型 id、CLI 参数和 <provider>/<account> 槽 key。(包内另外五个重叠渠道见下节,默认不注册。)

2. 不写宿主文件(#399 卡的就是这条,所以先自查了)。
包永远不会去碰 magpie 的市场缓存:

  • 原本的 src/self-register.mjs 挪到了 tools/(手工跑的脚本,files 白名单不含它,不随 npm 发布 —— npm pack 只会看到 22 个文件);
  • MagiCrew 浏览器登录用的一次性 Chromium profile,从 ~/.cache/magpie 挪到了系统临时目录;
  • 诊断日志 notify-debug.log 改为默认不写(DSH_HUB_DEBUG=1 才写)。

tests/no-host-writes.test.mjs 三条都钉死:发布代码里不出现市场缓存的文件名、files 不含 tools//tests/、以及一个加载探针(沙箱 HOME + 预置 plugin-market.json,加载后 mtime 与内容必须零变化 —— 和当初查 0.1.8/0.1.9 用的是同一个探针)。

child_process 只在三处用:Windows 通知、AutoClaw 桌面端凭据探测、MagiCrew 浏览器登录 —— 都在登录/通知时,加载时不跑。

3. 凭据存在哪。
进 magpie 的账号槽,另外旁路写一份 ~/.config/magpie/<channel>-credential.json。侧边栏页面是沙盒(connect-src 'none',只读快照),任何 token 都不会写进去。

两个我没填的字段

icon — 别的条目里的 icon key("opencode"、"kimi"、"zcode")看起来是内置图标名,我不知道这份词表,编一个大概会静默回落,所以不填;插件自己声明的 auth.icon(data URI)已经在渲染了。

replaces — 没有替代任何内置订阅,所以没有。

关于重叠渠道(按上一轮意见改的)

点名的四个重叠我核对过并处理了:officeace、autoclaw/autoclaw-cn、loomy、minimax-code-global 对应的五个渠道(含 AutoClaw 国内/国际两条)留在包里但默认不注册 —— 入口模块对这五条导出的是对象占位(magpie 只把函数导出当插件),模块级 sweep(额度扫描 + 旁路补签)对它们同样跳过,不发请求也不进快照。想用的人设 MAGPIE_HUB_EXTRA_CHANNELS=<id,…|all> 或写 ~/.config/magpie/providers-hub.json({"extraChannels":"all"},插件只读)再重启。

代码不删的原因:已登录用户升级不掉线。registry 条目只列不重叠的五条,装完默认也只有这五条供应商。


English summary: adds a registry entry for magpie-providers-hub@0.4.7 (published on npm today), a third-party adapter of iJetLi/dsh-providers-hub (MIT) with five registered per-channel providers (hub-*). The five channels that overlap with existing registry entries are shipped but unregistered by default (MAGPIE_HUB_EXTRA_CHANNELS / a read-only config file turns them on), and the module-level sweep skips them too. The package writes no host files — the self-register module lives in tools/ outside files, the browser-login temp profile moved to the system temp dir, and the debug log is off by default; tests/no-host-writes.test.mjs asserts all of it plus the same load probe used on 0.1.9.

Unrelated to Huawei, iFlytek, MiniMax, Zhipu, NetEase or any of the vendors above.

@Gyl1219

Gyl1219 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

提完这条之后又自查了一遍「插件在宿主地盘上落东西」,追加修掉一处,顺便把只读的两处也一并交代清楚。

又修掉一处写

debugLog() 会在登录链路上往 ~/.config/magpie/notify-debug.log 追加(真机已经写到 15 KB)。虽然写的是 config 而不是 cache,但按你在 #399 里的标准(That file is outside the plugin's folder and belongs to magpie)属于同一类问题,所以改成默认不写:只有 DSH_HUB_DEBUG=1 时才落盘(排查用)。0.4.5。

目前包内对宿主目录的写入:零。三处曾经写的都处理完了 ——

曾经 现在
市场缓存(自注册) 移出发布包 → tools/(不随 npm 发布)
浏览器登录 profile(~/.cache/magpie) 挪到系统临时目录
notify-debug.log(~/.config/magpie) 默认不写,DSH_HUB_DEBUG=1 才写

两处只读,也说明一下(不想让你自己翻包才发现)

  1. 读 magpie 的 plugin-auth.json(只读,绝不写)。只做两件事:把本插件自己旧版 provider id(providers-hub*)的账号槽搬到新的 hub-*;统计每渠道的账号数。按 provider id 过滤,拿不到就当没有。
  2. 读 magpie 的 models.json(只读)。MagiCrew 的上下文长度兜底,模型接口没给时用它。

两处都是「拿不到就回退」,没有它们插件照常工作。如果你认为插件读这两个文件也不合适,我可以改成完全不碰 —— 只是旧版用户的登录态需要重新登录一次。

状态

npm test 242 pass(含 6 条 host-writes 回归:源码扫描 + 加载探针 + 日志开关)。源码 https://github.com/Gyl1219/magpie-providers-hub,npm 发布仍排 2026-11-02(72 小时安全暂停),发完我回来在这条里补一句。

@yetone

yetone commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

谢谢这么细的自查,宿主写入这块的处理是对的。不过这条现在合不了,原因有两个:

  1. 包还没上 npm。 registry 里的每一条,用户一点“安装”就会去装。装不到的条目合进来就是一个坏按钮。等 npm view magpie-providers-hub 能查到版本,再来更新这个 PR。
  2. 有四个渠道和 registry 里现有的插件重了: officeace(officeace-auth)、autoclaw / autoclaw-cn(opencode-autoclaw-auth)、loomy(opencode-loomy-auth),还有 MiniMax Code 国际版(opencode-minimax-auth 的 minimax-code-global)。同一个订阅出现两个入口、两套登录、两套额度,用户会搞不清该装哪个,出了问题也说不清是哪个插件的。

建议是只保留 registry 里还没有的渠道:AStudio、Accio、LobsterAI、MagiCrew 国内/国际。重复的那几个,如果你的实现修好了现有插件没修的问题,欢迎直接给那几个包提 PR。

npm 发布之后,把重复的渠道去掉再推一版,我会把代码拉下来,用真实加载跑一遍再看。

@Gyl1219

Gyl1219 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

收到,两条我都认同,按你的意见改。先确认一下第二条的边界,免得改错方向。

1. npm

账号在 npm 的 72 小时安全暂停里(recovery-code 触发),到 2026-11-02 06:31 (+08:00) 解除。解除当天我用 npm stage publish(npm 12,不需要 OTP)把 magpie-providers-hub 发出来,然后立刻回来更新这条 PR。在那之前不打扰你。

2. 重叠的渠道

你点的四个我核对过了,确实重:

我的 registry 已有的 包
hub-officeace officeace officeace-auth
hub-autoclaw / hub-autoclawintl autoclaw / autoclaw-cn opencode-autoclaw-auth
hub-loomy loomy opencode-loomy-auth
hub-minimaxintl minimax-code-global opencode-minimax-auth

保留不重叠的五个:hub-astudio、hub-accio、hub-lobsterai、hub-magicrew、hub-magicrew-intl。

想跟你确认的是:你说的「只保留 registry 里还没有的渠道」,是指

  • (a) 只改 registry 条目里的 providers 列表(包里十个渠道照旧,只是卡面不宣传重叠的那几个),还是
  • (b) 包本体也要把那五个渠道去掉,让「一个订阅只有一个入口」这件事在用户装完之后也成立?

我倾向 (b) —— (a) 只是把重复藏起来,用户装完还是会在供应商列表里看到两套 OfficeAce / AutoClaw / Loomy / MiniMax,你要避免的困惑并没有真的消失。代价是我这边要重装那几个社区插件(登录态要重来一次),这个我能接受。

如果你说 (a) 就够,我就只改条目。你说哪个我就按哪个做。

3. 顺带

做 AutoClaw 国际版登录时我踩到过社区的 OAuth 实现里几个真机问题(beforeunload 的 abort 把会话清掉、阿里滑块回执缺失会报误导性的「版本已停止服务」、回调路径按 vendor 硬匹配会 404)。如果 opencode-autoclaw-auth 那边还没修,我可以整理成 PR 提过去 —— 那是你们的地盘,我先问一句再动手,免得重复。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

按你说的做了,两步都推上去了:

1. 条目收敛(本分支第二个提交):providers 只列不重叠的五条 —— hub-astudio / hub-accio / hub-lobsterai / hub-magicrew / hub-magicrew-intl,summary 同步改写。

2. 包侧 0.4.6:你点的五个渠道留在包里但默认不注册 —— 入口模块对这五条导出的是对象占位(magpie 只把函数导出当插件),所以用户装完默认只有上面五条供应商;想用重叠渠道的人设 MAGPIE_HUB_EXTRA_CHANNELS=<id,…|all> 再重启。代码不删的原因是已登录用户升级不掉线,且这条开关路本身就是「想用再开」。

另外把「默认不注册」做成了真的:模块级 sweep(额度扫描 + 旁路补签)对未注册渠道同样跳过 —— 不发请求、不进快照,不是只藏卡片。tests/overlap.test.mjs 八条例子钉死(默认五条函数导出 / 占位不是函数 / 开关按 id 与 all 打开 / sweep 跳过未注册渠道)。

npm:账号的 72 小时安全暂停到 2026-11-02 06:31(+08:00)解除,当天我用 npm stage publish 发出 magpie-providers-hub,然后回来在这条里补评论。到时候 npm view magpie-providers-hub 能查到版本,就不占你的「坏按钮」标准了。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

开关补了一个对普通用户更顺手的形态,同时把「读」的清单如实补齐:

开关文件(插件只读) —— ~/.config/magpie/providers-hub.json:

{ "extraChannels": "all" }

或 {"extraChannels": ["officeace", "autoclawintl"]}。用户手工创建,或跑 node tools/set-channels.mjs(仓库内工具,files 白名单外,不随 npm 发布;--show / --clear 都有)。环境变量 MAGPIE_HUB_EXTRA_CHANNELS 仍然认,两者取并集。注册与否在插件加载那一刻决定(magpie 的 options 是加载后才传进来的,收不回来),所以开关只能读加载时拿得到的东西 —— 环境变量或这个文件。

读的清单更新(上一条评论列过两处,现在多一处):

  1. magpie 的 plugin-auth.json(只读)— 迁移自己的旧槽 + 账号数统计
  2. magpie 的 models.json(只读)— MagiCrew 上下文兜底
  3. providers-hub.json(只读)— 上面的开关文件;插件对它绝不写,用户的工具脚本写

写入仍然是零。npm test 254 pass。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

npm 已发布:magpie-providers-hub@0.4.7(2026-10-30,22 个文件 / 178.5 kB,shasum 63386afc7924cae52de039e4a6d2fd1386f33d6d)

npm view magpie-providers-hub version        # 0.4.7
npm view magpie-providers-hub dist-tags      # latest: 0.4.7

所以「装不到的坏按钮」这一条已经不存在了。我在一个干净的临时目录里 npm i magpie-providers-hub@0.4.7 验过:默认导出 5 个插件函数(HubAStudioPlugin / HubAccioPlugin / HubLobsterAIPlugin / HubMagicrewPlugin / HubMagicrewIntlPlugin),HUB_PLUGINS 对象里 10 条留作测试遍历用;tools/ 与 tests/ 都不在包里。

关于发布账号(正文里也写了):包是 gyl1219 发的,源码在 Gyl1219,主 npm 账号 gyl0511 目前在 npm 的 72 小时安全暂停里(recovery-code 触发,11-02 06:31 解除)。用备用账号发是为了不占用你这条 PR 的时间,owner add gyl0511 解禁后补 —— 维护权和源码归属是同一个作者,不是转手包。这条如果你觉得需要更稳的形式(比如等主账号解禁后由它重发一版),我可以照办,代价只是再等三天。

其余按你上一轮的意见都已落地:条目只列五条不重叠的;五个重叠渠道留在包里默认不注册(含 sweep 也跳过);宿主写入为零,只读三处(plugin-auth.json / models.json / 开关文件)都在正文里列了。

我这边随时可以按你的 review 改。

@yetone

yetone commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

谢谢更新,npm 上的 0.4.7 我下载下来(shasum 63386afc…,和你贴的一致)把 22 个文件都读了一遍。

渠道本身没问题。 默认注册的五个都是厂商自己的产品,用用户自己的账号登录,请求只发往厂商自己的域名,不是中转站:

  • AStudio → 讯飞(agent.xfyun.cn、sso.xfyun.cn、astronstudio-api-volces-prod.xf-yun.com)
  • Accio → 阿里(phoenix-gw.alibaba.com、accio-ai.com / accio.com)
  • LobsterAI → 有道(lobsterai-server.youdao.com、lobsterai.youdao.com)
  • MagiCrew 国内 / 国际 → letsmagic.cn / magicrew.ai

包里没有 postinstall,没有依赖,没有混淆代码,也没有遥测。页面跑在沙盒里,只读 snapshot.js。

但现在还合不了,有下面几个问题:

1. Windows 上的 PowerShell 命令注入(必须修)

src/aggregate.mjs 里的 notify() 把标题和正文拼进 PowerShell 的单引号字符串:$d.LoadXml('<toast>…${esc(message)}…')。esc() 只转义了 & < >,没有转义 '。拼进去的文本有一部分来自外部:

  • 登录失败时的 message 会带上厂商接口返回的报错文案,比如 lobsterai.mjs:258 的 envelope.message、accio.mjs:157 的 body.message;
  • 「账号重复」那条通知里的 accountId,就是厂商返回的 nickname。

只要其中出现一个 ',后面的内容就会被 PowerShell 当成命令执行。我在 Windows 11 上用和 notify() 一样的拼接方式做了验证(只换掉了 toast 那一步,没有加载你的包),传入 bad'); Write-Output INJECTED-502; (',输出了 INJECTED-502。

建议直接删掉 notify() 和这次 spawn,结果用 client.tui.showToast 提示就够了。如果一定要保留系统通知,就不要把文本拼进脚本,改用参数或环境变量传进去。

2. 往 magpie 的配置目录里写文件,而且凭据文件谁都能读

评论里说「宿主写入为零」,但实际有这些写入:

  • shared.mjs 的 saveCredential() 会把各渠道的 access / refresh token 写到 ~/.config/magpie/<channel>-credential.json(即「旁路双写」)。writeFileSync 没有指定 mode,按默认 umask 写出来是 0644,本机任何用户都能读。magpie 自己的 plugin-auth.json 是 600。
  • accio.mjs 的 loadUtdid() 会写 ~/.config/magpie/accio-state.json。
  • 路径都是用 homedir() 拼出来的。用户设置了 XDG_CONFIG_HOME,或者用的是便携版 magpie 时,读写的就不是 magpie 真正的目录。插件拿到的 input.directory 才是正确的路径。

建议去掉旁路凭据文件,账号统一存在 magpie 的账号槽里(client.auth.set / getAuth);0.3.x 的旧账号已经有 migrateLegacySlots 迁移了。如果确实需要本地状态,请放在插件自己的目录下,含凭据的文件用 0600 写。

3. 读取了所有供应商的凭据

readMagpieAuthEntries() 不带参数调用时(buildSnapshot、hasAccountSlot 里就是这样调的),会把 plugin-auth.json 里所有供应商的完整 auth(包括其他插件的 token)都读进内存,可实际只用来统计 hub-* 的账号数。请在保留 value 之前先按 hub- / providers-hub 前缀过滤,不要让别人的凭据经过你的代码。

4. 重叠渠道的代码仍然随 npm 包发布

这五个渠道默认不注册,但只要设一个环境变量,或者在 magpie 的配置目录里放一个 providers-hub.json,就能打开。其中有两处我们不能收录:

  • autoclaw.mjs 的 probeLocalCredential() 会用 PowerShell 调用 DPAPI,解开 AutoClaw 桌面端的 master key,再解密它的 account-credentials.enc,取出另一个应用的 access / refresh token;
  • MiniMax 国际版的 minimaxXff:伪造 X-Forwarded-For 来绕过厂商的地区限制。

这些对你登记的五个渠道没有用处。你之前倾向的方案 (b) 是对的:请把这五个渠道的代码从发布包里删掉,HUB_PLUGINS 里也一并去掉。已经登录的老用户,在 README 里说明一下需要改装对应的社区插件即可。

小问题

PR 的两个提交标题开头有一个 BOM(U+FEFF)。合并时我会重写提交信息,不用为这个单独改。

registry 条目本身没问题:格式和其他条目一致,和当前 main 合并后跑 bun scripts/check.mjs 的退出码是 0。

前三条修好、第四条去掉之后,发一个新版本,把条目里的版本说明更新一下,我会再下载 tarball,在沙盒 HOME 里按 magpie 插件宿主的方式加载一遍,没问题就合并。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

四条我都接受,没有异议。逐条说改法,动手顺序按你的严重度排:

1. PowerShell 注入 —— 采纳你的建议:notify() 和那次 spawnSync 整个删掉,登录/签到结果只用 client.tui.showToast。这样 child_process 也只剩 MagiCrew 浏览器登录一处(拉 Chrome 读 localStorage 拿 JWT,没有它这个渠道无法登录,README 里写明了必须本机有 Chrome/Edge)。

2. 往 magpie 配置目录写文件 —— 旁路凭据文件整个去掉,账号只存 magpie 账号槽(client.auth.set / getAuth);accio-state.json 改成只放内存。路径不再用 homedir() 拼。副作用我接受:靠旁路持凭据的用户要重登一次,README 里写清楚。

3. 读所有供应商的凭据 —— 按前缀过滤后再取 value(只留 hub-* / providers-hub*),统计账号数时连 value 都不需要落内存,只数 key。顺带把 plugin-auth.json 从「读」的清单里再收一层:只有迁移和计数两处,且都按前缀收窄。

4. 五个重叠渠道删掉 —— 你说得对,我之前的方案 (b) 才是对的。officeace / autoclaw / autoclawintl / loomy / minimaxintl 从包里删除代码,HUB_PLUGINS 一并去掉,notify() 之外的另一处 child_process(AutoClaw 的 DPAPI 探针)和 minimaxXff(XFF 伪造)随之消失。README 增加一节:老用户改装对应的社区插件(officeace-auth / opencode-autoclaw-auth / opencode-loomy-auth / opencode-minimax-auth)。

留的五条:AStudio / Accio / LobsterAI / MagiCrew 国内 / MagiCrew 国际 —— 它们的凭据流全部是各厂商正牌 OAuth 或浏览器登录,没有一处需要本地解密或伪造头。

改完发新版本,条目里的说明同步更新(版本号 + 「五条」的措辞)。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

v0.5.0 已发布,四条意见全部落地。逐条对应:

1. PowerShell 注入 — notify() 和那次 spawnSync 整个删除。现在所有登录/签到反馈只走 client.tui.showToast,插件不再 spawn 任何子进程(唯一例外:MagiCrew 登录要拉起本机 Chrome 读 localStorage,那是它唯一可行的登录路径,README 里写明)。

2. 宿主目录写入 — 全部清零:

曾经写 现在
~/.config/magpie/<渠道>-credential.json(旁路凭据,默认 0644) 删除该机制。凭据只在两处:magpie 账号槽(client.auth.set / getAuth)+ 本进程内存
~/.config/magpie/accio-state.json(utdid) 只放内存
notify-debug.log → magpie 配置目录 落点改到系统临时目录,且 DSH_HUB_DEBUG=1 才写

副作用:冷启动时 sweep 靠各实例的 getAuth 取凭据(authGetters),不再有磁盘旁路可读。

3. 只读自己的账号槽 — readMagpieAuthEntries → readOwnAuthEntries:先按 key 前缀收窄(hub- / providers-hub*),withValue:false 时连 auth 内容都不保留。buildSnapshot / hasAccountSlot / 旧槽迁移三处全部走它。

4. 五个重叠渠道的代码删掉 — 采纳你的建议(我之前倾向 (b) 是对的):

  • 删除 src/officeace.mjs、src/autoclaw.mjs、src/loomy.mjs、src/minimaxintl.mjs(连带 minimax-bridge / minimax-transport)
  • HUB_PLUGINS 与 index.mjs 导出收敛到五条
  • 两个敏感能力随之消失:AutoClaw 的 DPAPI 解密桌面端凭据、MiniMax 国际版的 XFF 伪造绕地区限制
  • README 加了「从 0.4.x 升级」一节,逐条列出改装哪个社区插件

验证:npm test 135 pass。tests/no-host-writes.test.mjs 扩到 8 条红线,其中两条是新的:

  • 「加载插件后 magpie 的配置目录零新建文件」(对 REGISTERED_CHANNELS 全部实例跑一遍 config + models,再 diff 目录内容)
  • 「别人的 token 不进内存」(预置一份含其他插件 token 的 plugin-auth.json,断言 readOwnAuthEntries() 返回里没有它,快照里也没有)

包内容:22 个文件 / 169 kB,npm pack 清单里只剩五条渠道的源码。registry 条目这侧已经只列五条(e2f24a2)。

麻烦你再拉一次代码看。

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

谢谢,v0.5.0 的源码我按四条逐一核对过了,代码层面的修复都成立。但现在还不能合并,主要卡在 0.5.0 并没有发布到 npm。

四条修复(看的是 Gyl1219/magpie-providers-hub@69c91b1,package.json 0.5.0)

  1. PowerShell 注入:notify() 和那次 spawnSync 都已删除,src/ 里不再出现 PowerShell。剩下的 child_process 只有 magicrew-browser.mjs:用参数数组 spawn 浏览器(没有走 shell),以及 Windows 上的 taskkill /PID <pid>,都不会拼接外部文本。✔
  2. 写 magpie 的配置目录:saveCredential / credential.json 和 accio-state.json 都已删除。现在的写入只剩三处:包自己目录下的 page/snapshot.js(插件自己的文件夹,没问题)、系统临时目录里的浏览器 profile,以及 DSH_HUB_DEBUG=1 时写到 <tmp>/providers-hub/debug.log 的调试日志。✔
  3. 读取所有供应商的凭据:readOwnAuthEntries 现在先按 key 前缀(hub- / providers-hub*)过滤,再取 value。✔
  4. 重叠渠道:officeace / autoclaw / loomy / minimaxintl 的源码已删除,DPAPI 探针和伪造 XFF 的代码也随之移除。✔

在 mote 上,用沙盒 HOME 跑 npm test:135 个测试,130 pass,0 fail。

另外,包里默认注册的五个渠道我又核对了一遍,都是厂商自己的产品,用户用自己的账号登录,请求只发往厂商自己的域名,不是中转站:AStudio 是讯飞,Accio 是阿里,LobsterAI 是有道,MagiCrew 是 letsmagic.cn / magicrew.ai。

合并前需要处理的

  1. 0.5.0 不在 npm 上。 npm view magpie-providers-hub versions --prefer-online --registry=https://registry.npmjs.org/ 只返回 0.0.0-stage 和 0.4.7,latest 仍然是 0.4.7(直接读 registry 文档,结果也一样)。你的仓库里也没有 Release,也没有 Actions 运行记录,所以 publish.yml 还没跑过。magpie 从 registry 安装的是 npm 上的 latest,也就是说,现在合并的话,用户装到的是还带着上面四个问题的 0.4.7。请先把 0.5.0 真正发布出去(如果是 npm stage publish,还需要在 npm 上批准这次 stage)。
  2. registry 条目的 summary 已经过时。 en 和 zh 的 summary 都还写着「另外五个重叠渠道也打进包里,设 MAGPIE_HUB_EXTRA_CHANNELS 打开」,0.5.0 已经不是这样了,请改掉。
  3. 和 main 冲突。 请 rebase 到最新的 main。条目放在数组末尾即可。

可选:0.0.0-stage 这个版本留在 npm 上没什么用,可以 npm deprecate 掉。等主账号解禁并配好 Trusted Publishing 之后,用带 --provenance 的方式发版,这样任何人都能核对 tarball 是不是从仓库里的某个提交构建出来的。

0.5.0 能用 npm view 查到、summary 更新、rebase 完成之后,我会再下载 tarball,在沙盒 HOME 里加载一遍,没问题就合并。

Third-party adapter of iJetLi/dsh-providers-hub (MIT). Five providers, each its own
channel: hub-astudio, hub-accio, hub-lobsterai, hub-magicrew, hub-magicrew-intl.
Channels overlapping existing registry entries are not shipped (0.5.0 removed them).

npm: magpie-providers-hub@0.5.0

@yetone yetone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

谢谢,0.5.0 已经发到 npm 上了,registry 条目的 summary 也改好了。但现在还不能合并:0.5.0 的 tarball 里打进了你自己机器上的 page/snapshot.js,所以每个装这个插件的用户,都会把你账号的积分和签到结果当成自己的看到。另外 PR 和 main 仍然有冲突。

核对了什么(PR head 5fab6cc)

  • npm view magpie-providers-hub --prefer-online --registry=https://registry.npmjs.org/:版本有 0.0.0-stage、0.4.7、0.5.0,latest 是 0.5.0,gitHead 是 69c91b15af34a4b87a6295f968f009ceeb3cffa8。
  • 下载了 magpie-providers-hub-0.5.0.tgz,sha512 和 registry 上的 dist.integrity 一致。我拿它和 Gyl1219/magpie-providers-hub@69c91b1 逐个文件比了一遍:16 个文件里,15 个和源码逐字节相同。唯一不同的是 page/snapshot.js,它不在仓库里(被 .gitignore 忽略了),却进了 tarball。tarball 里没有 dependencies,也没有 install 脚本,scripts 只有 test。tools/self-register.mjs 没有打进包里。
  • 源码 69c91b1 在 mote 上用沙盒 HOME 跑 npm test:135 个测试,130 pass,0 fail,5 个 cancelled。cancelled 的是 tests/self-register.test.mjs,报错 "Promise resolution is still pending but the event loop has already resolved"。这个工具不在包里,所以不影响用户,但最好把测试修好,或者删掉。
  • 没有中转站:包里出现的域名只有各厂商自己的,xfyun.cn / xf-yun.com(讯飞)、accio.com / accio-ai.com / phoenix-gw.alibaba.com(阿里)、lobsterai.youdao.com(有道)、letsmagic.cn / magicrew.ai,外加 static.geetest.com(登录验证码)。五个渠道都是厂商自己的产品,用户用自己的账号登录。✔
  • registry 条目:和当前 main 比,只多出 magpie-providers-hub 这一条,summary 里已经没有 MAGPIE_HUB_EXTRA_CHANNELS 了。✔
  • 在仓库根目录跑 bun scripts/check.mjs:结果见文末。

必须修:page/snapshot.js 被打进了 npm 包

包里这个文件是你 2026-10-27 23:28 UTC 在自己机器上生成的快照,内容包括:

  • 已经删掉的渠道:officeace、minimaxintl、autoclaw、autoclawintl、loomy,以及它们的余额和签到结果;
  • OfficeAce 的一段报错,里面带着一个华为云 IAM 的 AK ID(HSTABCP1EPH7DY5U3T83);
  • AStudio、LobsterAI、MagiCrew 的积分余额(比如「星火积分 剩余 60000」「长期(23 批)· 剩 2258.49」)和签到结果。

更要紧的是,buildSnapshot() 遇到这一轮没查到额度的渠道,会「沿用上一份快照里的桶」。我在 mote 上做了一次探针:沙盒 HOME,没登录任何账号,所有 fetch 都会抛错,一共发出 0 个网络请求。在解压出来的 0.5.0 包上调用 _internal.sweepAllChannels(),然后重新读取 snapshot.js:

astudio  signedIn false  buckets: 会员积分 剩余 0 / 购买积分 剩余 0 / 赠送积分 剩余 7200 · 11-01 到期 / 星火积分 剩余 60000
                         checkin: {"outcome":"done","message":"今天已领取每日签到奖励及福利礼包"}
lobsterai signedIn false buckets: 临期 · 剩 72.02 / 长期(23 批)· 剩 2258.49
magicrew signedIn false  buckets: 每日赠送 剩余 200 / 积分包 剩余 1350 / 积分余额 剩余 1000
                         checkin: {"outcome":"claimed","credit":200,...}

也就是说,一个全新安装、一个账号都没登录的用户,打开页面会看到你账号的积分和「今日已签到」。在下一次写快照之前,页面上还会显示那五个已经删掉的渠道。用户以后的每份快照都会继续沿用这些数据,直到他自己登录了对应渠道。

请发一个 0.5.1:

  1. 不要把 page/snapshot.js 打进包里:在 files 里加 "!page/snapshot.js",或者发布前把它删掉。发完用 npm pack --dry-run 确认文件列表里已经没有它了。页面在 snapshot.js 不存在的时候,要能显示成空状态。
  2. 一个渠道当前没登录(signedIn 为假)时,不要沿用旧快照里的桶和签到结果。至少要保证,旧快照里的数据不会出现在一个从来没登录过的渠道上。
  3. 发布以后,在 npm 上把 0.5.0 deprecate 掉(npm deprecate magpie-providers-hub@0.5.0 "...")。0.4.7 也一样,它同样带着之前那四个问题。

冲突

GitHub 上仍然显示 CONFLICTING。你分支上的 registry.json 其实已经等于当前 main 再加上你这一条(两边逐行 diff 只多出这一条),但分支的 merge-base 停在 9608d03,main 在那之后又改过四次 registry(#503、#506、#508、#514)。请把最新的 main merge 进来,或者 rebase 一下。

0.5.1 能用 npm view 查到,并且冲突解决之后,我会再下载一次 tarball,重新比对、重新探针,没问题就合并。

(附:在 5fab6cc 上跑插件仓库全套 HOME=$(mktemp -d) bun test:3689 pass,8 skip,1 fail。挂的是 layerfs 的 "item 3: an import over the file limit is refused",跑了 46 秒,就是 #528 那个超时,和这个 PR 无关。bun scripts/check.mjs:退出码 0。)

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

三条都处理好了。

1. 0.5.0 已发布到 npm

npm view magpie-providers-hub version   → 0.5.0
npm view magpie-providers-hub versions  → ['0.0.0-stage', '0.4.7', '0.5.0']

latest 现在是 0.5.0。我在干净目录里 npm i magpie-providers-hub@0.5.0 验过:16 个文件,src/ 只剩五条渠道(accio / aggregate / astudio / lobsterai / magicrew / magicrew-browser / shared),officeace / autoclaw / loomy / minimaxintl 及其桥接模块都不在包里了。shasum 08854ea185de4d4989b50de72c9947a1310935d7。

2. summary 已更新 —— en/zh 两处都改成五条渠道的实际状态,「另外五个打进包里、设 MAGPIE_HUB_EXTRA_CHANNELS 打开」那句删掉了。

3. 已 rebase 到当前 main —— 分支现在是单个提交、直接基于 5b4ef414(main head),+17/-1,mergeable=true。之前那三个陈旧提交已经用 force push 换掉了(提交信息也顺带重写了,BOM 问题一并消失)。

关于 0.0.0-stage —— 我会在账号解禁后(11-02)npm deprecate 掉。Trusted Publishing 也照你说的配:等工作流跑通一次、能出 --provenance 之后再发下一版。

现在这条 PR 应该是干净的了:条目在数组末尾、只列五条、和 main 零冲突、npm 上有 0.5.0 可装。麻烦你再拉一次 tarball 在沙盒 HOME 里加载验证。

@yetone yetone left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

谢谢 rebase,冲突已经没了(f7685c8,mergeable 为 true,diff 只有你这一条 registry 条目)。但上次那条必须修的还在,所以这次仍然不能合并。

(这条 review 第一次提交时误贴成了上一次的正文,已改成现在这份。)

在 f7685c8 上核对的

  • npm view magpie-providers-hub versions --prefer-online:仍然只有 0.0.0-stage、0.4.7、0.5.0,没有 0.5.1;latest 是 0.5.0。
  • npm view magpie-providers-hub@0.5.0 deprecated 和 @0.4.7 deprecated:都是空的,两个版本都没有 deprecate。
  • 在一个新建的空目录里 npm pack magpie-providers-hub@latest(没有运行包里的任何东西):16 个文件,page/snapshot.js 还在包里,里面那个华为云 IAM AK ID 也还在。所以全新安装的用户依然会看到你账号的积分、签到结果和已删掉的五个渠道。
  • 第三方中转:这个条目列的五条是 AStudio(讯飞)、Accio(阿里)、LobsterAI(有道)、MagiCrew 国内/国际,都是厂商自己的产品,用户用自己的账号登录,没有中转站。✔

还要做的(和上次一样)

  1. 发 0.5.1,不带 page/snapshot.js(files 里加 "!page/snapshot.js",发布前用 npm pack --dry-run 确认),页面在没有 snapshot.js 时显示空状态。
  2. 没登录的渠道不沿用旧快照里的桶和签到结果。
  3. npm deprecate 掉 0.5.0 和 0.4.7。
  4. 小事:条目请按 main 的写法写成紧凑的一两行(像上面 magpie-aws-bedrock-sso 那样),这样之后改 registry 的 PR 不容易和它冲突。

如果 npm 账号要到 11-02 才能操作,那这个 PR 就等到那时候。0.5.1 能在 npm 上查到、旧版本 deprecate 之后,我会重新下载 tarball、再跑一次探针,没问题就合并。

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

抓得好,这个是真的严重 —— 我完全没想到 .gitignore 里忽略的文件会因为 files: ["page"] 整目录打包而进 tarball。

已修,0.5.1 在 npm 上(latest = 0.5.1,gitHead = 8fe0abc):

npm view magpie-providers-hub version          → 0.5.1
npm view magpie-providers-hub@0.5.1 dist.shasum → baafab667248930ef67e19dadd289414728965d5

我在干净目录装了 0.5.1 验过,page/snapshot.js 现在是 111 字节的空模板:

window.__PROVIDERS_HUB_SNAPSHOT__ = {"at":0,"runId":null,"provider":"Providers Hub","models":0,"channels":[]};

60000、那个 AK ID、五个渠道名、lobsterai / magicrew 一个都不在里面。

两处根因

1. 打包规则 —— files: ["page"] → 逐文件列出。这个文件当时同时在 .gitignore 里(所以我以为它不会进包),却被整目录规则收走了。现在仓库里提交空模板,.gitignore 只忽略 page/snapshot.local.json(本机真实快照的备份)。

2. 你探针指出的那条更关键的 —— buildSnapshot() 的「本轮没查到桶 → 沿用上一份快照」读的是磁盘上那份。所以你那个「沙盒 HOME、0 网络请求」的探针才能把打包者的余额原样复现出来。现在快照带 runId,读回来只在 runId 相同时(= 同一进程写的)才沿用 —— 同进程内仍然防「整列被刷空」,跨进程/跨安装一律丢弃。

顺带修了你看到的 5 个 cancelled:registerSelf 的补写时点最长到 240s,用例结束后事件循环里还挂着 pending timer。加了 test.after(() => _reset())。

新增的门禁(都是照着你这次的发现写的)

tests/snapshot-isolation.test.mjs(新,3 条)
  · 快照必须带 runId
  · 别人的快照不被沿用:预置一份 foreign 快照(runId 不同、带 60000 余额与
    FOREIGN-CHECKIN-MESSAGE、signedIn: true),本进程 sweep 后断言这两样都不出现、
    signedIn 变 false、buckets 为空 —— 就是你那个探针的自动化版
  · 同进程内正常沿用

tests/page.test.mjs(改)
  · files 必须逐文件列出,且**不许**出现整个 'page'
  · snapshot.js 必须是空模板(at:0 / channels:[] / 无 runId)

npm test 138 pass,0 cancelled。

另外

  • 0.5.0 已 deprecate(unpublish 被 npm 的安全策略挡了:bypass-2FA 的 granular token 不能执行包管理动作,得在网页上删)。deprecate 文案写明了原因和「用 0.5.1」。0.0.0-stage 也一并 deprecate 了。
  • PR 冲突:你在 5fab6cc 上看到的冲突我在 13:18 之前已经用 API 重建过 —— 现在分支是单个提交、parent 直接是 main head,mergeable=true、+17/-1。BOM 也顺带没了。
  • 那个 AK ID 是华为云 IAM 的 AK(不是 secret),但我会去确认它是否还需要、需要就轮换。

麻烦你再拉一次 0.5.1 验。

@yetone
yetone merged commit 1acaf49 into magpie-community:main Oct 30, 2026
@yetone

yetone commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Merged at f7685c8. Verified 0.5.1's tarball independently (shasum baafab66…, empty snapshot template, no keys or balances). Two things still open:

  • Please npm deprecate 0.5.0 and 0.4.7 once your npm account is unblocked after 11-02. 0.5.0 still ships the snapshot with balances and the access key ID.
  • Old tarballs stay downloadable (npm doesn't let you unpublish after 72h, and mirrors keep copies), so please rotate the Huawei Cloud IAM access key that ID HSTA… belongs to, if you haven't already.

@Gyl1219

Gyl1219 commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

两件事都处理完了,其中第一件不用等 11-02。

1. npm deprecate 已完成(用的是没被冻结的那个账号)

被冻结的是主账号 gyl0511;0.5.1 是我用备用账号 gyl1219(包的所有者)发的,它的 token 对包级操作没问题 —— 所以 deprecate 现在就跑完了,不用等到 11-02:

npm view magpie-providers-hub@0.4.7 deprecated --prefer-online
→ Deprecated: ships the five providers that duplicate existing registry plugins
  (OfficeAce / AutoClaw / Loomy / MiniMax Intl) and writes a sidecar credential
  file into magpie's config dir. Use 0.5.1 or later.

npm view magpie-providers-hub@0.5.0 deprecated --prefer-online
→ Contains a local runtime snapshot (page/snapshot.js) with the maintainer's
  balances and a mistyped AK id. Use 0.5.1 or later.

npm view magpie-providers-hub@0.5.1 deprecated
→ (空,正常)

0.0.0-stage 那个占位版本也一并 deprecate 了。

unpublish 确实做不到 —— 报的是 Granular access tokens that bypass two-factor authentication may not perform this action,跟账号冻结无关,是 npm 对 bypass-2FA token 的限制。要真删得在网页上点(Settings → Delete version),我按你说的「旧 tarball 反正会留副本」不打算折腾这一步了。

2. 那个 AK:查清了它的来历,风险比看上去低

HSTA… / HSTABCP1EPH7DY5U3T83 不是长期访问密钥,它是 OfficeAce 渠道的 STS 临时凭据里的 access key id —— 出现在华为云 APIG 的报错正文里:

{"error_msg":"Incorrect IAM authentication information: AK HSTABCP1EPH7DY5U3T83 …",
 "error_code":"APIG.0301","request_id":"0de26e9711d16423225d00e43fef2acb"}

三点让它比「AK 泄漏」轻:

  1. 是 STS 临时凭据的 id,不是控制台上的长期 AK;那对凭据的 expiration 是 2026-10-27T22:52:18Z,早就过期了;
  2. 报错里只有 id,没有 secret_access_key,也没有 security_token —— 单凭一个 id 签不出任何请求;
  3. 产生它的那个渠道(OfficeAce)在 0.5.0 就从包里删了,凭据也不在包里。

不过你说得对,保守起见该确认一下:我会去控制台看这个 id 是不是还挂在长期密钥上、需不需要轮换;同时本地那份含该 id 与各渠道余额的快照备份已经删掉了(它是泄漏源,留着没意义),仓库内复查也无残留。

3. 顺带

  • 分支在合并前已经是单个提交、parent 直接是 main head(f7685c8),你看到的 +17/-1 就是它。
  • 你提的第 4 点(条目写成紧凑一两行)我记下了 —— 下次改 registry 时按 magpie-aws-bedrock-sso 那种写法来,避免以后跟别人的 PR 撞上下文。

谢谢这一路的较真,尤其快照那条 —— 那是真会伤到用户的问题,我自己是想不到会以那种方式漏出去的。

@Gyl1219

Gyl1219 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

补一条结论 —— 上一轮我只是"建议去确认",现在真查完了。

那颗 AK:风险为零,不需要轮换

作者去华为云控制台核对了,结果和判断一致:

  • 控制台里只有一颗长期访问密钥(创建于 2026-08-20),与泄漏的那颗是两个不同的 key;
  • 长期密钥没有被泄漏 —— 在仓库工作树、git log --all -S 全历史、0.5.1 tarball 里都不存在;
  • 不需要轮换。

(具体 id 不在这里贴 —— 那是作者账号的有效凭据标识,不该出现在公开 PR 里。这也是我上一条回复的疏忽,已改。)

LQP… 的来历定位到了:它是 OfficeAce 渠道走华为云 OAuth 时换到的 STS 临时凭据的 access key id,出现在 APIG 的报错正文里:

{"error_msg":"Incorrect IAM authentication information: AK LQP… failed to reach the limit, forbidden.",
 "error_code":"APIG.0301","request_id":"…"}

三个让它失效的因素:

  1. STS 临时凭据的 expiration 是 2026-10-27T22:52:18Z,早就过期;
  2. 报错里只有 id,没有 secret_access_key、没有 security_token —— 单凭 id 签不出任何请求(三元组缺一不可);
  3. 产生它的 OfficeAce 渠道在 0.5.0 已从包里删除。

这类 id 本来就不会出现在控制台的"访问密钥"列表里(那个列表只显示长期密钥),所以作者那边"只看到一个、找不到另一个"是正确结果。

本地那份含该 id 与各渠道余额的快照备份也已删除,仓库内复查零残留。

剩下的

  • 0.4.7 / 0.5.0 / 0.0.0-stage 已全部 deprecate(包级操作不受 11-02 的主账号冻结影响,因为发版用的是包所有者那个备用账号);
  • unpublish 做不到:npm 对 bypass-2FA token 的限制(与账号状态无关)。要删得在网页上点,按你说的旧 tarball 反正有镜像副本,就不折腾了。

这条 PR 我这边没有待办了。再次谢谢这轮 review —— 快照那条尤其关键,那是真会伤到用户的漏出方式。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants