dsh-qoder-connect 0.1.1 → 0.1.2
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.md +1 -1
- package/lib/bin.js +1 -1
- package/lib/index.js +48 -16
- package/lib/{variants-CQg-9veJ.js → variants-C0iZD8AD.js} +24 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ dsh plugin --profile web add dsh-qoder-connect
|
|
|
79
79
|
|
|
80
80
|
### 生成 PAT
|
|
81
81
|
|
|
82
|
-
- **Qoder
|
|
82
|
+
- **Qoder(国内版)**:登录 qoder.com.cn → 账号设置 → 个人访问令牌,生成后复制。[直达获取](https://qoder.cn/account/integrations)
|
|
83
83
|
- **Qoder Global(国际版)**:登录 qoder.com → 账号设置 → 个人访问令牌,生成后复制。[直达获取](https://qoder.com/account/integrations)
|
|
84
84
|
|
|
85
85
|
(卡片内也有同样指引:「请先在 qoder.com.cn 的账号设置(账号 → 个人访问令牌)中生成 PAT,再粘贴到下方输入框。」)
|
package/lib/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { B as qoderCredentialIdentity, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, R as QoderCredentialStore, T as QoderCatalogStore, b as createQoderTransport, f as QODER_CONNECT_VERSION, i as variantFor, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, y as validateApiKey } from "./variants-
|
|
2
|
+
import { B as qoderCredentialIdentity, D as FALLBACK_QODER_MODELS, E as qoderCatalogPath, R as QoderCredentialStore, T as QoderCatalogStore, b as createQoderTransport, f as QODER_CONNECT_VERSION, i as variantFor, l as qoderHostHeartbeatPath, m as QoderUpstreamClient, r as QODER_VARIANTS, s as isHeartbeatProcessAlive, t as CHINA_VARIANT, u as readHostHeartbeat, y as validateApiKey } from "./variants-C0iZD8AD.js";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { realpathSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
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-C0iZD8AD.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";
|
|
@@ -539,7 +539,13 @@ function createQoderShim(options) {
|
|
|
539
539
|
return timingSafeEqual(a, b);
|
|
540
540
|
}
|
|
541
541
|
const server = createServer((req, res) => {
|
|
542
|
-
handle(req, res)
|
|
542
|
+
handle(req, res).catch((error) => {
|
|
543
|
+
logger?.warn("dsh-qoder-connect: loopback request failed", error);
|
|
544
|
+
try {
|
|
545
|
+
if (!res.headersSent) writeOpenAIError(res, 500, "internal", "loopback request failed");
|
|
546
|
+
else res.end();
|
|
547
|
+
} catch {}
|
|
548
|
+
});
|
|
543
549
|
});
|
|
544
550
|
const ready = new Promise((resolve, reject) => {
|
|
545
551
|
server.once("listening", () => resolve());
|
|
@@ -1582,6 +1588,21 @@ function probeSection(runtime, consent) {
|
|
|
1582
1588
|
};
|
|
1583
1589
|
}
|
|
1584
1590
|
/**
|
|
1591
|
+
* Run a detached promise without letting its failure kill the host.
|
|
1592
|
+
*
|
|
1593
|
+
* Node terminates the whole process on an unhandled rejection (exit code 1),
|
|
1594
|
+
* which the desktop shell reports as the backend having "exited unexpectedly"
|
|
1595
|
+
* — so a failing heartbeat write, loopback close, or background sweep in this
|
|
1596
|
+
* plugin would take the entire Harness down with it. Every fire-and-forget
|
|
1597
|
+
* call therefore carries a handler; a failure is a diagnostic, never a reason
|
|
1598
|
+
* for the host to die.
|
|
1599
|
+
*/
|
|
1600
|
+
function detach(ctx, work, what) {
|
|
1601
|
+
work.catch((error) => {
|
|
1602
|
+
ctx.logger.warn(`dsh-qoder-connect: ${what} failed`, error);
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1585
1606
|
* Start one variant: its loopback endpoint, provider registration, and
|
|
1586
1607
|
* configuration-card wiring.
|
|
1587
1608
|
*
|
|
@@ -1641,18 +1662,18 @@ async function startVariant(ctx, runtime) {
|
|
|
1641
1662
|
ctx.effect(() => () => {
|
|
1642
1663
|
releaseAdapter?.();
|
|
1643
1664
|
releaseDirectory?.();
|
|
1644
|
-
shim.close();
|
|
1665
|
+
detach(ctx, shim.close(), "loopback endpoint close");
|
|
1645
1666
|
});
|
|
1646
1667
|
} catch {
|
|
1647
1668
|
releaseAdapter?.();
|
|
1648
1669
|
releaseDirectory?.();
|
|
1649
|
-
shim.close();
|
|
1670
|
+
detach(ctx, shim.close(), "loopback endpoint close");
|
|
1650
1671
|
}
|
|
1651
1672
|
runtime.registered = true;
|
|
1652
1673
|
return true;
|
|
1653
1674
|
} catch (error) {
|
|
1654
1675
|
ctx.logger.error(`dsh-qoder-connect: ${variant.displayName} provider registration failed`, error);
|
|
1655
|
-
shim.close();
|
|
1676
|
+
detach(ctx, shim.close(), "loopback endpoint close");
|
|
1656
1677
|
return false;
|
|
1657
1678
|
}
|
|
1658
1679
|
}
|
|
@@ -1906,7 +1927,7 @@ function apply(ctx, config) {
|
|
|
1906
1927
|
stopped = true;
|
|
1907
1928
|
for (const timer of timers) clearInterval(timer);
|
|
1908
1929
|
timers.length = 0;
|
|
1909
|
-
clearHostHeartbeat();
|
|
1930
|
+
detach(ctx, clearHostHeartbeat(), "host heartbeat cleanup");
|
|
1910
1931
|
});
|
|
1911
1932
|
/**
|
|
1912
1933
|
* Fetch one variant's catalog for the current credential.
|
|
@@ -1967,11 +1988,15 @@ function apply(ctx, config) {
|
|
|
1967
1988
|
runtime.catalogSource = "live";
|
|
1968
1989
|
runtime.catalogFetchedAtMs = runtime.client.lastCatalog?.fetchedAtMs ?? Date.now();
|
|
1969
1990
|
runtime.catalogError = void 0;
|
|
1970
|
-
if (lastIdentities.get(runtime.variant.id) === identity)
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1991
|
+
if (lastIdentities.get(runtime.variant.id) === identity) try {
|
|
1992
|
+
runtime.savedCatalogs.set(identity, {
|
|
1993
|
+
source: runtime.client.lastCatalog?.source ?? "unknown",
|
|
1994
|
+
fetchedAtMs: runtime.client.lastCatalog?.fetchedAtMs ?? Date.now(),
|
|
1995
|
+
models: [...models]
|
|
1996
|
+
});
|
|
1997
|
+
} catch (error) {
|
|
1998
|
+
ctx.logger.warn(`dsh-qoder-connect: ${runtime.variant.displayName} catalog could not be saved for this account`, error);
|
|
1999
|
+
}
|
|
1975
2000
|
runtime.invalidate();
|
|
1976
2001
|
})().finally(() => {
|
|
1977
2002
|
if (runtime.inflightFetch?.promise === run) runtime.inflightFetch = void 0;
|
|
@@ -2022,16 +2047,23 @@ function apply(ctx, config) {
|
|
|
2022
2047
|
const syncAll = async () => {
|
|
2023
2048
|
for (const runtime of runtimes) await syncVariant(runtime);
|
|
2024
2049
|
};
|
|
2025
|
-
|
|
2050
|
+
/**
|
|
2051
|
+
* Start both variants, then begin the credential sweep.
|
|
2052
|
+
*
|
|
2053
|
+
* The chain carries its own failure handler: without one, a rejection here
|
|
2054
|
+
* would be an unhandled rejection — which Node turns into process
|
|
2055
|
+
* termination, taking the whole Harness down over one plugin's startup.
|
|
2056
|
+
*/
|
|
2057
|
+
detach(ctx, Promise.all(runtimes.map(async (runtime) => startVariant(ctx, runtime))).then(() => {
|
|
2026
2058
|
if (stopped) return;
|
|
2027
|
-
if (runtimes.some((runtime) => runtime.registered)) writeHostHeartbeat();
|
|
2028
|
-
syncAll();
|
|
2059
|
+
if (runtimes.some((runtime) => runtime.registered)) detach(ctx, writeHostHeartbeat(), "host heartbeat write");
|
|
2060
|
+
detach(ctx, syncAll(), "credential sweep");
|
|
2029
2061
|
const timer = setInterval(() => {
|
|
2030
|
-
syncAll();
|
|
2062
|
+
detach(ctx, syncAll(), "credential sweep");
|
|
2031
2063
|
}, credentialPollMs());
|
|
2032
2064
|
timer.unref?.();
|
|
2033
2065
|
timers.push(timer);
|
|
2034
|
-
});
|
|
2066
|
+
}), "variant startup");
|
|
2035
2067
|
}
|
|
2036
2068
|
//#endregion
|
|
2037
2069
|
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 };
|
|
@@ -2415,27 +2415,38 @@ var QoderUsageReader = class {
|
|
|
2415
2415
|
* Re-arranges standard Base64 chunks and translates characters against
|
|
2416
2416
|
* a custom alphabet to satisfy Qoder upstream WAF requirements.
|
|
2417
2417
|
*
|
|
2418
|
+
* The translation runs through a byte table applied to a Buffer instead of a
|
|
2419
|
+
* per-character string loop. The loop form (`out += char` plus
|
|
2420
|
+
* `alphabet.indexOf(char)`) costs ~800 ms on a 1 MB body and runs on the main
|
|
2421
|
+
* thread once per model request, which saturated the Harness event loop as
|
|
2422
|
+
* soon as a few large-context sessions were in flight. The table form is
|
|
2423
|
+
* byte-for-byte identical (the input is always Base64, i.e. ASCII) and about
|
|
2424
|
+
* 24x faster.
|
|
2425
|
+
*
|
|
2418
2426
|
* @module dsh-provider-qoder/qoder/transport/wire/encoding
|
|
2419
2427
|
*/
|
|
2420
2428
|
const qoderCustomAlphabet = "_doRTgHZBKcGVjlvpC,@aFSx#DPuNJme&i*MzLOEn)sUrthbf%Y^w.(kIQyXqWA!";
|
|
2421
2429
|
const qoderStdAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2430
|
+
/**
|
|
2431
|
+
* Byte translation table: every byte maps to itself unless it is a Base64
|
|
2432
|
+
* alphabet character (→ the custom alphabet) or the `=` pad (→ `$`).
|
|
2433
|
+
*/
|
|
2434
|
+
const encodeTable = (() => {
|
|
2435
|
+
const table = /* @__PURE__ */ new Uint8Array(256);
|
|
2436
|
+
for (let index = 0; index < 256; index++) table[index] = index;
|
|
2437
|
+
for (let index = 0; index < 64; index++) table[qoderStdAlphabet.charCodeAt(index)] = qoderCustomAlphabet.charCodeAt(index);
|
|
2438
|
+
table["=".charCodeAt(0)] = "$".charCodeAt(0);
|
|
2439
|
+
return table;
|
|
2440
|
+
})();
|
|
2422
2441
|
function qoderEncodeBody(plaintext) {
|
|
2423
2442
|
const std = Buffer.isBuffer(plaintext) ? plaintext.toString("base64") : Buffer.from(plaintext).toString("base64");
|
|
2424
2443
|
const n = std.length;
|
|
2425
2444
|
const a = Math.floor(n / 3);
|
|
2426
2445
|
const rearranged = std.slice(n - a) + std.slice(a, n - a) + std.slice(0, a);
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
if (c === "=") out += "$";
|
|
2432
|
-
else {
|
|
2433
|
-
const idx = qoderStdAlphabet.indexOf(c);
|
|
2434
|
-
if (idx >= 0) out += qoderCustomAlphabet[idx];
|
|
2435
|
-
else out += c;
|
|
2436
|
-
}
|
|
2437
|
-
}
|
|
2438
|
-
return out;
|
|
2446
|
+
const source = Buffer.from(rearranged, "latin1");
|
|
2447
|
+
const target = Buffer.allocUnsafe(n);
|
|
2448
|
+
for (let index = 0; index < n; index++) target[index] = encodeTable[source[index] ?? 0] ?? 0;
|
|
2449
|
+
return target.toString("latin1");
|
|
2439
2450
|
}
|
|
2440
2451
|
//#endregion
|
|
2441
2452
|
//#region src/qoder/transport/wire/thinking.ts
|
|
@@ -4002,7 +4013,7 @@ const DEFAULT_MAX_TOKENS = 32768;
|
|
|
4002
4013
|
const DEFAULT_CONTEXT_WINDOW = 18e4;
|
|
4003
4014
|
//#endregion
|
|
4004
4015
|
//#region src/version.ts
|
|
4005
|
-
const QODER_CONNECT_VERSION = "0.1.
|
|
4016
|
+
const QODER_CONNECT_VERSION = "0.1.2";
|
|
4006
4017
|
//#endregion
|
|
4007
4018
|
//#region src/host-heartbeat.ts
|
|
4008
4019
|
/**
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-qoder-connect",
|
|
3
3
|
"displayName": "DSH Qoder Connect",
|
|
4
4
|
"description": "将 Qoder(国内版 / 国际版)的模型接入 DeepSeek Harness — bring Qoder (CN & international) models into DeepSeek Harness.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.2",
|
|
6
6
|
"author": "corrinehu",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"dsh-plugin",
|