@lynn123411/dsh-a6api 1.0.0 → 1.0.1
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/lib/client.js +105 -33
- package/lib/client.js.map +3 -3
- package/lib/index.js +50 -27
- package/lib/index.js.map +2 -2
- package/lib/types/server/sync.d.ts +1 -1
- package/lib/types/types.d.ts +4 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -916,7 +916,8 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
916
916
|
messages: [{ role: "user", content: "1" }],
|
|
917
917
|
max_tokens: 1
|
|
918
918
|
}),
|
|
919
|
-
|
|
919
|
+
// 推理模型(如 grok-4.6)实测单次响应可达 40-90s+,阈值过短会被频繁掐断导致探测失败
|
|
920
|
+
signal: AbortSignal.timeout(18e4)
|
|
920
921
|
});
|
|
921
922
|
if (res.ok) {
|
|
922
923
|
requestOk = true;
|
|
@@ -925,11 +926,16 @@ async function probeSingleModel(baseURL, apiKey, userId, accessToken, modelName)
|
|
|
925
926
|
requestError = `HTTP ${res.status}: ${errText.slice(0, 150)}`;
|
|
926
927
|
}
|
|
927
928
|
} catch (err) {
|
|
928
|
-
|
|
929
|
+
const raw = err?.message || String(err);
|
|
930
|
+
if (raw.includes("aborted due to timeout") || err?.name === "TimeoutError") {
|
|
931
|
+
requestError = "\u63A2\u6D4B\u8D85\u65F6(\u9608\u503C180\u79D2) \u2014 \u63A8\u7406\u6A21\u578B\u54CD\u5E94\u8F83\u6162,\u5DF2\u4FDD\u7559\u4E0A\u6B21\u5546\u6237\u6570\u636E,\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
932
|
+
} else {
|
|
933
|
+
requestError = raw;
|
|
934
|
+
}
|
|
929
935
|
}
|
|
930
936
|
const durationMs = Date.now() - startTime;
|
|
931
937
|
if (requestOk && (userId || accessToken)) {
|
|
932
|
-
await sleep(
|
|
938
|
+
await sleep(1200);
|
|
933
939
|
try {
|
|
934
940
|
const logs = await fetchRecentLogs(userId, accessToken, 15);
|
|
935
941
|
const minTimestamp = Math.floor(startTime / 1e3) - 10;
|
|
@@ -1019,6 +1025,13 @@ import * as fsp from "node:fs/promises";
|
|
|
1019
1025
|
import * as path from "node:path";
|
|
1020
1026
|
import * as os from "node:os";
|
|
1021
1027
|
var A6API_CRED_REF = "A6API_API_KEY";
|
|
1028
|
+
async function atomicWriteFile(filePath, content, mode = 384) {
|
|
1029
|
+
const dir = path.dirname(filePath);
|
|
1030
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
1031
|
+
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
1032
|
+
await fsp.writeFile(tmpPath, content, { mode });
|
|
1033
|
+
await fsp.rename(tmpPath, filePath);
|
|
1034
|
+
}
|
|
1022
1035
|
function dshHome() {
|
|
1023
1036
|
return process.env.DSH_HOME || path.join(os.homedir(), ".dsh");
|
|
1024
1037
|
}
|
|
@@ -1070,8 +1083,8 @@ async function readPluginConfig() {
|
|
|
1070
1083
|
}
|
|
1071
1084
|
async function savePluginConfig(config) {
|
|
1072
1085
|
const filePath = configFile();
|
|
1073
|
-
|
|
1074
|
-
await
|
|
1086
|
+
const { apiKey: _apiKey, ...safeConfig } = config;
|
|
1087
|
+
await atomicWriteFile(filePath, JSON.stringify(safeConfig, null, 2));
|
|
1075
1088
|
if (config.apiKey && config.apiKey.trim()) {
|
|
1076
1089
|
await writeCredentialKey(A6API_CRED_REF, config.apiKey.trim());
|
|
1077
1090
|
}
|
|
@@ -1144,8 +1157,7 @@ async function writeCredentialKey(refKey, value) {
|
|
|
1144
1157
|
lines.push("refs:", ` ${refKey}: ${JSON.stringify(value)}`);
|
|
1145
1158
|
}
|
|
1146
1159
|
}
|
|
1147
|
-
await
|
|
1148
|
-
await fsp.writeFile(cFile, lines.join("\n"), "utf8");
|
|
1160
|
+
await atomicWriteFile(cFile, lines.join("\n"), 384);
|
|
1149
1161
|
}
|
|
1150
1162
|
async function syncToDshSettings(baseURL, modelIds) {
|
|
1151
1163
|
const sFile = settingsFile();
|
|
@@ -1238,8 +1250,7 @@ async function syncToDshSettings(baseURL, modelIds) {
|
|
|
1238
1250
|
} else {
|
|
1239
1251
|
lines.push(`llm-pi-ai:`, ` providers:`, ...a6apiBlockLines);
|
|
1240
1252
|
}
|
|
1241
|
-
await
|
|
1242
|
-
await fsp.writeFile(sFile, lines.join("\n"), "utf8");
|
|
1253
|
+
await atomicWriteFile(sFile, lines.join("\n"), 420);
|
|
1243
1254
|
}
|
|
1244
1255
|
async function getDshConfiguredModels() {
|
|
1245
1256
|
try {
|
|
@@ -1279,13 +1290,23 @@ async function getDshConfiguredModels() {
|
|
|
1279
1290
|
var name = "@lynn123411/dsh-a6api";
|
|
1280
1291
|
var inject = ["webServer"];
|
|
1281
1292
|
var PREFIX = "/api/dsh-a6api";
|
|
1293
|
+
var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
1294
|
+
function maskConfig(c) {
|
|
1295
|
+
return {
|
|
1296
|
+
...c,
|
|
1297
|
+
apiKey: c.apiKey ? MASK : "",
|
|
1298
|
+
accessToken: c.accessToken || c.sessionCookie ? MASK : "",
|
|
1299
|
+
sessionCookie: "",
|
|
1300
|
+
userId: c.userId ? MASK : "",
|
|
1301
|
+
hasApiKey: Boolean(c.apiKey),
|
|
1302
|
+
hasToken: Boolean(c.accessToken || c.sessionCookie)
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1282
1305
|
function sendJson(res, status, body) {
|
|
1283
1306
|
res.writeHead(status, {
|
|
1284
1307
|
"content-type": "application/json; charset=utf-8",
|
|
1285
|
-
"cache-control": "no-cache"
|
|
1286
|
-
|
|
1287
|
-
"access-control-allow-headers": "*",
|
|
1288
|
-
"access-control-allow-methods": "GET, POST, OPTIONS"
|
|
1308
|
+
"cache-control": "no-cache"
|
|
1309
|
+
// 不设 access-control-allow-origin:仅允许同源调用,阻断跨站读取与 CSRF 预检
|
|
1289
1310
|
});
|
|
1290
1311
|
res.end(JSON.stringify(body));
|
|
1291
1312
|
}
|
|
@@ -1313,6 +1334,7 @@ async function parseJsonBody(req) {
|
|
|
1313
1334
|
}
|
|
1314
1335
|
}
|
|
1315
1336
|
var merchantCardCache = /* @__PURE__ */ new Map();
|
|
1337
|
+
var MERCHANT_CARD_TTL_MS = 15 * 60 * 1e3;
|
|
1316
1338
|
function apply(ctx) {
|
|
1317
1339
|
const webServer = ctx.webServer || (ctx.get ? ctx.get("webServer") : null);
|
|
1318
1340
|
if (webServer && typeof webServer.register === "function") {
|
|
@@ -1324,11 +1346,7 @@ function apply(ctx) {
|
|
|
1324
1346
|
const url = new URL(req.url || "/", "http://localhost");
|
|
1325
1347
|
const pathname = url.pathname.replace(PREFIX, "") || "/";
|
|
1326
1348
|
if (req.method === "OPTIONS") {
|
|
1327
|
-
res.writeHead(204
|
|
1328
|
-
"access-control-allow-origin": "*",
|
|
1329
|
-
"access-control-allow-headers": "*",
|
|
1330
|
-
"access-control-allow-methods": "GET, POST, OPTIONS"
|
|
1331
|
-
});
|
|
1349
|
+
res.writeHead(204);
|
|
1332
1350
|
return res.end();
|
|
1333
1351
|
}
|
|
1334
1352
|
try {
|
|
@@ -1360,18 +1378,22 @@ function apply(ctx) {
|
|
|
1360
1378
|
];
|
|
1361
1379
|
}
|
|
1362
1380
|
if (config.userId || token) {
|
|
1363
|
-
const missing = modelIds.filter((m) =>
|
|
1381
|
+
const missing = modelIds.filter((m) => {
|
|
1382
|
+
const entry = merchantCardCache.get(m.toLowerCase());
|
|
1383
|
+
return !entry || Date.now() - entry.at >= MERCHANT_CARD_TTL_MS;
|
|
1384
|
+
});
|
|
1364
1385
|
if (missing.length > 0) {
|
|
1365
1386
|
const found = await getKnownMerchantsFromLogs(config.userId, token, missing);
|
|
1366
1387
|
for (const [mName, card] of Object.entries(found)) {
|
|
1367
|
-
merchantCardCache.set(mName.toLowerCase(), card);
|
|
1388
|
+
merchantCardCache.set(mName.toLowerCase(), { card, at: Date.now() });
|
|
1368
1389
|
}
|
|
1369
1390
|
}
|
|
1370
1391
|
}
|
|
1371
1392
|
const dshSet = new Set(dshConfiguredModels);
|
|
1372
1393
|
const models = modelIds.map((mId) => {
|
|
1373
1394
|
const meta = resolveModelMeta(mId);
|
|
1374
|
-
const
|
|
1395
|
+
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1396
|
+
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
1375
1397
|
return {
|
|
1376
1398
|
model_name: mId,
|
|
1377
1399
|
brand: meta.brand,
|
|
@@ -1386,7 +1408,7 @@ function apply(ctx) {
|
|
|
1386
1408
|
});
|
|
1387
1409
|
const recentLogs = await fetchRecentLogs(config.userId, token, 20);
|
|
1388
1410
|
const response = {
|
|
1389
|
-
config,
|
|
1411
|
+
config: maskConfig(config),
|
|
1390
1412
|
balance,
|
|
1391
1413
|
models,
|
|
1392
1414
|
dshConfiguredModels,
|
|
@@ -1397,10 +1419,11 @@ function apply(ctx) {
|
|
|
1397
1419
|
if (pathname === "/config" && req.method === "POST") {
|
|
1398
1420
|
const body = await parseJsonBody(req);
|
|
1399
1421
|
const current = await readPluginConfig();
|
|
1400
|
-
const rawToken = body.accessToken !== void 0 ? body.accessToken : body.sessionCookie !== void 0 ? body.sessionCookie : current.accessToken || current.sessionCookie || "";
|
|
1422
|
+
const rawToken = body.accessToken !== void 0 && body.accessToken !== MASK ? body.accessToken : body.sessionCookie !== void 0 && body.sessionCookie !== MASK ? body.sessionCookie : current.accessToken || current.sessionCookie || "";
|
|
1423
|
+
const newApiKey = body.apiKey !== void 0 && body.apiKey !== MASK ? body.apiKey : current.apiKey;
|
|
1401
1424
|
const updated = {
|
|
1402
1425
|
baseURL: body.baseURL !== void 0 ? body.baseURL : current.baseURL,
|
|
1403
|
-
apiKey:
|
|
1426
|
+
apiKey: newApiKey,
|
|
1404
1427
|
accessToken: rawToken,
|
|
1405
1428
|
sessionCookie: rawToken,
|
|
1406
1429
|
userId: body.userId !== void 0 ? body.userId : current.userId,
|
|
@@ -1415,7 +1438,7 @@ function apply(ctx) {
|
|
|
1415
1438
|
if (updated.activeModels.length > 0) {
|
|
1416
1439
|
await syncToDshSettings(updated.baseURL, updated.activeModels);
|
|
1417
1440
|
}
|
|
1418
|
-
return sendJson(res, 200, { ok: true, config: updated, balance });
|
|
1441
|
+
return sendJson(res, 200, { ok: true, config: maskConfig(updated), balance });
|
|
1419
1442
|
}
|
|
1420
1443
|
if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1421
1444
|
const config = await readPluginConfig();
|
|
@@ -1438,7 +1461,7 @@ function apply(ctx) {
|
|
|
1438
1461
|
if (modelName && modelName !== "all") {
|
|
1439
1462
|
const result = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, modelName);
|
|
1440
1463
|
if (result.merchant) {
|
|
1441
|
-
merchantCardCache.set(modelName.toLowerCase(), result.merchant);
|
|
1464
|
+
merchantCardCache.set(modelName.toLowerCase(), { card: result.merchant, at: Date.now() });
|
|
1442
1465
|
}
|
|
1443
1466
|
return sendJson(res, 200, { ok: true, result });
|
|
1444
1467
|
}
|
|
@@ -1453,7 +1476,7 @@ function apply(ctx) {
|
|
|
1453
1476
|
for (const m of modelIds) {
|
|
1454
1477
|
const r = await probeSingleModel(config.baseURL, config.apiKey, config.userId, token, m);
|
|
1455
1478
|
if (r.merchant) {
|
|
1456
|
-
merchantCardCache.set(m.toLowerCase(), r.merchant);
|
|
1479
|
+
merchantCardCache.set(m.toLowerCase(), { card: r.merchant, at: Date.now() });
|
|
1457
1480
|
}
|
|
1458
1481
|
results.push(r);
|
|
1459
1482
|
}
|