@eddyskywalker/dsh-chatgpt-subscription 0.18.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +103 -1
  3. package/lib/client.js +1546 -269
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +9092 -2555
  6. package/lib/types/client/glm-coding-plan/GlmCodingPlanComposerQuota.d.ts +27 -0
  7. package/lib/types/client/glm-coding-plan/GlmCodingPlanComposerQuota.d.ts.map +1 -0
  8. package/lib/types/client/glm-coding-plan/GlmCodingPlanSection.d.ts +22 -0
  9. package/lib/types/client/glm-coding-plan/GlmCodingPlanSection.d.ts.map +1 -0
  10. package/lib/types/client/glm-coding-plan/api.d.ts +4 -0
  11. package/lib/types/client/glm-coding-plan/api.d.ts.map +1 -0
  12. package/lib/types/client/glm-coding-plan/locales.d.ts +412 -0
  13. package/lib/types/client/glm-coding-plan/locales.d.ts.map +1 -0
  14. package/lib/types/client/glm-coding-plan/styles.d.ts +15 -0
  15. package/lib/types/client/glm-coding-plan/styles.d.ts.map +1 -0
  16. package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts +65 -0
  17. package/lib/types/client/glm-coding-plan/useGlmCodingPlanSection.d.ts.map +1 -0
  18. package/lib/types/client/hub/brand-icons.d.ts +7 -5
  19. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  20. package/lib/types/client/hub/providers.d.ts.map +1 -1
  21. package/lib/types/client/index.d.ts +2 -0
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/kimi-code/locales.d.ts +2 -2
  24. package/lib/types/client/minimax-code/locales.d.ts +2 -2
  25. package/lib/types/host/antigravity/oauth.d.ts +11 -1
  26. package/lib/types/host/antigravity/oauth.d.ts.map +1 -1
  27. package/lib/types/host/common/model-request-control.d.ts +1 -1
  28. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  29. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  30. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  31. package/lib/types/host/glm-coding-plan/account-pool.d.ts +152 -0
  32. package/lib/types/host/glm-coding-plan/account-pool.d.ts.map +1 -0
  33. package/lib/types/host/glm-coding-plan/adapter.d.ts +169 -0
  34. package/lib/types/host/glm-coding-plan/adapter.d.ts.map +1 -0
  35. package/lib/types/host/glm-coding-plan/adopt.d.ts +354 -0
  36. package/lib/types/host/glm-coding-plan/adopt.d.ts.map +1 -0
  37. package/lib/types/host/glm-coding-plan/biz-token-cache.d.ts +99 -0
  38. package/lib/types/host/glm-coding-plan/biz-token-cache.d.ts.map +1 -0
  39. package/lib/types/host/glm-coding-plan/client.d.ts +218 -0
  40. package/lib/types/host/glm-coding-plan/client.d.ts.map +1 -0
  41. package/lib/types/host/glm-coding-plan/exchange.d.ts +223 -0
  42. package/lib/types/host/glm-coding-plan/exchange.d.ts.map +1 -0
  43. package/lib/types/host/glm-coding-plan/mapper-anthropic.d.ts +308 -0
  44. package/lib/types/host/glm-coding-plan/mapper-anthropic.d.ts.map +1 -0
  45. package/lib/types/host/glm-coding-plan/mapper-openai.d.ts +213 -0
  46. package/lib/types/host/glm-coding-plan/mapper-openai.d.ts.map +1 -0
  47. package/lib/types/host/glm-coding-plan/model-catalog.d.ts +232 -0
  48. package/lib/types/host/glm-coding-plan/model-catalog.d.ts.map +1 -0
  49. package/lib/types/host/glm-coding-plan/oauth.d.ts +211 -0
  50. package/lib/types/host/glm-coding-plan/oauth.d.ts.map +1 -0
  51. package/lib/types/host/glm-coding-plan/routes.d.ts +93 -0
  52. package/lib/types/host/glm-coding-plan/routes.d.ts.map +1 -0
  53. package/lib/types/host/glm-coding-plan/signing.d.ts +323 -0
  54. package/lib/types/host/glm-coding-plan/signing.d.ts.map +1 -0
  55. package/lib/types/host/glm-coding-plan/token-store.d.ts +202 -0
  56. package/lib/types/host/glm-coding-plan/token-store.d.ts.map +1 -0
  57. package/lib/types/host/glm-coding-plan/types.d.ts +413 -0
  58. package/lib/types/host/glm-coding-plan/types.d.ts.map +1 -0
  59. package/lib/types/host/kimi-code/routes.d.ts +10 -0
  60. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  61. package/lib/types/index.d.ts +9 -0
  62. package/lib/types/index.d.ts.map +1 -1
  63. package/lib/types/shared/glm-coding-plan-contracts.d.ts +315 -0
  64. package/lib/types/shared/glm-coding-plan-contracts.d.ts.map +1 -0
  65. package/package.json +1 -1
