mslxdff 0.1.199 → 0.1.203

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 (327) hide show
  1. package/README.md +3 -3
  2. package/bin/mslxdff.js +1 -1
  3. package/package.json +38 -38
  4. package/plugins/README.md +14 -14
  5. package/plugins/prefer-model.mjs.example +14 -14
  6. package/src/anthropic/count-tokens.js +61 -61
  7. package/src/anthropic/request-body.js +126 -126
  8. package/src/anthropic/shared.js +18 -18
  9. package/src/anthropic/sse-aggregate.js +56 -56
  10. package/src/anthropic/translate.js +287 -287
  11. package/src/anthropic/web-search.js +361 -361
  12. package/src/api-kind.js +31 -31
  13. package/src/autostart.js +288 -288
  14. package/src/bench/probe.js +1 -1
  15. package/src/bench/runner.js +1 -1
  16. package/src/bench/via-probe.js +1 -1
  17. package/src/bench/via.js +144 -144
  18. package/src/bench/workbuddy-bench.js +1 -1
  19. package/src/chat/config.js +11 -11
  20. package/src/chat/cooling.js +81 -81
  21. package/src/chat/direct.js +58 -58
  22. package/src/chat/index.js +6 -6
  23. package/src/chat/spinner.js +38 -38
  24. package/src/chat/sse.js +69 -69
  25. package/src/chat/stats.js +247 -247
  26. package/src/chat/store.js +48 -48
  27. package/src/chat/tool-handlers.js +67 -67
  28. package/src/chat/tools.js +1 -1
  29. package/src/chat/upstream.js +59 -59
  30. package/src/chat-pipeline/auto-race.js +158 -158
  31. package/src/chat-pipeline/empty-turn.js +147 -147
  32. package/src/chat-pipeline/engine.js +17 -17
  33. package/src/chat-pipeline/serial-trial.js +283 -283
  34. package/src/chat-pipeline/terminal-verdict.js +147 -147
  35. package/src/chooser.js +54 -54
  36. package/src/claude-desktop/apply.js +101 -101
  37. package/src/claude-desktop/context1m.js +38 -38
  38. package/src/claude-desktop/doctor.js +146 -146
  39. package/src/claude-desktop/slots.js +152 -152
  40. package/src/cli/bootstrap.js +1 -1
  41. package/src/cli/commands/claude-desktop.js +284 -284
  42. package/src/cli/commands/daemon.js +231 -231
  43. package/src/cli/commands/model/list-live.js +22 -22
  44. package/src/cli/commands/model/list-providers.js +76 -76
  45. package/src/cli/commands/model/list-render.js +77 -77
  46. package/src/cli/commands/model/list-sort.js +52 -52
  47. package/src/cli/commands/model/list-tty.js +76 -76
  48. package/src/cli/commands/model/list.js +166 -166
  49. package/src/cli/commands/model/live-models.js +24 -24
  50. package/src/cli/commands/model/stats.js +43 -43
  51. package/src/cli/commands/model/status.js +47 -47
  52. package/src/cli/commands/model.js +51 -51
  53. package/src/cli/commands/provider/add.js +108 -108
  54. package/src/cli/commands/provider/allowlist.js +99 -99
  55. package/src/cli/commands/provider/cline-free.js +99 -99
  56. package/src/cli/commands/provider/cline-login.js +218 -218
  57. package/src/cli/commands/provider/cline-quota.js +65 -65
  58. package/src/cli/commands/provider/codearts-login.js +45 -45
  59. package/src/cli/commands/provider/config.js +119 -119
  60. package/src/cli/commands/provider/globalqwenwork-login.js +126 -126
  61. package/src/cli/commands/provider/keys-guard.js +50 -50
  62. package/src/cli/commands/provider/keys.js +161 -161
  63. package/src/cli/commands/provider/qoder-checkin.js +81 -81
  64. package/src/cli/commands/provider/qoder-login.js +102 -102
  65. package/src/cli/commands/provider/raccoon-login.js +180 -180
  66. package/src/cli/commands/provider/raccoon-password.js +162 -162
  67. package/src/cli/commands/provider/raccoon-quota.js +109 -109
  68. package/src/cli/commands/provider/traework-checkin.js +144 -144
  69. package/src/cli/commands/provider/traework-login.js +164 -164
  70. package/src/cli/commands/provider/workbuddy-login.js +153 -153
  71. package/src/cli/commands/provider/zcode-login.js +77 -77
  72. package/src/cli/commands/provider/zcode-quota.js +55 -55
  73. package/src/cli/commands/provider.js +1 -1
  74. package/src/cli/commands/stats.js +149 -149
  75. package/src/cli/commands/system.js +261 -261
  76. package/src/cli/commands/timezone.js +47 -47
  77. package/src/cli/commands/use-group.js +60 -60
  78. package/src/cli/commands/workbuddy.js +189 -189
  79. package/src/cli/format.js +141 -141
  80. package/src/cli/group-helpers.js +70 -70
  81. package/src/cli/help.js +80 -80
  82. package/src/cli/index.js +70 -70
  83. package/src/cli/interactive.js +121 -121
  84. package/src/cli/join-core.js +99 -99
  85. package/src/cli/join-wizard.js +175 -175
  86. package/src/cli/policy.js +136 -136
  87. package/src/cli/provider-row.js +126 -126
  88. package/src/cli/util.js +24 -24
  89. package/src/compat.js +53 -53
  90. package/src/events.js +38 -38
  91. package/src/free-lane.js +131 -131
  92. package/src/free-watcher.js +63 -63
  93. package/src/groups.js +262 -262
  94. package/src/logs.js +195 -195
  95. package/src/metrics.js +62 -62
  96. package/src/model-capabilities/context-lookup.js +65 -65
  97. package/src/model-capabilities/merge.js +142 -142
  98. package/src/model-trace.js +187 -187
  99. package/src/models.js +1 -1
  100. package/src/opencode-identity.js +72 -72
  101. package/src/peers.js +254 -251
  102. package/src/plugins.js +107 -107
  103. package/src/providers/base.js +164 -164
  104. package/src/providers/classify.js +22 -22
  105. package/src/providers/cline/allowlist-sync.js +93 -93
  106. package/src/providers/cline/free-catalog.js +71 -71
  107. package/src/providers/cline/headers.js +37 -37
  108. package/src/providers/cline/index.js +130 -130
  109. package/src/providers/cline/models.js +150 -150
  110. package/src/providers/cline/usage.js +274 -274
  111. package/src/providers/codearts/auth-pool.js +148 -148
  112. package/src/providers/codearts/concurrency.js +34 -34
  113. package/src/providers/codearts/const.js +65 -65
  114. package/src/providers/codearts/dpop.js +95 -95
  115. package/src/providers/codearts/index.js +175 -175
  116. package/src/providers/codearts/login.js +214 -214
  117. package/src/providers/codearts/models.js +182 -182
  118. package/src/providers/codearts/sign.js +96 -96
  119. package/src/providers/codearts/sse.js +204 -204
  120. package/src/providers/codearts/stream.js +209 -209
  121. package/src/providers/codearts/sts.js +124 -124
  122. package/src/providers/codearts.js +1 -1
  123. package/src/providers/dispatcher.js +146 -146
  124. package/src/providers/generic.js +156 -156
  125. package/src/providers/globalqwenwork/account-store.js +141 -141
  126. package/src/providers/globalqwenwork/constants.js +73 -73
  127. package/src/providers/globalqwenwork/crypto.js +2 -2
  128. package/src/providers/globalqwenwork/http.js +2 -2
  129. package/src/providers/globalqwenwork/index.js +331 -331
  130. package/src/providers/globalqwenwork/payload.js +147 -147
  131. package/src/providers/globalqwenwork/rsa.js +2 -2
  132. package/src/providers/globalqwenwork/sse.js +2 -2
  133. package/src/providers/globalqwenwork/stream.js +2 -2
  134. package/src/providers/globalqwenwork.js +1 -1
  135. package/src/providers/globalworkbuddy/account-store.js +198 -198
  136. package/src/providers/globalworkbuddy/auth.js +97 -97
  137. package/src/providers/globalworkbuddy/catalog.js +138 -138
  138. package/src/providers/globalworkbuddy/constants.js +73 -73
  139. package/src/providers/globalworkbuddy/device-auth.js +120 -120
  140. package/src/providers/globalworkbuddy/errors.js +98 -98
  141. package/src/providers/globalworkbuddy/headers.js +89 -89
  142. package/src/providers/globalworkbuddy/models.js +96 -96
  143. package/src/providers/globalworkbuddy/payload.js +80 -80
  144. package/src/providers/index.js +5 -5
  145. package/src/providers/model-id.js +172 -172
  146. package/src/providers/opencode.js +19 -19
  147. package/src/providers/openrouter.js +31 -31
  148. package/src/providers/qoder/account-store.js +106 -106
  149. package/src/providers/qoder/aggregate.js +60 -60
  150. package/src/providers/qoder/baseprompt.json +591 -591
  151. package/src/providers/qoder/checkin.js +160 -160
  152. package/src/providers/qoder/constants.js +44 -44
  153. package/src/providers/qoder/encode.js +41 -41
  154. package/src/providers/qoder/fingerprint.js +38 -38
  155. package/src/providers/qoder/models.js +77 -77
  156. package/src/providers/qoder/oauth.js +84 -84
  157. package/src/providers/qoder/payload.js +162 -162
  158. package/src/providers/qoder/request.js +27 -27
  159. package/src/providers/qoder/session.js +134 -134
  160. package/src/providers/qoder/sticky.js +38 -38
  161. package/src/providers/qoder/stream.js +116 -116
  162. package/src/providers/qoder.js +1 -1
  163. package/src/providers/raccoon/account-store.js +216 -216
  164. package/src/providers/raccoon/auth.js +67 -67
  165. package/src/providers/raccoon/chat.js +228 -228
  166. package/src/providers/raccoon/const.js +180 -165
  167. package/src/providers/raccoon/credits.js +185 -175
  168. package/src/providers/raccoon/crypto.js +43 -0
  169. package/src/providers/raccoon/envelope.js +31 -31
  170. package/src/providers/raccoon/headers.js +45 -45
  171. package/src/providers/raccoon/index.js +216 -216
  172. package/src/providers/raccoon/login.js +279 -279
  173. package/src/providers/raccoon/models.js +160 -160
  174. package/src/providers/raccoon/password-login.js +102 -87
  175. package/src/providers/raccoon/qr-codewords.js +115 -115
  176. package/src/providers/raccoon/qr.js +236 -236
  177. package/src/providers/raccoon/sse.js +135 -135
  178. package/src/providers/responses-channel.js +120 -120
  179. package/src/providers/share-keys.js +53 -53
  180. package/src/providers/traework/account-store.js +234 -234
  181. package/src/providers/traework/chat.js +217 -217
  182. package/src/providers/traework/checkin.js +77 -77
  183. package/src/providers/traework/constants.js +41 -41
  184. package/src/providers/traework/errors.js +52 -52
  185. package/src/providers/traework/headers.js +79 -79
  186. package/src/providers/traework/index.js +132 -132
  187. package/src/providers/traework/market-identity.js +116 -116
  188. package/src/providers/traework/models.js +98 -98
  189. package/src/providers/traework/payload.js +86 -86
  190. package/src/providers/traework/sse.js +184 -184
  191. package/src/providers/traework/token.js +52 -52
  192. package/src/providers/traework.js +2 -2
  193. package/src/providers/workbuddy/account-store.js +186 -186
  194. package/src/providers/workbuddy/auth.js +158 -158
  195. package/src/providers/workbuddy/balance.js +86 -86
  196. package/src/providers/workbuddy/cat-travel.js +125 -125
  197. package/src/providers/workbuddy/checkin.js +75 -75
  198. package/src/providers/workbuddy/desktop-info.js +78 -78
  199. package/src/providers/workbuddy/growth-api.js +67 -67
  200. package/src/providers/workbuddy/growth-plans.js +63 -63
  201. package/src/providers/workbuddy/growth.js +181 -181
  202. package/src/providers/workbuddy/index.js +217 -217
  203. package/src/providers/workbuddy/models.js +126 -126
  204. package/src/providers/workbuddy/payload.js +141 -141
  205. package/src/providers/workbuddy/rotation-log.js +83 -83
  206. package/src/providers/workbuddy/sanitize-tools.js +61 -61
  207. package/src/providers/workbuddy/sdk-chat.js +26 -26
  208. package/src/providers/workbuddy-balance.js +78 -78
  209. package/src/providers/zcode/account-store.js +129 -129
  210. package/src/providers/zcode/auth.js +28 -28
  211. package/src/providers/zcode/chat.js +179 -179
  212. package/src/providers/zcode/const.js +54 -54
  213. package/src/providers/zcode/context-shape.js +162 -162
  214. package/src/providers/zcode/headers.js +93 -93
  215. package/src/providers/zcode/index.js +124 -124
  216. package/src/providers/zcode/models.js +65 -65
  217. package/src/providers/zcode/oauth.js +120 -120
  218. package/src/providers/zcode/quota.js +176 -176
  219. package/src/providers/zcode/sse.js +196 -196
  220. package/src/readline-compat.js +25 -25
  221. package/src/responses/translate.js +311 -311
  222. package/src/routes/chat/broadband-handler.js +72 -72
  223. package/src/routes/chat/empty-turn-report.js +31 -31
  224. package/src/routes/chat/exhausted-handler.js +87 -87
  225. package/src/routes/chat/hedge-handler.js +130 -130
  226. package/src/routes/chat/index.js +1 -1
  227. package/src/routes/chat/local-handler.js +63 -63
  228. package/src/routes/chat/peer-handler.js +81 -81
  229. package/src/routes/chat.js +3 -3
  230. package/src/routes/fallback.js +88 -88
  231. package/src/routes/groups-relay.js +214 -214
  232. package/src/routes/groups.js +122 -122
  233. package/src/routes/helpers.js +83 -83
  234. package/src/routes/index.js +147 -147
  235. package/src/routes/messages-route.js +365 -365
  236. package/src/routes/peers.js +23 -1
  237. package/src/routes/relay-queue.js +181 -181
  238. package/src/routes/relay.js +60 -60
  239. package/src/routes/responses-route.js +213 -213
  240. package/src/routes/stream-body.js +84 -84
  241. package/src/routes/stream-hold.js +337 -337
  242. package/src/routes/stream-scan.js +366 -366
  243. package/src/routes.js +3 -3
  244. package/src/runtime/auto-update.js +113 -113
  245. package/src/runtime/bootstrap.js +8 -0
  246. package/src/runtime/broadband-stream.js +79 -79
  247. package/src/runtime/broadband.js +103 -103
  248. package/src/runtime/group-sync.js +81 -81
  249. package/src/runtime/keep-warm.js +120 -0
  250. package/src/runtime/lifecycle-forensics.js +116 -116
  251. package/src/runtime/lifecycle-log.js +87 -87
  252. package/src/runtime/log-stamp.js +73 -73
  253. package/src/runtime/proc-info.js +137 -137
  254. package/src/runtime/qoder-checkin.js +93 -93
  255. package/src/runtime/raccoon-checkin.js +126 -126
  256. package/src/runtime/server-lifecycle.js +189 -189
  257. package/src/runtime/traework-checkin.js +118 -118
  258. package/src/runtime/workbuddy-checkin.js +120 -120
  259. package/src/runtime/workbuddy-growth.js +133 -133
  260. package/src/server.js +7 -0
  261. package/src/state/facade.js +58 -58
  262. package/src/state/memory.js +162 -162
  263. package/src/state/merge.js +25 -25
  264. package/src/state/migrations/cline-unify.js +94 -94
  265. package/src/state/migrations.js +33 -33
  266. package/src/state/persist.js +42 -42
  267. package/src/state/provider-config.js +142 -142
  268. package/src/state/schemas/group.js +31 -31
  269. package/src/state/schemas/model.js +94 -94
  270. package/src/state/schemas/peer.js +31 -31
  271. package/src/state/schemas/port.js +10 -10
  272. package/src/state/schemas/provider.js +202 -202
  273. package/src/state/schemas/timezone.js +70 -70
  274. package/src/state/schemas/token.js +48 -48
  275. package/src/state/schemas/use-group.js +102 -102
  276. package/src/state/store.js +176 -176
  277. package/src/state.js +1 -1
  278. package/src/sync-claude-desktop.js +234 -234
  279. package/src/sync-claude.js +152 -152
  280. package/src/sync-codex.js +116 -116
  281. package/src/sync-workbuddy.js +158 -158
  282. package/src/talk-full-redact.js +129 -129
  283. package/src/talk-full.js +131 -131
  284. package/src/talk-log.js +257 -257
  285. package/src/text-tokens.js +52 -52
  286. package/src/thinking-effort.js +67 -67
  287. package/src/time.js +106 -106
  288. package/src/timeline.js +26 -26
  289. package/src/transport/index.js +1 -1
  290. package/src/upstream-engine/index.js +74 -72
  291. package/src/upstream-engine/mode.js +15 -15
  292. package/src/upstream-engine/sdk/attempt.js +221 -221
  293. package/src/upstream-engine/sdk/chat.js +28 -28
  294. package/src/upstream-engine/sdk/convert.js +192 -192
  295. package/src/upstream-engine/sdk/diagnose.js +62 -62
  296. package/src/upstream-engine/sdk/dispatch.js +55 -55
  297. package/src/upstream-engine/sdk/responses.js +176 -176
  298. package/src/upstream-engine/sdk/sse.js +166 -166
  299. package/src/upstream-responses.js +305 -305
  300. package/src/upstream.js +53 -1
  301. package/src/usage/record.js +106 -106
  302. package/src/usage/report.js +195 -195
  303. package/src/v6pool/claim.js +157 -157
  304. package/src/v6pool/config.js +65 -65
  305. package/src/v6pool/detect.js +130 -130
  306. package/src/v6pool/index.js +152 -152
  307. package/src/v6pool/prefix.js +140 -140
  308. package/src/v6pool/probe.js +40 -40
  309. package/src/v6pool/report.js +104 -104
  310. package/src/v6pool/rotate.js +263 -239
  311. package/src/warp-pool/config.js +74 -74
  312. package/src/warp-pool/health.js +106 -106
  313. package/src/warp-pool/identity.js +141 -141
  314. package/src/warp-pool/index.js +358 -358
  315. package/src/warp-pool/keys.js +29 -29
  316. package/src/warp-pool/nodes.js +95 -95
  317. package/src/warp-pool/ports.js +160 -160
  318. package/src/warp-pool/register.js +113 -113
  319. package/src/warp-pool/report.js +232 -232
  320. package/src/warp-pool/revive.js +168 -168
  321. package/src/warp-pool/rotate.js +214 -214
  322. package/src/warp-pool/strategy.js +43 -43
  323. package/src/warp-pool/verdict.js +69 -69
  324. package/src/warp-pool/wireproxy.js +162 -162
  325. package/docs/cli_help_mini.md +0 -146
  326. package/src/providers/AGENTS.md +0 -49
  327. package/src/routes/AGENTS.md +0 -37
