@eddyskywalker/dsh-chatgpt-subscription 0.13.2 → 0.15.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 (39) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +76 -8
  3. package/lib/client.js +183 -115
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +1110 -76
  6. package/lib/types/client/CodexSubscriptionSection.d.ts +1 -1
  7. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  8. package/lib/types/client/ProviderHubSection.d.ts.map +1 -1
  9. package/lib/types/client/api.d.ts +12 -0
  10. package/lib/types/client/api.d.ts.map +1 -1
  11. package/lib/types/client/claude/ClaudeSection.d.ts.map +1 -1
  12. package/lib/types/client/claude/locales.d.ts +2 -11
  13. package/lib/types/client/claude/locales.d.ts.map +1 -1
  14. package/lib/types/client/locales.d.ts +13 -1
  15. package/lib/types/client/locales.d.ts.map +1 -1
  16. package/lib/types/host/claude/model-catalog.d.ts +42 -20
  17. package/lib/types/host/claude/model-catalog.d.ts.map +1 -1
  18. package/lib/types/host/claude/types.d.ts +7 -2
  19. package/lib/types/host/claude/types.d.ts.map +1 -1
  20. package/lib/types/host/codex-account-pool.d.ts +129 -1
  21. package/lib/types/host/codex-account-pool.d.ts.map +1 -1
  22. package/lib/types/host/codex-adopt.d.ts +255 -0
  23. package/lib/types/host/codex-adopt.d.ts.map +1 -0
  24. package/lib/types/host/reasoning-collapse-guard/index.d.ts +166 -0
  25. package/lib/types/host/reasoning-collapse-guard/index.d.ts.map +1 -1
  26. package/lib/types/host/routes.d.ts +11 -1
  27. package/lib/types/host/routes.d.ts.map +1 -1
  28. package/lib/types/host/token-store.d.ts +26 -1
  29. package/lib/types/host/token-store.d.ts.map +1 -1
  30. package/lib/types/host/workbuddy/client.d.ts +1 -1
  31. package/lib/types/host/workbuddy/client.d.ts.map +1 -1
  32. package/lib/types/host/workbuddy/model-catalog.d.ts +77 -4
  33. package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -1
  34. package/lib/types/host/workbuddy/routes.d.ts +7 -1
  35. package/lib/types/host/workbuddy/routes.d.ts.map +1 -1
  36. package/lib/types/index.d.ts.map +1 -1
  37. package/lib/types/shared/contracts.d.ts +22 -0
  38. package/lib/types/shared/contracts.d.ts.map +1 -1
  39. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,60 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.15.0 - 2026-10-09
