qwenproxy-cli 1.4.7 → 1.5.0
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/dist/clean-cache.js +38 -5
- package/dist/delete-chats.js +262 -74
- package/dist/index.js +327 -111
- package/dist/login.js +169 -59
- package/dist/reset-cooldowns.js +25 -5
- package/package.json +1 -1
- package/src/core/account-manager.ts +9 -0
- package/src/core/database.ts +10 -5
- package/src/core/server-log-buffer.ts +10 -3
- package/src/core/version.ts +1 -1
- package/src/routes/chat/account.ts +22 -10
- package/src/routes/chat/helpers.ts +29 -1
- package/src/routes/chat/retry-policy.ts +9 -8
- package/src/routes/chat/streaming.ts +12 -5
- package/src/routes/upload.ts +19 -14
- package/src/services/playwright.ts +180 -58
- package/src/services/qwen-chat-pool.ts +18 -5
- package/src/services/qwen-headers.ts +2 -1
- package/src/services/qwen.ts +65 -15
package/dist/index.js
CHANGED
|
@@ -1608,6 +1608,7 @@ function runMigrations(db2) {
|
|
|
1608
1608
|
"sec_ch_ua_mobile TEXT",
|
|
1609
1609
|
"sec_ch_ua_platform TEXT",
|
|
1610
1610
|
"version TEXT",
|
|
1611
|
+
"refresh_token TEXT",
|
|
1611
1612
|
"captured_at INTEGER DEFAULT 0"
|
|
1612
1613
|
];
|
|
1613
1614
|
for (const col of authSessionCols) {
|
|
@@ -1686,11 +1687,11 @@ function saveAuthSession(accountId, session) {
|
|
|
1686
1687
|
INSERT OR REPLACE INTO qwen_auth_sessions (
|
|
1687
1688
|
account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
1688
1689
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
1689
|
-
user_id, token_expires_at, captured_at, updated_at
|
|
1690
|
+
user_id, token_expires_at, refresh_token, captured_at, updated_at
|
|
1690
1691
|
) VALUES (
|
|
1691
1692
|
?, ?, ?, ?, ?, ?,
|
|
1692
1693
|
?, ?, ?, ?,
|
|
1693
|
-
?, ?, ?, datetime('now')
|
|
1694
|
+
?, ?, ?, ?, datetime('now')
|
|
1694
1695
|
)
|
|
1695
1696
|
`);
|
|
1696
1697
|
stmt.run(
|
|
@@ -1706,6 +1707,7 @@ function saveAuthSession(accountId, session) {
|
|
|
1706
1707
|
session.version || null,
|
|
1707
1708
|
session.userId || null,
|
|
1708
1709
|
session.tokenExpiresAt || null,
|
|
1710
|
+
session.refreshToken || null,
|
|
1709
1711
|
session.capturedAt ?? Date.now()
|
|
1710
1712
|
);
|
|
1711
1713
|
}
|
|
@@ -1714,7 +1716,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
1714
1716
|
const row = database.prepare(
|
|
1715
1717
|
`SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
1716
1718
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
1717
|
-
user_id, token_expires_at, captured_at
|
|
1719
|
+
user_id, token_expires_at, refresh_token, captured_at
|
|
1718
1720
|
FROM qwen_auth_sessions WHERE account_id = ?`
|
|
1719
1721
|
).get(accountId);
|
|
1720
1722
|
if (!row) return null;
|
|
@@ -1722,7 +1724,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
1722
1724
|
if (capturedAt <= 0 || Date.now() - capturedAt > maxAgeMs) {
|
|
1723
1725
|
return null;
|
|
1724
1726
|
}
|
|
1725
|
-
if (row.token_expires_at) {
|
|
1727
|
+
if (!row.refresh_token && row.token_expires_at) {
|
|
1726
1728
|
const tokenExpMs = Number(row.token_expires_at) * 1e3;
|
|
1727
1729
|
if (tokenExpMs <= Date.now() + 5 * 60 * 1e3) {
|
|
1728
1730
|
return null;
|
|
@@ -1744,6 +1746,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
1744
1746
|
version: row.version || void 0,
|
|
1745
1747
|
userId: row.user_id || void 0,
|
|
1746
1748
|
tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : void 0,
|
|
1749
|
+
refreshToken: row.refresh_token || void 0,
|
|
1747
1750
|
capturedAt
|
|
1748
1751
|
};
|
|
1749
1752
|
}
|
|
@@ -2983,7 +2986,15 @@ function defaultCooldownDurationMs() {
|
|
|
2983
2986
|
return computeQuotaCooldownMs(Date.now());
|
|
2984
2987
|
}
|
|
2985
2988
|
function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
|
|
2989
|
+
if (cooldownMs !== void 0 && cooldownMs <= 0) {
|
|
2990
|
+
clearAccountCooldown(accountId);
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2986
2993
|
const duration = cooldownMs ?? defaultCooldownDurationMs();
|
|
2994
|
+
if (duration <= 0) {
|
|
2995
|
+
clearAccountCooldown(accountId);
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2987
2998
|
const until = Date.now() + duration;
|
|
2988
2999
|
const cooldownReason = reason ?? "RateLimited";
|
|
2989
3000
|
cooldowns.set(accountId, {
|
|
@@ -3983,7 +3994,8 @@ function buildQwenRequestHeaders(opts) {
|
|
|
3983
3994
|
"bx-v": opts.bxV || "2.5.37",
|
|
3984
3995
|
source: "web",
|
|
3985
3996
|
version: opts.version || getQwenWebVersion(),
|
|
3986
|
-
timezone: opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3997
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3998
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
3987
3999
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
3988
4000
|
// back to the static fingerprint otherwise.
|
|
3989
4001
|
"sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
|
|
@@ -4269,6 +4281,7 @@ __export(playwright_exports, {
|
|
|
4269
4281
|
parseCookiesForBrowser: () => parseCookiesForBrowser,
|
|
4270
4282
|
pruneAllPlaywrightProfiles: () => pruneAllPlaywrightProfiles,
|
|
4271
4283
|
prunePlaywrightProfileCaches: () => prunePlaywrightProfileCaches,
|
|
4284
|
+
refreshAccountToken: () => refreshAccountToken,
|
|
4272
4285
|
refreshHeaders: () => refreshHeaders,
|
|
4273
4286
|
refreshHeadersWithProfileReset: () => refreshHeadersWithProfileReset,
|
|
4274
4287
|
registerPlaywrightAccountForTests: () => registerPlaywrightAccountForTests,
|
|
@@ -5904,6 +5917,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
5904
5917
|
secChUaPlatform: cache3.headers["sec-ch-ua-platform"] || void 0,
|
|
5905
5918
|
version: cache3.headers["version"] || void 0,
|
|
5906
5919
|
tokenExpiresAt: exp || void 0,
|
|
5920
|
+
refreshToken: apiResult.refreshToken,
|
|
5907
5921
|
capturedAt: Date.now()
|
|
5908
5922
|
});
|
|
5909
5923
|
}
|
|
@@ -5937,6 +5951,13 @@ async function loginToQwen(accountId, email, password) {
|
|
|
5937
5951
|
markAccountHeadersReady(accountId);
|
|
5938
5952
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5939
5953
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
5954
|
+
let rTok = uiResult.refreshToken;
|
|
5955
|
+
if (!rTok) {
|
|
5956
|
+
try {
|
|
5957
|
+
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
5958
|
+
} catch {
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5940
5961
|
saveAuthSession2(accountId, {
|
|
5941
5962
|
cookie: cookieStr,
|
|
5942
5963
|
userAgent: cache3.headers["user-agent"] || "",
|
|
@@ -5948,6 +5969,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
5948
5969
|
secChUaPlatform: cache3.headers["sec-ch-ua-platform"] || void 0,
|
|
5949
5970
|
version: cache3.headers["version"] || void 0,
|
|
5950
5971
|
tokenExpiresAt: exp || void 0,
|
|
5972
|
+
refreshToken: rTok,
|
|
5951
5973
|
capturedAt: Date.now()
|
|
5952
5974
|
});
|
|
5953
5975
|
}
|
|
@@ -6126,7 +6148,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6126
6148
|
});
|
|
6127
6149
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
6128
6150
|
});
|
|
6129
|
-
return { success: true };
|
|
6151
|
+
return { success: true, token, refreshToken };
|
|
6130
6152
|
}
|
|
6131
6153
|
}
|
|
6132
6154
|
return {
|
|
@@ -6369,6 +6391,137 @@ async function loginViaUi(page, email, password) {
|
|
|
6369
6391
|
return { success: false, reason: err?.message || String(err) };
|
|
6370
6392
|
}
|
|
6371
6393
|
}
|
|
6394
|
+
async function refreshAccountToken(accountId) {
|
|
6395
|
+
const page = accountPages.get(accountId);
|
|
6396
|
+
if (!page || page.isClosed()) {
|
|
6397
|
+
return { success: false, error: "Account page not active" };
|
|
6398
|
+
}
|
|
6399
|
+
const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6400
|
+
const session = getValidAuthSession2(accountId);
|
|
6401
|
+
let refreshToken = session?.refreshToken;
|
|
6402
|
+
if (!refreshToken) {
|
|
6403
|
+
try {
|
|
6404
|
+
refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
6405
|
+
} catch {
|
|
6406
|
+
}
|
|
6407
|
+
}
|
|
6408
|
+
if (refreshToken) {
|
|
6409
|
+
try {
|
|
6410
|
+
const refreshResult = await page.evaluate(
|
|
6411
|
+
async ({ rTok }) => {
|
|
6412
|
+
try {
|
|
6413
|
+
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6414
|
+
method: "GET",
|
|
6415
|
+
credentials: "include",
|
|
6416
|
+
headers: {
|
|
6417
|
+
accept: "application/json, text/plain, */*",
|
|
6418
|
+
source: "web",
|
|
6419
|
+
version: "0.3.11",
|
|
6420
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
6421
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
6422
|
+
authorization: `Bearer ${rTok}`
|
|
6423
|
+
}
|
|
6424
|
+
});
|
|
6425
|
+
const json2 = await response.json().catch(() => null);
|
|
6426
|
+
return { ok: response.ok, status: response.status, data: json2 };
|
|
6427
|
+
} catch (e) {
|
|
6428
|
+
return { ok: false, error: e.message };
|
|
6429
|
+
}
|
|
6430
|
+
},
|
|
6431
|
+
{ rTok: refreshToken }
|
|
6432
|
+
);
|
|
6433
|
+
const json = refreshResult?.data;
|
|
6434
|
+
if (json && json.success === true && json.data) {
|
|
6435
|
+
const newAccessToken = json.data.access_token || json.data.token;
|
|
6436
|
+
const newRefreshToken = json.data.refresh_token || refreshToken;
|
|
6437
|
+
if (newAccessToken) {
|
|
6438
|
+
await page.context().addCookies([
|
|
6439
|
+
{
|
|
6440
|
+
name: "token",
|
|
6441
|
+
value: newAccessToken,
|
|
6442
|
+
domain: ".qwen.ai",
|
|
6443
|
+
path: "/",
|
|
6444
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
6445
|
+
httpOnly: false,
|
|
6446
|
+
secure: true,
|
|
6447
|
+
sameSite: "Lax"
|
|
6448
|
+
}
|
|
6449
|
+
]);
|
|
6450
|
+
await page.evaluate(
|
|
6451
|
+
({ tok, rTok }) => {
|
|
6452
|
+
try {
|
|
6453
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
6454
|
+
localStorage.setItem("token", tok);
|
|
6455
|
+
localStorage.setItem("access_token", tok);
|
|
6456
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
6457
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
6458
|
+
} catch {
|
|
6459
|
+
}
|
|
6460
|
+
},
|
|
6461
|
+
{ tok: newAccessToken, rTok: newRefreshToken }
|
|
6462
|
+
).catch(() => {
|
|
6463
|
+
});
|
|
6464
|
+
const liveCookies = await page.context().cookies();
|
|
6465
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6466
|
+
const cache3 = getHeaderCache(accountId);
|
|
6467
|
+
cache3.headers.cookie = cookieStr;
|
|
6468
|
+
cache3.lastRefresh = Date.now();
|
|
6469
|
+
markAccountHeadersReady(accountId);
|
|
6470
|
+
saveAuthSession2(accountId, {
|
|
6471
|
+
cookie: cookieStr,
|
|
6472
|
+
userAgent: cache3.headers["user-agent"] || session?.userAgent || "",
|
|
6473
|
+
bxV: cache3.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
6474
|
+
bxUa: cache3.headers["bx-ua"] || session?.bxUa || "",
|
|
6475
|
+
bxUmidtoken: cache3.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
6476
|
+
secChUa: cache3.headers["sec-ch-ua"] || session?.secChUa,
|
|
6477
|
+
secChUaMobile: cache3.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
6478
|
+
secChUaPlatform: cache3.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
6479
|
+
version: cache3.headers["version"] || session?.version,
|
|
6480
|
+
userId: session?.userId,
|
|
6481
|
+
tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
|
|
6482
|
+
refreshToken: newRefreshToken,
|
|
6483
|
+
capturedAt: Date.now()
|
|
6484
|
+
});
|
|
6485
|
+
return { success: true, token: newAccessToken };
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
} catch {
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
6492
|
+
const creds = getAccountCredentials2(accountId);
|
|
6493
|
+
if (creds && creds.email && creds.password) {
|
|
6494
|
+
const apiResult = await loginViaApi(page, creds.email, creds.password);
|
|
6495
|
+
if (apiResult.success) {
|
|
6496
|
+
const liveCookies = await page.context().cookies();
|
|
6497
|
+
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
6498
|
+
if (tokenCookie) {
|
|
6499
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6500
|
+
const cache3 = getHeaderCache(accountId);
|
|
6501
|
+
cache3.headers.cookie = cookieStr;
|
|
6502
|
+
cache3.lastRefresh = Date.now();
|
|
6503
|
+
markAccountHeadersReady(accountId);
|
|
6504
|
+
saveAuthSession2(accountId, {
|
|
6505
|
+
cookie: cookieStr,
|
|
6506
|
+
userAgent: cache3.headers["user-agent"] || session?.userAgent || "",
|
|
6507
|
+
bxV: cache3.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
6508
|
+
bxUa: cache3.headers["bx-ua"] || session?.bxUa || "",
|
|
6509
|
+
bxUmidtoken: cache3.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
6510
|
+
secChUa: cache3.headers["sec-ch-ua"] || session?.secChUa,
|
|
6511
|
+
secChUaMobile: cache3.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
6512
|
+
secChUaPlatform: cache3.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
6513
|
+
version: cache3.headers["version"] || session?.version,
|
|
6514
|
+
userId: session?.userId,
|
|
6515
|
+
tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
|
|
6516
|
+
refreshToken: apiResult.refreshToken,
|
|
6517
|
+
capturedAt: Date.now()
|
|
6518
|
+
});
|
|
6519
|
+
return { success: true, token: tokenCookie.value };
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6523
|
+
return { success: false, error: "Could not refresh or re-authenticate token" };
|
|
6524
|
+
}
|
|
6372
6525
|
async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
|
|
6373
6526
|
const page = pageOverride ?? accountPages.get(accountId);
|
|
6374
6527
|
if (!page || page.isClosed()) {
|
|
@@ -6885,16 +7038,9 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
6885
7038
|
}
|
|
6886
7039
|
};
|
|
6887
7040
|
if (forceReauth) {
|
|
6888
|
-
const
|
|
6889
|
-
if (
|
|
6890
|
-
|
|
6891
|
-
if (liveCookies.some((c) => c.name === "token")) {
|
|
6892
|
-
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6893
|
-
cache3.headers.cookie = cookieStr;
|
|
6894
|
-
cache3.lastRefresh = Date.now();
|
|
6895
|
-
markAccountHeadersReady(accountId);
|
|
6896
|
-
return;
|
|
6897
|
-
}
|
|
7041
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
7042
|
+
if (refreshed.success) {
|
|
7043
|
+
return;
|
|
6898
7044
|
}
|
|
6899
7045
|
await executeReauth();
|
|
6900
7046
|
} else {
|
|
@@ -7330,50 +7476,15 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7330
7476
|
const now = Date.now();
|
|
7331
7477
|
const currentUrl = page.url();
|
|
7332
7478
|
const cookie = await getCookies(accountId);
|
|
7333
|
-
if (cookie && isTokenExpiringSoon(cookie,
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
method: "GET",
|
|
7343
|
-
credentials: "include"
|
|
7344
|
-
}).catch(() => {
|
|
7345
|
-
});
|
|
7346
|
-
}).catch(() => {
|
|
7347
|
-
});
|
|
7348
|
-
const liveCookies = await page.context().cookies();
|
|
7349
|
-
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
7350
|
-
if (tokenCookie) {
|
|
7351
|
-
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
7352
|
-
const cache3 = getHeaderCache(accountId);
|
|
7353
|
-
cache3.headers.cookie = cookieStr;
|
|
7354
|
-
cache3.lastRefresh = Date.now();
|
|
7355
|
-
markAccountHeadersReady(accountId);
|
|
7356
|
-
lastKeepAliveNavigation.set(accountId, now);
|
|
7357
|
-
touchAccountActivity(accountId);
|
|
7358
|
-
return true;
|
|
7359
|
-
}
|
|
7360
|
-
} catch {
|
|
7361
|
-
}
|
|
7362
|
-
const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
|
|
7363
|
-
if (!loggedIn) {
|
|
7364
|
-
console.log(
|
|
7365
|
-
`\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
|
|
7366
|
-
);
|
|
7367
|
-
try {
|
|
7368
|
-
await refreshHeadersInternal(accountId, config.timeouts.headers, true);
|
|
7369
|
-
lastKeepAliveNavigation.set(accountId, now);
|
|
7370
|
-
touchAccountActivity(accountId);
|
|
7371
|
-
return true;
|
|
7372
|
-
} catch (err) {
|
|
7373
|
-
console.warn(
|
|
7374
|
-
`[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
|
|
7375
|
-
);
|
|
7376
|
-
}
|
|
7479
|
+
if (cookie && isTokenExpiringSoon(cookie, 5)) {
|
|
7480
|
+
console.log(
|
|
7481
|
+
`\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
7482
|
+
);
|
|
7483
|
+
const res = await refreshAccountToken(accountId);
|
|
7484
|
+
if (res.success) {
|
|
7485
|
+
lastKeepAliveNavigation.set(accountId, now);
|
|
7486
|
+
touchAccountActivity(accountId);
|
|
7487
|
+
return true;
|
|
7377
7488
|
}
|
|
7378
7489
|
}
|
|
7379
7490
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
@@ -8985,8 +9096,11 @@ ${thought}`);
|
|
|
8985
9096
|
}
|
|
8986
9097
|
return sections.join("\n\n");
|
|
8987
9098
|
}
|
|
9099
|
+
function isAnswerPhase(phase) {
|
|
9100
|
+
return typeof phase === "string" && ANSWER_PHASES.has(phase);
|
|
9101
|
+
}
|
|
8988
9102
|
function isThinkingPhase(phase) {
|
|
8989
|
-
return phase === "
|
|
9103
|
+
return typeof phase === "string" && THINKING_PHASES.has(phase);
|
|
8990
9104
|
}
|
|
8991
9105
|
function extractThinkingContent(delta) {
|
|
8992
9106
|
if (Array.isArray(delta?.extra?.summary_title?.content) || Array.isArray(delta?.extra?.summary_thought?.content)) {
|
|
@@ -9077,9 +9191,25 @@ function buildUsage(accumulator) {
|
|
|
9077
9191
|
}
|
|
9078
9192
|
return usage;
|
|
9079
9193
|
}
|
|
9194
|
+
var ANSWER_PHASES, THINKING_PHASES;
|
|
9080
9195
|
var init_helpers = __esm({
|
|
9081
9196
|
"src/routes/chat/helpers.ts"() {
|
|
9082
9197
|
"use strict";
|
|
9198
|
+
ANSWER_PHASES = /* @__PURE__ */ new Set([
|
|
9199
|
+
"answer",
|
|
9200
|
+
"deep_research_answer",
|
|
9201
|
+
"ReportGeneration",
|
|
9202
|
+
"PdfMdGen",
|
|
9203
|
+
"image",
|
|
9204
|
+
"image_generation",
|
|
9205
|
+
"image_gen",
|
|
9206
|
+
"t2i"
|
|
9207
|
+
]);
|
|
9208
|
+
THINKING_PHASES = /* @__PURE__ */ new Set([
|
|
9209
|
+
"think",
|
|
9210
|
+
"thinking_summary",
|
|
9211
|
+
"DeepThinking"
|
|
9212
|
+
]);
|
|
9083
9213
|
}
|
|
9084
9214
|
});
|
|
9085
9215
|
|
|
@@ -9253,18 +9383,18 @@ function classifyRetryAction(err, options) {
|
|
|
9253
9383
|
switchAccount: true,
|
|
9254
9384
|
forceNewChat: true,
|
|
9255
9385
|
retryAfterMs: baseDelayMs,
|
|
9256
|
-
accountCooldownMs:
|
|
9386
|
+
accountCooldownMs: 0,
|
|
9257
9387
|
accountCooldownReason: "PersonalizationFailed"
|
|
9258
9388
|
});
|
|
9259
9389
|
}
|
|
9260
|
-
if (code === "createchatinvalidresponse" || code === "createchatfailed" || code === "unauthorized" || message.includes("401 n\xE3o autorizado") || message.includes("n\xE3o tem permiss\xE3o para acessar") || message.includes("401 unauthorized") || message.includes('"code":"unauthorized"') || message.includes('"code": "unauthorized"')) {
|
|
9390
|
+
if (code === "createchatinvalidresponse" || code === "createchatfailed" || code === "unauthorized" || message.includes("401 n\xE3o autorizado") || message.includes("n\xE3o tem permiss\xE3o para acessar") || message.includes("401 unauthorized") || message.includes('"code":"unauthorized"') || message.includes('"code": "unauthorized"') || message.includes("token has expired")) {
|
|
9261
9391
|
return makeRetryAction("account_initialization_failed", {
|
|
9262
9392
|
switchAccount: true,
|
|
9263
9393
|
forceNewChat: true,
|
|
9264
9394
|
retryWithFullPrompt: true,
|
|
9265
9395
|
retryAfterMs: Math.min(baseDelayMs, 1e3),
|
|
9266
|
-
accountCooldownMs:
|
|
9267
|
-
accountCooldownReason: "
|
|
9396
|
+
accountCooldownMs: 0,
|
|
9397
|
+
accountCooldownReason: "AuthExpired"
|
|
9268
9398
|
});
|
|
9269
9399
|
}
|
|
9270
9400
|
if (isCorruptedChatHistoryError(err)) {
|
|
@@ -9681,8 +9811,8 @@ async function requestCompletionsInBrowser(params) {
|
|
|
9681
9811
|
const choice = chunk?.choices?.[0];
|
|
9682
9812
|
const delta = choice?.delta;
|
|
9683
9813
|
const phase = delta?.phase;
|
|
9684
|
-
const
|
|
9685
|
-
if (choice?.finish_reason || delta?.status === "finished" &&
|
|
9814
|
+
const isAnswerPhase2 = !phase || phase === "answer";
|
|
9815
|
+
if (choice?.finish_reason || delta?.status === "finished" && isAnswerPhase2) {
|
|
9686
9816
|
finished = true;
|
|
9687
9817
|
break;
|
|
9688
9818
|
}
|
|
@@ -10733,7 +10863,19 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
|
|
|
10733
10863
|
} catch {
|
|
10734
10864
|
}
|
|
10735
10865
|
}
|
|
10736
|
-
|
|
10866
|
+
const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
|
|
10867
|
+
if (isUnauthorized) {
|
|
10868
|
+
if (accountId) {
|
|
10869
|
+
try {
|
|
10870
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
10871
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
10872
|
+
if (refreshed.success) {
|
|
10873
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
10874
|
+
return createQwenChatSession(freshHeaders, model, accountId, chatMode);
|
|
10875
|
+
}
|
|
10876
|
+
} catch {
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10737
10879
|
throw new QwenUpstreamError(
|
|
10738
10880
|
`Qwen create chat unauthorized: ${raw.substring(0, 300)}`,
|
|
10739
10881
|
"Unauthorized",
|
|
@@ -11429,16 +11571,28 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
11429
11571
|
});
|
|
11430
11572
|
if (response2.status === 401) {
|
|
11431
11573
|
try {
|
|
11432
|
-
const
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11574
|
+
const rTok = localStorage.getItem("refresh_token");
|
|
11575
|
+
if (rTok) {
|
|
11576
|
+
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
11577
|
+
method: "GET",
|
|
11578
|
+
credentials: "include",
|
|
11579
|
+
headers: {
|
|
11580
|
+
accept: "application/json, text/plain, */*",
|
|
11581
|
+
source: "web",
|
|
11582
|
+
version: "0.3.11",
|
|
11583
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
11584
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
11585
|
+
authorization: `Bearer ${rTok}`
|
|
11586
|
+
},
|
|
11587
|
+
signal: AbortSignal.timeout(5e3)
|
|
11588
|
+
});
|
|
11438
11589
|
const refreshJson = await refreshRes.json().catch(() => null);
|
|
11439
|
-
|
|
11440
|
-
|
|
11590
|
+
const freshTok = refreshJson?.data?.access_token || refreshJson?.data?.token;
|
|
11591
|
+
const nextRTok = refreshJson?.data?.refresh_token;
|
|
11592
|
+
if (refreshJson?.success === true && freshTok) {
|
|
11441
11593
|
localStorage.setItem("token", freshTok);
|
|
11594
|
+
localStorage.setItem("access_token", freshTok);
|
|
11595
|
+
if (nextRTok) localStorage.setItem("refresh_token", nextRTok);
|
|
11442
11596
|
document.cookie = `token=${encodeURIComponent(freshTok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
11443
11597
|
if (headers2["authorization"] || headers2["Authorization"]) {
|
|
11444
11598
|
headers2["authorization"] = `Bearer ${freshTok}`;
|
|
@@ -11476,13 +11630,29 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
11476
11630
|
}
|
|
11477
11631
|
);
|
|
11478
11632
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
11479
|
-
|
|
11633
|
+
let response = await withQwenBrowserPage(
|
|
11480
11634
|
accountId,
|
|
11481
11635
|
evaluateRequest,
|
|
11482
11636
|
void 0,
|
|
11483
11637
|
options.timeoutMs,
|
|
11484
11638
|
recoverOnTimeout
|
|
11485
11639
|
);
|
|
11640
|
+
if (response.status === 401 && accountId) {
|
|
11641
|
+
try {
|
|
11642
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
11643
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
11644
|
+
if (refreshed.success) {
|
|
11645
|
+
response = await withQwenBrowserPage(
|
|
11646
|
+
accountId,
|
|
11647
|
+
evaluateRequest,
|
|
11648
|
+
void 0,
|
|
11649
|
+
options.timeoutMs,
|
|
11650
|
+
recoverOnTimeout
|
|
11651
|
+
);
|
|
11652
|
+
}
|
|
11653
|
+
} catch {
|
|
11654
|
+
}
|
|
11655
|
+
}
|
|
11486
11656
|
return new Response(response.raw, {
|
|
11487
11657
|
status: response.status,
|
|
11488
11658
|
headers: response.contentType ? { "content-type": response.contentType } : void 0
|
|
@@ -12005,7 +12175,11 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
|
|
|
12005
12175
|
);
|
|
12006
12176
|
try {
|
|
12007
12177
|
currentSettings = null;
|
|
12008
|
-
const {
|
|
12178
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
12179
|
+
if (accountId) {
|
|
12180
|
+
await refreshAccountToken2(accountId);
|
|
12181
|
+
}
|
|
12182
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
12009
12183
|
requestHeaders = buildCapturedQwenHeaders(freshHeaders, {
|
|
12010
12184
|
referer: qwenUrl("/settings/personalization")
|
|
12011
12185
|
});
|
|
@@ -12691,8 +12865,18 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12691
12865
|
);
|
|
12692
12866
|
}
|
|
12693
12867
|
};
|
|
12868
|
+
let forceRefresh = options?.forceNewChat === true;
|
|
12869
|
+
if (!forceRefresh && accountId) {
|
|
12870
|
+
try {
|
|
12871
|
+
const basic = await getBasicHeaders2(accountId);
|
|
12872
|
+
if (isTokenExpiringSoon(basic.cookie, 3)) {
|
|
12873
|
+
forceRefresh = true;
|
|
12874
|
+
}
|
|
12875
|
+
} catch {
|
|
12876
|
+
}
|
|
12877
|
+
}
|
|
12694
12878
|
const captured = await getQwenHeaders(
|
|
12695
|
-
|
|
12879
|
+
forceRefresh,
|
|
12696
12880
|
accountId
|
|
12697
12881
|
);
|
|
12698
12882
|
ensureNotAborted();
|
|
@@ -12706,7 +12890,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12706
12890
|
if (options && "chatSessionId" in options) {
|
|
12707
12891
|
if (options.chatSessionId === null || options.chatSessionId === "") {
|
|
12708
12892
|
const acquired = await acquireNewQwenChatSession(
|
|
12709
|
-
|
|
12893
|
+
activeHeaders,
|
|
12710
12894
|
model,
|
|
12711
12895
|
accountId,
|
|
12712
12896
|
options?.chatMode ?? "thread"
|
|
@@ -12714,6 +12898,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12714
12898
|
chatSessionId = acquired.chatId;
|
|
12715
12899
|
leasedWarmChat = acquired.leasedFromPool;
|
|
12716
12900
|
createdNewChat = true;
|
|
12901
|
+
try {
|
|
12902
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
12903
|
+
activeHeaders = fresh.headers;
|
|
12904
|
+
} catch {
|
|
12905
|
+
}
|
|
12717
12906
|
} else {
|
|
12718
12907
|
chatSessionId = options.chatSessionId;
|
|
12719
12908
|
}
|
|
@@ -12721,7 +12910,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12721
12910
|
chatSessionId = captured.chatSessionId;
|
|
12722
12911
|
if (!chatSessionId) {
|
|
12723
12912
|
const acquired = await acquireNewQwenChatSession(
|
|
12724
|
-
|
|
12913
|
+
activeHeaders,
|
|
12725
12914
|
model,
|
|
12726
12915
|
accountId,
|
|
12727
12916
|
options?.chatMode ?? "thread"
|
|
@@ -12729,6 +12918,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
12729
12918
|
chatSessionId = acquired.chatId;
|
|
12730
12919
|
leasedWarmChat = acquired.leasedFromPool;
|
|
12731
12920
|
createdNewChat = true;
|
|
12921
|
+
try {
|
|
12922
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
12923
|
+
activeHeaders = fresh.headers;
|
|
12924
|
+
} catch {
|
|
12925
|
+
}
|
|
12732
12926
|
}
|
|
12733
12927
|
}
|
|
12734
12928
|
ensureNotAborted();
|
|
@@ -13887,10 +14081,12 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
|
|
|
13887
14081
|
}
|
|
13888
14082
|
data = await response.json().catch(() => null);
|
|
13889
14083
|
}
|
|
13890
|
-
const
|
|
14084
|
+
const codeLower = (data?.data?.code || data?.code || "").toLowerCase();
|
|
14085
|
+
const detailsLower = (typeof data?.data?.details === "string" ? data.data.details : typeof data?.message === "string" ? data.message : "").toLowerCase();
|
|
14086
|
+
const is401 = data?.success === false && (codeLower === "unauthorized" || detailsLower.includes("401") || detailsLower.includes("token has expired") || detailsLower.includes("unauthorized"));
|
|
13891
14087
|
if (is401) {
|
|
13892
14088
|
try {
|
|
13893
|
-
const {
|
|
14089
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
13894
14090
|
const { getBasicHeaders: getBasicHeaders3 } = await Promise.resolve().then(() => (init_auth_playwright(), auth_playwright_exports));
|
|
13895
14091
|
const { loadAccounts: loadAccounts3 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
13896
14092
|
const resolvedId2 = accountId ?? loadAccounts3()[0]?.id;
|
|
@@ -13898,16 +14094,18 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
|
|
|
13898
14094
|
console.warn(
|
|
13899
14095
|
`[Upload] STS token 401 \u2014 refreshing session with re-auth and retrying...`
|
|
13900
14096
|
);
|
|
13901
|
-
await
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
14097
|
+
const refreshResult = await refreshAccountToken2(resolvedId2);
|
|
14098
|
+
if (refreshResult.success) {
|
|
14099
|
+
const fresh = await getBasicHeaders3(resolvedId2);
|
|
14100
|
+
headers.cookie = fresh.cookie;
|
|
14101
|
+
headers["user-agent"] = fresh.userAgent;
|
|
14102
|
+
headers["bx-v"] = fresh.bxV;
|
|
14103
|
+
if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
|
|
14104
|
+
if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
|
|
14105
|
+
const retryRes = await doFetch(headers);
|
|
14106
|
+
if (retryRes.ok) {
|
|
14107
|
+
data = await retryRes.json().catch(() => null);
|
|
14108
|
+
}
|
|
13911
14109
|
}
|
|
13912
14110
|
}
|
|
13913
14111
|
} catch (refreshErr) {
|
|
@@ -17022,15 +17220,26 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17022
17220
|
}
|
|
17023
17221
|
}
|
|
17024
17222
|
if (policy.reason === "account_initialization_failed") {
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
currentAccountId
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17223
|
+
const cooldownMs = policy.accountCooldownMs ?? 0;
|
|
17224
|
+
if (cooldownMs > 0) {
|
|
17225
|
+
console.warn(
|
|
17226
|
+
`\u26A0\uFE0F [Chat] Account initialization failed | ${currentAccountEmail} | cooldown=${Math.round(cooldownMs / 1e3)}s`
|
|
17227
|
+
);
|
|
17228
|
+
markAccountFailed(currentAccountId);
|
|
17229
|
+
markAccountRateLimited(
|
|
17230
|
+
currentAccountId,
|
|
17231
|
+
cooldownMs,
|
|
17232
|
+
policy.accountCooldownReason
|
|
17233
|
+
);
|
|
17234
|
+
} else {
|
|
17235
|
+
console.warn(
|
|
17236
|
+
`\u26A0\uFE0F [Chat] Account auth expired/retryable | ${currentAccountEmail} | rotating without cooldown`
|
|
17237
|
+
);
|
|
17238
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
17239
|
+
Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
|
|
17240
|
+
});
|
|
17241
|
+
}
|
|
17242
|
+
}
|
|
17034
17243
|
return { success: false, error: err };
|
|
17035
17244
|
}
|
|
17036
17245
|
if (policy.retryable && shouldSwitchAccount && !isSingleAccount && accountSwitches < maxAccountSwitches) {
|
|
@@ -17039,7 +17248,7 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17039
17248
|
console.warn(
|
|
17040
17249
|
`\u{1F504} [Chat] Switching account after ${policy.reason} | ${currentAccountEmail} -> ${maskEmail(nextAccount.email)}`
|
|
17041
17250
|
);
|
|
17042
|
-
if (policy.accountCooldownMs
|
|
17251
|
+
if ((policy.accountCooldownMs ?? 0) > 0) {
|
|
17043
17252
|
markAccountRateLimited(
|
|
17044
17253
|
currentAccountId,
|
|
17045
17254
|
policy.accountCooldownMs,
|
|
@@ -19922,7 +20131,7 @@ async function processNonStreamingResponse(params) {
|
|
|
19922
20131
|
foundStr = true;
|
|
19923
20132
|
}
|
|
19924
20133
|
}
|
|
19925
|
-
} else if (delta.phase
|
|
20134
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
19926
20135
|
isThinkingChunk = false;
|
|
19927
20136
|
if (delta.content !== void 0) {
|
|
19928
20137
|
const newContent = delta.content || "";
|
|
@@ -20661,13 +20870,17 @@ async function processStreamingResponse(params) {
|
|
|
20661
20870
|
return false;
|
|
20662
20871
|
}
|
|
20663
20872
|
const switchAccount = policy.switchAccount && !retryInvalidInputOnSameAccount;
|
|
20664
|
-
if (switchAccount && (policy.accountCooldownMs
|
|
20873
|
+
if (switchAccount && (policy.accountCooldownMs ?? 0) > 0) {
|
|
20665
20874
|
markAccountRateLimited(
|
|
20666
20875
|
currentAccountId,
|
|
20667
20876
|
policy.accountCooldownMs,
|
|
20668
20877
|
policy.accountCooldownReason || "StreamRetry"
|
|
20669
20878
|
);
|
|
20670
20879
|
}
|
|
20880
|
+
if (policy.accountCooldownReason === "AuthExpired") {
|
|
20881
|
+
Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
|
|
20882
|
+
});
|
|
20883
|
+
}
|
|
20671
20884
|
retryContext.releaseAccountLease?.();
|
|
20672
20885
|
retryContext.releaseAccountLease = null;
|
|
20673
20886
|
await reader.cancel().catch(() => void 0);
|
|
@@ -20911,7 +21124,7 @@ async function processStreamingResponse(params) {
|
|
|
20911
21124
|
}
|
|
20912
21125
|
);
|
|
20913
21126
|
}
|
|
20914
|
-
if (delta.phase
|
|
21127
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
20915
21128
|
upstreamDone = true;
|
|
20916
21129
|
if (!clientDisconnected) flushWrites();
|
|
20917
21130
|
break;
|
|
@@ -20934,7 +21147,7 @@ async function processStreamingResponse(params) {
|
|
|
20934
21147
|
foundStr = true;
|
|
20935
21148
|
}
|
|
20936
21149
|
}
|
|
20937
|
-
} else if (delta.phase
|
|
21150
|
+
} else if (isAnswerPhase(delta.phase)) {
|
|
20938
21151
|
isThinkingChunk = false;
|
|
20939
21152
|
if (delta.content !== void 0) {
|
|
20940
21153
|
const newContent = delta.content || "";
|
|
@@ -21312,7 +21525,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21312
21525
|
if (!delta) {
|
|
21313
21526
|
continue;
|
|
21314
21527
|
}
|
|
21315
|
-
if (delta.phase
|
|
21528
|
+
if (isAnswerPhase(delta.phase) && delta.status === "finished") {
|
|
21316
21529
|
upstreamDone = true;
|
|
21317
21530
|
break retryReadLoop;
|
|
21318
21531
|
}
|
|
@@ -25506,11 +25719,14 @@ function recordServerLog(level, text) {
|
|
|
25506
25719
|
}
|
|
25507
25720
|
line = line.replace(/^(?:\[?(?:INFO|WARN|WARNING|ERROR|ERR|DEBUG)\]?\s+)+/i, "").replace(/([\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}]\uFE0F?)\s{2,}/gu, "$1 ");
|
|
25508
25721
|
if (!line) continue;
|
|
25509
|
-
const
|
|
25722
|
+
const now = Date.now();
|
|
25723
|
+
const isDuplicate = logHistory.slice(-10).some(
|
|
25724
|
+
(entry2) => (entry2.time === time || entry2._ts && now - entry2._ts < 1500) && entry2.level === level && entry2.message === line
|
|
25725
|
+
);
|
|
25510
25726
|
if (isDuplicate) {
|
|
25511
25727
|
continue;
|
|
25512
25728
|
}
|
|
25513
|
-
const entry = { time, level, message: line };
|
|
25729
|
+
const entry = { time, level, message: line, _ts: now };
|
|
25514
25730
|
logHistory.push(entry);
|
|
25515
25731
|
if (logHistory.length > MAX_LOG_HISTORY) {
|
|
25516
25732
|
logHistory.shift();
|
|
@@ -25673,7 +25889,7 @@ function getAppVersion() {
|
|
|
25673
25889
|
}
|
|
25674
25890
|
} catch {
|
|
25675
25891
|
}
|
|
25676
|
-
cachedVersion = "v1.
|
|
25892
|
+
cachedVersion = "v1.5.0";
|
|
25677
25893
|
return cachedVersion;
|
|
25678
25894
|
}
|
|
25679
25895
|
var cachedVersion;
|