@@ -0,0 +1,354 @@
1
+ /**
2
+ * W2:adopt —— 读取本机已登录的 ZCode 客户端登录态,免除二次登录(计划 D6)。
3
+ *
4
+ * 上游落盘事实全部来自官方开源源码,逐字证据在每条常量的注释里(计划 §3.7,证据等级 A):
5
+ *
6
+ * - `shared-credentials.ts`(`SHARED_ZCODE_CREDENTIAL_KEYS`、`resolveSharedZCodeCredentialsPath`、
7
+ * `saveZaiLoginCredentials`):路径、键名、以及 `oauth:bigmodel:*` 与 `oauth:zai:*` 是**两区各自的**凭据。
8
+ * - `credential-cipher.ts`:`enc:v1:` 前缀、AES-256-GCM、iv 12B / tag 16B、
9
+ * 密钥 `sha256(secret)`,secret 取 `ZCODE_CREDENTIAL_SECRET`,否则
10
+ * `zcode-credential-fallback:{platform}:{homedir}:{username}`。
11
+ *
12
+ * 两条必须照搬的规则(与 `src/host/claude/adopt.ts`、`src/host/codex-adopt.ts` 同一份纪律):
13
+ *
14
+ * ---------------------------------------------------------------------------
15
+ * 规则 1 —— 本模块永不写入。它是 reader,而且只是 reader。
16
+ * ---------------------------------------------------------------------------
17
+ * 不得创建、修改、移动或删除任何属于 ZCode 客户端的文件:不以写方式打开任何文件、
18
+ * 不建目录、不改名、不 unlink。保证是**结构性**的而不是散文里的承诺——本模块从
19
+ * `node:fs/promises` **按名**只导入 `readFile` 与 `stat` 两个函数,不导入整个命名空间,
20
+ * 于是引入任何写操作都必须改动 import 列表,而那正是复审者第一行会看的地方。
21
+ * 测试断言的是这条规则**可观测**的更强形式:adopt 跑完后凭据文件的字节、大小、mtime
22
+ * 三者不变,所在目录没有新增条目。
23
+ *
24
+ * ---------------------------------------------------------------------------
25
+ * 规则 2 —— 存在性与内容分成两个函数。
26
+ * ---------------------------------------------------------------------------
27
+ * `zcodeCredentialPresence()` 便宜到可以在插件启动时调用,只 `stat`、从不 open:
28
+ * 没 opt in 的用户,他的令牌内容根本不会被读过——既不被它读,也不被渲染卡片的
29
+ * 路由读。`readZcodeLocalSignIn()` 才是完整读取,且只在用户显式选择之后才被调用。
30
+ * 分开是全部意义所在:一个「顺手先读一下以防万一」的启动路径会让 opt in 变成装饰。
31
+ *
32
+ * ---------------------------------------------------------------------------
33
+ * 规则 3 —— 本模块不持有凭据存储,也不刷新。
34
+ * ---------------------------------------------------------------------------
35
+ * 上游**没有** refresh_token 交换接口(计划 §3.6:官方 `oauthService.ts:972-975` 直接抛错),
36
+ * 所以「刷新」在这里不存在可做的事——adopt 到手的 OAuth access_token 是一次性快照。
37
+ * 落盘与号池是 W6 的活,本模块只把兑换链的产物交出去。
38
+ *
39
+ * ---------------------------------------------------------------------------
40
+ * 规则 4 —— 认不出来的形状永远给一个**值**,永远不给异常。
41
+ * ---------------------------------------------------------------------------
42
+ * adopt 是「省一次登录」的快捷路径,**失败永不阻断**(计划 D6、R6):没有文件、
43
+ * 读不了、不是 JSON、形状不认识、解不开、以及**上游兑换失败**,全部落在
44
+ * `GlmAdoptResultDto` 的 `reason` 枚举里,卡片据此给出不同的下一步。
45
+ * 一个异常逃进设置路由只会变成 500,而「没找到本机登录态」不是错误,是正常状态。
46
+ *
47
+ * 唯一原样抛出的是**调用方自己发起的取消**(`options.signal` 已 aborted):那不是
48
+ * 「上游没结论」,把取消吞成 `exchange-failed` 会让 W6 以为上游真的拒绝了。
49
+ * 这条与 `oauth.ts` 的 `pollCliFlow` 同规则。
50
+ *
51
+ * ---------------------------------------------------------------------------
52
+ * 这份文件里最容易写错的地方:两区的令牌必须**按区取**,绝不能扫
53
+ * ---------------------------------------------------------------------------
54
+ * `credentials.json` 是**平铺**的一包字符串,而且同时装着 `oauth:zai:access_token` 与
55
+ * `oauth:bigmodel:access_token`——这是**两个部署各自独立的凭据**(计划 §1、§3.3)。
56
+ * 「在 JSON 里找到第一个 access_token」这种扫法会按序列化顺序挑中其中一个,
57
+ * 于是另一个部署的凭据被当成用户当前的订阅凭据送进兑换链——**串区**。
58
+ * 所以读取是对 `oauth:{区}:access_token` 的直接、定址查找,永远不是树遍历;
59
+ * 测试里放一份**两个区的令牌都在**的 fixture,并断言回来的是哪一个(两种取值顺序都覆盖:
60
+ * 一个懒惰的实现对其中一种会靠运气答对)。
61
+ */
62
+ import type { GlmAdoptResultDto, GlmRegion } from '../../shared/glm-coding-plan-contracts.ts';
63
+ import type { GlmCodingPlanKey } from './exchange.ts';
64
+ /**
65
+ * 改写 ZCode 数据根目录的环境变量(逐字:`resolveSharedZCodeCredentialsPath` 里的
66
+ * `ZCODE_DATA_BASE_DIR_ENV_KEY`)。客户端自己的优先级是 `baseDir ?? env ?? homedir()`,
67
+ * 这里照抄同一个口径。
68
+ */
69
+ export declare const ZCODE_DATA_BASE_DIR_ENV = "ZCODE_DATA_BASE_DIR";
70
+ /**
71
+ * 覆盖凭据加密密钥的环境变量(逐字:`credential-cipher.ts` 的 `CREDENTIAL_SECRET_ENV_KEY`)。
72
+ *
73
+ * **未设也能解密**:没有它时密钥由 `{platform}:{homedir}:{username}` 推导,
74
+ * 所以普通用户的 adopt 走得通(计划 §3.7 的最后一行)。
75
+ */
76
+ export declare const ZCODE_CREDENTIAL_SECRET_ENV = "ZCODE_CREDENTIAL_SECRET";
77
+ /** 凭据文件名(逐字:`shared-credentials.ts` 的 `resolveSharedZCodeCredentialsPath`)。 */
78
+ export declare const ZCODE_CREDENTIAL_FILE = "credentials.json";
79
+ /**
80
+ * 凭据文档里的八个键(逐字:`SHARED_ZCODE_CREDENTIAL_KEYS`)。
81
+ *
82
+ * `oauth:{zai|bigmodel}:*` 是**两个部署各自的**令牌,`oauth:active_provider` 说明
83
+ * 哪一区是当前活跃登录态。上游 `clearZaiLoginCredentials` 还透露了一件事:这个键
84
+ * 只在活跃区是 zai 时才存在(它会连同 zai 的三个键一起删掉)。
85
+ */
86
+ export declare const ZCODE_CREDENTIAL_KEYS: {
87
+ readonly activeProvider: "oauth:active_provider";
88
+ readonly zaiAccessToken: "oauth:zai:access_token";
89
+ readonly zaiRefreshToken: "oauth:zai:refresh_token";
90
+ readonly zaiUserInfo: "oauth:zai:user_info";
91
+ readonly bigmodelAccessToken: "oauth:bigmodel:access_token";
92
+ readonly bigmodelRefreshToken: "oauth:bigmodel:refresh_token";
93
+ readonly bigmodelUserInfo: "oauth:bigmodel:user_info";
94
+ readonly zcodeJwtToken: "zcodejwttoken";
95
+ };
96
+ /** 活跃区字面量 → 本线路的区域(`zai` → 国际区,`bigmodel` → 国区,计划 §3.1)。 */
97
+ export declare function regionForActiveProvider(value: string | undefined): GlmRegion | undefined;
98
+ /** 加密值前缀(逐字:`credential-cipher.ts` 的 `ENCRYPTED_VALUE_PREFIX`)。 */
99
+ export declare const ENCRYPTED_VALUE_PREFIX = "enc:v1:";
100
+ /** 加密算法(逐字:`CREDENTIAL_CIPHER_ALGORITHM`)。 */
101
+ export declare const CREDENTIAL_CIPHER_ALGORITHM = "aes-256-gcm";
102
+ /** iv 字节数(逐字:`CREDENTIAL_CIPHER_IV_BYTES`)。 */
103
+ export declare const CREDENTIAL_CIPHER_IV_BYTES = 12;
104
+ /** GCM 认证标签字节数(逐字:`CREDENTIAL_CIPHER_AUTH_TAG_BYTES`)。 */
105
+ export declare const CREDENTIAL_CIPHER_AUTH_TAG_BYTES = 16;
106
+ /** 一份 adopt 成功的凭据来自哪里,供卡片与号池记录。 */
107
+ export declare const ZCODE_CREDENTIAL_SOURCE = "zcode-client";
108
+ /** adopt 失败原因,直接取自冻结契约的 `GlmAdoptResultDto['reason']`,不另立一套。 */
109
+ export type GlmAdoptReason = NonNullable<GlmAdoptResultDto['reason']>;
110
+ /**
111
+ * 每个失败原因对应的一句可照做的下一步(给 W6 的 route 层与 W7 的设置卡片用)。
112
+ *
113
+ * 六个取值里 `secret-unavailable` 在本实现中**不会产生**,但契约已冻结了它,
114
+ * 这里仍给它一句文案:判据见 `zcodeCredentialFallbackSecret` 的注释——
115
+ * 密钥永远能从 platform + homedir + username 推出来,所以「拿不到密钥」
116
+ * 这个状态在本线路里根本不成立,它是为将来某个要求显式 secret 的变体预留的。
117
+ */
118
+ export declare const ADOPT_REASON_HINTS: Readonly<Record<GlmAdoptReason, string>>;
119
+ /**
120
+ * ZCode 的数据根目录。
121
+ *
122
+ * 变量优先级与客户端一致:非空的 `ZCODE_DATA_BASE_DIR` 说了算,空白值按未设置处理
123
+ * (否则在 Windows 上会解析成一个盘符相对路径),否则是 `os.homedir()`。
124
+ *
125
+ * 在**调用时**读,不是模块加载时:设完变量再导入本模块的测试也必须看得见。
126
+ */
127
+ export declare function zcodeDataBaseDir(env?: NodeJS.ProcessEnv): string;
128
+ /** 本模块会看的那一个凭据文件路径。 */
129
+ export declare function zcodeCredentialsPath(env?: NodeJS.ProcessEnv): string;
130
+ /**
131
+ * 候选路径,优先级最高的一个在前。
132
+ *
133
+ * 刻意**只有一条**候选,这是个明说的判断:这里的环境变量正是**客户端自己的**判定口径
134
+ * (客户端也是 `baseDir ?? env ?? homedir()`),多查一个常规路径等于去猜一个客户端
135
+ * 当前并未使用的配置,把另一个配置的凭据当成现在的登录态(照 `codex-adopt.ts` 同一条理由)。
136
+ * 实际查过的那条路径由 `zcodeCredentialPresence` 一并返回并由卡片打印,
137
+ * 所以「没找到」永远伴随着确切的位置。
138
+ *
139
+ * 纯函数,不碰文件系统,只为渲染卡片也可以安全调用。
140
+ */
141
+ export declare function zcodeCredentialPaths(env?: NodeJS.ProcessEnv): string[];
142
+ /** 启动阶段那个问题的答案:有没有东西可以 adopt。 */
143
+ export interface ZcodeCredentialPresence {
144
+ /** 有没有候选存在。它**不**对「能不能用」发表任何意见。 */
145
+ present: boolean;
146
+ /** 第一个存在的候选,或 null。 */
147
+ path: string | null;
148
+ /**
149
+ * 查过的每一个候选,按优先级,无论存不存在。
150
+ *
151
+ * 一起返回是为了让卡片打印它确切地看过哪些地方:只告诉用户第一个路径,
152
+ * 「没装 ZCode」与「你的 ZCode 存在别处」这两件事看起来完全一样。
153
+ */
154
+ searched: string[];
155
+ }
156
+ /**
157
+ * 本机有没有一份 ZCode 登录态可以提供。
158
+ *
159
+ * 可以在插件启动时安全调用:它**从不**以读方式打开任何候选,所以没 opt in 的用户,
160
+ * 他的令牌内容根本不会被读到。只有 stat 跑起来,它泄露的只是存在性与时间戳。
161
+ *
162
+ * 刻意**不**在这里回答「文件有效吗 / 过期吗 / 是不是 JSON」——那需要本模块存在的理由
163
+ * 之外的那次读取,而卡片不需要它:有效性由读取(opt in 之后)回答,并以
164
+ * 「凭据」或一个 `reason` 的形式落回卡片。
165
+ *
166
+ * paths 可注入,所以测试跑在 fixture 上而不是真实用户目录上;生产调用方取默认值。
167
+ */
168
+ export declare function zcodeCredentialPresence(paths?: readonly string[]): Promise<ZcodeCredentialPresence>;
169
+ /**
170
+ * 没有显式 secret 时用来推导密钥的那段字符串(逐字:`resolveCredentialSecret`)。
171
+ *
172
+ * 逐字格式是 `zcode-credential-fallback:{platform}:{homedir}:{username}`。
173
+ * 三段都由**本进程**取,所以「设了 `ZCODE_CREDENTIAL_SECRET` 的用户 adopt 会失败」
174
+ * (计划 R6)只在两种情况下发生:DSH host 与 ZCode 客户端跑在不同用户账号下,
175
+ * 或者客户端被显式配置了 secret 而 host 没有。这两种都表现为 `decrypt-failed`,
176
+ * 不是 `secret-unavailable`——密钥永远推得出来,推出来的那个解不开才是真实情况。
177
+ *
178
+ * `userInfo()` 抛错时退回 `unknown`,与上游逐字一致(它的注释写着:某些打包或沙箱
179
+ * 运行时解析不出系统用户名)。所以本函数**永不抛错**。
180
+ */
181
+ export declare function zcodeCredentialFallbackSecret(): string;
182
+ /**
183
+ * 凭据密钥:`sha256(secret)`,32 字节(逐字:`deriveCipherKey`)。
184
+ *
185
+ * `ZCODE_CREDENTIAL_SECRET` 去空白后非空才作数,否则用 `zcodeCredentialFallbackSecret()`。
186
+ */
187
+ export declare function deriveZcodeCredentialKey(env?: NodeJS.ProcessEnv): Buffer;
188
+ /**
189
+ * 解一个凭据值,**永不抛错**:解不开就是 undefined。
190
+ *
191
+ * 没带 `enc:v1:` 前缀的值按**明文**原样返回——上游 `decrypt` 正是这么做的,
192
+ * 旧版本客户端写下的明文值仍然可读(计划 §3.7 的「值可能是明文(旧值)」)。
193
+ *
194
+ * 带前缀时逐字复刻上游的校验顺序:正好三段、每段非空、iv 12 字节、tag 16 字节,
195
+ * 然后 `aes-256-gcm` 解密。**任何**一步不满足都返回 undefined 而不是异常——
196
+ * 上游抛的是 Error,但本模块的契约是一个值(规则 4)。
197
+ *
198
+ * 密钥由调用方传进来而不是在这里读 env:读取一次文件只该推导一次密钥,
199
+ * 而把这个纯函数原样暴露出去,测试就能用**同一套推导**造出真密文来验它。
200
+ */
201
+ export declare function decryptZcodeCredentialValue(value: string, key: Buffer): string | undefined;
202
+ /**
203
+ * 这枚 access_token 自己说过它过期了吗?
204
+ *
205
+ * RFC 7519 的 `exp` 以**秒**计,所以这里换算成毫秒再与 `now` 比——一千倍的单位差在
206
+ * 方向错的时候是静默的,而本模块的方向正是「宁可早判过期」。
207
+ * 只认有限且为正的数字;字符串、null、NaN、缺字段一律当「这份令牌没说过」= 没过期。
208
+ *
209
+ * **为什么「没说过」不判过期**,与 `codex-adopt.ts` 的做法相反、且是有意的:
210
+ * 那边的凭据要靠 `expiresAt` 维持可识别性,所以宁可拒收整份文档;这边没有这层需要——
211
+ * 计划 §3.6 记明上游**没有** refresh 接口,access_token 是一次性快照,而兑换链的第一步
212
+ * `POST {business}/api/auth/z/login {token}` 本身就是「这个 token 还有效吗」的真判据。
213
+ * 先判死只会把 adopt 从「省一次登录」变成「令牌不是 JWT 的机器上永远失败」。
214
+ * 不成立的代价由 `adoptZcodeSignIn` 里的 `token-expired` 兜住,而那一条的证据更强。
215
+ */
216
+ export declare function zcodeAccessTokenExpired(accessToken: string, now?: number): boolean;
217
+ /** 从文件里读到的那一份登录态。不含兑换产物——那是 `adoptZcodeSignIn` 的事。 */
218
+ export interface ZcodeLocalSignIn {
219
+ /** 确切读的是哪个文件,卡片照它显示。 */
220
+ readonly sourcePath: string;
221
+ readonly region: GlmRegion;
222
+ /** 兑换链的输入(计划 §3.2):它是 `POST /api/auth/z/login` 的 `token`。 */
223
+ readonly accessToken: string;
224
+ /**
225
+ * 同包里的 refresh_token。**只做留档与诊断**:计划 §3.6 记明上游没有刷新接口,
226
+ * 不要据此实现任何刷新路径。
227
+ */
228
+ readonly refreshToken: string | null;
229
+ /**
230
+ * ZCode 平台自己的 JWT(控制台口径 `zcodejwttoken`)。本线路不拿它做任何鉴权:
231
+ * 计划 §9 明确记录它的完整 payload 字段未验证,缺一份真实解码样本。
232
+ */
233
+ readonly zcodeJwtToken: string | null;
234
+ readonly userId: string | null;
235
+ readonly email: string | null;
236
+ readonly displayName: string | null;
237
+ /**
238
+ * 上游给了头像时只记「有」。
239
+ *
240
+ * 刻意**不取**原始字节:冻结契约 `GlmAccountDto` 的注释写明了这一点
241
+ * (「本插件不落 `user.avatar` 的原始字节,只保留是否已取到」)。
242
+ * 顺带说明一个容易误解的地方:上游 `ZaiLoginCredentialUser.avatar` 是**字符串**(头像地址),
243
+ * 不是字节流,所以这里连它都不往外带,只带一个布尔。
244
+ */
245
+ readonly hasAvatar: boolean;
246
+ /** 凭据文件最后被写入的时刻(Unix 毫秒)。它是 mtime,**不是**登录时刻,仅供诊断。 */
247
+ readonly sourceModifiedAt: number | null;
248
+ /** 本次读取的时刻(Unix 毫秒)。 */
249
+ readonly readAt: number;
250
+ }
251
+ /** 读取失败的三种形状,对应 `GlmAdoptResultDto` 里同样三个 reason。 */
252
+ export type ZcodeLocalReadFailure = 'no-credentials-file' | 'not-a-zcode-login' | 'decrypt-failed';
253
+ /** 读取的结果,永远是一个值(规则 4)。 */
254
+ export type ZcodeLocalRead = {
255
+ readonly ok: true;
256
+ readonly signIn: ZcodeLocalSignIn;
257
+ } | {
258
+ readonly ok: false;
259
+ readonly reason: ZcodeLocalReadFailure;
260
+ readonly sourcePath: string | null;
261
+ };
262
+ /**
263
+ * 读本机 ZCode 的登录态;第一个能用的候选获胜。
264
+ *
265
+ * **只在用户显式 opt in 之后调用**(见文件头的规则 2)。
266
+ *
267
+ * 永不抛出、永不 reject:文件缺失、读不了、不是 JSON、形状不认识、解不开,
268
+ * 全部落成上面那个失败值。设置路由不该因为用户的文件正被写到一半而失败。
269
+ *
270
+ * 解不开的值一律判 `decrypt-failed` 而不是「不是 ZCode 登录」:那份文件**确实**是
271
+ * 一份 ZCode 登录态,只是本进程算不出它的密钥——这两个原因给用户的下一步不同。
272
+ */
273
+ export declare function readZcodeLocalSignIn(paths?: readonly string[], env?: NodeJS.ProcessEnv): Promise<ZcodeLocalRead>;
274
+ /** adopt 成功时交出去的那份凭据。host 侧专用,凭据不过共享契约边界。 */
275
+ export interface GlmAdoptedCredential {
276
+ /**
277
+ * 号池标记,每一次快照上都是字面量 `true`。
278
+ *
279
+ * 是一个**判别式**而不是一句描述:存了这个记录的号池必须拒绝刷新它,
280
+ * 而一个能拿来判断的布尔比一句能被读过去的注释更难被忽略。
281
+ */
282
+ readonly adopted: true;
283
+ /** 这份快照来自哪一处本地登录态。 */
284
+ readonly source: typeof ZCODE_CREDENTIAL_SOURCE;
285
+ /** 读的是哪个文件,卡片照它显示。 */
286
+ readonly sourcePath: string;
287
+ readonly region: GlmRegion;
288
+ /** 兑换链的输入,只做留档与诊断。 */
289
+ readonly accessToken: string;
290
+ /** 见 `ZcodeLocalSignIn.refreshToken`:没有刷新接口,不要据此实现刷新。 */
291
+ readonly refreshToken: string | null;
292
+ readonly zcodeJwtToken: string | null;
293
+ readonly userId: string | null;
294
+ readonly email: string | null;
295
+ readonly displayName: string | null;
296
+ readonly hasAvatar: boolean;
297
+ /**
298
+ * 本插件接管这份登录态的时刻(Unix 毫秒)。
299
+ *
300
+ * 它**不是**上游的登录时刻——那份文件里没有这个信息,凭据文件的 mtime 也只是
301
+ * 「最后一次写盘」而不是「最后一次登录」。宁可给一个诚实的口径,
302
+ * 也不要让一个看起来更精确、其实是猜的数被卡片显示出来。
303
+ */
304
+ readonly authenticatedAt: number;
305
+ /**
306
+ * 兑换链的产物,与浏览器登录产出的是**同一种东西**(计划 D4)。
307
+ *
308
+ * 里面已经包含验活结果:拿不到 Coding Plan 套餐的账号在这一步就报错,
309
+ * 而不是带着一把「能创建但不能用」的 Key 进对话。
310
+ */
311
+ readonly codingPlan: GlmCodingPlanKey;
312
+ }
313
+ /** adopt 一次的结果:契约 DTO(给 route 与客户端)加上 host 侧的凭据(只给 W6)。 */
314
+ export interface GlmAdoptOutcome {
315
+ readonly result: GlmAdoptResultDto;
316
+ readonly adopted: GlmAdoptedCredential | null;
317
+ }
318
+ export interface GlmAdoptOptions {
319
+ /** 注入的候选路径;测试跑在 fixture 上,生产调用方取默认值。 */
320
+ paths?: readonly string[];
321
+ /** 环境变量(含 `ZCODE_DATA_BASE_DIR` / `ZCODE_CREDENTIAL_SECRET` 与 `DSH_GLM_*`)。 */
322
+ env?: NodeJS.ProcessEnv;
323
+ /** 注入的 fetch;测试全部走它,所以本文件离网。 */
324
+ fetchFn?: typeof fetch;
325
+ signal?: AbortSignal;
326
+ /** 单次上游请求超时;缺省走 `exchange.ts` 的 `DISCOVERY_TIMEOUT_MS`。 */
327
+ timeoutMs?: number;
328
+ }
329
+ /**
330
+ * adopt:读本机 ZCode 的登录态,解出 access_token,走**与浏览器登录完全相同**的那一套兑换。
331
+ *
332
+ * 「完全相同」是硬要求(计划 D4、D6):兑换链只写在 `exchange.ts` 一处,
333
+ * adopt 不另写一份。两者的产物都是一把**已经验过活**的 Coding Plan Key,
334
+ * 下游(W6 的 token-store / account-pool)因此完全不需要区分凭据是登进来的还是捡回来的。
335
+ *
336
+ * 失败**永不阻断**(计划 D6、R6):每一种失败都变成 `GlmAdoptResultDto` 的一个 reason,
337
+ * 卡片据此引导用户回落浏览器登录。唯一的例外是调用方自己发起的取消,原样抛出
338
+ * (见文件头的规则 4)。
339
+ *
340
+ * `attempted` 的口径:**找到凭据文件并进入了读取之后**为 true。
341
+ * 只有 `no-credentials-file` 是 false —— 那时候根本没有 adopt 可谈,
342
+ * 机器上什么都没装是最常见也最不需要打扰用户的状态。
343
+ */
344
+ export declare function adoptZcodeSignIn(options?: GlmAdoptOptions): Promise<GlmAdoptOutcome>;
345
+ /**
346
+ * 一份值是不是本模块产出的 adopt 快照。
347
+ *
348
+ * 号池用它判断一条已存记录是否**永远**不得刷新(本线路上游本来也没有刷新接口,
349
+ * 所以「不得刷新」正是 adopt 账号的正确处置)。它只认本模块的快照:
350
+ * 一份本插件自己登出来的凭据没有 `adopted` 字段,绝不能被误认成可丢弃的快照,
351
+ * 否则用户在设置里登的号会被当成外来物丢掉。
352
+ */
353
+ export declare function isAdoptedZcodeCredential(value: unknown): value is GlmAdoptedCredential;
354
+ //# sourceMappingURL=adopt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adopt.d.ts","sourceRoot":"","sources":["../../../../src/host/glm-coding-plan/adopt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 业务 JWT 缓存(计划 §3.6)。
3
+ *
4
+ * 这一层的存在理由是上游的**两个坏消息**里的好消息:
5
+ *
6
+ * 1. OAuth access_token **不能刷新**——官方 `oauthService.ts:972-975` 对刷新直接抛
7
+ * 「OAuth 暂未提供 refresh token 交换接口,请重新登录」,两个 adapter 也都没实现
8
+ * `refreshToken()`(计划 §3.6、R3)。所以 access_token 过期只能重登。
9
+ * 2. 业务 JWT **可以重取**:`POST {business}/api/auth/z/login` 拿 access_token 再换一次,
10
+ * 响应带 `expires_in`。官方自己按「提前 120 秒 + 0~60 秒随机抖动」重取
11
+ * (`zai-business-token-cache.ts:64-73`,证据等级 A)。
12
+ *
13
+ * 不自己缓存这一层的后果很具体:access_token 还在,业务 JWT 却先过期,
14
+ * 用户在一个长会话的中途被踢出登录态,而上游明确说了它不会帮你续。
15
+ *
16
+ * **这一层只服务国际区。** 逐字:TriDefender/zcode-api 的 bigmodel 分支(国区)
17
+ * 跳过 `/api/auth/z/login`,直接拿裸 OAuth access_token 打 `/api/biz/*`——
18
+ * 国区压根没有业务 JWT 可缓存。所以本缓存对国区账号直接报错,而不是去
19
+ * bigmodel.cn 反复打一个那条链路根本不用的端点。
20
+ *
21
+ * 抖动的作用是防尖峰:同一批登录的多个账号会在同一秒一起到期,没有抖动就是一次尖峰。
22
+ *
23
+ * 并发处置照 kimi-code 的单飞写法(`ensureAccessToken`):同一账号的并发调用共享一次重取,
24
+ * 否则一次工具调用风暴会在到期瞬间打出 N 个 `z/login`。
25
+ */
26
+ import type { GlmRegion } from '../../shared/glm-coding-plan-contracts.ts';
27
+ import { type GlmExchangeOptions } from './exchange.ts';
28
+ /** 缓存里的一条业务 JWT。host 侧专用,不进共享契约。 */
29
+ export interface GlmBizTokenEntry {
30
+ token: string;
31
+ /** 失效时刻(Unix 毫秒),来自 `expires_in`。 */
32
+ expiresAt: number;
33
+ /** 上游给的 `expires_in`(秒),只做诊断用。 */
34
+ expiresInSec: number;
35
+ /** 取到它的时刻(Unix 毫秒)。 */
36
+ fetchedAt: number;
37
+ }
38
+ /**
39
+ * 重取时刻的随机抖动秒数:`[0, 60)`(逐字:`zai-business-token-cache.ts:64-73`)。
40
+ *
41
+ * 抽成带 `random` 入参的函数是为了让测试能钉住抖动值——用真实随机数的话,
42
+ * 「提前量是 120~180 秒」这条断言每次跑都可能落在边界附近。
43
+ */
44
+ export declare function refreshJitterSeconds(random?: () => number): number;
45
+ /**
46
+ * 提前量:120 秒(官方固定值)+ 本次抽到的抖动秒数。
47
+ *
48
+ * 提前量不随 `expires_in` 变化:官方用的是固定 120 秒,本线路照抄。
49
+ * 只在一处算、每条缓存各自持有一个抖动值,是为了同一账号的判断在整段生命周期里稳定,
50
+ * 否则每次调用重抽抖动会让「还有 130 秒就不刷新」的判定随机翻面。
51
+ */
52
+ export declare function refreshThresholdMs(jitterSeconds: number): number;
53
+ /** 一个账号的身份:缓存按它分桶,两区账号各自一份。 */
54
+ export interface GlmBizTokenIdentity {
55
+ /** 账号的稳定键(号池里的 accountId 或登录会话 id)。 */
56
+ accountKey: string;
57
+ region: GlmRegion;
58
+ }
59
+ /**
60
+ * 一个账号的业务 JWT 缓存。
61
+ *
62
+ * 实例化而不是模块级单例:一个号池可以同时持有两区多个账号(计划 §1、§8.2),
63
+ * 单例只能存一份那种凭据,而区域是凭据的属性,混在一份里等于允许串区。
64
+ *
65
+ * 但**重取**是跨实例单飞的(见文件末尾的 `inFlightBizTokenFetches`):
66
+ * 两个持有同一账号的组件各自建了缓存对象时,仍然只该打一次上游。
67
+ */
68
+ export declare class GlmBizTokenCache {
69
+ readonly accountKey: string;
70
+ readonly region: GlmRegion;
71
+ private readonly flightKey;
72
+ private entry;
73
+ private readonly jitterSeconds;
74
+ private readonly thresholdMs;
75
+ constructor(identity: GlmBizTokenIdentity, options?: {
76
+ random?: () => number;
77
+ });
78
+ /** 本缓存抽到的抖动秒数(诊断用,也让测试能断言抖动确实落在 0~60)。 */
79
+ get jitter(): number;
80
+ /** 当前缓存里有没有一条还没到提前量的业务 JWT。 */
81
+ peek(now?: number): GlmBizTokenEntry | null;
82
+ /** 直接放进去一条(登录或 adopt 拿到业务 JWT 时用)。 */
83
+ put(entry: GlmBizTokenEntry): void;
84
+ /** 丢掉缓存。access_token 重登之后必须调,否则会继续用上一段登录态的 JWT。 */
85
+ clear(): void;
86
+ /** 有效期判定:剩余时间严格大于提前量才算新鲜。 */
87
+ private isFresh;
88
+ /**
89
+ * 返回一条可用的业务 JWT,必要时重取。
90
+ *
91
+ * 重取失败不写缓存:宁可让这一次调用失败,也不能拿一条没验证过的 JWT 顶上去。
92
+ */
93
+ ensure(input: {
94
+ accessToken: string;
95
+ }, options?: GlmExchangeOptions): Promise<GlmBizTokenEntry>;
96
+ }
97
+ /** 测试缝:忘掉所有在飞的重取。 */
98
+ export declare function resetInFlightBizTokenFetches(): void;
99
+ //# sourceMappingURL=biz-token-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"biz-token-cache.d.ts","sourceRoot":"","sources":["../../../../src/host/glm-coding-plan/biz-token-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2CAA2C,CAAA;AAK1E,OAAO,EAAmC,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAExF,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAA;IACpB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,MAAM,MAAoB,GAAG,MAAM,CAG/E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,+BAA+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,SAAS,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;gBAExB,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAQlF,0CAA0C;IAC1C,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,+BAA+B;IAC/B,IAAI,CAAC,GAAG,GAAE,MAAmB,GAAG,gBAAgB,GAAG,IAAI;IAMvD,sCAAsC;IACtC,GAAG,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAIlC,mDAAmD;IACnD,KAAK,IAAI,IAAI;IAIb,6BAA6B;IAC7B,OAAO,CAAC,OAAO;IAIf;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAwC1G;AAWD,qBAAqB;AACrB,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}