dsh-qoder-connect 0.1.4 → 0.1.8
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/README.en.md +13 -0
- package/README.md +13 -0
- package/assets/7.png +0 -0
- package/lib/bin.js +1 -1
- package/lib/client.js +739 -5
- package/lib/index.d.ts +97 -4
- package/lib/index.js +508 -41
- package/lib/{variants-CSeJqJpR.js → variants-DE76gF0Y.js} +186 -2
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as QODER_GLOBAL_AUTH_PATH, B as qoderCredentialIdentity, C as probeModel, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, F as QODER_AUTH_FILENAME, G as qoderPluginDataDir, H as QODER_DATA_DIR_ENV, I as QODER_PAT_ENV_CN, K as qoderStateDir, L as QODER_PAT_ENV_GLOBAL, M as QODER_GLOBAL_STATUS_PATH, N as QODER_PROBE_PATH, O as QoderCatalog, P as QODER_STATUS_PATH, R as QoderCredentialStore, S as PROBE_EFFORT_CANDIDATES, T as QoderCatalogStore, U as QODER_DATA_DIR_NAME, V as qoderOwnAuthPath, W as qoderMachineIdPath, _ as modelInfoOf, a as QODER_HOST_HEARTBEAT_FILENAME, b as createQoderTransport, c as processStartTimeMs, d as writeHostHeartbeat, f as QODER_CONNECT_VERSION, g as kindFromQoderFailure, h as classifyUpstreamError, i as variantFor, j as QODER_GLOBAL_PROBE_PATH, k as QODER_AUTH_PATH, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, n as GLOBAL_VARIANT, o as clearHostHeartbeat, p as KIND_STATUS, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, v as normalizeCredits, w as randomSentinel, x as getMachineId, y as validateApiKey, z as patTail } from "./variants-
|
|
1
|
+
import { A as QODER_GLOBAL_AUTH_PATH, B as qoderCredentialIdentity, C as probeModel, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, F as QODER_AUTH_FILENAME, G as qoderPluginDataDir, H as QODER_DATA_DIR_ENV, I as QODER_PAT_ENV_CN, K as qoderStateDir, L as QODER_PAT_ENV_GLOBAL, M as QODER_GLOBAL_STATUS_PATH, N as QODER_PROBE_PATH, O as QoderCatalog, P as QODER_STATUS_PATH, R as QoderCredentialStore, S as PROBE_EFFORT_CANDIDATES, T as QoderCatalogStore, U as QODER_DATA_DIR_NAME, V as qoderOwnAuthPath, W as qoderMachineIdPath, _ as modelInfoOf, a as QODER_HOST_HEARTBEAT_FILENAME, b as createQoderTransport, c as processStartTimeMs, d as writeHostHeartbeat, f as QODER_CONNECT_VERSION, g as kindFromQoderFailure, h as classifyUpstreamError, i as variantFor, j as QODER_GLOBAL_PROBE_PATH, k as QODER_AUTH_PATH, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, n as GLOBAL_VARIANT, o as clearHostHeartbeat, p as KIND_STATUS, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, v as normalizeCredits, w as randomSentinel, x as getMachineId, y as validateApiKey, z as patTail } from "./variants-DE76gF0Y.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -1049,22 +1049,28 @@ async function qoderWebStatus(deps) {
|
|
|
1049
1049
|
...statusWithModels,
|
|
1050
1050
|
probe: deps.probe(),
|
|
1051
1051
|
...deps.probeKey === void 0 ? {} : { probeKey: deps.probeKey },
|
|
1052
|
-
...deps.useMaximumContextWindow === void 0 ? {} : { useMaximumContextWindow: deps.useMaximumContextWindow() }
|
|
1052
|
+
...deps.useMaximumContextWindow === void 0 ? {} : { useMaximumContextWindow: deps.useMaximumContextWindow() },
|
|
1053
|
+
...deps.disabledModels === void 0 ? {} : { disabledModels: deps.disabledModels() }
|
|
1053
1054
|
};
|
|
1054
1055
|
const refreshedAt = deps.jobTokenRefreshedAt?.();
|
|
1055
1056
|
const withRefreshNotice = refreshedAt === void 0 ? probed : {
|
|
1056
1057
|
...probed,
|
|
1057
1058
|
jobTokenRefreshedAt: refreshedAt
|
|
1058
1059
|
};
|
|
1060
|
+
const checkInRecord = deps.checkIn?.();
|
|
1061
|
+
const withCheckIn = checkInRecord === void 0 ? withRefreshNotice : {
|
|
1062
|
+
...withRefreshNotice,
|
|
1063
|
+
checkIn: checkInRecord
|
|
1064
|
+
};
|
|
1059
1065
|
try {
|
|
1060
1066
|
const credits = await deps.client.fetchCredits();
|
|
1061
1067
|
return {
|
|
1062
|
-
...
|
|
1068
|
+
...withCheckIn,
|
|
1063
1069
|
credits
|
|
1064
1070
|
};
|
|
1065
1071
|
} catch (error) {
|
|
1066
1072
|
return {
|
|
1067
|
-
...
|
|
1073
|
+
...withCheckIn,
|
|
1068
1074
|
creditsError: safeMessage(error)
|
|
1069
1075
|
};
|
|
1070
1076
|
}
|
|
@@ -1169,11 +1175,27 @@ function parseAction(text) {
|
|
|
1169
1175
|
const wrapped = parsed;
|
|
1170
1176
|
const action = wrapped["action"];
|
|
1171
1177
|
if (action === "clear") return { action: "clear" };
|
|
1178
|
+
if (action === "clear-checkin-logs") return { action: "clear-checkin-logs" };
|
|
1179
|
+
if (action === "checkin") return { action: "checkin" };
|
|
1172
1180
|
if (action === "refresh") return { action: "refresh" };
|
|
1173
1181
|
if (action === "set-maximum-context-window") return typeof wrapped["enabled"] === "boolean" ? {
|
|
1174
1182
|
action: "set-maximum-context-window",
|
|
1175
1183
|
enabled: wrapped["enabled"]
|
|
1176
1184
|
} : void 0;
|
|
1185
|
+
if (action === "set-models-enabled") {
|
|
1186
|
+
if (typeof wrapped["enabled"] !== "boolean") return void 0;
|
|
1187
|
+
const rawModels = wrapped["models"];
|
|
1188
|
+
const rawModel = wrapped["model"];
|
|
1189
|
+
let models = [];
|
|
1190
|
+
if (Array.isArray(rawModels)) models = rawModels.filter((m) => typeof m === "string" && m.trim() !== "").map((m) => m.trim());
|
|
1191
|
+
else if (typeof rawModel === "string" && rawModel.trim() !== "") models = [rawModel.trim()];
|
|
1192
|
+
if (models.length === 0) return void 0;
|
|
1193
|
+
return {
|
|
1194
|
+
action: "set-models-enabled",
|
|
1195
|
+
models,
|
|
1196
|
+
enabled: wrapped["enabled"]
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1177
1199
|
if (action === "probe") {
|
|
1178
1200
|
const model = wrapped["model"];
|
|
1179
1201
|
if (typeof model !== "string" || model.trim() === "") return void 0;
|
|
@@ -1212,6 +1234,19 @@ function qoderProbeHandler(deps, key) {
|
|
|
1212
1234
|
return;
|
|
1213
1235
|
}
|
|
1214
1236
|
try {
|
|
1237
|
+
if (action.action === "clear-checkin-logs") {
|
|
1238
|
+
deps.clearCheckInLogs?.();
|
|
1239
|
+
json(res, 200, { state: "cleared" });
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
if (action.action === "checkin") {
|
|
1243
|
+
if (deps.checkIn === void 0) {
|
|
1244
|
+
json(res, 404, { error: "checkin-not-supported" });
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1247
|
+
json(res, 200, await deps.checkIn());
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1215
1250
|
if (action.action === "clear") {
|
|
1216
1251
|
deps.clear();
|
|
1217
1252
|
json(res, 200, { state: "cleared" });
|
|
@@ -1233,6 +1268,17 @@ function qoderProbeHandler(deps, key) {
|
|
|
1233
1268
|
json(res, 200, await deps.setMaximumContextWindow(action.enabled === true));
|
|
1234
1269
|
return;
|
|
1235
1270
|
}
|
|
1271
|
+
if (action.action === "set-models-enabled") {
|
|
1272
|
+
if (deps.setModelsEnabled === void 0) {
|
|
1273
|
+
json(res, 404, { error: "models-enabled-setting-not-supported" });
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
json(res, 200, await deps.setModelsEnabled({
|
|
1277
|
+
models: action.models ?? (action.model ? [action.model] : []),
|
|
1278
|
+
enabled: action.enabled === true
|
|
1279
|
+
}));
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1236
1282
|
json(res, 200, await deps.probe(action.model));
|
|
1237
1283
|
} catch (error) {
|
|
1238
1284
|
json(res, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -1254,6 +1300,244 @@ function registerQoderProbeRoute(ctx, deps, key) {
|
|
|
1254
1300
|
}, "dsh-qoder-connect: probe control route");
|
|
1255
1301
|
}
|
|
1256
1302
|
//#endregion
|
|
1303
|
+
//#region src/checkin-scheduler.ts
|
|
1304
|
+
/** Scheduling and catch-up orchestration for daily 10:00 (UTC+8) check-in. */
|
|
1305
|
+
var JsonFileCheckInStore = class {
|
|
1306
|
+
filePath;
|
|
1307
|
+
constructor(filePath) {
|
|
1308
|
+
this.filePath = filePath ?? join(qoderPluginDataDir(), "checkin-status.json");
|
|
1309
|
+
}
|
|
1310
|
+
readAll() {
|
|
1311
|
+
try {
|
|
1312
|
+
if (!existsSync(this.filePath)) return {};
|
|
1313
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
1314
|
+
return JSON.parse(raw);
|
|
1315
|
+
} catch {
|
|
1316
|
+
return {};
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
read(variantId) {
|
|
1320
|
+
return this.readAll()[variantId];
|
|
1321
|
+
}
|
|
1322
|
+
clearLogs(variantId) {
|
|
1323
|
+
try {
|
|
1324
|
+
const all = this.readAll();
|
|
1325
|
+
if (all[variantId]) {
|
|
1326
|
+
all[variantId] = {
|
|
1327
|
+
...all[variantId],
|
|
1328
|
+
logs: []
|
|
1329
|
+
};
|
|
1330
|
+
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
1331
|
+
writeFileSync(this.filePath, JSON.stringify(all, null, 2), "utf-8");
|
|
1332
|
+
}
|
|
1333
|
+
} catch {}
|
|
1334
|
+
}
|
|
1335
|
+
write(variantId, record) {
|
|
1336
|
+
try {
|
|
1337
|
+
const all = this.readAll();
|
|
1338
|
+
const existingLogs = all[variantId]?.logs ?? [];
|
|
1339
|
+
const newLog = {
|
|
1340
|
+
id: `${record.lastDate}-${record.lastAt}`,
|
|
1341
|
+
date: record.lastDate,
|
|
1342
|
+
timestamp: record.lastAt,
|
|
1343
|
+
status: record.status,
|
|
1344
|
+
...record.amount === void 0 ? {} : { amount: record.amount },
|
|
1345
|
+
...record.message === void 0 ? {} : { message: record.message }
|
|
1346
|
+
};
|
|
1347
|
+
const updatedLogs = [newLog, ...existingLogs.filter((l) => l.id !== newLog.id)].slice(0, 30);
|
|
1348
|
+
all[variantId] = {
|
|
1349
|
+
...record,
|
|
1350
|
+
logs: updatedLogs
|
|
1351
|
+
};
|
|
1352
|
+
mkdirSync(dirname(this.filePath), { recursive: true });
|
|
1353
|
+
writeFileSync(this.filePath, JSON.stringify(all, null, 2), "utf-8");
|
|
1354
|
+
} catch {}
|
|
1355
|
+
}
|
|
1356
|
+
};
|
|
1357
|
+
/**
|
|
1358
|
+
* Returns the current date in YYYY-MM-DD standardized on UTC+8 (Beijing Time).
|
|
1359
|
+
*/
|
|
1360
|
+
function getUtc8DateString(nowMs = Date.now()) {
|
|
1361
|
+
const d = new Date(nowMs);
|
|
1362
|
+
const utc8 = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1363
|
+
return `${utc8.getFullYear()}-${String(utc8.getMonth() + 1).padStart(2, "0")}-${String(utc8.getDate()).padStart(2, "0")}`;
|
|
1364
|
+
}
|
|
1365
|
+
/** Clamp any stored/typed value onto a real minute of the day. */
|
|
1366
|
+
function normalizeCheckInMinute(value) {
|
|
1367
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 600;
|
|
1368
|
+
const whole = Math.trunc(value);
|
|
1369
|
+
if (whole < 0 || whole > 1439) return 600;
|
|
1370
|
+
return whole;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Calculates milliseconds until the next occurrence of `minuteOfDay` (UTC+8).
|
|
1374
|
+
*
|
|
1375
|
+
* Five seconds past the configured minute are used so the request lands after
|
|
1376
|
+
* the upstream has flipped the day over rather than on the boundary itself.
|
|
1377
|
+
*/
|
|
1378
|
+
function msUntilNextCheckIn(minuteOfDay, nowMs = Date.now()) {
|
|
1379
|
+
const minute = normalizeCheckInMinute(minuteOfDay);
|
|
1380
|
+
const d = new Date(nowMs);
|
|
1381
|
+
const utc8Time = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1382
|
+
const targetUtc8 = new Date(utc8Time.getTime());
|
|
1383
|
+
targetUtc8.setHours(Math.floor(minute / 60), minute % 60, 5, 0);
|
|
1384
|
+
let diff = targetUtc8.getTime() - utc8Time.getTime();
|
|
1385
|
+
if (diff <= 0) {
|
|
1386
|
+
targetUtc8.setDate(targetUtc8.getDate() + 1);
|
|
1387
|
+
diff = targetUtc8.getTime() - utc8Time.getTime();
|
|
1388
|
+
}
|
|
1389
|
+
return diff;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Whether today's configured check-in moment (UTC+8) has already passed.
|
|
1393
|
+
*/
|
|
1394
|
+
function isPastCheckInTime(minuteOfDay, nowMs = Date.now()) {
|
|
1395
|
+
const minute = normalizeCheckInMinute(minuteOfDay);
|
|
1396
|
+
const d = new Date(nowMs);
|
|
1397
|
+
const utc8 = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1398
|
+
return utc8.getHours() * 60 + utc8.getMinutes() >= minute;
|
|
1399
|
+
}
|
|
1400
|
+
var CheckInScheduler = class {
|
|
1401
|
+
targets;
|
|
1402
|
+
isEnabled;
|
|
1403
|
+
store;
|
|
1404
|
+
onResult;
|
|
1405
|
+
now;
|
|
1406
|
+
/**
|
|
1407
|
+
* One timer per enabled variant, keyed by variant id.
|
|
1408
|
+
*
|
|
1409
|
+
* Per-variant rather than one shared timer because the two products may be
|
|
1410
|
+
* configured to different moments; a single timer would have to wake for the
|
|
1411
|
+
* earliest and then decide who was due, which is the same bookkeeping with a
|
|
1412
|
+
* worse failure mode.
|
|
1413
|
+
*/
|
|
1414
|
+
timers = /* @__PURE__ */ new Map();
|
|
1415
|
+
/**
|
|
1416
|
+
* Variants with a sweep in progress, so concurrent callers cannot double-claim.
|
|
1417
|
+
*/
|
|
1418
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
1419
|
+
/** When each variant's timer is next due, epoch ms, for the card to show. */
|
|
1420
|
+
nextRuns = /* @__PURE__ */ new Map();
|
|
1421
|
+
disposed = false;
|
|
1422
|
+
constructor(options) {
|
|
1423
|
+
this.targets = options.targets;
|
|
1424
|
+
this.isEnabled = options.isEnabled;
|
|
1425
|
+
this.store = options.store ?? new JsonFileCheckInStore();
|
|
1426
|
+
this.onResult = options.onResult;
|
|
1427
|
+
this.now = options.now ?? Date.now;
|
|
1428
|
+
}
|
|
1429
|
+
start() {
|
|
1430
|
+
if (this.disposed) return;
|
|
1431
|
+
this.sweepAll(true);
|
|
1432
|
+
this.rearm();
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Re-run the startup catch-up once the toggles are actually readable.
|
|
1436
|
+
*
|
|
1437
|
+
* `start()` runs while the plugin is still assembling, before the settings
|
|
1438
|
+
* section that owns these toggles has handed over its stored values, so that
|
|
1439
|
+
* first sweep sees the raw plugin config and skips the day. The host calls
|
|
1440
|
+
* this again from the section's source callback; the sweep is idempotent, so
|
|
1441
|
+
* a day already handled costs nothing.
|
|
1442
|
+
*/
|
|
1443
|
+
catchUp() {
|
|
1444
|
+
if (this.disposed) return;
|
|
1445
|
+
this.sweepAll(true);
|
|
1446
|
+
}
|
|
1447
|
+
dispose() {
|
|
1448
|
+
this.disposed = true;
|
|
1449
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
1450
|
+
this.timers.clear();
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Re-place every variant's timer.
|
|
1454
|
+
*
|
|
1455
|
+
* Called after each fire and whenever configuration lands or changes, so a
|
|
1456
|
+
* user editing the time on the card does not have to restart DSH for it to
|
|
1457
|
+
* take effect.
|
|
1458
|
+
*
|
|
1459
|
+
* A timer is placed for every target, including variants whose toggle is
|
|
1460
|
+
* currently off. `start()` runs while the plugin is still assembling, when
|
|
1461
|
+
* the stored toggles are not readable yet, so gating placement on
|
|
1462
|
+
* `isEnabled` left a variant with NO timer at all — and nothing re-armed it
|
|
1463
|
+
* afterwards, which is precisely how a configured 12:13 check-in never
|
|
1464
|
+
* fired. Whether the work is due is decided inside the sweep, where the
|
|
1465
|
+
* configuration is current; the timer only decides when to look.
|
|
1466
|
+
*/
|
|
1467
|
+
rearm() {
|
|
1468
|
+
if (this.disposed) return;
|
|
1469
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
1470
|
+
this.timers.clear();
|
|
1471
|
+
this.nextRuns.clear();
|
|
1472
|
+
const nowMs = this.now();
|
|
1473
|
+
for (const target of this.targets) {
|
|
1474
|
+
const delay = msUntilNextCheckIn(target.minuteOfDay(), nowMs);
|
|
1475
|
+
this.nextRuns.set(target.variantId, nowMs + delay);
|
|
1476
|
+
const timer = setTimeout(() => {
|
|
1477
|
+
this.timers.delete(target.variantId);
|
|
1478
|
+
this.sweepAll(false, target.variantId).finally(() => {
|
|
1479
|
+
this.rearm();
|
|
1480
|
+
});
|
|
1481
|
+
}, delay);
|
|
1482
|
+
timer.unref?.();
|
|
1483
|
+
this.timers.set(target.variantId, timer);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
/** When this variant's timer is next due, epoch ms; absent before first arm. */
|
|
1487
|
+
nextRunAt(variantId) {
|
|
1488
|
+
return this.nextRuns.get(variantId);
|
|
1489
|
+
}
|
|
1490
|
+
async sweepAll(isCatchUp, only) {
|
|
1491
|
+
if (this.disposed) return;
|
|
1492
|
+
const nowMs = this.now();
|
|
1493
|
+
const today = getUtc8DateString(nowMs);
|
|
1494
|
+
for (const target of this.targets) {
|
|
1495
|
+
if (only !== void 0 && target.variantId !== only) continue;
|
|
1496
|
+
if (!this.isEnabled(target.variantId)) continue;
|
|
1497
|
+
if (this.inFlight.has(target.variantId)) continue;
|
|
1498
|
+
this.inFlight.add(target.variantId);
|
|
1499
|
+
try {
|
|
1500
|
+
await this.sweepOne(target, isCatchUp, nowMs, today);
|
|
1501
|
+
} finally {
|
|
1502
|
+
this.inFlight.delete(target.variantId);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
async sweepOne(target, isCatchUp, nowMs, today) {
|
|
1507
|
+
const record = this.store.read(target.variantId);
|
|
1508
|
+
if (record?.lastDate === today && (record.status === "claimed" || record.status === "already-claimed")) {
|
|
1509
|
+
if (!isCatchUp) this.store.write(target.variantId, {
|
|
1510
|
+
lastDate: today,
|
|
1511
|
+
lastAt: nowMs,
|
|
1512
|
+
status: "already-claimed",
|
|
1513
|
+
...record.amount === void 0 ? {} : { amount: record.amount },
|
|
1514
|
+
message: "Scheduled check-in ran; today was already claimed"
|
|
1515
|
+
});
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (isCatchUp && !isPastCheckInTime(target.minuteOfDay(), nowMs)) return;
|
|
1519
|
+
let result;
|
|
1520
|
+
try {
|
|
1521
|
+
result = await target.checkIn();
|
|
1522
|
+
} catch {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
try {
|
|
1526
|
+
if (result.status !== "error") {
|
|
1527
|
+
this.store.write(target.variantId, {
|
|
1528
|
+
lastDate: result.date,
|
|
1529
|
+
lastAt: result.timestamp,
|
|
1530
|
+
status: result.status,
|
|
1531
|
+
amount: result.amount,
|
|
1532
|
+
message: result.message
|
|
1533
|
+
});
|
|
1534
|
+
if (result.status === "claimed") target.onClaimed?.();
|
|
1535
|
+
}
|
|
1536
|
+
this.onResult?.(result);
|
|
1537
|
+
} catch {}
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
//#endregion
|
|
1257
1541
|
//#region src/job-token-hint.ts
|
|
1258
1542
|
/** The name recorded on the hint row (and therefore its displayed title). */
|
|
1259
1543
|
const JOB_TOKEN_HINT_NAME = "qoder";
|
|
@@ -1421,8 +1705,21 @@ const MAXIMUM_CONTEXT_WINDOW_CN_FIELD = z.boolean().default(true).description("U
|
|
|
1421
1705
|
* overrides" rather than a default object the host must keep in sync.
|
|
1422
1706
|
*/
|
|
1423
1707
|
const MODEL_CONTEXT_WINDOWS_FIELD = z.dict(z.number().min(1), z.string()).description("Per-model context-window overrides for Qoder Global (model id → tokens)");
|
|
1708
|
+
/** Disabled models list (blacklist) */
|
|
1709
|
+
const DISABLED_MODELS_FIELD = z.array(z.string()).default([]).description("Models hidden from the provider catalog and picker");
|
|
1424
1710
|
/** Sidebar quota toggle (one per variant; both live on the shared quota card). */
|
|
1425
1711
|
const QUOTA_TOGGLE_FIELD = z.boolean().default(false).description("Show this variant’s remaining-credit card in the sidebar footer (off by default)");
|
|
1712
|
+
/** Automatic check-in toggle. */
|
|
1713
|
+
const AUTO_CHECK_IN_FIELD = z.boolean().default(false).description("每天自动签到领取算力额度(默认关闭)");
|
|
1714
|
+
/**
|
|
1715
|
+
* When a variant checks in, as minutes past midnight in UTC+8.
|
|
1716
|
+
*
|
|
1717
|
+
* Stored as a plain minute count rather than a "HH:mm" string so the schema
|
|
1718
|
+
* itself rejects an impossible time: a browser time input yields 0–1439, and
|
|
1719
|
+
* anything outside that range fails Host validation instead of silently
|
|
1720
|
+
* scheduling a request at a moment that never arrives.
|
|
1721
|
+
*/
|
|
1722
|
+
const CHECK_IN_MINUTE_FIELD = z.number().default(600).min(0).max(1439).description("每日自动签到的时刻(自 UTC+8 午夜起的分钟数,600 = 10:00)");
|
|
1426
1723
|
/**
|
|
1427
1724
|
* Quota poll interval: default 5 minutes, floor 1 minute. The status route
|
|
1428
1725
|
* performs a live upstream billing call per request with no cache, so an
|
|
@@ -1441,6 +1738,10 @@ const Config = z.object({
|
|
|
1441
1738
|
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1442
1739
|
sidebarQuotaCN: QUOTA_TOGGLE_FIELD,
|
|
1443
1740
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1741
|
+
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1742
|
+
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1743
|
+
checkInMinuteCN: CHECK_IN_MINUTE_FIELD,
|
|
1744
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_FIELD,
|
|
1444
1745
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1445
1746
|
});
|
|
1446
1747
|
/**
|
|
@@ -1455,12 +1756,14 @@ const Config = z.object({
|
|
|
1455
1756
|
const CHINA_SECTION = z.object({
|
|
1456
1757
|
probeConsent: PROBE_CONSENT_FIELD,
|
|
1457
1758
|
useMaximumContextWindowCN: MAXIMUM_CONTEXT_WINDOW_CN_FIELD,
|
|
1458
|
-
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD
|
|
1759
|
+
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1760
|
+
disabledModelsCN: DISABLED_MODELS_FIELD
|
|
1459
1761
|
});
|
|
1460
1762
|
/** The global card's settings section and its context-window preferences. */
|
|
1461
1763
|
const GLOBAL_SECTION = z.object({
|
|
1462
1764
|
useMaximumContextWindow: MAXIMUM_CONTEXT_WINDOW_FIELD,
|
|
1463
|
-
modelContextWindows: MODEL_CONTEXT_WINDOWS_FIELD
|
|
1765
|
+
modelContextWindows: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1766
|
+
disabledModels: DISABLED_MODELS_FIELD
|
|
1464
1767
|
});
|
|
1465
1768
|
/**
|
|
1466
1769
|
* The shared quota card's section: both sidebar toggles and the poll interval.
|
|
@@ -1472,8 +1775,51 @@ const GLOBAL_SECTION = z.object({
|
|
|
1472
1775
|
const QUOTA_SECTION = z.object({
|
|
1473
1776
|
sidebarQuotaCN: QUOTA_TOGGLE_FIELD,
|
|
1474
1777
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1778
|
+
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1779
|
+
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1780
|
+
checkInMinuteCN: CHECK_IN_MINUTE_FIELD,
|
|
1781
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_FIELD,
|
|
1475
1782
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1476
1783
|
});
|
|
1784
|
+
/**
|
|
1785
|
+
* Every field each settings section owns, and therefore every field the live
|
|
1786
|
+
* configuration has to carry through.
|
|
1787
|
+
*
|
|
1788
|
+
* One list per section, shared by the merge and by the test that pins it to
|
|
1789
|
+
* the schema. Writing the merge out by hand is what broke auto check-in: the
|
|
1790
|
+
* `qoder-quota` section grew four fields (`autoCheckInCN`, `autoCheckInGlobal`,
|
|
1791
|
+
* `checkInMinuteCN`, `checkInMinuteGlobal`) while the merge kept copying only
|
|
1792
|
+
* the three that predated them, so `current().autoCheckInCN` read `undefined`
|
|
1793
|
+
* forever and the scheduler saw the toggle as permanently off. The card saved
|
|
1794
|
+
* it, the file held it, and nothing ever acted on it.
|
|
1795
|
+
*/
|
|
1796
|
+
const CN_SECTION_KEYS = [
|
|
1797
|
+
"probeConsent",
|
|
1798
|
+
"useMaximumContextWindowCN",
|
|
1799
|
+
"modelContextWindowsCN",
|
|
1800
|
+
"disabledModelsCN"
|
|
1801
|
+
];
|
|
1802
|
+
const GLOBAL_SECTION_KEYS = [
|
|
1803
|
+
"useMaximumContextWindow",
|
|
1804
|
+
"modelContextWindows",
|
|
1805
|
+
"disabledModels"
|
|
1806
|
+
];
|
|
1807
|
+
const QUOTA_SECTION_KEYS = [
|
|
1808
|
+
"sidebarQuotaCN",
|
|
1809
|
+
"sidebarQuotaGlobal",
|
|
1810
|
+
"autoCheckInCN",
|
|
1811
|
+
"autoCheckInGlobal",
|
|
1812
|
+
"checkInMinuteCN",
|
|
1813
|
+
"checkInMinuteGlobal",
|
|
1814
|
+
"quotaPollMs"
|
|
1815
|
+
];
|
|
1816
|
+
/** Copy the declared fields off one section's source, skipping absent ones. */
|
|
1817
|
+
function pickFields(source, keys) {
|
|
1818
|
+
const value = source();
|
|
1819
|
+
const picked = {};
|
|
1820
|
+
for (const key of keys) if (value[key] !== void 0) picked[key] = value[key];
|
|
1821
|
+
return picked;
|
|
1822
|
+
}
|
|
1477
1823
|
/** Stable identity key used by credentials, probe records, and catalog entries. */
|
|
1478
1824
|
const credentialIdentity = qoderCredentialIdentity;
|
|
1479
1825
|
/** The settings namespace a variant's card and provider directory entry use. */
|
|
@@ -1519,10 +1865,23 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1519
1865
|
logger: ctx.logger
|
|
1520
1866
|
});
|
|
1521
1867
|
let jobTokenRefreshedAt;
|
|
1868
|
+
const resolveAttachmentService = () => {
|
|
1869
|
+
const service = ctx.get("attachments");
|
|
1870
|
+
if (service === void 0) throw new Error("dsh-qoder-connect: no attachment service is available");
|
|
1871
|
+
return service;
|
|
1872
|
+
};
|
|
1873
|
+
const attachments = {
|
|
1874
|
+
get imageLimits() {
|
|
1875
|
+
return resolveAttachmentService().imageLimits;
|
|
1876
|
+
},
|
|
1877
|
+
readImageRequest: async (attachment, policy, signal) => await resolveAttachmentService().readImageRequest(attachment, policy, signal),
|
|
1878
|
+
saveImage: async (request) => await resolveAttachmentService().saveImage(request)
|
|
1879
|
+
};
|
|
1522
1880
|
const transport = createQoderTransport({
|
|
1523
1881
|
region: variant.region,
|
|
1524
1882
|
resolvePat: () => store.patPromise(),
|
|
1525
1883
|
resolveMachineId: () => getMachineId([qoderMachineIdPath()]),
|
|
1884
|
+
attachments,
|
|
1526
1885
|
onJobTokenRefreshed: (info) => {
|
|
1527
1886
|
jobTokenRefreshedAt = info.at;
|
|
1528
1887
|
ctx.logger.warn(`dsh-qoder-connect: ${variant.displayName} job token was auto-refreshed after an upstream rejection`);
|
|
@@ -1538,18 +1897,17 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1538
1897
|
providerId: variant.id,
|
|
1539
1898
|
getPat: () => store.patPromise(),
|
|
1540
1899
|
transport,
|
|
1541
|
-
attachments
|
|
1542
|
-
const service = ctx.get("attachments");
|
|
1543
|
-
if (service === void 0) throw new Error("dsh-qoder-connect: no attachment service is available to store images");
|
|
1544
|
-
return await service.saveImage(request);
|
|
1545
|
-
} }
|
|
1900
|
+
attachments
|
|
1546
1901
|
});
|
|
1547
1902
|
const fallback = fallbackFor(variant);
|
|
1548
1903
|
const catalog = new QoderCatalog(fallback);
|
|
1549
|
-
if (variant.id !== CHINA_VARIANT.id)
|
|
1550
|
-
|
|
1904
|
+
if (variant.id !== CHINA_VARIANT.id) {
|
|
1905
|
+
catalog.setUseMaximumContextWindow(config.useMaximumContextWindow === true);
|
|
1906
|
+
if (config.disabledModels !== void 0) catalog.setDisabledModels(config.disabledModels);
|
|
1907
|
+
} else {
|
|
1551
1908
|
catalog.setUseMaximumContextWindow(config.useMaximumContextWindowCN === true);
|
|
1552
1909
|
if (config.modelContextWindowsCN !== void 0) catalog.setModelContextWindows(config.modelContextWindowsCN);
|
|
1910
|
+
if (config.disabledModelsCN !== void 0) catalog.setDisabledModels(config.disabledModelsCN);
|
|
1553
1911
|
}
|
|
1554
1912
|
catalog.setVisible(false);
|
|
1555
1913
|
const probeStore = new QoderProbeStore({
|
|
@@ -1582,7 +1940,8 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1582
1940
|
inflightFetch: void 0,
|
|
1583
1941
|
invalidate: () => {},
|
|
1584
1942
|
registered: false,
|
|
1585
|
-
jobTokenRefreshedAt: () => jobTokenRefreshedAt
|
|
1943
|
+
jobTokenRefreshedAt: () => jobTokenRefreshedAt,
|
|
1944
|
+
checkIn: (signal) => transport.checkIn(signal)
|
|
1586
1945
|
};
|
|
1587
1946
|
}
|
|
1588
1947
|
/** The catalog provenance the card displays. */
|
|
@@ -1740,6 +2099,41 @@ function apply(ctx, config) {
|
|
|
1740
2099
|
*/
|
|
1741
2100
|
const lastIdentities = /* @__PURE__ */ new Map();
|
|
1742
2101
|
const runtimes = QODER_VARIANTS.map((variant) => createVariantRuntime(ctx, config, variant, () => current(), (id) => lastIdentities.get(id)));
|
|
2102
|
+
const checkInStore = new JsonFileCheckInStore();
|
|
2103
|
+
const checkInScheduler = new CheckInScheduler({
|
|
2104
|
+
targets: runtimes.map((runtime) => ({
|
|
2105
|
+
variantId: runtime.variant.id,
|
|
2106
|
+
checkIn: (signal) => runtime.checkIn(signal),
|
|
2107
|
+
minuteOfDay: () => {
|
|
2108
|
+
const cfg = current();
|
|
2109
|
+
return normalizeCheckInMinute((runtime.variant.id === CHINA_VARIANT.id ? cfg.checkInMinuteCN : cfg.checkInMinuteGlobal) ?? 600);
|
|
2110
|
+
},
|
|
2111
|
+
onClaimed: () => {
|
|
2112
|
+
runtime.client.fetchCredits().catch(() => void 0);
|
|
2113
|
+
}
|
|
2114
|
+
})),
|
|
2115
|
+
isEnabled: (variantId) => {
|
|
2116
|
+
const cfg = current();
|
|
2117
|
+
if (variantId === CHINA_VARIANT.id) return cfg.autoCheckInCN === true;
|
|
2118
|
+
return cfg.autoCheckInGlobal === true;
|
|
2119
|
+
},
|
|
2120
|
+
store: checkInStore
|
|
2121
|
+
});
|
|
2122
|
+
checkInScheduler.start();
|
|
2123
|
+
/**
|
|
2124
|
+
* The startup catch-up, run once more from the settings source callback.
|
|
2125
|
+
*
|
|
2126
|
+
* `start()` above runs while the plugin is still assembling: the section
|
|
2127
|
+
* that stores these toggles has not handed over its values yet, so that
|
|
2128
|
+
* sweep reads the raw plugin config, sees no toggle, and skips the day. The
|
|
2129
|
+
* retry is what makes "boot after 10:00" actually claim.
|
|
2130
|
+
*/
|
|
2131
|
+
let startupCatchUpDone = false;
|
|
2132
|
+
const runStartupCatchUpOnce = () => {
|
|
2133
|
+
if (startupCatchUpDone) return;
|
|
2134
|
+
startupCatchUpDone = true;
|
|
2135
|
+
checkInScheduler.catchUp();
|
|
2136
|
+
};
|
|
1743
2137
|
installJobTokenHint(ctx);
|
|
1744
2138
|
const probeKey = createProbeKey();
|
|
1745
2139
|
/**
|
|
@@ -1754,6 +2148,8 @@ function apply(ctx, config) {
|
|
|
1754
2148
|
const authKey = createAuthKey();
|
|
1755
2149
|
let setMaximumContextWindow;
|
|
1756
2150
|
let setMaximumContextWindowCN;
|
|
2151
|
+
let setModelsEnabled;
|
|
2152
|
+
let setModelsEnabledCN;
|
|
1757
2153
|
/**
|
|
1758
2154
|
* Point a variant at an account identity, invalidating whatever the previous
|
|
1759
2155
|
* one left behind.
|
|
@@ -1817,13 +2213,28 @@ function apply(ctx, config) {
|
|
|
1817
2213
|
path: runtime.variant.statusPath,
|
|
1818
2214
|
store: runtime.store,
|
|
1819
2215
|
client: runtime.client,
|
|
1820
|
-
models: () => runtime.catalog.
|
|
2216
|
+
models: () => runtime.catalog.all(),
|
|
1821
2217
|
catalog: () => catalogSection(runtime),
|
|
1822
2218
|
probe: () => probeSection(runtime, current().probeConsent === true),
|
|
1823
2219
|
probeKey,
|
|
1824
2220
|
authKey,
|
|
1825
|
-
...runtime.variant.id === CHINA_VARIANT.id ? {
|
|
1826
|
-
|
|
2221
|
+
...runtime.variant.id === CHINA_VARIANT.id ? {
|
|
2222
|
+
useMaximumContextWindow: () => current().useMaximumContextWindowCN === true,
|
|
2223
|
+
disabledModels: () => current().disabledModelsCN ?? []
|
|
2224
|
+
} : {
|
|
2225
|
+
useMaximumContextWindow: () => current().useMaximumContextWindow === true,
|
|
2226
|
+
disabledModels: () => current().disabledModels ?? []
|
|
2227
|
+
},
|
|
2228
|
+
jobTokenRefreshedAt: runtime.jobTokenRefreshedAt,
|
|
2229
|
+
checkIn: () => {
|
|
2230
|
+
const record = checkInStore.read(runtime.variant.id);
|
|
2231
|
+
if (record === void 0) return void 0;
|
|
2232
|
+
const nextRunAt = checkInScheduler.nextRunAt(runtime.variant.id);
|
|
2233
|
+
return {
|
|
2234
|
+
...record,
|
|
2235
|
+
...nextRunAt === void 0 ? {} : { nextRunAt }
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
1827
2238
|
});
|
|
1828
2239
|
registerQoderAuthRoute(webCtx, {
|
|
1829
2240
|
path: runtime.variant.authPath,
|
|
@@ -1886,19 +2297,58 @@ function apply(ctx, config) {
|
|
|
1886
2297
|
reason: runtime.catalogError
|
|
1887
2298
|
};
|
|
1888
2299
|
},
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2300
|
+
clearCheckInLogs: () => {
|
|
2301
|
+
checkInStore.clearLogs(runtime.variant.id);
|
|
2302
|
+
},
|
|
2303
|
+
checkIn: async () => {
|
|
2304
|
+
const result = await runtime.checkIn();
|
|
2305
|
+
if (result.status !== "error") {
|
|
2306
|
+
checkInStore.write(runtime.variant.id, {
|
|
2307
|
+
lastDate: result.date,
|
|
2308
|
+
lastAt: result.timestamp,
|
|
2309
|
+
status: result.status,
|
|
2310
|
+
...result.amount === void 0 ? {} : { amount: result.amount },
|
|
2311
|
+
...result.message === void 0 ? {} : { message: result.message }
|
|
2312
|
+
});
|
|
2313
|
+
if (result.status === "claimed") runtime.client.fetchCredits().catch(() => void 0);
|
|
2314
|
+
}
|
|
2315
|
+
return {
|
|
2316
|
+
state: result.status,
|
|
2317
|
+
...result.amount === void 0 ? {} : { amount: result.amount },
|
|
2318
|
+
...result.message === void 0 ? {} : { reason: result.message }
|
|
1899
2319
|
};
|
|
1900
|
-
|
|
1901
|
-
|
|
2320
|
+
},
|
|
2321
|
+
...runtime.variant.id === CHINA_VARIANT.id ? {
|
|
2322
|
+
setMaximumContextWindow: async (enabled) => {
|
|
2323
|
+
if (setMaximumContextWindowCN === void 0) return {
|
|
2324
|
+
state: "failed",
|
|
2325
|
+
reason: "settings are unavailable"
|
|
2326
|
+
};
|
|
2327
|
+
return setMaximumContextWindowCN(enabled);
|
|
2328
|
+
},
|
|
2329
|
+
setModelsEnabled: async (opts) => {
|
|
2330
|
+
if (setModelsEnabledCN === void 0) return {
|
|
2331
|
+
state: "failed",
|
|
2332
|
+
reason: "settings are unavailable"
|
|
2333
|
+
};
|
|
2334
|
+
return setModelsEnabledCN(opts);
|
|
2335
|
+
}
|
|
2336
|
+
} : {
|
|
2337
|
+
setMaximumContextWindow: async (enabled) => {
|
|
2338
|
+
if (setMaximumContextWindow === void 0) return {
|
|
2339
|
+
state: "failed",
|
|
2340
|
+
reason: "settings are unavailable"
|
|
2341
|
+
};
|
|
2342
|
+
return setMaximumContextWindow(enabled);
|
|
2343
|
+
},
|
|
2344
|
+
setModelsEnabled: async (opts) => {
|
|
2345
|
+
if (setModelsEnabled === void 0) return {
|
|
2346
|
+
state: "failed",
|
|
2347
|
+
reason: "settings are unavailable"
|
|
2348
|
+
};
|
|
2349
|
+
return setModelsEnabled(opts);
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
1902
2352
|
}, probeKey);
|
|
1903
2353
|
}
|
|
1904
2354
|
});
|
|
@@ -1911,16 +2361,11 @@ function apply(ctx, config) {
|
|
|
1911
2361
|
};
|
|
1912
2362
|
/** Merge all sections into the whole config the rest of the plugin reads. */
|
|
1913
2363
|
const merged = () => ({
|
|
1914
|
-
...sources.cn
|
|
1915
|
-
...sources.
|
|
1916
|
-
...sources.
|
|
1917
|
-
...sources.global().useMaximumContextWindow === void 0 ? {} : { useMaximumContextWindow: sources.global().useMaximumContextWindow },
|
|
1918
|
-
...sources.global().modelContextWindows === void 0 ? {} : { modelContextWindows: sources.global().modelContextWindows },
|
|
1919
|
-
...sources.quota().sidebarQuotaCN === void 0 ? {} : { sidebarQuotaCN: sources.quota().sidebarQuotaCN },
|
|
1920
|
-
...sources.quota().sidebarQuotaGlobal === void 0 ? {} : { sidebarQuotaGlobal: sources.quota().sidebarQuotaGlobal },
|
|
1921
|
-
...sources.quota().quotaPollMs === void 0 ? {} : { quotaPollMs: sources.quota().quotaPollMs }
|
|
2364
|
+
...pickFields(sources.cn, CN_SECTION_KEYS),
|
|
2365
|
+
...pickFields(sources.global, GLOBAL_SECTION_KEYS),
|
|
2366
|
+
...pickFields(sources.quota, QUOTA_SECTION_KEYS)
|
|
1922
2367
|
});
|
|
1923
|
-
const
|
|
2368
|
+
const applyCatalogSettings = (next) => {
|
|
1924
2369
|
let changed = false;
|
|
1925
2370
|
for (const runtime of runtimes) {
|
|
1926
2371
|
const isChina = runtime.variant.id === CHINA_VARIANT.id;
|
|
@@ -1928,11 +2373,13 @@ function apply(ctx, config) {
|
|
|
1928
2373
|
if (runtime.catalog.setUseMaximumContextWindow(preference === true)) changed = true;
|
|
1929
2374
|
const overrides = isChina ? next.modelContextWindowsCN : next.modelContextWindows;
|
|
1930
2375
|
if (overrides !== void 0 && runtime.catalog.setModelContextWindows(overrides)) changed = true;
|
|
2376
|
+
const disabled = isChina ? next.disabledModelsCN : next.disabledModels;
|
|
2377
|
+
if (runtime.catalog.setDisabledModels(disabled ?? [])) changed = true;
|
|
1931
2378
|
}
|
|
1932
2379
|
if (changed) for (const runtime of runtimes) runtime.invalidate();
|
|
1933
2380
|
};
|
|
1934
2381
|
const repointStores = () => {
|
|
1935
|
-
|
|
2382
|
+
applyCatalogSettings(merged());
|
|
1936
2383
|
};
|
|
1937
2384
|
settingsCtx.settings.installSection(ctx, QODER_SETTINGS_NS, CHINA_SECTION, config, {
|
|
1938
2385
|
setSource(source) {
|
|
@@ -1952,8 +2399,13 @@ function apply(ctx, config) {
|
|
|
1952
2399
|
setSource(source) {
|
|
1953
2400
|
sources.quota = source;
|
|
1954
2401
|
current = merged;
|
|
2402
|
+
checkInScheduler.rearm();
|
|
2403
|
+
runStartupCatchUpOnce();
|
|
1955
2404
|
},
|
|
1956
|
-
onChange: () => {
|
|
2405
|
+
onChange: () => {
|
|
2406
|
+
checkInScheduler.rearm();
|
|
2407
|
+
checkInScheduler.sweepAll(true);
|
|
2408
|
+
}
|
|
1957
2409
|
});
|
|
1958
2410
|
setMaximumContextWindow = async (enabled) => {
|
|
1959
2411
|
await settingsCtx.settings.update(QODER_GLOBAL_SETTINGS_NS, { useMaximumContextWindow: enabled });
|
|
@@ -1963,9 +2415,24 @@ function apply(ctx, config) {
|
|
|
1963
2415
|
await settingsCtx.settings.update(QODER_SETTINGS_NS, { useMaximumContextWindowCN: enabled });
|
|
1964
2416
|
return { state: "updated" };
|
|
1965
2417
|
};
|
|
2418
|
+
setModelsEnabled = async ({ models, enabled }) => {
|
|
2419
|
+
const currentDisabled = new Set(sources.global().disabledModels ?? []);
|
|
2420
|
+
for (const m of models) if (enabled) currentDisabled.delete(m);
|
|
2421
|
+
else currentDisabled.add(m);
|
|
2422
|
+
await settingsCtx.settings.update(QODER_GLOBAL_SETTINGS_NS, { disabledModels: Array.from(currentDisabled) });
|
|
2423
|
+
return { state: "updated" };
|
|
2424
|
+
};
|
|
2425
|
+
setModelsEnabledCN = async ({ models, enabled }) => {
|
|
2426
|
+
const currentDisabled = new Set(sources.cn().disabledModelsCN ?? []);
|
|
2427
|
+
for (const m of models) if (enabled) currentDisabled.delete(m);
|
|
2428
|
+
else currentDisabled.add(m);
|
|
2429
|
+
await settingsCtx.settings.update(QODER_SETTINGS_NS, { disabledModelsCN: Array.from(currentDisabled) });
|
|
2430
|
+
return { state: "updated" };
|
|
2431
|
+
};
|
|
1966
2432
|
});
|
|
1967
2433
|
ctx.effect(() => () => {
|
|
1968
2434
|
stopped = true;
|
|
2435
|
+
checkInScheduler.dispose();
|
|
1969
2436
|
for (const timer of timers) clearInterval(timer);
|
|
1970
2437
|
timers.length = 0;
|
|
1971
2438
|
detach(ctx, clearHostHeartbeat(), "host heartbeat cleanup");
|
|
@@ -2107,4 +2574,4 @@ function apply(ctx, config) {
|
|
|
2107
2574
|
}), "variant startup");
|
|
2108
2575
|
}
|
|
2109
2576
|
//#endregion
|
|
2110
|
-
export { CHINA_VARIANT, Config, FALLBACK_QODER_MODELS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, QODER_AUTH_FILENAME, QODER_AUTH_PATH, QODER_DATA_DIR_ENV, QODER_DATA_DIR_NAME, QODER_GLOBAL_AUTH_PATH, QODER_GLOBAL_PROBE_PATH, QODER_GLOBAL_SETTINGS_NS, QODER_GLOBAL_STATUS_PATH, QODER_HOST_HEARTBEAT_FILENAME, QODER_PAT_ENV_CN, QODER_PAT_ENV_GLOBAL, QODER_PROBE_FILENAME, QODER_PROBE_PATH, QODER_PROVIDER, QODER_QUOTA_SETTINGS_NS, QODER_SETTINGS_NS, QODER_STATUS_PATH, QODER_STREAM_IDLE_TIMEOUT_MS, QODER_VARIANTS, QUOTA_POLL_DEFAULT_MS, QUOTA_POLL_MIN_MS, QoderCatalog, QoderCatalogStore, QoderCredentialStore, QoderProbeService, QoderProbeStore, QoderUpstreamClient, apply, classifyUpstreamError, clearHostHeartbeat, createAuthKey, createQoderAdapter, createQoderShim, fingerprintModel, inject, isHeartbeatProcessAlive, kindFromQoderFailure, modelInfoOf, name, normalizeCredits, patTail, probeModel, processStartTimeMs, qoderAuthHandler, qoderCatalogPath, qoderCredentialIdentity, qoderHostHeartbeatPath, qoderOwnAuthPath, qoderPluginDataDir, qoderProbePath, randomSentinel, readHostHeartbeat, registerQoderAuthRoute, variantFor };
|
|
2577
|
+
export { CHINA_VARIANT, CN_SECTION_KEYS, Config, FALLBACK_QODER_MODELS, GLOBAL_SECTION_KEYS, GLOBAL_VARIANT, KIND_STATUS, PROBE_EFFORT_CANDIDATES, QODER_AUTH_FILENAME, QODER_AUTH_PATH, QODER_DATA_DIR_ENV, QODER_DATA_DIR_NAME, QODER_GLOBAL_AUTH_PATH, QODER_GLOBAL_PROBE_PATH, QODER_GLOBAL_SETTINGS_NS, QODER_GLOBAL_STATUS_PATH, QODER_HOST_HEARTBEAT_FILENAME, QODER_PAT_ENV_CN, QODER_PAT_ENV_GLOBAL, QODER_PROBE_FILENAME, QODER_PROBE_PATH, QODER_PROVIDER, QODER_QUOTA_SETTINGS_NS, QODER_SETTINGS_NS, QODER_STATUS_PATH, QODER_STREAM_IDLE_TIMEOUT_MS, QODER_VARIANTS, QUOTA_POLL_DEFAULT_MS, QUOTA_POLL_MIN_MS, QUOTA_SECTION_KEYS, QoderCatalog, QoderCatalogStore, QoderCredentialStore, QoderProbeService, QoderProbeStore, QoderUpstreamClient, apply, classifyUpstreamError, clearHostHeartbeat, createAuthKey, createQoderAdapter, createQoderShim, fingerprintModel, inject, isHeartbeatProcessAlive, kindFromQoderFailure, modelInfoOf, name, normalizeCredits, patTail, probeModel, processStartTimeMs, qoderAuthHandler, qoderCatalogPath, qoderCredentialIdentity, qoderHostHeartbeatPath, qoderOwnAuthPath, qoderPluginDataDir, qoderProbePath, randomSentinel, readHostHeartbeat, registerQoderAuthRoute, variantFor };
|