@@ -1,173 +1,173 @@
1
- // 模型 ID 前缀解析:`<provider>/<rawId>`。默认供应商(opencode)保持裸 id(向后兼容),
2
- // 其它供应商必须带前缀,请求按前缀路由到对应上游通道。
3
- //
4
- // WorkBuddy 别名系统:
5
- // WorkBuddy 不支持模型 ID 中的 /,写入 models.json 时替换为 -
6
- // mslxdff 在收到请求时通过固定映射表还原为 / 格式
7
- // 映射表持久化在 ~/.config/mslxdff/model-aliases.json
8
-
9
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
10
- import { dirname, join } from "node:path";
11
- import os from "node:os";
12
-
13
- export const DEFAULT_PROVIDER = "opencode";
14
-
15
- const ALIASES = { oc: "opencode", opencode: "opencode", clinebot: "cline", "cline-bot": "cline" };
16
-
17
- // WorkBuddy 别名映射(alias → canonical):cline-z-ai-glm-5.3-flash → cline/z-ai/glm-5.3-flash
18
- const _modelAliases = new Map();
19
-
20
- function aliasFile() {
21
- const env = process.env.MSLXDFF_ALIASES_FILE;
22
- if (typeof env === "string" && env.trim()) return env.trim();
23
- return join(os.homedir(), ".config", "mslxdff", "model-aliases.json");
24
- }
25
-
26
- export function loadModelAliases(file) {
27
- const fp = file || aliasFile();
28
- try {
29
- const raw = JSON.parse(readFileSync(fp, "utf8"));
30
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
31
- for (const [k, v] of Object.entries(raw)) {
32
- if (typeof v === "string" && v) _modelAliases.set(String(k), v);
33
- }
34
- _aliasLoaded = true;
35
- }
36
- } catch {}
37
- }
38
-
39
- export function registerModelAlias(alias, canonical) {
40
- const a = String(alias || "").trim();
41
- const c = String(canonical || "").trim();
42
- if (!a || !c || a === c) return;
43
- _modelAliases.set(a, c);
44
- }
45
-
46
- export function persistModelAliases(file) {
47
- const fp = file || aliasFile();
48
- try {
49
- mkdirSync(dirname(fp), { recursive: true });
50
- const obj = Object.fromEntries(_modelAliases);
51
- writeFileSync(fp, JSON.stringify(obj, null, 2), "utf8");
52
- } catch {}
53
- }
54
-
55
- /** 别名文件路径(单一真相,供只读消费方复用,别再各自拼一遍)。 */
56
- export function modelAliasFile() {
57
- return aliasFile();
58
- }
59
-
60
- /** 当前已装载的 alias 快照(只读;不触发重读,判"会不会被覆盖"用)。 */
61
- export function snapshotModelAliases() {
62
- return Object.fromEntries(_modelAliases);
63
- }
64
-
65
- let _aliasLoaded = false;
66
- export function getModelAlias(id) {
67
- const key = String(id || "").trim();
68
- let hit = _modelAliases.get(key);
69
- if (hit) return hit;
70
- // 未命中时尝试加载(支持 daemon 运行期间由 -setto 进程写入的新 alias)
71
- try { loadModelAliases(); } catch {}
72
- return _modelAliases.get(key) || null;
73
- }
74
-
75
- // 反向查询:canonical id → alias(如 cline/z-ai/glm-5.3-flash → cline-z-ai-glm-5.3-flash)
76
- export function getAliasForModel(canonicalId) {
77
- const c = String(canonicalId || "").trim();
78
- if (!_aliasLoaded) { try { loadModelAliases(); } catch {} }
79
- for (const [alias, target] of _modelAliases) {
80
- if (target === c) return alias;
81
- }
82
- return null;
83
- }
84
-
85
- export function normalizeProviderId(p) {
86
- const id = String(p || "").trim();
87
- return ALIASES[id] || id;
88
- }
89
-
90
- // 把(可能带前缀的)模型 id 拆成 { provider, raw, prefixed }
91
- // - 无前缀(含默认供应商裸 id)→ provider = DEFAULT_PROVIDER
92
- // - 前缀是已知供应商 → 对应路由;开头的 `oc/` 视为 opencode 别名
93
- export function splitModelId(id, knownProviders = []) {
94
- const s = String(id || "");
95
- if (!s) return { provider: DEFAULT_PROVIDER, raw: s, prefixed: false };
96
- const idx = s.indexOf("/");
97
- if (idx > 0) {
98
- const head = normalizeProviderId(s.slice(0, idx));
99
- const known = new Set([DEFAULT_PROVIDER, ...knownProviders]);
100
- if (known.has(head)) {
101
- return { provider: head, raw: s.slice(idx + 1), prefixed: true };
102
- }
103
- }
104
- return { provider: DEFAULT_PROVIDER, raw: s, prefixed: false };
105
- }
106
-
107
- // 生成对外暴露的模型 id:默认供应商裸 id(向后兼容),其它供应商带前缀
108
- export function joinModelId(provider, raw, { force = false } = {}) {
109
- provider = normalizeProviderId(provider || "");
110
- raw = String(raw || "");
111
- if (provider === DEFAULT_PROVIDER && !force) return raw;
112
- return `${provider}/${raw}`;
113
- }
114
-
115
- // 观测用全称:始终带前缀(opencode 也带),用于 modelStats / calls.log 聚合主键
116
- export function toFullId(provider, raw) {
117
- provider = normalizeProviderId(provider || "");
118
- raw = String(raw || "").trim();
119
- if (!raw) return "";
120
- if (!provider || provider === DEFAULT_PROVIDER) return `${DEFAULT_PROVIDER}/${raw}`;
121
- return `${provider}/${raw}`;
122
- }
123
-
124
- // 归一任意输入为全称:裸 id -> opencode/xxx,已带前缀保持不变(大小写保留,provider 归一)
125
- // 优先查 WorkBuddy 别名表(固定映射),命中直接还原为 / 格式
126
- export function normalizeFullId(id, knownProviders = []) {
127
- const s = String(id || "").trim();
128
- if (!s) return "";
129
- // 1. 查别名表(cline-z-ai-glm-5.3-flash → cline/z-ai/glm-5.3-flash)
130
- const aliased = getModelAlias(s);
131
- if (aliased) return aliased;
132
- // 2. 已含 /,走原有逻辑
133
- const idx = s.indexOf("/");
134
- if (idx > 0) {
135
- const head = normalizeProviderId(s.slice(0, idx));
136
- if (knownProviders.length) {
137
- const known = new Set([DEFAULT_PROVIDER, ...knownProviders.map(normalizeProviderId)]);
138
- if (known.has(head)) return `${head}/${s.slice(idx + 1)}`;
139
- return `${DEFAULT_PROVIDER}/${s}`;
140
- }
141
- return `${head}/${s.slice(idx + 1)}`;
142
- }
143
- return `${DEFAULT_PROVIDER}/${s}`;
144
- }
145
-
146
- const BUILTIN_PROVIDERS = new Set([DEFAULT_PROVIDER, "openrouter"]);
147
-
148
- /**
149
- * 过滤 provider 已不存在的 picks 孤儿(纯函数,零 IO,可单测)。
150
- * - 含 `/` 前缀:head(归一+小写)∉ 已知集合 → stale(如 amddev/*)。
151
- * - Bare id:∉ 上游 liveIds ∪ allowlist 展开 → stale(如 laguna-s-2.1-free)。
152
- * - provider 存在但未启用(如 deepseek 缺 baseUrl)→ 保留,不可用由 [error] 表达。
153
- * - 未知一律保留(默认放行不误删显示);dash 别名经 resolveAlias 还原后判定;
154
- * 上游 liveIds 为空时 bare 一律保留(无法判定则放行)。
155
- * 数据不动,只决定进不进 `-models` 交互列表 — 见 .scratch/models-stale-picks/SPEC.md。
156
- */
157
- export function filterStalePicks(pickedIds, { knownProviders = [], liveIds = [], allowedIds = [], resolveAlias = getModelAlias } = {}) {
158
- const known = new Set([...BUILTIN_PROVIDERS, ...(knownProviders || []).map((p) => normalizeProviderId(p).toLowerCase())]);
159
- const live = new Set((liveIds || []).map((id) => String(id).toLowerCase()));
160
- const allowed = new Set((allowedIds || []).map((id) => String(id).toLowerCase()));
161
- return (pickedIds || []).filter((pick) => {
162
- const raw = String(pick || "").trim();
163
- if (!raw) return false;
164
- let id = raw;
165
- try { id = resolveAlias(raw) || raw; } catch { id = raw; }
166
- const slash = String(id).indexOf("/");
167
- if (slash > 0) return known.has(normalizeProviderId(id.slice(0, slash)).toLowerCase());
168
- // 上游列表为空(刷新与缓存双失败的极端情况)时无法判定 bare 生死 → 保留(默认放行)
169
- if (live.size === 0) return true;
170
- const low = String(id).toLowerCase();
171
- return live.has(low) || allowed.has(low);
172
- });
1
+ // 模型 ID 前缀解析:`<provider>/<rawId>`。默认供应商(opencode)保持裸 id(向后兼容),
2
+ // 其它供应商必须带前缀,请求按前缀路由到对应上游通道。
3
+ //
4
+ // WorkBuddy 别名系统:
5
+ // WorkBuddy 不支持模型 ID 中的 /,写入 models.json 时替换为 -
6
+ // mslxdff 在收到请求时通过固定映射表还原为 / 格式
7
+ // 映射表持久化在 ~/.config/mslxdff/model-aliases.json
8
+
9
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import os from "node:os";
12
+
13
+ export const DEFAULT_PROVIDER = "opencode";
14
+
15
+ const ALIASES = { oc: "opencode", opencode: "opencode", clinebot: "cline", "cline-bot": "cline" };
16
+
17
+ // WorkBuddy 别名映射(alias → canonical):cline-z-ai-glm-5.3-flash → cline/z-ai/glm-5.3-flash
18
+ const _modelAliases = new Map();
19
+
20
+ function aliasFile() {
21
+ const env = process.env.MSLXDFF_ALIASES_FILE;
22
+ if (typeof env === "string" && env.trim()) return env.trim();
23
+ return join(os.homedir(), ".config", "mslxdff", "model-aliases.json");
24
+ }
25
+
26
+ export function loadModelAliases(file) {
27
+ const fp = file || aliasFile();
28
+ try {
29
+ const raw = JSON.parse(readFileSync(fp, "utf8"));
30
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
31
+ for (const [k, v] of Object.entries(raw)) {
32
+ if (typeof v === "string" && v) _modelAliases.set(String(k), v);
33
+ }
34
+ _aliasLoaded = true;
35
+ }
36
+ } catch {}
37
+ }
38
+
39
+ export function registerModelAlias(alias, canonical) {
40
+ const a = String(alias || "").trim();
41
+ const c = String(canonical || "").trim();
42
+ if (!a || !c || a === c) return;
43
+ _modelAliases.set(a, c);
44
+ }
45
+
46
+ export function persistModelAliases(file) {
47
+ const fp = file || aliasFile();
48
+ try {
49
+ mkdirSync(dirname(fp), { recursive: true });
50
+ const obj = Object.fromEntries(_modelAliases);
51
+ writeFileSync(fp, JSON.stringify(obj, null, 2), "utf8");
52
+ } catch {}
53
+ }
54
+
55
+ /** 别名文件路径(单一真相,供只读消费方复用,别再各自拼一遍)。 */
56
+ export function modelAliasFile() {
57
+ return aliasFile();
58
+ }
59
+
60
+ /** 当前已装载的 alias 快照(只读;不触发重读,判"会不会被覆盖"用)。 */
61
+ export function snapshotModelAliases() {
62
+ return Object.fromEntries(_modelAliases);
63
+ }
64
+
65
+ let _aliasLoaded = false;
66
+ export function getModelAlias(id) {
67
+ const key = String(id || "").trim();
68
+ let hit = _modelAliases.get(key);
69
+ if (hit) return hit;
70
+ // 未命中时尝试加载(支持 daemon 运行期间由 -setto 进程写入的新 alias)
71
+ try { loadModelAliases(); } catch {}
72
+ return _modelAliases.get(key) || null;
73
+ }
74
+
75
+ // 反向查询:canonical id → alias(如 cline/z-ai/glm-5.3-flash → cline-z-ai-glm-5.3-flash)
76
+ export function getAliasForModel(canonicalId) {
77
+ const c = String(canonicalId || "").trim();
78
+ if (!_aliasLoaded) { try { loadModelAliases(); } catch {} }
79
+ for (const [alias, target] of _modelAliases) {
80
+ if (target === c) return alias;
81
+ }
82
+ return null;
83
+ }
84
+
85
+ export function normalizeProviderId(p) {
86
+ const id = String(p || "").trim();
87
+ return ALIASES[id] || id;
88
+ }
89
+
90
+ // 把(可能带前缀的)模型 id 拆成 { provider, raw, prefixed }
91
+ // - 无前缀(含默认供应商裸 id)→ provider = DEFAULT_PROVIDER
92
+ // - 前缀是已知供应商 → 对应路由;开头的 `oc/` 视为 opencode 别名
93
+ export function splitModelId(id, knownProviders = []) {
94
+ const s = String(id || "");
95
+ if (!s) return { provider: DEFAULT_PROVIDER, raw: s, prefixed: false };
96
+ const idx = s.indexOf("/");
97
+ if (idx > 0) {
98
+ const head = normalizeProviderId(s.slice(0, idx));
99
+ const known = new Set([DEFAULT_PROVIDER, ...knownProviders]);
100
+ if (known.has(head)) {
101
+ return { provider: head, raw: s.slice(idx + 1), prefixed: true };
102
+ }
103
+ }
104
+ return { provider: DEFAULT_PROVIDER, raw: s, prefixed: false };
105
+ }
106
+
107
+ // 生成对外暴露的模型 id:默认供应商裸 id(向后兼容),其它供应商带前缀
108
+ export function joinModelId(provider, raw, { force = false } = {}) {
109
+ provider = normalizeProviderId(provider || "");
110
+ raw = String(raw || "");
111
+ if (provider === DEFAULT_PROVIDER && !force) return raw;
112
+ return `${provider}/${raw}`;
113
+ }
114
+
115
+ // 观测用全称:始终带前缀(opencode 也带),用于 modelStats / calls.log 聚合主键
116
+ export function toFullId(provider, raw) {
117
+ provider = normalizeProviderId(provider || "");
118
+ raw = String(raw || "").trim();
119
+ if (!raw) return "";
120
+ if (!provider || provider === DEFAULT_PROVIDER) return `${DEFAULT_PROVIDER}/${raw}`;
121
+ return `${provider}/${raw}`;
122
+ }
123
+
124
+ // 归一任意输入为全称:裸 id -> opencode/xxx,已带前缀保持不变(大小写保留,provider 归一)
125
+ // 优先查 WorkBuddy 别名表(固定映射),命中直接还原为 / 格式
126
+ export function normalizeFullId(id, knownProviders = []) {
127
+ const s = String(id || "").trim();
128
+ if (!s) return "";
129
+ // 1. 查别名表(cline-z-ai-glm-5.3-flash → cline/z-ai/glm-5.3-flash)
130
+ const aliased = getModelAlias(s);
131
+ if (aliased) return aliased;
132
+ // 2. 已含 /,走原有逻辑
133
+ const idx = s.indexOf("/");
134
+ if (idx > 0) {
135
+ const head = normalizeProviderId(s.slice(0, idx));
136
+ if (knownProviders.length) {
137
+ const known = new Set([DEFAULT_PROVIDER, ...knownProviders.map(normalizeProviderId)]);
138
+ if (known.has(head)) return `${head}/${s.slice(idx + 1)}`;
139
+ return `${DEFAULT_PROVIDER}/${s}`;
140
+ }
141
+ return `${head}/${s.slice(idx + 1)}`;
142
+ }
143
+ return `${DEFAULT_PROVIDER}/${s}`;
144
+ }
145
+
146
+ const BUILTIN_PROVIDERS = new Set([DEFAULT_PROVIDER, "openrouter"]);
147
+
148
+ /**
149
+ * 过滤 provider 已不存在的 picks 孤儿(纯函数,零 IO,可单测)。
150
+ * - 含 `/` 前缀:head(归一+小写)∉ 已知集合 → stale(如 amddev/*)。
151
+ * - Bare id:∉ 上游 liveIds ∪ allowlist 展开 → stale(如 laguna-s-2.1-free)。
152
+ * - provider 存在但未启用(如 deepseek 缺 baseUrl)→ 保留,不可用由 [error] 表达。
153
+ * - 未知一律保留(默认放行不误删显示);dash 别名经 resolveAlias 还原后判定;
154
+ * 上游 liveIds 为空时 bare 一律保留(无法判定则放行)。
155
+ * 数据不动,只决定进不进 `-models` 交互列表 — 见 .scratch/models-stale-picks/SPEC.md。
156
+ */
157
+ export function filterStalePicks(pickedIds, { knownProviders = [], liveIds = [], allowedIds = [], resolveAlias = getModelAlias } = {}) {
158
+ const known = new Set([...BUILTIN_PROVIDERS, ...(knownProviders || []).map((p) => normalizeProviderId(p).toLowerCase())]);
159
+ const live = new Set((liveIds || []).map((id) => String(id).toLowerCase()));
160
+ const allowed = new Set((allowedIds || []).map((id) => String(id).toLowerCase()));
161
+ return (pickedIds || []).filter((pick) => {
162
+ const raw = String(pick || "").trim();
163
+ if (!raw) return false;
164
+ let id = raw;
165
+ try { id = resolveAlias(raw) || raw; } catch { id = raw; }
166
+ const slash = String(id).indexOf("/");
167
+ if (slash > 0) return known.has(normalizeProviderId(id.slice(0, slash)).toLowerCase());
168
+ // 上游列表为空(刷新与缓存双失败的极端情况)时无法判定 bare 生死 → 保留(默认放行)
169
+ if (live.size === 0) return true;
170
+ const low = String(id).toLowerCase();
171
+ return live.has(low) || allowed.has(low);
172
+ });
173
173
  }
