mslxdff 0.1.203 → 0.1.207

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 (330) hide show
  1. package/README.md +3 -3
  2. package/bin/mslxdff.js +1 -1
  3. package/docs/cli_help_mini.md +146 -0
  4. package/package.json +38 -38
  5. package/plugins/README.md +14 -14
  6. package/plugins/prefer-model.mjs.example +14 -14
  7. package/src/anthropic/canonical-json.js +55 -0
  8. package/src/anthropic/count-tokens.js +61 -61
  9. package/src/anthropic/request-body.js +183 -126
  10. package/src/anthropic/request-shape.js +60 -0
  11. package/src/anthropic/session-id.js +58 -0
  12. package/src/anthropic/shared.js +18 -18
  13. package/src/anthropic/sse-aggregate.js +56 -56
  14. package/src/anthropic/tool-media.js +75 -0
  15. package/src/anthropic/translate.js +296 -287
  16. package/src/anthropic/web-search.js +361 -361
  17. package/src/api-kind.js +31 -31
  18. package/src/autostart.js +288 -288
  19. package/src/bench/probe.js +1 -1
  20. package/src/bench/runner.js +1 -1
  21. package/src/bench/via-probe.js +1 -1
  22. package/src/bench/via.js +144 -144
  23. package/src/bench/workbuddy-bench.js +1 -1
  24. package/src/chat/config.js +11 -11
  25. package/src/chat/cooling.js +81 -81
  26. package/src/chat/direct.js +58 -58
  27. package/src/chat/index.js +6 -6
  28. package/src/chat/spinner.js +38 -38
  29. package/src/chat/sse.js +69 -69
  30. package/src/chat/stats.js +247 -247
  31. package/src/chat/store.js +48 -48
  32. package/src/chat/tool-handlers.js +67 -67
  33. package/src/chat/tools.js +1 -1
  34. package/src/chat/upstream.js +59 -59
  35. package/src/chat-pipeline/auto-race.js +158 -158
  36. package/src/chat-pipeline/empty-turn.js +147 -147
  37. package/src/chat-pipeline/engine.js +17 -17
  38. package/src/chat-pipeline/index.js +8 -40
  39. package/src/chat-pipeline/serial-trial.js +279 -283
  40. package/src/chat-pipeline/terminal-verdict.js +132 -147
  41. package/src/chooser.js +54 -54
  42. package/src/claude-desktop/apply.js +101 -101
  43. package/src/claude-desktop/context1m.js +38 -38
  44. package/src/claude-desktop/doctor.js +146 -146
  45. package/src/claude-desktop/slots.js +152 -152
  46. package/src/cli/bootstrap.js +1 -1
  47. package/src/cli/commands/claude-desktop.js +284 -284
  48. package/src/cli/commands/daemon.js +231 -231
  49. package/src/cli/commands/model/list-live.js +22 -22
  50. package/src/cli/commands/model/list-providers.js +76 -76
  51. package/src/cli/commands/model/list-render.js +77 -77
  52. package/src/cli/commands/model/list-sort.js +52 -52
  53. package/src/cli/commands/model/list-tty.js +76 -76
  54. package/src/cli/commands/model/list.js +166 -166
  55. package/src/cli/commands/model/live-models.js +24 -24
  56. package/src/cli/commands/model/stats.js +43 -43
  57. package/src/cli/commands/model/status.js +47 -47
  58. package/src/cli/commands/model.js +51 -51
  59. package/src/cli/commands/provider/add.js +108 -108
  60. package/src/cli/commands/provider/allowlist.js +99 -99
  61. package/src/cli/commands/provider/cline-free.js +99 -99
  62. package/src/cli/commands/provider/cline-login.js +218 -218
  63. package/src/cli/commands/provider/cline-quota.js +65 -65
  64. package/src/cli/commands/provider/codearts-login.js +45 -45
  65. package/src/cli/commands/provider/config.js +119 -119
  66. package/src/cli/commands/provider/globalqwenwork-login.js +126 -126
  67. package/src/cli/commands/provider/keys-guard.js +50 -50
  68. package/src/cli/commands/provider/keys.js +161 -161
  69. package/src/cli/commands/provider/qoder-checkin.js +81 -81
  70. package/src/cli/commands/provider/qoder-login.js +102 -102
  71. package/src/cli/commands/provider/raccoon-login.js +180 -180
  72. package/src/cli/commands/provider/raccoon-password.js +162 -162
  73. package/src/cli/commands/provider/raccoon-quota.js +109 -109
  74. package/src/cli/commands/provider/traework-checkin.js +144 -144
  75. package/src/cli/commands/provider/traework-login.js +164 -164
  76. package/src/cli/commands/provider/workbuddy-login.js +153 -153
  77. package/src/cli/commands/provider/zcode-login.js +77 -77
  78. package/src/cli/commands/provider/zcode-quota.js +55 -55
  79. package/src/cli/commands/provider.js +1 -1
  80. package/src/cli/commands/stats.js +158 -149
  81. package/src/cli/commands/system.js +261 -261
  82. package/src/cli/commands/timezone.js +47 -47
  83. package/src/cli/commands/use-group.js +60 -60
  84. package/src/cli/commands/workbuddy.js +189 -189
  85. package/src/cli/format.js +141 -141
  86. package/src/cli/group-helpers.js +70 -70
  87. package/src/cli/help.js +80 -80
  88. package/src/cli/index.js +70 -70
  89. package/src/cli/interactive.js +121 -121
  90. package/src/cli/join-core.js +99 -99
  91. package/src/cli/join-wizard.js +175 -175
  92. package/src/cli/policy.js +136 -136
  93. package/src/cli/provider-row.js +126 -126
  94. package/src/cli/util.js +24 -24
  95. package/src/compat.js +53 -53
  96. package/src/events.js +38 -38
  97. package/src/free-lane.js +131 -131
  98. package/src/free-watcher.js +63 -63
  99. package/src/groups.js +262 -262
  100. package/src/logs.js +195 -195
  101. package/src/metrics.js +71 -62
  102. package/src/model-capabilities/context-lookup.js +65 -65
  103. package/src/model-capabilities/merge.js +142 -142
  104. package/src/model-trace.js +187 -187
  105. package/src/models.js +1 -1
  106. package/src/opencode-identity.js +72 -72
  107. package/src/peers.js +254 -254
  108. package/src/plugins.js +107 -107
  109. package/src/providers/AGENTS.md +49 -0
  110. package/src/providers/base.js +164 -164
  111. package/src/providers/classify.js +22 -22
  112. package/src/providers/cline/allowlist-sync.js +93 -93
  113. package/src/providers/cline/free-catalog.js +71 -71
  114. package/src/providers/cline/headers.js +37 -37
  115. package/src/providers/cline/index.js +130 -130
  116. package/src/providers/cline/models.js +150 -150
  117. package/src/providers/cline/usage.js +274 -274
  118. package/src/providers/codearts/auth-pool.js +148 -148
  119. package/src/providers/codearts/concurrency.js +34 -34
  120. package/src/providers/codearts/const.js +65 -65
  121. package/src/providers/codearts/dpop.js +95 -95
  122. package/src/providers/codearts/index.js +175 -175
  123. package/src/providers/codearts/login.js +214 -214
  124. package/src/providers/codearts/models.js +182 -182
  125. package/src/providers/codearts/sign.js +96 -96
  126. package/src/providers/codearts/sse.js +204 -204
  127. package/src/providers/codearts/stream.js +209 -209
  128. package/src/providers/codearts/sts.js +124 -124
  129. package/src/providers/codearts.js +1 -1
  130. package/src/providers/dispatcher.js +146 -146
  131. package/src/providers/generic.js +156 -156
  132. package/src/providers/globalqwenwork/account-store.js +141 -141
  133. package/src/providers/globalqwenwork/constants.js +73 -73
  134. package/src/providers/globalqwenwork/crypto.js +2 -2
  135. package/src/providers/globalqwenwork/http.js +2 -2
  136. package/src/providers/globalqwenwork/index.js +331 -331
  137. package/src/providers/globalqwenwork/payload.js +147 -147
  138. package/src/providers/globalqwenwork/rsa.js +2 -2
  139. package/src/providers/globalqwenwork/sse.js +2 -2
  140. package/src/providers/globalqwenwork/stream.js +2 -2
  141. package/src/providers/globalqwenwork.js +1 -1
  142. package/src/providers/globalworkbuddy/account-store.js +198 -198
  143. package/src/providers/globalworkbuddy/auth.js +97 -97
  144. package/src/providers/globalworkbuddy/catalog.js +138 -138
  145. package/src/providers/globalworkbuddy/constants.js +73 -73
  146. package/src/providers/globalworkbuddy/device-auth.js +120 -120
  147. package/src/providers/globalworkbuddy/errors.js +98 -98
  148. package/src/providers/globalworkbuddy/headers.js +89 -89
  149. package/src/providers/globalworkbuddy/models.js +96 -96
  150. package/src/providers/globalworkbuddy/payload.js +80 -80
  151. package/src/providers/index.js +5 -5
  152. package/src/providers/model-id.js +172 -172
  153. package/src/providers/opencode.js +19 -19
  154. package/src/providers/openrouter.js +31 -31
  155. package/src/providers/qoder/account-store.js +106 -106
  156. package/src/providers/qoder/aggregate.js +60 -60
  157. package/src/providers/qoder/baseprompt.json +591 -591
  158. package/src/providers/qoder/checkin.js +160 -160
  159. package/src/providers/qoder/constants.js +44 -44
  160. package/src/providers/qoder/encode.js +41 -41
  161. package/src/providers/qoder/fingerprint.js +38 -38
  162. package/src/providers/qoder/models.js +77 -77
  163. package/src/providers/qoder/oauth.js +84 -84
  164. package/src/providers/qoder/payload.js +162 -162
  165. package/src/providers/qoder/request.js +27 -27
  166. package/src/providers/qoder/session.js +134 -134
  167. package/src/providers/qoder/sticky.js +38 -38
  168. package/src/providers/qoder/stream.js +116 -116
  169. package/src/providers/qoder.js +1 -1
  170. package/src/providers/raccoon/account-store.js +216 -216
  171. package/src/providers/raccoon/auth.js +67 -67
  172. package/src/providers/raccoon/chat.js +228 -228
  173. package/src/providers/raccoon/const.js +180 -180
  174. package/src/providers/raccoon/credits.js +185 -185
  175. package/src/providers/raccoon/crypto.js +43 -43
  176. package/src/providers/raccoon/envelope.js +31 -31
  177. package/src/providers/raccoon/headers.js +45 -45
  178. package/src/providers/raccoon/index.js +216 -216
  179. package/src/providers/raccoon/login.js +279 -279
  180. package/src/providers/raccoon/models.js +160 -160
  181. package/src/providers/raccoon/password-login.js +102 -102
  182. package/src/providers/raccoon/qr-codewords.js +115 -115
  183. package/src/providers/raccoon/qr.js +236 -236
  184. package/src/providers/raccoon/sse.js +135 -135
  185. package/src/providers/responses-channel.js +120 -120
  186. package/src/providers/share-keys.js +53 -53
  187. package/src/providers/traework/account-store.js +234 -234
  188. package/src/providers/traework/chat.js +217 -217
  189. package/src/providers/traework/checkin.js +77 -77
  190. package/src/providers/traework/constants.js +41 -41
  191. package/src/providers/traework/errors.js +52 -52
  192. package/src/providers/traework/headers.js +79 -79
  193. package/src/providers/traework/index.js +132 -132
  194. package/src/providers/traework/market-identity.js +116 -116
  195. package/src/providers/traework/models.js +98 -98
  196. package/src/providers/traework/payload.js +86 -86
  197. package/src/providers/traework/sse.js +184 -184
  198. package/src/providers/traework/token.js +52 -52
  199. package/src/providers/traework.js +2 -2
  200. package/src/providers/workbuddy/account-store.js +186 -186
  201. package/src/providers/workbuddy/auth.js +158 -158
  202. package/src/providers/workbuddy/balance.js +86 -86
  203. package/src/providers/workbuddy/cat-travel.js +125 -125
  204. package/src/providers/workbuddy/checkin.js +75 -75
  205. package/src/providers/workbuddy/desktop-info.js +78 -78
  206. package/src/providers/workbuddy/growth-api.js +67 -67
  207. package/src/providers/workbuddy/growth-plans.js +63 -63
  208. package/src/providers/workbuddy/growth.js +181 -181
  209. package/src/providers/workbuddy/index.js +217 -217
  210. package/src/providers/workbuddy/models.js +126 -126
  211. package/src/providers/workbuddy/payload.js +141 -141
  212. package/src/providers/workbuddy/rotation-log.js +83 -83
  213. package/src/providers/workbuddy/sanitize-tools.js +61 -61
  214. package/src/providers/workbuddy/sdk-chat.js +26 -26
  215. package/src/providers/workbuddy-balance.js +78 -78
  216. package/src/providers/zcode/account-store.js +129 -129
  217. package/src/providers/zcode/auth.js +28 -28
  218. package/src/providers/zcode/chat.js +179 -179
  219. package/src/providers/zcode/const.js +54 -54
  220. package/src/providers/zcode/context-shape.js +162 -162
  221. package/src/providers/zcode/headers.js +93 -93
  222. package/src/providers/zcode/index.js +124 -124
  223. package/src/providers/zcode/models.js +65 -65
  224. package/src/providers/zcode/oauth.js +120 -120
  225. package/src/providers/zcode/quota.js +176 -176
  226. package/src/providers/zcode/sse.js +196 -196
  227. package/src/readline-compat.js +25 -25
  228. package/src/responses/translate.js +311 -311
  229. package/src/routes/AGENTS.md +37 -0
  230. package/src/routes/chat/broadband-handler.js +72 -72
  231. package/src/routes/chat/empty-turn-report.js +31 -31
  232. package/src/routes/chat/exhausted-handler.js +87 -87
  233. package/src/routes/chat/hedge-handler.js +130 -130
  234. package/src/routes/chat/index.js +1 -1
  235. package/src/routes/chat/local-handler.js +63 -63
  236. package/src/routes/chat/peer-handler.js +81 -81
  237. package/src/routes/chat.js +3 -3
  238. package/src/routes/fallback.js +88 -88
  239. package/src/routes/groups-relay.js +214 -214
  240. package/src/routes/groups.js +122 -122
  241. package/src/routes/helpers.js +93 -83
  242. package/src/routes/index.js +147 -147
  243. package/src/routes/messages-route.js +375 -365
  244. package/src/routes/peers.js +10 -2
  245. package/src/routes/relay-queue.js +181 -181
  246. package/src/routes/relay.js +60 -60
  247. package/src/routes/responses-route.js +213 -213
  248. package/src/routes/stream-body.js +84 -84
  249. package/src/routes/stream-hold.js +337 -337
  250. package/src/routes/stream-scan.js +366 -366
  251. package/src/routes.js +3 -3
  252. package/src/runtime/auto-update.js +113 -113
  253. package/src/runtime/broadband-stream.js +79 -79
  254. package/src/runtime/broadband.js +103 -103
  255. package/src/runtime/group-sync.js +81 -81
  256. package/src/runtime/keep-warm.js +120 -120
  257. package/src/runtime/lifecycle-forensics.js +116 -116
  258. package/src/runtime/lifecycle-log.js +87 -87
  259. package/src/runtime/log-stamp.js +73 -73
  260. package/src/runtime/proc-info.js +137 -137
  261. package/src/runtime/qoder-checkin.js +93 -93
  262. package/src/runtime/raccoon-checkin.js +126 -126
  263. package/src/runtime/server-lifecycle.js +189 -189
  264. package/src/runtime/traework-checkin.js +118 -118
  265. package/src/runtime/workbuddy-checkin.js +120 -120
  266. package/src/runtime/workbuddy-growth.js +133 -133
  267. package/src/state/facade.js +58 -58
  268. package/src/state/memory.js +162 -162
  269. package/src/state/merge.js +25 -25
  270. package/src/state/migrations/cline-unify.js +94 -94
  271. package/src/state/migrations.js +33 -33
  272. package/src/state/persist.js +42 -42
  273. package/src/state/provider-config.js +142 -142
  274. package/src/state/schemas/group.js +31 -31
  275. package/src/state/schemas/model.js +94 -94
  276. package/src/state/schemas/peer.js +31 -31
  277. package/src/state/schemas/port.js +10 -10
  278. package/src/state/schemas/provider.js +202 -202
  279. package/src/state/schemas/timezone.js +70 -70
  280. package/src/state/schemas/token.js +48 -48
  281. package/src/state/schemas/use-group.js +102 -102
  282. package/src/state/store.js +176 -176
  283. package/src/state.js +1 -1
  284. package/src/sync-claude-desktop.js +234 -234
  285. package/src/sync-claude.js +152 -152
  286. package/src/sync-codex.js +116 -116
  287. package/src/sync-workbuddy.js +158 -158
  288. package/src/talk-full-redact.js +129 -129
  289. package/src/talk-full.js +131 -131
  290. package/src/talk-log.js +257 -257
  291. package/src/text-tokens.js +52 -52
  292. package/src/thinking-effort.js +67 -67
  293. package/src/time.js +106 -106
  294. package/src/timeline.js +26 -26
  295. package/src/transport/index.js +1 -1
  296. package/src/upstream-engine/index.js +74 -74
  297. package/src/upstream-engine/mode.js +15 -15
  298. package/src/upstream-engine/sdk/attempt.js +221 -221
  299. package/src/upstream-engine/sdk/chat.js +28 -28
  300. package/src/upstream-engine/sdk/convert.js +192 -192
  301. package/src/upstream-engine/sdk/diagnose.js +62 -62
  302. package/src/upstream-engine/sdk/dispatch.js +55 -55
  303. package/src/upstream-engine/sdk/responses.js +176 -176
  304. package/src/upstream-engine/sdk/sse.js +166 -166
  305. package/src/upstream-responses.js +305 -305
  306. package/src/usage/cache-fields.js +58 -0
  307. package/src/usage/record.js +109 -106
  308. package/src/usage/report.js +209 -195
  309. package/src/v6pool/claim.js +157 -157
  310. package/src/v6pool/config.js +65 -65
  311. package/src/v6pool/detect.js +130 -130
  312. package/src/v6pool/index.js +152 -152
  313. package/src/v6pool/prefix.js +140 -140
  314. package/src/v6pool/probe.js +40 -40
  315. package/src/v6pool/report.js +104 -104
  316. package/src/v6pool/rotate.js +263 -263
  317. package/src/warp-pool/config.js +74 -74
  318. package/src/warp-pool/health.js +106 -106
  319. package/src/warp-pool/identity.js +141 -141
  320. package/src/warp-pool/index.js +358 -358
  321. package/src/warp-pool/keys.js +29 -29
  322. package/src/warp-pool/nodes.js +95 -95
  323. package/src/warp-pool/ports.js +160 -160
  324. package/src/warp-pool/register.js +113 -113
  325. package/src/warp-pool/report.js +232 -232
  326. package/src/warp-pool/revive.js +168 -168
  327. package/src/warp-pool/rotate.js +214 -214
  328. package/src/warp-pool/strategy.js +43 -43
  329. package/src/warp-pool/verdict.js +69 -69
  330. package/src/warp-pool/wireproxy.js +162 -162
