dsh-qoder-connect 0.1.5 → 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 +7 -2
- package/README.md +7 -2
- package/lib/bin.js +1 -1
- package/lib/client.js +449 -8
- package/lib/index.d.ts +58 -4
- package/lib/index.js +332 -97
- package/lib/{variants-BEiQve5M.js → variants-DE76gF0Y.js} +36 -3
- 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,7 +1049,8 @@ 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 : {
|
|
@@ -1181,6 +1182,20 @@ function parseAction(text) {
|
|
|
1181
1182
|
action: "set-maximum-context-window",
|
|
1182
1183
|
enabled: wrapped["enabled"]
|
|
1183
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
|
+
}
|
|
1184
1199
|
if (action === "probe") {
|
|
1185
1200
|
const model = wrapped["model"];
|
|
1186
1201
|
if (typeof model !== "string" || model.trim() === "") return void 0;
|
|
@@ -1253,6 +1268,17 @@ function qoderProbeHandler(deps, key) {
|
|
|
1253
1268
|
json(res, 200, await deps.setMaximumContextWindow(action.enabled === true));
|
|
1254
1269
|
return;
|
|
1255
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
|
+
}
|
|
1256
1282
|
json(res, 200, await deps.probe(action.model));
|
|
1257
1283
|
} catch (error) {
|
|
1258
1284
|
json(res, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
@@ -1336,14 +1362,25 @@ function getUtc8DateString(nowMs = Date.now()) {
|
|
|
1336
1362
|
const utc8 = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1337
1363
|
return `${utc8.getFullYear()}-${String(utc8.getMonth() + 1).padStart(2, "0")}-${String(utc8.getDate()).padStart(2, "0")}`;
|
|
1338
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
|
+
}
|
|
1339
1372
|
/**
|
|
1340
|
-
* Calculates milliseconds until the next
|
|
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.
|
|
1341
1377
|
*/
|
|
1342
|
-
function
|
|
1378
|
+
function msUntilNextCheckIn(minuteOfDay, nowMs = Date.now()) {
|
|
1379
|
+
const minute = normalizeCheckInMinute(minuteOfDay);
|
|
1343
1380
|
const d = new Date(nowMs);
|
|
1344
1381
|
const utc8Time = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1345
1382
|
const targetUtc8 = new Date(utc8Time.getTime());
|
|
1346
|
-
targetUtc8.setHours(
|
|
1383
|
+
targetUtc8.setHours(Math.floor(minute / 60), minute % 60, 5, 0);
|
|
1347
1384
|
let diff = targetUtc8.getTime() - utc8Time.getTime();
|
|
1348
1385
|
if (diff <= 0) {
|
|
1349
1386
|
targetUtc8.setDate(targetUtc8.getDate() + 1);
|
|
@@ -1352,13 +1389,13 @@ function msUntilNext10amUtc8(nowMs = Date.now()) {
|
|
|
1352
1389
|
return diff;
|
|
1353
1390
|
}
|
|
1354
1391
|
/**
|
|
1355
|
-
*
|
|
1356
|
-
* Current time is past today's 10:00:00 AM (UTC+8) and today has not yet settled a check-in.
|
|
1392
|
+
* Whether today's configured check-in moment (UTC+8) has already passed.
|
|
1357
1393
|
*/
|
|
1358
|
-
function
|
|
1359
|
-
|
|
1394
|
+
function isPastCheckInTime(minuteOfDay, nowMs = Date.now()) {
|
|
1395
|
+
const minute = normalizeCheckInMinute(minuteOfDay);
|
|
1360
1396
|
const d = new Date(nowMs);
|
|
1361
|
-
|
|
1397
|
+
const utc8 = new Date(d.getTime() + (d.getTimezoneOffset() + 480) * 6e4);
|
|
1398
|
+
return utc8.getHours() * 60 + utc8.getMinutes() >= minute;
|
|
1362
1399
|
}
|
|
1363
1400
|
var CheckInScheduler = class {
|
|
1364
1401
|
targets;
|
|
@@ -1366,7 +1403,21 @@ var CheckInScheduler = class {
|
|
|
1366
1403
|
store;
|
|
1367
1404
|
onResult;
|
|
1368
1405
|
now;
|
|
1369
|
-
|
|
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();
|
|
1370
1421
|
disposed = false;
|
|
1371
1422
|
constructor(options) {
|
|
1372
1423
|
this.targets = options.targets;
|
|
@@ -1378,56 +1429,113 @@ var CheckInScheduler = class {
|
|
|
1378
1429
|
start() {
|
|
1379
1430
|
if (this.disposed) return;
|
|
1380
1431
|
this.sweepAll(true);
|
|
1381
|
-
this.
|
|
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);
|
|
1382
1446
|
}
|
|
1383
1447
|
dispose() {
|
|
1384
1448
|
this.disposed = true;
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
this.timer = void 0;
|
|
1388
|
-
}
|
|
1449
|
+
for (const timer of this.timers.values()) clearTimeout(timer);
|
|
1450
|
+
this.timers.clear();
|
|
1389
1451
|
}
|
|
1390
|
-
|
|
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() {
|
|
1391
1468
|
if (this.disposed) return;
|
|
1392
|
-
const
|
|
1393
|
-
this.
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
+
}
|
|
1398
1485
|
}
|
|
1399
|
-
|
|
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) {
|
|
1400
1491
|
if (this.disposed) return;
|
|
1401
1492
|
const nowMs = this.now();
|
|
1402
1493
|
const today = getUtc8DateString(nowMs);
|
|
1403
1494
|
for (const target of this.targets) {
|
|
1495
|
+
if (only !== void 0 && target.variantId !== only) continue;
|
|
1404
1496
|
if (!this.isEnabled(target.variantId)) continue;
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
if (!isCatchUp && record?.lastDate === today && (record.status === "claimed" || record.status === "already-claimed")) continue;
|
|
1408
|
-
let pat;
|
|
1497
|
+
if (this.inFlight.has(target.variantId)) continue;
|
|
1498
|
+
this.inFlight.add(target.variantId);
|
|
1409
1499
|
try {
|
|
1410
|
-
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1500
|
+
await this.sweepOne(target, isCatchUp, nowMs, today);
|
|
1501
|
+
} finally {
|
|
1502
|
+
this.inFlight.delete(target.variantId);
|
|
1413
1503
|
}
|
|
1414
|
-
if (!pat) continue;
|
|
1415
|
-
try {
|
|
1416
|
-
const result = await target.service.checkIn(pat);
|
|
1417
|
-
if (result.status !== "error") {
|
|
1418
|
-
this.store.write(target.variantId, {
|
|
1419
|
-
lastDate: result.date,
|
|
1420
|
-
lastAt: result.timestamp,
|
|
1421
|
-
status: result.status,
|
|
1422
|
-
amount: result.amount,
|
|
1423
|
-
message: result.message
|
|
1424
|
-
});
|
|
1425
|
-
if (result.status === "claimed") target.onClaimed?.();
|
|
1426
|
-
}
|
|
1427
|
-
this.onResult?.(result);
|
|
1428
|
-
} catch {}
|
|
1429
1504
|
}
|
|
1430
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
|
+
}
|
|
1431
1539
|
};
|
|
1432
1540
|
//#endregion
|
|
1433
1541
|
//#region src/job-token-hint.ts
|
|
@@ -1597,10 +1705,21 @@ const MAXIMUM_CONTEXT_WINDOW_CN_FIELD = z.boolean().default(true).description("U
|
|
|
1597
1705
|
* overrides" rather than a default object the host must keep in sync.
|
|
1598
1706
|
*/
|
|
1599
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");
|
|
1600
1710
|
/** Sidebar quota toggle (one per variant; both live on the shared quota card). */
|
|
1601
1711
|
const QUOTA_TOGGLE_FIELD = z.boolean().default(false).description("Show this variant’s remaining-credit card in the sidebar footer (off by default)");
|
|
1602
1712
|
/** Automatic check-in toggle. */
|
|
1603
|
-
const AUTO_CHECK_IN_FIELD = z.boolean().default(false).description("
|
|
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)");
|
|
1604
1723
|
/**
|
|
1605
1724
|
* Quota poll interval: default 5 minutes, floor 1 minute. The status route
|
|
1606
1725
|
* performs a live upstream billing call per request with no cache, so an
|
|
@@ -1621,6 +1740,8 @@ const Config = z.object({
|
|
|
1621
1740
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1622
1741
|
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1623
1742
|
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1743
|
+
checkInMinuteCN: CHECK_IN_MINUTE_FIELD,
|
|
1744
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_FIELD,
|
|
1624
1745
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1625
1746
|
});
|
|
1626
1747
|
/**
|
|
@@ -1635,12 +1756,14 @@ const Config = z.object({
|
|
|
1635
1756
|
const CHINA_SECTION = z.object({
|
|
1636
1757
|
probeConsent: PROBE_CONSENT_FIELD,
|
|
1637
1758
|
useMaximumContextWindowCN: MAXIMUM_CONTEXT_WINDOW_CN_FIELD,
|
|
1638
|
-
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD
|
|
1759
|
+
modelContextWindowsCN: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1760
|
+
disabledModelsCN: DISABLED_MODELS_FIELD
|
|
1639
1761
|
});
|
|
1640
1762
|
/** The global card's settings section and its context-window preferences. */
|
|
1641
1763
|
const GLOBAL_SECTION = z.object({
|
|
1642
1764
|
useMaximumContextWindow: MAXIMUM_CONTEXT_WINDOW_FIELD,
|
|
1643
|
-
modelContextWindows: MODEL_CONTEXT_WINDOWS_FIELD
|
|
1765
|
+
modelContextWindows: MODEL_CONTEXT_WINDOWS_FIELD,
|
|
1766
|
+
disabledModels: DISABLED_MODELS_FIELD
|
|
1644
1767
|
});
|
|
1645
1768
|
/**
|
|
1646
1769
|
* The shared quota card's section: both sidebar toggles and the poll interval.
|
|
@@ -1654,8 +1777,49 @@ const QUOTA_SECTION = z.object({
|
|
|
1654
1777
|
sidebarQuotaGlobal: QUOTA_TOGGLE_FIELD,
|
|
1655
1778
|
autoCheckInCN: AUTO_CHECK_IN_FIELD,
|
|
1656
1779
|
autoCheckInGlobal: AUTO_CHECK_IN_FIELD,
|
|
1780
|
+
checkInMinuteCN: CHECK_IN_MINUTE_FIELD,
|
|
1781
|
+
checkInMinuteGlobal: CHECK_IN_MINUTE_FIELD,
|
|
1657
1782
|
quotaPollMs: QUOTA_POLL_FIELD
|
|
1658
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
|
+
}
|
|
1659
1823
|
/** Stable identity key used by credentials, probe records, and catalog entries. */
|
|
1660
1824
|
const credentialIdentity = qoderCredentialIdentity;
|
|
1661
1825
|
/** The settings namespace a variant's card and provider directory entry use. */
|
|
@@ -1701,10 +1865,23 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1701
1865
|
logger: ctx.logger
|
|
1702
1866
|
});
|
|
1703
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
|
+
};
|
|
1704
1880
|
const transport = createQoderTransport({
|
|
1705
1881
|
region: variant.region,
|
|
1706
1882
|
resolvePat: () => store.patPromise(),
|
|
1707
1883
|
resolveMachineId: () => getMachineId([qoderMachineIdPath()]),
|
|
1884
|
+
attachments,
|
|
1708
1885
|
onJobTokenRefreshed: (info) => {
|
|
1709
1886
|
jobTokenRefreshedAt = info.at;
|
|
1710
1887
|
ctx.logger.warn(`dsh-qoder-connect: ${variant.displayName} job token was auto-refreshed after an upstream rejection`);
|
|
@@ -1720,18 +1897,17 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1720
1897
|
providerId: variant.id,
|
|
1721
1898
|
getPat: () => store.patPromise(),
|
|
1722
1899
|
transport,
|
|
1723
|
-
attachments
|
|
1724
|
-
const service = ctx.get("attachments");
|
|
1725
|
-
if (service === void 0) throw new Error("dsh-qoder-connect: no attachment service is available to store images");
|
|
1726
|
-
return await service.saveImage(request);
|
|
1727
|
-
} }
|
|
1900
|
+
attachments
|
|
1728
1901
|
});
|
|
1729
1902
|
const fallback = fallbackFor(variant);
|
|
1730
1903
|
const catalog = new QoderCatalog(fallback);
|
|
1731
|
-
if (variant.id !== CHINA_VARIANT.id)
|
|
1732
|
-
|
|
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 {
|
|
1733
1908
|
catalog.setUseMaximumContextWindow(config.useMaximumContextWindowCN === true);
|
|
1734
1909
|
if (config.modelContextWindowsCN !== void 0) catalog.setModelContextWindows(config.modelContextWindowsCN);
|
|
1910
|
+
if (config.disabledModelsCN !== void 0) catalog.setDisabledModels(config.disabledModelsCN);
|
|
1735
1911
|
}
|
|
1736
1912
|
catalog.setVisible(false);
|
|
1737
1913
|
const probeStore = new QoderProbeStore({
|
|
@@ -1764,7 +1940,8 @@ function createVariantRuntime(ctx, config, variant, current, identityOf) {
|
|
|
1764
1940
|
inflightFetch: void 0,
|
|
1765
1941
|
invalidate: () => {},
|
|
1766
1942
|
registered: false,
|
|
1767
|
-
jobTokenRefreshedAt: () => jobTokenRefreshedAt
|
|
1943
|
+
jobTokenRefreshedAt: () => jobTokenRefreshedAt,
|
|
1944
|
+
checkIn: (signal) => transport.checkIn(signal)
|
|
1768
1945
|
};
|
|
1769
1946
|
}
|
|
1770
1947
|
/** The catalog provenance the card displays. */
|
|
@@ -1926,8 +2103,11 @@ function apply(ctx, config) {
|
|
|
1926
2103
|
const checkInScheduler = new CheckInScheduler({
|
|
1927
2104
|
targets: runtimes.map((runtime) => ({
|
|
1928
2105
|
variantId: runtime.variant.id,
|
|
1929
|
-
|
|
1930
|
-
|
|
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
|
+
},
|
|
1931
2111
|
onClaimed: () => {
|
|
1932
2112
|
runtime.client.fetchCredits().catch(() => void 0);
|
|
1933
2113
|
}
|
|
@@ -1940,6 +2120,20 @@ function apply(ctx, config) {
|
|
|
1940
2120
|
store: checkInStore
|
|
1941
2121
|
});
|
|
1942
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
|
+
};
|
|
1943
2137
|
installJobTokenHint(ctx);
|
|
1944
2138
|
const probeKey = createProbeKey();
|
|
1945
2139
|
/**
|
|
@@ -1954,6 +2148,8 @@ function apply(ctx, config) {
|
|
|
1954
2148
|
const authKey = createAuthKey();
|
|
1955
2149
|
let setMaximumContextWindow;
|
|
1956
2150
|
let setMaximumContextWindowCN;
|
|
2151
|
+
let setModelsEnabled;
|
|
2152
|
+
let setModelsEnabledCN;
|
|
1957
2153
|
/**
|
|
1958
2154
|
* Point a variant at an account identity, invalidating whatever the previous
|
|
1959
2155
|
* one left behind.
|
|
@@ -2017,14 +2213,28 @@ function apply(ctx, config) {
|
|
|
2017
2213
|
path: runtime.variant.statusPath,
|
|
2018
2214
|
store: runtime.store,
|
|
2019
2215
|
client: runtime.client,
|
|
2020
|
-
models: () => runtime.catalog.
|
|
2216
|
+
models: () => runtime.catalog.all(),
|
|
2021
2217
|
catalog: () => catalogSection(runtime),
|
|
2022
2218
|
probe: () => probeSection(runtime, current().probeConsent === true),
|
|
2023
2219
|
probeKey,
|
|
2024
2220
|
authKey,
|
|
2025
|
-
...runtime.variant.id === CHINA_VARIANT.id ? {
|
|
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
|
+
},
|
|
2026
2228
|
jobTokenRefreshedAt: runtime.jobTokenRefreshedAt,
|
|
2027
|
-
checkIn: () =>
|
|
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
|
+
}
|
|
2028
2238
|
});
|
|
2029
2239
|
registerQoderAuthRoute(webCtx, {
|
|
2030
2240
|
path: runtime.variant.authPath,
|
|
@@ -2091,16 +2301,7 @@ function apply(ctx, config) {
|
|
|
2091
2301
|
checkInStore.clearLogs(runtime.variant.id);
|
|
2092
2302
|
},
|
|
2093
2303
|
checkIn: async () => {
|
|
2094
|
-
|
|
2095
|
-
try {
|
|
2096
|
-
pat = await runtime.store.patPromise();
|
|
2097
|
-
} catch {
|
|
2098
|
-
return {
|
|
2099
|
-
state: "failed",
|
|
2100
|
-
reason: "No PAT available"
|
|
2101
|
-
};
|
|
2102
|
-
}
|
|
2103
|
-
const result = await runtime.transport.checkInService.checkIn(pat);
|
|
2304
|
+
const result = await runtime.checkIn();
|
|
2104
2305
|
if (result.status !== "error") {
|
|
2105
2306
|
checkInStore.write(runtime.variant.id, {
|
|
2106
2307
|
lastDate: result.date,
|
|
@@ -2117,19 +2318,37 @@ function apply(ctx, config) {
|
|
|
2117
2318
|
...result.message === void 0 ? {} : { reason: result.message }
|
|
2118
2319
|
};
|
|
2119
2320
|
},
|
|
2120
|
-
...runtime.variant.id === CHINA_VARIANT.id ? {
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
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
|
+
}
|
|
2133
2352
|
}, probeKey);
|
|
2134
2353
|
}
|
|
2135
2354
|
});
|
|
@@ -2142,16 +2361,11 @@ function apply(ctx, config) {
|
|
|
2142
2361
|
};
|
|
2143
2362
|
/** Merge all sections into the whole config the rest of the plugin reads. */
|
|
2144
2363
|
const merged = () => ({
|
|
2145
|
-
...sources.cn
|
|
2146
|
-
...sources.
|
|
2147
|
-
...sources.
|
|
2148
|
-
...sources.global().useMaximumContextWindow === void 0 ? {} : { useMaximumContextWindow: sources.global().useMaximumContextWindow },
|
|
2149
|
-
...sources.global().modelContextWindows === void 0 ? {} : { modelContextWindows: sources.global().modelContextWindows },
|
|
2150
|
-
...sources.quota().sidebarQuotaCN === void 0 ? {} : { sidebarQuotaCN: sources.quota().sidebarQuotaCN },
|
|
2151
|
-
...sources.quota().sidebarQuotaGlobal === void 0 ? {} : { sidebarQuotaGlobal: sources.quota().sidebarQuotaGlobal },
|
|
2152
|
-
...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)
|
|
2153
2367
|
});
|
|
2154
|
-
const
|
|
2368
|
+
const applyCatalogSettings = (next) => {
|
|
2155
2369
|
let changed = false;
|
|
2156
2370
|
for (const runtime of runtimes) {
|
|
2157
2371
|
const isChina = runtime.variant.id === CHINA_VARIANT.id;
|
|
@@ -2159,11 +2373,13 @@ function apply(ctx, config) {
|
|
|
2159
2373
|
if (runtime.catalog.setUseMaximumContextWindow(preference === true)) changed = true;
|
|
2160
2374
|
const overrides = isChina ? next.modelContextWindowsCN : next.modelContextWindows;
|
|
2161
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;
|
|
2162
2378
|
}
|
|
2163
2379
|
if (changed) for (const runtime of runtimes) runtime.invalidate();
|
|
2164
2380
|
};
|
|
2165
2381
|
const repointStores = () => {
|
|
2166
|
-
|
|
2382
|
+
applyCatalogSettings(merged());
|
|
2167
2383
|
};
|
|
2168
2384
|
settingsCtx.settings.installSection(ctx, QODER_SETTINGS_NS, CHINA_SECTION, config, {
|
|
2169
2385
|
setSource(source) {
|
|
@@ -2183,8 +2399,13 @@ function apply(ctx, config) {
|
|
|
2183
2399
|
setSource(source) {
|
|
2184
2400
|
sources.quota = source;
|
|
2185
2401
|
current = merged;
|
|
2402
|
+
checkInScheduler.rearm();
|
|
2403
|
+
runStartupCatchUpOnce();
|
|
2186
2404
|
},
|
|
2187
|
-
onChange: () => {
|
|
2405
|
+
onChange: () => {
|
|
2406
|
+
checkInScheduler.rearm();
|
|
2407
|
+
checkInScheduler.sweepAll(true);
|
|
2408
|
+
}
|
|
2188
2409
|
});
|
|
2189
2410
|
setMaximumContextWindow = async (enabled) => {
|
|
2190
2411
|
await settingsCtx.settings.update(QODER_GLOBAL_SETTINGS_NS, { useMaximumContextWindow: enabled });
|
|
@@ -2194,6 +2415,20 @@ function apply(ctx, config) {
|
|
|
2194
2415
|
await settingsCtx.settings.update(QODER_SETTINGS_NS, { useMaximumContextWindowCN: enabled });
|
|
2195
2416
|
return { state: "updated" };
|
|
2196
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
|
+
};
|
|
2197
2432
|
});
|
|
2198
2433
|
ctx.effect(() => () => {
|
|
2199
2434
|
stopped = true;
|
|
@@ -2339,4 +2574,4 @@ function apply(ctx, config) {
|
|
|
2339
2574
|
}), "variant startup");
|
|
2340
2575
|
}
|
|
2341
2576
|
//#endregion
|
|
2342
|
-
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 };
|