dsh-router-core 0.3.4 → 0.3.6
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/cordis.patch.yml +7 -1
- package/lib/client.js +1 -1
- package/lib/index.js +5 -2
- package/lib/suppliers/nvidia.js +339 -0
- package/lib/suppliers/opencode.js +137 -0
- package/lib/suppliers/openrouter.js +222 -0
- package/lib/types/index.d.ts +5 -2
- package/package.json +15 -12
- package/src/index.ts +5 -2
package/cordis.patch.yml
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
# 路由系统 (Routing System) sidebar entry beside dsh-mnemon's 记忆系统; its
|
|
5
5
|
# host half serves /router/api/* (OpenAI-compatible /v1/* routing).
|
|
6
6
|
# No dependency on other plugins.
|
|
7
|
+
#
|
|
8
|
+
# `name` 是**模块说明符**(不是显示名),必须与 package.json 的 name 逐字一致:
|
|
9
|
+
# 宿主 loader 拿它去 `import(name)`,客户端 client-modules 拿它去
|
|
10
|
+
# `require.resolve(name + '/package.json')` 定位 client bundle。包名已改
|
|
11
|
+
# dsh-router-core,这里不同步就从 npm 装完两边都加载不到(`id` 只是配置树
|
|
12
|
+
# 里的句柄,可以随便取)。
|
|
7
13
|
- insert:
|
|
8
14
|
- id: dsh-router
|
|
9
|
-
name: 'dsh-router'
|
|
15
|
+
name: 'dsh-router-core'
|
package/lib/client.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -3184,8 +3184,11 @@ var CredentialStore = class {
|
|
|
3184
3184
|
};
|
|
3185
3185
|
//#endregion
|
|
3186
3186
|
//#region src/index.ts
|
|
3187
|
-
/**
|
|
3188
|
-
|
|
3187
|
+
/**
|
|
3188
|
+
* Plugin identity for cordis.yml rows — 必须与 package.json 的 name 一致。
|
|
3189
|
+
* loader 拿配置行里的 `name` 去 `import(name)`,写错就是从 npm 装完加载不到。
|
|
3190
|
+
*/
|
|
3191
|
+
const name = "dsh-router-core";
|
|
3189
3192
|
/** Services required before mounting: the webserver (routes) + llm (设置-模型). */
|
|
3190
3193
|
const inject = ["webServer", "llm"];
|
|
3191
3194
|
function writeJson(res, status, body) {
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
//#region src/suppliers/nvidia/plugin.ts
|
|
4
|
+
/**
|
|
5
|
+
* nvidia 供应商插件 —— 参考 9Router(open-sse) 的 nvidia 实现。
|
|
6
|
+
*
|
|
7
|
+
* 上游:https://integrate.api.nvidia.com/v1(OpenAI 兼容)
|
|
8
|
+
* - chat: POST /v1/chat/completions(SSE 流式 / 非流式)
|
|
9
|
+
* - models: GET /v1/models(公开,无需鉴权,返回全量模型)
|
|
10
|
+
*
|
|
11
|
+
* API key 账号:走「添加链接 + 连接池」(同 openrouter),弹窗填名字+key,
|
|
12
|
+
* 一个供应商可有多个命名 key,按池顺序/策略尝试。凭证存通用 CredentialStore
|
|
13
|
+
* (SQLite:auths/credentials.sqlite,{ name, apiKey })。key 有效性用 chat 探测
|
|
14
|
+
* (无 key → 400,无效 key → 401,有效 → 200)。
|
|
15
|
+
*
|
|
16
|
+
* 模型不内置、不缓存:listModels 每次从上游 /v1/models 拉取(全量,不过滤),
|
|
17
|
+
* 缓存由 dsh-router 核心统一管。
|
|
18
|
+
*/
|
|
19
|
+
const id = "nvidia";
|
|
20
|
+
const name = "NVIDIA NIM";
|
|
21
|
+
const priority = 20;
|
|
22
|
+
/**
|
|
23
|
+
* 面板图标(NVIDIA 官方 logo,128×128 PNG,base64 内联)。
|
|
24
|
+
*
|
|
25
|
+
* **必须内联,不能放网络 URL**:之前是 `http://localhost:20128/...`(9router
|
|
26
|
+
* 的端口),9router 没跑时就是一张坏图 —— 面板图标不该依赖另一个服务活着,
|
|
27
|
+
* 更不该依赖特定端口。跟 opencode / openrouter / traework 的做法保持一致。
|
|
28
|
+
*/
|
|
29
|
+
const icon = "data:image/png;base64,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";
|
|
30
|
+
const BASE = "https://integrate.api.nvidia.com/v1";
|
|
31
|
+
const MODELS_URL = `${BASE}/models`;
|
|
32
|
+
const CHAT_URL = `${BASE}/chat/completions`;
|
|
33
|
+
/** 默认前缀(用户可在面板改;loader 包装会优先用 store 里的值)。 */
|
|
34
|
+
/** 非聊天用途的模型(护栏/翻译/解析/检测/图像生成等),聊天路由用不上,拉列表时过滤掉。 */
|
|
35
|
+
const NON_CHAT_MODEL = /(guard|safety|moderation|translate|transcription|parse|detector|embed|rerank|diffusion|tts|asr|calibration)/i;
|
|
36
|
+
/** 剥 alias 前缀(nv/xxx → xxx)。 */
|
|
37
|
+
/** 剥本供应商 alias 前缀(只剥自己的,模型 id 自带的斜杠保留)。 */
|
|
38
|
+
function stripAlias(model, alias) {
|
|
39
|
+
return alias !== "" && model.startsWith(`${alias}/`) ? model.slice(alias.length + 1) : model;
|
|
40
|
+
}
|
|
41
|
+
function factory(env) {
|
|
42
|
+
let modelsCache;
|
|
43
|
+
/** 上次 chatOnce 失败原因(供核心测试模型汇总诊断)。 */
|
|
44
|
+
function listKeys() {
|
|
45
|
+
return env.credentials.list(id);
|
|
46
|
+
}
|
|
47
|
+
function getKey(uid) {
|
|
48
|
+
return env.credentials.get(id, uid);
|
|
49
|
+
}
|
|
50
|
+
/** 账号顺序:池顺序优先,未配置按凭证原始顺序。 */
|
|
51
|
+
function orderedKeys() {
|
|
52
|
+
const byUid = new Map(listKeys().map((uid) => [uid, uid]));
|
|
53
|
+
const order = env.store.get(id).poolOrder;
|
|
54
|
+
return [...order.filter((u) => byUid.has(u)), ...listKeys().filter((u) => !order.includes(u))].map((uid) => ({
|
|
55
|
+
uid,
|
|
56
|
+
acct: getKey(uid)
|
|
57
|
+
})).filter((x) => x.acct !== void 0);
|
|
58
|
+
}
|
|
59
|
+
/** 当前前缀(与 loader 包装一致:store 覆盖默认值)。 */
|
|
60
|
+
function currentAlias() {
|
|
61
|
+
return env.store.get("nvidia").alias || "nvidia";
|
|
62
|
+
}
|
|
63
|
+
/** 已确认不可用的模型(下线/未授权)。 */
|
|
64
|
+
const deadModels = /* @__PURE__ */ new Set();
|
|
65
|
+
/** 已确认可用的模型(避免重复探测)。 */
|
|
66
|
+
const okModels = /* @__PURE__ */ new Set();
|
|
67
|
+
const PROBE_CONCURRENCY = 8;
|
|
68
|
+
function probeFile() {
|
|
69
|
+
return join(env.dataDir, "nvidia-models.json");
|
|
70
|
+
}
|
|
71
|
+
function loadProbeState() {
|
|
72
|
+
try {
|
|
73
|
+
const j = JSON.parse(readFileSync(probeFile(), "utf8"));
|
|
74
|
+
for (const m of j.dead ?? []) deadModels.add(m);
|
|
75
|
+
for (const m of j.ok ?? []) okModels.add(m);
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
function saveProbeState() {
|
|
79
|
+
try {
|
|
80
|
+
mkdirSync(env.dataDir, { recursive: true });
|
|
81
|
+
const tmp = `${probeFile()}.tmp`;
|
|
82
|
+
writeFileSync(tmp, JSON.stringify({
|
|
83
|
+
dead: [...deadModels],
|
|
84
|
+
ok: [...okModels],
|
|
85
|
+
at: Date.now()
|
|
86
|
+
}));
|
|
87
|
+
renameSync(tmp, probeFile());
|
|
88
|
+
} catch {}
|
|
89
|
+
}
|
|
90
|
+
loadProbeState();
|
|
91
|
+
/** 探测单个模型:'ok' | 'dead' | 'unauthorized' | 'unknown'。 */
|
|
92
|
+
async function probeModel(apiKey, model) {
|
|
93
|
+
try {
|
|
94
|
+
const r = await fetch(CHAT_URL, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
Authorization: `Bearer ${apiKey}`,
|
|
99
|
+
"User-Agent": "dsh-router"
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
model,
|
|
103
|
+
messages: [{
|
|
104
|
+
role: "user",
|
|
105
|
+
content: "ping"
|
|
106
|
+
}],
|
|
107
|
+
stream: false,
|
|
108
|
+
max_tokens: 1
|
|
109
|
+
}),
|
|
110
|
+
signal: AbortSignal.timeout(15e3)
|
|
111
|
+
});
|
|
112
|
+
if (r.ok) return "ok";
|
|
113
|
+
if (r.status === 401 || r.status === 403) return "unauthorized";
|
|
114
|
+
if (r.status === 404 || r.status === 410) return "dead";
|
|
115
|
+
return "unknown";
|
|
116
|
+
} catch {
|
|
117
|
+
return "unknown";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** 并发探测全部「未探测过」的模型,更新 dead/ok 并持久化。 */
|
|
121
|
+
async function probeAll(ids) {
|
|
122
|
+
const acct = orderedKeys()[0];
|
|
123
|
+
if (acct === void 0) return;
|
|
124
|
+
const todo = ids.filter((m) => !deadModels.has(m) && !okModels.has(m) && !NON_CHAT_MODEL.test(m));
|
|
125
|
+
if (todo.length === 0) return;
|
|
126
|
+
let cursor = 0;
|
|
127
|
+
let unauthorized = false;
|
|
128
|
+
let changed = false;
|
|
129
|
+
await Promise.all(Array.from({ length: Math.min(PROBE_CONCURRENCY, todo.length) }, async () => {
|
|
130
|
+
for (;;) {
|
|
131
|
+
const i = cursor++;
|
|
132
|
+
if (i >= todo.length || unauthorized) return;
|
|
133
|
+
const model = todo[i];
|
|
134
|
+
const r = await probeModel(acct.acct.apiKey, model);
|
|
135
|
+
if (r === "unauthorized") unauthorized = true;
|
|
136
|
+
else if (r === "ok") {
|
|
137
|
+
okModels.add(model);
|
|
138
|
+
changed = true;
|
|
139
|
+
} else if (r === "dead") {
|
|
140
|
+
deadModels.add(model);
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}));
|
|
145
|
+
if (changed) saveProbeState();
|
|
146
|
+
env.log(`nvidia probe: ${okModels.size} ok / ${deadModels.size} dead`);
|
|
147
|
+
}
|
|
148
|
+
async function refreshModels(force = false) {
|
|
149
|
+
try {
|
|
150
|
+
const resp = await fetch(MODELS_URL, {
|
|
151
|
+
headers: { "User-Agent": "dsh-router" },
|
|
152
|
+
signal: AbortSignal.timeout(15e3)
|
|
153
|
+
});
|
|
154
|
+
if (!resp.ok) return [];
|
|
155
|
+
const raw = (await resp.json()).data ?? [];
|
|
156
|
+
const models = [];
|
|
157
|
+
const ids = /* @__PURE__ */ new Set();
|
|
158
|
+
for (const m of raw) {
|
|
159
|
+
if (m.id === "" || ids.has(m.id)) continue;
|
|
160
|
+
ids.add(m.id);
|
|
161
|
+
const entry = { id: m.id };
|
|
162
|
+
if ((m.context_length ?? 0) > 0) entry.context_length = Math.round((m.context_length ?? 0) / 1e3);
|
|
163
|
+
models.push(entry);
|
|
164
|
+
}
|
|
165
|
+
if (models.length > 0) modelsCache = models;
|
|
166
|
+
if (force && models.length > 0) await probeAll(models.map((m) => m.id));
|
|
167
|
+
return models.filter((m) => !deadModels.has(m.id) && !NON_CHAT_MODEL.test(m.id));
|
|
168
|
+
} catch {
|
|
169
|
+
return (modelsCache ?? []).filter((m) => !deadModels.has(m.id) && !NON_CHAT_MODEL.test(m.id));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function allModels(force) {
|
|
173
|
+
return refreshModels(force);
|
|
174
|
+
}
|
|
175
|
+
/** key 有效性探测:只有 401/403 才算 key 无效。
|
|
176
|
+
* NIM 模型下线很快(410)或没授权给该账号(404),那是模型问题不是 key 问题——
|
|
177
|
+
* 固定探测某个模型迟早腐化,故 404/410 视为 key 有效。 */
|
|
178
|
+
async function probeKey(apiKey) {
|
|
179
|
+
const probe = await fetch(CHAT_URL, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: {
|
|
182
|
+
"Content-Type": "application/json",
|
|
183
|
+
Authorization: `Bearer ${apiKey}`,
|
|
184
|
+
"User-Agent": "dsh-router"
|
|
185
|
+
},
|
|
186
|
+
body: JSON.stringify({
|
|
187
|
+
model: "nvidia/nemotron-3-ultra-550b-a55b",
|
|
188
|
+
messages: [{
|
|
189
|
+
role: "user",
|
|
190
|
+
content: "ping"
|
|
191
|
+
}],
|
|
192
|
+
stream: false,
|
|
193
|
+
max_tokens: 1
|
|
194
|
+
}),
|
|
195
|
+
signal: AbortSignal.timeout(3e4)
|
|
196
|
+
});
|
|
197
|
+
if (probe.ok) return { ok: true };
|
|
198
|
+
if (probe.status === 401 || probe.status === 403) {
|
|
199
|
+
const text = await probe.text().catch(() => "");
|
|
200
|
+
return {
|
|
201
|
+
ok: false,
|
|
202
|
+
error: `key 无效: ${probe.status} ${text.slice(0, 200)}`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return { ok: true };
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
id,
|
|
209
|
+
name,
|
|
210
|
+
priority: 20,
|
|
211
|
+
icon,
|
|
212
|
+
status: () => {
|
|
213
|
+
const accounts = orderedKeys().map(({ uid, acct }) => ({
|
|
214
|
+
uid,
|
|
215
|
+
nickname: acct.name || "API Key",
|
|
216
|
+
credits: 0,
|
|
217
|
+
state: "ok"
|
|
218
|
+
}));
|
|
219
|
+
return {
|
|
220
|
+
id,
|
|
221
|
+
name,
|
|
222
|
+
accounts
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
listModels: (force) => allModels(!!force),
|
|
226
|
+
async addApiKey(input) {
|
|
227
|
+
const key = input.apiKey.trim();
|
|
228
|
+
if (key === "") return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: "API key 不能为空"
|
|
231
|
+
};
|
|
232
|
+
try {
|
|
233
|
+
const r = await probeKey(key);
|
|
234
|
+
if (!r.ok) return r;
|
|
235
|
+
} catch (err) {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
error: `验证失败: ${err.message}`
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
let n = listKeys().length + 1;
|
|
242
|
+
let uid = `key-${n}`;
|
|
243
|
+
while (getKey(uid) !== void 0) uid = `key-${++n}`;
|
|
244
|
+
env.credentials.save(id, uid, {
|
|
245
|
+
name: input.name.trim() || `Key ${n}`,
|
|
246
|
+
apiKey: key
|
|
247
|
+
});
|
|
248
|
+
env.log(`nvidia add api key ${uid}`);
|
|
249
|
+
return {
|
|
250
|
+
ok: true,
|
|
251
|
+
account: {
|
|
252
|
+
uid,
|
|
253
|
+
nickname: input.name.trim() || `Key ${n}`
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
async removeLink(uid) {
|
|
258
|
+
if (getKey(uid) === void 0) return false;
|
|
259
|
+
env.credentials.remove(id, uid);
|
|
260
|
+
return true;
|
|
261
|
+
},
|
|
262
|
+
/** 对单个 key 调一次上游。选号/冷却/换号是核心的活,这里只报结果。 */
|
|
263
|
+
async chatOnce(uid, req) {
|
|
264
|
+
const base = stripAlias(req.model, currentAlias());
|
|
265
|
+
if (!(await allModels(false)).some((m) => m.id === base)) return {
|
|
266
|
+
ok: false,
|
|
267
|
+
state: "no_such_model",
|
|
268
|
+
message: `unknown model ${JSON.stringify(req.model)}`
|
|
269
|
+
};
|
|
270
|
+
const acct = getKey(uid);
|
|
271
|
+
if (acct === void 0) return {
|
|
272
|
+
ok: false,
|
|
273
|
+
state: "no_such_model",
|
|
274
|
+
message: `unknown account ${JSON.stringify(uid)}`
|
|
275
|
+
};
|
|
276
|
+
let body = req.rawBody;
|
|
277
|
+
try {
|
|
278
|
+
const obj = JSON.parse(body);
|
|
279
|
+
obj.model = base;
|
|
280
|
+
body = JSON.stringify(obj);
|
|
281
|
+
} catch {}
|
|
282
|
+
let upstream;
|
|
283
|
+
try {
|
|
284
|
+
upstream = await fetch(CHAT_URL, {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
"Content-Type": "application/json",
|
|
288
|
+
Authorization: `Bearer ${acct.apiKey}`,
|
|
289
|
+
"User-Agent": "dsh-router"
|
|
290
|
+
},
|
|
291
|
+
body,
|
|
292
|
+
signal: AbortSignal.timeout(12e4)
|
|
293
|
+
});
|
|
294
|
+
} catch (err) {
|
|
295
|
+
return {
|
|
296
|
+
ok: false,
|
|
297
|
+
state: "transport",
|
|
298
|
+
message: err.message
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
if (upstream.status < 200 || upstream.status >= 300) {
|
|
302
|
+
const text = await upstream.text().catch(() => "");
|
|
303
|
+
const msg = `upstream ${upstream.status}: ${text.slice(0, 120)}`;
|
|
304
|
+
return {
|
|
305
|
+
ok: false,
|
|
306
|
+
state: upstream.status === 429 ? "rate_limit" : upstream.status === 401 || upstream.status === 403 ? "session_dead" : upstream.status === 404 ? "unavailable" : "unknown",
|
|
307
|
+
message: msg
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
if (req.stream) {
|
|
311
|
+
if (!upstream.body) return {
|
|
312
|
+
ok: false,
|
|
313
|
+
state: "transport",
|
|
314
|
+
message: "nvidia upstream: empty stream body"
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
ok: true,
|
|
318
|
+
stream: upstream.body
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const text = await upstream.text().catch(() => "");
|
|
322
|
+
if (text === "") return {
|
|
323
|
+
ok: false,
|
|
324
|
+
state: "transport",
|
|
325
|
+
message: "nvidia upstream: empty body"
|
|
326
|
+
};
|
|
327
|
+
return {
|
|
328
|
+
ok: true,
|
|
329
|
+
status: upstream.status,
|
|
330
|
+
body: text
|
|
331
|
+
};
|
|
332
|
+
},
|
|
333
|
+
dispose: () => {
|
|
334
|
+
modelsCache = void 0;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
export { factory as default, icon, id, name, priority };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//#region src/suppliers/opencode/plugin.ts
|
|
2
|
+
const id = "opencode";
|
|
3
|
+
const name = "OpenCode Free";
|
|
4
|
+
const priority = 0;
|
|
5
|
+
/** 面板图标。 */
|
|
6
|
+
const icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAeGVYSWZNTQAqAAAACAAEARoABQAAAAEAAAA+ARsABQAAAAEAAABGASgAAwAAAAEAAgAAh2kABAAAAAEAAABOAAAAAAAAAJAAAAABAAAAkAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAgKADAAQAAAABAAAAgAAAAACaA7zWAAAACXBIWXMAABYlAAAWJQFJUiTwAAA+7ElEQVR4Ae2dXay36VXW9/t2ZtrpACXpB6UfVAKKKIIIoZSStlIUEwsp0VhJ9MCGwIkJBjUx8YR44seRiZ7ogfFAoxgMUquppIQ6pIhVxEoMYCBpGWxVWtKvmWln5p3X63etda3/ep6935lpqZ60d/s897rXuta11r3u+/n4//fe79y6+jy27/3e7/2qRx999GtF+Ybbt29/q/qvv3v37leqf/AcRnqrbt26NT06xrHRi8fj6Db+6aefNh6C+IXjrGMcLuTdbvKN/V58yeOMQ5+8wgtm8+y5bJ6tb/lx9R8U5tef97zn/aJ436M5/MZP//RPP5K4v9u+qv+7YPmu7/qu1yip71Gi3yeab1L/KuieeuopT5pi3NR2cW6yR3cTTjHuotdxC1mNxfVcPPB+sAkdOw3obCTk6BFiQ6adx6U9nom5eVp2jCDJRXoSMRYM8mqTW3Tbjsym5dAGsO999933iPT/XfifuHPnzs+85z3v+UB8P5f+c94AWvjvUCJvV1AW/qVKxovOwt+06EwmBaBPq/rcrRVpDLbgg+veBZPNhV02qzJOnNM4izG+5xiMTy0+Vu+8gztxTH7YGz8bID7dBzvq8DMZlD0PdVUvejbCAw884F6Q/6PjndL9/Xe/+92/hM9n2y4r8Rw93/CGN3y9Av41LfKfVUIPfOYzn7l68sknDzs7CW/KntNdrlT5evLYF3YKEt2puMAPmLZH5x7fxIIeJ7XY6Gm+PdB3rHCk6AWqwotuNi94xuZtX2OtDNllI5vXAJ3goYNk6QYTNbh7yfjFprvB1fOf/3zuEI9K/RPy+7u6I/zXcD+XfhJ5NvCb3vSm++6///4f1WL/FQV66ac//ekrXfV3udo7IXVV/O73xORSRUucYBkjq50xUygMwMAAVNt55yoNphB9Fne7H3y8GMTFHj7CdC4HjpMevh3fWPw27rnIOIJLS2zpnFNzTn7BpU8MNsILXvACNgJ3hL8l/d/TRngquGfqn/dMxthe97rXvVLk/0CL/ZeeeOKJh1h8Fp6mJFOMEZMYZjA9tnyBY6mGDoxauOLjHht3Duzd7Lh5rWh/+DhoLePWkMsmap0NkqfQyJ1PAk4+UDYvGEEvcRIAHUc46AOOPn18zr3sELso6hNzw6pggrEWWheSfkh35+8R6Ote+cpX/vwjjzzyye1wk1zZ32RpnW75f0i5/GMN/8hjjz02C8+kKse6pTFOy2Q1XmKsF/zmkNxzng0zDggdy/GRl29ws4BRBBd/kmlb5p2xIdg7Tuzow2us7NgMxZgGtUzhi799AYNjEyNySDaJ1N5tiUvf8PjO3DvW1luWfnS8Izz44IP4vE88P/jwww//cvvd2CXRG41a/O+Q4Z9rh7368ccfd2I9SfutZD0ZxqsxUA4uzBh6vGBTYOu2HXkXCkDs6mfHoEvrnFyQzsdQ7Bkj4rPH8V99SCf3ky16x8LmQEU6uvh0jtH7XQjdyiPQQ3+yxz+xwCYPz6fjeBNoMzyitfuBn/u5n3vvgXQNxnnpLHLlS3innvNfxS1fxOeCO5kuon06uK8i4Chl367nCW/bnpz5buJuzskbAsLYoU/tt7kdt33p8EGXHMfetoN+z6HCzaY91AAbWHHsDba5HZaT2rWcSbhM122td3eBub7mx5d563BsgNwJtAl+U+Jb7nUnuPEd4PWvf/0rxPPPtHv+AFc+DWL6FTwTOCcdKFgwG2cbyS59kj7YCLdiEdrjJj/4N5d1+DUWwZsWnuhtrOkc8kpO/a7RsOmMXRzmljUc5scuHtsk2xZemFqF3ldF64xHVosNrOu6eAog2ubxOKfGOWbsfCTXBniRMK979atf/a9veie4tgF+6Id+6P6PfOQj/1CJ//F95UMqHfFcaCZGUJp0DoxxN2z4VOc+uN0jmxNfaHvMMDjbCQXn0s+m6DjB4ytVXeEMnqHZp3mBTS4Vqngiyx7oIVbzDxf5wAVYrc2XrtSujzGyXAOBYRJqiJ7rhcF5MsRvcm67x/LzdzP63uDl0r/6pS996U99+MMfPnwzd20D6OPEjyjYj/biw+fgCMmkkxk9JnAElDLJpBgeNzi20NFjii8DK9akU6DoN4d1ISE/+DJuLobOXR2hPEbF+OQD7hqmfYBenBewHWyTOvz0ntdWYBfesZs3c5fJtkkyvMGbsDdCbB2vh440NWj8XT3Gb+mj4h984Qtf+KkPfOADh/eBmRDg1772td8o4Lv1Wf+lfLmzA2PvYOgtP5MdPK19DlXdesnOAc6lt5zT2ZbxxkeneI5F3KVLzofiNP8sxvZN7GDEdW0xsSUGMjFPLQrcUzNDTtipT/JODy0OGh844p/4PfZcwHe8Ges7nCut7Uelf/173/veXwNDu12dz7f0/PvLeu578dEQFOJzsO0TW/oktDCIw3PCZQNKnVqdPC8FwDAg8O1j3fIPJ3aacfQ3tFFi76I5DvKey/YP1sCb+Yc3Mbd/65I3/WFzLX6JXgOlUu8E4dt9uAGp2RRdcFzQai+W/q//2I/92Kz7FOvbvu3bvkkvDD8v4At5eeiPX/F3MYe9tQQjUHrUpwQ8ubanKMScqzQ+STj+6LUZ58rAR8fkm5jyA0Me6sbsvOCQLn47l8m5fYGOzoObT+GK9TAPlMxjxbSKk3STHBi1M1d0mcshX4ydq50Zotut7aPSmG9qjUPW+t7SneAJPRLe9L73ve8/AMxO4Or/ESX2QhknkMaVaS/yMLfQ5hTbWBYtesEcvHXKYYowdwSogsdRzWP6hQd2GGKPMj5WrFOHs58wziVY9bT5Ygs3xlbW4pgpOnq1cFRwjVvvmhkwU7R7fGa+zRe9QdLRrFOfeRLLBmx9AEKfPPCxLT0OVpavOVIH1lZr8YDGP9iY2gDf/u3f/hop3sKVrwYHLRshk0tQ27BzpEm2neyjV08D4kQyaB8b2n7mchLCTQB8lbxde0JNI3K10x3LdwUA8CdGHNrf+QLZOLg0Zg5uDNGpOScpPd5+8UfXbebLeGNriIebY5u8fOHec3buxO42tY3i3MPVMZ2D7MnFXDwKZP++7/zO7/wqfHMH+G4p/SNd9XA4IjIg9XMwVjsUDbuVjcO9daaKrD58Eku2Y5/Acajl1mo8ipWTN0Kwu8cPrBp+lnsqVnInQjj5TIyTDb0LLjyNHIC4aTyLga0P0qQxtj+DhZ2cwKj5JHvypk/MzJnx5N3c8NMclxDyywGv72yyJ2FBak26Bi/RRvgT4G73C8GfgmwXyB4ijaOwTkK9J4azbNEdZLjSkOFQs7JtScyw1nnR22+Ka8fiC+nBt/HuyFlcNC+ABcVNTx7I9EDpF/8hz9hkJ25iuheF/XJqvgy9KDM4CvhPHeBpLlE4GdtLrHegdk/84K1unPNjWn0kXw27cJc4+Ke2/B7H7Vv66PcqKX9Bt/9X9vM/BB3bXYJMYLiTaICt2/7INAcV3i8l/XOFscluWfabCrz5TGZCQYmXlly2LraND0468y78Oc7ekIL5arJu+UB9SeKyUazHZ8VDd1Nz3PxI9wRIju7vweXcto38TuNJBJteBjk+9NBDD732Pi361+r5+fK++lmAa4sgMnSHJFBdi9yYTEJ2cwmbwvEWevX2t7/9SsGzE70rK0Q8q++JmCOW1mXoHh3tzBH9TTbg6GmNk/uoUO9B5PTY027SYbP+pnzj2D1f0lz96q/+6tW73vUuz2HlkRoNl4mPec68E2v5J9TkKJsfodoAX6mf7v6++6T4o1p8fyOIYxcjCwa5VLOQDoZCDdJzYhMoiQp3WGDuMt///d9/pZ9XJ7kv6J6a8wL7jne8g+OuvonNorsuq/61y1Vzapq1Uk/Nsx5ev+UzmyNFbt/EeON9UnyLNsA4CjgLW9xzJUwg+cxCQ9hN4txpzBdD7Ipzi7fQ/F4Bd4Mvtiv/Sh2PRX69jl/xUqOWLJ4XKjLrYUNvgtROulmPxuC61wC3weDHmgvzrbdl+JpNhKMaDlanb+Uk1HqDWo6jA8fefOjsS2COL7ZLBagVB7/Vg1Y1S8vQCyIMbdd5PhqHTXaL9DkgQ8k4cuN/P3eA12BoAALgcWgnmznRFolxpb2cwwe0dxo+/A6hD3QLc3H8ApWoBTWhPl1vr0NfKJapeduoUm61s05l9joCTSXti1986U2gXrivvE+DBxsQUn8chAQmtfKoTSGod6CJIZKdzq3JjbdjBRlbFr7jjb68vzDP1I+6pDbaBP5toXtVo2tM3fcaSH24xdu21mDWFn9i0ut4kA3ggbq9m5C9kIt4SIJV7xbC7okbk9wvbgzU5jES0Bd6r5pkDWYjqIZeROpFfcB0k3hZ7NQ8xt3HZ+HDORfufX2bsR/MTcAieqE09gpCEjM9gWnRIcMVPWMaYw7Z/HNpqfxdAH45DPwCPuXqp9eROqefKyi1Vj29kJRMupFXCf3oxdxfkeeKlGu9SMrtFo+cfQfAP0B4I88iV6zLbd8gsqkNcS3R5jsvuPH45CAwjYCzA7dcSVdykuV4M+7kcxNuJiWsdiZn50Fhgt8ydo8LWD7UfOExpYXfq7L5B3B4Ozc3dehNIHEuNOpJbvMe1RQJMYwSdtk8H4zyz9qMjliY6OH3BkAgAfoAWk4SOLmhpwWH3DoMnje6tnss2ZugJ5nJGtM4XNwyEwYHWQkv8qOtXK/712TLmrl1v6s4OXSMIrpssqEPHzgdmfDYJSTH5O6+eY1vjvimJvDpcFqTTy8U/I7Xte+x10Yyixk6h2c9sk5giXFuse9HgHhuvOqFnY1x5jn4ALQCh0tLTayRfRYeebc9OsgL97n4ECNxE8/8izcXQLDB7TEcrjQ9NdmgAnI1jP6zyTX5rcXaNTxHYjxXsXPR2onDt/xK5Vp25iDOXh4+BtoAYRNlnB5e2zo5HG7ENpcTAcOBDgI11w4dPBr70Pj/WyPmvdoz2eITTProp38G/sFsAby+BTzVQ/RVb6CsCY2YHdf1t7L1wWiY2se8N9GsG26NrUdA0AlA7xUWIbbWo5M4d4lDIuEgGTDdZwLhST8TgntjGe+GQzRbDuYmXWznHqx+XaXUFDayNLElF0DoaBNfPrlCr+VZ877GGd9iKk5VZ3D6Jm5qQWzxZxF98azwUAhSz/XwKQ8Xu/MhnP3g6hb78G2OeQmEF+c+7JtJhqxjNe/1QPhSIPDxBRw5xYsu49jpORLPuPY3ZzlSBiQX0cVE7DEdbTjgZKxjsMQo0OQWfPLHbE4EGjw9L+dyzlPj4AzXKTjGjr90Wg0As6HAXJt7cYqm59sYOvA0jPFT39MqvMZZ/AKXXzaCdX4HIAAkuzGOPnLG3eMwmW3/4GWXWEkt3YEXfRpkjLYOG/q5clHwUpOiLH9MaeEYThkoeqK5X77B47/l8LlvfOzpD5geYKNOaRM3HBgk3z7Vvv1YpFk8dN0QRHshjg1V1OjyG1Kxt79dt87vAO3sbLexZQeFILbuIZNYeZJYMPBhQIdMA0sP3r0WkatNHw8wuhjn3jyAT43JhfdkmqGDNO9Z7sQ8H/N0gWeiYnH68sfXiQ9zTXTr4GAuuaMBjW65HcThzdzLx7TUiIOf24fHeZrWsyGWC79JnXOlm+8BCrxAwtjPtde8vQHaccEOE+D37ewX5wDhkrzrloU56MLfeLsD4IsIVZpgRcmGkDRZd3Gicy/dBz/wgatH9ZfKFAh/9Odmju2/ZLAUlLy6sHb3uInCmQkefGpQm0Qy83jJS15y9RVf8RVXTzMnYq02vDfo9dq+87cjeGouip5GXTyMsdFW3oc7xdIb16fhwlUcHrMJZgOcCgImwdltkScBiPHREd32Mb6DuwtHdATnqFzEJUNNLYhTT/GUN/hf0S9PvP/977/SP0i1C3FyuD7El5w/3+3LvuzLrt74xjdevfzlL7+6ozmRpwJNGKTDJ/GTPTVIj+OuK8MbcgdCPVJ/z42xWhY8PSmkvMhODn9+IURjci5795BYnxPjYKKjRxcfxmc/2ZLEwYa6fwvZBTM/BGpETrZW9Mnc8uNn5vrrliv9xas5OyahJB7zJk50yGnoMo4cHJjtl/HZN35ve9vbrt785jfXTzp1B7hEKY9kZE5d8SIfTC4CuJrP9TrHT27SzxVPmjpmAxCtcdFpeLnT4QqGBj+HXwJ5pnKgODfporQzhCSt/sbgjU9gY+DciTgGCaxg5izgaG3v8MjOUbGTK7deyUOTVIm1mlMiIextQ4dqwaqYrYATu32RddzkMzWDi/cZ5iFP03Dm5dVxmEdjbNTJpBl030FHS54MzNFa6Sxhiz49BuwcqVPrMn8740uu8wjAgRaijAGqqZugKAzVCaUHKBoTMHrb0DfBBVsOnK/FdKE6ngF9Mk85jI/eA4ZzYSvZir/tO1/L5MUE5csRP1Ohd0zl0hjPo2X798UwziZIrWBBZuElOgDjm3SGkk2lQ9zdzmPlQC7ecFwEapnnCjU6Ywlx5vQGaGVI6FPgzDu7J0R22UE7oY3P15IpohM0edVicvFV00WhOMR/pgYR8Tj4hUr6bqZPElE+Q+9iEU8++J5ddyKeh7iC8ViPsXJSDsyjuQ6LzJ1BTjUv5gdJ96fc4LYZ/fhIJrnWpY4MjVFc/FI3y5zw8aBxDO2kE3QccwcA08b0DFOA8Hj22fUGiCRG9UkCE7cYEvCEYqOPn4UalEhSo1yC9AoyCm5t8PApQH/7TgziqvOkBxcBdct7EZ0XegqhlgDRjw/GblNQfKhDiq8YxYO++GaRITaZ9CEVkQNiywuiOGnOsW/fHsOtw3opSIXT/DVUYqePPT6MkdXIceYg/eGrYBsSoHEpGNhpEDWZe2RafCWmmPGXaZ5lgsvcRUphRGQO8zRBNBTIX5io4ODw56BI/YulgaJ3ccyjGI5V+TgUerXB1LDO5C98uJz7ZbiRNVfeQXiRDQZ/Dimmx4tg/r5DvbELx9wUyPoTz140oewretdR0IrheLbWnJojdY+f57Swzo+xXwK5kraRWHiedFbdYHMxwXJkEoBpjHPHSNLoXZTtI1kEmOxjKYWUDo5tB8cG4B2gr5ZrscHQlNdh10uVRba98zrApGNsO6fkjg6ZRlx+k3c3e7Tf9nfuaz74xL6Ssdix1TmHmAXPNVp3ieQB1eYr6sNmnliyHTaHHwEUd71MDRkBOgv4LQuLHbV1JGWhlIiMzYFM1q1TV7ciC1KXqRcXUHNSrHl7BtzNdtnog2Xz0jSeK4YxcRtDDpMjNhp4+oL5agomm+VQKKDyCQZX569NMHHFZR22KULnmyKAwQYVsoTDxYBvcuoLR8PLVd/UiRPa1Dk5OrwdV84aO27H8EU1L4ExdjAwCTJyYw5FaH4ngr3bYGRPMtcWYr4Gxql9YTAZbtJ5vBIHmjn1HSAL4FCZB7DkA37LcHQ7YKK8oZ+JicfphU9Yx+1Jzt3OC9t527nn0ovquXkep7nt2CaucjgmtsS1r2IHDxabjtkEYFrnmrXP6PD1TwNDAvjcWhfDBES/CEcff/KRPPozN+MAwpUgti1+czZ+8fsWrE3gRwC5JB/6xAu3/BIuFOBlnruD7dK5kHDQ2p8BWFR26t45bCwAFpnNSWsfyxo4x61DBgsHfWwnzqljOGPPuAJcP29cW7NBHMvvAAQN8Cz32AXYwYIPKTgWg75xgjhvqarI8UkPNvgpTjnPle9CgutAMOIfDuzro2Aml5iM8bQbAvGkix2VuaQwEEipagi+m9zmivI81+MHEsjD7x4/x0OoPJDGhtz5JYeJbb9L7tZvX9mtS7+5WkeczCn8Dav5s1H3x8BJrLjB+rlnImavxu52kRcmpOBosxGQ1ZwEvm1zj8wj4HY/w31rBM+kseGphn5a2+Gi0bMB1Huh2YCN3b1CXa488Br7rtFYOjCisZt7fDjAb0D09MRVKz/5kjP6dqi5aOD3mdJ6bsyRSH4EXvidFzDmrLgmJidioB/ulrFx1HAW2wqUaiQTQCkqP29g+G56B9iOdk4QJSaxbpFmg70SCDl4zau++NgYdATkiA89+jRn2xiKRKNI1mfc/vj2MYUKb/hwJ163M24XJ58kjGkn0dWLIgItPOojJ4daUHIHlJjdJwcoDnbGwYb8MnbI1Aec/cveNNZ5MS/ug8N/8uw5mUPY0c8dAIBaDPbV2LzwIKs3pscjC2QgTu2DDbz5outhYWTPm37bnZw/F6/CmIcsLnMxLtRlqjuAsImPmpb5kD/peHOi96By3IuQn6Pjy5yAbbt1GLuZRvIFJ06NbJ55aYScA04aY8v4rBY/IDoyJ6k9B/wcDxeUYFqOHgCq3WKbPHCdOwAOIWvniVIx5uUHGGQ7QhIgYBJmcsZaWfzG4UrWzYuZQSWGDDXz6hBgY+cLIfzNoTzK1IW062HBMBtrwWLNk9jh6D5YFzS5MYH4QtWPEFSYiD+Pk3wDiAvH8PZ8QhS7AGRUVw+M4tQBn3td/a5l8jSiT3DQZHMNWu1Otmu62OOX8b4DmGuSEzfyDfwmV3KZD1zonLQHlZPH+C9OzNOSjONI2w6X236QyiPPUW095wRv52Y3cXhXwhW53Rki7qJ4Z0bHXMRlu7DB2SkYOIA0F4+u4JOH85aeWMb528uWrSwDq1ZDcfJLoR2anmbf9OU+OU189EV3bW7FfcM5ucePXA+/D9CTDHEK0fja7SsxJ5zChBR7t2wg+FCOATt+fr4Lj888DlLAxoCdmHAfC+YcuCp1CDabbWJZCcmlOR/Fj0niLEJyBW0OKZh4yRrwnokD8bJxiJvY5AcJLT2Fxp6mwjApD5k38Wn0cKu5dsxLsm3rZLvGJui4gCTOBmVy+FrXviHquVTceQQIZBIC4pyWsXS2n8iiCznm8Ex0OHQEa4rWVSHKHscpjsnsBqs47OmZ+gdBX/7lX371spe9zD8PqA8AnTep9kWzptLelF7FyWi+4MM3V9pl/pKAT0th79x5Wl8Df9r/4MW5XgbXArRYBFWCyj/yJY/S38BlZ/C9BpPLTQIYNZ+2HR3czWMT8jwCpCEQ+Xj3RVbvnURP6yTOZPZJAHAEpMOFHtsaI0MG0CAnJlVu9WW2zyQWTHNpE9zWv4n/fH0PcL8/rpWezVbUHRoqceh/vnpLa2UZnCU2/kc2+lcHLenUM5ClUmX+/uRCLvwqAhsP3ejhlC2uDNOcP/7MGQx+MiLT6HVIjbZacBpJNG5qvXDjgz+eUeC0ZMcs5op3+CIozg0w0SZAD0aHISSw7cgyJN5FkA6TDN4IyADPt0a7t63xBLy60wVxvApAXE/mPn2PcP99z/P3CdjTkDy+qFRs5atx3csuBkvoWQ7+z2dzT9HTMWUwDHj94SeB+5+4SezkNY805ckclIx5kE2tkedb2sq15XD0EFwSyYVkE/qOO2uCATh67OFI3ypMTmgeAWjUwMm3k10DjDFscnTtt2IUOXrMTcNHsPlVZ/vIPhkSO3Hxk1P7mzdjBsh5PvJLmLef5jdxpePKZeIGuwDSO3xxSH9Xi4eZK97BiYlCMLLRFtUC88/l2gWkZefpFOHlO406QBGTRr6eVw18pdugk4pUuYFpLHPgIohP12pcIqz+XGt85VZz7b5SrQV2YsLQZka8v8Ap3bVHgGNhCGmCE8Us1aN2MmBp2HpRJlBz2I/Fb5zxHqzCNdY243SyjgKNlkWWZ/tJ8ILS+7bdNmlRuc1V3QovfGThnX/zEU+KjgZn2dkyfB94KSHkwZGOo5iLBaUONF5yJ9dwdW2xg7UvcbspHw+c10U3uThH6bFLPmwIdO0XDpIENgEQw03vTKNonGcmnfvoMqRH1/pO8XJForjJjl98wSBTgJH5bdoKaV30JGF9+zOmwC6yrsKdBzLYE43Hnoy5mo8A3ihdGzlxawdHuYqnvEBYCkmhJpZz6NjGwqX5mAhf53SZr1Ru3iCSPL9SieqyQKjathdaKl/2sR16TPGDa/GRfU2j8wHHbxMa71MZEmyIGgNkWvwI0A1hfKKMLckc9CuRQ7G0MeDPcfbJpOplSpvI2PydQaGv5zyrF0BVg7P8aSq9zrWBPC/rK4+uXXV2qbgoVg0OOSf/9Mk7Y+bsj8LE7jpyUXTulQyJVaskDa0rv3FYJdYkEqN9vJZt8/o0zDGIlXeAyypWIviIC3/32KOwcwfKZrFO2JlIHEkEnu6Nt6+C+0WpJ4xOJICBujDWIW9e2dGbQ/q8UetmoH8PRXeEvMBBJ53+H0rz1KCv9H4UKOtWW+p3COVCOiSj5gn05nia5z93C8VPbv7Sp/MZH80teQbH2Hc+YR2UuSB3y9w0vNSq7IwBht4e6ORjHR2QxWE89ug7zHTz18EAOGg4qJtgcSbxMsnY2PToTzYNnZdf/sDJ/65+dFtKAhGz4lXi6NQqi0sMO5SpcB2rUshCcOXoi5N+ycPGvqtJcKWxuHAqz34BLCu4CgrWc62h5Kp22ZtJWC9+Y6YmAqUWMpVcSbgu2Hy1m8wAYNVa1/4VSBbVj9rRuwTdCzZvI7P47XvIAYqOQGe/prIa7v09gJUL4F1j5ToRaGHGJwnERh8doOiRCUxjpgJxLiw+TBjF8g8OPn72f2lZfP4aR4sPoxbaDX8JWm6p+krEQLwKb9jlhHfXqynMhw6fmJwvcbXhHOHCkHml3zWYWti/fFRgzxt8fITrSJ2+S1M1h0OcsbumrTPhxNBo6ztDuyqOv8XEzrGreSZ0/RIv5IyXbEwH2EkIUk8MKZOwsfhyMGF6WvotY7ejbulsCl76EjsyeDBNYxwKs3Kl69DfvwBTI2+d7WDmUutsPb0wtgAyDh3/a72xsDMHHgPM4fLWn/yYT2SBhakxOoLVrNmrFxyYUx06lbp9x24OsajH7ixl87D9q/DFR6i5/WPHLVzklncA5EmggU6AHbOCgTFpwWu3lsrzso9s7pvH1NExYHFp7Wd5TiSuJJMoVwnNG6KT9yeA1vG2rf8YTi9+h5cL5dE2o1TFRXr1f+trSS8LS0a+h+AIkE+tvLqkz47ouH4MeB7Fn3w9J+XM4tLQ09DHVharfWJu2MJR8Kyj0U6q7chzd5bOsrmLsgJ6Eq3gWSgfHYINr/2uPQIA4QaQngD0aibpQNkUZejkPQC4Jt+yf84ePX0mnZ+Y4Tt2FSRhOxkSATCbBzt4soQrt/l+3fSGKI6euzrSgsa+mg7+3gI9Q29LYbQcFb/xdqxyeIpc9WnmECmcnhObosfcIWiicTOGJNQqN4s1j9ajEafMTsqLhLx0DZkN09kXV2ONL80h1vA2yeUOwERaWZErqHUhpVfzqfE4MdZwNgwYTxCMfDJ/68Fl8XHMbdBGnfDhmAZHD8A+r78DcE4qrbmepw2gH87ol6i8IVhUUg1Pp9Y5XfTg9BN3kvAjhoVnOjUhNmG/V5i3H0nwsuH0bWFyJRfno/x85Z83Qdcydy6ms329geWDrrmcSMbgw4+sVin2RgGnVqfLOtRksBS+pMLh73jzKSDW3SdoB8ht5xqxcE5IEzFxONC3r1UMgWYD5DaJER0FCp4eLD04F68maj1z8jOYwvHip4+Anjv7UItaa9DpaOwEbUs12CzSyxXa+vpXuqqj8717t/4Laj0Qvn5GQE7cBWoTHN9PknfFa3IT1ByxZ57BFp/nK9E/Z/BX5nLTFC9/90c9aF0XD5ZvGY2gbJf6SaVE7Md6HPz4L4aUi84sQgeBIIQ4R8a+b0v2JRiY+NJvHXLG9NkAyAt7iQ8fHLWKE8N46SggtWDMwfwiF59dSKkEzwBZQv3femNra8jfE7AeOK1iXO4muZmR/xxyhIfmfNVjyyLP/CqAcdjTknf3gtfdpO1O2spSkFpPKgzal9VcA/zFtTHjI71lYRCN8S+EoOfA0AGuLTLENHA0JtG6+KC2EXIdvtrBc9i4enTDSUEq8cHODJYPeIp8+/AXwXUlkp7tqk98mSOyE3T+lWrFJS+yqgWmrzwvedVspFdZwJpfgq9+XTheyNZju2Nb+ac+5kRfBeDs5vlL0qXuuGtTeEErl0p9cymO7wpydX2haGx4CTU64b0W6PoA5xLhd/gUgCWNCTUxHJETFJiDYEsTfgZ26trHTg8nE6Ln7jMOZSzoJV6N249Y/C4+PQebYQ7/gItbtEtdlWNRJ0AJLMVscy8eIZifzubtzY0OC3FMqZOecPgn/7v9HpB86N1w0GG30hzOzL14hZEcvgZ5wZpLHfELF11wB1INmndmzBi1jrMOvdthA8hhAgaQfiViTCdjcwcKdHQGtrbxTgZ8DlWi8FrM4azEhw8stvggZ8z7Afq6SmquUMLqxeckNZ11OmN3CMP1TtJLhR5U4TDqKOVRVK7Ey4vgzse5iDxfDSfPzIEIyOmRczTWRsnOpm25K1gHhY7DBRhOEx9Pg9uYxPQvhOwdiExT/AQVtnJCjY0xdjXGE4CcGwvM7TQ29qS70WdjwouOZ2u9A/SGYCH4WMYP33QXoGx3/dm94jtBpc1tnPThuuPp+KTkPSWZ4Lv4lGSXmi9TL4pefOJOLezPZqyN2LnJnZyJSZQ9p+D23Arix+cBC+bURhHOxEkf/E322MjBnwKcoIJ0Uj3NWliBa6WZhI5FmCTqtiFgbMH1WF29E8CFjTgcNN/CLV2KFZ5WH3g1cB7Y6uoXF48S/khYi8me1Fmy/+/CE9OLp5Mnl8yNlCZjjGrA3TAxZ620omogBTE6f/rkY5feQcm/6UwVSvDRg6MOq3d9dAIyF5bs6K2TXsMp+WEQgzgBYHPYliuPmpyhOtUGQKABpGPcAVtdhizaiXA2iPxkcmAnpisVW9emegjhySFg/QRPeudBLhXOxa3MbCy9PwHUZoS4Po5pzNV/xxppq/lzPHXrO0LNTjaTrl/yUGrK2mpLzgFcaZ3OJML86r/vwzsM6vwyijczvjQ5Mcd8GmCzlrrqkzq5DuLpphL4YvHfGjTGIVJHCqxWtSLDlunDoT5rGB3radk1bjsKPwLUOzAKAAI7KGO1JDAkKE+E+DlCB7BPFyDcm9NxGku2DmL8SnTHcCHZOJUfKbjAT915yrv4ln//QimQBZHUahol+wKGW1dzeGu/sORSg3ceIajFh6vwl/S90L5ycwcozDwCCKmNSssce+D8kR1F8WicmbsaajfGxD34x9hr0kP7gBN+/E++l+SPvnUHULAhgXQF9SWtsQnoE0Q+I6MryGQYGFzZjROjJ2swfnx8ynNds5jHAjj0NHBVLmpbPxji2e+fBejqp34KaiwnZF4NUlKPNWQmgW08PteaAyZqbRSygOOONt7TTz9lOTkycBwAHGq7Lskuc058z63x0uEYKHy7toL6QsMeuYSbcc4HPmJ0s290+RQwZOJhNw5oOe1ksvg7Wcv4Q6bmSTBuzkmUhZXd1I0//AqVHFxACgt2ML0hsgG4FcMDJjXDtfDQw8O45ArJAvpGr6u++vhWRmCjKZlF98uieUSpuwgx+fqZTTj5CE4+57kxBuN/RbQoPQ5210Oyo2DTPNRd6oicWuwUrezY+LQNX6J11pUbdvAY1W5lA6Cb5JFDEgKB0aVlR+FnIhkmcWTw2NohSYCv4qmA+1tICqQELrfIxuG/YzevF5mQfBS7o3u5F0k+9VavsJLzUQychmo6KQZJJSEbrOhUVW9zNdxg5NXIYV4EtQGYE4f1stGYm+fEgHl5kzKo2MGe/RirHVIEmyaZJljFa7zNkWUnH5ode+wcBZy1Q+8NgLBbj4litcYVcYFikyqLnYW6hg3/8llMtcC7QF6kjg0Qv2QIV/H0YmrM7ZhXOn4djHsB2Fzb+Hu1mQoGHVBf7hkB0Fcu9J65T+gYNyu+vgPwTWD9QIh8nKOAzCNjeGbMACI6iyVvv65TBTKy0ij4lNVZCbtxDEVVCYcTRcGKbJ+jn18IQaFjk87CyhHZEU4YOGsmjQFLAhzIjUemmYaizIKnKOor/QLmLIfiUh85/HDwbZwudW2CffcgKXLAJ0VRms60xuYiSKUpQVh8CtSGxHbioJmdc/cc+BSQ/GRCpqVHJlfG1mHv2kRvPPNoX1yQY2+9hsVjvJQodIdxQIatpzO28ZVQc8rG2HZ4mcP5DlDl6GCQ0JrMP8a1t7VzskpkswU7aXX+lzgmOWH88YbANHBwN2Bu/9jQ2SY7/xbfPCLkW/ry5+rXV0PaBPq/rkwa55opGPaHFk2ank1dldo0vg2A7Bzw6SHSsdlYKvLm6udOQADyoTGvzAl9PrGgo4Hzdwc87tSKp+YTDL0OHATvjPFrDvxikC6i7cGnvhjV4mtO/DdX3gHQpw0wRBhEdIkWZPcQEql7dTUmeBq6NORwI+eInf5eeP6cms3AZ+987PJKwtPvEcoWhulqu7UuQfKs0Jg85a1Lz4Py7e1intFDSb4sdm0AZPKpnKrYyZ3+UoGak+faFwCpEJuDuezA96gda0BtXGscbmqNsYk6N5dTka//gEdGc5zvAJ7qcqriKDmCNhEcJtPYcvT0wbXBYwUzb+PmSkkcYZ1s+4+MsG0a2JZi43/HH8Wq8Leeriurwhnqk5e+XFlBSLvXoP5fCYKxXR1zQdt+VhsrPe8AevnjDkB+YDnIZ88JH77ioZ8mHA0/Dn64Zbk2BUYN56OzbXC3z4EKrA7rgjGw9fDS0iOrdl54dBznd4AJoInMiuOoAA4mJ4lVcGRsEKGk10FzQXDDrmZzibXzkMHlCKdv161Hl0ZhabkD1Bu+Cs63aLqdi0nB+rLuRSO6n+ri260nQlZWJ0rytl0Wjz2FaKQUODnz3kG7fVt/nKq7ADlioyHzh6uM++o2Jr6ZW+YdP8Z9SGUupxc/uIORbv6NAvSr4eOFwg88DZm8yDXtXr8RZGcccaIRLE7RZZwAPdbwErB1crG/yUgixSIZ+MLJl0L4J1HkbUfGh69+eZ7WFzJVFG8KBfR26HCVCZpjm8n01jFiphg0qJ6/Nxi/Ioaq8tXXAM4tfyK+I3heWyH5pjlHlzmqz4W060jYpCzIJEqtjKfbGGQ4aek9OI33D4OGOKQohL9U4SInGS/WOUAHYhMRfBJpLhdiJq5FpDlVZPngZ11PABldjtrBbBJ9BMRFVz4bQueZrBng8WKZztkXc+EqDotpE0GMLzTIyq3UbMwqB/PFhzsAX0YlL/x2LQrnmRmTTV38dQaDnqPyuej3GGqNSaqmUDCfGzcbQZwNvYCI0ziJlzXZj4AhBkwDeCJHnQW1EWzwYBs/OI1lvuxYDMEjP6VbuP/BxebBn2Kk5cui6MGyAe6/n/9OwH1Xn/iUYvIdgPyUibjjSc9A00LJ7HqI5dgWJngD2omOjxlqxDGPZNPq9s+/E+D4PYfMz3cB6dInucwpevA5iIHsOJ2xxs6gdUDcFs4+KINtGR7POr7xyXgeARhipKcF5IFO0SOi09ibABy2jNvmjhOt7ZZZYMbZ9bFlfRifWzYFtieffPLqh3/4h6/e+ta3Hp5n+ITDsrB7DviyXZSpe9b64LAIjGWxdxO5fU/6r/7qr/ZH1eRNn8OUjluhMmf0kYNNHVdIXz0aO5Hwn+Y0V+nyG1E+nqV8MpkD19wB8AiGXo56xNb30tEH06QOItm9TnNHiEJ+NKii8vpkfO4B8VKXz8+M5e9iRsb+0Y9+9OpLvuRLrr70S7/UtlxJYKpWFS++9DQKDvZejXziAyZ+yLG5mmuMniv64x//uDHB0XMQj7ij7xy2PTI4qOWTxWK8c5rFxqdbsFJV/j2HuvXWJCxjJx+pMg3dQ1cLaRM1zoBZ3BNGw8mJIAWu3ostRRLEZgy4fdhJJ5LDKb1AxmGnOPxd4JP6Uuh//tZvGQdHzQVELRK+tCx2csqMzz4G9wku/OjBhQt5+yUmPYtPH932Qwff9kcXLveKve0a+s1esWcBY8cW306ZTipf5amzxz5VUrMOrqsM7Wv8bIDod9+TMtdE0gTUZLpMpAmna79DUdro3ZKixIGY8UHO4QUgX8XCnmcn3/nzCcD/znD7ZsHBhQv+nrRDkfnZTiwaPZ8i8lu64UAfH94/iBM8sm3t3wZzxT+9beuUueTLrM0rmIuc3OIGF7rkFD39wtpXqvSG3WC3/vAOgCZB1MvH65Vk5lK/Fxn+scn/2m6FDwyTzYSJR8OP27sXt4ueAss4C26c/B0HXPvm+wOT5QSnDjbBcDXe/i0Hzo9ryQcbfXJ0/ICWT3CYvFnV4xM9mzSXGxjavmOA20chZg1w9fSUi4sEdjUNPb4Rs+Zh34yXv0XfLzH2BEwKsQ6pL8UQ2juigw4P4ymU1hEf2tINtgW51MRj2GNkOMa/sVztKS4Y/kSMAo9OZOG5o8cELV/ARD9Y+e+WnIPDtmUlZDj+4UAxfrYe52xs5w7X9mv4xIht+ISXTPMmwL8bQgaVFAqt1SHf0tkl+uYY/x5ffiewFdktAL3g3TvZfTs1u074KVH3YFHRn7DWSe9kmTCNK96GLjA6+GJ3Tqv4jMGza/n4SNxUwVgKhY7NAVYHmFx18NOit0+Pt4xPcgCPnCucMXba5sl/PAob+hzB2qFPiQVn8ImnsUsSblyCQZTeF1nbM/YG6Fj7Tn3gKtdZK2cz7wAeXU6zkASidRKeeQf3JOOydIJOEKnrto998ZQvhUyxVGT/MKcJw8cQmcMF05grO3xm1Tg4vUGNvDdhCpzv3jMm1+RLT9s28sviow+GiIW2i0/JkwHy5sY3PLFnTstPYtXLhLXgLdYaxDc5azybYPuYKMnKsMdLfflZQDu7E9h9JhD/jUEGt8lQaezdiD3+mryTRIUPR4ocnMDWz1hCCp580oNJO2+A6OkTJxuBHs7Ng5yDfGOLTH8tD8jh6Q0DJi1cWezMMzmEFxxy+OMXruQBb7CtuwRr28Ygq9UCIihOWuTEQH+4AwgAOgFmIQHGCUjLc6uJXTaZ5ooyV/AY1IC6oPFxgQgrWwptkHSVjke+erBzO9/fvGGNH/zmk87fLpbr6MIXPGZ80O8DHXGyYcBhJ/vBS+ZRlBdHiW7xSR7R40eLnnF0K/ah5vGlB5MWOf7q58JqjIM1zo6S/VgmvmSvDdjZAEkMI4QJFsLy8cgQxsRFg0vjeujx6OKbnlgcuSpE5Be6hF25XAreBUiBN7ZzmeKSRJ772BIXn+2HHBs+iSul9dseHC+eNMZI4aPPvKIDk3zDjS686BjnaF4NXVbBLl/jtg7srA86xqSB7zM0uBxHGE2hvuADPxuAYGoBJgg9jgRIIAczEIZu5NHYSbIJQWSHmhcsB4tEQ/bHNcWiYPDQUtDIuweTAz0t8eMPb1pscBKDRnw+TcjROYyfbMjY4YieHF2IxWsineBNA+9x94yTy9jgkN5NcvyFS30llhxfsPJvp6obY+xbDcy8dbIMGUPwamOeDSD7RXshyK+BjUML2QweNvllB0kbHYCOjegYTHbpnHxn6UJg2xuBcfAkHzlFg5gWGz0Hdni9uXrRz1z+t4ZlCyd+tM0dn/BjJ7/4pN86MAI4djiDs63t6MiBHlw2ZzDSzy6W3YsdG3js9Gpdwly7s0EMjy90jbeeDfC4jgc90inxINYwpJirMghqwWk4OIiX3oGio08DQ4Ez2e2TwqPbR4obLP2Zkys2OGyJvXmiSy70m/Oc18YjBx9O69Cf8t04MPGlj+/GrBx8i9Y7jIMJP7UGU8PLxQqf2mAkC3bZMy17jfANtvWPcy/8cN+K7diEUlez8iJbamfLwpOBI0rvbBrupMAC6d44isyxeZpsCoUtvviDp2eB0wcTvmyoju8uMcDQ4uNBn4JJz2d65OQNLDkk72Dp816wbbHjRzOu52SFTrHFr3FS20cuboEnn6l3480tmdp6gdVT++Csg1O6YHOH+yAb4Fd6A+BEk8pt7yo7JmFZB9d4+7bfEPTYYHAcmSxc4Ystdvy2PYu+8cHgs7HBYCcWDczeHDse8sbgDzYYbIkf+cwVf3xuyiW6nRs+afjtunROLOCsiXS0uLhnrCO7JXjbOtbocFbzvAD0mv8Gj4Bf1I7/k0ogz5JdMJwOG8HsXp/ErWeOAqJIhuntC0cSonh8RHv00UcPH9Wa1wUk0ZsaHGcb4+a+yeVz1t2L96x/tvGzJUDu3HHyH5+Aj9Y9A0Fc61mHKMC1DdFtj5MbuvBGJqbaL7IB/r12w10dt/Y/w9oOFbkScABOIVbvRadXc7KSPQMUQG2QCjWqJ5544u7DDz9864EHHpikhrgFcE1zNnn8bPaz02eLP/v/vx5zQXzoQx+aua14St1LsFSzOdBNfZG7xrNRDOi6Sx4uFl/rzUewn2UD/A85fli/ZfMKNkAKzy1JHjtA81k1OFjx0ZFM3aPriN4YOGt89elPf/rWT/7kTzL8YjtV4KGHHqK4aDlN3XpNrq0FuLOt6+71aS74XHts3IH5jSqt7/+S+tfZAB+S4Ze0MK948MEHcxXbAb8OAKET2KSyW02vg5ZN4AQwKqBcfEUbo012i80nU3m0r8bGRUkPpn095NQg52llYTxuvLrajMKOXlgXF9vihMINHc3OOHZ+S3/ORSb7ZCJZNDhi25Tm79PkJaBluHjzJy45qJlDfepwyAk7Nvw6yPCjjw57xuh41HMRSvxlHR9iA/Cm9I7PfOYzvAc4qMaToOR7NWNixJFASYrAajtJK7jdceQOQ0zh8tcq+IiiFpDd2nypKLZz3GwK++rkOO13LT5pghE3VKQ9fvFBh7wbOh32wV+HAdI5RmNNhgo9uhXD/lIFbwwwDmrSLqZKfALiY6VOjNWMFT56i+hjA4+8mgfUWxsA9b/U8TQbgPYuPRc+IsNLdBeAPEk60CKSWHNbPf5JBHmSID8c1GxnzET1wsM7B7b82JiQ8QvXnnh06RMnhR6sOG3jBCdt60pdm8DGOgGM4yEGZvmH37jO1bjkXTRzPtiSh6ypa+zmZaNzqDYeg98+w1qCTJf8V262xi96Um+dY+pC5/n/2wK/G4dsgN+U/A69mb/9BS94QRzomwfxsilwVMNm0h6M0sJlUwTjxeqJmpDkOM5NodDbDztj2pYZd05ZHPh9i4uJHty5wR3OZQOLQebLvGLv2IkV9TP2ywdccsmEGVuXDaCevGLHJ/HIibHbkq1kLksXmOuGftsee+wx7O/U8UGEJIX8Oh3vefGLX/wAdwFu0TTI+3Y9hDZcTknyorlBSiLq7+olxFcCunDHBd1ujMlBLQb7orvHxAcXno1r+ZlyvslfaVg9sW/gnl11nkOwPYeajEDKhebHXy4M3QVsb71/QVR+8UktFmWtEYpT3MzRvfj0Xzr9DL9R/Zhw3yH4+/HJHQD5P+r48U996lN/no9oJKCxJ4wx5N07eckz6cYcxrjpcPJth2d+LIlOE98Y/Il98MkQX7lsvG+d0kN1LWcrS+/84Wmsc4idfnE4R1Q6kkfw1gmb+dt15yvZZgxp0YFLHNkY+tS94eHG3ljjkMHtXpjkaDcT4Fjlm9zRc6F94hOfwP8nNPxv6GgB1ejq6uskvPdFL3rRi/lIsuwjdwKHQgh3HidR9CmodUrEycLTx3CDpaGny0QYSzYO3fmuARYHtcN84iefFLNQN5zh7XxSwEE1z4yDlWLHFaz2hfQ35WH/m7jg4+Bi4DEAhjtCN897BhgXP360UlvMybjY+eJNf7vAs/+7dcwG2HcAHH9Nx9/55Cc/+bd1F5gvaxZ5gqeg7hUkE44eLpLiysFnWia4FxF+UYQ7kzFt28wVDMVZergdP3aNZfZiOG4GsjNmSLzkPGAbrhd35zVy55C48IaPGlyCV8DD/AB3Dl54xqSTnOBOO1E19HInkOIQrnmtazBfvl1pTRn+TR2z+LZzOrX7Nf4nelP/M3ofONxiwSWAxOSL2vplQ7WLE7/BgU1r2QpIY0OEJ7zqHXPhPYZHukPRo8ImEF3ahmZBdw/ODsRpX/tIH5z12NN2jPYbLJjFFZdDj38f288yd03ZMgnLHdt2yWMHBl7k86j9nd/5HTbBv5Duz+l4cgcO6dYhv0LHv9Engj/Mf6Id0hsmkOBTJBxJdF/dUk3CMo0PeuAkT69j4iDTEjcy/YkblRdfHaE9Tt82dxgr1GURMyfw5KEGzBw6Oa+2mUOnGCfnBj/ruHkPm7l9PU/kxpCHJ0NPS48oDDm4v6kW4NPw40/WHn/88f8k3Vt11PfNAajPhJZqxG+U9E5tgldnE4xlCQqSpJbWootCEj2xsTOOfvfIuf1JNi+9HJ1n/IaoBSA7huSqnOzQtH14UAtjzviq37rBAgOzG/zAt+4kJ/fJKxzwtf/wolObfBiAbz3Da2Mr+7S52RT9eLr7sY99jC99HhHsLToOt/74808s36v9bxnepy+I/piOFz3/+c+HWLFq4iTHQBiJh+d3dLniwj8FRlGul6sRXS8+tBQD3jphrEWjT+GzKof4BpBQLVpVtvNrvkCSQwq9eSOD9XwQ0ggYOX3HG64TZOdK3Pi7hy/+4et+NpLG5mjstZzixxrpi55buvKz+D8g23+O/dw/0wYAy+75GW2Ab9Ez5JV6L+C/ZbODHyYi7LbhT7Muk6Ywa7LxL2AvmnYxPsa1nwshfQqVONuf2sRnF87cOtlXfNdsXDUrJ+N7DP+OgS2xTWWwU3XKDMGPLTy9cIkT8PB3DsPdPI7NvLpFYC7m8KRjFE5rdUtXPs/8/yL123Tcc/Fxe7YNAIY7wb9Vgq/S7eQbCJifXUufhMHRSHDrRl6TsK7z9xWzbEVSCiYpqe4CwfddIoVIQe3X8dHZjk9zD74xk1c7EgjRMS30uOM3zCsbrvSG40tTP9wd3+MTT7DD23YvLMaVezCJF35gBxvf8vHM1x3gx2X4Czp+PYB79c9lA+DLZ4h/paQ+pm+Tvlm76yG+08+3VptcmEtWXcTYT5MyjrnKPj6NnaIxjt+a8Pi0Tl0FC5ae1mrL63SO59XDjp82GfZrmHCCg1fj2qEo1Fqnrly792Ju3y3jxzg+0KCjSSeT5yGx1PFthZVak1t8yaMN8Nuy/w25/lUdH4fj2dpz3QDw8N3wL+h4t3bYg7ob/F7dbh7AwFWZBOlzyOQESbrtuf1qOC9DYA6F3H6xiSO+cLmosrnBRSNOt0PVosdN9gI3Ht/2P1x14Qxh+uhPfTjDv3ONK33soxOPdZsPmYMpCzjc0at3rny1q8/3t/Tt7RNak38q7A/q+Ckd9T2+hGdrIX823E32b5byL+p4i+4EL+Mlka+QuTPwiyUk220WNwsRffcGyraLdigUfosPt3CCc6Gwg4NHKg0nPpBzs5+UiY2D/fBv32s24Y05kzEmbGJuuX2AhC+5ozv4WaETPHDsxliL7F8f0x3Y3+trzDd779Dxj3T8/MY/V/kZq/QcSX6PcG/W8ad1fIPuBq/ijpBNwIb4bNqpeNdcn82Ow8a0fF6483jibN/NddJrWOsYffoQ7fGWY3+uPS+HHLrbupf8W/LlBzks/L/T8UEdn3P7fGyAHfzVGnyNjjfp+BYdX6vjNToe1PHF9tlXgL/Z+LCOX9HB2/zDOnix+00dn5f2fwGNEovHhSlHVwAAAABJRU5ErkJggg==";
|
|
7
|
+
const BASE = "https://opencode.ai/zen/v1";
|
|
8
|
+
const MODELS_URL = `${BASE}/models`;
|
|
9
|
+
const CHAT_URL = `${BASE}/chat/completions`;
|
|
10
|
+
const UA = "opencode";
|
|
11
|
+
/** 免费模型识别:id 以 -free 结尾,或已知不带后缀的免费模型。 */
|
|
12
|
+
const KNOWN_FREE = /* @__PURE__ */ new Set(["big-pickle"]);
|
|
13
|
+
function factory(env) {
|
|
14
|
+
let modelsCache;
|
|
15
|
+
/** 上次 chatOnce 失败原因(供核心测试模型汇总诊断)。 */
|
|
16
|
+
async function refreshModels() {
|
|
17
|
+
try {
|
|
18
|
+
const resp = await fetch(MODELS_URL, {
|
|
19
|
+
headers: {
|
|
20
|
+
"User-Agent": UA,
|
|
21
|
+
"x-opencode-client": "desktop"
|
|
22
|
+
},
|
|
23
|
+
signal: AbortSignal.timeout(15e3)
|
|
24
|
+
});
|
|
25
|
+
if (!resp.ok) return [];
|
|
26
|
+
const raw = (await resp.json()).data ?? [];
|
|
27
|
+
const models = [];
|
|
28
|
+
for (const m of raw) {
|
|
29
|
+
if (m.id === "") continue;
|
|
30
|
+
if (!m.id.endsWith("-free") && !KNOWN_FREE.has(m.id)) continue;
|
|
31
|
+
models.push({ id: m.id });
|
|
32
|
+
}
|
|
33
|
+
if (models.length > 0) modelsCache = models;
|
|
34
|
+
return models;
|
|
35
|
+
} catch {
|
|
36
|
+
return modelsCache ?? [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function allModels(force) {
|
|
40
|
+
return refreshModels();
|
|
41
|
+
}
|
|
42
|
+
/** 当前前缀(与 loader 包装一致:store 覆盖默认值)。 */
|
|
43
|
+
function currentAlias() {
|
|
44
|
+
return env.store.get("opencode").alias || "opencode";
|
|
45
|
+
}
|
|
46
|
+
/** 剥本供应商 alias 前缀(只剥自己的,模型 id 自带的斜杠保留)。 */
|
|
47
|
+
function stripAlias(model, alias) {
|
|
48
|
+
return alias !== "" && model.startsWith(`${alias}/`) ? model.slice(alias.length + 1) : model;
|
|
49
|
+
}
|
|
50
|
+
/** 剥 alias 前缀(oc/xxx → xxx)后判断是否 free 模型。 */
|
|
51
|
+
function isFreeModel(model) {
|
|
52
|
+
const base = stripAlias(model.trim(), currentAlias());
|
|
53
|
+
return base.endsWith("-free") || KNOWN_FREE.has(base);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
id,
|
|
57
|
+
name,
|
|
58
|
+
priority: 0,
|
|
59
|
+
icon,
|
|
60
|
+
status: () => ({
|
|
61
|
+
id,
|
|
62
|
+
name,
|
|
63
|
+
accounts: []
|
|
64
|
+
}),
|
|
65
|
+
listModels: (force) => allModels(!!force),
|
|
66
|
+
/** 无账号供应商:uid 恒为空,只认 free 模型。 */
|
|
67
|
+
async chatOnce(_uid, req) {
|
|
68
|
+
if (!isFreeModel(req.model)) return {
|
|
69
|
+
ok: false,
|
|
70
|
+
state: "no_such_model",
|
|
71
|
+
message: `unknown free model ${JSON.stringify(req.model)}`
|
|
72
|
+
};
|
|
73
|
+
let body = req.rawBody;
|
|
74
|
+
try {
|
|
75
|
+
const obj = JSON.parse(body);
|
|
76
|
+
obj.model = stripAlias(String(obj.model ?? req.model), currentAlias());
|
|
77
|
+
body = JSON.stringify(obj);
|
|
78
|
+
} catch {}
|
|
79
|
+
let upstream;
|
|
80
|
+
try {
|
|
81
|
+
upstream = await fetch(CHAT_URL, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
Authorization: "Bearer public",
|
|
86
|
+
"User-Agent": UA,
|
|
87
|
+
"x-opencode-client": "desktop"
|
|
88
|
+
},
|
|
89
|
+
body,
|
|
90
|
+
signal: AbortSignal.timeout(12e4)
|
|
91
|
+
});
|
|
92
|
+
} catch (err) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
state: "transport",
|
|
96
|
+
message: `opencode upstream: ${err.message}`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (upstream.status < 200 || upstream.status >= 300) {
|
|
100
|
+
const text = await upstream.text().catch(() => "");
|
|
101
|
+
const msg = `upstream ${upstream.status}: ${text.slice(0, 200)}`;
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
state: upstream.status === 429 ? "rate_limit" : upstream.status === 404 ? "unavailable" : "unknown",
|
|
105
|
+
message: msg
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (req.stream) {
|
|
109
|
+
if (!upstream.body) return {
|
|
110
|
+
ok: false,
|
|
111
|
+
state: "transport",
|
|
112
|
+
message: "opencode upstream: empty stream body"
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
stream: upstream.body
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const text = await upstream.text().catch(() => "");
|
|
120
|
+
if (text === "") return {
|
|
121
|
+
ok: false,
|
|
122
|
+
state: "transport",
|
|
123
|
+
message: "opencode upstream: empty body"
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
ok: true,
|
|
127
|
+
status: upstream.status,
|
|
128
|
+
body: text
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
dispose: () => {
|
|
132
|
+
modelsCache = void 0;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { factory as default, icon, id, name, priority };
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
//#region src/suppliers/openrouter/plugin.ts
|
|
2
|
+
const id = "openrouter";
|
|
3
|
+
const name = "OpenRouter";
|
|
4
|
+
const priority = 10;
|
|
5
|
+
/** 面板图标。 */
|
|
6
|
+
const icon = "data:image/png;base64,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";
|
|
7
|
+
const BASE = "https://openrouter.ai/api/v1";
|
|
8
|
+
const MODELS_URL = `${BASE}/models`;
|
|
9
|
+
const CHAT_URL = `${BASE}/chat/completions`;
|
|
10
|
+
/** 默认前缀(用户可在面板改;loader 包装会优先用 store 里的值)。 */
|
|
11
|
+
/** 免费模型过滤(同 9Router openrouter-free):pricing 全 0 且 context ≥ 200k。 */
|
|
12
|
+
function isFreeModelMeta(m) {
|
|
13
|
+
return m.pricing?.prompt === "0" && m.pricing?.completion === "0" && (m.context_length ?? 0) >= 2e5;
|
|
14
|
+
}
|
|
15
|
+
/** 剥 alias 前缀(or/xxx → xxx)。 */
|
|
16
|
+
/** 剥本供应商 alias 前缀(只剥自己的,模型 id 自带的斜杠保留)。 */
|
|
17
|
+
function stripAlias(model, alias) {
|
|
18
|
+
return alias !== "" && model.startsWith(`${alias}/`) ? model.slice(alias.length + 1) : model;
|
|
19
|
+
}
|
|
20
|
+
function factory(env) {
|
|
21
|
+
let modelsCache;
|
|
22
|
+
let freeIds = /* @__PURE__ */ new Set();
|
|
23
|
+
/** 上次 chatOnce 失败原因(供核心测试模型汇总诊断)。 */
|
|
24
|
+
function listKeys() {
|
|
25
|
+
return env.credentials.list(id);
|
|
26
|
+
}
|
|
27
|
+
function getKey(uid) {
|
|
28
|
+
return env.credentials.get(id, uid);
|
|
29
|
+
}
|
|
30
|
+
/** 账号顺序:池顺序优先,未配置按凭证原始顺序。 */
|
|
31
|
+
function orderedKeys() {
|
|
32
|
+
const byUid = new Map(listKeys().map((uid) => [uid, uid]));
|
|
33
|
+
const order = env.store.get(id).poolOrder;
|
|
34
|
+
return [...order.filter((u) => byUid.has(u)), ...listKeys().filter((u) => !order.includes(u))].map((uid) => ({
|
|
35
|
+
uid,
|
|
36
|
+
acct: getKey(uid)
|
|
37
|
+
})).filter((x) => x.acct !== void 0);
|
|
38
|
+
}
|
|
39
|
+
/** 当前前缀(与 loader 包装一致:store 覆盖默认值)。 */
|
|
40
|
+
function currentAlias() {
|
|
41
|
+
return env.store.get("openrouter").alias || "openrouter";
|
|
42
|
+
}
|
|
43
|
+
async function refreshModels() {
|
|
44
|
+
try {
|
|
45
|
+
const resp = await fetch(MODELS_URL, {
|
|
46
|
+
headers: {
|
|
47
|
+
"User-Agent": "dsh-router",
|
|
48
|
+
"HTTP-Referer": "https://endpoint-proxy.local",
|
|
49
|
+
"X-Title": "dsh-router"
|
|
50
|
+
},
|
|
51
|
+
signal: AbortSignal.timeout(15e3)
|
|
52
|
+
});
|
|
53
|
+
if (!resp.ok) return [];
|
|
54
|
+
const raw = (await resp.json()).data ?? [];
|
|
55
|
+
const models = [];
|
|
56
|
+
const ids = /* @__PURE__ */ new Set();
|
|
57
|
+
for (const m of raw) {
|
|
58
|
+
if (m.id === "" || !isFreeModelMeta(m)) continue;
|
|
59
|
+
if (ids.has(m.id)) continue;
|
|
60
|
+
ids.add(m.id);
|
|
61
|
+
const entry = { id: m.id };
|
|
62
|
+
if ((m.context_length ?? 0) > 0) entry.context_length = Math.round((m.context_length ?? 0) / 1e3);
|
|
63
|
+
models.push(entry);
|
|
64
|
+
}
|
|
65
|
+
if (models.length > 0) {
|
|
66
|
+
modelsCache = models;
|
|
67
|
+
freeIds = ids;
|
|
68
|
+
}
|
|
69
|
+
return models;
|
|
70
|
+
} catch {
|
|
71
|
+
return modelsCache ?? [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function allModels(force) {
|
|
75
|
+
return refreshModels();
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
name,
|
|
80
|
+
priority: 10,
|
|
81
|
+
icon,
|
|
82
|
+
status: () => {
|
|
83
|
+
const accounts = orderedKeys().map(({ uid, acct }) => ({
|
|
84
|
+
uid,
|
|
85
|
+
nickname: acct.name || "API Key",
|
|
86
|
+
credits: 0,
|
|
87
|
+
state: "ok"
|
|
88
|
+
}));
|
|
89
|
+
return {
|
|
90
|
+
id,
|
|
91
|
+
name,
|
|
92
|
+
accounts
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
listModels: (force) => allModels(!!force),
|
|
96
|
+
async addApiKey(input) {
|
|
97
|
+
const key = input.apiKey.trim();
|
|
98
|
+
if (key === "") return {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: "API key 不能为空"
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
const probe = await fetch(`${BASE}/key`, {
|
|
104
|
+
headers: {
|
|
105
|
+
Authorization: `Bearer ${key}`,
|
|
106
|
+
"User-Agent": "dsh-router"
|
|
107
|
+
},
|
|
108
|
+
signal: AbortSignal.timeout(15e3)
|
|
109
|
+
});
|
|
110
|
+
if (!probe.ok) {
|
|
111
|
+
const text = await probe.text().catch(() => "");
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: `key 无效: ${probe.status} ${text.slice(0, 200)}`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
error: `验证失败: ${err.message}`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
let n = listKeys().length + 1;
|
|
124
|
+
let uid = `key-${n}`;
|
|
125
|
+
while (getKey(uid) !== void 0) uid = `key-${++n}`;
|
|
126
|
+
env.credentials.save(id, uid, {
|
|
127
|
+
name: input.name.trim() || `Key ${n}`,
|
|
128
|
+
apiKey: key
|
|
129
|
+
});
|
|
130
|
+
env.log(`openrouter add api key ${uid}`);
|
|
131
|
+
return {
|
|
132
|
+
ok: true,
|
|
133
|
+
account: {
|
|
134
|
+
uid,
|
|
135
|
+
nickname: input.name.trim() || `Key ${n}`
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
async removeLink(uid) {
|
|
140
|
+
if (getKey(uid) === void 0) return false;
|
|
141
|
+
env.credentials.remove(id, uid);
|
|
142
|
+
return true;
|
|
143
|
+
},
|
|
144
|
+
/** 对单个 key 调一次上游。选号/冷却/换号是核心的活,这里只报结果。 */
|
|
145
|
+
async chatOnce(uid, req) {
|
|
146
|
+
const base = stripAlias(req.model, currentAlias());
|
|
147
|
+
if (!freeIds.has(base) && !(await allModels(false)).some((m) => m.id === base)) return {
|
|
148
|
+
ok: false,
|
|
149
|
+
state: "no_such_model",
|
|
150
|
+
message: `unknown free model ${JSON.stringify(req.model)}`
|
|
151
|
+
};
|
|
152
|
+
const acct = getKey(uid);
|
|
153
|
+
if (acct === void 0) return {
|
|
154
|
+
ok: false,
|
|
155
|
+
state: "no_such_model",
|
|
156
|
+
message: `unknown account ${JSON.stringify(uid)}`
|
|
157
|
+
};
|
|
158
|
+
let body = req.rawBody;
|
|
159
|
+
try {
|
|
160
|
+
const obj = JSON.parse(body);
|
|
161
|
+
obj.model = base;
|
|
162
|
+
body = JSON.stringify(obj);
|
|
163
|
+
} catch {}
|
|
164
|
+
let upstream;
|
|
165
|
+
try {
|
|
166
|
+
upstream = await fetch(CHAT_URL, {
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "application/json",
|
|
170
|
+
Authorization: `Bearer ${acct.apiKey}`,
|
|
171
|
+
"HTTP-Referer": "https://endpoint-proxy.local",
|
|
172
|
+
"X-Title": "dsh-router"
|
|
173
|
+
},
|
|
174
|
+
body,
|
|
175
|
+
signal: AbortSignal.timeout(12e4)
|
|
176
|
+
});
|
|
177
|
+
} catch (err) {
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
state: "transport",
|
|
181
|
+
message: err.message
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (upstream.status < 200 || upstream.status >= 300) {
|
|
185
|
+
const text = await upstream.text().catch(() => "");
|
|
186
|
+
const msg = `upstream ${upstream.status}: ${text.slice(0, 120)}`;
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
state: upstream.status === 429 ? "rate_limit" : upstream.status === 401 || upstream.status === 403 ? "session_dead" : upstream.status === 404 ? "unavailable" : "unknown",
|
|
190
|
+
message: msg
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (req.stream) {
|
|
194
|
+
if (!upstream.body) return {
|
|
195
|
+
ok: false,
|
|
196
|
+
state: "transport",
|
|
197
|
+
message: "openrouter upstream: empty stream body"
|
|
198
|
+
};
|
|
199
|
+
return {
|
|
200
|
+
ok: true,
|
|
201
|
+
stream: upstream.body
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const text = await upstream.text().catch(() => "");
|
|
205
|
+
if (text === "") return {
|
|
206
|
+
ok: false,
|
|
207
|
+
state: "transport",
|
|
208
|
+
message: "openrouter upstream: empty body"
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
ok: true,
|
|
212
|
+
status: upstream.status,
|
|
213
|
+
body: text
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
dispose: () => {
|
|
217
|
+
modelsCache = void 0;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
export { factory as default, icon, id, name, priority };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Plugin identity for cordis.yml rows — 必须与 package.json 的 name 一致。
|
|
3
|
+
* loader 拿配置行里的 `name` 去 `import(name)`,写错就是从 npm 装完加载不到。
|
|
4
|
+
*/
|
|
5
|
+
export declare const name = "dsh-router-core";
|
|
3
6
|
/** Services required before mounting: the webserver (routes) + llm (设置-模型). */
|
|
4
7
|
export declare const inject: string[];
|
|
5
8
|
/** Plugin body. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-router-core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "DSH plugin: a simplified 9router (AI routing gateway). Adds a 路由系统 (Routing System) sidebar entry beside 记忆系统; the panel shows suppliers, account pool, combos, and endpoint/API keys.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"lib/index.js",
|
|
46
46
|
"lib/client.js",
|
|
47
47
|
"lib/client-registry.js",
|
|
48
|
+
"lib/suppliers/*.js",
|
|
48
49
|
"lib/types/**/*.d.ts",
|
|
49
50
|
"src",
|
|
50
51
|
"cordis.patch.yml",
|
|
@@ -53,17 +54,19 @@
|
|
|
53
54
|
"LICENSE"
|
|
54
55
|
],
|
|
55
56
|
"scripts": {
|
|
56
|
-
"build": "node scripts/check-css.mjs && rm -rf lib && tsc -p tsconfig.build.json && tsdown && npm run build:client",
|
|
57
|
-
"build:client": "DSH_CLIENT_ID=dsh-router DSH_CLIENT_FILE=client.js vite build -c client/vite.config.ts && DSH_CLIENT_ID=dsh-external/dsh-router DSH_CLIENT_FILE=client-registry.js vite build -c client/vite.config.ts",
|
|
57
|
+
"build": "node scripts/check-css.mjs && node scripts/check-publish.mjs && rm -rf lib && tsc -p tsconfig.build.json && tsdown && npm run build:client && node scripts/check-publish.mjs",
|
|
58
|
+
"build:client": "DSH_CLIENT_ID=dsh-router-core DSH_CLIENT_FILE=client.js vite build -c client/vite.config.ts && DSH_CLIENT_ID=dsh-external/dsh-router DSH_CLIENT_FILE=client-registry.js vite build -c client/vite.config.ts",
|
|
58
59
|
"typecheck": "tsc --noEmit",
|
|
59
60
|
"test": "node --test \"src/**/*.test.ts\"",
|
|
60
61
|
"bundle": "tsdown",
|
|
61
62
|
"watch": "tsdown --watch",
|
|
62
|
-
"check:css": "node scripts/check-css.mjs"
|
|
63
|
+
"check:css": "node scripts/check-css.mjs",
|
|
64
|
+
"check:publish": "node scripts/check-publish.mjs --pack",
|
|
65
|
+
"prepublishOnly": "npm run build && node scripts/check-publish.mjs --pack"
|
|
63
66
|
},
|
|
64
67
|
"license": "MIT",
|
|
65
68
|
"peerDependencies": {
|
|
66
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
69
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
67
70
|
"@deepseek-ai/dsh-client-locale": "*",
|
|
68
71
|
"@deepseek-ai/dsh-client-runtime": "*",
|
|
69
72
|
"@deepseek-ai/dsh-client-ui-slots": "*",
|
|
@@ -87,14 +90,14 @@
|
|
|
87
90
|
}
|
|
88
91
|
},
|
|
89
92
|
"devDependencies": {
|
|
90
|
-
"@types/node": "^24.
|
|
91
|
-
"@types/react": "~18.3.
|
|
92
|
-
"@types/react-dom": "~18.3.
|
|
93
|
-
"cordis": "^4.0.0-rc.
|
|
94
|
-
"react": "^18.
|
|
93
|
+
"@types/node": "^24.13.3",
|
|
94
|
+
"@types/react": "~18.3.31",
|
|
95
|
+
"@types/react-dom": "~18.3.7",
|
|
96
|
+
"cordis": "^4.0.0-rc.9",
|
|
97
|
+
"react": "^18.3.1",
|
|
95
98
|
"react-dom": "18.2.0",
|
|
96
|
-
"tsdown": "^0.22.
|
|
97
|
-
"typescript": "^5.
|
|
99
|
+
"tsdown": "^0.22.14",
|
|
100
|
+
"typescript": "^5.9.3",
|
|
98
101
|
"vite": "8.1.3",
|
|
99
102
|
"vite-plugin-css-injected-by-js": "^3.5.2"
|
|
100
103
|
},
|
package/src/index.ts
CHANGED
|
@@ -41,8 +41,11 @@ import type { SupplierEnv, SupplierModule } from './suppliers/contract.ts'
|
|
|
41
41
|
import { SupplierConfigStore } from './supplier-config.ts'
|
|
42
42
|
import { CredentialStore } from './credential-store.ts'
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Plugin identity for cordis.yml rows — 必须与 package.json 的 name 一致。
|
|
46
|
+
* loader 拿配置行里的 `name` 去 `import(name)`,写错就是从 npm 装完加载不到。
|
|
47
|
+
*/
|
|
48
|
+
export const name = 'dsh-router-core'
|
|
46
49
|
|
|
47
50
|
/** Services required before mounting: the webserver (routes) + llm (设置-模型). */
|
|
48
51
|
export const inject = ['webServer', 'llm']
|