mslxdff 0.1.231 → 0.1.233
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.
- package/bin/mslxdff.js +3 -3
- package/package.json +1 -1
- package/src/auto.js +264 -254
- package/src/bench/cline-bench.js +42 -42
- package/src/bench/probe.js +70 -70
- package/src/bench/report.js +162 -162
- package/src/bench/runner.js +77 -77
- package/src/bench/via-probe.js +124 -124
- package/src/bench/via-routes.js +87 -87
- package/src/bench/workbuddy-bench.js +54 -54
- package/src/chat/engine.js +160 -160
- package/src/chat/gateway.js +163 -163
- package/src/chat/orchestrator.js +234 -234
- package/src/chat/prompt.js +70 -70
- package/src/chat/repl.js +88 -88
- package/src/chat/terminal.js +135 -135
- package/src/chat/tools.js +306 -306
- package/src/chat-pipeline/index.js +155 -145
- package/src/chat-pipeline/policy.js +84 -84
- package/src/cli/commands/group.js +266 -266
- package/src/cli/commands/model/picks.js +50 -50
- package/src/cli/commands/provider/bench-via.js +247 -247
- package/src/cli/commands/provider/bench.js +141 -141
- package/src/cli/commands/provider/index.js +139 -139
- package/src/cli/commands/provider/models.js +149 -149
- package/src/cli/commands/provider/qwenwork-login.js +119 -119
- package/src/cli/commands/provider/raccoon-quota.js +156 -156
- package/src/cli/commands/sync.js +310 -310
- package/src/cli/status.js +279 -279
- package/src/daemon.js +246 -246
- package/src/model-capabilities/enrich.js +86 -86
- package/src/model-capabilities/index.js +183 -183
- package/src/model-capabilities/parse.js +70 -70
- package/src/model-trace.js +10 -2
- package/src/models.js +292 -225
- package/src/peers.js +345 -345
- package/src/providers/agentrouter.js +238 -238
- package/src/providers/base.js +164 -164
- package/src/providers/cline/auth.js +228 -228
- package/src/providers/cline/chat.js +307 -307
- package/src/providers/cline.js +2 -2
- package/src/providers/codearts/chat.js +207 -207
- package/src/providers/dispatcher.js +147 -147
- package/src/providers/globalqwenwork/cosy.js +123 -123
- package/src/providers/globalqwenwork/crypto.js +218 -218
- package/src/providers/globalqwenwork/http.js +20 -20
- package/src/providers/globalqwenwork/rsa.js +54 -54
- package/src/providers/globalqwenwork/sse.js +268 -268
- package/src/providers/globalqwenwork/stream.js +130 -130
- package/src/providers/globalqwenwork/upstream.js +122 -122
- package/src/providers/keyring.js +60 -60
- package/src/providers/qoder/account-store.js +18 -0
- package/src/providers/qoder/chat.js +292 -203
- package/src/providers/qoder/checkin.js +25 -12
- package/src/providers/qoder/index.js +311 -249
- package/src/providers/qoder/models.js +64 -2
- package/src/providers/qoder/oauth.js +42 -1
- package/src/providers/qoder/payload.js +116 -80
- package/src/providers/qoder/request.js +7 -2
- package/src/providers/qoder/session.js +26 -10
- package/src/providers/qoder/sse.js +233 -145
- package/src/providers/qoder/sticky.js +5 -2
- package/src/providers/qoder/wire-messages.js +242 -0
- package/src/providers/qwenwork/account-store.js +133 -133
- package/src/providers/qwenwork/constants.js +67 -67
- package/src/providers/qwenwork/cosy.js +120 -120
- package/src/providers/qwenwork/crypto.js +218 -218
- package/src/providers/qwenwork/http.js +20 -20
- package/src/providers/qwenwork/index.js +337 -337
- package/src/providers/qwenwork/payload.js +144 -144
- package/src/providers/qwenwork/rsa.js +54 -54
- package/src/providers/qwenwork/sse.js +268 -268
- package/src/providers/qwenwork/stream.js +130 -130
- package/src/providers/qwenwork/upstream.js +120 -120
- package/src/providers/qwenwork.js +1 -1
- package/src/providers/raccoon/chat.js +1 -1
- package/src/providers/raccoon/const.js +219 -202
- package/src/providers/raccoon/credits.js +285 -285
- package/src/providers/raccoon/index.js +15 -2
- package/src/providers/raccoon/models.js +68 -17
- package/src/providers/registry.js +89 -89
- package/src/providers/workbuddy/auth.js +16 -0
- package/src/providers/workbuddy/chat.js +298 -264
- package/src/providers/workbuddy/client-headers.js +148 -0
- package/src/providers/workbuddy/desktop-info.js +17 -1
- package/src/providers/workbuddy/models.js +8 -20
- package/src/providers/workbuddy/reshape.js +152 -152
- package/src/providers/workbuddy.js +2 -2
- package/src/reasoning.js +32 -32
- package/src/routes/chat/broadband-handler.js +80 -80
- package/src/routes/chat/gateway.js +46 -46
- package/src/routes/chat/relay-pipeline.js +282 -282
- package/src/routes/chat/via-route-handler.js +156 -156
- package/src/routes/hedge.js +260 -260
- package/src/routes/models-route.js +167 -167
- package/src/routes/peers.js +373 -373
- package/src/routes/stream.js +364 -347
- package/src/runtime/bootstrap.js +75 -75
- package/src/runtime/keep-warm.js +135 -135
- package/src/runtime/provider-gate.js +75 -75
- package/src/runtime/providers-setup.js +187 -187
- package/src/runtime/raccoon-checkin.js +175 -175
- package/src/server.js +85 -85
- package/src/state/schemas/allowlist.js +92 -92
- package/src/sync-opencode.js +280 -280
- package/src/transport/index.js +244 -244
- package/src/transport/pool.js +56 -56
- package/src/transport/retry.js +24 -24
- package/src/transport/sse.js +93 -93
- package/src/upstream-probe/display.js +52 -52
- package/src/upstream-probe/probe.js +49 -49
- package/src/upstream-probe/rotate.js +110 -110
- package/src/upstream-probe/start.js +45 -45
- package/src/upstream.js +355 -355
- package/docs/cli_help_mini.md +0 -148
- package/src/providers/AGENTS.md +0 -49
- package/src/routes/AGENTS.md +0 -37
|
@@ -41,38 +41,54 @@ export function raccoonFallbackModels() {
|
|
|
41
41
|
return RACCOON_FALLBACK_MODELS.map((m) => ({ ...m }));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* 单条目录条目:字段名容错(缺字段即省略,不猜、不填假值)。
|
|
46
|
+
*
|
|
47
|
+
* 上游真实字段(2026-10-22 `GET /api/web/llm/v2/model_catalog` 实测):
|
|
48
|
+
* `name`=模型 id(请求体 `model` 用的就是它)、`description`=展示名(如 `DeepSeek-V4.1-Flash`)、
|
|
49
|
+
* `visible: false`=上游对自己隐藏的内部模型、`params.context_window` / `params.max_tokens`、
|
|
50
|
+
* `billing_multiplier` / `billing_effective_multiplier`。
|
|
51
|
+
* ⚠ **id 绝不能取 `description`**:旧`pickString(["modelName","name","displayName","description"])`
|
|
52
|
+
* 把 id 与展示名混在一串里,一旦上游条目的 `name` 缺失就会拿展示名当 id 去发请求(必 404/400)。
|
|
53
|
+
*/
|
|
45
54
|
function parseRaccoonModelEntry(raw) {
|
|
46
55
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
56
|
+
// 上游用 visible:false 表示「我这边不展示」,这类内部模型不进目录
|
|
57
|
+
if (raw.visible === false) return null;
|
|
47
58
|
const id = pickString(raw, ["id", "modelId", "model_id", "modelName", "name"]);
|
|
48
59
|
if (!id) return null;
|
|
49
60
|
const model = { id };
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
// 展示名单独取,且排除与 id 同值的情况(否则列表会出现「xxx · xxx」)
|
|
62
|
+
const name = pickString(raw, ["description", "displayName", "modelName", "name"]);
|
|
63
|
+
if (name && name !== id) model.name = name;
|
|
64
|
+
const params = raw.params && typeof raw.params === "object" ? raw.params : {};
|
|
65
|
+
const ctx = pickNumber(raw, ["contextWindow", "context_window", "contextLength", "context_length"]) ?? pickNumber(params, ["context_window", "contextWindow"]);
|
|
53
66
|
if (ctx !== undefined && ctx > 0) model.contextWindow = ctx;
|
|
54
|
-
const maxTokens = pickNumber(raw, ["maxTokens", "max_tokens", "maxCompletionTokens"]);
|
|
67
|
+
const maxTokens = pickNumber(raw, ["maxTokens", "max_tokens", "maxCompletionTokens"]) ?? pickNumber(params, ["max_tokens", "maxTokens"]);
|
|
55
68
|
if (maxTokens !== undefined && maxTokens > 0) model.maxTokens = maxTokens;
|
|
56
69
|
const img = pickBool(raw, ["supportsImage", "supports_image", "supportsVision"]);
|
|
57
70
|
if (img !== undefined) model.supportsImage = img;
|
|
58
|
-
const eff = pickNumber(raw, ["effectiveMultiplier", "effective_multiplier", "multiplier", "costMultiplier"]);
|
|
71
|
+
const eff = pickNumber(raw, ["effectiveMultiplier", "effective_multiplier", "billing_effective_multiplier", "multiplier", "costMultiplier"]);
|
|
59
72
|
if (eff !== undefined && eff >= 0) model.multiplier = eff;
|
|
60
|
-
const base = pickNumber(raw, ["baseMultiplier", "base_multiplier"]);
|
|
73
|
+
const base = pickNumber(raw, ["baseMultiplier", "base_multiplier", "billing_multiplier"]);
|
|
61
74
|
if (base !== undefined && base > 0) model.baseMultiplier = base;
|
|
62
75
|
return model;
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* 目录信封 → 条目数组。
|
|
80
|
+
*
|
|
81
|
+
* ⚠ **主形状是 `data.categories[]`,只有 `type: "chat"` 那组才是可路由模型**(2026-10-22 上游实测)。
|
|
82
|
+
* 旧实现只认 `data` 为数组 / `data.models` / `data.list` / `data.items` 四种,而真实报文是
|
|
83
|
+
* `{data:{categories:[...]}}` —— 结果每次都解析出 0 条、静默回落静态兜底表,症状是
|
|
84
|
+
* 「上游新上架的模型(如 `sn-deepseek-v4-1-flash-1017`)在网关里永远看不见」。
|
|
85
|
+
* 另外两条判据同样来自实测:`name` 才是模型 id(`description` 是展示名)、`visible: false`
|
|
86
|
+
* 是上游对自己隐藏的内部模型(`raccoon-*`),不该进目录。
|
|
87
|
+
*
|
|
88
|
+
* 四种旧形状继续兼容(上游改版/其他信封时兜底),按顺序取第一个非空的。
|
|
89
|
+
*/
|
|
66
90
|
export function parseRaccoonCatalog(data) {
|
|
67
|
-
const list =
|
|
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
|
-
: [];
|
|
91
|
+
const list = pickCatalogList(data);
|
|
76
92
|
const out = [];
|
|
77
93
|
const seen = new Set();
|
|
78
94
|
for (const raw of list) {
|
|
@@ -84,6 +100,19 @@ export function parseRaccoonCatalog(data) {
|
|
|
84
100
|
return out;
|
|
85
101
|
}
|
|
86
102
|
|
|
103
|
+
function pickCatalogList(data) {
|
|
104
|
+
const arr = (v) => (Array.isArray(v) ? v : null);
|
|
105
|
+
// 真实形状:{ data: { categories: [{ type: "chat", models: [...] }] } }
|
|
106
|
+
const root = data && typeof data === "object" && !Array.isArray(data) ? data : null;
|
|
107
|
+
const categories = arr(root?.categories) ?? arr(root?.data?.categories);
|
|
108
|
+
if (categories) {
|
|
109
|
+
const chat = categories.find((c) => c && typeof c === "object" && c.type === "chat");
|
|
110
|
+
const chatModels = arr(chat?.models);
|
|
111
|
+
if (chatModels) return chatModels;
|
|
112
|
+
}
|
|
113
|
+
return arr(data) ?? arr(root?.models) ?? arr(root?.list) ?? arr(root?.items) ?? [];
|
|
114
|
+
}
|
|
115
|
+
|
|
87
116
|
/** 倍率文案:0 → 免费;促销 → `x基→x实`;其余 → `x实`(含 x1)。 */
|
|
88
117
|
export function raccoonMultiplierLabel(model) {
|
|
89
118
|
const eff = typeof model?.multiplier === "number" ? model.multiplier : undefined;
|
|
@@ -143,6 +172,28 @@ export function seedRaccoonAllowlist({ ids, file } = {}) {
|
|
|
143
172
|
return { allowed: [...cur, ...added], added };
|
|
144
173
|
}
|
|
145
174
|
|
|
175
|
+
/**
|
|
176
|
+
* 把**上游目录**的模型 id 只增不减并入 allowlist。
|
|
177
|
+
*
|
|
178
|
+
* 为什么需要:白名单此前只在登录时按**静态兜底表**播种一次,上游后来上架的模型
|
|
179
|
+
* (如 2026-10 的 `sn-deepseek-v4-1-flash-1017`)就永远进不来 —— dispatcher 拿白名单
|
|
180
|
+
* 过滤 `listModels()`,模型看得见列表却选不到、调用直接被挡。上游目录才是这家唯一真相
|
|
181
|
+
* (积分制,模型 id 全由上游定义),故每次成功拉到目录就同步一次。
|
|
182
|
+
*
|
|
183
|
+
* **为什么只增不减**:目录是**按账号**下发的(套餐不同可见模型数不同),某次拉到一份
|
|
184
|
+
* 较短的目录不代表模型下架;双向收敛会在换号/降级套餐时把白名单越缩越小、把能用的模型删掉。
|
|
185
|
+
* 真要下架的模型留着只会 404,代价远小于误删。对齐 cline 的 `planMerge` 保守口径(ADR-0067)。
|
|
186
|
+
*/
|
|
187
|
+
export function mergeRaccoonCatalogIntoAllowlist(models, { file } = {}) {
|
|
188
|
+
const ids = (Array.isArray(models) ? models : []).map((m) => String(m?.id || "").trim()).filter(Boolean);
|
|
189
|
+
if (!ids.length) return { added: [] };
|
|
190
|
+
const opts = file ? { file } : {};
|
|
191
|
+
const cur = loadProviderAllowedModels("raccoon", opts);
|
|
192
|
+
const added = ids.filter((m) => !cur.includes(m));
|
|
193
|
+
if (added.length) saveProviderAllowedModels("raccoon", [...cur, ...added], opts);
|
|
194
|
+
return { added };
|
|
195
|
+
}
|
|
196
|
+
|
|
146
197
|
/** 对外目录条目(`/v1/models` 与 CLI models 共用形状)。 */
|
|
147
198
|
export function buildRaccoonModelList({ id = "raccoon", models } = {}) {
|
|
148
199
|
const list = models || raccoonFallbackModels();
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 可扩展供应商注册表:新增特殊供应商时,仅在此文件注册 + 新增对应 provider 文件即可
|
|
3
|
-
* 每个条目:{ id, match(id, baseUrl) => bool, load() => Promise<factory> }
|
|
4
|
-
* 匹配优先级:按数组顺序,首个命中即用;未命中走通用 generic
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export const customProviders = [
|
|
8
|
-
{
|
|
9
|
-
id: "workbuddy",
|
|
10
|
-
match: (id, baseUrl) => id === "workbuddy" || String(baseUrl).includes("copilot.tencent"),
|
|
11
|
-
load: () => import("./workbuddy.js").then((m) => m.createWorkbuddyProvider),
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
id: "traework",
|
|
15
|
-
match: (id, baseUrl) => id === "traework" || String(baseUrl).includes("trae"),
|
|
16
|
-
load: () => import("./traework.js").then((m) => m.createTraeworkProvider),
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: "cline",
|
|
20
|
-
match: (id, baseUrl) => id === "cline" || String(baseUrl).includes("cline.bot"),
|
|
21
|
-
load: () => import("./cline.js").then((m) => m.createClineProvider),
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: "codearts",
|
|
25
|
-
match: (id, baseUrl) => id === "codearts" || String(baseUrl).includes("myhuaweicloud.com"),
|
|
26
|
-
load: () => import("./codearts.js").then((m) => m.createCodeartsProvider),
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: "qoder",
|
|
30
|
-
match: (id, baseUrl) => id === "qoder" || String(baseUrl).includes("qoder"),
|
|
31
|
-
load: () => import("./qoder/index.js").then((m) => m.createQoderProvider),
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
// ⚠ 必须排在 qwenwork 之前:qwenwork 的 match 用 `includes("qwenwork")`,
|
|
35
|
-
// 而 "globalqwenwork" 与 "gateway.qwenwork.ai"/"globalqwenwork://native" 都含该子串,
|
|
36
|
-
// 若后置会被 cn 条目抢先命中(first-match-wins),把国际站 token 打到 cn 网关。
|
|
37
|
-
id: "globalqwenwork",
|
|
38
|
-
match: (id, baseUrl) => id === "globalqwenwork" || String(baseUrl).includes("qwenwork.ai"),
|
|
39
|
-
load: () => import("./globalqwenwork/index.js").then((m) => m.createGlobalQwenworkProvider),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: "qwenwork",
|
|
43
|
-
match: (id, baseUrl) => id === "qwenwork" || String(baseUrl).includes("qwenwork"),
|
|
44
|
-
load: () => import("./qwenwork/index.js").then((m) => m.createQwenworkProvider),
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: "zcode",
|
|
48
|
-
match: (id, baseUrl) => id === "zcode" || String(baseUrl).includes("zcode.z.ai"),
|
|
49
|
-
load: () => import("./zcode/index.js").then((m) => m.createZcodeProvider),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: "railway",
|
|
53
|
-
// opencode zen free 层的另一个 IP 出口(Railway 反代),只暴露 free 模型。
|
|
54
|
-
// 匹配必须排在 generic 之前,且不能与其它条目撞(railway.app 域名足够独特)。
|
|
55
|
-
match: (id, baseUrl) => id === "railway" || String(baseUrl).includes("railway.app"),
|
|
56
|
-
load: () => import("./railway.js").then((m) => m.createRailwayProvider),
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
id: "raccoon",
|
|
60
|
-
match: (id, baseUrl) => id === "raccoon" || String(baseUrl).includes("xiaohuanxiong.com"),
|
|
61
|
-
load: () => import("./raccoon/index.js").then((m) => m.createRaccoonProvider),
|
|
62
|
-
},
|
|
63
|
-
// agentrouter 定制工厂已于 2026-10-21 摘除:现网成功率仅 ~48%(502/400/422/500 高发),
|
|
64
|
-
// 从注册表 + 预热名单移除后网关不再解析 agentrouter/* 模型。工厂文件 src/providers/agentrouter.js
|
|
65
|
-
// 与其测试保留,日后要重新启用只需在此加回一条 { id:"agentrouter", ... } 并同步 dispatcher 预热名单。
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
// 供 bench/probe 等需要定制化解析模型列表的场景
|
|
69
|
-
export const customNormalizers = [
|
|
70
|
-
{
|
|
71
|
-
id: "cline",
|
|
72
|
-
match: (baseUrl) => String(baseUrl).includes("cline.bot"),
|
|
73
|
-
normalize: (json) => Array.isArray(json?.free) ? json.free : null,
|
|
74
|
-
},
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
export async function getCustomProviderFactory(id, baseUrl) {
|
|
78
|
-
for (const entry of customProviders) {
|
|
79
|
-
try { if (entry.match(id, baseUrl)) return await entry.load(); } catch {}
|
|
80
|
-
}
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function getCustomNormalizer(baseUrl) {
|
|
85
|
-
for (const entry of customNormalizers) {
|
|
86
|
-
try { if (entry.match(baseUrl)) return entry.normalize; } catch {}
|
|
87
|
-
}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 可扩展供应商注册表:新增特殊供应商时,仅在此文件注册 + 新增对应 provider 文件即可
|
|
3
|
+
* 每个条目:{ id, match(id, baseUrl) => bool, load() => Promise<factory> }
|
|
4
|
+
* 匹配优先级:按数组顺序,首个命中即用;未命中走通用 generic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const customProviders = [
|
|
8
|
+
{
|
|
9
|
+
id: "workbuddy",
|
|
10
|
+
match: (id, baseUrl) => id === "workbuddy" || String(baseUrl).includes("copilot.tencent"),
|
|
11
|
+
load: () => import("./workbuddy.js").then((m) => m.createWorkbuddyProvider),
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "traework",
|
|
15
|
+
match: (id, baseUrl) => id === "traework" || String(baseUrl).includes("trae"),
|
|
16
|
+
load: () => import("./traework.js").then((m) => m.createTraeworkProvider),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "cline",
|
|
20
|
+
match: (id, baseUrl) => id === "cline" || String(baseUrl).includes("cline.bot"),
|
|
21
|
+
load: () => import("./cline.js").then((m) => m.createClineProvider),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "codearts",
|
|
25
|
+
match: (id, baseUrl) => id === "codearts" || String(baseUrl).includes("myhuaweicloud.com"),
|
|
26
|
+
load: () => import("./codearts.js").then((m) => m.createCodeartsProvider),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "qoder",
|
|
30
|
+
match: (id, baseUrl) => id === "qoder" || String(baseUrl).includes("qoder"),
|
|
31
|
+
load: () => import("./qoder/index.js").then((m) => m.createQoderProvider),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
// ⚠ 必须排在 qwenwork 之前:qwenwork 的 match 用 `includes("qwenwork")`,
|
|
35
|
+
// 而 "globalqwenwork" 与 "gateway.qwenwork.ai"/"globalqwenwork://native" 都含该子串,
|
|
36
|
+
// 若后置会被 cn 条目抢先命中(first-match-wins),把国际站 token 打到 cn 网关。
|
|
37
|
+
id: "globalqwenwork",
|
|
38
|
+
match: (id, baseUrl) => id === "globalqwenwork" || String(baseUrl).includes("qwenwork.ai"),
|
|
39
|
+
load: () => import("./globalqwenwork/index.js").then((m) => m.createGlobalQwenworkProvider),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "qwenwork",
|
|
43
|
+
match: (id, baseUrl) => id === "qwenwork" || String(baseUrl).includes("qwenwork"),
|
|
44
|
+
load: () => import("./qwenwork/index.js").then((m) => m.createQwenworkProvider),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "zcode",
|
|
48
|
+
match: (id, baseUrl) => id === "zcode" || String(baseUrl).includes("zcode.z.ai"),
|
|
49
|
+
load: () => import("./zcode/index.js").then((m) => m.createZcodeProvider),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "railway",
|
|
53
|
+
// opencode zen free 层的另一个 IP 出口(Railway 反代),只暴露 free 模型。
|
|
54
|
+
// 匹配必须排在 generic 之前,且不能与其它条目撞(railway.app 域名足够独特)。
|
|
55
|
+
match: (id, baseUrl) => id === "railway" || String(baseUrl).includes("railway.app"),
|
|
56
|
+
load: () => import("./railway.js").then((m) => m.createRailwayProvider),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "raccoon",
|
|
60
|
+
match: (id, baseUrl) => id === "raccoon" || String(baseUrl).includes("xiaohuanxiong.com"),
|
|
61
|
+
load: () => import("./raccoon/index.js").then((m) => m.createRaccoonProvider),
|
|
62
|
+
},
|
|
63
|
+
// agentrouter 定制工厂已于 2026-10-21 摘除:现网成功率仅 ~48%(502/400/422/500 高发),
|
|
64
|
+
// 从注册表 + 预热名单移除后网关不再解析 agentrouter/* 模型。工厂文件 src/providers/agentrouter.js
|
|
65
|
+
// 与其测试保留,日后要重新启用只需在此加回一条 { id:"agentrouter", ... } 并同步 dispatcher 预热名单。
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// 供 bench/probe 等需要定制化解析模型列表的场景
|
|
69
|
+
export const customNormalizers = [
|
|
70
|
+
{
|
|
71
|
+
id: "cline",
|
|
72
|
+
match: (baseUrl) => String(baseUrl).includes("cline.bot"),
|
|
73
|
+
normalize: (json) => Array.isArray(json?.free) ? json.free : null,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
export async function getCustomProviderFactory(id, baseUrl) {
|
|
78
|
+
for (const entry of customProviders) {
|
|
79
|
+
try { if (entry.match(id, baseUrl)) return await entry.load(); } catch {}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function getCustomNormalizer(baseUrl) {
|
|
85
|
+
for (const entry of customNormalizers) {
|
|
86
|
+
try { if (entry.match(baseUrl)) return entry.normalize; } catch {}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
@@ -53,6 +53,18 @@ export function parseRateLimitResetMs(bodyText, clock = Date.now) {
|
|
|
53
53
|
return ms > 0 ? ms : 0;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
// 账号级风控封禁:腾讯 workbuddy `403 {"code":11140,"msg":"request illegal",displayMsg:"…restricted due to unusual activity/账号存在异常行为…"}`。
|
|
57
|
+
// 与 auth 失败、额度耗尽、限流都不同:**刷新 token 无效**(2026-10-21 实测:refresh 成功换发新 token 后照样 11140,
|
|
58
|
+
// 且换新 UA/完整客户端头集合同样 11140——判的是账号本体,不是凭据或请求指纹)。
|
|
59
|
+
// 命中即应长冷却该号并如实把 403 透回,绝不进 refresh→重打 的连锁(旧链每个用户请求白烧 号数×2 次上游调用 + 号数 次 refresh)。
|
|
60
|
+
export function isAccountRestricted(status, bodyText) {
|
|
61
|
+
if (status !== 403) return false;
|
|
62
|
+
const t = String(bodyText || "");
|
|
63
|
+
if (/"code"\s*:\s*11140\b/.test(t)) return true;
|
|
64
|
+
const lower = t.toLowerCase();
|
|
65
|
+
return lower.includes("request illegal") || lower.includes("restricted due to unusual activity") || t.includes("账号存在异常行为");
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
export function isAuthError(status, bodyText) {
|
|
57
69
|
if (status === 401 || status === 403) return true;
|
|
58
70
|
const t = String(bodyText || "").toLowerCase();
|
|
@@ -86,10 +98,14 @@ export function createAuthService({
|
|
|
86
98
|
}
|
|
87
99
|
const p = (async () => {
|
|
88
100
|
const url = joinUrl(resolvedBase, "/v2/plugin/auth/token/refresh");
|
|
101
|
+
// 回退到 2026-10-21 17:09 实测**可用**的旧头形(refresh 成功换发过新 token):refresh 无活体抓包,
|
|
102
|
+
// 不赌未验证口径;11140 只打 chat、refresh 不在风控画像主战场。X-Refresh-Token 是 refresh 特有凭据头。
|
|
103
|
+
// X-Auth-Refresh-Source: "plugin" 是 CLI bundle 三条 refreshSession 逐字共有的真值(@7064132/@7126898/@7144850),补上无副作用。
|
|
89
104
|
const headers = {
|
|
90
105
|
"Content-Type": "application/json",
|
|
91
106
|
Authorization: `Bearer ${key}`,
|
|
92
107
|
"X-Refresh-Token": rt,
|
|
108
|
+
"X-Auth-Refresh-Source": "plugin",
|
|
93
109
|
"X-User-Id": uid,
|
|
94
110
|
"X-Domain": auth.domain || "www.codebuddy.cn",
|
|
95
111
|
"User-Agent": "CLI/2.115.0 WorkBuddy/2.115.0",
|