@@ -1,160 +1,160 @@
1
- // raccoon 模型目录:上游 `model_catalog` 解析 + 兜底目录 + 积分倍率展示名 + allowlist 补齐。
2
- // 倍率是这家供应商的**成本唯一载体**(积分制,不是免费额度),所以它在模型名与列表里都必须出现:
3
- // 0 → 「免费」;1 → 也显示 `x1`(省略会让用户以为「没倍率=不扣分」);促销 → `x0.2→x0.1`。
4
- import {
5
- RACCOON_FALLBACK_MODELS,
6
- RACCOON_REQUEST_TIMEOUT_MS,
7
- raccoonModelCatalogUrl,
8
- } from "./const.js";
9
- import { raccoonAuthHeaders } from "./headers.js";
10
- import { parseRaccoonEnvelope } from "./envelope.js";
11
- import { loadProviderAllowedModels, saveProviderAllowedModels } from "../../state/schemas/allowlist.js";
12
- import { joinModelId } from "../model-id.js";
13
-
14
- const pickString = (src, keys) => {
15
- for (const k of keys) {
16
- const v = src?.[k];
17
- if (typeof v === "string" && v.trim().length > 0) return v.trim();
18
- }
19
- return "";
20
- };
21
-
22
- const pickNumber = (src, keys) => {
23
- for (const k of keys) {
24
- const v = src?.[k];
25
- if (typeof v === "number" && Number.isFinite(v)) return v;
26
- if (typeof v === "string" && /^-?\d+(\.\d+)?$/.test(v.trim())) return Number(v.trim());
27
- }
28
- return undefined;
29
- };
30
-
31
- const pickBool = (src, keys) => {
32
- for (const k of keys) {
33
- const v = src?.[k];
34
- if (typeof v === "boolean") return v;
35
- }
36
- return undefined;
37
- };
38
-
39
- /** 兜底目录(const.js 的静态表,字段名已归一)。 */
40
- export function raccoonFallbackModels() {
41
- return RACCOON_FALLBACK_MODELS.map((m) => ({ ...m }));
42
- }
43
-
44
- /** 单条目录条目:字段名容错(缺字段即省略,不猜、不填假值)。 */
45
- function parseRaccoonModelEntry(raw) {
46
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
47
- const id = pickString(raw, ["id", "modelId", "model_id", "modelName", "name"]);
48
- if (!id) return null;
49
- const model = { id };
50
- const name = pickString(raw, ["modelName", "name", "displayName", "description"]);
51
- if (name) model.name = name;
52
- const ctx = pickNumber(raw, ["contextWindow", "context_window", "contextLength", "context_length"]);
53
- if (ctx !== undefined && ctx > 0) model.contextWindow = ctx;
54
- const maxTokens = pickNumber(raw, ["maxTokens", "max_tokens", "maxCompletionTokens"]);
55
- if (maxTokens !== undefined && maxTokens > 0) model.maxTokens = maxTokens;
56
- const img = pickBool(raw, ["supportsImage", "supports_image", "supportsVision"]);
57
- if (img !== undefined) model.supportsImage = img;
58
- const eff = pickNumber(raw, ["effectiveMultiplier", "effective_multiplier", "multiplier", "costMultiplier"]);
59
- if (eff !== undefined && eff >= 0) model.multiplier = eff;
60
- const base = pickNumber(raw, ["baseMultiplier", "base_multiplier"]);
61
- if (base !== undefined && base > 0) model.baseMultiplier = base;
62
- return model;
63
- }
64
-
65
- /** 目录信封 → 条目数组。兼容 `data` 为数组、`data.models`、`data.list`、`data.items` 四种形状。 */
66
- export function parseRaccoonCatalog(data) {
67
- const list = Array.isArray(data)
68
- ? data
69
- : Array.isArray(data?.models)
70
- ? data.models
71
- : Array.isArray(data?.list)
72
- ? data.list
73
- : Array.isArray(data?.items)
74
- ? data.items
75
- : [];
76
- const out = [];
77
- const seen = new Set();
78
- for (const raw of list) {
79
- const m = parseRaccoonModelEntry(raw);
80
- if (!m || seen.has(m.id)) continue;
81
- seen.add(m.id);
82
- out.push(m);
83
- }
84
- return out;
85
- }
86
-
87
- /** 倍率文案:0 → 免费;促销 → `x基→x实`;其余 → `x实`(含 x1)。 */
88
- export function raccoonMultiplierLabel(model) {
89
- const eff = typeof model?.multiplier === "number" ? model.multiplier : undefined;
90
- if (eff === undefined) return "";
91
- if (eff === 0) return "免费";
92
- const fmt = (v) => String(Number(v.toFixed(4)));
93
- const base = typeof model?.baseMultiplier === "number" ? model.baseMultiplier : undefined;
94
- if (base !== undefined && base > eff) return `x${fmt(base)}→x${fmt(eff)}`;
95
- return `x${fmt(eff)}`;
96
- }
97
-
98
- /** 列表/模型卡展示名:`<名字或 id> · <倍率>`(无倍率时省略后缀)。 */
99
- export function raccoonDisplayName(model) {
100
- const base = model?.name || model?.id || "";
101
- const label = raccoonMultiplierLabel(model);
102
- return label ? `${base} · ${label}` : base;
103
- }
104
-
105
- /** 拉上游目录;失败/空目录返回 null(调用方回落兜底表)。 */
106
- export async function fetchRaccoonModelCatalog({
107
- credential,
108
- fetchImpl = fetch,
109
- env = process.env,
110
- timeoutMs = RACCOON_REQUEST_TIMEOUT_MS,
111
- } = {}) {
112
- if (!credential?.access_token) return null;
113
- try {
114
- const res = await fetchImpl(raccoonModelCatalogUrl(), {
115
- method: "GET",
116
- headers: raccoonAuthHeaders(credential, { env }),
117
- signal: AbortSignal.timeout(timeoutMs),
118
- });
119
- if (!res.ok) return null;
120
- const envelope = parseRaccoonEnvelope(await res.json(), res.status);
121
- if (envelope.code !== 0) return null;
122
- const models = parseRaccoonCatalog(envelope.data);
123
- return models.length > 0 ? models : null;
124
- } catch {
125
- return null;
126
- }
127
- }
128
-
129
- /** 上游优先、兜底次之。返回 `{ models, source }`,source ∈ upstream|fallback。 */
130
- export async function listRaccoonModels({ credential, fetchImpl, env } = {}) {
131
- const upstream = await fetchRaccoonModelCatalog({ credential, fetchImpl, env });
132
- if (upstream) return { models: upstream, source: "upstream" };
133
- return { models: raccoonFallbackModels(), source: "fallback" };
134
- }
135
-
136
- /** 登录后把兜底目录 id 只增不减并入 allowlist(避免「登录成功却处处 403」)。 */
137
- export function seedRaccoonAllowlist({ ids, file } = {}) {
138
- const opts = file ? { file } : {};
139
- const target = ids || RACCOON_FALLBACK_MODELS.map((m) => m.id);
140
- const cur = loadProviderAllowedModels("raccoon", opts);
141
- const added = target.filter((m) => !cur.includes(m));
142
- if (added.length) saveProviderAllowedModels("raccoon", [...cur, ...added], opts);
143
- return { allowed: [...cur, ...added], added };
144
- }
145
-
146
- /** 对外目录条目(`/v1/models` 与 CLI models 共用形状)。 */
147
- export function buildRaccoonModelList({ id = "raccoon", models } = {}) {
148
- const list = models || raccoonFallbackModels();
149
- return list.map((m) => ({
150
- id: joinModelId(id, m.id),
151
- object: "model",
152
- owned_by: "raccoon",
153
- name: raccoonDisplayName(m),
154
- context_length: m.contextWindow,
155
- max_tokens: m.maxTokens,
156
- supports_image: m.supportsImage,
157
- multiplier: m.multiplier,
158
- mark: raccoonMultiplierLabel(m) || undefined,
159
- }));
160
- }
1
+ // raccoon 模型目录:上游 `model_catalog` 解析 + 兜底目录 + 积分倍率展示名 + allowlist 补齐。
2
+ // 倍率是这家供应商的**成本唯一载体**(积分制,不是免费额度),所以它在模型名与列表里都必须出现:
3
+ // 0 → 「免费」;1 → 也显示 `x1`(省略会让用户以为「没倍率=不扣分」);促销 → `x0.2→x0.1`。
4
+ import {
5
+ RACCOON_FALLBACK_MODELS,
6
+ RACCOON_REQUEST_TIMEOUT_MS,
7
+ raccoonModelCatalogUrl,
8
+ } from "./const.js";
9
+ import { raccoonAuthHeaders } from "./headers.js";
10
+ import { parseRaccoonEnvelope } from "./envelope.js";
11
+ import { loadProviderAllowedModels, saveProviderAllowedModels } from "../../state/schemas/allowlist.js";
12
+ import { joinModelId } from "../model-id.js";
13
+
14
+ const pickString = (src, keys) => {
15
+ for (const k of keys) {
16
+ const v = src?.[k];
17
+ if (typeof v === "string" && v.trim().length > 0) return v.trim();
18
+ }
19
+ return "";
20
+ };
21
+
22
+ const pickNumber = (src, keys) => {
23
+ for (const k of keys) {
24
+ const v = src?.[k];
25
+ if (typeof v === "number" && Number.isFinite(v)) return v;
26
+ if (typeof v === "string" && /^-?\d+(\.\d+)?$/.test(v.trim())) return Number(v.trim());
27
+ }
28
+ return undefined;
29
+ };
30
+
31
+ const pickBool = (src, keys) => {
32
+ for (const k of keys) {
33
+ const v = src?.[k];
34
+ if (typeof v === "boolean") return v;
35
+ }
36
+ return undefined;
37
+ };
38
+
39
+ /** 兜底目录(const.js 的静态表,字段名已归一)。 */
40
+ export function raccoonFallbackModels() {
41
+ return RACCOON_FALLBACK_MODELS.map((m) => ({ ...m }));
42
+ }
43
+
44
+ /** 单条目录条目:字段名容错(缺字段即省略,不猜、不填假值)。 */
45
+ function parseRaccoonModelEntry(raw) {
46
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
47
+ const id = pickString(raw, ["id", "modelId", "model_id", "modelName", "name"]);
48
+ if (!id) return null;
49
+ const model = { id };
50
+ const name = pickString(raw, ["modelName", "name", "displayName", "description"]);
51
+ if (name) model.name = name;
52
+ const ctx = pickNumber(raw, ["contextWindow", "context_window", "contextLength", "context_length"]);
53
+ if (ctx !== undefined && ctx > 0) model.contextWindow = ctx;
54
+ const maxTokens = pickNumber(raw, ["maxTokens", "max_tokens", "maxCompletionTokens"]);
55
+ if (maxTokens !== undefined && maxTokens > 0) model.maxTokens = maxTokens;
56
+ const img = pickBool(raw, ["supportsImage", "supports_image", "supportsVision"]);
57
+ if (img !== undefined) model.supportsImage = img;
58
+ const eff = pickNumber(raw, ["effectiveMultiplier", "effective_multiplier", "multiplier", "costMultiplier"]);
59
+ if (eff !== undefined && eff >= 0) model.multiplier = eff;
60
+ const base = pickNumber(raw, ["baseMultiplier", "base_multiplier"]);
61
+ if (base !== undefined && base > 0) model.baseMultiplier = base;
62
+ return model;
63
+ }
64
+
65
+ /** 目录信封 → 条目数组。兼容 `data` 为数组、`data.models`、`data.list`、`data.items` 四种形状。 */
66
+ export function parseRaccoonCatalog(data) {
67
+ const list = Array.isArray(data)
68
+ ? data
69
+ : Array.isArray(data?.models)
70
+ ? data.models
71
+ : Array.isArray(data?.list)
72
+ ? data.list
73
+ : Array.isArray(data?.items)
74
+ ? data.items
75
+ : [];
76
+ const out = [];
77
+ const seen = new Set();
78
+ for (const raw of list) {
79
+ const m = parseRaccoonModelEntry(raw);
80
+ if (!m || seen.has(m.id)) continue;
81
+ seen.add(m.id);
82
+ out.push(m);
83
+ }
84
+ return out;
85
+ }
86
+
87
+ /** 倍率文案:0 → 免费;促销 → `x基→x实`;其余 → `x实`(含 x1)。 */
88
+ export function raccoonMultiplierLabel(model) {
89
+ const eff = typeof model?.multiplier === "number" ? model.multiplier : undefined;
90
+ if (eff === undefined) return "";
91
+ if (eff === 0) return "免费";
92
+ const fmt = (v) => String(Number(v.toFixed(4)));
93
+ const base = typeof model?.baseMultiplier === "number" ? model.baseMultiplier : undefined;
94
+ if (base !== undefined && base > eff) return `x${fmt(base)}→x${fmt(eff)}`;
95
+ return `x${fmt(eff)}`;
96
+ }
97
+
98
+ /** 列表/模型卡展示名:`<名字或 id> · <倍率>`(无倍率时省略后缀)。 */
99
+ export function raccoonDisplayName(model) {
100
+ const base = model?.name || model?.id || "";
101
+ const label = raccoonMultiplierLabel(model);
102
+ return label ? `${base} · ${label}` : base;
103
+ }
104
+
105
+ /** 拉上游目录;失败/空目录返回 null(调用方回落兜底表)。 */
106
+ export async function fetchRaccoonModelCatalog({
107
+ credential,
108
+ fetchImpl = fetch,
109
+ env = process.env,
110
+ timeoutMs = RACCOON_REQUEST_TIMEOUT_MS,
111
+ } = {}) {
112
+ if (!credential?.access_token) return null;
113
+ try {
114
+ const res = await fetchImpl(raccoonModelCatalogUrl(), {
115
+ method: "GET",
116
+ headers: raccoonAuthHeaders(credential, { env }),
117
+ signal: AbortSignal.timeout(timeoutMs),
118
+ });
119
+ if (!res.ok) return null;
120
+ const envelope = parseRaccoonEnvelope(await res.json(), res.status);
121
+ if (envelope.code !== 0) return null;
122
+ const models = parseRaccoonCatalog(envelope.data);
123
+ return models.length > 0 ? models : null;
124
+ } catch {
125
+ return null;
126
+ }
127
+ }
128
+
129
+ /** 上游优先、兜底次之。返回 `{ models, source }`,source ∈ upstream|fallback。 */
130
+ export async function listRaccoonModels({ credential, fetchImpl, env } = {}) {
131
+ const upstream = await fetchRaccoonModelCatalog({ credential, fetchImpl, env });
132
+ if (upstream) return { models: upstream, source: "upstream" };
133
+ return { models: raccoonFallbackModels(), source: "fallback" };
134
+ }
135
+
136
+ /** 登录后把兜底目录 id 只增不减并入 allowlist(避免「登录成功却处处 403」)。 */
137
+ export function seedRaccoonAllowlist({ ids, file } = {}) {
138
+ const opts = file ? { file } : {};
139
+ const target = ids || RACCOON_FALLBACK_MODELS.map((m) => m.id);
140
+ const cur = loadProviderAllowedModels("raccoon", opts);
141
+ const added = target.filter((m) => !cur.includes(m));
142
+ if (added.length) saveProviderAllowedModels("raccoon", [...cur, ...added], opts);
143
+ return { allowed: [...cur, ...added], added };
144
+ }
145
+
146
+ /** 对外目录条目(`/v1/models` 与 CLI models 共用形状)。 */
147
+ export function buildRaccoonModelList({ id = "raccoon", models } = {}) {
148
+ const list = models || raccoonFallbackModels();
149
+ return list.map((m) => ({
150
+ id: joinModelId(id, m.id),
151
+ object: "model",
152
+ owned_by: "raccoon",
153
+ name: raccoonDisplayName(m),
154
+ context_length: m.contextWindow,
155
+ max_tokens: m.maxTokens,
156
+ supports_image: m.supportsImage,
157
+ multiplier: m.multiplier,
158
+ mark: raccoonMultiplierLabel(m) || undefined,
159
+ }));
160
+ }
@@ -1,102 +1,102 @@
1
- // raccoon 密码登录(手机号 + 密码)—— 多账号自动化添加的捷径(免扫码)。
2
- // 契约、端点选择理由与「为什么必须加密」见 const.js 的 raccoonPasswordLoginUrl 注释;
3
- // 加密算法与 key 见 crypto.js(单一源)。
4
- //
5
- // ⚠ 安全红线:密码**只作为请求体出现一次**(且出站前已被加密)。本模块不落盘、不打印、不记日志;
6
- // 抛出的错误里也绝不带上请求体原文(上游 message 是它自己的文案,安全)。
7
- import {
8
- RACCOON_PASSWORD_ERRORS,
9
- RACCOON_REQUEST_TIMEOUT_MS,
10
- raccoonPasswordLoginUrl,
11
- } from "./const.js";
12
- import { raccoonAnonymousHeaders } from "./headers.js";
13
- import { isRaccoonOk, parseRaccoonEnvelope, raccoonEnvelopeText } from "./envelope.js";
14
- import { decodeJwtExpMs } from "./auth.js";
15
- import { raccoonEncryptField } from "./crypto.js";
16
-
17
- /** nation_code 归一:接受 `"86"` / `"+86"` / `86`,统一成不带加号的数字串。 */
18
- export function normalizeNationCode(raw) {
19
- const s = String(raw ?? "").trim().replace(/^\+/, "");
20
- return /^\d{1,4}$/.test(s) ? s : "";
21
- }
22
-
23
- /** 手机号归一:只留数字(去空格、连字符、括号)。空结果由调用方判错。 */
24
- export function normalizePhone(raw) {
25
- return String(raw ?? "").replace(/[^\d]/g, "");
26
- }
27
-
28
- /** 上游登录失败 → 人话 Error。命中码表用它,否则透出上游 message(绝不带请求体)。 */
29
- function passwordLoginError(envelope, status) {
30
- const key = String(envelope?.code ?? "");
31
- const friendly = RACCOON_PASSWORD_ERRORS[key];
32
- const err = new Error(`raccoon: 密码登录失败:${friendly || raccoonEnvelopeText(envelope, `HTTP ${status}`)}`);
33
- err.code = envelope?.code;
34
- err.status = status;
35
- return err;
36
- }
37
-
38
- /**
39
- * 手机号 + 密码 → credential(与扫码登录**同一形状**,可直接喂 saveRaccoonAccount)。
40
- *
41
- * 返回 `{ access_token, refresh_token, expires_at?, office_identity? }`;
42
- * 失败抛「可操作」的错误(码表命中给人话,未命中透出上游文案)。
43
- * 本地校验先于出网:手机号/密码为空当场报错,不白跑一次请求。
44
- *
45
- * ⚠ 出站前 phone/password 都经 `raccoonEncryptField` 加密(上游契约,见 const.js)——
46
- * 发明文只会换来一句「手机号或密码不正确」,把格式错误伪装成密码错误。
47
- */
48
- export async function loginRaccoonWithPassword({ phone, password, nationCode = "86" } = {}, {
49
- fetchImpl = fetch,
50
- env = process.env,
51
- timeoutMs = RACCOON_REQUEST_TIMEOUT_MS,
52
- } = {}) {
53
- const nc = normalizeNationCode(nationCode);
54
- const ph = normalizePhone(phone);
55
- if (!nc) throw new Error("raccoon: 国家码不合法(用 86 这类数字,或 +86)");
56
- if (!ph) throw new Error("raccoon: 手机号不能为空");
57
- if (String(password ?? "").length === 0) throw new Error("raccoon: 密码不能为空");
58
-
59
- // 未知错误码时透出上游 message 而非裸码 —— 上游新旧通道对同一错误的形态不同:
60
- // 新 IAM 把 message 放顶层(AUTH_ACCOUNT_OR_PASSWORD_INVALID),旧 web 只有 message/details。
61
- const body = {
62
- nation_code: nc,
63
- phone: raccoonEncryptField(ph),
64
- password: raccoonEncryptField(String(password)),
65
- };
66
-
67
- let res;
68
- try {
69
- res = await fetchImpl(raccoonPasswordLoginUrl(), {
70
- method: "POST",
71
- headers: raccoonAnonymousHeaders({ env }),
72
- body: JSON.stringify(body),
73
- signal: AbortSignal.timeout(timeoutMs),
74
- });
75
- } catch (error) {
76
- throw new Error(`raccoon: 密码登录请求失败:${error?.message ?? error}`);
77
- }
78
-
79
- let parsed;
80
- try {
81
- parsed = await res.json();
82
- } catch {
83
- throw new Error(`raccoon: 密码登录响应不是 JSON(HTTP ${res.status})`);
84
- }
85
- const envelope = parseRaccoonEnvelope(parsed, res.status);
86
- // 上游把错误码同时塞在 `code` 与 `message`:web 通道 `{code:"AUTH_...",message:"AUTH_..."}`
87
- // 时 message 是裸码,不能当人话用 —— 故这里把 code 也纳入,让码表能命中。
88
- if (!isRaccoonOk(envelope)) throw passwordLoginError(envelope, res.status);
89
-
90
- const data = envelope.data ?? {};
91
- const accessToken = typeof data.access_token === "string" ? data.access_token : "";
92
- if (!accessToken) throw new Error("raccoon: 密码登录响应缺少 access_token");
93
- const refreshToken = typeof data.refresh_token === "string" ? data.refresh_token : "";
94
- const officeIdentity = typeof data.office_identity === "string" ? data.office_identity : "";
95
- const expMs = decodeJwtExpMs(accessToken);
96
- return {
97
- access_token: accessToken,
98
- refresh_token: refreshToken,
99
- ...(expMs === undefined ? {} : { expires_at: String(expMs) }),
100
- ...(officeIdentity ? { office_identity: officeIdentity } : {}),
101
- };
102
- }
1
+ // raccoon 密码登录(手机号 + 密码)—— 多账号自动化添加的捷径(免扫码)。
2
+ // 契约、端点选择理由与「为什么必须加密」见 const.js 的 raccoonPasswordLoginUrl 注释;
3
+ // 加密算法与 key 见 crypto.js(单一源)。
4
+ //
5
+ // ⚠ 安全红线:密码**只作为请求体出现一次**(且出站前已被加密)。本模块不落盘、不打印、不记日志;
6
+ // 抛出的错误里也绝不带上请求体原文(上游 message 是它自己的文案,安全)。
7
+ import {
8
+ RACCOON_PASSWORD_ERRORS,
9
+ RACCOON_REQUEST_TIMEOUT_MS,
10
+ raccoonPasswordLoginUrl,
11
+ } from "./const.js";
12
+ import { raccoonAnonymousHeaders } from "./headers.js";
13
+ import { isRaccoonOk, parseRaccoonEnvelope, raccoonEnvelopeText } from "./envelope.js";
14
+ import { decodeJwtExpMs } from "./auth.js";
15
+ import { raccoonEncryptField } from "./crypto.js";
16
+
17
+ /** nation_code 归一:接受 `"86"` / `"+86"` / `86`,统一成不带加号的数字串。 */
18
+ export function normalizeNationCode(raw) {
19
+ const s = String(raw ?? "").trim().replace(/^\+/, "");
20
+ return /^\d{1,4}$/.test(s) ? s : "";
21
+ }
22
+
23
+ /** 手机号归一:只留数字(去空格、连字符、括号)。空结果由调用方判错。 */
24
+ export function normalizePhone(raw) {
25
+ return String(raw ?? "").replace(/[^\d]/g, "");
26
+ }
27
+
28
+ /** 上游登录失败 → 人话 Error。命中码表用它,否则透出上游 message(绝不带请求体)。 */
29
+ function passwordLoginError(envelope, status) {
30
+ const key = String(envelope?.code ?? "");
31
+ const friendly = RACCOON_PASSWORD_ERRORS[key];
32
+ const err = new Error(`raccoon: 密码登录失败:${friendly || raccoonEnvelopeText(envelope, `HTTP ${status}`)}`);
33
+ err.code = envelope?.code;
34
+ err.status = status;
35
+ return err;
36
+ }
37
+
38
+ /**
39
+ * 手机号 + 密码 → credential(与扫码登录**同一形状**,可直接喂 saveRaccoonAccount)。
40
+ *
41
+ * 返回 `{ access_token, refresh_token, expires_at?, office_identity? }`;
42
+ * 失败抛「可操作」的错误(码表命中给人话,未命中透出上游文案)。
43
+ * 本地校验先于出网:手机号/密码为空当场报错,不白跑一次请求。
44
+ *
45
+ * ⚠ 出站前 phone/password 都经 `raccoonEncryptField` 加密(上游契约,见 const.js)——
46
+ * 发明文只会换来一句「手机号或密码不正确」,把格式错误伪装成密码错误。
47
+ */
48
+ export async function loginRaccoonWithPassword({ phone, password, nationCode = "86" } = {}, {
49
+ fetchImpl = fetch,
50
+ env = process.env,
51
+ timeoutMs = RACCOON_REQUEST_TIMEOUT_MS,
52
+ } = {}) {
53
+ const nc = normalizeNationCode(nationCode);
54
+ const ph = normalizePhone(phone);
55
+ if (!nc) throw new Error("raccoon: 国家码不合法(用 86 这类数字,或 +86)");
56
+ if (!ph) throw new Error("raccoon: 手机号不能为空");
57
+ if (String(password ?? "").length === 0) throw new Error("raccoon: 密码不能为空");
58
+
59
+ // 未知错误码时透出上游 message 而非裸码 —— 上游新旧通道对同一错误的形态不同:
60
+ // 新 IAM 把 message 放顶层(AUTH_ACCOUNT_OR_PASSWORD_INVALID),旧 web 只有 message/details。
61
+ const body = {
62
+ nation_code: nc,
63
+ phone: raccoonEncryptField(ph),
64
+ password: raccoonEncryptField(String(password)),
65
+ };
66
+
67
+ let res;
68
+ try {
69
+ res = await fetchImpl(raccoonPasswordLoginUrl(), {
70
+ method: "POST",
71
+ headers: raccoonAnonymousHeaders({ env }),
72
+ body: JSON.stringify(body),
73
+ signal: AbortSignal.timeout(timeoutMs),
74
+ });
75
+ } catch (error) {
76
+ throw new Error(`raccoon: 密码登录请求失败:${error?.message ?? error}`);
77
+ }
78
+
79
+ let parsed;
80
+ try {
81
+ parsed = await res.json();
82
+ } catch {
83
+ throw new Error(`raccoon: 密码登录响应不是 JSON(HTTP ${res.status})`);
84
+ }
85
+ const envelope = parseRaccoonEnvelope(parsed, res.status);
86
+ // 上游把错误码同时塞在 `code` 与 `message`:web 通道 `{code:"AUTH_...",message:"AUTH_..."}`
87
+ // 时 message 是裸码,不能当人话用 —— 故这里把 code 也纳入,让码表能命中。
88
+ if (!isRaccoonOk(envelope)) throw passwordLoginError(envelope, res.status);
89
+
90
+ const data = envelope.data ?? {};
91
+ const accessToken = typeof data.access_token === "string" ? data.access_token : "";
92
+ if (!accessToken) throw new Error("raccoon: 密码登录响应缺少 access_token");
93
+ const refreshToken = typeof data.refresh_token === "string" ? data.refresh_token : "";
94
+ const officeIdentity = typeof data.office_identity === "string" ? data.office_identity : "";
95
+ const expMs = decodeJwtExpMs(accessToken);
96
+ return {
97
+ access_token: accessToken,
98
+ refresh_token: refreshToken,
99
+ ...(expMs === undefined ? {} : { expires_at: String(expMs) }),
100
+ ...(officeIdentity ? { office_identity: officeIdentity } : {}),
101
+ };
102
+ }