@eddyskywalker/dsh-chatgpt-subscription 0.18.0 → 0.19.0

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +103 -1
  3. package/lib/client.js +1543 -269
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +9092 -2555
  6. package/lib/types/client/glm-coding-plan/GlmCodingPlanComposerQuota.d.ts +27 -0
  7. package/lib/types/client/glm-coding-plan/GlmCodingPlanComposerQuota.d.ts.map +1 -0
  8. package/lib/types/client/glm-coding-plan/GlmCodingPlanSection.d.ts +22 -0
  9. package/lib/types/client/glm-coding-plan/GlmCodingPlanSection.d.ts.map +1 -0
  10. package/lib/types/client/glm-coding-plan/api.d.ts +4 -0
  11. package/lib/types/client/glm-coding-plan/api.d.ts.map +1 -0
  12. package/lib/types/client/glm-coding-plan/locales.d.ts +412 -0
  13. package/lib/types/client/glm-coding-plan/locales.d.ts.map +1 -0
  14. package/lib/types/client/glm-coding-plan/styles.d.ts +15 -0
  15. package/lib/types/client/glm-coding-plan/styles.d.ts.map +1 -0
  16. package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts +65 -0
  17. package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts.map +1 -0
  18. package/lib/types/client/hub/brand-icons.d.ts +7 -5
  19. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  20. package/lib/types/client/hub/providers.d.ts.map +1 -1
  21. package/lib/types/client/index.d.ts +2 -0
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/kimi-code/locales.d.ts +2 -2
  24. package/lib/types/client/minimax-code/locales.d.ts +2 -2
  25. package/lib/types/host/antigravity/oauth.d.ts +11 -1
  26. package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
  27. package/lib/types/host/common/model-request-control.d.ts +1 -1
  28. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  29. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  30. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  31. package/lib/types/host/glm-coding-plan/account-pool.d.ts +152 -0
  32. package/lib/types/host/glm-coding-plan/account-pool.d.ts.map +1 -0
  33. package/lib/types/host/glm-coding-plan/adapter.d.ts +169 -0
  34. package/lib/types/host/glm-coding-plan/adapter.d.ts.map +1 -0
  35. package/lib/types/host/glm-coding-plan/adopt.d.ts +354 -0
  36. package/lib/types/host/glm-coding-plan/adopt.d.ts.map +1 -0
  37. package/lib/types/host/glm-coding-plan/biz-token-cache.d.ts +99 -0
  38. package/lib/types/host/glm-coding-plan/biz-token-cache.d.ts.map +1 -0
  39. package/lib/types/host/glm-coding-plan/client.d.ts +218 -0
  40. package/lib/types/host/glm-coding-plan/client.d.ts.map +1 -0
  41. package/lib/types/host/glm-coding-plan/exchange.d.ts +223 -0
  42. package/lib/types/host/glm-coding-plan/exchange.d.ts.map +1 -0
  43. package/lib/types/host/glm-coding-plan/mapper-anthropic.d.ts +308 -0
  44. package/lib/types/host/glm-coding-plan/mapper-anthropic.d.ts.map +1 -0
  45. package/lib/types/host/glm-coding-plan/mapper-openai.d.ts +213 -0
  46. package/lib/types/host/glm-coding-plan/mapper-openai.d.ts.map +1 -0
  47. package/lib/types/host/glm-coding-plan/model-catalog.d.ts +232 -0
  48. package/lib/types/host/glm-coding-plan/model-catalog.d.ts.map +1 -0
  49. package/lib/types/host/glm-coding-plan/oauth.d.ts +211 -0
  50. package/lib/types/host/glm-coding-plan/oauth.d.ts.map +1 -0
  51. package/lib/types/host/glm-coding-plan/routes.d.ts +93 -0
  52. package/lib/types/host/glm-coding-plan/routes.d.ts.map +1 -0
  53. package/lib/types/host/glm-coding-plan/signing.d.ts +323 -0
  54. package/lib/types/host/glm-coding-plan/signing.d.ts.map +1 -0
  55. package/lib/types/host/glm-coding-plan/token-store.d.ts +202 -0
  56. package/lib/types/host/glm-coding-plan/token-store.d.ts.map +1 -0
  57. package/lib/types/host/glm-coding-plan/types.d.ts +413 -0
  58. package/lib/types/host/glm-coding-plan/types.d.ts.map +1 -0
  59. package/lib/types/host/kimi-code/routes.d.ts +10 -0
  60. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  61. package/lib/types/index.d.ts +9 -0
  62. package/lib/types/index.d.ts.map +1 -1
  63. package/lib/types/shared/glm-coding-plan-contracts.d.ts +315 -0
  64. package/lib/types/shared/glm-coding-plan-contracts.d.ts.map +1 -0
  65. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.19.0 - 2026-10-11
