Aa728848/dsh-chatgpt-subscription

dsh-chatgpt-subscription

仓库尚未提供可用的公开描述。

미분류deepseek-harnessdeepseek-harness-plugin

DSH ChatGPT Subscription

让 DSH(DeepSeek Harness)通过 ChatGPT 订阅使用 Gpt 系列模型的插件。

插件注册 codex-chatgpt Provider(显示名 “Codex(ChatGPT 订阅)”),以当前 Host 用户的 ChatGPT OAuth 登录态访问模型,并在设置页展示账号信息、连接状态与订阅额度。支持 Windows 与 Linux。

目录

  • 线路总览
  • 功能特性
  • 模型目录
  • 环境要求
  • 安装
  • 使用
  • 推理塌缩守卫(熔断,默认关闭)
  • Command Code 线路
  • Claude(订阅)线路
  • GLM(ZCode 账号)线路
  • Step Code(Step Plan 订阅)线路
  • Cline Pass(OAuth 订阅)线路
  • Qoder(订阅)线路
  • OpenDesign(订阅,API Key / 浏览器授权)线路
  • 子代理模型授权
  • 随包分发的 Agent Preset
  • 本机登录
  • 安全边界
  • 插件路由
  • 组件看板卡片(dsh-widget-board)
  • 开发与验证
  • 故障排查

线路总览

本插件同时接入 11 条订阅线路,每条各自独立注册 Provider、独立登录、独立额度卡片。

Provider ID · 订阅 · 协议 · 登录方式 · 模型目录来源 codex-chatgpt · ChatGPT(Plus / Pro / Business…) · Responses · 浏览器 OAuth(localhost:1455 回调),或导入本机 Codex CLI 登录 · 实时 /backend-api/codex/models kimi-code · Kimi 会员 · OpenAI 兼容 Chat Completions(可选 Anthropic Messages) · 设备码(RFC 8628) · 实时 /v1/models + 本地兜底 command-code · Command Code · Anthropic / OpenAI 双轨 · 浏览器 OAuth 或粘贴 API Key · 实时 /provider/v1/models claude-subscription · Claude Pro / Max · Anthropic Messages · 手动粘贴 / loopback 回调 · 实时 /v1/models + 本地兜底 minimax-code · MiniMax Code 编程订阅 · Anthropic Messages · 复用桌面端登录态 或设备码 · 硬编码(见下) antigravity · Google Antigravity · Gemini · Google OAuth · 内置表 glm-coding-plan · GLM Coding Plan(ZCode 账号,国区 BigModel / 国际区 Z.ai) · Anthropic Messages + OpenAI Chat Completions(按模型声明) · ZCode 账号 OAuth(浏览器中转轮询)+ 可选导入本机 ZCode 登录态 · 随包目录(源自官方 zcode-builtin.json) step-code · StepFun Step Plan 订阅(国区 api.stepfun.com / 国际区 api.stepfun.ai)或开放平台 API Key · OpenAI Chat Completions · 云端轮询浏览器授权(不开本地回调端口)/ 手工粘贴 Platform API Key / 只读导入本机 ~/.stepcode/auth.json · 实时运行时发现(随包表刻意为空) cline-pass · Cline Pass · OpenAI Chat Completions · 浏览器设备码 OAuth(WorkOS,账号入池、自动续期);API Key 仅作兼容回退 · 随包表兜底 + 官方 recommended-models / models.dev / 上游网关目录补全 qoder · Qoder 订阅(全球区 qoder.com / 中国站 qoder.com.cn) · Qoder 自有(COSY 签名 + 信封 SSE) · 浏览器设备流(仅全球区)/ 手工粘贴 PAT(两区)/ 只读导入本机 ~/.qoder/.auth/user · 实时运行时发现(COSY 签名 GET)+ 随包能力兜底表 opendesign · OpenDesign 订阅(Go / Plus / Pro / Max) · OpenAI Chat Completions · 浏览器设备授权(AMR 设备流,签发控制 Key + 运行时 Key);粘贴 Cloud 控制台的 API Key 作回退 · 随包表兜底(源自官方公开价目)+ 实时 GET /v1/models

只有 minimax-code 是硬编码目录,而且是有意的:该端点的 GET /v1/models 未对订阅流量开放(返回 503 direct_route_not_configured),任何「实时目录」都只会是一个必然失败的请求。其取值转录自本机官方客户端的 config.yaml。
线路之间互不影响:某条线路的登录失败、额度耗尽或上游故障都不会波及其它线路;它们共用同一套号池内核与账号卡片,但不是同一个池。
⚠️ 合规提示:claude-subscription 使用的订阅凭据转发方式与 Anthropic 现行条款存在冲突(详见该章节开头的「风险须知」),且插件未获官方授权;antigravity 线路的用户亦有账号被限制的公开报告。请自行评估风险。

功能特性

登录与会话

  • Authorization Code + PKCE(S256)登录,一次性 localhost 回调;
  • 支持 token 刷新、登录取消与账号注销;
  • Windows 使用 CurrentUser DPAPI 加密存储 token;Linux 使用当前用户独占的 0600 文件存储;明文不发送给 Client;
  • 设置页会明确显示当前存储类型,并在 Linux 上提示文件存储未额外加密。
  • 本机已经登录过 Codex CLI 的用户可以不再走一次 OAuth(详见「本机登录」):ChatGPT 卡片的账号池里,「登录」按钮旁边还有一个「导入本机 Codex CLI 登录」按钮——两者是同一件事的两种做法,布局与 Claude 页面上那个收编按钮一致。它只看凭据文件是否存在、从不读取令牌,且要你显式点那个导入按钮之后才会读取。导入得到的是一份快照,永不由本插件刷新——ChatGPT 的 refresh token 会轮换,而 CLI 会在原地刷新自己的文件,两个进程各自刷新会互相作废,最后是你被本插件的好意踢出 Codex CLI,而进程内的单飞解决不了这个跨进程竞态。快照过期后请回 Codex CLI 重新登录后再导入;本插件绝不写入或删除 Codex CLI 的任何文件。

