@eddyskywalker/dsh-chatgpt-subscription 0.31.0 → 0.33.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 (131) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +112 -1
  3. package/lib/client.js +34731 -3832
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +12922 -2851
  6. package/lib/types/client/cline-pass/ClinePassComposerQuota.d.ts +20 -0
  7. package/lib/types/client/cline-pass/ClinePassComposerQuota.d.ts.map +1 -0
  8. package/lib/types/client/cline-pass/ClinePassSection.d.ts +17 -0
  9. package/lib/types/client/cline-pass/ClinePassSection.d.ts.map +1 -0
  10. package/lib/types/client/cline-pass/api.d.ts +84 -0
  11. package/lib/types/client/cline-pass/api.d.ts.map +1 -0
  12. package/lib/types/client/cline-pass/locales.d.ts +1025 -0
  13. package/lib/types/client/cline-pass/locales.d.ts.map +1 -0
  14. package/lib/types/client/cline-pass/styles.d.ts +7 -0
  15. package/lib/types/client/cline-pass/styles.d.ts.map +1 -0
  16. package/lib/types/client/hub/brand-icons.d.ts +18 -2
  17. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  18. package/lib/types/client/hub/hub-styles.d.ts.map +1 -1
  19. package/lib/types/client/hub/providers.d.ts.map +1 -1
  20. package/lib/types/client/index.d.ts +4 -0
  21. package/lib/types/client/index.d.ts.map +1 -1
  22. package/lib/types/client/mermaid/renderer.d.ts +23 -0
  23. package/lib/types/client/mermaid/renderer.d.ts.map +1 -1
  24. package/lib/types/client/minimax-code/MinimaxVideoDocumentBody.d.ts +31 -14
  25. package/lib/types/client/minimax-code/MinimaxVideoDocumentBody.d.ts.map +1 -1
  26. package/lib/types/client/minimax-code/MinimaxVideoToolView.d.ts.map +1 -1
  27. package/lib/types/client/minimax-code/VideoFormDialog.d.ts +46 -50
  28. package/lib/types/client/minimax-code/VideoFormDialog.d.ts.map +1 -1
  29. package/lib/types/client/minimax-code/locales.d.ts +80 -20
  30. package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
  31. package/lib/types/client/minimax-code/styles.d.ts.map +1 -1
  32. package/lib/types/client/minimax-code/video-command-host.d.ts +17 -11
  33. package/lib/types/client/minimax-code/video-command-host.d.ts.map +1 -1
  34. package/lib/types/client/minimax-code/video-command-icon.d.ts +38 -0
  35. package/lib/types/client/minimax-code/video-command-icon.d.ts.map +1 -0
  36. package/lib/types/client/minimax-code/video-command.d.ts +53 -59
  37. package/lib/types/client/minimax-code/video-command.d.ts.map +1 -1
  38. package/lib/types/client/minimax-code/video-contract.d.ts +36 -0
  39. package/lib/types/client/minimax-code/video-contract.d.ts.map +1 -1
  40. package/lib/types/client/minimax-code/video-media-dropzone.d.ts +57 -0
  41. package/lib/types/client/minimax-code/video-media-dropzone.d.ts.map +1 -0
  42. package/lib/types/client/minimax-code/video-media-input.d.ts +173 -0
  43. package/lib/types/client/minimax-code/video-media-input.d.ts.map +1 -0
  44. package/lib/types/client/minimax-code/video-presets.d.ts +67 -0
  45. package/lib/types/client/minimax-code/video-presets.d.ts.map +1 -0
  46. package/lib/types/client/minimax-code/video-slots.d.ts +58 -5
  47. package/lib/types/client/minimax-code/video-slots.d.ts.map +1 -1
  48. package/lib/types/client/minimax-code/video-workspace.d.ts +41 -0
  49. package/lib/types/client/minimax-code/video-workspace.d.ts.map +1 -0
  50. package/lib/types/client/progress-updates/markdown-lite.d.ts +19 -6
  51. package/lib/types/client/progress-updates/markdown-lite.d.ts.map +1 -1
  52. package/lib/types/client/qoder/QoderComposerQuota.d.ts +41 -0
  53. package/lib/types/client/qoder/QoderComposerQuota.d.ts.map +1 -0
  54. package/lib/types/client/qoder/QoderSection.d.ts +26 -0
  55. package/lib/types/client/qoder/QoderSection.d.ts.map +1 -0
  56. package/lib/types/client/qoder/api.d.ts +4 -0
  57. package/lib/types/client/qoder/api.d.ts.map +1 -0
  58. package/lib/types/client/qoder/locales.d.ts +310 -0
  59. package/lib/types/client/qoder/locales.d.ts.map +1 -0
  60. package/lib/types/client/qoder/styles.d.ts +16 -0
  61. package/lib/types/client/qoder/styles.d.ts.map +1 -0
  62. package/lib/types/client/qoder/useQoderSection.d.ts +97 -0
  63. package/lib/types/client/qoder/useQoderSection.d.ts.map +1 -0
  64. package/lib/types/host/antigravity/adapter.d.ts.map +1 -1
  65. package/lib/types/host/cline-pass/account-pool.d.ts +63 -0
  66. package/lib/types/host/cline-pass/account-pool.d.ts.map +1 -0
  67. package/lib/types/host/cline-pass/adapter.d.ts +49 -0
  68. package/lib/types/host/cline-pass/adapter.d.ts.map +1 -0
  69. package/lib/types/host/cline-pass/catalog.d.ts +49 -0
  70. package/lib/types/host/cline-pass/catalog.d.ts.map +1 -0
  71. package/lib/types/host/cline-pass/client.d.ts +118 -0
  72. package/lib/types/host/cline-pass/client.d.ts.map +1 -0
  73. package/lib/types/host/cline-pass/engine.d.ts +223 -0
  74. package/lib/types/host/cline-pass/engine.d.ts.map +1 -0
  75. package/lib/types/host/cline-pass/line.d.ts +55 -0
  76. package/lib/types/host/cline-pass/line.d.ts.map +1 -0
  77. package/lib/types/host/cline-pass/mapper.d.ts +112 -0
  78. package/lib/types/host/cline-pass/mapper.d.ts.map +1 -0
  79. package/lib/types/host/cline-pass/oauth.d.ts +134 -0
  80. package/lib/types/host/cline-pass/oauth.d.ts.map +1 -0
  81. package/lib/types/host/cline-pass/protocol.d.ts +186 -0
  82. package/lib/types/host/cline-pass/protocol.d.ts.map +1 -0
  83. package/lib/types/host/cline-pass/routes.d.ts +71 -0
  84. package/lib/types/host/cline-pass/routes.d.ts.map +1 -0
  85. package/lib/types/host/cline-pass/store.d.ts +97 -0
  86. package/lib/types/host/cline-pass/store.d.ts.map +1 -0
  87. package/lib/types/host/cline-pass/token-store.d.ts +168 -0
  88. package/lib/types/host/cline-pass/token-store.d.ts.map +1 -0
  89. package/lib/types/host/cline-pass/tools.d.ts +21 -0
  90. package/lib/types/host/cline-pass/tools.d.ts.map +1 -0
  91. package/lib/types/host/cline-pass/types.d.ts +168 -0
  92. package/lib/types/host/cline-pass/types.d.ts.map +1 -0
  93. package/lib/types/host/common/model-request-control.d.ts +1 -1
  94. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  95. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  96. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  97. package/lib/types/host/kimi-code/adapter.d.ts +3 -1
  98. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  99. package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
  100. package/lib/types/host/qoder/account-pool.d.ts +137 -0
  101. package/lib/types/host/qoder/account-pool.d.ts.map +1 -0
  102. package/lib/types/host/qoder/adapter.d.ts +134 -0
  103. package/lib/types/host/qoder/adapter.d.ts.map +1 -0
  104. package/lib/types/host/qoder/adopt.d.ts +146 -0
  105. package/lib/types/host/qoder/adopt.d.ts.map +1 -0
  106. package/lib/types/host/qoder/client.d.ts +189 -0
  107. package/lib/types/host/qoder/client.d.ts.map +1 -0
  108. package/lib/types/host/qoder/cosy.d.ts +173 -0
  109. package/lib/types/host/qoder/cosy.d.ts.map +1 -0
  110. package/lib/types/host/qoder/mapper.d.ts +235 -0
  111. package/lib/types/host/qoder/mapper.d.ts.map +1 -0
  112. package/lib/types/host/qoder/model-catalog.d.ts +185 -0
  113. package/lib/types/host/qoder/model-catalog.d.ts.map +1 -0
  114. package/lib/types/host/qoder/oauth.d.ts +134 -0
  115. package/lib/types/host/qoder/oauth.d.ts.map +1 -0
  116. package/lib/types/host/qoder/routes.d.ts +96 -0
  117. package/lib/types/host/qoder/routes.d.ts.map +1 -0
  118. package/lib/types/host/qoder/token-store.d.ts +231 -0
  119. package/lib/types/host/qoder/token-store.d.ts.map +1 -0
  120. package/lib/types/host/qoder/types.d.ts +257 -0
  121. package/lib/types/host/qoder/types.d.ts.map +1 -0
  122. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
  123. package/lib/types/host/workbuddy/mapper.d.ts +1 -0
  124. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
  125. package/lib/types/index.d.ts +17 -0
  126. package/lib/types/index.d.ts.map +1 -1
  127. package/lib/types/shared/cline-pass-contracts.d.ts +267 -0
  128. package/lib/types/shared/cline-pass-contracts.d.ts.map +1 -0
  129. package/lib/types/shared/qoder-contracts.d.ts +293 -0
  130. package/lib/types/shared/qoder-contracts.d.ts.map +1 -0
  131. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,76 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.33.0 - 2026-10-20