6
+ - **[GLM(ZCode 账号)] 新增第九条 Provider 线路:用 ZCode 账号 OAuth 登录 GLM Coding Plan,国区与国际区两套部署各自独立**
7
+ - **问题:GLM Coding Plan 此前只能以「粘贴 API Key」的形态接入,而公开的 OpenAI 兼容面根本不生效套餐额度。** 官方文档的 Common mistakes 段写明走通用面 `/api/paas/v4` 会变成按量计费——用户明明买了订阅,却在一条声称提供 GLM 的线路上看着余额被扣。要拿到真正吃套餐额度的端点,必须先有 Coding Plan Key,而 Key 又只能由 ZCode **账号**授权换出来。
8
+ - **修法:新增 `glm-coding-plan` Provider,把「账号授权」接到 DSH 上。** 浏览器里由用户用自己的 ZCode 账号完成授权,插件只做两件事:按 ZCode 自己的 CLI OAuth 控制面开一次流(`POST https://zcode.z.ai/api/v1/oauth/cli/init`),再按上游给的节奏轮询(`GET .../oauth/cli/poll/{flow_id}`),两个请求共用同一个本地 `randomBytes(32)` 生成的 Bearer pollToken——它只是把一次开流与它的轮询绑在一起,**不是登录凭据**、不落盘。拿到 OAuth access_token 之后跑兑换链,换出 `{apiKeyId}.{secretKey}` 形式的 Coding Plan Key,最后只打**套餐专用**端点。凭据落盘前先用配额接口验一次活:账号没有套餐就在**登录阶段**报错,而不是等到某次对话里以一个莫名其妙的 429 暴露出来。**本线路不做 API Key 粘贴**——账号授权产出的就是同一种凭据,下游完全共用。
9
+ - **两区不是同一个部署,连鉴权形状都不一样,这是实现里唯一容易写错的地方。** 国区:业务面 `https://bigmodel.cn`、模型面 `https://open.bigmodel.cn`,并且**跳过 `/api/auth/z/login`**——直接拿 OAuth access_token 当**裸 `Authorization`**(不加 `Bearer`)打 `/api/biz/*`。国际区:业务面与模型面同为 `https://api.z.ai`,先 `POST /api/auth/z/login` 换业务 JWT,之后 `/api/biz/*` 带 `Bearer <业务 JWT>`。推论是**业务 JWT 的自动重取(提前 120 秒 + 0~60 秒抖动防尖峰)只服务国际区**,国区连这一层都没有。同一个号池可以同时持有两区账号且互不串用。本仓库历史里那句「国区没有浏览器登录」是错的,本线路删除了它——国区**有**浏览器登录,只是入口是 `bigmodel.cn/login?appId=zcode`、参数名与国际区不同。
10
+ - **落地方式:复活历史 `zhipu` 线路的骨架,但只取 hunk,没有整条 cherry-pick。** `7e40fce` 提供了 23 文件 / +6284 行的完整骨架(adapter / client / mapper / model-catalog / routes / token-store / account-pool / contracts + 卡片 + 测试),省掉约六千行重写。但那套代码随后被 `7eb3ee2`「Revert GLM」整个撤掉、`fb07c2e` 又用 Claude 订阅线路顶替了它的位置,所以今天的 master 上既没有那个目录,也没有它依赖的 `ZhipuSection`。`9ad8a53`(12 文件 / +1599 行)里的 `oauth.ts` 是一份**自包含**的 716 行实现,值得按 hunk 取;但它同批次的 `routes.ts` / `contracts.ts` / `types.ts` / `index.ts` / `ZhipuSection.tsx` / `locales.ts` / `styles.ts` / `test/zhipu-ui.test.tsx` **全部假定骨架仍在原处**——旧目录名 `zhipu`、旧契约形状、旧的文件级样式约定,以及旧注册点的代码上下文。整条 cherry-pick 会把已退役的装配方式一并复活,并直接覆盖 `src/index.ts` 这个八条线路共用的热点文件。**结论:自包含的实现按 hunk 取,依赖旧树形状的一律按今天的架构重写**(这也是新线路改名 `glm-coding-plan`、目录落到 `src/host/glm-coding-plan/` 的原因)。
11
+ - **三处历史事实被官方 `zcode-builtin.json`(revision 31)纠正,纠正前的值都属于「看起来合理、但会算错账」。**
12
+ - **输出上限**:此前是「历史转录的 docs.z.ai 通用参数表 131072」与「未验证的兜底 32768」。现按官方规则逐模型取值——GLM-5.3 家族与 GLM-5.2 是 **128000**、GLM-5-Turbo 是 **64000**,而 128000 正是**官方客户端自己在 Coding Plan 面上发的那个数**。两个来源不一致时按本仓库「取小值」的规则取 128000:高报会被网关拒(1214),低报只是提前压缩一段对话。GLM-5-Turbo 与 GLM-5.3 同代却差一倍,本身就是「输出上限不得按家族名推」的一条硬证据。
13
+ - **`supportsImages` 全部为 `true`**:官方基础 `modelRules` 对 GLM-5.3 / GLM-5.2 / GLM-5-Turbo 写的都是 false,是两条 `modelMatch: ".*"` 的**站点规则**把本线路这两个 Coding Plan 基址(`api.z.ai/api/anthropic` 与 `open.bigmodel.cn/api/anthropic`)的 `supportsImage` 打开成 true。因此这条能力读作「**经本线路这两个端点时**收不收图」,而不是「模型本身多模态」——照抄基础规则会让四个模型全部退化成纯文本。
14
+ - **套餐归属此前写反了**:曾断言「`GLM-5.2` / `GLM-5-Turbo` 是 Start Plan 独有,Coding Plan 请求它们会拿到 1311」。官方规则集里四个 `account:*-coding-plan` provider 启用的是**同样四个** id,而 `account:*-start-plan` 缺的才是 **`GLM-5.3`**。写反的代价是把两个确实在套餐里的模型判成未订阅,用户连试都不能试。留着这份对照是为了让复审记住一条结论:**套餐归属是账号的属性,不是模型的属性**——同一个 id 在不同 provider 规则下启用的集合是不同的,而「从模型 id 反推账号套餐」这条路是走不通的。
15
+ - **两个协议面必须共用同一张错误判读表,第一版并没有,于是 `NO_PERMISSION` 被判成了鉴权失败。** Anthropic 面(`mapper-anthropic.ts` 的 `classifyGlmUpstreamError`)先查正文里的业务码,只在没有业务码时让状态行兜底;OpenAI 面(`mapper-openai.ts` 的 `classifyGlmOpenAiFailure`)最初**先看状态行**,而 `NO_PERMISSION`(1220) 的文档状态**就是 403**。后果不是分类难看,而是**行为错了**:号池的鉴权出口用的就是 `AUTH`,于是这个账号被送去重新登录,而重新登录根本解决不了「套餐没开这个 API」——用户看到的是「明明套餐在,却一直要重新登录」,且每次重登都必然再失败一次。现在两面按同一条规则走:**业务码说了算,状态行只在业务码不认识时兜底**(非鉴权业务码集合由冻结的错误码表直接推导,不另抄一份)。
16
+ - **裸 429 的兜底也必须两面一致,而且默认方向不能是「重试」。** 冻结的错误码表里 429 共七条,只有两条可重试(1302 频率限制、1305 瞬时过载),其余五条全是额度类(1113 无余额 / 1308 具名窗口用尽 / 1309 套餐失效 / 1310 周月额度用尽 / 1311 模型不在套餐)。所以正文没给业务码的裸 429 **判不可重试**;`Retry-After` 仍然读取并下发(界面上「N 秒后再试」比「未知错误」有用得多),但它不改变判定。赌它是限流然后进入自动退避重试,赌错的后果是在一个已经用尽的窗口上反复失败、还多烧一次额度。号池侧的冷却另走一条:判据是**状态码 429** 而不是错误码,否则「窗口已用尽」永远走不到冷却那一步,而那恰恰是最需要冷却的场景。
17
+ - **测试**:本线路 11 个测试文件共 **413 例**——契约 49 / OAuth 63 / Anthropic 映射 50 / OpenAI 映射 53 / adopt 41 / client(目录与配额)47 / UI 34 / adapter 21 / 签名 21 / 路由 30 / 浏览器注入口 4。另有回归:`test/client-style-ownership.test.ts` 那份手工维护的 `SHEETS` 列表补上本线路的样式表——该表的用途逐字写着「本插件注入的每一张 sheet 都带归属标记」,而这张表由 `src/client/index.tsx` 无条件安装,漏掉它就等于不再覆盖那张表;测试不依赖真实凭据或网络。
18
+ - **验证:`npm run typecheck` 0 错误;全量 `npm test` 208 passed | 1 skipped(209 文件)、3428 passed | 7 skipped | 0 failed。**
19
+ - **诚实声明:本线路没有实机账号,全部为 mock 验收。** 开发与验收环境不具备真实的 ZCode 账号,因此**没有任何一条上游行为经过 live 验证**。仍未验证的项已在 README「已知限制」里逐条写明:① 上游**没有 refresh_token 交换接口**,`access_token` 过期只能重新登录(业务 JWT 可自动重取,且只服务国际区);② 客户端签名层(Client Signing V4)**默认关闭**——唯一有实测记录的 A/B 对照显示带签名与不带签名的响应完全一致(都是 429/1113),但「服务端会因缺签名而拒绝」这件事**从未被观察到**,所以这是「默认关」而不是「已验证不需要签名」;③ **国区兑换链未验证**(证据是三个社区实现的逆向实现,等级 B,三者都以国际区为主),国区配额该打 `bigmodel.cn` 还是官方文档里那句「亦可用 `open.bigmodel.cn`」同样没有实测;④ `poll_interval_sec` 的实际下发值、桌面回调中转里的握手标识是否轮换、周度 / 月度窗口长度(按词义取 7 天 / 30 天)。为此兑换链的每一个端点都做了 `DSH_GLM_*` 环境变量覆盖——上游一次变更让整条链失效时,回滚是改一个环境变量,而不是发一个版本。
20
+ - **登录成功后卡片仍说「尚未开始登录」,而且「取消登录」出现在没有流程的时候——两处都不是显示瑕疵,是状态机在谎报。**
21
+ - **终态被清空**:`GET /login/status` 原先在流程不再 pending 时执行 `latestLogin = null`,于是「登录成功」这个事实在**下一次读**就被丢掉,卡片拿到 `idle`,用户刚完成授权却看到「尚未开始登录」。`POST /login/manual`(手工粘贴)同样在成功后立刻清掉,现象一模一样。现在终态**保留到下一次开流或用户取消**——不给 TTL、也不做「读到一次就清」:卡片在流程结束前后会读好几次(2 秒一次的 `/login/status` 与它带出来的 `/status` 交错完成),「读一次」实际是「谁先到谁拿走」,必然漏;而 TTL 只是把同一个竞态换成另一个竞态。用用户动作当生命周期边界,没有时钟、没有后台任务,也不破坏「只有这一条 GET 推进状态机」的既有约定(新增断言:终态之后再读不会再打上游)。顺带修了同一条路由的应答形状——`/login/manual` 原先回的是登录**流程**状态,而路由表与客户端都按整份 `GlmStatusDto` 用,模型区与账号区会随即空掉。
22
+ - **取消按钮的位置与可见性**:「取消登录」原先被放在「手工粘贴授权码」那一行里,和「提交授权码」并排。它撤销的是**这一次浏览器授权流程**,属于流程控制,放在粘贴行里既位置错、也让人以为它只影响粘贴。现在移入登录流程区,且**只在 pending 时渲染**——没有进行中的流程时根本没有可取消的东西。
23
+ - **手工粘贴在登录完成后仍占大块版面**:它必须保留(浏览器回调被拦时这是唯一出路,这条理由成立),但改成**折叠**而不是条件渲染:pending / error 自动展开(这两个阶段它真的能生效),idle / complete 自动收起,用户随时可点开。条件渲染在 idle 时连入口都没有,而折叠保留了「一次点击就能拿到」这个出路。
24
+ - **「尚无配额数据」是一条真 bug,不在拉取环节**:自动拉取本来就有(无快照时 `GET /status` 会 await 一次)。问题是 `quotaError` 在状态装配处被**写死成 `null`**,而配额读取的失败是以返回值呈现、不抛异常的,于是那句面向用户的原因被整个丢掉,用户分不清「上游还没给这把 Key 开配额」「这把 Key 读不了配额(免费额度账号恰恰如此)」和「根本没试过」。现在两类失败都如实下发。
25
+ - **「没有 Coding Plan 就不能登录」是错的,会把新用户挡在门外。**
26
+ - 原实现把「验活探不到付费套餐」当作**硬拒**。但官方文档逐字写明:新用户连接 BigModel 账号后**自动获得体验套餐**,首次使用起 5 天内每天 800 万 token;闲时任务则是订阅用户在算力富余时段**免费完成、不消耗套餐额度**。也就是说**「没有付费套餐」不等于「没有额度」**,而硬拒挡掉的恰恰是最需要免费额度的新用户。
27
+ - 现在把「验活失败」拆成**两个业务码族**:`BAD_CREDENTIAL_REASONS`(1000/1001/1003,鉴权族)仍然失败——Key 是坏的;套餐族(1113/1220/1309/1310/1311)**放行**并标为免费体验额度;两者都判不出来时落在 `unknown`,**同样放行**。理由是代价不对称:额度耗尽时上游会回 429,那是**可恢复**的;误拒一个有效账号**不可恢复**——用户再也登不进这条线路,且无从自查。卡片如实显示当前是付费套餐还是免费体验额度,**不把免费额度伪装成付费套餐**。
28
+ - 代价必须写明:上游若对无套餐账号也回裸 401/403 而不给业务码,那么「凭据无效」与「没有付费套餐」在响应层面**不可区分**,此时 `unknown` 放行意味着**无效凭据可能进池**,其表现是请求期 401 而非登录期报错。这一层没有实机样本,属未验证。
29
+ - **测试会真的弹出系统浏览器,而且生产路径会开两个授权页——后者是仓库里早就修过的毛病又犯了一次。**
30
+ - **测试泄漏**:`routes.ts` 调 `beginWebLogin` 时**根本没有 `openBrowser` 透传口**,于是缺省走平台实现 `spawn('cmd'|'open'|'xdg-open')`,跑测试时真的拉起浏览器。修法是先给装配层加一栏纯增量的可选项作为注入口,再在测试装配里统一注入空函数(一次盖住全部 12 处用例),并加 `vi.mock('node:child_process')` 的护栏断言 `spawn` 调用数为 0。护栏**先写后修**,所以泄漏是被真实复现过的(`expected [ 'cmd' ] to deeply equal []`),另有一条正向对照用例证明这条护栏不是空断言。
31
+ - **生产双开**:卡片自己会 `window.open` 授权页,而 host 侧又会 `spawn` 一次,于是**每次登录弹出两个一模一样的页面**。这正是 `test/login-opens-browser-once.test.ts` 当初为 Claude 与 Command Code 修掉的同一个问题,GLM 又犯了一次。现在照兄弟线路的形状改成「**host 恒定不开、卡片开**」,注入口降级为可选覆盖,并把 GLM 纳入那条既有护栏。
32
+ - **顺带收口两处同款隐患**(纯增量、生产行为零变化):`kimi-code` 的 `/login` 分支同样没有透传 `openBrowser`,形状与 GLM 缺陷完全一样;`antigravity` 的 `loginAndSave` 则是**硬编码**开浏览器,连注入口都没有。两处都补上了注入口,并用**反向对照**测试钉住「不注入时照样 spawn」,证明生产行为未变。
33
+ - **本轮明确不做**:全仓级 `test/setup` 兜底(对三个浏览器命令做「记而不执行 + 断言 0 次」)——它会打死三处**刻意**断言 spawn 被调用过的正向对照用例,需要先设计逃生口;且它一次影响全部 209 个测试文件,评估结论已记录,留待单独决定。
34
+ - **本轮明确不做**:Agent 行为层映射(ZCode 的 `/mode plan|build|edit|yolo|guarded`、工具契约、`~/.zcode/AGENTS.md` 生态复用)——本轮只做线路层;也不改动 command-code / workbuddy 里已有的 GLM 模型行(那是它们各自上游的模型,与本线路无关),不绕过套餐限制,也不把公开 API 能力自动等同于订阅网关能力。
35
+
5
36
  ## 0.18.0 - 2026-10-11