模型接入

  • 固定 Codex Responses 地址,支持流式文本、reasoning summary、图片输入与工具调用/结果;
  • 模型目录来自订阅后端(GET /backend-api/codex/models):
  • 按账号缓存并持久化到本地快照,重启后无需等待网络即可渲染选择器;
  • 随包发布的模型表是兜底而非上限:取不到目录时选择器变宽,而不是消失;
  • 目录里本表没听过的模型照常出现,但未声明的能力按保守值回落,不会凭空多出图片或思考档位;
  • 只有目录确实列出的模型会进入选择器——它是「这个账号能调什么」的权威;
  • 请求带 `openai-beta: responses=experimental`:
  • 该订阅后端在 beta 标志下提供,官方 Codex CLI 一直发送这个头;
  • 缺它时请求面不同:当前后端宽容,但这正是收紧后会变成 400/403 的那一行;
  • 读取参数默认值与官方 Codex 一致(依据订阅目录 GET /backend-api/codex/models,与官方随包的 codex-rs/models-manager/models.json 同源):
  • 推理摘要默认不发:目录里每个模型都声明 default_reasoning_summary: none,官方以此为准。此前本插件在未配置时发 summary: auto,而摘要属于计费生成,等于每一轮都多花一块官方从不花的额度;现在默认省略该字段,用户显式选择 auto / concise / detailed 时照旧发送,选「无」也改为省略字段而不是发 summary: none(后者是让后端先生成再丢弃)。
  • 输出详细程度默认发目录值 `low`:官方客户端每轮都发送模型目录里的 default_verbosity(当前全部为 low)。此前本插件在未配置时整个不发 text 字段,于是服务端套用隐含的 medium——一个从未打开过该设置的用户,会拿到比官方客户端更啰嗦、也更贵的回答。现在未配置时按目录发 low,显式选择仍以用户为准。
  • 目录里没有记录的模型不猜:仅对该表声明了 supportsOutputVerbosity 的模型发送 text,避免把一个模型可能拒收的字段强加给它;
  • 多轮续传,避免每轮重发整个历史:
  • 每个会话发送稳定的 prompt_cache_key,让后端复用提示前缀;
  • 回传后端在响应头给出的 x-codex-turn-state,让它续接该轮;
  • 后端不再下发该头时立即停止回送——不重放过期值;
  • 对话报文绝不发送 `max_output_tokens`:订阅版 Responses 端点在部分账号/模型上直接以 400 Unsupported parameter: max_output_tokens 拒收该字段(#29,实测 gpt-6-sol / gpt-6.1-sol),官方 CLI 的请求结构里也没有这个字段;输出长度由服务端默认值决定,撞上限仍以 max-tokens 结束原因上报(详见「模型目录」);
  • 单次请求的图片负载有上限(2 MiB,可用 `DSH_CODEX_MAX_IMAGE_BYTES` 覆盖):历史里的每张图片每轮都会被重新内联,此前这条线路没有任何总量约束,长会话的请求体随读图数量单调增长,超过传输层能接受的体积后整轮失败,而报错只是一句没有信息量的「Codex could not be reached.」(#50)。现在按最旧优先把超出预算的图片替换为一条明确的占位文本,请求体因此有界,且模型知道那张图已经不在了;
  • 预算是在构造好的请求体上执行的,因为图片有两种到达方式:粘贴的附件是 { type: 'image', attachment } 块,而工具读取的本地图片是 `` 链接、由映射器在构造时才取回并内联。只扫消息块的方案看不到第二种;
  • 数字取自 #50 的实测(≤3 MB 全过、≥4 MB 全挂),而不是照搬别的线路:Kimi 的 1.5 MB 是按它自己的 2 MB 请求上限定的,用在这里会静默删掉本端点能接受的图片;MiniMax 的 16 MB 是按 64 MB 请求体定的,这里没有那个上限;
  • 只约束图片这部分。纯文本本身就超出传输层体积的请求仍然会被拒绝,任何图片预算都救不了它;
  • 请求路径的传输层失败会带上原因链:此前 request() 把任何失败都报成同一句 Codex could not be reached.,而 DSH 只持久化 {message, code},于是 NGHTTP2_ENHANCE_YOUR_CALM、ECONNRESET、UND_ERR_SOCKET 在界面和日志里长得一模一样——上面那条体积问题正是因此无法自助定位。现在与流中段断流(streamFailure)一致,把 cause 链拼进 message;
  • Antigravity(Gemini / Claude)线路同样接受图片输入:DSH 以 { type: 'image', attachment } 下发的粘贴图片会经附件服务读出字节并按 Gemini inlineData 发出,读不出的图片降级为一条可见的说明文本而不是被静默丢弃。单次请求的图片 base64 负载超过 12 MiB 时,最旧的图片按上游同款占位文案替换为文本,避免整条请求被体积上限拒绝;
  • 原样转发 DSH 暴露的工具 schema;命令工具兼容 pwsh / powershell、bash、sh 与 shell,并按 PowerShell、Bash 或 POSIX sh 注入对应说明;
  • 429/5xx 由 DSH retry policy 接管;401 只强制刷新并重试一次,支持 AbortSignal;
  • Codex、Code review 及上游返回的额外窗口额度,支持 Credits、月度消费控制与 reset credits 展示;60 秒缓存、15 秒上游节流并遵守 Retry-After;
  • 提供 ChatGPT 订阅侧 Codex 搜索 provider,可在设置页切换 DSH 默认搜索或 Codex 订阅搜索;
  • 新增 codex_image_generate 工具,生成图片后通过 DSH 附件系统保存并在会话中渲染;
  • 可选 composer 快捷用量徽标,按当前 codex-chatgpt 模型显示最紧张窗口的剩余额度。

Command Code 线路

  • 注册 command-code Provider,使用 Command Code 的 Provider API 与账户 API;模型 id 决定线路:claude-* 走 Anthropic Messages(/provider/v1/messages),其余模型走 OpenAI Chat Completions(/provider/v1/chat/completions),因为该 API 会拒绝把模型发到格式不符的端点;
  • 浏览器登录复刻官方 CLI 的回环回调契约(127.0.0.1:5959 起顺延,/callback 接受 Studio 页面的跨域 POST),也可在设置页手动粘贴 API Key;两条路径都先用 /alpha/whoami 验证再加密保存;
  • 模型目录取自公开的 /provider/v1/models,每个模型的 context_length 作为默认上下文窗口,可逐模型覆盖;
  • 模型能力逐模型查表(src/host/command-code/model-catalog.ts,转录自官方 CLI 的模型注册表):是否接受图片输入、支持哪些思考档位由该表决定,未知模型回落纯文本;
  • 图片能力不能靠厂商/模型名前缀推断——同厂商内部就会自相矛盾:deepseek/deepseek-v4.1-flash 与 deepseek/deepseek-v4-flash-vision-exp 支持图片,而 deepseek/deepseek-v4-flash、deepseek/deepseek-v4-pro 不支持;z-ai/glm-5.3-flash 支持而 zai-org/GLM-5.3 不支持;
  • 额度与用量来自账户 API 的账单/用量线路,任一条失败不影响其余;
  • 瞬时失败按 DSH retry policy 有界重试:command-code 路由显式声明 normal 策略(最多 3 次,1.5s 起指数退避、15s 上限、0.2 抖动),覆盖 RATE_LIMIT、SERVER、TIMEOUT、TRANSPORT;
  • 上游模型供应商临时不可用(502/503/504/500,典型响应体是 "{\"error\":{\"type\":\"server_error\"}}")被归类为 SERVER 并自动重试;
  • 429 会带上上游的 Retry-After 让退避按对方的节奏走;401/403 与 ABORTED 明确不重试;
  • 模型勾选(含线路标签)、思考深度、上下文窗口覆盖与额度在「设置 → 订阅服务 → Command Code」标签页中配置,输入框右侧另有额度胶囊。

Kimi Code 线路

  • 注册 kimi-code Provider,接入 Moonshot 的 Kimi Code 订阅(https://www.kimi.com/code)。它与 Moonshot 开放平台(pay-as-you-go)是两套互不通用的系统:订阅的模型接口是 https://api.kimi.com/coding/v1,凭据只来自订阅 OAuth;把开放平台的 key 或 base URL 用在这里会被判为 401 Invalid Authentication;
  • 登录用 RFC 8628 设备码流程(auth.kimi.com):设置页点「设备码登录」后直接展示用户码与一次性链接(浏览器会自动打开),装好后无需回调端口、无浏览器环境也能手工完成;slow_down 会按 RFC 调宽轮询间隔,设备码过期会自动重新申请而不是直接失败;
  • 访问令牌到期前按 max(300s, expires_in×0.5) 自动续期,同进程并发调用共用一次刷新;被拒的 refresh token 进入冷却并提示重新登录;
  • 瞬时失败按错误类别重试(src/host/kimi-code/adapter.ts)。会重试(有界退避,最多 3 次,1.5s 起步、15s 上限、0.2 抖动,并遵守上游 Retry-After):
  • 上游模型供应商临时不可用(典型是 502 "{\"error\":{\"message\":\"Upstream model provider is temporarily unavailable. Please try again in a moment.\",\"type\":\"server_error\"}}");
  • 真正的 429 背压(too many requests / engine is currently overloaded)、连接失败与流停滞;
  • 不重试,直接给出可操作提示(换模型 / 降上下文 / 等窗口重置 / 重新登录):配额耗尽型的 429、403 的账号额度上限、401 里的套餐权限不足、400 请求格式错误;
  • 服务把这些含义压进同一个状态码,因此分类读响应正文而不只看状态码;
  • 模型目录为订阅侧的六款模型:k3(1M 上下文,需 Allegretto+;Moderato 上限 256K,故默认按 256K 计算,可用上下文覆盖升到 1M)、k3-256k、kimi-for-coding(K2.8 Preview)、kimi-for-coding-highspeed(约 6× 速度、3× 额度)、k3.1-pro(K3.1 Pro,默认 256K、可覆盖到 1M,需 Moderato 及以上,1M 需 Allegretto 及以上)与 k3.1-pro-highspeed(K3.1 Pro Highspeed,1M 上下文、约 2 倍配额,需 Pro / Allegretto 及以上),运行时以 GET /v1/models 为准;
  • K3 行为按其官方文档实现:
  • 思考档位只发 low / high / max(其余写法收敛映射,未知档位不发送);关闭思考发 thinking:{type:"disabled"},开启时发 thinking:{type,effort,keep:"all"};
  • 开启思考时每条 assistant 消息都回传 `reasoning_content`(无推理则回传空串——服务要求的是空值而非省略,否则 400);
  • 不发送 temperature(采样参数按模型固定,显式值会报错);
  • 工具调用 id 截断到 64 字符;
  • stop 按上限裁剪为最多 5 条、每条 ≤32 字节,超长整条丢弃(截断的停止串会在错误位置终止生成);
  • K3.1 Pro 只支持思考模式:关闭思考(none)会被服务端以 400 拒绝,所以它的档位只有 low / high / max、默认 max;「默认思考档位」下拉只列出勾选模型真正声明的档位,任一勾选模型允许关闭思考时它才出现,被拒时卡片上直接写明原因;
  • 本线路对全部模型使用 OpenAI 兼容 chat completions(与官方 agent 的 type = "kimi" 一致):K3.1 Pro 系列与其它模型同一协议、同一端点(/v1/chat/completions),Anthropic Messages 只在实时目录为某个 id 声明时启用;视频输入按官方型号表声明走,K3.1 Pro 系列可用;
  • K3 的长思考不会被截断:输出上限跟随上下文窗口(保留 4096 余量),因为 reasoning_content 计入输出,固定 32K 会把 max 档的长推理中途截断并返回 length;调用方已知 prompt 规模时上限会被下调到放得下,未知时不做猜测;
  • 请求体上限按模型分档:1M 档 8 MiB / 256K 档 2 MiB,本地即拒绝:该端点最常见的 400 是 total message size N exceeds limit 2097152,但 2,097,152 来自官方文档的通用错误示例,从未针对 1M 上下文档位实测——1M 档的真实会话(中文正文 3–4 MB)会被本地守卫提前拦下,而宿主压缩也救不回来(压缩请求自己同样超限);官方 kimi-cli 全仓库没有字节守卫,说明这个上限至少不是客户端必需的。现在按模型档位:k3 / k3.1-pro / k3.1-pro-highspeed / kimi-for-coding = 8 MiB(8,388,608,待实测的上界),k3-256k / kimi-for-coding-highspeed = 2 MiB(保持原值,窗口与上限本就对齐)。8 MiB 的依据是「1M 窗口的正文大小」:一个 1M token 的请求体按英文约 4 MB、按密集中文约 4.5 MB,8 MiB 留出近一倍余量,所以字节上限不会成为 1M 会话的实际瓶颈;真正先到的是模型自身的 token 窗口——服务端回 Your request exceeded model token limit,该措辞已被 common/context-overflow.ts 归一到 CONTEXT_WINDOW_EXCEEDED,压缩与重试照旧生效。拦截仍按真实序列化体积,并按「循环实际做了什么」给出处方(有图可丢但丢了也补不上缺口 / 图已丢完仍超限 / 本来无图,三种情况文案不同,不再声称发生了没发生的省略);DSH_KIMI_CODE_MAX_BODY_BYTES 仍是全局强制覆盖、优先级最高,确有实测上限的部署(前置代理/网关)用它指定该档上限,取值非正整数时回落到该模型的默认值而不是关闭守卫;带视频的请求走 64 MiB 档,不受该变量影响;超限时报的是可恢复的溢出码(CONTEXT_WINDOW_EXCEEDED):DSH 会对该码强制压缩本轮历史并重试同一步,所以长会话的表现是「压缩一次后继续」,而不是整轮失败——官方 kimi-cli 从不特殊处理 2097152,它靠 85% 提前压缩避开,详见下文「上下文窗口」那条;
  • 缓存是自动的,且无法手动干预:
  • Kimi 按请求内容哈希命中前缀缓存。实测 prompt_cache_key 与 Anthropic cache_control 标记均被忽略(设与不设、同 key 与异 key 命中的是同一缓存),设备 id 与协议切换也不影响;
  • TTL 实测在 300–1800 秒之间,按 256 token 对齐;/messages 与 /chat/completions 共享同一缓存;
  • 真正决定命中率的是内容稳定性:同一会话内 system 或工具列表一旦变化会使整个前缀缓存失效(实测归零)。因此应保持工具集合稳定、把新增内容追加在末尾;
  • 卡片会显示滚动命中率,方便验证效果;
  • 写入时长可手动选择(设置页):prompt_cache_options(OpenAI 兼容线路)或顶层 cache_control(Anthropic 兼容线路)控制缓存写入时长,与上面的「无法干预」不矛盾——那一条说的是缓存身份(由内容前缀哈希决定),TTL 管的是另一回事;
  • 档位在首次写入时锁定、之后无法改写,命中时按原 TTL 续期(官方口径:不传时按 5 分钟档写入,只有 5m / 1h 两档);1 小时写入约为 5 分钟的两倍价,只有同一前缀会在一小时内被反复读取才划算。不选择时不发任何缓存字段;
  • 设置卡的缓存区块把写入单独列成一行(总量 + 5 分钟档 + 1 小时档):写入是独立计费项,折进「新处理 tokens」会让命中率说错话;某个分档是 0 时照实显示 0——响应头缺失只表示服务端没回报该档,不等于那里确实没写;
  • 视频输入可用(k3、kimi-for-coding、k3.1-pro):
  • DSH 的模态词表只有 text/image,但它是可合并扩展的接口,本插件用 TypeScript 模块增强把它扩到 video(未改动 DSH 任何代码),因此视频走 DSH 真实的能力通道,而不是只能显示在提示里;
  • 适配器把视频映射为服务文档的 {type:'video_url',video_url:{url:'data:…'}}。视频与图片各有独立预算(图片 1.5 MB、视频 48 MiB base64,最旧优先省略),请求体校验只在确实带视频时才放宽到 64 MiB;
  • 图片预算可用 DSH_KIMI_CODE_MAX_IMAGE_BYTES 覆盖(取值非正整数时回落到默认值)。视频预算没有环境变量出口,与 MiniMax 线路一致;
  • 以下情况会把视频降级为明确的文字说明而不是猜字段发出去:k3-256k 只接受图片、文档白名单外的容器、以及未文档化视频内容块的 Anthropic 线路;
  • `dynamically_loaded_tools`(仅 K3)已实现:K3 接受消息级工具声明(messages[].tools),可在会话中途用「无 content 字段的 system 消息」注入完整工具定义。官方把「保持顶层 tools 字节稳定」列为该特性的目的之一——中途修改/删除已发出的声明会使缓存从该点起失效,而在末尾追加不影响已缓存前缀,所以这是提升缓存命中的正道。声明按请求重发(服务端不保留),且仅在模型声明该能力时发送;
  • 额度卡片区分 5 小时 / 7 天 / 月度(会员共享池)/ 月度(Kimi Code 池) 四个窗口并显示重置时间,另可显示加油包余额;设置页为「设置 → 订阅服务 → Kimi Code」标签页,对话输入框右侧有该线路的额度胶囊。

MiniMax Code(编程订阅)线路

  • 注册 minimax-code Provider,接入 MiniMax Code 编程订阅。它与 MiniMax 开放平台(按量计费的 API Key)是两套互不通用的系统:
  • 订阅的模型接口是 Anthropic Messages 协议(https://agent.minimax.cn/mavis/api/v1/llm/v1/messages);
  • 只用 `authorization: Bearer ` 认证——实测 x-api-key 一律返回 401 "{\"code\":401,\"message\":\"token is required\"}",因此本线路没有 x-api-key 回退分支(回退只会在每次请求上白花一个往返);
  • 复用桌面端登录态,而不是让你再登录一次:凭据就是 MiniMax Code 自己写的 ~/.minimax/auth///mcode-public/auth.json。
  • 只读优先:仍在有效期内(且距离到期还有 5 分钟以上)的令牌原样使用;
  • 进入续期窗口才轮换,并走原子替换(临时文件 + rename),失败或中断都让原文件保持逐字节不变;
  • 不创建、不删除、不等待 auth.lock(那是桌面端自己的刷新锁,第二方碰它就可能打断官方客户端的刷新);
  • 两种登录来源互不覆盖:
  • 桌面端的 auth.json 归桌面端所有,本插件只读;若本机没有它,才在设置页用 RFC 8628 设备码流程(PKCE S256)登录,凭据存到本插件自己的 $DSH_HOME/storages/minimax-code-credentials.json;
  • 登出只作用于本插件自己那份凭据:桌面端的登录态会被续期写回(只读优先),但绝不撤销、绝不删除,登出请求对它会被拒绝并说明原因(撤销它等于把你从正在跑的 MiniMax Code 里踢下线);
  • 区域是凭据属性:cn 用 account.minimax.cn / agent.minimax.cn,global 用对应 .io 域名。两个区域的目录都会被探测,因此国际区账号不会因为没有国区文件而「未登录」;读回凭据时以记录里存的区域为准,而不是拿默认区域覆盖它;
  • 模型目录是硬编码的,而且必须硬编码(src/host/minimax-code/model-catalog.ts):
  • 该端点的 GET /v1/models 未对订阅流量开放(503 "{\"errorCode\":50115,\"errorReason\":\"direct_route_not_configured\"}"),所以任何「实时目录」都只会是一个必然失败的请求;
  • 四款模型:M2.7 / M2.7 HighSpeed / M3 取自本机 ~/.minimax/config.yaml 的模型表;M3.1 Flash 是 2026-10-19 换上的正式版 id(MiniMax-M3.1-Flash,去掉了 -Preview 后缀),取自 MiniMax 自己的 Model Invocation 页 Supported Models 表并在该订阅端点上实测(正式 id、output_config.effort 的 low/max、内联图片)。无论哪一条,都不从不存在的接口推断;
  • 思考形态按模型表三态实现,且一律走 `output_config`,不发 `thinking` 对象(该字段从未被文档确认,发出去只是一种形状的猜测):M2.7 系列恒定开启且没有档位(不发送任何字段);M3 是开关二态(关闭时 output_config.effort: 'none');M3.1 Flash 强制开启并可指定档位(output_config.effort 取 low/medium/high/xhigh/max,省略即 max)。未知或越档的取值一律回落到该模型目录声明的默认档,不会把模型没有声明的档位发出去;
  • 视频已在能力声明里,并且真的能发出去:M3 与 M3.1 Flash 的 inputModalities 都是 ['text', 'image', 'video'](model-catalog.ts),线上形状取自实测——{ type: 'video', source: { type: 'base64', … } } 被该端点接受并解码(同一段实测也否掉了 data-URL 字符串那种封装,它会在 : 处按裸 base64 解码失败)。能不能读到字节是宿主注入的接缝:AttachmentVideoReader(adapter.ts:372,类型在 src/host/common/video-request.ts)由宿主提供,DSH 自己的附件服务只存图片,所以视频只能来自本部署里某个真实的生产者;宿主不提供读取器、或某一条读不出来时,resolveRequestVideos 把它解析成 unavailable,映射器(mapper.ts:416)才退化成一段说明文本——modalities.ts 的 videoOmissionText 按 4 种 reason 给出各自不同的措辞(unsupported-model / unsupported-container / unreadable / unsupported-wire),让模型知道「有这段视频、为什么没带上」,而不是对着空白回答。降级发生在单个片段上,不发生在声明上:声明的依据是接缝存在且线上形状实测可用,不是「任何情况下都一定能读到」;
  • 失败按 8 级判决分类,判词决定重试与号池动作(classifyMinimaxFailure,adapter.ts:388-470;读的是原始正文而不是 summarizeFailureBody 抽出的那一个字段,因为同一个判词在两种信封里放的位置不同):
  • 上下文超限——isHttpContextOverflow 先判,CONTEXT_OVERFLOW_CODE,终局、不归账号;
  • 账号级额度耗尽——`403` 或 `429` 都可能落到这一类,判据是正文点名了一个用尽的窗口,与状态码无关:isSpentWindow403(adapter.ts:355)管 403,429 分支(adapter.ts:425-431)用同一个谓词问同一个问题,两条都返回同一个 quotaExhaustedFailure(adapter.ts:368:PROVIDER_ERROR / retryable: false / accountScoped: true)。判据本身是两个臂(先问上游用量限额的数字码、再问措辞表兜底;这个先后只是求值顺序,不代表这条路由上真正会开火的是前者):namesUsageLimitStatusCode(adapter.ts:311)读两种信封里的上游码(平台信封 base_resp.status_code、客户端信封 errorCode,并额外往流内错误体 { error: … } 里多读一层),命中 USAGE_LIMIT_UPSTREAM_STATUS_CODES = {2056, 2067} 即算;非 JSON 或没有该字段的正文回落到措辞表 QUOTA_EXHAUSTED_PATTERNS。两个臂汇进同一个判决 namesSpentWindow = namesUsageLimitStatusCode || 措辞表(adapter.ts:334)——它们不是两套规则,而是同一个问题的两种承载方式:一句中文没有码,一个裸码没有句子。但要把定位说准:数字码臂在这条 Messages 路由上目前不会开火,它是跨信封的前向兼容臂(哪天服务真的在 Messages 路由上带出 2056/2067/errorCode,它就会生效),不是这条路由当前的主要判据。2026-10-20 的活体探针(scripts/probe-minimax-capabilities.probe.ts,原始证据 docs/minimax-code-probe-capabilities.md)把这条路由的错误信封量了个遍:端点上只有两种信封,没有一种带 `base_resp`——① 请求被拒 400 是 {"type":"error","error":{"type":"invalid_request_error","message":"…"}}(参数类错误上多一个 request_id),没有任何数字字段;② 认证被拒 401 是 {"code":401,"message":"auth failed"},顶层 `code` 是这条路由唯一的数字字段。base_resp.status_code 那个信封属于这条线路的别的路由(配额 coding_plan/remains、Files API、reset-card),不属于 Messages。连探针抓到的 2013 也不是字段:它只以文本形式嵌在 error.message 的括号里(… does not support max tokens > 524288 (2013)),而且是通用「参数非法」码——超限的 max_tokens、空 body、畸形 JSON 全都归它;本仓库在 reset-card 与 Files API 上另有语义,绝不拿它当额度判决。所以在 Messages 路由上当前真正 operative 的那一条是措辞表:数字码臂留在这里,是为了不让一个将来可能带上数字码的信封被当成措辞去猜,而不是因为这条路由现在会给码。它必须排在下面的凭据分支之前,因为 401/403 的正文在这条线上是同一句 token is required,光看状态码分不出「窗口用尽」与「凭据被拒」。号池据此冷却到窗口重置(未命中周窗口词冷却 5 小时,命中 WEEKLY_QUOTA_WINDOW_PATTERN 冷却 7 天,见 adapter.ts:172-184),且刻意不采信 Retry-After——等 N 秒不会让一个用尽的窗口重新可用;
  • 凭据失效——401/403 里不属于上面的,INVALID_CREDENTIAL,终局且归账号,但走 markAuthFailed 而不是 markCooldown;这条线的 401 正文在「没发凭据」和「凭据被拒」两种情况下是同一句 token is required,所以提示直接指向重新登录;
  • 纯背压 429——没有点名用尽窗口的 429(数字码与措辞两个臂都没命中),RATE_LIMIT,可重试且不归账号(同一套餐后面的每个账号都会被同样拒绝,冷却它只会把号池抽干);
  • 5xx——SERVER,可重试、不归账号;
  • 400——PROVIDER_ERROR,终局:请求本身不被接受,重发一次也不会被接受;
  • 其他状态码——PROVIDER_ERROR,终局,正文为空时提示「无响应」;
  • 401 另有一次性补救(合并下方一条):第一次 401 会强制续期一次并原样重试同一个请求体,重试再被拒才是终局;
  • 可重试的四类(RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT)走 DSH retry policy 的有界退避:最多 3 次,1.5s 起步、15s 上限、0.2 抖动;终局类不重试——重试只会推迟用户真正需要看到的那句提示;
  • 401 会强制续期一次再重试:本地时钟看着还有效、服务端却拒收的令牌(在桌面端被撤销、时钟偏移、桌面端已轮换)与「真的需要重新登录」在状态码上无法区分,所以第一次 401 会强制刷新一次并原样重试同一个请求体;重试再被拒才是终局;
  • 刷新令牌是单次使用的,因此续期提前 5 分钟、并全进程共用一次轮换:
  • 这是「每小时自己掉线、然后要求重新登录」的根因。access token 实测只有 1 小时,而原先的续期阈值是「到期前 60 秒」——等于每次都在令牌已经会被拒收的那一瞬间才去轮换;
  • 并发请求(状态卡轮询、签到、用量读取、模型请求)会各自拿同一枚刷新令牌去换,除第一个之外全部拿到 invalid_grant,而这条终局判定会被记成「该账号需要重新登录」并写进号池(重启也在);
  • 现在:① 续期窗口 PRE_EXPIRY_REFRESH_MS 为 5 分钟,轮换发生在服务端还没有拒收之前;② 同一份凭证的轮换按身份(`recordKey` / `loginEpoch`)单飞,后到的调用者要么加入在途轮换、要么采纳结果,绝不二次花费同一枚令牌;
  • ③ 轮换先看 storage 再相信终局判定——磁盘上的凭证若已前进(别人刚成功轮换,或桌面端自己刷新过),直接采纳,不记 tombstone、不报「重新登录」;
  • ④ 号池行上遗留的过期标记会在下次读取或请求时自动核对并清除;⑤ 卡片补上了「重新登录」按钮(onRelogin 此前未接线);⑥ 响应未返回新 refresh token 时沿用旧值,不再当作失败;
  • 请求体上限是本线路自己的 64 MB,不是 Kimi 线路的 2 MB(本条为修复项,详见 CHANGELOG):
  • 原缺陷:守卫直接复用了 Kimi 线路的 MAX_MESSAGE_BODY_BYTES(2,097,152),理由写着「两条线路上游都是同一族端点」。2,097,152 与 `total message size N exceeds limit 2097152` 是 Kimi Code 自己文档里的网关上限(见其错误参考),MiniMax 没有任何文档这样规定;
  • 代价:真实会话在 2,098,045 字节时被本地拒绝——请求根本没发出去,这一轮就失败了。该数字离 2,097,152 只差 893 字节,而超过 2 MB 本身不代表这个路由发不出去;
  • 更自相矛盾的地方:本线路的模型目录给 M3 标了 512K 上下文(可选 1M)、单张图片 10 MB——一个 2 MB 的整包上限与「单图 10 MB」根本不能共存,一张合法图片就能单独触发它;
  • 现在:上限改为本线路自己的 DEFAULT_MAX_MESSAGE_BODY_BYTES(64 MB,MiniMax 对带媒体请求自己公布的请求体量级),普通会话永远不会被本地拒绝,守卫只拦真正的失控请求(如每轮追加数 MB 工具结果的死循环),且仍在花掉连接之前拦下并给出同样的可操作提示;
  • 确有实测上限的部署(前置代理/网关)可设 DSH_MINIMAX_CODE_MAX_BODY_BYTES 指定;取值非正整数时回落到默认值而不是关闭守卫;
  • 同一个错误在图片预算上又犯了一次,而且更隐蔽(已一并修复):本线路调用的是 Kimi 的 offloadOldestRequestImages,其预算是 Kimi 的 1,500,000——那是为塞进 Kimi 自己的 2 MB 请求体上限而定的数字。MiniMax 的模型表允许单图 10 MB 原始字节(base64 约 13.3 MB),超出近 9 倍,于是一张普通截图就被换成占位文本;
  • 比拒绝更糟:omit 是静默替换——没有报错、没有计数,模型是在一个「图根本不存在」的对话上作答的,而现场没有任何线索指向原因。现在预算是本线路自己的 DEFAULT_MAX_REQUEST_IMAGE_BYTES(16 MB,装得下模型 10 MB 单图上限并留出余量,且远在 64 MB 请求体上限之内),可用 DSH_MINIMAX_CODE_MAX_IMAGE_BYTES 覆盖;
  • 改法:共享的是机制(度量、按最旧优先丢弃、不动持久历史),不是数字。offloadOldestRequestImages(options, maxBytes) 现把预算变成参数,缺省仍是 Kimi 的值,其余线路行为逐字节不变;
  • 守卫的形状仍与 Kimi 共用,数字不共用——字节上限是上游网关的属性,不是什么通用常量;
  • 额度面板有两套用量端点,第一轮打错了那一套:
  • 平台端点 /v1/token_plan/remains(api.minimax.cn / api.minimaxi.com / api.minimax.io)只接受平台 API 密钥:mcode-public 登录态在四种认证写法(Bearer / 原始 / x-api-key / 两者都带)下全部被拒,且是 HTTP 200 + `base_resp.status_code: 1004`——不是 401,很容易被误读为成功;
  • 官方 mmx CLI 能用该端点,是因为它的 OAuth 是平台登录,与本线路的 mcode 登录是两套身份;
  • mcode 自己的端点在官方客户端源码里(packages/tui/src/account/matrix-account-client.ts):GET https://agent.minimaxi.com/v1/api/openplatform/coding_plan/remains(国际 agent.minimax.io),路径与平台端点完全不同。本插件用的就是这条(本线路的 agent.minimax.cn 优先、官方主机兜底);
  • ⚠️ 但它带 `yy` / `x-timestamp` / `x-signature` / `User-Agent: MiniMaxCode` 四个「官方客户端标识头」——官方注释自己写明这些是「把请求标记为来自 MiniMax 第一方客户端」的字面量;
  • 本插件 types.ts 已明确决定不伪造官方客户端身份(伪造既失信,也可能是封号理由),因此默认不发,宁可拿到一个拒绝让卡片如实说明;
  • 确实要数字的用户可设 DSH_MINIMAX_CODE_QUOTA_ATTRIBUTION=1 显式选择该取舍;
  • 用量端点不在 MiniMax 的 API 文档里(文档只说「用量显示在控制台的用量条上」,这大概就是最初判定「没有端点」的原因),它来自官方 CLI——mmx quota show。返回的每个 model_remains 行带两个窗口(5 小时滚动 + 每周),卡片因此有两根条,各自显示重置时刻与「已用/总量」;
  • 三个必须照抄官方实现的解析细节:
  • ① ***_usage_count 的语义是模糊的(老响应是「剩余」、新响应是「已用」),官方用显式百分比消歧——照抄,否则进度条会反过来**;
  • ② 周窗口带显示倍率(返回的是百分比 × weekly_boost_permille,可超过 100%);
  • ③ status: 3 通常表示「不限量」,但两个总量都为 0 时它表示「当前套餐不含该模型」——渲染成不限量会凭空许诺额度;
  • 三处降噪防护:① base_resp 非 0 视为凭据被拒,只试一台就停并记住 30 分钟;② 其它失败按 unreachable 区分、记住 10 分钟;③ /status 只读缓存、绝不在轮询里发网络请求;
  • 读用量永远不影响对话:独立只读路径,失败只让卡片换一句话;
  • 设置面与其余各线路逐项对齐(该 PR 最初缺失这一整块,用户明确指出):
  • ① 「启用此供应商」总开关——关闭后适配器不暴露任何模型(包括已勾选的),因为「关掉」如果还能用就等于没关;
  • ② 模型勾选——只有勾选的模型进入对话页的模型选择列表,另有「全选 / 全不选」;
  • ③ 模型上下文窗口覆盖——每个已勾选模型可填自定义容量(接受 1M / 512K / 200000),「恢复默认」以 null 语义在 host 侧删除该键而不是写入 0;
  • ④ 默认思考深度——全局档位只在模型确实列了该档时生效,否则回落到该模型自己的默认档(广告一个模型会拒绝的档位只会让服务端拒掉请求),且「关闭思考」只对可关闭的模型生效;
  • ⑤ 号池管理(见下条);
  • 目录仍然硬编码,但「这一安装当前怎么用它」是用户设置:两者分开存储(目录随代码发布、设置随用户数据保存),因此一份用户数据不会看起来像一条已发布的目录项;
  • 号池与多账号登录:复用与其它线路同一个号池内核(src/host/common/account-pool.ts)与同一张共享账号卡片,具备顺序耗尽 / 轮询 / 粘性调度、429 冷却换号、设为主账号与备注。身份键不是令牌:桌面端凭据用它自己的 recordKey(同一个记录槽每次轮换都是同一账号,因此在池里原地更新),插件自持凭据用 loginEpoch(每次设备码登录就是一次独立会话)——用刷新令牌做键在每次轮换后都会把同一账号看成新账号;
  • 桌面端登录态可被显式「导入」为号池账号(POST /accounts {action:'adopt'}):只读取、只新增一行,不改动官方客户端的任何文件;重复导入按记录槽身份原地更新,不会产生重复账号。在设置页新发起的设备码登录会自动加入号池(pollWebLogin 的 onSave 钩子)——没有这个钩子,新登录只会写进镜像文件而号池看不见,这正是「多账号登录没配好」的根因;该钩子的失败不会让登录失败,因为凭据此时已经落盘。

Claude(订阅)线路

  • 注册 claude-subscription Provider,以 Claude Pro / Max 订阅的 OAuth 登录态访问 Claude 模型(Anthropic Messages 接口),不使用 API Key、也不按量计费;
  • ⚠️ 风险须知(插件不设确认步骤,但事实不变):Anthropic 现行条款明确写明不允许第三方应用提供 Claude.ai 登录、也不允许代用户经 Free / Pro / Max 凭据转发请求,并保留不经预告的执法权;已有开源项目被下架、有账号因此被限制。本插件未获 Anthropic 任何授权或认可,使用风险由使用者自行承担。此前版本在卡片上设有确认步骤与配套主机门禁,两者均已移除;移除的只是那一步交互,不改变上述事实;
  • 这不是抄一个 token 就能用:订阅令牌要求请求完整模仿 Claude Code 的身份,否则会被服务端拒绝或分类判别。这些都在代码里显式实现并有测试锁定:
  • Authorization: Bearer 且 `x-api-key` 必须缺省;
  • user-agent: claude-cli/、x-app: cli;
  • anthropic-beta 至少含 oauth-2025-04-20 与 claude-code-20250219;
  • system 的首个块必须是 Claude Code 身份声明;
  • OAuth 用 PKCE,且 state 与 verifier 独立生成。这一点特意不照抄参照实现:本机参照实现把 PKCE verifier 直接当作 OAuth state(state: verifier),那会把本应保密的 verifier 写进授权 URL、地址栏、浏览器历史乃至剪贴板。本线路是两次独立随机抽样,并有测试断言授权 URL 中不出现原始 verifier;
  • 两条登录路径,模式在流程开始时确定且不可中途切换:默认是手动粘贴(授权页把码显示在屏幕上,粘贴 #;也容错接受整条重定向 URL)。可选 loopback 回调,绑定 127.0.0.1 并顺序探测可用端口(Windows 的保留端口段会让固定端口绑定失败)。端口探测失败降级为手动而不是报错。切换模式等于作废当前流程并重新发起——授权码与签发它的那次请求的 redirect_uri 绑定,这是该流程最常见的失败;
  • 一次授权只兑换一次:浏览器回调与手动粘贴可能同时到达,用 compare-and-set 保证只有一方发起兑换(另一方得到「正在处理中」,已结算的流程得到 410 且不做任何兑换)。错误的 state 只拒绝那一个请求,不会终止正在进行的合法登录。回调服务器只绑 loopback、只应答 /callback、只接受本机来源;
  • 刷新是单飞的:并发请求共享同一次刷新。否则到期瞬间的一批请求会各自轮换刷新令牌,除第一个之外全部作废(上游会给出终局判定)。刷新令牌轮换后先读回校验再落盘;
  • 模型能力逐模型查表(`src/host/claude/model-