@eddyskywalker/dsh-chatgpt-subscription 0.32.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.
- package/CHANGELOG.md +36 -0
- package/README.md +49 -1
- package/lib/client.js +3535 -1191
- package/lib/client.js.map +1 -1
- package/lib/index.js +7225 -2833
- package/lib/types/client/hub/brand-icons.d.ts +17 -1
- package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
- package/lib/types/client/hub/hub-styles.d.ts.map +1 -1
- package/lib/types/client/hub/providers.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/minimax-code/VideoFormDialog.d.ts +46 -50
- package/lib/types/client/minimax-code/VideoFormDialog.d.ts.map +1 -1
- package/lib/types/client/minimax-code/locales.d.ts +80 -20
- package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
- package/lib/types/client/minimax-code/styles.d.ts.map +1 -1
- package/lib/types/client/minimax-code/video-command-host.d.ts +17 -11
- package/lib/types/client/minimax-code/video-command-host.d.ts.map +1 -1
- package/lib/types/client/minimax-code/video-command-icon.d.ts +38 -0
- package/lib/types/client/minimax-code/video-command-icon.d.ts.map +1 -0
- package/lib/types/client/minimax-code/video-command.d.ts +53 -59
- package/lib/types/client/minimax-code/video-command.d.ts.map +1 -1
- package/lib/types/client/minimax-code/video-media-dropzone.d.ts +57 -0
- package/lib/types/client/minimax-code/video-media-dropzone.d.ts.map +1 -0
- package/lib/types/client/minimax-code/video-media-input.d.ts +173 -0
- package/lib/types/client/minimax-code/video-media-input.d.ts.map +1 -0
- package/lib/types/client/minimax-code/video-presets.d.ts +67 -0
- package/lib/types/client/minimax-code/video-presets.d.ts.map +1 -0
- package/lib/types/client/minimax-code/video-workspace.d.ts +41 -0
- package/lib/types/client/minimax-code/video-workspace.d.ts.map +1 -0
- package/lib/types/client/qoder/QoderComposerQuota.d.ts +41 -0
- package/lib/types/client/qoder/QoderComposerQuota.d.ts.map +1 -0
- package/lib/types/client/qoder/QoderSection.d.ts +26 -0
- package/lib/types/client/qoder/QoderSection.d.ts.map +1 -0
- package/lib/types/client/qoder/api.d.ts +4 -0
- package/lib/types/client/qoder/api.d.ts.map +1 -0
- package/lib/types/client/qoder/locales.d.ts +310 -0
- package/lib/types/client/qoder/locales.d.ts.map +1 -0
- package/lib/types/client/qoder/styles.d.ts +16 -0
- package/lib/types/client/qoder/styles.d.ts.map +1 -0
- package/lib/types/client/qoder/useQoderSection.d.ts +97 -0
- package/lib/types/client/qoder/useQoderSection.d.ts.map +1 -0
- package/lib/types/host/common/model-request-control.d.ts +1 -1
- package/lib/types/host/common/model-request-control.d.ts.map +1 -1
- package/lib/types/host/common/request-diagnostics.d.ts +1 -1
- package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
- package/lib/types/host/kimi-code/adapter.d.ts +3 -1
- package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
- package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
- package/lib/types/host/qoder/account-pool.d.ts +137 -0
- package/lib/types/host/qoder/account-pool.d.ts.map +1 -0
- package/lib/types/host/qoder/adapter.d.ts +134 -0
- package/lib/types/host/qoder/adapter.d.ts.map +1 -0
- package/lib/types/host/qoder/adopt.d.ts +146 -0
- package/lib/types/host/qoder/adopt.d.ts.map +1 -0
- package/lib/types/host/qoder/client.d.ts +189 -0
- package/lib/types/host/qoder/client.d.ts.map +1 -0
- package/lib/types/host/qoder/cosy.d.ts +173 -0
- package/lib/types/host/qoder/cosy.d.ts.map +1 -0
- package/lib/types/host/qoder/mapper.d.ts +235 -0
- package/lib/types/host/qoder/mapper.d.ts.map +1 -0
- package/lib/types/host/qoder/model-catalog.d.ts +185 -0
- package/lib/types/host/qoder/model-catalog.d.ts.map +1 -0
- package/lib/types/host/qoder/oauth.d.ts +134 -0
- package/lib/types/host/qoder/oauth.d.ts.map +1 -0
- package/lib/types/host/qoder/routes.d.ts +96 -0
- package/lib/types/host/qoder/routes.d.ts.map +1 -0
- package/lib/types/host/qoder/token-store.d.ts +231 -0
- package/lib/types/host/qoder/token-store.d.ts.map +1 -0
- package/lib/types/host/qoder/types.d.ts +257 -0
- package/lib/types/host/qoder/types.d.ts.map +1 -0
- package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
- package/lib/types/host/workbuddy/mapper.d.ts +1 -0
- package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
- package/lib/types/index.d.ts +5 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/qoder-contracts.d.ts +293 -0
- package/lib/types/shared/qoder-contracts.d.ts.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
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
|
+
|
|
5
41
|
## 0.32.0 - 2026-10-20
|
|
6
42
|
- **[cline-pass] 凭据后端读不出来时不再吞掉 `CLINE_PASS_API_KEY`:环境变量与密钥环是两条独立来源**
|
|
7
43
|
- **现象**:在**没有 Secret Service 的 Linux 环境**(裸 CI runner 最典型)里,`cline-pass` 的账号列表是空的,即使显式导出了 `CLINE_PASS_API_KEY`——于是「随包内置的密钥改用环境变量注入」这条文档化的用法在这些环境里**静默失效**。
|
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
- [GLM(ZCode 账号)线路](#glmzcode-账号线路)
|
|
20
20
|
- [Step Code(Step Plan 订阅)线路](#step-codestep-plan-订阅线路)
|
|
21
21
|
- [Cline Pass(OAuth 订阅)线路](#cline-passoauth-订阅线路)
|
|
22
|
+
- [Qoder(订阅)线路](#qoder订阅线路)
|
|
22
23
|
- [子代理模型授权](#子代理模型授权0215-起032-起强制指定模型)
|
|
23
24
|
- [随包分发的 Agent Preset](#随包分发的-agent-preset)
|
|
24
25
|
- [本机登录](#本机登录)
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
|
|
30
31
|
## 线路总览
|
|
31
32
|
|
|
32
|
-
本插件同时接入 **
|
|
33
|
+
本插件同时接入 **11 条订阅线路**,每条各自独立注册 Provider、独立登录、独立额度卡片。
|
|
33
34
|
|
|
34
35
|
| Provider ID | 订阅 | 协议 | 登录方式 | 模型目录来源 |
|
|
35
36
|
| --- | --- | --- | --- | --- |
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
| `glm-coding-plan` | GLM Coding Plan(ZCode 账号,国区 BigModel / 国际区 Z.ai) | Anthropic Messages + OpenAI Chat Completions(按模型声明) | ZCode 账号 OAuth(浏览器中转轮询)+ 可选导入本机 ZCode 登录态 | 随包目录(源自官方 `zcode-builtin.json`) |
|
|
44
45
|
| `step-code` | StepFun Step Plan 订阅(国区 api.stepfun.com / 国际区 api.stepfun.ai)或开放平台 API Key | OpenAI Chat Completions | **云端轮询**浏览器授权(不开本地回调端口)/ 手工粘贴 Platform API Key / 只读导入本机 `~/.stepcode/auth.json` | **实时运行时发现**(随包表刻意为空) |
|
|
45
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)+ 随包能力兜底表 |
|
|
46
48
|
|
|
47
49
|
> **只有 minimax-code 是硬编码目录,而且是有意的**:该端点的 `GET /v1/models` **未对订阅流量开放**(返回 503 `direct_route_not_configured`),任何「实时目录」都只会是一个必然失败的请求。其取值转录自本机官方客户端的 `config.yaml`。
|
|
48
50
|
>
|
|
@@ -1016,6 +1018,52 @@ access token / refresh token / 授权码 / PKCE verifier**;诊断里提到某
|
|
|
1016
1018
|
|
|
1017
1019
|
与其它线路一样,所有修改状态的路由只接受同源 JSON POST 并校验 `Origin` 与 `Host`。**响应里永远不含 api_key、`Oasis-Token` 或账号密码的原文**——下发的只有「有没有凭据、从哪来、大概什么时候过期、上次读它出了什么问题」。
|
|
1018
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
|
+
|
|
1019
1067
|
## Cline Pass(OAuth 订阅)线路
|
|
1020
1068
|
|
|
1021
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` 用户或某些部署),但不再是默认入口。
|