6
+ - **[qoder] 看板上的 Qoder 卡片用的是字母 Q,不是官方标志——现在换成官方那张 412×412 应用图标**
7
+ - **现象**:设置页订阅列表里其余线路都是自家标志,只有 Qoder 是一个灰底字母方块。
8
+ - **真因**:[brand-icons.tsx](../src/client/hub/brand-icons.tsx) 里那条注释给出的理由是「官方 favicon 是一枚 73 KB 的复合字形,体量不适合内联」。那个 73 KB 确实存在——但 `https://qoder.com/favIcon.svg` 现在返回的是**整站 HTML**(73,379 字节,HTML 长度不是图标大小),页面真正挂的图标是 `<link rel="icon" type="image/png">` 指向的一张 alicdn PNG。于是「太大所以不做」这个判断建立在一次看错的抓取上。
9
+ - **改法**:`ProviderBrand` 新增 `image` 形态(data URI + `fit`),`BrandTile` 在它有值时画 `<img>` 并铺满底砖;Qoder 用官方 412×412 PNG(4,990 字节,sha256 `2276f0f8…26f3d`,2026-10-21 抓取 HTTP 200),base64 后 6.6 KB。旧注释连同 monogram 一起删掉,换成带 URL / 抓取时间 / 字节数 / 校验和的出处。
10
+ - **验证**:`npm run typecheck` 0 错误;`test/hub-overview-ui.test.tsx` 新增一例断言 Qoder 卡渲染 data URI 且不再出现 `dsh-hub-brand-mono`;全量 `npx vitest run` **280 文件 / 4572 passed / 7 skipped**。
11
+ - **[workbuddy] 会话一长就整轮失败、红条只有一句「必须以 system 提示词开头」——真因是上游 WAF 在按竞品客户端身份做指纹拦截**
12
+ - **现象**:截图那次是国际区账号第 2 轮起每轮必挂,报错前缀写着 `rejected the message history: the conversation must open with a system prompt`,与正文 `{"code":11128,"msg":"Illegal API invocation from an unapproved channel"}` 自相矛盾(历史首条本来就是 system)。同一账号发一句 `ping` 却可能正常,像是「聊着聊着就坏」。
13
+ - **真因**:`11128` 被上游复用给了至少两种拒绝,而 `unapproved channel` 这一种是**内容侧的 WAF**——网关对 `system`、`assistant.content` 以及 `assistant.tool_calls[].function.arguments` 里出现的第三方 Agent 身份声明做**精确短语匹配**,命中即整单拒绝,与鉴权、额度、模型、区域都无关;又因为每轮都全量重放历史,一条 assistant 消息或一次工具参数中毒,此后每轮必挂。本机对失败会话的 1,552 行系统提示词做行级二分,定位出**唯一触发行**是首行 `You are an AI agent powered by DeepSeek Harness.`(国内区在 `system`/`assistant` 中原样出现即 400,把 `AI agent` 改成 `AI assistant` 后同一份提示词 200);`Claude Code`、`Codex CLI`、`OhMyOpenCode`、`x-anthropic-billing-header` 则两区都会触发。
14
+ - **为什么还牵扯到别的区域**:号池里两个 `intl` 账号被上游瞬时 `429 (code 14003)` 打回后按既有逻辑冷却并**在同一次请求内轮换到 `cn` 账号**,而 `cn` 网关恰好拦首行那句话——于是「国际区账号」的会话撞上的是国内区的规则。
15
+ - **改法([mapper.ts](../src/host/workbuddy/mapper.ts))**:`sanitizeText()` 增加出站指纹改写(覆盖 `options.system`、`role: "system"` 消息、`assistant` 文本、以及 `tool_calls[].function.arguments` 的序列化参数),每条只改**实测确认会触发的那一句**、不动其余指令语义;同时把「剥 `\0`」拆成 `stripNulls()`,**入站 SSE 仍只做 `\0` 清理**——模型自己吐出的字不该被改写。
16
+ - **改法([adapter.ts](../src/host/workbuddy/adapter.ts))**:`code === 11128` 的判定从只认 `conversation id` 扩到 `conversation id|unapproved channel`,并把原始 detail 一并带出;此前除 `conversation id` 外的所有 11128 都被翻译成「必须以 system 提示词开头」,把读者指向一段本来正确无误的历史。
17
+ - **验证**:两区对拍——同一份含 5 类指纹的请求,脱敏前国内区与国际区都 `400 / 11128`,脱敏后都 `200`;新增 `test/workbuddy-message-shape.test.ts` 的跨 `system`/`assistant`/`tool_calls` 用例与 `test/workbuddy-adapter.test.ts` 的两种 11128 文案用例;`npm run typecheck` 0 错误,全量 `npx vitest run` **280 文件 / 4571 passed / 7 skipped**,`npm run build` 通过;用完整 1,552 行系统提示词带上下文对号池 4 个账号(2 `intl` + 2 `cn`)端到端实测,**四个账号全部 200**。
18
+ - **[qoder] 新增第十二条订阅线路:Qoder(全球区 qoder.com / 中国站 qoder.com.cn)**
19
+ - **协议面**:Qoder 自有的 COSY 签名对话协议(`POST {base}/algo/api/v2/service/pro/sse/agent_chat_generation`)——AES-128-CBC 加密的 UserInfo + RSA-1024 包裹的会话密钥 + canonical MD5 签名、自定义 Base64 alphabet + 三段交换的 body codec、外层信封包 inner JSON 的明文 SSE。全部算法按 Liki4/qodercli2api 的全合成 frozen fixtures(qodercli v1.1.34 互操作基线)**逐字节锁定**(`test/fixtures/qoder/` vendored,`test/qoder-cosy.test.ts` 8 例),并与 simonsmh/pi-provider-qoder 的 live 验证实现互相印证。
20
+ - **登录三路径**:浏览器设备流(仅全球区,challenge/verifier 轮询,不开本地回调端口,host 不自行开浏览器——`test/login-opens-browser-once.test.ts` 钉住)、手动粘贴 PAT(`jobToken/exchange` 兑换,PAT 本体留在 host 加密存储里供过期重兑换)、adopt 只读导入本机 qodercli 登录态(`~/.qoder/.auth/user`,AES 解密由 fixture 锁定;**区域不猜,是对两区 userinfo 各探一次探出来的**)。
21
+ - **账号模型**:多账号号池,凭据**会过期、可刷新**(提前 1 小时,逐字上游文档;PAT 来源重兑换、其余走 refresh_token 轮换),两区账号可同池共存(区域是凭据的属性)。
22
+ - **模型与额度**:目录实时发现(COSY 签名的 `/algo/api/v2/model/list`)+ 随包能力兜底表;COSY 版本每日跟随官方 CLI release feed(落后会拿到缩减目录);能力一律来自目录字段(`max_input_tokens` 那个陈旧地板不信)。额度卡片读 `/api/v2/quota/usage`(**非公开个人端点**,失败绝不影响模型请求),三只桶(个人/加购/组织包)分开画绝对量;订阅无公开单价,整页只有 token 与 Credits 数字,没有金额。
23
+ - **验证**:`npm run typecheck` 0 错误;新增 9 个 qoder 套件;全量测试与 build 通过。**未用真实账号联调**(mock + frozen fixtures 验收),联调清单见 `docs/qoder-line-plan.md` §9。
24
+ - **[kimi-code] K3 跑到一半突然整轮失败,红条上只有一个 `terminated` 配 `UNKNOWN`——现在它是可重试的 `TRANSPORT`**
25
+ - **现象**:`k3` 的长回答(截图那次已经执行 27 次思考、调用 30 次工具)偶发中断,界面是「本轮运行失败 terminated」,右侧徽标 `UNKNOWN`;手动重发一次立刻恢复。
26
+ - **真因**:连接在响应体中途被掐断时 undici 抛的是裸 `TypeError: terminated`——它自身不带任何错误码,真原因(ECONNRESET、对端关闭、body timeout)只挂在 `.cause` 上。[adapter.ts](../src/host/kimi-code/adapter.ts) 的读流循环只改写 mapper 已经定性的 in-band 错误,其余原样 `throw error`,于是这个裸值一路漏到 harness:`normalizeLlmFailure` 对它只能归一成 `UNKNOWN`,而 `UNKNOWN` 不在本线路的 `retryableCodes`(RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT)里——一次断连就把整轮判死,连一次自动重试都没有,同时还把 `.cause` 丢掉,报告里连发生过什么都说不出来。
27
+ - **对照**:同仓库 codex 线路早为同一形状写过 `streamFailure()`([responses-client.ts](../src/host/responses-client.ts)),claude 线路有 `outputStarted ? PROVIDER_ERROR : TRANSPORT` 的分支;kimi 是唯一照旧写法留下的那条。
28
+ - **改法**:新增 `streamFailure()`([adapter.ts](../src/host/kimi-code/adapter.ts)),把逃出读循环的非 `LlmError` 值改判成 `TRANSPORT`,并把 `.cause` 链拼进消息(`Kimi Code stream failed: terminated: read ECONNRESET`);调用方取消仍是 `ABORTED`,本线路自己定性的 `LlmError` 原样通过。`assertStreamComplete` 那条同类形状——连接干净结束、但始终没等到终止事件——同步从 `PROVIDER_ERROR` 改成 `TRANSPORT`([mapper.ts](../src/host/kimi-code/mapper.ts)),两种「说到一半没了」都交给 harness 的退避重试(最多 3 次,1.5s–15s)。
29
+ - **为什么已经出字也重试**:失败的那次 attempt 由 DSH 记成 `assistant/attempt`——不进行模型历史、也不作为正文渲染(宿主 `turn-process.ts` 的 `visibleAssistantEvent` 对 attempt 恒为不可见),工具调用要等流结束才执行,所以重试既不会让模型看到重复内容,也不会重跑工具;用户看到的只是已流出的那段字被重试的那一次顶替,比整轮作废划算。
30
+ - **验证**:`npx vitest run test/kimi-code-adapter.test.ts test/kimi-code-stream-idle.test.ts test/kimi-code-mapper.test.ts` **90 passed**(新增 3 条断流用例;把 `throw streamFailure(...)` 临时改回 `throw error` 时,断流用例当场复现裸 `TypeError: terminated`,证明用例咬得住);`npm run typecheck` 0 错误;全量 `npx vitest run` **270 文件 / 4491 passed / 7 skipped**。
31
+ - **[minimax-code] `/video` 不再是「先选档位、再往输入框塞一段模板」:命令行有了图标,档位搬进面板,面板也终于不再把整页压暗**
32
+ - **现象**:斜杠菜单里 `/video` 是**唯一一行没有图标**的,混在「压缩 / 权限 / 模型 / 导出」里明显没对齐;点它先弹一个只有「常用档位 + 自定义…」的小列表,选中任何一档都会把一整段 `用 minimax_generate_video 生成视频:prompt=(在这里写画面描述)…` 灌进输入框,用户还得回到那段话里找该改哪一处;而「自定义…」打开的面板盖着一层半透明遮罩,整页对话被压暗,参数又是 9 个同权重字段竖着排下来。
33
+ - **改法([video-command.ts](../src/client/minimax-code/video-command.ts))**:`popupSelect` 换成 `action`——命令只做一件事:打开面板。图标走宿主的 `CommandContribution.icon`([`video-command-icon.tsx`](../src/client/minimax-code/video-command-icon.tsx),按 `IconProps` 的 `{ size, className }` 同形自绘,因为 `dsh-client-ui-primitives` 的 `lib` 是宿主装配产物、插件装不到)。**宿主侧限制**:`ui-commands` 的 `matchEnter` 只在裸命令时把一行交给客户端贡献(`if (!bare) return undefined`),所以「`/video` 后面直接写描述」做不到;改为**打开时把输入框里已有的文字当成画面描述带进来**(`composerDraftOf` 读 `InputState.draft`,并剥掉可能残留的前导命令 token)。
34
+ - **改法([VideoFormDialog.tsx](../src/client/minimax-code/VideoFormDialog.tsx) / [styles.ts](../src/client/minimax-code/styles.ts))**:去掉遮罩,面板变成阴影浮起的居中浮层(`margin:auto` 保住「内容太高仍可滚动」),点浮层外仍然关闭;版面按「先说画面、再说参数」重排——描述占最大位,下面一排**档位胶囊**,再一行模型/时长/分辨率,比例按联动规则锁死或可选,水印与参考媒体收进「更多参数」。
35
+ - **参考媒体**(新增 [video-media-input.ts](../src/client/minimax-code/video-media-input.ts) 与 [video-media-dropzone.tsx](../src/client/minimax-code/video-media-dropzone.tsx)):四排纯文本框换成四个媒体块,**浏览 / 拖拽 / 粘贴**三入口合一,路径显示成可移除的标签。桌面端靠 `__DSH_HOST_PATHS__.pathFor` 把 `File` 还原成真实绝对路径;拿不到路径时如实提示而不是编一条。`file://` 地址还原成本机路径;`http(s)` 链接里**只有 `cdn.hailuoai.com` 被接受**(宿主 `assertMinimaxCdnUrl` 实测只读这一个主机),其余公网地址当场拒绝——否则会变成计费之后才被服务端打回。CDN 参考视频读不到时长,所以每出现一条就长出一行「它的时长」,填了才进请求(宿主 `MINIMAX_VIDEO_REFERENCE_DURATION_REQUIRED`)。
36
+ - **两处回调**:① 档位胶囊原本会折成两行(三个全名并排塞不进 600px),改成`grid-auto-flow:column` 等宽一行,胶囊上只留 `H3 · 4秒` 这类短标签,分辨率与比例挂在 `title`/`aria-label` 上仍然查得到;② 每个媒体块补了一行**手填入口**——路径与 `@工作区文件` 都收(`stripReferencePrefix` 去掉那个前缀,因为工具参数要的是普通路径),回车即添加。
37
+ - **版面再调整**:面板从 `600px` 加宽到 `880px` 并改成两栏(左栏只放**画面描述**,右栏放档位、参数与参考媒体,≤720px 时折回一栏);拖拽区与输入行**合并为同一个框**——已收下的标签在上、输入行贴底,整块都是放置目标;输入框里的 `@` 现在会弹出**真实工作区候选**(新增 [video-workspace.ts](../src/client/minimax-code/video-workspace.ts) 包住宿主 `remote.workspaceFiles.list`:`@` 后的目录片段决定列哪一层,选中文件即入列、选中目录则继续下钻,`Tab` 取第一项、`Esc` 先收候选再关面板);宿主没有这条远端时自动退化为纯手填,不报错。
38
+ - **两栏落地后的三处修正**:① 描述框的 `width:100%` 加上左右内边距把它**顶出了栏宽**,文字一路压到右栏的「档位」标签上——补 `box-sizing:border-box`,两栏间距同时从 16px 放到 28px;② 展开「更多参数」后左栏只剩一小块输入框、下半截全空——描述框改为跟随右栏一起长(`flex:1` 一路传到 textarea,上限 520px);③ 左栏标题行的「已带入输入框里的文字」提示与右栏列头贴在一起,允许换行并在挤不下时省略。
39
+ - **验证**:`npm run typecheck` 0 错误;全量 `npx vitest run` **270 文件 / 4488 passed / 7 skipped**(新增 `test/minimax-code-video-media-input.test.ts`,扩写命令与面板两个套件);`npm run build` 通过。
40
+
41
+ ## 0.32.0 - 2026-10-20
42
+ - **[cline-pass] 凭据后端读不出来时不再吞掉 `CLINE_PASS_API_KEY`:环境变量与密钥环是两条独立来源**
43
+ - **现象**:在**没有 Secret Service 的 Linux 环境**(裸 CI runner 最典型)里,`cline-pass` 的账号列表是空的,即使显式导出了 `CLINE_PASS_API_KEY`——于是「随包内置的密钥改用环境变量注入」这条文档化的用法在这些环境里**静默失效**。
44
+ - **真因**:`account-pool.ts` 的 `legacyAccount` 投影第一行就是 `await store.read()`。读取平台加密后端在 Linux 上要有个解锁的 keyring;没有就**抛错**,而这一抛把后面那句 `ambientCredentials()`(读环境变量)一起带走了——**两个本来互不相干的来源被串成了一条链**。Windows 有 DPAPI、macOS 有 Keychain,所以本机从未暴露,只有 CI 红。
45
+ - **改法**:`store.read().catch(() => null)`——密钥环答不上来就当作「没有已存凭据」,环境变量照常投影。**不是妥协**:环境变量本来就不需要密钥环可读,它从来没有依赖过存储。
46
+ - **验证**:`test/cline-pass-plugin.test.ts` 新增一例把 `store.read` 打桩成 reject 并断言环境变量投影仍然生效(**5 passed**);同线路另外 2 个套件(`account-refresh` / `routes`)一并回归通过。
47
+ - **[cline-pass] 高级设置与整站版式对齐:输入框终于有主题样式,六行数值与三行开关各归其位**
48
+ - **现象**:设置页「高级设置」里的六个输入框是**浏览器原生外观**(白色实心框 + 粗边框),与旁边 `.dsha-select` 的深色圆角控件格格不入;原因是 [styles.ts](../src/client/cline-pass/styles.ts) 用了 `.dsha-input` 这个类名,而**全站样式表从未定义过它**——其他线路的输入框都自带各自的 `-control input` 规则,本线路抄了类名没抄规则。
49
+ - **错位**:原布局用一张双列 Grid(`.dshcp-field`)把所有控件塞在右半屏,而 `<select>` 被 `.dsha-select` 的 `max-width:220px` 卡住,于是**输入框顶到最右、下拉框缩在中间**;「在模型选择器中列出网关目录的额外模型」的复选框被 Grid 推到距左 240px 处,与下方「注册 `cline_pass_*` 管理工具」那个贴右的复选框**不在同一条竖线**上。
50
+ - **说明串了行**:`t.pinSpellingHint`(「网关可能在两次请求间切换管线…」)被写在整张 Grid **下方**,视觉上成了「列出网关目录额外模型」的副标题,而它真正的 `t.exposeCatalogHint` 根本没渲染。
51
+ - **改法**:① 补上 `.dsha-input` 的主题样式(`--dsw-alias-bg-layer-2` 底、`0.5px` 边框、`7px` 圆角、`focus-visible` 描边),与 `.dsha-select` 同一套 token;② 六个数值行改用共享 `.dsha-row`(左标签右控件),右侧统一 `240px` 定宽(`.dshcp-adv-control`),数值与地址分别用等宽字体(`.dshcp-adv-num` 右对齐 / `.dshcp-adv-mono`);③ 三行开关改用共享 `.dsha-pref-row`(左侧粗体标题 + 灰字说明、右侧 `.dshcp-check` 复选框),三枚复选框在右侧对齐;④ 每句提示归位到它自己那一行的标题下,并补回漏掉的 `t.exposeCatalogHint`。
52
+ - **验证**:`npm run typecheck` 0 错误;`test/cline-pass-section.test.tsx` **12 passed**(新增一例断言 6 行 `.dsha-row` + 3 行 `.dsha-pref-row` 且每行副标题与自己的标题配对);`npm run build` 通过。
53
+ - **[进展更新] 面板正文终于能画代码块、公式与 mermaid:不再把源码原样摊在读者面前**
54
+ - **现象**:进展正文走的是本插件自己的 `MarkdownLite` 渲染器(平台那个 `MarkdownText` 的 lib 是浏览器构建,顶层 `import css from X.module.css` 会让任何 Node/vitest 导入方直接失败),而它只认段落、列表、GFM 表格、行内代码、粗体与标题——于是正文里只要出现围栏代码块或公式,**源码就逐字显示出来**。
55
+ - **改法([markdown-lite.tsx](../src/client/progress-updates/markdown-lite.tsx))**:加围栏代码块(按 CommonMark 的闭合规则,**未闭合的围栏吞掉剩余全文**,所以流式输出永远不会漏出反引号)与 KaTeX 公式(行内 `$..$` / `\(..\)`、块级 `$$..$$` / `\[..\]`)。KaTeX 的 HTML 经 `DOMParser` 映射成 React 元素,**不走 `dangerouslySetInnerHTML`**,渲染器「无注入」的性质得以保持;美元符号的判定照抄平台语法守卫,因此「prices $5 to $10」仍然是散文。
56
+ - **改法([renderer.ts](../src/client/mermaid/renderer.ts))**:语法关键字表从 10 种扩到 mermaid 11 能识别的 **47 种**(journey、quadrantChart、C4Context、sankey-beta 等此前**根本看不见**);并且旧代码只匹配**行首**关键字,于是非常常见的 `%%{init}%%`、注释与 frontmatter 前言会把一张本来合法的图判死——新增 `stripMermaidPreamble` 在匹配前剥掉它们。横幅语言标签改读文档化的 `[data-code-block-banner]` 钩子,而不是 `firstElementChild`(后者的 `textContent` 是语言名加复制按钮文案)。
57
+ - **一个容易踩空的点**:平台对 mermaid 围栏**不会**打印 "mermaid"(高亮器没有 mermaid 语法,`CodeToolbar` 回落到 `codeBlock.title` 占位符)。所以那个标签对特定语言是**否决票**、对 mermaid 是**显式放行**,而占位符的含义是「没有标签」,此时由关键字表说了算。
58
+ - **依赖**:`katex` 进 `dependencies`(`^0.18.7`,平台基线钉的同一个版本);它的 CSS 与字体仍来自宿主页面,所以 bundle 只是变大,不会多打包一份样式表。
59
+ - **验证**:`npm run typecheck` 0 错误;新增 [`test/markdown-lite.test.tsx`](../test/markdown-lite.test.tsx) 与扩写的 [`test/mermaid-renderer.test.ts`](../test/mermaid-renderer.test.ts);全量 `npm test` **268 文件 / 4452 passed / 7 skipped**。
60
+ - **[antigravity] 上游 5xx 与「根本没连上」不再判本轮死刑:按可重试码上报,交给宿主的退避重试**
61
+ - **现象与真因**:个别轮次直接失败并显示 `Antigravity API error (500): No response`(徽标 `PROVIDER_ERROR`),手动重发立刻能过。上游那次 500 确实是瞬时抖动,但**这条线路把它归成了终局错误码**:`adapter.ts` 里非 429 的非 2xx 一律抛 `PROVIDER_ERROR`,而这条线路的 `providerRetryPolicy()` 返回 `undefined`,宿主因此套用默认策略,默认可重试码只有 `EMPTY_RESPONSE / RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT` —— `PROVIDER_ERROR` 不在其中,`llm-retry` 一次都不会退避重试,直接把这一轮判死。
62
+ - **还有一个会撒谎的状态码**:`fetch` 在**每个**端点上都抛错(DNS / TLS / 连接被重置)时根本没有 `response` 对象,而旧代码用 `response?.status ?? 500` 补了一个 **500**,于是"根本没连上"和"上游真的回了 500"在界面上长得一模一样。
63
+ - **改法**:① 所有端点都没拿到 HTTP 响应 → `TRANSPORT`(带 `cause`),不再伪造 500;② `status >= 500` → `SERVER`;③ 400 / 401 / 403 / 404 仍是 `PROVIDER_ERROR` 终局,429 的两条既有判决不变。瞬时故障从此由宿主的 5 次退避重试(500ms 起、上限 10s)接管,真正被拒的请求仍然当场失败。
64
+ - **验证**:`npm run typecheck` 0 错误;`npm test` **268 文件 / 4429 passed / 7 skipped**。`test/antigravity-adapter.test.ts` 新增 1 例(全端点 `fetch` 拒绝 → `TRANSPORT`),503 那条断言的 code 由 `PROVIDER_ERROR` 改为 `SERVER`,其"端点优先级"的本意不变。
65
+ - **[cline-pass] 新增第 10 条订阅线路:Cline Pass(WorkOS OAuth 登录 + 上游渠道钉住)**
66
+ - **范围**:把 MIT 许可的 [`dsh-cline-pass`](https://github.com/yhshzh/dsh-cline-pass) 的功能按本仓库的线路规范兼容移植为独立线路 `cline-pass`,复用共享内核(`AccountPoolCore`、并发闸、空闲看门狗、请求图片预算、`claimProviderRoute`、账号卡片 / 模型清单 / 上下文编辑器 / composer 胶囊),**不改动任何既有线路的行为**。
67
+ - **登录**:Cline 账号走 **WorkOS 设备码 OAuth**(与官方 CLI 的 `cline` / `cline-pass` 同一条链),浏览器确认后账号自动入池;access token 以 `Bearer workos:<token>` 发送,到期前自动 refresh,轮换后的 refresh token 先落盘再使用;`invalid_grant` 标记账号失效并要求重新登录,网络/5xx 不误登出。静态 API Key 保留为兼容回退。端点、client id 与 `workos:` 前缀转录自 Cline 官方开源仓库(`sdk/packages/core/src/auth/cline.ts`、`auth/provider-auth-registry.ts`、`sdk/packages/shared/src/runtime/cline-environment.ts`,commit `ca7402c`,2026-10-20)。
68
+ - **能力**:OpenAI Chat Completions 流式(文本 / reasoning / 工具调用 / 图片);多账号(OAuth)或 API Key 顺序 / 轮询 / 粘性调度、429 冷却、401/403 失效标记与换号;**上游渠道探测 / 逐渠道校验 / 钉住 / 排除**,`strict` 与 `preferred` 两种模式,首个 token 前故障转移;两种管线写法默认都写(`provider.*` 与 `providerOptions.gateway.*`),并以响应回读的实际渠道判定钉住是否被采纳;官方目录(`recommended-models` + models.dev + 上游网关)刷新;按账号读取 5 小时 / 周 / 月额度;进程内请求历史(含失败尝试链与 token 用量);8 个 `cline_pass_*` 管理工具默认不注册,卡片内可开启(重启生效)。
69
+ - **注册点**:`src/index.ts`(线路工厂 / 路由 / hub 摘要 / dispose / 导出尾部)、`CONTROLLED_PROVIDERS`、`DIAGNOSTIC_PROVIDERS`、`hub/providers.tsx`、`hub/brand-icons.tsx`、`client/index.tsx`(locale / 样式 / 胶囊 order 44),以及既有计数断言各追加一项。
70
+ - **传输**:对"请求尚未发出"的 TLS/connect 失败自动重试 2 次,并在最终错误里说明"未到达网关、钉住未生效";实测本机到 `api.cline.bot` 的 Node TLS 握手会间歇失败(同一时刻 curl 正常、IPv6 不可达),这与上游钉住无关。流中段断流不重试,避免重复计费。
71
+ - **安全**:OAuth access / refresh token 与 API Key 只进平台加密凭据库(DPAPI / Keychain / Secret Service),响应与日志均不出现原文;登录流程 DTO 逐字段投影;所有写路由同源校验;错误信息最多回显上游一行原因。
72
+ - **文档与品牌**:新增 [`docs/cline-pass-line-plan.md`](docs/cline-pass-line-plan.md)(每个常量的出处、决策、**未验证清单**);README 增加线路章节与路由表;协议证据矩阵增加一行;hub 卡片改用 Cline 官方标记(`assets/branding/logos/cline-icon-white.svg` + 官方 app icon 的 `#343B42` 底砖),不再用字母占位。
73
+ - **验证**:`npm run typecheck` 0 错误;全量 `npx vitest run` **267 文件 / 4390 passed**(在 upstream 0.31.0 之上新增 8 个测试文件、68 例);`npm run build` 与 `npm pack --dry-run` 通过,`lib/types/host/cline-pass/*` 已进包。
74
+
5
75
  ## 0.31.0 - 2026-10-20
6
76
  - **[minimax-code] 视频生成现在能带素材、选比例:首/尾帧、参考图/视频/音频、任意比例与 AIGC 水印**
7
77
  - **为什么能带素材**:上游那套媒体工具走的**不是**平台 API Key,而是**订阅网关** —— 和我们 Messages 线路同一个 origin、同一把订阅令牌、四个请求头。所以生成视频不需要额外配 Key。
package/README.md CHANGED
@@ -18,6 +18,8 @@
18
18
  - [Claude(订阅)线路](#claude订阅线路)
19
19
  - [GLM(ZCode 账号)线路](#glmzcode-账号线路)
20
20
  - [Step Code(Step Plan 订阅)线路](#step-codestep-plan-订阅线路)
21
+ - [Cline Pass(OAuth 订阅)线路](#cline-passoauth-订阅线路)
22
+ - [Qoder(订阅)线路](#qoder订阅线路)
21
23
  - [子代理模型授权](#子代理模型授权0215-起032-起强制指定模型)
22
24
  - [随包分发的 Agent Preset](#随包分发的-agent-preset)
23
25
  - [本机登录](#本机登录)
@@ -28,7 +30,7 @@
28
30
 
29
31
  ## 线路总览
30
32
 
31
- 本插件同时接入 **9 条订阅线路**,每条各自独立注册 Provider、独立登录、独立额度卡片。
33
+ 本插件同时接入 **11 条订阅线路**,每条各自独立注册 Provider、独立登录、独立额度卡片。
32
34
 
33
35
  | Provider ID | 订阅 | 协议 | 登录方式 | 模型目录来源 |
34
36
  | --- | --- | --- | --- | --- |
@@ -41,6 +43,8 @@
41
43
  | `antigravity` | Google Antigravity | Gemini | Google OAuth | 内置表 |
42
44
  | `glm-coding-plan` | GLM Coding Plan(ZCode 账号,国区 BigModel / 国际区 Z.ai) | Anthropic Messages + OpenAI Chat Completions(按模型声明) | ZCode 账号 OAuth(浏览器中转轮询)+ 可选导入本机 ZCode 登录态 | 随包目录(源自官方 `zcode-builtin.json`) |
43
45
  | `step-code` | StepFun Step Plan 订阅(国区 api.stepfun.com / 国际区 api.stepfun.ai)或开放平台 API Key | OpenAI Chat Completions | **云端轮询**浏览器授权(不开本地回调端口)/ 手工粘贴 Platform API Key / 只读导入本机 `~/.stepcode/auth.json` | **实时运行时发现**(随包表刻意为空) |
46
+ | `cline-pass` | Cline Pass | OpenAI Chat Completions | **浏览器设备码 OAuth**(WorkOS,账号入池、自动续期);API Key 仅作兼容回退 | 随包表兜底 + 官方 `recommended-models` / models.dev / 上游网关目录补全 |
47
+ | `qoder` | Qoder 订阅(全球区 qoder.com / 中国站 qoder.com.cn) | Qoder 自有(COSY 签名 + 信封 SSE) | **浏览器设备流**(仅全球区)/ 手工粘贴 PAT(两区)/ 只读导入本机 `~/.qoder/.auth/user` | **实时运行时发现**(COSY 签名 GET)+ 随包能力兜底表 |
44
48
 
45
49
  > **只有 minimax-code 是硬编码目录,而且是有意的**:该端点的 `GET /v1/models` **未对订阅流量开放**(返回 503 `direct_route_not_configured`),任何「实时目录」都只会是一个必然失败的请求。其取值转录自本机官方客户端的 `config.yaml`。
46
50
  >
@@ -1014,6 +1018,113 @@ access token / refresh token / 授权码 / PKCE verifier**;诊断里提到某
1014
1018
 
1015
1019
  与其它线路一样,所有修改状态的路由只接受同源 JSON POST 并校验 `Origin` 与 `Host`。**响应里永远不含 api_key、`Oasis-Token` 或账号密码的原文**——下发的只有「有没有凭据、从哪来、大概什么时候过期、上次读它出了什么问题」。
1016
1020
 
1021
+ ## Qoder(订阅)线路
1022
+
1023
+ \`qoder\`(显示名 **Qoder(订阅)**)接入 **Qoder 订阅**(全球区 \`qoder.com\` / 中国站 \`qoder.com.cn\`)。两个区域是**两套互不通用**的部署:一个区铸造的凭据打另一个区的端点会被上游直接拒绝。因此**区域是凭据的属性而不是请求的属性**,号池里可以同时持有两区账号且互不串用。
1024
+
1025
+ > ⚠️ **这条线路的协议面是逆向工程成果,不是官方公开契约,这是本线路唯一的合规与稳定性风险点。** COSY 签名、body codec、额度端点都来自开源实现([pi-provider-qoder](https://github.com/simonsmh/pi-provider-qoder),live 验证)与 [qodercli2api](https://github.com/Liki4/qodercli2api) 对官方 Qoder CLI v1.1.34 的全合成 frozen fixtures——本插件把每一步**逐字节**锁在测试里(\`test/fixtures/qoder/\` vendored),但上游随时可能改协议或收紧风控。额度端点(\`/api/v2/quota/usage\`)更是**非公开的个人接口**:它的失败绝不影响模型请求,卡片把它画成尽力而为的读数,不是账单。COSY 版本跟随官方 CLI 的 release feed(落后会拿到**缩减的**模型目录),拉取失败回落随包版本。
1026
+
1027
+ **登录**(三条路,任选其一,互不替代):
1028
+
1029
+ 1. **浏览器设备流**(仅全球区):本线路**不开本地回调端口**,流程跑在 host 上,你在浏览器里人工确认,卡片每 2 秒跟随一次进度。授权页地址始终显示在卡片上——host 不自行开浏览器,即使自动打开失败你也能自己复制打开;
1030
+ 2. **手工粘贴 PAT**(两区都支持):在账号的 Integrations 页生成一个 Personal Access Token(\`pt-...\`)粘贴进来。它先被兑换成短期 job token(\`jt-...\`)使用;**PAT 本体只存在 host 的加密存储里**,用途只有一个:过期后重新兑换;
1031
+ 3. **只读导入本机 \`qodercli\` 登录态**(可选):读 \`~/.qoder/.auth/user\`(AES-128-CBC 信封,key 是 \`machine_id\` 前 16 个字符——格式逐字来自协议文档,且有 frozen fixture 锁定)。**只读**,从不创建、修改或删除 qodercli 的任何文件。文件不记录区域,所以导入时对两区的 userinfo 各探一次(只读 GET),哪区认账就是哪区;两区都不认(token 已过期/被撤销)就如实告知,**不替你猜一个必然 401 的区域**。
1032
+
1033
+ **凭据会过期、可刷新**(与 step-code 的静态凭据相反):号池在 \`expiresAt - 1 小时\` 前自动续期——PAT 来源拿存下的 PAT 重兑换,其余走 \`refresh_token\` 轮换(轮换的新对先落盘再使用)。刷新被上游判 401/403 时账号标记为失效并提示重新登录;网络/5xx 只算瞬时失败,不会误登出。
1034
+
1035
+ **能力**:Qoder 自有对话面(OpenAI 形状的消息数组包在 \`RemoteChatAsk\` 信封里,过自定义 body codec,COSY 头签名,应答是信封 SSE)。模型目录**运行时发现**(COSY 签名的 \`GET /algo/api/v2/model/list?Encode=1\`)+ 随包能力兜底表;能力(上下文窗口 / 图片输入 / 思考档位)全部来自目录字段(\`context_config\` / \`is_vl\` / \`thinking_config\`),**不从模型名推断**,目录里那个 \`max_input_tokens\` 陈旧地板值明确不信。档位在本地先收敛到**模型真实梯子**再发出去;toggle 形态的模型只发开关。输出上限取上游文档化的 128K(目录不报输出上限)。
1036
+
1037
+ **缓存与用量**:响应 usage 里读缓存命中(\`prompt_tokens_details.cached_tokens\`,顶层 \`cached_tokens\` 兜底),缓存写入读 \`cache_write_tokens\`(上游不报就是 0,不做「恒零」声明)。**整页不出现任何金额**:订阅没有公开的 token 单价,Credits 与金额之间没有官方汇率——只显示 token 与 Credits 数字。
1038
+
1039
+ **已知限制**:
1040
+
1041
+ - **未用真实账号联调**:全部验收来自 mock + frozen fixtures(\`docs/qoder-line-plan.md\` §9 是联调清单与逐项风险)。上线时的实际置信度就是这句话,不打包成「已验证」;
1042
+ - **中国站没有设备流**:它只接受 PAT 粘贴或导入本机 qodercli 登录态(卡片在选中国站时不显示浏览器登录按钮,host 侧同样拒绝);
1043
+ - **额度端点是非公开个人接口**(见开头的风险提示);三只桶分开渲染,缺桶不画,没有总额的桶不参与任何百分比;
1044
+ - **品牌标记是 monogram**:官方 favicon 是一枚 73KB 的复合字形,不适合内联转录;卡片用字母 Q,底砖与字形色的取值都取自官方站点资产(出处记在 \`brand-icons.tsx\` 注释里);
1045
+ - **\`chat_context\` 按官方 fixture 发最小形态** \`{}\`:若实测发现模型路由异常,对照 pi-provider 的 \`extra.modelConfig\` 补上(计划 §9 风险 10)。
1046
+
1047
+ ### 插件路由(Qoder)
1048
+
1049
+ 所有路由都以 \`/qoder/api\` 为前缀:
1050
+
1051
+ | 方法 | 路径 | 说明 |
1052
+ | --- | --- | --- |
1053
+ | GET | \`/status\` | 区域、号池、模型目录、额度快照、缓存统计与登录流程;页面唯一的读口 |
1054
+ | POST | \`/settings\` | 启用开关、勾选模型、上下文窗口覆盖、默认思考档位、手选账号 |
1055
+ | POST | \`/accounts\` | 号池动作:\`set-primary\` / \`clear-cooldown\` / \`delete\` / \`relogin\` / \`strategy\` |
1056
+ | POST | \`/login/start\` | 开一次设备流登录(仅全球区),立即返回流程状态(不阻塞、不开本地回调端口、host 不自行开浏览器) |
1057
+ | GET | \`/login/status\` | 轮询登录流程状态;host 在后台轮询上游 |
1058
+ | POST | \`/login/cancel\` | 取消登录流程 |
1059
+ | POST | \`/login/manual\` | 手工粘贴 PAT(**请求体必然带凭据,刻意不在冻结契约里声明**) |
1060
+ | POST | \`/adopt\` | 只读导入本机 qodercli 登录态;失败**永不阻断**,每种失败都是契约里的一个 reason |
1061
+ | POST | \`/connection/test\` | 对目录端点做一次 COSY 签名的探测(**裸 DTO,不走信封**);它答「这个账号能不能读目录」,答不了「这个具体模型会不会成功」 |
1062
+ | POST | \`/quota\` | 强制刷新额度;一次失败的额度读不是一次失败的请求,卡片照旧渲染 |
1063
+ | POST | \`/catalog/refresh\` | 强制刷新模型目录 |
1064
+
1065
+ 与其它线路一样,所有修改状态的路由只接受同源 JSON POST 并校验 \`Origin\` 与 \`Host\`。**响应里永远不含 job token、PAT 或刷新令牌的原文**——下发的只有「有没有凭据、从哪来、大概什么时候过期」。
1066
+
1067
+ ## Cline Pass(OAuth 订阅)线路
1068
+
1069
+ `cline-pass`(显示名 **Cline Pass**)接入 [Cline Pass](https://cline.bot/cline-pass)。与其它订阅线路一样,它**用 OAuth 登录**:Cline 账号走 **WorkOS 设备码授权**(RFC 8628 形态),浏览器确认后账号自动加入号池;聊天请求携带 `Authorization: Bearer workos:<accessToken>`,access token 到期前用轮换的 refresh token 自动续期。静态 API Key 仍保留为**兼容回退**(旧 `dsh-cline-pass` 用户或某些部署),但不再是默认入口。
1070
+
1071
+ 设备码流程的四个上游调用与常量来自 Cline 官方开源仓库,不是猜的:
1072
+
1073
+ - `POST https://api.workos.com/user_management/authorize/device`(client id `client_01K3A541FN8TA3EPPHTD2325AR`);
1074
+ - 浏览器确认后轮询 `POST https://api.workos.com/user_management/authenticate`;
1075
+ - `POST https://api.cline.bot/api/v1/auth/register` 换取 Cline 会话;
1076
+ - `POST https://api.cline.bot/api/v1/auth/refresh` 续期。
1077
+
1078
+ 对应源码:Cline 仓库 `sdk/packages/core/src/auth/cline.ts`、`auth/provider-auth-registry.ts`(`workos:` 前缀)、`sdk/packages/shared/src/runtime/cline-environment.ts`(client id 与端点)、`sdk/packages/llms/src/providers/vendors/cline.ts`(Bearer 发送)。抓取日期 2026-10-20。
1079
+
1080
+ **能力**
1081
+
1082
+ - OpenAI Chat Completions 协议,流式输出;透传工具 schema、工具调用/结果,图片按 `image_url` 内联,`reasoning_effort` 原样下发;支持 `temperature`、`stop` 与 `max_tokens`;
1083
+ - 模型目录:随包 15 个 Cline Pass 模型是**兜底**。点「刷新模型目录」会读官方 `/ai/cline/recommended-models`、models.dev 与上游网关目录,只增不减,并把每个模型的能力(上下文窗口、输出上限、图片、推理档位)一起补上;新发布、本版本不认识的模型按保守值录取,不会被隐藏;
1084
+ - **上游渠道可观测、可钉住**:同一模型可能由 OpenRouter(direct 管线)或 Vercel AI Gateway(planner 管线)转发。卡片可以对单个模型「探测」渠道、逐个「校验」、按顺序钉住或排除,并在**首个 token 之前**失败切换到下一个渠道;`strict` 只发允许列表,`preferred` 按顺序尝试。钉住字段默认**两种写法都写**(`provider.only/order/sort` 与 `providerOptions.gateway.*`),因为网关可以在两次请求之间换管线,只写一种会让钉住悄悄失效;
1085
+ - **钉住是否被采纳以响应为准**:网关对未知/不可用的 `only` 不报错,直接从别的渠道服务。因此探测、校验与真实请求都会回读响应里的实际服务渠道;「请求 baseten、实际 deepseek」会被明确标成未被采纳,而不是当成成功;
1086
+ - **登录态**:设备码确认后账号自动入池;access token 以 `workos:` 前缀发送,到期前 5 分钟自动 refresh(refresh token 轮换并先落盘再使用);refresh 被上游判 `invalid_grant` 时账号标记为失效并提示重新登录,网络/5xx 只算瞬时失败,不会误登出;
1087
+ - **号池**:多账号(OAuth)或 API Key、顺序/轮询/粘性三种调度、429 冷却、401/403 标记失效后换号;额度耗尽(`QUOTA`)与鉴权失败是**账号级**失败,会换账号;普通 429 是**渠道级**,只换渠道,不牺牲账号容量;
1088
+ - **额度**:`GET /users/me/plan/usage-limits` 的 `five_hour` / `weekly` / `monthly` 三个窗口,**按账号**读取并显示;读取失败只报原因,不阻断请求;
1089
+ - **请求历史**:记录模型、账号、实际服务渠道、首 token / 总耗时、token 用量、错误与失败尝试链;只在进程内存里,重启清空;
1090
+ - **管理工具**:`cline_pass_status` / `models` / `probe` / `validate` / `test` / `pin` / `accounts` / `history` 八个工具,**默认不注册**——它们的 schema 会出现在每次请求的工具列表里,而大多数对话不需要它们。在卡片「高级设置」里勾选后,**重启 DSH** 生效;Provider 与设置卡本身不受影响。
1091
+
1092
+ **设置页路由**(前缀 `/cline-pass/api`)
1093
+
1094
+ | 方法 | 路径 | 说明 |
1095
+ | --- | --- | --- |
1096
+ | GET | `/status` | 号池、登录流程、模型与渠道观测、额度快照、历史条数、行级设置;页面唯一的读口 |
1097
+ | POST | `/login/start` | 申请 WorkOS 设备码,返回短码与授权链接(不打开浏览器) |
1098
+ | GET | `/login/status?id=` | 轮询设备码;host 按 WorkOS 的 interval 节流,成功时完成 `auth/register` 并把账号入池 |
1099
+ | POST | `/login/cancel` | 放弃当前设备码;之后即使浏览器确认也不会落盘 |
1100
+ | POST | `/settings` | 启用开关、默认网关、默认上下文/输出预算、每账号并发、流空闲超时、历史条数、目录/工具开关、钉住写法 |
1101
+ | GET/POST | `/accounts` | 号池动作:`add` / `test` / `set-primary` / `set-alias` / `delete` / `clear-cooldown` / `strategy` |
1102
+ | GET/POST | `/models` | 模型勾选(`enabledModelIds`,`null` = 全选)与线路启用 |
1103
+ | POST | `/models/pin`、`/models/reset` | 保存/清除单个模型的渠道钉住 |
1104
+ | POST | `/models/probe`、`/models/validate`、`/models/test` | 探测渠道、逐渠道校验、按当前钉住发一次真实小请求 |
1105
+ | POST | `/models/visibility`、`/models/visibility-all` | 单个或批量显示/隐藏模型 |
1106
+ | POST | `/models/context-window` | 单个模型的上下文窗口覆盖(`null` 恢复默认) |
1107
+ | POST | `/catalog/refresh` | 重新扫描官方订阅目录 |
1108
+ | GET/POST | `/usage` | 读缓存 / 强制为每个可用账号读一次官方额度 |
1109
+ | GET | `/history` | 进程内请求历史(`limit`、`model` 过滤) |
1110
+
1111
+ OAuth 是默认入口;「使用 API Key」按钮保留为兼容回退,部署也可以用 `CLINE_PASS_API_KEY` 提供第一把 Key:号池为空时它会被投射为「首要账号」,无需先粘贴(环境变量不会被写入凭据库)。**从 `dsh-cline-pass` 迁移时不会自动导入旧插件的设置与凭据引用**——OAuth 账号重新登录一次即可,静态 Key 需要在卡片里重新添加;两个插件不要同时启用,否则会争抢同一个 `cline-pass` 路由(本卡会明确显示占用方)。
1112
+
1113
+ 所有修改状态的路由只接受同源 JSON POST 并校验 `Origin` 与 `Host`。**响应里永远不含 access token、refresh token 或 API Key 原文**:下发的只有账号 id、别名、`authKind`、冷却/失效状态与错误原因;OAuth 会话与 Key 都存在操作系统的加密凭据库(Windows DPAPI / macOS Keychain / Linux Secret Service)里。登录流程的 DTO 同样是逐字段投影,而不是展开整个会话对象。
1114
+
1115
+ **已知边界与风险**
1116
+
1117
+ - **本线路来自 `dsh-cline-pass` 的功能移植**,网关的渠道探测/钉住/故障转移语义沿用该插件(MIT)与 `cline-pass-switcher`(MIT)的公开实现;OAuth 链路的端点、client id 与 `workos:` 前缀转录自 Cline 官方开源仓库(见上)。网关与 WorkOS 都不是公开契约:渠道名、`provider.only` 是否被采纳、错误措辞、设备码策略都可能变化;本线路的策略是**读回真实渠道并如实报告**,不把一次 200 当成钉住成功的证据。
1118
+ - **没有实时订阅账号的验收记录**:本版本的准确度上限是「协议与映射按线上抓取/上游实现对齐 + mock 全绿」。设备码登录的四个请求与 refresh 链都有 mock 覆盖,但**尚未用一个真实 Cline 账号走完浏览器确认**;首次实机请先完成一次登录,再对目标模型点「探测」与「测试」。
1119
+ - **两个管线对渠道名的拼写不同**(例如 Z.AI 在 planner 侧是 `zai`、direct 侧是 `z-ai`),本线路按管线翻译;即便如此,**未知渠道不会让请求失败,只会被网关静默忽略**,所以「保存了钉住」不等于「钉住生效」。
1120
+ - **Host → 网关的 TLS/connect 失败与钉住无关**:请求体(含 `provider.only`)还没发出,网关也就没有机会选择上游。这类失败会先自动重试 2 次(仅限"请求未发出"的握手/连接错误),仍失败时错误文案会明确说明"未到达网关、钉住未生效"。如果反复出现,通常是本机到 `api.cline.bot` 的网络/代理/TUN 问题:在 ChatGPT 线路的代理设置里改用显式代理,或在 `$DSH_HOME/.env` 写 `HTTPS_PROXY=http://127.0.0.1:7897`(本机 Clash 端口)后再试。
1121
+ - **额度窗口是网关的读数,不是账单**;接口失败时卡片保留上一次读数并显示原因,不编造 0。
1122
+ - **模型目录随订阅变化**:随包表只覆盖发布时的模型;刷新失败不会清空已有目录,新模型按保守能力录取。
1123
+
1124
+ ### 插件路由(Cline Pass)
1125
+
1126
+ 见上表;与其它线路一样,路由表在 `src/host/cline-pass/routes.ts` 与客户端 `api.ts` 各有一份手写副本,集成结束时逐条对照。设置卡在「设置 → 订阅服务 → Cline Pass」,对话输入框右侧有该线路的额度胶囊(显示当前已读账号中最紧张的窗口)。
1127
+
1017
1128
  ## 本机登录
1018
1129
 
1019
1130
  **每条线路只在自己的详情页里回答关于本机的问题**。ChatGPT(Codex)线路的 ChatGPT 卡片里,「登录」按钮**旁边**就是「导入本机 Codex CLI 登录」——与 Claude 页面上那个收编按钮同样的位置、同样的次要样式;点它可以省掉再走一次 ChatGPT 授权。Claude Code 与 MiniMax Code 各自的设置页里本来就各有一个导入入口,不在这里重复。