6
+
7
+ - **[WorkBuddy] 补上网关「在服务但从不公布」的模型,并按厂商归拢模型列表**
8
+ - **根因是一处反直觉的实测事实:`/v3/config` 不是服务全集。** 国际区实测,`gpt-6-sol`、`gpt-6-luna`、`gemini-3.8-flash` 三个 id 都能正常返回 200 的流式补全,却**完全不在目录里**(该接口只公布 `gpt-6-astra` 一个 GPT-6 成员)。判据只有一条——直接问:`code 11102`「service info not found」是没有这个模型,`code 11133`「provider rejected params」是模型存在、只是参数被拒。**`11133` 不能当存在性证据**:`max_tokens` 给得太小会把目录里的正常模型也波及进来(`gpt-5.5`、`gpt-5.4` 都会落到这一类)。
9
+ - **只加进内置表等于加了个看不见的模型。** 联网时实时目录会**整体替换**内置表,所以这三个 id 单列一份实测表(`UNPUBLISHED_MODELS`),并在**实时目录解析**与**快照反序列化**两处合并进去;已公布的条目永远优先,合并只填目录没提到的 id。国际区因此从 22 个变 25 个。
10
+ - **哪些字段是量出来的、哪些是继承的,界线写在代码里。** 存在性、思考档位、图片支持是**打接口测出来的**(`gpt-6-sol` 精确接受 `low`/`medium`/`high`/`xhigh`/`max`、拒绝 `minimal`,与 `gpt-6-astra` 公布的档位表一致;两者都接受 1×1 PNG)。上下文窗口、输出上限、`canDisableThinking` 则是**继承同族已公布的兄弟模型**(`gpt-6-astra` / `gemini-3.5-flash`)并就地标注——网关对这些 id 不公布任何信息,而 `max_tokens` 也不受窗口约束(连 `gpt-6-astra` 自己都接受远超其公布上限的值),从请求侧无法反推。三者仍可在设置页逐模型覆盖。
11
+ - **同一份现场实读还修正了国际区三处数值**:`glm-5.3-flash` 国际区也开始服务(此前标为仅国区);`glm-5.3` / `glm-5.2` 国际区输出上限是 48000、`kimi-k2.8-preview` 是 32000。**一条表同时覆盖两区时取小值**:高报会让请求被网关拒绝,低报只是提前压缩,且仍可覆盖。
12
+ - **国区同步**:新增 `hy4-preview-f`、`space-bunny`,`hy4-preview-x` 已下线。
13
+ - **模型列表按厂商分组,不再让网关的排列顺序决定观感。** 网关给 `/v3/config` 的顺序是交错厂商的:一行 DeepSeek、一行 GPT-6-Astra、两行混元、一行 Kimi、再三行 GPT——八个 OpenAI 模型在设置页里从不挨在一起。现在按厂商族分组,族内**新代在前**。
14
+ - **组内排序按版本号比较,而不是照抄目录序。** 这是被实测逼出来的:只做分组时「在服务但不公布」的模型因为追加在已公布列表之后,`gpt-6-sol` 会渲染到 `gpt-5.4` **下面**——最新的沉底。改成比较 id 里的数字段后归位,且数字按**数值**比(`gpt-6.10` 在 `gpt-6.6` 之前),同版本保持目录序(`-flash` 仍领着 `-flash-sg`),读不出版本的 id(`primary-model` 这类别名)保持原位而不瞎猜。
15
+ - **分组放在 host 而不是设置页。** `modelsForRegion` 同样是请求路径解析模型的入口,在那儿排一次,picker 与 adapter 不可能出现两套顺序;客户端一行未改。
16
+ - 厂商归属有个坑:`hy3` / `hy4-preview` / `hunyuan-chat` 是腾讯混元的三种写法,必须归到同一组;没有任何规则命中的 id **自成一族**,而不是被扫进一个共享的 `other` 桶。
17
+ - 测试:新增分组与排序断言(族必须连续、组内新代在前、数值比较、同版本保持目录序),并扩充三个既有目录测试。
18
+ - 验证:`npm run typecheck` 0 错误;全量 `npm test` **2886 passed / 7 skipped**。另加两个一次性探针(`scripts/probe-workbuddy-catalog.probe.ts` 拉实时目录做差异、`scripts/probe-workbuddy-unlisted.probe.ts` 给目录外的 id 测档位/图片/输出上限),本条的每一项数值都是它们的输出。
19
+
20
+ - **[思维保护] 推理坍缩守卫补上第二条独立信号:改写型死循环**
21
+ - **原守卫对「换个说法反复说同一件事」是瞎的。** 唯一率信号只看得见**逐字**重复;一段把同一结论用不同措辞重述的流实测只有 0.48,而阈值是 0.85,于是一路跑到输出上限也没人拦。这不是理论问题——归档样本 `paraphrased-loop-reasoning.txt` 就是这种形态。
22
+ - **第二条信号量的是「复述」而非「新颖」。** 把窗口切成句子,逐句与至少 `semanticLag` 句之前的内容做 3-gram Jaccard 相似度并取最大值。**枚举式推理也会重复,但它是相邻重复**——每一项借用上一项的句式;复述信号**忽略相邻性**,这正是区分两者的关键,所以健康的枚举流不会被误伤。
23
+ - **两条路径,低门槛那条在每一步都更严。** 高门槛路径(原规则,未改动)要求唯一率 ≥ 0.85 且复述 ≥ 0.35;低门槛路径(新)允许唯一率低到 0.6,代价是复述门槛抬到 0.45、**外加一道高路径不需要的新颖度上限**(不同句比例 ≤ 0.47),且需要更多次确认(5 次 vs 3 次)。**用更弱的证据换更多、更长的确认。**
24
+ - **确认是连续窗口计数,不是单窗口触发。** 两个信号必须在同一窗口同时过线,且连续 `semanticConfirmations` 次才动手;只有一方过线、或只有单个异常窗口,一律**只记日志不动作**。断流后连击归零,下一次尝试不会继承它没挣到的确认数。
25
+ - **近失也留痕。** 单信号命中有专门的 alone 日志(每种每轮只报一次,避免数百行刷屏),连击在够数前断掉也有 cleared 日志——否则两段短连击在日志里与一段长连击长得一模一样,确认数事后无从推理。
26
+ - 测试:守卫测试 **39 → 80 例**(源文件 480 → 948 行),新增归档样本 fixture 与真实的改写型坍缩样本。
27
+ - 验证:全量 `npm test` 2886 passed / 7 skipped;守卫专项 80 例全通过。
28
+
29
+ ## 0.14.0 - 2026-10-08
30
+
31
+ - **[Claude] 支持 Claude Haiku 5.5(`claude-haiku-5-5`),并把申报的客户端版本抬到 2.1.293**
32
+ - **能力表新增一行**(`src/host/claude/model-catalog.ts`):官方 2026-10-07 发布,1M 上下文 / 128K 输出 / 支持图片,档位 `low`–`max`。**固定 id,没有日期后缀也没有别名**,所以不像 4.5 那样成对出现。
33
+ - **每个字段来自官方文档,不是从模型名推的**:这一行与邻居在三个字段上直接冲突——`claude-haiku-4-5` 是 `budget`、可关思考、支持 temperature,5.5 三条全反(自适应思考、档位控制深度、拒绝非默认采样参数)。能力表的地基就是「名字不决定任何字段」,照抄邻居就是这条地基要防的那种错。
34
+ - **不能用 `budget`**:官方迁移指南写明手动预算形式 `{type:"enabled",budget_tokens:N}` 在 5.5 上是 400。照抄 4.5 那一行会发出模型直接拒绝的请求。
35
+ - **也不进 `mid-convo` 分支**:官方默认档位是 `medium`,而 `mid-convo` 在调用方未指定档位时会强制 `effort: high`——等于每次请求都替用户多花一档、多付一档的钱。这与 Opus 5.5 是同一个坑、同一份理由,因此 Haiku 5.5 同样靠 `bindsThinkingToPrefix` 拿到 `block_binding`,而**不**接受强制 effort。测试里钉死 mid-convo 白名单的那条断言也没有收录它。
36
+ - **`canDisableThinking: true`——本表第一个为真的本地新增行**:官方明确 `thinking:{type:"disabled"}` 在 `low`/`medium`/`high` 被接受、在 `xhigh`/`max` 是 400。这条不对称能安全记成一个布尔值,靠的是本线路**发这个 form 的时机**:只有调用方明确要求关闭思考时才发,且此时**不申报任何档位**,请求因此跑在官方默认 `medium` 上——落在被接受的一侧;想用 `xhigh`/`max` 的人一定开着思考,走的是 adaptive form。反过来把「xhigh 以上 400」读成「不能关闭」才是错的——那会把低档位本来可用的开关一起拿走。
37
+ - **与号池相关的一条用户可见后果**:官方写明 Haiku 5.5 的思考块只在产出它的账号(或其关联账号)里有效,换账号回放会被**静默丢弃**——请求成功,模型看不到那段推理。多账号轮换因此表现为「同一段对话里答案质量忽高忽低」而**不报错**。这一条记在能力表的行注释与 README 里,因为它是号池的行为,不是模型的性质。
38
+ - **申报版本 2.1.285 → 2.1.293**:Haiku 5.5 首发于 Claude Code 2.1.293,而 `types.ts` 早就写着「默认版本必须 ≥ 首次提供每个模型的那个发行版」。这不是假设:加这一行时 npm 的 `stable` 标签**仍停在 2.1.285**(插件当时申报的值),`latest` 已经是 2.1.293——不抬版本的结果是照常广告一个每次请求都会被 `claude_code_version_too_old` 拒掉的模型,而本地预检看不出来。该行**没有** `minCliVersion`:没见过上游真的拒它,就不臆造门槛数字(Sonnet 5.5 也是这么处理的)。版本锁另加一条断言,把「≥ 2.1.293」写进测试而不只写进注释。
39
+ - **保真锁同步跟着加**:本机参照目录(pi-ai 0.87.1)里没有这个模型,所以它进 `LOCALLY_CURATED_MODEL_IDS`,并在 `test/claude-model-catalog.test.ts` 里逐字段断言官方文档的值;该文件的三处说明文字(「今天两行」「快照之外还差几行」「两行的理由」)一并改成三行,否则文档会比锁先过期。
40
+ - **新装默认勾选里的 Haiku 槽位换成 5.5**:默认列表每族只放一个当前代模型,所以 `claude-haiku-4-5` 被 `claude-haiku-5-5` 顶掉,长度不变。这是安装期的明确决策而不是静默追加:「未编辑过默认列表」按成员比对,老用户存的旧默认仍按原样生效,不会被这次改动悄悄改写。
41
+ - 验证:`npm run typecheck` 0 错误;`test/claude-model-catalog.test.ts` **26 passed / 0 failed**(本机装有参照目录,15 行转录保真断言与「快照之外恰为这两行」都真的跑了,不是 skip)、`test/claude-mapper.test.ts` 91、`test/claude-adapter.test.ts` 43、`test/claude-routes.test.ts` 21、`test/claude-token-store.test.ts` 45、`test/provider-output-reservation.test.ts` 181 全通过;全量 `npm test` **2826 passed / 11 failed / 7 skipped**,11 个失败全部是 `test/claude-oauth.test.ts` 的 `No bindable loopback port in the probe range.`——本机 bind `127.0.0.1` 直接 `EACCES`(已单独探测确认),与本次改动无关。
42
+ - **[Codex] 在 ChatGPT 卡片里导入本机 Codex CLI 的登录(可选)**
43
+ - **重复登录是纯粹的摩擦**:已经在官方 CLI 登录过的用户,登录的正是这个 Provider 服务的那份订阅,却还要再走一次完整的 OAuth。新增 `POST /adopt`,让它点一次就少一整轮登录。
44
+ - **导入得到的是快照,本插件永不再刷新它**:ChatGPT 的 refresh token 会轮换,而 CLI 会在**原地刷新自己的文件**——两个进程刷同一份授权会互相作废,输的一方握着服务端已经作废的令牌,结果是用户**被本插件的好意踢出 Codex CLI**。进程内的单飞解决不了,因为竞态在**进程之间**:两个进程唯一共享的就是那个文件,而按只读规则本插件不能写它。因此快照只在 CLI 还为它作保时可用,**过期是收编这件事的一部分而不是缺陷**。
45
+ - **只读是结构性的,不是承诺**:`codex-adopt.ts` 只按名字从 `node:fs/promises` 导入 `readFile` 与 `stat`,加一次写操作必须先改这行 import(而 import 是评审第一眼看的地方);测试断言一次读取前后凭据文件的**字节、大小与 mtime 完全不变**。
46
+ - **存在性与内容是两个函数**:`codexCliCredentialPresence()` 只 `stat`,开机即可跑;`readCodexCliCredentials()` 只在你点了导入之后才调用。合成一个「顺手先读一下以防万一」的启动路径,正是会把 opt-in 变成装饰的那种做法。
47
+ - **每条线路只在自己的页面里回答自己的问题**:一开始把这三处做成了总览页上一次跨线路的统一只读扫描(一条统一扫描路由加一个扫描模块),结果那一块替 Claude Code 与 MiniMax Code 回答了它们自己已经回答过的问题,只能挂一句「该供应商在自己的设置页里已有导入入口,请到那里导入」——而用户就在离那一页一步之遥的地方。现在没有跨线路扫描了:Codex CLI 的答案由 `GET /status` 上的 `codexCliSignInAvailable` 给出,Claude 与 MiniMax 继续用各自的既有读取器与各自的导入入口。**「文件在哪」只有一份答案,因为只有各自的读取器拥有它。**
48
+ - **宿主顺带报出它 stat 过的那个路径**:`GET /status` 多带一个 `codexCliSignInPath`(`codexCliCredentialPaths()` 的唯一候选,纯函数、不碰文件系统),有了它「没找到」才可追问「你到底找哪儿了」。**卡片本身不显示它**——卡片上除了按钮不加文字,本条只保证答案在接口上随时可取。
49
+ - **「已检测到」不等于可用**:判断可用必须读取文件,那正是 opt-in 之后才做的事。未识别的形状(文件不存在、不是 JSON、`auth_mode` 不是 `chatgpt`、`tokens` 不是对象、缺 access/refresh token、access token 不是带可用 `exp` 的 JWT)一律给出 `undefined` 而不是异常,卡片报「格式无法识别」;邮箱与套餐来自 id token 自己的声明,没有就不填。`exp` 由秒换算成毫秒——错向时是静默的,而错向另一边会让一枚凭据看起来几个世纪都不过期。
50
+ - **不猜 Kimi Code**:本机 Kimi Code 的凭据文件没有区域 / `oauthHost` / `baseUrl`,插件宁可拒绝也不猜,因此该行不出现,而不是显示成一个「装上就能收编」的承诺。
51
+ - **卡片与号池共同钉住「只借 access token」**:`codexNeedsRefresh` 对 adopted 凭据**恒答否**,刷新钩子本身也直接拒绝(双重挡住是为了让将来漏掉前者的调用者大声失败,而不是悄悄花掉一枚不属于本插件的授权);过期即退出调度且**不产生任何写入**,摘要标 `removable: false`,删除只能走显式的 `removeImportedAccount`。池文件往返保存该标记,于是重启后也不会把快照当成自持凭据。
52
+ - **导入只是多一行**:不触碰当前存储的登录态;去重键 `accountId ?? email` 让同一账号重复导入时**原地更新**而不是产生重复账号。
53
+ - 客户端只多一个按钮,且只在 **ChatGPT 卡片**里:账号池卡片头部那行「登录」**旁边**的「导入本机 Codex CLI 登录」,用共享卡片的 `renderLoginActions` 插槽,和 Claude 页面那个收编按钮同一套布局——登录保留主按钮样式,导入是次要的那个。这个按钮**不受检测结果影响,永远都在**:一个会自己消失的按钮回答不了「我本机到底登录了没有」,而没有登录时点它也并非无声无息,宿主会回一句明说缘由的错误,并显示在该卡片已有的错误条里。**卡片上不添加任何说明文字**:按钮已经说明了自己的动作,而「有没有」「找到了吗」「去哪儿找的」三段散文正是用户要求移除的「无关文字」(Claude 页面上同样的三段一并移除)。
54
+ - **导入必须能撤销,而撤销的形状由路由决定**:`/adopt/disable` 刻意不接受 `accountId`(池对 adopted 行的删除拒绝是一条**安全**规则,逐条撤销的入口一旦能抵达自持登录就会打破它),于是撤销做成了 ChatGPT 账号池卡片下方的一个**池级**按钮「停止导入全部本机登录」,文案与提示都写明「全部」。导入行被标为 `removable: false`,共享卡片据此隐去 Delete;没有这个按钮时导入就是**单向门**,只能手改 pool 文件才能收回——一个只进不出的功能不算做完。
55
+ - 路由:`POST /adopt`(body `{source:'codex'}`,未知来源返回一句指向真正拥有它的供应商的话,而不是静默无操作)、`POST /adopt/disable`;`GET /status` 另带 `codexCliSignInAvailable` 与 `codexCliSignInPath`。
56
+ - 测试:新增 `test/codex-adopt.test.ts`(19 例)与 `test/codex-local-signin-ui.test.tsx`(6 例),并扩充 `test/codex-account-pool.test.ts`(27 例)与 `test/routes.test.ts`(10 例)。
57
+ - 验证:`npm run typecheck` 与 `npm run build` 均 0 错误;`test/client-registration.test.ts` **16 passed / 0 failed**、`test/codex-import-undo-ui.test.tsx` 6、`test/codex-local-signin-ui.test.tsx` 6、`test/routes.test.ts` 10、`test/codex-account-pool.test.ts` 27、`test/codex-adopt.test.ts` 19 全通过;全量 `npm test` **2823 passed / 11 failed / 7 skipped**,11 个失败全部是 `test/claude-oauth.test.ts` 的 `No bindable loopback port in the probe range.`,在**未改动的工作树上同样复现**,与本功能无关。
58
+
5
59
  ## 0.13.2 - 2026-10-08
