qwenproxy-cli 1.6.4 → 1.6.5
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 +73 -10
- package/dist/delete-chats.js +172 -44
- package/dist/index.js +234 -58
- package/dist/login.js +81 -15
- package/dist/reset-cooldowns.js +76 -11
- package/package.json +1 -1
- package/src/core/mutex.ts +4 -0
- package/src/core/version.ts +1 -1
- package/src/routes/anthropic/translate.ts +15 -7
- package/src/routes/chat/helpers.ts +4 -2
- package/src/routes/chat/streaming.ts +32 -10
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +41 -15
- package/src/services/qwen-headers.ts +3 -2
- package/src/services/qwen.ts +87 -30
- package/src/tools/tool-call-heuristics.ts +88 -0
- package/src/utils/timezone.ts +56 -0
package/dist/delete-chats.js
CHANGED
|
@@ -1915,6 +1915,7 @@ var init_mutex = __esm({
|
|
|
1915
1915
|
locked = false;
|
|
1916
1916
|
lockedAt = 0;
|
|
1917
1917
|
lockedByKey = "";
|
|
1918
|
+
epoch = 0;
|
|
1918
1919
|
async acquire(timeoutMs = 3e5, key = "") {
|
|
1919
1920
|
const holdLimitMs = this.maxHoldMs;
|
|
1920
1921
|
if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
|
|
@@ -1925,6 +1926,7 @@ var init_mutex = __esm({
|
|
|
1925
1926
|
this.locked = false;
|
|
1926
1927
|
this.lockedAt = 0;
|
|
1927
1928
|
this.lockedByKey = "";
|
|
1929
|
+
this.epoch++;
|
|
1928
1930
|
}
|
|
1929
1931
|
if (!this.locked) {
|
|
1930
1932
|
this.locked = true;
|
|
@@ -1963,10 +1965,12 @@ var init_mutex = __esm({
|
|
|
1963
1965
|
}
|
|
1964
1966
|
}
|
|
1965
1967
|
createRelease() {
|
|
1968
|
+
const epoch = this.epoch;
|
|
1966
1969
|
let released = false;
|
|
1967
1970
|
return () => {
|
|
1968
1971
|
if (released) return;
|
|
1969
1972
|
released = true;
|
|
1973
|
+
if (epoch !== this.epoch) return;
|
|
1970
1974
|
this.release();
|
|
1971
1975
|
};
|
|
1972
1976
|
}
|
|
@@ -2906,6 +2910,49 @@ var init_waf_isolation = __esm({
|
|
|
2906
2910
|
}
|
|
2907
2911
|
});
|
|
2908
2912
|
|
|
2913
|
+
// src/utils/timezone.ts
|
|
2914
|
+
function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
|
|
2915
|
+
const dayName = DAYS[date.getDay()];
|
|
2916
|
+
const monthName = MONTHS[date.getMonth()];
|
|
2917
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
2918
|
+
const year = date.getFullYear();
|
|
2919
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
2920
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
2921
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
2922
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
2923
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
2924
|
+
const absOffset = Math.abs(offsetMinutes);
|
|
2925
|
+
const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
|
|
2926
|
+
const offsetMins = String(absOffset % 60).padStart(2, "0");
|
|
2927
|
+
const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
|
|
2928
|
+
return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
|
|
2929
|
+
}
|
|
2930
|
+
function sanitizeHeaderForFetch(val) {
|
|
2931
|
+
if (typeof val !== "string") return "";
|
|
2932
|
+
return val.replace(/[^\x00-\xFF]/g, "");
|
|
2933
|
+
}
|
|
2934
|
+
var DAYS, MONTHS;
|
|
2935
|
+
var init_timezone = __esm({
|
|
2936
|
+
"src/utils/timezone.ts"() {
|
|
2937
|
+
"use strict";
|
|
2938
|
+
DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
2939
|
+
MONTHS = [
|
|
2940
|
+
"Jan",
|
|
2941
|
+
"Feb",
|
|
2942
|
+
"Mar",
|
|
2943
|
+
"Apr",
|
|
2944
|
+
"May",
|
|
2945
|
+
"Jun",
|
|
2946
|
+
"Jul",
|
|
2947
|
+
"Aug",
|
|
2948
|
+
"Sep",
|
|
2949
|
+
"Oct",
|
|
2950
|
+
"Nov",
|
|
2951
|
+
"Dec"
|
|
2952
|
+
];
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
2955
|
+
|
|
2909
2956
|
// src/services/qwen-headers.ts
|
|
2910
2957
|
import { v4 as uuidv4 } from "uuid";
|
|
2911
2958
|
function getSystemAcceptLanguage(customLocale) {
|
|
@@ -2949,8 +2996,8 @@ function buildQwenRequestHeaders(opts) {
|
|
|
2949
2996
|
"bx-v": opts.bxV || "2.5.37",
|
|
2950
2997
|
source: "web",
|
|
2951
2998
|
version: opts.version || getQwenWebVersion(),
|
|
2952
|
-
timezone: opts.extra?.timezone || opts.extra?.Timezone || (
|
|
2953
|
-
Timezone: opts.extra?.Timezone || opts.extra?.timezone || (
|
|
2999
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
|
|
3000
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
|
|
2954
3001
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
2955
3002
|
// back to the static fingerprint otherwise.
|
|
2956
3003
|
"sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
|
|
@@ -2977,6 +3024,7 @@ var init_qwen_headers = __esm({
|
|
|
2977
3024
|
init_qwen_url();
|
|
2978
3025
|
init_config();
|
|
2979
3026
|
init_fingerprint();
|
|
3027
|
+
init_timezone();
|
|
2980
3028
|
dynamicWebVersion = null;
|
|
2981
3029
|
QWEN_WEB_VERSION = config.qwen.webVersion;
|
|
2982
3030
|
DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
|
|
@@ -4613,6 +4661,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4613
4661
|
}
|
|
4614
4662
|
}
|
|
4615
4663
|
}
|
|
4664
|
+
const currentCache = getHeaderCache(account.id);
|
|
4665
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
4666
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
4667
|
+
try {
|
|
4668
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
4669
|
+
if (refreshRes.success) {
|
|
4670
|
+
console.log(
|
|
4671
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
} catch (err) {
|
|
4675
|
+
console.warn(
|
|
4676
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
4677
|
+
);
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4616
4680
|
return;
|
|
4617
4681
|
}
|
|
4618
4682
|
const cookies = await acctContext.cookies();
|
|
@@ -4900,7 +4964,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
4900
4964
|
email: normalizedEmail,
|
|
4901
4965
|
requestId,
|
|
4902
4966
|
version: getQwenWebVersion() || "0.3.12",
|
|
4903
|
-
timezone: (
|
|
4967
|
+
timezone: formatAsciiTimezone()
|
|
4904
4968
|
}
|
|
4905
4969
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4906
4970
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -4995,7 +5059,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
4995
5059
|
code: cleanCode,
|
|
4996
5060
|
requestId,
|
|
4997
5061
|
version: getQwenWebVersion() || "0.3.12",
|
|
4998
|
-
timezone: (
|
|
5062
|
+
timezone: formatAsciiTimezone()
|
|
4999
5063
|
}
|
|
5000
5064
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
5001
5065
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -5276,7 +5340,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5276
5340
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
5277
5341
|
const requestId = crypto3.randomUUID();
|
|
5278
5342
|
const evalRes = await page.evaluate(
|
|
5279
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
5343
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
5280
5344
|
try {
|
|
5281
5345
|
const response = await fetch(signinUrl2, {
|
|
5282
5346
|
method: "POST",
|
|
@@ -5288,7 +5352,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5288
5352
|
source: "web",
|
|
5289
5353
|
version: "0.3.11",
|
|
5290
5354
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5291
|
-
timezone:
|
|
5355
|
+
timezone: tz,
|
|
5292
5356
|
"x-request-id": requestId2
|
|
5293
5357
|
},
|
|
5294
5358
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5299,7 +5363,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5299
5363
|
return { ok: false, error: e.message };
|
|
5300
5364
|
}
|
|
5301
5365
|
},
|
|
5302
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
5366
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
5303
5367
|
).catch(() => null);
|
|
5304
5368
|
let data = evalRes?.data;
|
|
5305
5369
|
let signinSuccess = Boolean(
|
|
@@ -5308,7 +5372,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5308
5372
|
if (!signinSuccess && !data?.data?.code) {
|
|
5309
5373
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
5310
5374
|
const fallbackRes = await page.evaluate(
|
|
5311
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
5375
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
5312
5376
|
try {
|
|
5313
5377
|
const response = await fetch(fallbackUrl2, {
|
|
5314
5378
|
method: "POST",
|
|
@@ -5319,7 +5383,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5319
5383
|
"content-type": "application/json",
|
|
5320
5384
|
source: "web",
|
|
5321
5385
|
version: "0.3.11",
|
|
5322
|
-
timezone:
|
|
5386
|
+
timezone: tz,
|
|
5323
5387
|
"x-request-id": requestId2
|
|
5324
5388
|
},
|
|
5325
5389
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5330,7 +5394,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5330
5394
|
return { ok: false, error: e.message };
|
|
5331
5395
|
}
|
|
5332
5396
|
},
|
|
5333
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
5397
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
5334
5398
|
).catch(() => null);
|
|
5335
5399
|
if (fallbackRes?.data) {
|
|
5336
5400
|
data = fallbackRes.data;
|
|
@@ -5662,7 +5726,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5662
5726
|
if (refreshToken) {
|
|
5663
5727
|
try {
|
|
5664
5728
|
const refreshResult = await page.evaluate(
|
|
5665
|
-
async ({ rTok }) => {
|
|
5729
|
+
async ({ rTok, tz }) => {
|
|
5666
5730
|
try {
|
|
5667
5731
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5668
5732
|
method: "GET",
|
|
@@ -5672,7 +5736,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5672
5736
|
source: "web",
|
|
5673
5737
|
version: "0.3.11",
|
|
5674
5738
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5675
|
-
timezone:
|
|
5739
|
+
timezone: tz,
|
|
5676
5740
|
authorization: `Bearer ${rTok}`
|
|
5677
5741
|
}
|
|
5678
5742
|
});
|
|
@@ -5682,7 +5746,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5682
5746
|
return { ok: false, error: e.message };
|
|
5683
5747
|
}
|
|
5684
5748
|
},
|
|
5685
|
-
{ rTok: refreshToken }
|
|
5749
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
5686
5750
|
);
|
|
5687
5751
|
const json = refreshResult?.data;
|
|
5688
5752
|
if (json && json.success === true && json.data) {
|
|
@@ -5720,6 +5784,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5720
5784
|
const cache = getHeaderCache(accountId);
|
|
5721
5785
|
cache.headers.cookie = cookieStr;
|
|
5722
5786
|
cache.lastRefresh = Date.now();
|
|
5787
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5723
5788
|
markAccountHeadersReady(accountId);
|
|
5724
5789
|
saveAuthSession2(accountId, {
|
|
5725
5790
|
cookie: cookieStr,
|
|
@@ -5754,6 +5819,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5754
5819
|
const cache = getHeaderCache(accountId);
|
|
5755
5820
|
cache.headers.cookie = cookieStr;
|
|
5756
5821
|
cache.lastRefresh = Date.now();
|
|
5822
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5757
5823
|
markAccountHeadersReady(accountId);
|
|
5758
5824
|
saveAuthSession2(accountId, {
|
|
5759
5825
|
cookie: cookieStr,
|
|
@@ -6808,11 +6874,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6808
6874
|
const now = Date.now();
|
|
6809
6875
|
const currentUrl = page.url();
|
|
6810
6876
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
6811
|
-
const cookie = await getCookies(accountId);
|
|
6877
|
+
const cookie = await getCookies(accountId, true);
|
|
6812
6878
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
6813
6879
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
6814
|
-
|
|
6815
|
-
|
|
6880
|
+
logger.debug(
|
|
6881
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
6816
6882
|
);
|
|
6817
6883
|
const res = await refreshAccountToken(accountId);
|
|
6818
6884
|
if (res.success) {
|
|
@@ -6820,6 +6886,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6820
6886
|
touchAccountActivity(accountId);
|
|
6821
6887
|
return true;
|
|
6822
6888
|
}
|
|
6889
|
+
logger.warn(
|
|
6890
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
6891
|
+
);
|
|
6823
6892
|
}
|
|
6824
6893
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
6825
6894
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -7057,6 +7126,7 @@ var init_playwright = __esm({
|
|
|
7057
7126
|
init_qwen_headers();
|
|
7058
7127
|
init_paths();
|
|
7059
7128
|
init_jwt();
|
|
7129
|
+
init_timezone();
|
|
7060
7130
|
requireLocal = createRequire(import.meta.url);
|
|
7061
7131
|
contextInitHooks = [];
|
|
7062
7132
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -7070,7 +7140,7 @@ var init_playwright = __esm({
|
|
|
7070
7140
|
headerCaches = /* @__PURE__ */ new Map();
|
|
7071
7141
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
7072
7142
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
7073
|
-
COOKIE_CACHE_TTL =
|
|
7143
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
7074
7144
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
7075
7145
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
7076
7146
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -8442,6 +8512,8 @@ __export(qwen_exports, {
|
|
|
8442
8512
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
8443
8513
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
8444
8514
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
8515
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
8516
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
8445
8517
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
8446
8518
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
8447
8519
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -8455,10 +8527,13 @@ __export(qwen_exports, {
|
|
|
8455
8527
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
8456
8528
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
8457
8529
|
disableNativeTools: () => disableNativeTools,
|
|
8530
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
8458
8531
|
fetchQwenModels: () => fetchQwenModels,
|
|
8459
8532
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
8460
8533
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
8534
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
8461
8535
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
8536
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
8462
8537
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
8463
8538
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
8464
8539
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -8474,6 +8549,8 @@ __export(qwen_exports, {
|
|
|
8474
8549
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
8475
8550
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
8476
8551
|
setToolCapNotice: () => setToolCapNotice,
|
|
8552
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
8553
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
8477
8554
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
8478
8555
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
8479
8556
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -8760,7 +8837,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
8760
8837
|
function getAccountStreamMutex(accountId) {
|
|
8761
8838
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
8762
8839
|
if (!slots2) {
|
|
8763
|
-
slots2 = {
|
|
8840
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
8764
8841
|
accountStreamMutexes.set(accountId, slots2);
|
|
8765
8842
|
}
|
|
8766
8843
|
return slots2;
|
|
@@ -8768,27 +8845,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
8768
8845
|
function streamSlotCapacity() {
|
|
8769
8846
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
8770
8847
|
}
|
|
8771
|
-
function createStreamSlotRelease(slots2) {
|
|
8848
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
8772
8849
|
let released = false;
|
|
8773
8850
|
return () => {
|
|
8774
8851
|
if (released) return;
|
|
8775
8852
|
released = true;
|
|
8776
|
-
slots2.
|
|
8853
|
+
slots2.activeSlots.delete(slotId);
|
|
8777
8854
|
const next = slots2.queue.shift();
|
|
8778
8855
|
if (next) {
|
|
8779
|
-
|
|
8780
|
-
|
|
8856
|
+
const nextId = nextStreamSlotId++;
|
|
8857
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
8858
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
8781
8859
|
}
|
|
8782
8860
|
};
|
|
8783
8861
|
}
|
|
8862
|
+
function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
|
|
8863
|
+
const now = Date.now();
|
|
8864
|
+
let sweptCount = 0;
|
|
8865
|
+
for (const [accountId, slots2] of accountStreamMutexes.entries()) {
|
|
8866
|
+
for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
|
|
8867
|
+
if (now - startedAt >= maxHoldMs) {
|
|
8868
|
+
logger.warn(
|
|
8869
|
+
`[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
|
|
8870
|
+
);
|
|
8871
|
+
slots2.activeSlots.delete(slotId);
|
|
8872
|
+
sweptCount++;
|
|
8873
|
+
const next = slots2.queue.shift();
|
|
8874
|
+
if (next) {
|
|
8875
|
+
const nextId = nextStreamSlotId++;
|
|
8876
|
+
slots2.activeSlots.set(nextId, now);
|
|
8877
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
8878
|
+
}
|
|
8879
|
+
}
|
|
8880
|
+
}
|
|
8881
|
+
}
|
|
8882
|
+
return sweptCount;
|
|
8883
|
+
}
|
|
8884
|
+
function ensureStaleSlotSweeperStarted() {
|
|
8885
|
+
if (staleSlotSweeperTimer) return;
|
|
8886
|
+
staleSlotSweeperTimer = setInterval(() => {
|
|
8887
|
+
try {
|
|
8888
|
+
sweepStaleAccountStreamSlots();
|
|
8889
|
+
} catch (err) {
|
|
8890
|
+
logger.error("[Qwen] Error in stale stream slot sweeper", {
|
|
8891
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8892
|
+
});
|
|
8893
|
+
}
|
|
8894
|
+
}, 6e4);
|
|
8895
|
+
staleSlotSweeperTimer.unref();
|
|
8896
|
+
}
|
|
8897
|
+
function stopStaleSlotSweeperForTests() {
|
|
8898
|
+
if (staleSlotSweeperTimer) {
|
|
8899
|
+
clearInterval(staleSlotSweeperTimer);
|
|
8900
|
+
staleSlotSweeperTimer = null;
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8784
8903
|
async function acquireAccountStreamLock(accountId) {
|
|
8904
|
+
ensureStaleSlotSweeperStarted();
|
|
8785
8905
|
const slots2 = getAccountStreamMutex(accountId);
|
|
8786
|
-
if (slots2.
|
|
8787
|
-
|
|
8788
|
-
|
|
8906
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
8907
|
+
const slotId = nextStreamSlotId++;
|
|
8908
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
8909
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
8789
8910
|
}
|
|
8790
8911
|
return new Promise((resolve) => {
|
|
8791
|
-
slots2.queue.push(() => resolve(
|
|
8912
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
8792
8913
|
});
|
|
8793
8914
|
}
|
|
8794
8915
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -8880,11 +9001,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
8880
9001
|
"x-request-id",
|
|
8881
9002
|
"x-accel-buffering"
|
|
8882
9003
|
]);
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
9004
|
+
const result = {};
|
|
9005
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
9006
|
+
if (browserAllowedHeaders.has(name.toLowerCase())) {
|
|
9007
|
+
result[name] = sanitizeHeaderForFetch(String(value ?? ""));
|
|
9008
|
+
}
|
|
9009
|
+
}
|
|
9010
|
+
return result;
|
|
8888
9011
|
}
|
|
8889
9012
|
async function requestQwenTextInBrowser(accountId, method, path6, headers, body, options = {}) {
|
|
8890
9013
|
const url = qwenUrl(path6);
|
|
@@ -8905,7 +9028,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8905
9028
|
browserHeaders["Content-Type"] = "application/json";
|
|
8906
9029
|
}
|
|
8907
9030
|
const evaluateRequest = (page) => page.evaluate(
|
|
8908
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
9031
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
8909
9032
|
const controller = new AbortController();
|
|
8910
9033
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
8911
9034
|
try {
|
|
@@ -8929,7 +9052,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8929
9052
|
source: "web",
|
|
8930
9053
|
version: "0.3.11",
|
|
8931
9054
|
"x-request-origin": "https://chat.qwen.ai",
|
|
8932
|
-
timezone:
|
|
9055
|
+
timezone: tz,
|
|
8933
9056
|
authorization: `Bearer ${rTok}`
|
|
8934
9057
|
},
|
|
8935
9058
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -8974,7 +9097,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8974
9097
|
headers: browserHeaders,
|
|
8975
9098
|
body,
|
|
8976
9099
|
referrer: options.referrer,
|
|
8977
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
9100
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
9101
|
+
tz: formatAsciiTimezone()
|
|
8978
9102
|
}
|
|
8979
9103
|
);
|
|
8980
9104
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -9652,7 +9776,7 @@ async function disableNativeTools(accountId) {
|
|
|
9652
9776
|
async (page) => {
|
|
9653
9777
|
const requestId = crypto5.randomUUID();
|
|
9654
9778
|
const response = await page.evaluate(
|
|
9655
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
9779
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
9656
9780
|
const controller = new AbortController();
|
|
9657
9781
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9658
9782
|
try {
|
|
@@ -9664,7 +9788,7 @@ async function disableNativeTools(accountId) {
|
|
|
9664
9788
|
accept: "application/json, text/plain, */*",
|
|
9665
9789
|
"content-type": "application/json",
|
|
9666
9790
|
"x-request-id": requestId2,
|
|
9667
|
-
timezone:
|
|
9791
|
+
timezone: tz,
|
|
9668
9792
|
source: "web"
|
|
9669
9793
|
},
|
|
9670
9794
|
body: JSON.stringify(payload2),
|
|
@@ -9676,7 +9800,7 @@ async function disableNativeTools(accountId) {
|
|
|
9676
9800
|
clearTimeout(timeoutId);
|
|
9677
9801
|
}
|
|
9678
9802
|
},
|
|
9679
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
9803
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
9680
9804
|
);
|
|
9681
9805
|
return response;
|
|
9682
9806
|
}
|
|
@@ -9893,7 +10017,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
9893
10017
|
const requestHeaders = {
|
|
9894
10018
|
source: "web",
|
|
9895
10019
|
version: "0.2.89",
|
|
9896
|
-
timezone: (
|
|
10020
|
+
timezone: formatAsciiTimezone(),
|
|
9897
10021
|
"x-request-id": crypto5.randomUUID(),
|
|
9898
10022
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
9899
10023
|
};
|
|
@@ -9937,7 +10061,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
9937
10061
|
}
|
|
9938
10062
|
const requestHeaders = {
|
|
9939
10063
|
version: "0.2.89",
|
|
9940
|
-
timezone: (
|
|
10064
|
+
timezone: formatAsciiTimezone(),
|
|
9941
10065
|
"x-request-id": crypto5.randomUUID(),
|
|
9942
10066
|
Referer: qwenUrl("/settings/chats")
|
|
9943
10067
|
};
|
|
@@ -9989,7 +10113,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9989
10113
|
async (page) => {
|
|
9990
10114
|
const requestId = crypto5.randomUUID();
|
|
9991
10115
|
const response2 = await page.evaluate(
|
|
9992
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
10116
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
9993
10117
|
const controller = new AbortController();
|
|
9994
10118
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9995
10119
|
try {
|
|
@@ -9998,7 +10122,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9998
10122
|
headers: {
|
|
9999
10123
|
accept: "application/json, text/plain, */*",
|
|
10000
10124
|
"x-request-id": requestId2,
|
|
10001
|
-
timezone:
|
|
10125
|
+
timezone: tz,
|
|
10002
10126
|
source: "web"
|
|
10003
10127
|
},
|
|
10004
10128
|
signal: controller.signal
|
|
@@ -10008,7 +10132,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10008
10132
|
clearTimeout(timeoutId);
|
|
10009
10133
|
}
|
|
10010
10134
|
},
|
|
10011
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
10135
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
10012
10136
|
);
|
|
10013
10137
|
return response2;
|
|
10014
10138
|
}
|
|
@@ -10047,7 +10171,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10047
10171
|
bxUa,
|
|
10048
10172
|
bxUmidtoken,
|
|
10049
10173
|
extra: {
|
|
10050
|
-
timezone: (
|
|
10174
|
+
timezone: formatAsciiTimezone()
|
|
10051
10175
|
}
|
|
10052
10176
|
}),
|
|
10053
10177
|
void 0,
|
|
@@ -10810,7 +10934,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
10810
10934
|
clearTimeout(timeoutId);
|
|
10811
10935
|
}
|
|
10812
10936
|
}
|
|
10813
|
-
var sleep4, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, accountStreamMutexes, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
10937
|
+
var sleep4, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, accountStreamMutexes, nextStreamSlotId, staleSlotSweeperTimer, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
10814
10938
|
var init_qwen = __esm({
|
|
10815
10939
|
"src/services/qwen.ts"() {
|
|
10816
10940
|
"use strict";
|
|
@@ -10828,6 +10952,7 @@ var init_qwen = __esm({
|
|
|
10828
10952
|
init_captcha_coordinator();
|
|
10829
10953
|
init_captcha_solver();
|
|
10830
10954
|
init_account_concurrency();
|
|
10955
|
+
init_timezone();
|
|
10831
10956
|
init_qwen_errors();
|
|
10832
10957
|
init_qwen_thread_state();
|
|
10833
10958
|
init_qwen_chat_pool();
|
|
@@ -10857,7 +10982,10 @@ var init_qwen = __esm({
|
|
|
10857
10982
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
10858
10983
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
10859
10984
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
10985
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
10860
10986
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
10987
|
+
nextStreamSlotId = 1;
|
|
10988
|
+
staleSlotSweeperTimer = null;
|
|
10861
10989
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
10862
10990
|
ui: {
|
|
10863
10991
|
autoTags: false,
|