6
37
 
7
38
  - **[MiniMax Code] 额度耗尽后不换号,而且把「限流」判成了「欠费」**
package/README.md CHANGED
@@ -15,6 +15,7 @@
15
15
  - [Command Code 线路](#command-code-线路)
16
16
  - [WorkBuddy 线路](#workbuddy-线路)
17
17
  - [Claude(订阅)线路](#claude订阅线路)
18
+ - [GLM(ZCode 账号)线路](#glmzcode-账号线路)
18
19
  - [子代理模型授权](#子代理模型授权0215-起032-起强制指定模型)
19
20
  - [随包分发的 Agent Preset](#随包分发的-agent-preset)
20
21
  - [本机登录](#本机登录)
@@ -25,7 +26,7 @@
25
26
 
26
27
  ## 线路总览
27
28
 
28
- 本插件同时接入 **7 条订阅线路**,每条各自独立注册 Provider、独立登录、独立额度卡片。
29
+ 本插件同时接入 **8 条订阅线路**,每条各自独立注册 Provider、独立登录、独立额度卡片。
29
30
 
30
31
  | Provider ID | 订阅 | 协议 | 登录方式 | 模型目录来源 |
31
32
  | --- | --- | --- | --- | --- |
@@ -36,6 +37,7 @@
36
37
  | `claude-subscription` | Claude Pro / Max | Anthropic Messages | 手动粘贴 / loopback 回调 | 实时 `/v1/models` + 本地兜底 |
37
38
  | `minimax-code` | MiniMax Code 编程订阅 | Anthropic Messages | **复用桌面端登录态** 或设备码 | **硬编码**(见下) |
38
39
  | `antigravity` | Google Antigravity | Gemini | Google OAuth | 内置表 |
40
+ | `glm-coding-plan` | GLM Coding Plan(ZCode 账号,国区 BigModel / 国际区 Z.ai) | Anthropic Messages + OpenAI Chat Completions(按模型声明) | ZCode 账号 OAuth(浏览器中转轮询)+ 可选导入本机 ZCode 登录态 | 随包目录(源自官方 `zcode-builtin.json`) |
39
41
 
40
42
  > **只有 minimax-code 是硬编码目录,而且是有意的**:该端点的 `GET /v1/models` **未对订阅流量开放**(返回 503 `direct_route_not_configured`),任何「实时目录」都只会是一个必然失败的请求。其取值转录自本机官方客户端的 `config.yaml`。
41
43
  >
@@ -760,6 +762,106 @@ access token / refresh token / 授权码 / PKCE verifier**;诊断里提到某
760
762
  **SHA-256 指纹前缀**(`sha256:.../len:...`),**不是**令牌的任何一段原文——早前版本会打印前 6 个字符,
761
763
  那本身就是一次凭据泄露,因为同一个字符串会被渲染到设置卡片并写进 Host 日志(有测试锁定)。
762
764
 
765
+ ## GLM(ZCode 账号)线路
766
+
767
+ `glm-coding-plan`(显示名 **GLM(ZCode 账号)**)接入 **GLM Coding Plan** 订阅:你在 ZCode 页面用自己的**账号**完成授权,插件用 ZCode 自己的 CLI OAuth 控制面换出 Coding Plan Key,再打**套餐专用**端点——不粘贴 API Key,也不需要你手工铸造 Key。国区(BigModel)与国际区(Z.ai)是两套互不连通的部署:各自独立登录、独立凭据、独立号池行,同一个号池里可以同时存在两区账号且互不串用。
768
+
769
+ > ⚠️ **本线路没有经过实机账号验证。** 开发与验收环境不具备真实的 ZCode 账号,全部验收都是 mock 验收。下面「已知限制」逐条写明哪些上游行为**仍未验证**、以及拿到账号后必须先复验什么——那不是免责套话,是复审清单。
770
+
771
+ **登录**:
772
+
773
+ 1. 确认自己属于国区还是国际区。**付费套餐与免费体验额度都可以登录**:官方文档写明新用户连接 BigModel 账号后自动获得「体验套餐:无需付费,在首次使用起的 5 天内,每天提供 800 万 token 的 GLM 系列模型体验额度」,所以本线路**不再要求**必须先订阅 Coding Plan;
774
+ 2. 重启 DSH,打开 **设置 → 订阅服务 → GLM(ZCode 账号)**;
775
+ 3. 选好区域后点「登录」:插件开一次 OAuth 流并自动打开授权页;浏览器打不开不是致命错误,授权链接始终显示在卡片上,可自行复制打开。卡片每 2 秒轮询一次进度;
776
+ 4. 授权完成后插件自动跑兑换链换出 Coding Plan Key、验一次活、加密落盘并加入号池。验活只回答「这把 Key 会不会被上游认出来」,**不要求账号有付费套餐**:认得出来就落盘,卡片上如实标出当前是「付费套餐」还是「免费体验额度」。浏览器没走通时可以「手工粘贴」回调链接或授权码——粘贴只是「去问一次上游」的信号,**凭据仍然只来自上游轮询**;
777
+ 5. 也可以点「导入本机 ZCode 登录态」省掉一次登录(见下)。
778
+
779
+ **导入本机 ZCode 登录态(可选,只读)**:只读本机已登录的 ZCode 客户端凭据文件 `~/.zcode/v2/credentials.json`(`ZCODE_DATA_BASE_DIR` 可覆盖),**从不创建、修改或删除** ZCode 的任何文件。该文件的加密密钥可由「平台 + 用户名 + 主目录」推导,因此普通用户的导入不依赖额外密钥;**设了 `ZCODE_CREDENTIAL_SECRET` 的用户会导入失败**,此时回落浏览器登录、不阻断。导入到的凭据与浏览器登录走**同一条兑换链**,下游完全共用。
780
+
781
+ **上游接口**:
782
+
783
+ | 用途 | 路径 | 鉴权 |
784
+ | --- | --- | --- |
785
+ | OAuth 开流 | `POST https://zcode.z.ai/api/v1/oauth/cli/init` | `Authorization: Bearer <pollToken>`;pollToken 是本地 `randomBytes(32)` 生成的绑定令牌,**不是登录凭据**、不落盘 |
786
+ | OAuth 轮询 | `GET https://zcode.z.ai/api/v1/oauth/cli/poll/{flow_id}` | 同一个 Bearer pollToken |
787
+ | 业务 JWT(**仅国际区**) | `POST {业务 host}/api/auth/z/login` | 请求体带 OAuth access_token;响应给出业务 JWT 与 `expires_in` |
788
+ | 兑换:默认机构与项目 | `GET {业务 host}/api/biz/customer/getCustomerInfo` | 国际区 `Authorization: Bearer <业务 JWT>`;国区是**裸 access_token**(见「两区差异」) |
789
+ | 兑换:Key 列表 / 创建 | `GET` / `POST {业务 host}/api/biz/v1/organization/{orgId}/projects/{projectId}/api_keys` | 同上 |
790
+ | 兑换:取 secretKey | `GET {业务 host}/api/biz/v1/organization/{orgId}/projects/{projectId}/api_keys/copy/{apiKeyId}` | 同上 |
791
+ | 模型生成(Anthropic 面) | `POST {api host}/api/anthropic/v1/messages` | `x-api-key` 与 `Authorization: Bearer` **同值双头**(该兼容网关两个都读) |
792
+ | 模型生成(OpenAI 兼容面) | `POST {api host}/api/coding/paas/v4/chat/completions` | `Authorization: Bearer <Coding Plan Key>` |
793
+ | 配额 | `GET {业务 host}/api/monitor/usage/quota/limit` | **`authorization: <id>.<secret>` 裸 Key,不可加 `Bearer`**(加了会鉴权失败,与模型面正好相反) |
794
+
795
+ 模型面刻意**不是**通用开放平台的 `/api/paas/v4`:官方文档的 Common mistakes 段写明走那个面套餐额度不生效——用户会看到「买了套餐却在扣余额」。所以 `glm-5.1` / `glm-4.7` 那批只挂在通用面上的模型**不在本线路目录里**;上游目录真出现、而本插件未转录过的 id 会作为「未声明模型」进入目录(不猜协议、不发思考档位、不认图),而不是凭空补一行能力。
796
+
797
+ 轮换出来的 Key 落盘前会用配额接口验一次活,而这一步的两类失败**处置相反**:**凭据本身无效**(鉴权类业务码 1000 / 1001 / 1003)仍然在**登录阶段**报错——放行等于拿着一把废 Key 进对话,用户只会在某次请求里以一个莫名其妙的 401 看到它;**凭据有效但没有付费套餐**(配额面按余额 / 套餐 / 额度维度拒绝)则**放行**,并把额度类型如实记为「免费体验额度」。额度耗尽时由上游返回 429 告知,本线路不预判、也不绕过。
798
+
799
+ **两区差异**:
800
+
801
+ | | 国区 `cn` | 国际区 `intl` |
802
+ | --- | --- | --- |
803
+ | 业务 host(兑换链、配额) | `https://bigmodel.cn` | `https://api.z.ai` |
804
+ | 模型面 host | `https://open.bigmodel.cn` | `https://api.z.ai`(国际区把控制台与模型面放在同一个源站上) |
805
+ | 浏览器授权入口 | `https://bigmodel.cn/login?appId=zcode` | `https://chat.z.ai/api/oauth/authorize?client_id=client_P8X5CMWmlaRO9gyO-KSqtg` |
806
+ | 开流请求里的 `provider` | `bigmodel` | `zai` |
807
+ | 业务 JWT | **跳过 `/api/auth/z/login`**,拿 OAuth access_token 当**裸 `Authorization`** 直接打 `/api/biz/*` | 先换业务 JWT,之后 `/api/biz/*` 带 `Bearer <业务 JWT>` |
808
+
809
+ 两区共用同一个 OAuth 控制面(`zcode.z.ai/api/v1/oauth/cli/*`),分开的只有上面这几行。**业务 JWT 的自动重取只服务国际区**——国区没有这一层。授权参数里**没有 PKCE**(两个 provider 的授权参数只有 redirect / appId / state),本插件照抄上游形状,不自带 `code_challenge`。本仓库历史里那句「国区没有浏览器登录」是错的,本线路删除了它:国区**有**浏览器登录,只是入口与参数名都不同。
810
+
811
+ **模型目录**:随包目录 4 个模型(`GLM-5.3` / `GLM-5.3-Flash` / `GLM-5.2` / `GLM-5-Turbo`),逐模型事实转录自官方开源版内置配置 `zai-org/ZCode` 的 `config/provider/zcode-builtin.json`(revision 31)。能力**逐模型查表,不从模型名推断**,其中三处是被官方配置纠正过的历史值:
812
+
813
+ - **输出上限**:GLM-5.3 家族与 GLM-5.2 是 **128000**、GLM-5-Turbo 是 **64000**。历史转录引用的 docs.z.ai 通用参数表给的是 131072,按本仓库「取小值」的规则取 128000(高报会被网关拒,低报只是提前压缩);128000 也正是**官方客户端自己在 Coding Plan 面上发的那个数**。同一代模型上限差一倍,正是「不得按家族名推」的又一处证据;
814
+ - **图片输入**:四个模型的 `supportsImages` 全为 `true`——**不是因为模型多模态**,而是官方配置里两条 `modelMatch: ".*"` 的站点规则把 `api.z.ai/api/anthropic` 与 `open.bigmodel.cn/api/anthropic` 这两个 Coding Plan 基址的 `supportsImage` 打开成 true(基础规则对 `GLM-5.3` / `GLM-5.2` / `GLM-5-Turbo` 写的都是 false)。所以这条能力读作「**经本线路这两个端点时**收不收图」;
815
+ - **套餐归属**:这四个**全部**是 Coding Plan 模型;反倒是 **Start Plan 缺 `GLM-5.3`**。这一条曾被写反过,写反的代价是把两个确实在套餐里的模型判成未订阅、用户连试都不能试。留着它是为了让复审记住:**套餐归属是账号的属性,不是模型的属性**,同一个 id 在不同 provider 规则下启用的集合是不同的。
816
+
817
+ 思考档位同样逐模型声明:`GLM-5.3` / `GLM-5.3-Flash` 三档且**不可关闭思考**(上游把 `thinking.type: "disabled"` 当错误拒绝),`GLM-5.2` 两档(模型页面写的是更宽的词汇,但它同时写明套餐如何映射,真正能上线缆的只有这两级)且可关闭,`GLM-5-Turbo` **不收档位**(梯子为空,请求里根本不发思考控制字段)。DSH 词汇表更宽的 `minimal` / `medium` / `xhigh` 先收敛到模型真实的梯子再发出去,平手时向上取。
818
+
819
+ **瞬时失败按错误类别重试**:`glm-coding-plan` 路由显式声明 `normal` 策略(最多 3 次,1.5s 起指数退避、15s 上限、0.2 抖动),覆盖 `RATE_LIMIT` / `SERVER` / `TIMEOUT` / `TRANSPORT`。分类**不能停在状态行**——冻结的错误码表里七条 429 中**只有两条可重试**(1302 频率限制、1305 瞬时过载),其余五条全是额度类,所以**正文没给业务码的裸 429 一律判不可重试**(`Retry-After` 仍然读取并下发,它不改变判定)。同一张表在两个协议面上是**同一份取值**,Anthropic 面与 OpenAI 面各有一份判读函数,改一处必须同时改另一处。
820
+
821
+ ### 插件路由(GLM Coding Plan)
822
+ 所有路由都以 `/glm-coding-plan/api` 为前缀:
823
+
824
+ | 方法 | 路径 | 说明 |
825
+ | --- | --- | --- |
826
+ | GET | `/status` | 账号、区域、配额快照、模型目录、登录流程与路由归属(`serving` / `conflict`);页面唯一的读口 |
827
+ | POST | `/settings` | 启用开关、勾选模型、上下文窗口覆盖、默认思考深度、选中账号;**先落盘再发** `llm/adapters-updated` |
828
+ | POST | `/accounts` | 号池动作:`set-primary` / `clear-cooldown` / `delete` / `relogin` / `strategy`(`sequential` / `round-robin` / `sticky`) |
829
+ | POST | `/login/start` | 按区域开一次 OAuth 流,立即返回流程状态(不阻塞) |
830
+ | GET | `/login/status` | 轮询登录流程状态;**只有这一条 GET 会推进状态机**——host 刻意不在后台轮询,关掉设置页之后就不会继续打上游 |
831
+ | POST | `/login/cancel` | 取消登录流程 |
832
+ | POST | `/login/manual` | 手工粘贴回调链接或授权码 |
833
+ | POST | `/adopt` | 导入本机 ZCode 登录态(只读,作快照入池);失败**永不阻断**,每种失败都是契约里的一个 reason,卡片据此回落浏览器登录 |
834
+ | POST | `/connection/test` | 沿用兑换链验活那条路径(配额接口裸 Key):它答「这把 Key 还能不能读配额」,**答不了**「这个模型能不能被调用」——后者要花一次真实生成,交给用户发第一条消息 |
835
+ | POST | `/quota` | 强制刷新配额;一次失败的配额读不是一次失败的请求,卡片照旧渲染 |
836
+ | POST | `/catalog/refresh` | 强制刷新模型目录 |
837
+
838
+ 与其它线路一样,所有修改状态的路由只接受同源 JSON POST,并校验 `Origin` 与 `Host`。`/adopt` 与 `/connection/test` 的 DTO 定在**顶层**(不带 `{ok,value}` 信封),其余一律走信封。**响应里永远不含 access token、refresh token、业务 JWT 或 Coding Plan Key 的原文**——登录状态 DTO 是逐字段显式投影出来的,而不是展开整个会话对象;将来给会话对象加字段会因此变成一次必须被看见的改动。
839
+
840
+ **已知限制(拿到账号后请先复验这几条)**:
841
+
842
+ - **本线路的全部验收都是 mock,没有任何实机账号验证。** 开发与验收环境不具备真实的 ZCode 账号:下面每一条上游行为都是照官方源码、官方文档与社区逆向实现实现的,并由 mock 走通流程与分支,**没有一条经过 live 验证**。因此本节不是「已知问题」清单,而是**拿到账号后的复审清单**——先复验它们,再谈这条线路在你的账号上可用。同批仍未验证的细项还有:服务端实际下发的 `poll_interval_sec`(只见最小 1 秒的校验,无实测样本)、桌面回调中转路径里的握手标识是否会轮换、以及周度 / 月度配额窗口的长度(按词义取 7 天 / 30 天,上游只给了「周度」「月度」这个名称,缺一份真实配额响应样本里的窗口起止时刻;5 小时那一档有逐字证据,不受影响)。
843
+ - **支持免费体验额度与付费套餐,但免费额度的具体行为未经验证。** 本线路现在
844
+ 「有额度就放行」:验活只判断这把 Coding Plan Key 会不会被上游认出来,不再要求账号
845
+ 已订阅付费 Coding Plan。官方文档(逐字:zcode.z.ai/cn/docs/configuration)写明新用户
846
+ 连接 BigModel 账号后自动获得「体验套餐:无需付费,在首次使用起的 5 天内,每天提供
847
+ 800 万 token 的 GLM 系列模型体验额度」,welcome 页也写明「新用户可享受每日免费的 GLM
848
+ 旗舰模型体验额度」——所以**「没有付费套餐」不等于「没有额度」**,登录阶段硬拒会把免费额度的
849
+ 新用户全部挡在门外。卡片会如实区分「付费套餐 / 免费体验额度 / 未判定」,不把免费额度
850
+ 渲染成套餐名。
851
+ **仍未验证(拿到账号后请先复验)**:
852
+ - 免费额度账号在配额接口上究竟返回什么形状——是 403 + 业务码(当前按「已知套餐类业务码」
853
+ 与「裸 401/403」两支分别处理),还是能读出一份带窗口的快照。**没有一份实机样本**;
854
+ - 「凭据无效」与「没有付费套餐」在上游是否**一定**可区分。当前只认三个鉴权类业务码
855
+ (1000 / 1001 / 1003)为凭据无效;分不出时按 `unknown` **放行**——因为额度耗尽的 429
856
+ 可恢复,而误拒一个有效账号不可恢复(用户再也登不进这条线路,且无从自查);
857
+ - 免费额度是否真的能服务模型面(配额接口答得了不代表模型接口答得了),以及它耗尽时的
858
+ 确切错误码与重置节奏;
859
+ - 「闲时任务」类免费算力(welcome 页所述)本线路**未接入**,它属于另一条调度路径。
860
+ - **上游没有 refresh_token 交换接口。** 官方客户端对刷新直接抛「OAuth 暂未提供 refresh token 交换接口,请重新登录」。因此 `access_token` 过期**只能重新登录**;插件能自动重取的只有业务 JWT 这一层(提前 120 秒 + 0~60 秒抖动防尖峰),而且**它只服务国际区**。账号行上的「重新登录」在 host 这一侧只做两件事:撤掉鉴权失败标记与冷却,让这一行重新参与轮转;真正的登录要由卡片再开一次流程。
861
+ - **客户端签名层(Client Signing V4)默认关闭。** 唯一有实测记录的项目做过 A/B 对照:**带签名与不带签名的响应完全一致**(都是 429/1113),从未观察到服务端因为缺签名而拒绝请求。没有证据表明缺签名会被拒时,默认开启一个逆向来的签名算法只会平白增加故障面,因此它默认关、需要显式打开。反过来,**服务端是否真会因为缺签名返回签名错误,至今从未被观察到**——这条不是「已验证不需要签名」,而是「从未验证过」。
862
+ - **国区兑换链未验证。** 证据来自三个社区实现(等级 B,逆向实测而非官方契约),三者**都以国际区为主**,国区业务面的形状是否同构没有实测样本。国区兑换失败会带上明确说明与下一步建议(改用国际区,或用 `DSH_GLM_CN_BUSINESS_HOST` 等环境变量指向实测过的部署),**不静默失败**。同理,国区配额到底打 `bigmodel.cn` 还是官方文档里那句「亦可用 `open.bigmodel.cn`」也没有实测,本插件取 `bigmodel.cn`。
863
+ - **兑换链的所有端点都可用 `DSH_GLM_*` 环境变量覆盖**(两区的业务 host、模型面 host、授权入口、appId / client_id、Key 名、OAuth 控制面基址,签名层开关也在其中)。这是为等级 B 的兑换链准备的退路:上游一次变更让整条链失效时,回滚是改一个环境变量,而不是发一个版本。
864
+
763
865
  ## 本机登录
764
866
 
765
867
  **每条线路只在自己的详情页里回答关于本机的问题**。ChatGPT(Codex)线路的 ChatGPT 卡片里,「登录」按钮**旁边**就是「导入本机 Codex CLI 登录」——与 Claude 页面上那个收编按钮同样的位置、同样的次要样式;点它可以省掉再走一次 ChatGPT 授权。Claude Code 与 MiniMax Code 各自的设置页里本来就各有一个导入入口,不在这里重复。