6
60
 
7
61
  - **[Kimi Code] 读图请求按「实际发送体积」计量,超限时回收而非直接拒绝**
package/README.md CHANGED
@@ -17,6 +17,7 @@
17
17
  - [Claude(订阅)线路](#claude订阅线路)
18
18
  - [子代理模型授权](#子代理模型授权0215-起032-起强制指定模型)
19
19
  - [随包分发的 Agent Preset](#随包分发的-agent-preset)
20
+ - [本机登录](#本机登录)
20
21
  - [安全边界](#安全边界)
21
22
  - [插件路由](#插件路由)
22
23
  - [开发与验证](#开发与验证)
@@ -28,7 +29,7 @@
28
29
 
29
30
  | Provider ID | 订阅 | 协议 | 登录方式 | 模型目录来源 |
30
31
  | --- | --- | --- | --- | --- |
31
- | `codex-chatgpt` | ChatGPT(Plus / Pro / Business…) | Responses | 浏览器 OAuth(localhost:1455 回调) | **实时** `/backend-api/codex/models` |
32
+ | `codex-chatgpt` | ChatGPT(Plus / Pro / Business…) | Responses | 浏览器 OAuth(localhost:1455 回调),或导入本机 Codex CLI 登录 | **实时** `/backend-api/codex/models` |
32
33
  | `kimi-code` | Kimi 会员 | Anthropic Messages | **设备码**(RFC 8628) | 实时 `/v1/models` + 本地兜底 |
33
34
  | `command-code` | Command Code | Anthropic / OpenAI 双轨 | 浏览器 OAuth 或粘贴 API Key | 实时 `/provider/v1/models` |
34
35
  | `workbuddy-subscription` | 腾讯 WorkBuddy / CodeBuddy | OpenAI 兼容(仅流式) | 扫描桌面端凭据或官方浏览器授权 | 实时 `/v3/config` |
@@ -49,6 +50,7 @@
49
50
  - 支持 token 刷新、登录取消与账号注销;
50
51
  - Windows 使用 CurrentUser DPAPI 加密存储 token;Linux 使用当前用户独占的 `0600` 文件存储;明文不发送给 Client;
51
52
  - 设置页会明确显示当前存储类型,并在 Linux 上提示文件存储未额外加密。
53
+ - **本机已经登录过 Codex CLI 的用户可以不再走一次 OAuth**(详见「本机登录」):**ChatGPT 卡片**的账号池里,「登录」按钮**旁边**还有一个「导入本机 Codex CLI 登录」按钮——两者是同一件事的两种做法,布局与 Claude 页面上那个收编按钮一致。它**只看凭据文件是否存在、从不读取令牌**,且要你显式点那个导入按钮之后才会读取。导入得到的是一份**快照**,**永不由本插件刷新**——ChatGPT 的 refresh token 会轮换,而 CLI 会在原地刷新自己的文件,两个进程各自刷新会互相作废,最后是你被本插件的好意踢出 Codex CLI,而进程内的单飞解决不了这个**跨进程**竞态。快照过期后请**回 Codex CLI 重新登录后再导入**;本插件**绝不写入或删除 Codex CLI 的任何文件**。
52
54
 
53
55
  ### 模型接入
54
56
 
@@ -151,6 +153,7 @@
151
153
  - 插件托管账号可真正删除;桌面扫描账号只能从本插件隐藏/恢复,永不删除 CodeBuddy 的原文件;
152
154
  - 两区模型清单不同,把模型发到不服务它的区会返回 400 `code 11102`,因此模型选择器**按当前账号区域过滤**;
153
155
  - **模型目录取自网关自己的 `/v3/config`**(官方 CLI 启动时读的就是它):每个模型的真实上下文上限、输出上限、是否接受图片、以及可用的思考档位都在这里,不做任何按模型名猜测。`/v1/models` 在这条线路上是 404,所以此前只能靠内置表——现在内置表只作为离线兜底,且是**从真实 `/v3/config` 转录**的(早期手写版本把 `glm-5.3`、`kimi-k3` 的窗口猜成 200K/256K,实际都是 1M);
156
+ - **目录不是服务全集,模型必须实测**:实测国际区的 `gpt-6-sol`、`gpt-6-luna`、`gemini-3.8-flash` 都能正常返回 200,却**完全不在 `/v3/config` 里**(该接口只公布 `gpt-6-astra`)。而联网时目录会**整体替换**内置表,只加进内置表等于加了个看不见的模型——所以这三个单列一份实测表(`UNPUBLISHED_MODELS`),并合并进实时目录。存在性、思考档位、图片支持是**打接口量出来的**(`gpt-6-sol` 只收 `low`/`medium`/`high`/`xhigh`/`max`,`minimal` 被拒);上下文与输出上限**继承同族已公布的兄弟模型**(`gpt-6-astra` / `gemini-3.5-flash`),因为网关对这些 id 不公布任何信息,而 `max_tokens` 也不受窗口约束(连 `gpt-6-astra` 自己都接受远超其公布上限的值),无从反推。三者仍可在设置页逐模型覆盖;判定一个 id 是否存在只有一条路——直接问:`code 11102` 是没有这个模型,`code 11133` 是模型存在但参数被拒;
154
157
  - 目录同时区分**默认服务的上下文长度**与**模型上限**(如 `deepseek-v4.1-flash` 默认 300K、最大 1M)。本线路不发送显式长度参数,所以 DSH 的压缩与溢出判断按**默认服务长度**计算,不会让请求越过后端实际接受的窗口;
155
158
  - 思考档位**逐模型**取目录声明的档位表,回落顺序为「调用方显式指定 → 用户配置的默认档 → 目录为该模型声明的默认档」;
156
159
  - **最后一档不能省**:上游在请求不带 `reasoning_effort` 时返回**空的 `reasoning_content`**(实测同一提示:不带字段 0 字符,带字段 130–215 字符)——不发送等于静默丢弃模型的思考;
@@ -363,6 +366,8 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。GPT-6 系列
363
366
 
364
367
  **设置 → Codex 订阅 → 网络代理** 同时控制 GPT 与 Antigravity(Gemini)的 Host 请求,可选择系统代理(自动检测)、自定义代理或直连。Gemini 模型生成、网页登录后的令牌交换、令牌刷新、账号信息、项目发现、配额与模型目录查询均使用此设置;修改后对后续请求生效,无需重启 DSH。浏览器中的 Google 授权页面使用浏览器自己的网络设置。
365
368
 
369
+ 已经用官方 Codex CLI 登录过的用户可以跳过第 3 步的 OAuth:**设置 → 订阅服务 → ChatGPT**,在账号池卡片里点「登录」**旁边**的「导入本机 Codex CLI 登录」即可(见「本机登录」)。
370
+
366
371
  ### Command Code
367
372
 
368
373
  1. 重启 `dsh web`;
@@ -653,10 +658,12 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
653
658
  档位的模型生效。**档位原样透传**(`ReasoningEffortId` 在 harness 里是无约束 brand,本仓库多条线路都直接使用
654
659
  `xhigh`),不做任何收敛,以免把用户选的档位静默降级。
655
660
 
656
- **模型表**共 16 条:14 条转录自本机随 harness 安装的参照目录,另加 2 条**本地新增**:**Claude Opus 5.5**
657
- (`claude-opus-5-5`,官方 2026-09-22 发布,1M 上下文 / 128K 输出 / 支持图片,档位 `low`–`max`),以及
658
- **Claude Sonnet 5.5**(`claude-sonnet-5-5`,官方 2026-09-28 发布,规格同上;不支持 temperature、思考不可关闭;
659
- 官方默认档位为 `high`,且思考块与会话前缀绑定,因此走 `mid-convo` 分支并带 `block_binding`)。以下说明针对 Opus 5.5。
661
+ **模型表**共 17 条:15 条转录自本机随 harness 安装的参照目录(**Claude Opus 5.5** 已在参照目录中,只是
662
+ `thinkingMode` 一项按官方文档取值,见下),另加 2 条**本地新增**:**Claude Sonnet 5.5**
663
+ (`claude-sonnet-5-5`,官方 2026-09-28 发布,1M 上下文 / 128K 输出 / 支持图片;不支持 temperature、思考不可关闭;
664
+ 官方默认档位为 `high`,且思考块与会话前缀绑定,因此走 `mid-convo` 分支并带 `block_binding`),以及
665
+ **Claude Haiku 5.5**(`claude-haiku-5-5`,官方 2026-10-07 发布,规格同上;**固定 id,无日期后缀也没有别名**)。
666
+ 以下说明针对 Opus 5.5。
660
667
  它有一条**与其它模型不同的硬约束**:**思考永远开启、不能关闭**——官方文档明确 `thinking:{type:"disabled"}`
661
668
  与 `{type:"enabled",budget_tokens:N}` **都会返回 400**,因此能力表把它标为不可关闭思考,档位表是控制思考深度的
662
669
  唯一手段。它的**默认档位是 `medium`**(其余带 effort 的模型默认 `high`),这一点被刻意保留:本仓库有两类模型走
@@ -666,11 +673,26 @@ DSH 设置页的「Subagent」卡片会把勾选的模型写成会话级的允
666
673
  adaptive 分支对这类模型额外发送 `block_binding: drop_block`(**不**强制 effort)。
667
674
 
668
675
  它还有一条**版本门槛**:上游要求申报的客户端版本 **≥ 2.1.280** 才为该模型提供服务。能力表用 `minCliVersion`
669
- 记录这一点(**只有这一行有门槛**,其余 15 行缺省——缺省表示「未知」而不是「无门槛」,不臆造数字)。插件申报的
670
- 默认版本为 **2.1.285**(不低于首次提供 Sonnet 5.5 的 Claude Code 2.1.284),并且**在发出请求之前就本地校验**「申报版本 vs 该模型门槛」,不满足时直接给出模型、当前
676
+ 记录这一点(**只有这一行有门槛**,其余 16 行缺省——缺省表示「未知」而不是「无门槛」,不臆造数字)。插件申报的
677
+ 默认版本为 **2.1.293**(不低于首次提供 Sonnet 5.5 的 Claude Code 2.1.284,也不低于首次提供 **Haiku 5.5 的 2.1.293**——
678
+ 加这一行时 npm 的 `stable` 标签仍停在 2.1.285,所以这里必须读 `latest` 而不是 `stable`),并且**在发出请求之前就本地校验**「申报版本 vs 该模型门槛」,不满足时直接给出模型、当前
671
679
  版本与要求版本,**而不是白花一个往返让上游返回 400**。测试另有一条不变量:**申报的默认版本必须 ≥ 表中每个模型的
672
680
  门槛**——这条锁让「加了高门槛模型却忘了抬版本」无法通过 CI。
673
681
 
682
+ **Claude Haiku 5.5** 有三条与上面两个 5.5 不同的地方,都是照抄官方文档、不是从模型名推的:
683
+
684
+ - **它可以关闭思考,但只在低档位**。官方明确 `thinking:{type:"disabled"}` 在 `low`/`medium`/`high` 被接受,在
685
+ `xhigh`/`max` 是 400——因此能力表把它标为**可关闭**(`canDisableThinking: true`),这是本表里唯一一个为真的
686
+ 本地新增行。这条线路只在调用方明确要求「关闭思考」时才发这个 form,且此时**不申报任何档位**,请求因此跑在
687
+ 官方默认 `medium` 上——正落在被接受的一侧;而想用 `xhigh`/`max` 的人一定开着思考,走的是 adaptive form。
688
+ - **它不能沿用 Haiku 4.5 的 `budget` 表**。官方迁移指南写明手动预算形式
689
+ `{type:"enabled",budget_tokens:N}` 在 5.5 上是 400,所以这一行改走 `adaptive`;又因为官方默认档位是
690
+ `medium`(与 Opus 5.5 同列),它同样**不进 `mid-convo` 分支**——那一档会强制 `effort: high`,等于每次请求
691
+ 都替用户多花一档。它同样是官方文档列明的**思考块前缀校验模型**,所以 `bindsThinkingToPrefix` 为真。
692
+ - **它的思考块只在产出它的账号里有效**。官方写明换账号回放会被**静默丢弃**(请求成功,模型看不到那段推理)。
693
+ Claude 号池会在多个账号之间轮换,所以长会话中途换号会表现为「同一段对话里答案质量忽高忽低」而**不报错**——
694
+ 这是换号的行为,不是模型不稳定。另外 5.5 与 4.7 之后的模型用同一个新 tokenizer,同样的文本约多 30% token。
695
+
674
696
  ### 收编本机已有的 Claude Code 登录(可选,默认关闭)
675
697
 
676
698
  如果你本机已经登录过 Claude Code,可以点「收编本机登录」把它作为一条账号导入,省去再次登录。
@@ -735,6 +757,47 @@ access token / refresh token / 授权码 / PKCE verifier**;诊断里提到某
735
757
  **SHA-256 指纹前缀**(`sha256:.../len:...`),**不是**令牌的任何一段原文——早前版本会打印前 6 个字符,
736
758
  那本身就是一次凭据泄露,因为同一个字符串会被渲染到设置卡片并写进 Host 日志(有测试锁定)。
737
759
 
760
+ ## 本机登录
761
+
762
+ **每条线路只在自己的详情页里回答关于本机的问题**。ChatGPT(Codex)线路的 ChatGPT 卡片里,「登录」按钮**旁边**就是「导入本机 Codex CLI 登录」——与 Claude 页面上那个收编按钮同样的位置、同样的次要样式;点它可以省掉再走一次 ChatGPT 授权。Claude Code 与 MiniMax Code 各自的设置页里本来就各有一个导入入口,不在这里重复。
763
+
764
+ ### 只看文件在不在,从不读令牌
765
+
766
+ 打开 ChatGPT 卡片时,宿主用一次 `stat` 回答这件事,**没有任何文件被打开读取**:
767
+
768
+ | 线路 | 查过的路径 | 导入入口在哪 |
769
+ | --- | --- | --- |
770
+ | Codex CLI | `$CODEX_HOME/auth.json`,未设置该变量时为 `~/.codex/auth.json` | ChatGPT 卡片账号池里「登录」旁边的那个按钮 |
771
+ | Claude Code | `~/.claude/.credentials.json`(目录可用 `CLAUDE_CONFIG_DIR` 改,见「Claude(订阅)线路」) | Claude 线路自己的设置页 |
772
+ | MiniMax Code | `~/.minimax/auth/<buildEnv>/<region>/mcode-public/auth.json`,国区与国际区各一(见「MiniMax Code(编程订阅)线路」) | 不需要按钮:它的令牌库直接采用本机那份凭据 |
773
+
774
+ - **为什么不放在总览页**:总览页上一块跨线路的扫描,替别的线路回答问题,最后只能写一句「该供应商在自己的设置页里已有导入入口,请到那里导入」——而用户就在离那一页一步之遥的地方,那句话既答错了地方,本身也自相矛盾。信号归各自的线路,位置也就归各自的线路。
775
+ - **Kimi Code 不在这里**:本机 Kimi Code 的凭据文件**不带**本插件需要的区域、`oauthHost` 与 `baseUrl`,而插件**拒绝猜**这三个值,因此既不扫描也不显示——那一行若是显示成「未检测到」,看起来就像装个插件就能收编。
776
+
777
+ 理由不是谨慎,而是这一块的位置:它要渲染的是一份**邀请**(一个可以点的按钮),而此时你还没有同意任何事。一个会读文件的扫描器会让后面那个 opt-in 变成装饰,同时为一页只需要「有 / 没有」的信息在内存里握着别家订阅的令牌。所以这一块**不显示**任何账号、套餐、邮箱或到期时间:这些答案要导入之后才存在,导入前显示它们等于提前读取。
778
+
779
+ 因此**「已检测到」只表示那个文件在**,不表示它可用——判断可用必须读取文件,而那是 opt-in 之后的事。文件在却形状不认识时(不是 JSON、`auth_mode` 不是 `chatgpt`、`tokens` 不是对象、缺 access 或 refresh token、access token 不是一个带可用 `exp` 的 JWT),导入会明确报出「格式无法识别」而不是抛错或静默成功,也不会把默认值当成事实;邮箱与套餐则来自 id token 自己的声明,没有就不填。查过的路径**两种状态都写出来**,包括没找到的时候——「你到底去哪儿找了」问的往往正是它,而一个只说「没找到」却不给路径的答案没法据此行动。
780
+
781
+ ### 导入 Codex CLI 登录(可选)
782
+
783
+ 在 ChatGPT 卡片账号池里点「登录」**旁边**的「导入本机 Codex CLI 登录」。三点与 Claude Code 的收编同源:
784
+
785
+ - 导入的是**快照**,**本插件永不再刷新它**;
786
+ - 快照过期后请**回 Codex CLI 重新登录、再导入一次**,本插件里没有这条登录路径;
787
+ - 本插件**绝不写入、不创建、不移动、不删除** Codex CLI 的任何文件。
788
+
789
+ 前两条的理由值得写全,因为这是整个功能最容易被误解的地方:ChatGPT 的 refresh token 会轮换,而 CLI 会在**原地刷新自己的文件**。两个进程刷同一份授权会互相作废,输的一方握着服务端已经作废的令牌,结果是**你被本插件的好意踢出 Codex CLI**。进程内的单飞解决不了它——这是**跨进程**竞态,两个进程之间唯一共享的就是那个文件,而按上一条本插件不能写它。所以这份快照只在 CLI 自己还为它作保的期间可用,**过期是收编这件事的一部分,而不是一个待修的缺陷**。
790
+
791
+ 「绝不写入」是**结构性**的保证而不是承诺:`codex-adopt.ts` 只按名字从 `node:fs/promises` 导入 `readFile` 与 `stat`,加一次写操作必须先改这行 import,而 import 正是评审第一眼看的地方;测试还断言一次读取前后该凭据文件的**字节、大小与 mtime 完全不变**。
792
+
793
+ ### 导入之后
794
+
795
+ - **只是号池里多一行**,不替换你当前的登录:已存储的登录态原样保留,两者在同一个池里参与调度;
796
+ - **同一个账号导入两次会原地更新**,不会多出一个重复账号:号池的去重键是 `accountId ?? email`;
797
+ - **导入的行与插件自持的登录语义不同**:它**永不被刷新**——`needsRefresh` 钩子对 adopted 凭据恒答否,刷新钩子本身也直接拒绝,两者都挡住是为了让将来漏掉前者的调用者大声失败而不是悄悄花掉一枚不属于本插件的授权;过期即**退出调度**且**不产生任何写入**,摘要标为**不可删除**,删除只能走显式的 `removeImportedAccount`(`POST /adopt/disable` 清掉插件这边记下的快照)。因此你在本插件里自己签入的账号不会被这个动作顺手清掉,而 CLI 自己的文件更不会被删除或改写;
798
+ - **过期不是失败**:卡片与号池会说明这份快照已过期、该回 Codex CLI 里重新登录后再导入一次;这一行随之退出调度,而不是拿一枚作废的令牌去试一次请求。
799
+ - **导入可以撤销,但撤销是「全部」而非「某一行」**:借来的凭据在账号池里被标为**不可删除**(这正是「过期不产生任何写入」与「插件自持的登录不会被顺手清掉」两条规则的代价),因此删除入口不在每一行上,而在 ChatGPT 标签页账号池卡片下方的一个按钮:**停止导入全部本机登录**。点它走 `POST /adopt/disable`,清掉插件这边记下的**所有**导入快照;你在本插件里自己签入的账号不在其中,**Codex CLI 自己的登录文件也不会被删除或改写**——这一步只是让本插件忘记它拷贝的那一份。按池级语义设计而不是行级,是因为该路由刻意不接受 `accountId`:一个摆在某一行上、却会连带清空邻居的按钮,比没有按钮更糟,所以按钮文案与提示里都写明「全部」,将来若改成逐行语义能被看见。
800
+
738
801
  ## 安全边界
739
802
 
740
803
  Antigravity 的 access token / refresh token 使用独立的系统凭据存储:Windows 使用 CurrentUser DPAPI(`$DSH_HOME/storages/antigravity-oauth.json.dpapi`),macOS 使用登录钥匙串,Linux 使用 Secret Service。macOS / Linux 的服务名为 `dsh-antigravity`,账号键按旧凭据文件的绝对路径生成,隔离不同的 `DSH_HOME`。
@@ -747,6 +810,8 @@ Antigravity 的 access token / refresh token 使用独立的系统凭据存储
747
810
 
748
811
  **收编(adopt)是只读的**:本插件从不创建、修改、移动或删除 Claude Code 的任何文件;它只读取。收编得到的快照**永不由本插件刷新**(原因见上文)。
749
812
 
813
+ **Codex CLI 的导入(adopt)同样是只读的**:本插件从不创建、修改、移动或删除 Codex CLI 的任何文件。存在性与内容是两个函数——`/status` 里的 `codexCliSignInAvailable` 由一次 `stat` 回答,**在你点击导入之前没有任何令牌被读取**,而这一个字段也不含任何凭据内容(旁边的 `codexCliSignInPath` 只是被查看的那个文件路径)。导入得到的快照**永不由本插件刷新**;过期后该行退出调度,且**不产生任何写入**(原因见「本机登录」)。
814
+
750
815
  macOS 钥匙串服务名为 `Claude Code-credentials`(**未核实**,本仓库不读取它:一份第三方指南与真实客户端对该名称说法不一致,因此它被明确列为非目标)。
751
816
 
752
817
  升级后首次访问 Antigravity 凭据时,会读取旧 `storages/antigravity-oauth.json`,加密保存并读回校验;成功后删除旧 JSON,通常无需重新登录。失败会保留旧文件并报告错误,不会回退到明文存储。注销同时清理旧文件和新凭据。Linux 需要 `secret-tool`(libsecret 工具包)及可用、已解锁的 Secret Service 钥匙环;无桌面服务的主机也需要配置该服务。系统凭据存储保护落盘数据,不防御当前用户下已获权限的进程。
@@ -772,7 +837,7 @@ Antigravity 的 Gemini 用量以流结束时的上游累计计数为准,缓存
772
837
 
773
838
  | 方法 | 路径 | 说明 |
774
839
  | --- | --- | --- |
775
- | GET | `/status` | 查询账号、连接状态与额度 |
840
+ | GET | `/status` | 查询账号、连接状态与额度;另带 `codexCliSignInAvailable`(本机是否有可导入的 Codex CLI 登录,**只 `stat` 不读取**)与 `codexCliSignInPath`(为此查看过的那个文件路径) |
776
841
  | POST | `/login/start` | 开始 OAuth 登录 |
777
842
  | GET | `/login/events?loginId=...` | SSE 订阅登录进度 |
778
843
  | POST | `/login/cancel` | 取消登录任务 |
@@ -781,6 +846,8 @@ Antigravity 的 Gemini 用量以流结束时的上游累计计数为准,缓存
781
846
  | POST | `/quota/refresh` | 刷新额度 |
782
847
  | POST | `/connection/test` | 测试连接 |
783
848
  | POST | `/preferences/update` | 更新搜索来源和 composer 快捷用量偏好 |
849
+ | POST | `/adopt` | 导入本机登录(body `{source:'codex'}`),**只读**:快照作为一行加入 ChatGPT 号池,不触碰当前登录,也不触碰 Codex CLI 的文件 |
850
+ | POST | `/adopt/disable` | 停止导入:清掉插件这边记录的导入快照;**不删除也不改写 Codex CLI 的文件** |
784
851
 
785
852
  状态响应只包含脱敏 email、套餐、账号 ID 后四位、token 到期时间和额度 DTO。
786
853
 
@@ -842,6 +909,7 @@ MiniMax、Kimi、Codex、Claude、Command Code、WorkBuddy 和 Antigravity 的
842
909
  | 模型请求报持续 400 / 「You're out of extra usage」 | 可能是身份或版本门槛:订阅令牌要求 Claude Code 身份头与 `system` 首块,且服务端会校验你申报的客户端版本。此类错误被归类为**请求问题而非凭据问题**,不会把你登出 |
843
910
  | 额度显示为空 / 某项显示「—」 | 额度接口的键集随账号类型变化,未提供的窗口会显示为未知而**不会伪造 0**。注意 **`utilization` 是已用百分比,0% 表示尚未使用(正常态)**,不是额度耗尽;真正的耗尽会以 429 与响应头状态呈现。接口被限流时卡片保留上次成功快照并标注时间 |
844
911
  | 收编后提示「本机 Claude Code 登录已过期」 | 这是设计如此:收编的是**快照**,插件永不刷新它(Claude Code 刷新同一枚轮换令牌,两个进程各自刷新会互相作废)。回 Claude Code 重新登录后**再收编一次**即可 |
912
+ | 导入后提示「从 Codex CLI 导入的登录已过期」 | 同样如此:导入的是**快照**,插件永不刷新它(ChatGPT 的 refresh token 会轮换,而 CLI 会原地刷新自己的文件,两个进程各自刷新会互相作废,最后是你被本插件踢出 Codex CLI)。回 **Codex CLI** 重新登录后**再导入一次**即可——本插件里没有这条登录路径 |
845
913
  | 账号被标为「无法识别账号身份」 | 服务端这个账号既没返回 uuid 也没返回 email,插件无法在再次登录时自动认出它。用卡片上的**手动合并**把它并入既有账号;这是已知限制,插件不会用假 id 掩盖它 |
846
914
  | 模型不出现在选择器里 | 依次检查:卡片是否显示「模型路由已被其他 Provider 占用」(另一适配器持有该 id 时本插件会如实报告冲突而非抛错);以及模型是否已勾选 |
847
915
  | Kimi Code 发送视频却没有画面 | `k3-256k` 不支持视频,切到 `k3` 或 `kimi-for-coding`;容器须在白名单内(mp4/mpeg/mov/avi/x-flv/mpg/webm/wmv/3gpp);若该模型走的是 Anthropic 线路,视频会降级为文字(该协议没有文档化的视频块)。以上情况模型都会收到明确的文字说明,据此向你说明而不是凭空回答 |