@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.
Files changed (79) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +49 -1
  3. package/lib/client.js +3535 -1191
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +7225 -2833
  6. package/lib/types/client/hub/brand-icons.d.ts +17 -1
  7. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  8. package/lib/types/client/hub/hub-styles.d.ts.map +1 -1
  9. package/lib/types/client/hub/providers.d.ts.map +1 -1
  10. package/lib/types/client/index.d.ts +2 -0
  11. package/lib/types/client/index.d.ts.map +1 -1
  12. package/lib/types/client/minimax-code/VideoFormDialog.d.ts +46 -50
  13. package/lib/types/client/minimax-code/VideoFormDialog.d.ts.map +1 -1
  14. package/lib/types/client/minimax-code/locales.d.ts +80 -20
  15. package/lib/types/client/minimax-code/locales.d.ts.map +1 -1
  16. package/lib/types/client/minimax-code/styles.d.ts.map +1 -1
  17. package/lib/types/client/minimax-code/video-command-host.d.ts +17 -11
  18. package/lib/types/client/minimax-code/video-command-host.d.ts.map +1 -1
  19. package/lib/types/client/minimax-code/video-command-icon.d.ts +38 -0
  20. package/lib/types/client/minimax-code/video-command-icon.d.ts.map +1 -0
  21. package/lib/types/client/minimax-code/video-command.d.ts +53 -59
  22. package/lib/types/client/minimax-code/video-command.d.ts.map +1 -1
  23. package/lib/types/client/minimax-code/video-media-dropzone.d.ts +57 -0
  24. package/lib/types/client/minimax-code/video-media-dropzone.d.ts.map +1 -0
  25. package/lib/types/client/minimax-code/video-media-input.d.ts +173 -0
  26. package/lib/types/client/minimax-code/video-media-input.d.ts.map +1 -0
  27. package/lib/types/client/minimax-code/video-presets.d.ts +67 -0
  28. package/lib/types/client/minimax-code/video-presets.d.ts.map +1 -0
  29. package/lib/types/client/minimax-code/video-workspace.d.ts +41 -0
  30. package/lib/types/client/minimax-code/video-workspace.d.ts.map +1 -0
  31. package/lib/types/client/qoder/QoderComposerQuota.d.ts +41 -0
  32. package/lib/types/client/qoder/QoderComposerQuota.d.ts.map +1 -0
  33. package/lib/types/client/qoder/QoderSection.d.ts +26 -0
  34. package/lib/types/client/qoder/QoderSection.d.ts.map +1 -0
  35. package/lib/types/client/qoder/api.d.ts +4 -0
  36. package/lib/types/client/qoder/api.d.ts.map +1 -0
  37. package/lib/types/client/qoder/locales.d.ts +310 -0
  38. package/lib/types/client/qoder/locales.d.ts.map +1 -0
  39. package/lib/types/client/qoder/styles.d.ts +16 -0
  40. package/lib/types/client/qoder/styles.d.ts.map +1 -0
  41. package/lib/types/client/qoder/useQoderSection.d.ts +97 -0
  42. package/lib/types/client/qoder/useQoderSection.d.ts.map +1 -0
  43. package/lib/types/host/common/model-request-control.d.ts +1 -1
  44. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  45. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  46. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  47. package/lib/types/host/kimi-code/adapter.d.ts +3 -1
  48. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  49. package/lib/types/host/kimi-code/mapper.d.ts.map +1 -1
  50. package/lib/types/host/qoder/account-pool.d.ts +137 -0
  51. package/lib/types/host/qoder/account-pool.d.ts.map +1 -0
  52. package/lib/types/host/qoder/adapter.d.ts +134 -0
  53. package/lib/types/host/qoder/adapter.d.ts.map +1 -0
  54. package/lib/types/host/qoder/adopt.d.ts +146 -0
  55. package/lib/types/host/qoder/adopt.d.ts.map +1 -0
  56. package/lib/types/host/qoder/client.d.ts +189 -0
  57. package/lib/types/host/qoder/client.d.ts.map +1 -0
  58. package/lib/types/host/qoder/cosy.d.ts +173 -0
  59. package/lib/types/host/qoder/cosy.d.ts.map +1 -0
  60. package/lib/types/host/qoder/mapper.d.ts +235 -0
  61. package/lib/types/host/qoder/mapper.d.ts.map +1 -0
  62. package/lib/types/host/qoder/model-catalog.d.ts +185 -0
  63. package/lib/types/host/qoder/model-catalog.d.ts.map +1 -0
  64. package/lib/types/host/qoder/oauth.d.ts +134 -0
  65. package/lib/types/host/qoder/oauth.d.ts.map +1 -0
  66. package/lib/types/host/qoder/routes.d.ts +96 -0
  67. package/lib/types/host/qoder/routes.d.ts.map +1 -0
  68. package/lib/types/host/qoder/token-store.d.ts +231 -0
  69. package/lib/types/host/qoder/token-store.d.ts.map +1 -0
  70. package/lib/types/host/qoder/types.d.ts +257 -0
  71. package/lib/types/host/qoder/types.d.ts.map +1 -0
  72. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
  73. package/lib/types/host/workbuddy/mapper.d.ts +1 -0
  74. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
  75. package/lib/types/index.d.ts +5 -0
  76. package/lib/types/index.d.ts.map +1 -1
  77. package/lib/types/shared/qoder-contracts.d.ts +293 -0
  78. package/lib/types/shared/qoder-contracts.d.ts.map +1 -0
  79. package/package.json +1 -1
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Qoder(订阅)线路的 COSY 签名与请求 codec。
3
+ *
4
+ * 本文件是整条线路协议风险最高的部分,所以它的每一步都被一份**全合成 frozen
5
+ * fixture** 锁成 byte-exact(`test/qoder-cosy.test.ts`,fixture 来自
6
+ * Liki4/qodercli2api `testdata/protocol/1.1.34/`,逐字算法见它的
7
+ * `docs/oauth.md` §5 与 `docs/inference-protocol.md` §2-§4):
8
+ *
9
+ * - {@link generateQoderRuntimeAuthFields} —— runtime-fields.json;
10
+ * - {@link decryptQoderAdoptPayload} —— credential.json;
11
+ * - {@link encodeQoderBody} —— infer-user-no-org.json 的 `body_string`;
12
+ * - {@link buildQoderCosyHeaders} —— infer-user-no-org.json 的 18 头集合与签名。
13
+ *
14
+ * 与 pi-provider-qoder `src/cosy.ts` 的分歧(它多发 `Cosy-Machineos` /
15
+ * `Cosy-Clientip` / `Cosy-Bodyhash` / `Cosy-Bodylength` / `Cosy-Sigpath` /
16
+ * `X-Request-Id` 六个头)以 **fixture 为准**:那是官方 pinned WASM 的头集合,
17
+ * pi 的超集只是「被服务端容忍」,不是协议的一部分(计划 §9 风险 3)。
18
+ *
19
+ * 本文件不做任何网络 I/O,也不持有凭据:它吃的是调用方递进来的材料,吐出的是
20
+ * 头与 body。machine id 的读取顺序(qodercli 原配 → 本线路自己的文件 → 内存生成)
21
+ * 见 {@link resolveQoderMachineId}。
22
+ */
23
+ /**
24
+ * runtime RSA-1024 SPKI 公钥(**非秘密公钥**)。
25
+ *
26
+ * 逐字来源:qodercli2api `docs/oauth.md` §5(从 pinned v1.1.34 WASM 提取,
27
+ * SPKI DER 162 bytes,SHA-256 `a6a4aa468d90c618966e38122abfb06e566c18c9b21090d61f3e9b796b33e569`),
28
+ * 与 pi-provider `cosy.ts` 内嵌的那把逐字相同。用途只有一个:PKCS#1 v1.5 加密
29
+ * 每次请求的 runtime AES key(16 个 ASCII 字符),产物是 `Cosy-Key` 头与签名的
30
+ * `key` 分量。**不要**与同一 WASM 里另一把 2048-bit profile RSA 公钥混淆。
31
+ */
32
+ export declare const QODER_RUNTIME_RSA_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA8iMH5c02LilrsERw9t6Pv5Nc\n4k6Pz1EaDicBMpdpxKduSZu5OANqUq8er4GM95omAGIOPOh+Nx0spthYA2BqGz+l\n6HRkPJ7S236FZz73In/KVuLnwI8JJ2CbuJap8kvheCCZpmAWpb/cPx/3Vr/J6I17\nXcW+ML9FoCI6AOvOzwIDAQAB\n-----END PUBLIC KEY-----";
33
+ /**
34
+ * 官方的字节变换(逐字 docs/oauth.md §5.1 与 inference-protocol.md §3.1):
35
+ * RNG bytes `R[16]` 逐字节反转为 `U[i]=R[15-i]`,再置 UUID 版本/变体位
36
+ * `U[6]=(U[6]&0x0f)|0x40`、`U[8]=(U[8]&0x3f)|0x80`。
37
+ *
38
+ * 同一段变换服务两个产物:runtime AES key(hex 的前 16 个字符)与
39
+ * COSY request UUID(完整 hex 按 8-4-4-4-12 格式化)。
40
+ */
41
+ export declare function qoderMaskedBytes(entropy: Buffer): Buffer;
42
+ /**
43
+ * 熵 → COSY request UUID。
44
+ *
45
+ * 逐字 inference-protocol.md §3.1:完整 masked U 按 lowercase `8-4-4-4-12` 格式化,
46
+ * 不再反转字段内部字节。每次 prepare 重新生成且不得复用(服务端对重复 `request_id`
47
+ * 返回 code 103)。
48
+ */
49
+ export declare function qoderRequestUuid(entropy: Buffer): string;
50
+ /**
51
+ * 一次请求的 runtime 凭据材料。
52
+ *
53
+ * 逐字 docs/oauth.md §5(frozen fixture `runtime-fields.json` 锁定):
54
+ *
55
+ * 1. `K` = {@link qoderMaskedBytes} 的 lowercase hex 的前 16 个字符,作为
56
+ * **16 个 ASCII 字节**使用(绝不再做 hex decode);
57
+ * 2. `encryptUserInfo` = AES-128-CBC(key=IV=K 的原始字节,PKCS#7)加密
58
+ * 调用方给的 **raw UTF-8 字符串原样**(不 parse / 不 re-marshal——字段顺序、
59
+ * 空白和转义都进 AES 明文),输出 strict standard padded Base64;
60
+ * 3. `key` = 用 {@link QODER_RUNTIME_RSA_PUBLIC_KEY}(PKCS#1 v1.5)加密 `K`
61
+ * 的 standard padded Base64。
62
+ *
63
+ * @param rawUserInfo - UserInfo 的原始 JSON 串(原样加密,不规范化)。
64
+ * @param entropy - 测试缝:16 字节熵的供应处(fixture 用固定 tape 驱动)。
65
+ */
66
+ export declare function generateQoderRuntimeAuthFields(rawUserInfo: string, entropy?: () => Buffer): {
67
+ encryptUserInfo: string;
68
+ key: string;
69
+ };
70
+ /**
71
+ * 解密一份 qodercli 的凭据文件内容。
72
+ *
73
+ * 格式(逐字 docs/oauth.md §4):strict standard padded Base64 信封 →
74
+ * AES-128-CBC(key=IV=`machineId` 前 16 个字符的 raw UTF-8 字节,PKCS#7 严格校验)。
75
+ * 官方加载兼容:去除外围空白后以 `{` 开头的内容按明文 JSON 解析——本函数同一行为。
76
+ *
77
+ * 解密失败(格式不对、padding 坏了、key 不对)抛错;调用方(adopt.ts)把它归到
78
+ * `invalid-credentials`,**绝不在这里猜第二把 key**。
79
+ */
80
+ export declare function decryptQoderAdoptPayload(content: string, machineId: string): string;
81
+ /**
82
+ * 请求体编码:raw bytes → standard padded Base64 → alphabet 逐位置映射(`=`→`$`)
83
+ * → 外三段交换(`k=floor(len/3)`,输出 `s[len-k:] + s[k:len-k] + s[:k]`)。
84
+ *
85
+ * 它是**可逆的确定性编码,不是加密**(inference-protocol.md §4 原话);算法不依赖
86
+ * user、model、clock 或 randomness,输入的每个字节(含字段顺序与空白)都进输出——
87
+ * 所以调用方一旦拼好 JSON 就不要再经任何一轮 parse/re-marshal。
88
+ *
89
+ * @returns ASCII 字符串(签名参与的就是这串字符的 UTF-8 字节,两者相同)。
90
+ */
91
+ export declare function encodeQoderBody(raw: string | Buffer): string;
92
+ /** COSY 签名身份材料(一次请求一份)。 */
93
+ export interface QoderCosyIdentity {
94
+ /** `Cosy-User`:上游账号 id。 */
95
+ uid: string;
96
+ /** UserInfo 的原始 JSON 串(被 AES 进 payload 的 `info`)。 */
97
+ rawUserInfo: string;
98
+ /** `Cosy-MachineId` / `Cosy-MachineToken`(同一个 36 字符 UUID)。 */
99
+ machineId: string;
100
+ /** `Cosy-Version`:当前生效的 COSY 版本(跟随 CLI release,client.ts 管)。 */
101
+ cosyVersion: string;
102
+ /** `Cosy-Data-Policy`:true → `agree`,否则 `disagree`。 */
103
+ dataPolicyAgreed: boolean;
104
+ /** 组织 id;非空才出现在头里(presence 矩阵,docs/inference-protocol.md §2)。 */
105
+ organizationId?: string | null;
106
+ /** 组织 tags;长度大于零才出现在头里(逗号连接)。 */
107
+ organizationTags?: readonly string[];
108
+ }
109
+ /**
110
+ * COSY 签名(inference-protocol.md §3.3,fixture 锁定)。
111
+ *
112
+ * canonical bytes 精确为 `payloadB64 LF key LF unixSeconds LF encodedBody LF signedPath`
113
+ * ——五段、四个 `\n`,**末尾没有 LF**;签名是小写 hex MD5。
114
+ */
115
+ export declare function qoderCosySignature(input: {
116
+ payloadB64: string;
117
+ key: string;
118
+ unixSeconds: string;
119
+ encodedBody: string;
120
+ signedPath: string;
121
+ }): string;
122
+ /**
123
+ * 一个 URL 的签名路径:pathname 剥掉 `/algo` 前缀,不含 query
124
+ * (inference-protocol.md §1:签名 path 固定为 `/api/v2/service/pro/sse/agent_chat_generation`;
125
+ * pi-provider `computeSigPath` 把它泛化成逐 URL 计算,模型列表 GET 的签名照同一规则)。
126
+ */
127
+ export declare function cosySignedPath(url: string): string;
128
+ export interface QoderCosyHeadersOptions {
129
+ /** 已编码的 body(GET 无 body 时为空串)。 */
130
+ encodedBody: string;
131
+ /** 完整请求 URL(签名路径从这里剥)。 */
132
+ url: string;
133
+ /** Accept 头:对话是 `text/event-stream`,模型列表是 `application/json`。 */
134
+ accept: string;
135
+ /** 是否带 `Content-Type: application/json`(有 body 的 POST 才带)。 */
136
+ withContentType: boolean;
137
+ /** 上行模型 key(`X-Model-Key`);空/缺省时连同 `X-Model-Source` 一起省略。 */
138
+ modelKey?: string;
139
+ /** 模型来源(`X-Model-Source`,通常 `system` 或 `custom`)。 */
140
+ modelSource?: string;
141
+ /** 测试缝:时钟(Unix 毫秒)。 */
142
+ now?: number;
143
+ /** 测试缝:runtime fields 的熵(fixture 驱动)。 */
144
+ entropy?: () => Buffer;
145
+ /** 测试缝:request UUID 的熵(fixture 驱动)。缺省与 runtime fields 共用一段。 */
146
+ requestEntropy?: () => Buffer;
147
+ }
148
+ /**
149
+ * 一套完整的 COSY 请求头(条件 presence 逐字 inference-protocol.md §2 的矩阵)。
150
+ *
151
+ * payload 的字段顺序固定(`version, requestId, info, cosyVersion, ideVersion`——
152
+ * 它参与签名,散装键序就是一次签名漂移),所以这里按字面量顺序写键而不是用 Map。
153
+ */
154
+ export declare function buildQoderCosyHeaders(identity: QoderCosyIdentity, options: QoderCosyHeadersOptions): Record<string, string>;
155
+ /** qodercli 的 machine_id 文件路径(本插件**只读**它,理由见 adopt.ts 的文件头)。 */
156
+ export declare function qoderCliMachineIdPath(env?: NodeJS.ProcessEnv): string;
157
+ /** 读取qodercli 原配的 machine id(不存在时 null;只读)。 */
158
+ export declare function readQoderCliMachineId(env?: NodeJS.ProcessEnv): string | null;
159
+ /**
160
+ * 本线路发请求用的 machine id。
161
+ *
162
+ * 读取顺序(逐字 pi-provider `cosy.ts` `getMachineId` 的同一决定):
163
+ *
164
+ * 1. qodercli 的原配 `~/.qoder/.auth/machine_id`(**只读**)——adopt 来的凭据
165
+ * 与它配套,同一个 id 让账号在两边的审计记录里是同一条;
166
+ * 2. 本线路自己持久化的那份(`storages/qoder-machine-id`);
167
+ * 3. 都没有时生成一个并尽力落盘(落不了盘就用内存里的那份——身份在一次
168
+ * 进程内一致即可,持久化失败不值得让请求失败)。
169
+ */
170
+ export declare function resolveQoderMachineId(env?: NodeJS.ProcessEnv): string;
171
+ /** 测试用:清掉进程内记忆。 */
172
+ export declare function clearQoderMachineIdMemo(): void;
173
+ //# sourceMappingURL=cosy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosy.d.ts","sourceRoot":"","sources":["../../../../src/host/qoder/cosy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Qoder(订阅)线路对话端的 wire mapping。
3
+ *
4
+ * 这条线路的协议面**不是** OpenAI:它把一份 OpenAI 形状的对话包进 Qoder 自己的
5
+ * \`RemoteChatAsk\` 信封,过一道自定义 body codec,再用 COSY 头签名(cosy.ts)。
6
+ * 本文件只管「对话形状 ⇄ 信封里的 \`messages\`/\`parameters\`」与「信封 SSE ⇄ DSH chunks」;
7
+ * 签名与编码在 cosy.ts,传输在 adapter.ts——三层各管一层,是因为每层都有独立的
8
+ * fixture/测试锁定它(cosy 层有 frozen fixtures,本层有 mapper 测试)。
9
+ *
10
+ * 逐字来源(docs/qoder-line-plan.md §3.5/§3.6):
11
+ *
12
+ * - 请求信封字段集:qodercli2api \`infer-user-no-org.json\` 的 \`body_raw\`(frozen fixture);
13
+ * - \`parameters\` 里的思考控制:pi-provider \`stream.ts\`(\`enable_thinking\` 布尔 +
14
+ * \`reasoning_effort\` 档位,仅当目录的 \`thinking_config.enabled.efforts\` 存在才发档位);
15
+ * - 响应信封:\`data:{headers, body, statusCodeValue}\`,\`body\` 再解析出 OpenAI chunk;
16
+ * \`[DONE]\` 两种形态(裸帧与信封 \`body:"[DONE]"\`)都结束读取;\`event:finish\` 是
17
+ * 权威终止事件(qodercli2api §7);
18
+ * - usage 口径:\`prompt_tokens\` **含** \`cached_tokens\`,DSH 互斥计数所以减回去
19
+ * (pi-provider \`stream.ts\` 同一减法)。
20
+ *
21
+ * 与 step-code mapper 同源的辅助(消息投影、图片预算、tool_call 聚合)按同一套写,
22
+ * 因为上游信封里装的确实是 OpenAI 形状的消息数组。
23
+ *
24
+ * @module dsh-chatgpt-subscription/qoder-mapper
25
+ */
26
+ import type { QoderModelEntry } from './model-catalog.ts';
27
+ import { LlmError, type GenerateOptions, type OutboundContentBlock, type StreamChunk, type TokenUsage } from '../common/llm-compat.ts';
28
+ import { resolveRequestImages, type AttachmentImageReader, type ResolvedRequestImage, type ResolvedRequestImages } from '../common/request-images.ts';
29
+ export { resolveRequestImages };
30
+ export type { AttachmentImageReader, ResolvedRequestImage, ResolvedRequestImages };
31
+ /**
32
+ * 一次请求允许携带的 Base64 图片总量。
33
+ *
34
+ * Qoder 没有公开的逐请求图片上限,所以这是防御性预算而不是文档化限制:
35
+ * 与 step-code 取同一个 12 MB,让失控的对话降级成占位文本而不是拖垮传输。
36
+ */
37
+ export declare const QODER_MAX_REQUEST_IMAGE_BYTES: number;
38
+ /** 共享图片预算,供想要插件保守上限的调用方使用。 */
39
+ export declare const QODER_SHARED_IMAGE_BUDGET = 1500000;
40
+ /**
41
+ * \`request_id\` 重复时上游报的业务错误码(逐字 qodercli2api §5:
42
+ * 服务端对重复值返回 code 103)。它**不是**可安全自动重试的状态——
43
+ * 重试必须用新的 request_id,而不是重发同一份。
44
+ */
45
+ export declare const QODER_ERROR_DUPLICATE_REQUEST_ID = "103";
46
+ /** token 失效的业务错误码(CN 报「Login expired」,pi-provider oauth.ts 注释)。 */
47
+ export declare const QODER_ERROR_LOGIN_EXPIRED = "105";
48
+ /**
49
+ * 响应侧「缓存读入量」的解析优先级,从先到后。
50
+ *
51
+ * 1. \`prompt_tokens_details.cached_tokens\` —— OpenAI 规范的字段(pi-provider 的主读法);
52
+ * 2. **顶层 \`cached_tokens\`** —— Qoder 网关可能直接平铺(step-code 线路的教训:
53
+ * 官方文档只展示顶层字段时,它必须在自己那条分支上被认);
54
+ * 3. \`prompt_cache_hit_tokens\` —— DeepSeek 一族网关的命名(Qoder 的部分模型上游是它);
55
+ * 4. 0。
56
+ *
57
+ * 都不命中时取 0:0 表示「上游没报」,而不是「一定没命中」。
58
+ */
59
+ export declare const QODER_CACHE_READ_FIELD_ORDER: readonly ["prompt_tokens_details.cached_tokens", "cached_tokens", "prompt_cache_hit_tokens"];
60
+ /**
61
+ * 一次请求超过 {@link QODER_MAX_REQUEST_IMAGE_BYTES} 时,把最旧的内联图片换成
62
+ * 可见占位文本。持久历史不动,只有即将发出的这份请求变(与兄弟线路同一机制)。
63
+ */
64
+ export declare function offloadOldestQoderRequestImages(options: GenerateOptions, images?: ResolvedRequestImages): GenerateOptions;
65
+ /** 信封里的消息形状(OpenAI Chat Completions 词汇)。 */
66
+ type QoderWireMessage = Record<string, unknown>;
67
+ /**
68
+ * 收窄一个 tool_call id 到 Qoder 接受的形状(\`^[a-zA-Z0-9_-]{1,64}$\`,
69
+ * 逐字 pi-provider transform.ts 的注释)。线上重写,映射不回内部——
70
+ * DSH 自己的 id 本来就在这个形状里,这只是对上游边界的防守。
71
+ */
72
+ export declare function qoderWireToolCallId(id: string): string;
73
+ /** 线上请求需要的、而对话本身不携带的旋钮。 */
74
+ export interface QoderWireRequestOptions {
75
+ /** 所选模型是否声明图片输入(\`is_vl\`)。 */
76
+ supportsImages?: boolean;
77
+ /** 调用方没给输出上限时用的上限;通常取目录行的 \`defaultMaxTokens\`。 */
78
+ defaultMaxTokens?: number;
79
+ /** 该模型是否推理模型(不是推理模型就一个思考字段都不发)。 */
80
+ reasoning?: boolean;
81
+ /** 该模型声明的真实档位梯子;空 = 没有 effort 字段(toggle 形态或不推理)。 */
82
+ reasoningEfforts?: readonly string[];
83
+ /** 思考能否关闭(目录 \`thinking_config.disabled\`)。 */
84
+ thinkingDisableable?: boolean;
85
+ }
86
+ /** 出厂默认:不认图、不发思考字段、没有梯子。 */
87
+ export declare const DEFAULT_QODER_WIRE_REQUEST_OPTIONS: QoderWireRequestOptions;
88
+ /**
89
+ * 一行目录的线上旋钮(字段逐列复制只住在这一处;adapter 只加它自己的覆盖)。
90
+ */
91
+ export declare function qoderRequestOptionsFor(entry: QoderModelEntry | undefined, overrides?: {
92
+ defaultMaxTokens?: number;
93
+ }): QoderWireRequestOptions;
94
+ /**
95
+ * 发到线上的思考控制(\`parameters\` 里的两个字段),或两个都不发。
96
+ *
97
+ * 逐字 pi-provider \`stream.ts\` 的语义(来源 \`@qoder-ai/qodercli\`):
98
+ *
99
+ * - 模型不是推理模型 → 一个字段都不发;
100
+ * - 推理模型但调用方没选档位 → \`enable_thinking: false\`(不默认让它想——
101
+ * 想了更慢更费额度,用户没要求);
102
+ * - 调用方选了档位:收敛到模型真实梯子(types.ts 的 convergeQoderEffort);
103
+ * 梯子是 effort 形态才发 \`reasoning_effort\`,toggle 形态只发开关;
104
+ * 收敛到 \`none\`(或模型说思考可关而调用方要关)→ \`enable_thinking: false\`。
105
+ */
106
+ export declare function resolveQoderThinking(requested: unknown, request: QoderWireRequestOptions): {
107
+ enable_thinking: boolean;
108
+ reasoning_effort?: string;
109
+ } | undefined;
110
+ /**
111
+ * 会话与请求的稳定 id(缓存亲和)。
112
+ *
113
+ * 逐字 pi-provider \`stream.ts\` 的设计:\`session_id\` 的稳定段来自账号+模型的哈希,
114
+ * 会话段来自宿主给的 sessionId(没有才现造一个);\`request_set_id\` 是会话内容的
115
+ * 稳定哈希(重试同一份请求时不变,服务端去重靠它配合 \`request_id\` 的分工:
116
+ * \`request_id\` 每次请求唯一,重复报 code 103)。
117
+ */
118
+ export declare function qoderSessionId(uid: string, modelKey: string, sessionId: string | undefined): string;
119
+ /** 会话内容的稳定哈希(\`request_set_id\` / \`chat_record_id\`)。 */
120
+ export declare function qoderChatRecordId(modelKey: string, messages: readonly QoderWireMessage[], tools: unknown, maxTokens: number): string;
121
+ /** 装配一份 RemoteChatAsk 的调用方材料。 */
122
+ export interface QoderChatRequestContext {
123
+ /** 上行模型 key(\`X-Model-Key\` / \`model_config.key\`)。 */
124
+ modelKey: string;
125
+ /** 目录行(\`model_config\` 的其余字段从这里还原成上游形状)。 */
126
+ entry: QoderModelEntry | undefined;
127
+ /** 当前生效的 COSY 版本(\`business.version\` 填它,与官方 CLI 一致)。 */
128
+ cosyVersion: string;
129
+ /** DSH 会话 id(缓存亲和的稳定段之外的那一段)。 */
130
+ sessionId?: string;
131
+ }
132
+ /**
133
+ * 拼一份 RemoteChatAsk 请求体(**未编码**的 JSON 对象)。
134
+ *
135
+ * 字段集与取值逐字按 frozen fixture 的 \`body_raw\`(docs/qoder-line-plan.md §3.5);
136
+ * 顶层 \`system\` 与领头的 \`role:system\` 消息**都填**(官方 CLI 两处都填——
137
+ * fixture 锁定;pi-provider 实测顶层字段被服务端忽略,填两份是无害的冗余)。
138
+ *
139
+ * 返回对象之后,调用方(adapter)**必须**用 \`JSON.stringify\` 串行化一次、
140
+ * 拿那一份字节去编码+签名+发送:签名覆盖的是字节,任何中间的一道
141
+ * parse/re-marshal 都会让签名的字节与发送的字节漂移。
142
+ */
143
+ export declare function buildQoderChatRequest(options: GenerateOptions, images: ResolvedRequestImages, request: QoderWireRequestOptions, context: QoderChatRequestContext): Record<string, unknown>;
144
+ /** 一帧 usage 读下来的归一化结果。 */
145
+ export interface QoderParsedUsage {
146
+ /** 端点的聚合 prompt 计数(缓存拆分前)。 */
147
+ promptTokens: number;
148
+ /** 未命中输入,与两个缓存桶互斥。 */
149
+ inputTokens: number;
150
+ outputTokens: number;
151
+ cacheReadTokens: number;
152
+ cacheWriteTokens: number;
153
+ reasoningTokens: number;
154
+ }
155
+ /** 一帧 usage 的缓存读入量,按 {@link QODER_CACHE_READ_FIELD_ORDER} 逐条走。 */
156
+ export declare function readQoderCacheReadTokens(usage: Record<string, unknown>): number;
157
+ /**
158
+ * 归一化一帧 \`usage\` 到 DSH 的互斥计数。
159
+ *
160
+ * Qoder 遵循 OpenAI 语义:\`prompt_tokens\` **含** \`cached_tokens\`,所以缓存两桶
161
+ * 从聚合里减回去(pi-provider \`stream.ts\` 的同一减法)。\`cacheable_tokens\` 是容量
162
+ * 指标而不是写入计数(首轮写入时它也是 0),**不**映射到 cacheWrite。
163
+ *
164
+ * \`usage.credits\` / \`billable\`(Qoder 的计费字段)不进 DSH 的 TokenUsage——
165
+ * 它没有对应的格子,而订阅单价未公开,折成金额是编造(计划 §5)。
166
+ */
167
+ export declare function parseQoderUsage(raw: unknown): QoderParsedUsage | undefined;
168
+ interface PendingToolCall {
169
+ blockIndex: number;
170
+ id: string;
171
+ name: string;
172
+ arguments: string;
173
+ started: boolean;
174
+ }
175
+ /** 一条 Qoder 流式响应的累计状态。 */
176
+ export interface QoderStreamState {
177
+ blocks: OutboundContentBlock[];
178
+ current: OpenTextBlock | null;
179
+ /** wire tool index -> 累计中的调用。 */
180
+ toolCalls: Map<number, PendingToolCall>;
181
+ hasContent: boolean;
182
+ hasToolCall: boolean;
183
+ finishReason: string | null;
184
+ /** 终止依据到了(\`[DONE]\` 任一形态或 \`event:finish\`)。 */
185
+ done: boolean;
186
+ /** 终止 chunks 已经发出过。 */
187
+ finished: boolean;
188
+ inputTokens: number;
189
+ outputTokens: number;
190
+ cacheReadTokens: number;
191
+ cacheWriteTokens: number;
192
+ reasoningTokens: number;
193
+ sawUsage: boolean;
194
+ }
195
+ interface OpenTextBlock {
196
+ index: number;
197
+ type: 'text' | 'reasoning';
198
+ text: string;
199
+ }
200
+ export declare function createQoderStreamState(): QoderStreamState;
201
+ /**
202
+ * 喂一行 SSE。
203
+ *
204
+ * 信封形状(qodercli2api §7,pi-provider \`stream.ts\` 同一读法):
205
+ *
206
+ * - \`data:{"headers":{…},"body":"<inner JSON string>","statusCodeValue":200}\`:
207
+ * \`body\` 再解析一次得到 OpenAI \`chat.completion.chunk\`;
208
+ * - \`statusCodeValue != 200\` 是**错误信封**,转成 LlmError 抛出;
209
+ * - \`[DONE]\` 两种形态都结束:裸 \`data:[DONE]\` 与信封 \`body:"[DONE]"\`;
210
+ * - \`event:finish\` 是权威终止事件(它自己的 data 里是耗时统计,不是内容);
211
+ * - 心跳与其它 \`event:\` 帧不产生任何 chunk(调用方在字节层喂看门狗)。
212
+ */
213
+ export declare function processQoderStreamLine(line: string, state: QoderStreamState): StreamChunk[];
214
+ /** 一次应答的 DSH 记账(缓存字段为 0 时省略)。 */
215
+ export declare function qoderStreamTokenUsage(state: QoderStreamState): TokenUsage;
216
+ /**
217
+ * 冲刷所有打开的块,然后发 usage 与终止 finish。
218
+ *
219
+ * 顺序是硬要求:DSH 在 \`finish\` 上结算一次调用,之后到达的 usage 永远入不了账。
220
+ */
221
+ export declare function closeQoderStream(state: QoderStreamState): StreamChunk[];
222
+ /**
223
+ * 没有终止事件的流是被截断的回复,不是一份完整答案。
224
+ */
225
+ export declare function assertQoderStreamComplete(state: QoderStreamState): void;
226
+ /**
227
+ * 把一次失败(非 2xx 响应,或信封里的错误状态)分类成 DSH failure。
228
+ *
229
+ * 错误码是 harness 的共享词汇而不是本线路自己的:号池认得 \`AUTH\`
230
+ * (「这个账号必须重新登录」),而重试集合不会捡起一次套餐拒绝。
231
+ * Qoder 的两个业务码有自己的归处:\`103\`(重复 request_id)不是可安全
232
+ * 自动重试的状态(qodercli2api §8 原话);\`105\`(Login expired)归为 AUTH。
233
+ */
234
+ export declare function classifyQoderFailure(status: number, detail: string): LlmError;
235
+ //# sourceMappingURL=mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapper.d.ts","sourceRoot":"","sources":["../../../../src/host/qoder/mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Qoder(订阅)线路的模型目录:目录解析、能力装配、随包兜底表。
3
+ *
4
+ * 存在的原因是本线路的一条硬规则(与兄弟线路同源):**能力不得从模型名推断**。
5
+ * 能力字段一律来自 `GET {base}/algo/api/v2/model/list?Encode=1` 的端点字段
6
+ * (`is_vl` / `is_reasoning` / `thinking_config` / `context_config`),拿不到的走区域兜底。
7
+ *
8
+ * ## 随包兜底表
9
+ *
10
+ * 与 step-code 不同,本线路**有**一张随包表:Qoder 的目录请求需要 COSY 签名,
11
+ * 没登录就拿不到目录,而官方 CLI 要求先登录才可用——随包表让「还没登录」的
12
+ * 选择器不为空。表的内容照录 pi-provider-qoder `src/catalog.ts` 的
13
+ * `staticModels` / `staticCnModels`(逐条在注释里给出处);它是**能力兜底**,
14
+ * 不是权威列表——登录后的运行时目录随时顶替它(client.ts 的缓存与快照)。
15
+ *
16
+ * 真正保证「取不到目录时选择器不为空」的另一半是 {@link undeclaredQoderModelEntry}:
17
+ * 上游给什么 id 就以未声明行进入目录,不猜窗口、不认图、不发档位。
18
+ */
19
+ import type { QoderModelOption, QoderReasoningEffort, QoderRegion } from '../../shared/qoder-contracts.ts';
20
+ /**
21
+ * 一行模型能力。与 `QoderModelOption` 同义,去掉的只是「用户设置」那一半
22
+ * (见 {@link catalogModelOptions})。多了两个只在 host 侧有意义的字段:
23
+ * `upstreamKey`(线上请求用的上游 `key`,与公开 id 是两回事)与
24
+ * `source`(`X-Model-Source` 头与 `model_config.source` 的取值)。
25
+ */
26
+ export interface QoderModelEntry {
27
+ /** 公开 id:上游 `display_name` 去空白。 */
28
+ id: string;
29
+ /** 显示名(上游 `display_name` 原样)。 */
30
+ name: string;
31
+ /** 上行请求用的上游 key(`X-Model-Key` 与 `model_config.key`)。 */
32
+ upstreamKey: string;
33
+ /** 模型来源(`X-Model-Source`,通常 `system` 或 `custom`;pi-provider stream.ts)。 */
34
+ source: string;
35
+ /** 一句话能力描述:思考形态 / 可选档位 / 输入模态 / 上下文窗口。由 {@link describeQoderModel} 生成。 */
36
+ hint: string;
37
+ /** 服务该模型的区域。 */
38
+ regions: readonly QoderRegion[];
39
+ /** 该模型是否推理模型(目录 `is_reasoning` / `thinking_config` 存在);`model_config` 照它填。 */
40
+ reasoning: boolean;
41
+ /** 该模型**真实**接受的思考档位梯子;空数组 = 不发任何思考控制字段。 */
42
+ reasoningEfforts: readonly QoderReasoningEffort[];
43
+ /** 思考能否关闭(目录 `thinking_config.disabled` 存在与否)。 */
44
+ thinkingDisableable: boolean;
45
+ /** 是否接受 image 输入(目录 `is_vl`)。 */
46
+ supportsImages: boolean;
47
+ /** 上下文窗口:`context_config` 里最大的 `token_count`,没有时走区域兜底。 */
48
+ defaultContextWindow: number;
49
+ /** 调用方不指定输出上限时的输出上限(目录不报,取文档化上限 131072)。 */
50
+ defaultMaxTokens: number;
51
+ /** 上游描述;没有逐字来源时为 null(不编文案)。 */
52
+ description: string | null;
53
+ }
54
+ /** 一行能力去掉 `hint` 的样子:hint 由 {@link withHint} 从其余字段生成,不手写。 */
55
+ export type QoderModelFacts = Omit<QoderModelEntry, 'hint'>;
56
+ /**
57
+ * 上游 `/algo/api/v2/model/list` 的一个条目(**任何字段都可能缺失**)。
58
+ *
59
+ * 逐字 pi-provider `catalog.ts` 的 `QoderModelEntry`。`max_input_tokens` 是陈旧的
60
+ * 180K 地板(pi-provider 注释:even for models that accept 1M-token prompts),
61
+ * 所以上下文窗口只信 `context_config` 里最大的 `token_count`。
62
+ */
63
+ export interface QoderUpstreamModelRecord {
64
+ key?: unknown;
65
+ enable?: unknown;
66
+ display_name?: unknown;
67
+ max_input_tokens?: unknown;
68
+ context_config?: unknown;
69
+ is_vl?: unknown;
70
+ is_reasoning?: unknown;
71
+ thinking_config?: unknown;
72
+ source?: unknown;
73
+ description?: unknown;
74
+ }
75
+ /**
76
+ * 上游 `display_name` → 公开模型 id:去掉全部空白字符
77
+ * (逐字 pi-provider `toQoderModelId`:`replace(/\s+/g, "")`)。
78
+ */
79
+ export declare function toQoderModelId(displayName: string | undefined): string;
80
+ /** 上游 `context_config` → 上下文窗口:所有选项里最大的 `token_count`(pi-provider `contextWindowFromCatalog`)。 */
81
+ export declare function contextWindowFromRecord(record: QoderUpstreamModelRecord, region: QoderRegion): number;
82
+ /**
83
+ * 上游 `thinking_config` → 本线路的档位梯子。
84
+ *
85
+ * 逐字 pi-provider `buildThinkingLevelMap` 的两种形态:
86
+ *
87
+ * - **effort 形态**:`thinking_config.enabled.efforts` 的键集就是这个模型完整的档位集合
88
+ * (`none`/关闭值不在这个集合里,它在 `thinking_config.disabled` 里);
89
+ * - **toggle 形态**:`enabled` 存在但没有 `efforts`——只有开/关,没有档位,
90
+ * 梯子为空而 `thinkingDisableable` 照 `disabled` 是否存在决定。
91
+ *
92
+ * 认不出的档位名被丢弃而不是猜:上游加了一个我们没命名过的档位时,
93
+ * 猜一个近似的会让用户选到模型可能拒绝的值。
94
+ */
95
+ export declare function readQoderThinkingConfig(record: QoderUpstreamModelRecord): {
96
+ efforts: readonly QoderReasoningEffort[];
97
+ thinkingDisableable: boolean;
98
+ isReasoning: boolean;
99
+ };
100
+ /**
101
+ * 上游一个条目 → 一行能力。
102
+ *
103
+ * 每一个字段都按「端点说了就用,没说就走兜底」的顺序取值(计划 §4):
104
+ *
105
+ * - 公开 id:`display_name` 去空白;上行 key:`key`。两者缺一不可——
106
+ * 只有 id 没有 key 的条目发不出请求(`X-Model-Key` 是它的取值),跳过而不是猜。
107
+ * - `enable === false` 的条目跳过:它明说自己不可用。
108
+ * - 上下文窗口:`context_config` 最大 `token_count` → 区域兜底。
109
+ * - 思考:`thinking_config` / `is_reasoning`;输入模态:`is_vl`。
110
+ * - 输出上限:目录不报,取 {@link QODER_MAX_OUTPUT_TOKENS}。
111
+ */
112
+ export declare function entryFromQoderRecord(record: QoderUpstreamModelRecord, region: QoderRegion): QoderModelEntry | null;
113
+ /**
114
+ * 解析上游 `GET {base}/algo/api/v2/model/list?Encode=1` 的应答。
115
+ *
116
+ * 应答形状 `{ chat: [...] }`(逐字 pi-provider `updateQoderModelsCache` 的读取)。
117
+ * 无 key / 无 display_name / 显式 `enable:false` 的条目被丢弃。
118
+ *
119
+ * @returns 能力行,顺序与上游一致;解析不出任何东西时是空数组。
120
+ */
121
+ export declare function parseQoderModelListing(payload: unknown, region: QoderRegion): QoderModelEntry[];
122
+ /** 随包发布的全球区目录(能力兜底,登录后被运行时目录顶替)。 */
123
+ export declare const QODER_MODEL_CATALOG_GLOBAL: readonly QoderModelEntry[];
124
+ /** 随包发布的中国站目录。 */
125
+ export declare const QODER_MODEL_CATALOG_CN: readonly QoderModelEntry[];
126
+ /** 一个区域的随包兜底表。 */
127
+ export declare function bundledQoderCatalog(region: QoderRegion): readonly QoderModelEntry[];
128
+ /** 首次安装默认勾选的模型:空数组 = 不过滤(与 step-code 同一语义,见 token-store 的注释)。 */
129
+ export declare const DEFAULT_VISIBLE_MODEL_IDS: readonly string[];
130
+ /**
131
+ * 未知模型的**未声明**行。
132
+ *
133
+ * 什么都不主张:`reasoningEfforts: []`(不发思考字段)、`supportsImages: false`
134
+ * (不认图比乱认图安全)、窗口取区域兜底。未声明行**默认可见**:协议面是固定的
135
+ * COSY 对话面,调它是合法的,未知的只是能力(与 step-code 同一决定,与 glm 相反)。
136
+ */
137
+ export declare function undeclaredQoderModelEntry(id: string, options?: {
138
+ region?: QoderRegion;
139
+ upstreamKey?: string;
140
+ }): QoderModelEntry;
141
+ /**
142
+ * 查一行模型能力;给定目录里没有时返回**未声明行**而不是 undefined。
143
+ * 先按公开 id 精确匹配,再按上游 key 匹配(请求面拿的是 key),都不中才未声明。
144
+ */
145
+ export declare function resolveQoderModel(id: string, catalog: readonly QoderModelEntry[], region?: QoderRegion): QoderModelEntry;
146
+ /** 该模型真实接受的思考档位;不在目录里时是空数组(即「不发任何思考控制字段」)。 */
147
+ export declare function qoderReasoningEfforts(id: string, catalog: readonly QoderModelEntry[]): readonly QoderReasoningEffort[];
148
+ /** 该模型的输出上限;不在目录里时取 {@link QODER_MAX_OUTPUT_TOKENS}。 */
149
+ export declare function maxOutputTokensFor(id: string, catalog: readonly QoderModelEntry[]): number;
150
+ /** 该模型的上下文窗口;不在目录里时取区域兜底。 */
151
+ export declare function defaultContextWindowFor(id: string, catalog: readonly QoderModelEntry[], region?: QoderRegion): number;
152
+ /** 目录里的全部公开 id。 */
153
+ export declare function listModelIds(catalog: readonly QoderModelEntry[]): string[];
154
+ /** 目录条目数,供 hub 概览显示「M 个模型可用」。 */
155
+ export declare function catalogSize(catalog: readonly QoderModelEntry[]): number;
156
+ /** {@link catalogSize} 的别名(与兄弟线路的调用形状一致)。 */
157
+ export declare const catalogTotal: typeof catalogSize;
158
+ /**
159
+ * 从一行能力生成设置卡上的一句话描述(四个要素:思考形态 / 档位 / 输入模态 / 上下文窗口)。
160
+ *
161
+ * 由数据生成而不是逐行手写,是为了让文案**不可能与能力表漂移**(同 step-code 的
162
+ * describeStepCodeModel,连理由都同源)。
163
+ */
164
+ export declare function describeQoderModel(entry: QoderModelEntry, prefix?: string): string;
165
+ /** 用户设置对目录装配的两处覆盖。 */
166
+ export interface QoderCatalogOptionOverrides {
167
+ /**
168
+ * 用户勾选的模型 id。
169
+ *
170
+ * **缺省或空数组 = 不过滤(全选)**;非空 = 显式选择(含只有一项)。
171
+ * 语义逐字照抄 step-code:schemastery 会把缺省的 `z.array(z.string())` 归一化成 `[]`,
172
+ * 所以没有「null = 不过滤」的第三态。真要把模型全关,关线路总开关是明确且语义正确的动作。
173
+ */
174
+ enabledModelIds?: readonly string[];
175
+ /** 逐模型上下文窗口覆盖;null / 非正数表示用目录默认值。 */
176
+ contextWindowOverrides?: Readonly<Record<string, number | null | undefined>>;
177
+ }
178
+ /**
179
+ * 把目录装配成设置面的模型行。
180
+ *
181
+ * `hint` 在这里生成(而不是写死在表里),所以「文案与能力表一致」是结构保证的。
182
+ * 未声明行**默认可见**(见 {@link undeclaredQoderModelEntry})。
183
+ */
184
+ export declare function catalogModelOptions(catalog: readonly QoderModelEntry[], overrides?: QoderCatalogOptionOverrides): QoderModelOption[];
185
+ //# sourceMappingURL=model-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../../../../src/host/qoder/model-catalog.ts"],"names":[],"mappings":"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"}