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,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
+ }