@@ -1,20 +1,20 @@
1
- import { createUpstreamClient } from "../upstream.js";
2
- import { joinModelId } from "./model-id.js";
3
-
4
- // opencode Provider:复用现有 createUpstreamClient 的全部能力(头/重试/匿名免费通道/keep-alive 预热),
5
- // 模型 id 保持裸 id(向后兼容)。
6
- export function createOpenCodeProvider({ upstream, modelsService, baseUrl, authToken, hooks } = {}) {
7
- const client = upstream ?? createUpstreamClient({ baseUrl, authToken, hooks });
8
- return {
9
- id: "opencode",
10
- upstream: client,
11
- chat: (body, opts) => client.chat(body, opts),
12
- preheat: (args) => client.preheat(args),
13
- close: () => client.close(),
14
- async listModels() {
15
- const data = await modelsService?.get?.();
16
- const list = data?.data ?? [];
17
- return list.map((m) => ({ ...m, id: joinModelId("opencode", m.id) }));
18
- },
19
- };
1
+ import { createUpstreamClient } from "../upstream.js";
2
+ import { joinModelId } from "./model-id.js";
3
+
4
+ // opencode Provider:复用现有 createUpstreamClient 的全部能力(头/重试/匿名免费通道/keep-alive 预热),
5
+ // 模型 id 保持裸 id(向后兼容)。
6
+ export function createOpenCodeProvider({ upstream, modelsService, baseUrl, authToken, hooks } = {}) {
7
+ const client = upstream ?? createUpstreamClient({ baseUrl, authToken, hooks });
8
+ return {
9
+ id: "opencode",
10
+ upstream: client,
11
+ chat: (body, opts) => client.chat(body, opts),
12
+ preheat: (args) => client.preheat(args),
13
+ close: () => client.close(),
14
+ async listModels() {
15
+ const data = await modelsService?.get?.();
16
+ const list = data?.data ?? [];
17
+ return list.map((m) => ({ ...m, id: joinModelId("opencode", m.id) }));
18
+ },
19
+ };
20
20
  }
@@ -1,31 +1,31 @@
1
- import { createGenericProvider } from "./generic.js";
2
-
3
- const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
4
-
5
- export function createOpenRouterProvider({
6
- apiKeys,
7
- apiKey,
8
- baseUrl = process.env.MSLXDFF_OPENROUTER_BASE_URL || DEFAULT_BASE_URL,
9
- fetchImpl,
10
- headers,
11
- noAgent = false,
12
- ...rest
13
- } = {}) {
14
- return createGenericProvider({
15
- id: "openrouter",
16
- baseUrl,
17
- apiKeys,
18
- apiKey,
19
- modelsPath: "/models",
20
- chatPath: "/chat/completions",
21
- headers: {
22
- "HTTP-Referer": process.env.MSLXDFF_OPENROUTER_REFERER || "https://github.com/mslxdff",
23
- "X-Title": process.env.MSLXDFF_OPENROUTER_TITLE || "mslxdff",
24
- ...(headers || {}),
25
- },
26
- mapModel: (raw) => raw.filter((m) => Number(m.pricing?.prompt || 0) === 0 && Number(m.pricing?.completion || 0) === 0),
27
- fetchImpl,
28
- noAgent,
29
- ...rest,
30
- });
31
- }
1
+ import { createGenericProvider } from "./generic.js";
2
+
3
+ const DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
4
+
5
+ export function createOpenRouterProvider({
6
+ apiKeys,
7
+ apiKey,
8
+ baseUrl = process.env.MSLXDFF_OPENROUTER_BASE_URL || DEFAULT_BASE_URL,
9
+ fetchImpl,
10
+ headers,
11
+ noAgent = false,
12
+ ...rest
13
+ } = {}) {
14
+ return createGenericProvider({
15
+ id: "openrouter",
16
+ baseUrl,
17
+ apiKeys,
18
+ apiKey,
19
+ modelsPath: "/models",
20
+ chatPath: "/chat/completions",
21
+ headers: {
22
+ "HTTP-Referer": process.env.MSLXDFF_OPENROUTER_REFERER || "https://github.com/mslxdff",
23
+ "X-Title": process.env.MSLXDFF_OPENROUTER_TITLE || "mslxdff",
24
+ ...(headers || {}),
25
+ },
26
+ mapModel: (raw) => raw.filter((m) => Number(m.pricing?.prompt || 0) === 0 && Number(m.pricing?.completion || 0) === 0),
27
+ fetchImpl,
28
+ noAgent,
29
+ ...rest,
30
+ });
31
+ }