@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/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
- signal: AbortSignal.timeout(15e3)
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
- requestError = err?.message || String(err);
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(700);
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
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
1074
- await fsp.writeFile(filePath, JSON.stringify(config, null, 2), "utf8");
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 fsp.mkdir(path.dirname(cFile), { recursive: true });
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 fsp.mkdir(path.dirname(sFile), { recursive: true });
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
- "access-control-allow-origin": "*",
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) => !merchantCardCache.has(m.toLowerCase()));
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 cachedCard = merchantCardCache.get(mId.toLowerCase());
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: body.apiKey !== void 0 ? body.apiKey : current.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
  }