qwenproxy-cli 1.6.3 → 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 +76 -12
- package/dist/delete-chats.js +182 -49
- package/dist/index.js +245 -64
- package/dist/login.js +90 -19
- package/dist/reset-cooldowns.js +79 -13
- 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/retry-policy.ts +3 -1
- 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 +55 -21
- package/src/services/qwen-headers.ts +3 -2
- package/src/services/qwen.ts +88 -31
- 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);
|
|
@@ -3213,6 +3261,7 @@ __export(playwright_exports, {
|
|
|
3213
3261
|
closeIdlePlaywrightAccounts: () => closeIdlePlaywrightAccounts,
|
|
3214
3262
|
closePlaywrightForAccount: () => closePlaywrightForAccount,
|
|
3215
3263
|
evictIdlePlaywrightContextsToLimit: () => evictIdlePlaywrightContextsToLimit,
|
|
3264
|
+
getAccountMutex: () => getAccountMutex,
|
|
3216
3265
|
getActivePlaywrightAccountIds: () => getActivePlaywrightAccountIds,
|
|
3217
3266
|
getBasicHeaders: () => getBasicHeaders,
|
|
3218
3267
|
getCookies: () => getCookies,
|
|
@@ -3362,9 +3411,10 @@ function getAccountMutex(accountId) {
|
|
|
3362
3411
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
3363
3412
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
3364
3413
|
const lockState = mutex.state();
|
|
3365
|
-
|
|
3414
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
3415
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
3366
3416
|
logger.warn(
|
|
3367
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
3417
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
3368
3418
|
);
|
|
3369
3419
|
return;
|
|
3370
3420
|
}
|
|
@@ -4166,7 +4216,8 @@ async function getBasicHeaders(accountId) {
|
|
|
4166
4216
|
}
|
|
4167
4217
|
const release = await acquireAccountMutex(
|
|
4168
4218
|
accountId,
|
|
4169
|
-
`headers:${accountId.substring(0, 12)}
|
|
4219
|
+
`headers:${accountId.substring(0, 12)}`,
|
|
4220
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 3e4)
|
|
4170
4221
|
);
|
|
4171
4222
|
try {
|
|
4172
4223
|
touchAccountActivity(accountId);
|
|
@@ -4259,8 +4310,10 @@ async function getBasicHeaders(accountId) {
|
|
|
4259
4310
|
return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
|
|
4260
4311
|
}
|
|
4261
4312
|
}
|
|
4313
|
+
let refreshedInThisCall = false;
|
|
4262
4314
|
if (headersAge > HEADER_CACHE_TTL && !cache.refreshInProgress) {
|
|
4263
4315
|
try {
|
|
4316
|
+
refreshedInThisCall = true;
|
|
4264
4317
|
await refreshHeadersInternal(accountId);
|
|
4265
4318
|
} catch (error) {
|
|
4266
4319
|
if (!hadUsableHeaders) throw error;
|
|
@@ -4329,7 +4382,7 @@ async function getBasicHeaders(accountId) {
|
|
|
4329
4382
|
} catch {
|
|
4330
4383
|
}
|
|
4331
4384
|
}
|
|
4332
|
-
if (!hasRequiredQwenHeaders(cache.headers)) {
|
|
4385
|
+
if (!hasRequiredQwenHeaders(cache.headers) && !refreshedInThisCall) {
|
|
4333
4386
|
console.log(
|
|
4334
4387
|
`\u{1F504} [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`
|
|
4335
4388
|
);
|
|
@@ -4608,6 +4661,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4608
4661
|
}
|
|
4609
4662
|
}
|
|
4610
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
|
+
}
|
|
4611
4680
|
return;
|
|
4612
4681
|
}
|
|
4613
4682
|
const cookies = await acctContext.cookies();
|
|
@@ -4895,7 +4964,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
4895
4964
|
email: normalizedEmail,
|
|
4896
4965
|
requestId,
|
|
4897
4966
|
version: getQwenWebVersion() || "0.3.12",
|
|
4898
|
-
timezone: (
|
|
4967
|
+
timezone: formatAsciiTimezone()
|
|
4899
4968
|
}
|
|
4900
4969
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4901
4970
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -4990,7 +5059,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
4990
5059
|
code: cleanCode,
|
|
4991
5060
|
requestId,
|
|
4992
5061
|
version: getQwenWebVersion() || "0.3.12",
|
|
4993
|
-
timezone: (
|
|
5062
|
+
timezone: formatAsciiTimezone()
|
|
4994
5063
|
}
|
|
4995
5064
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4996
5065
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -5271,7 +5340,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5271
5340
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
5272
5341
|
const requestId = crypto3.randomUUID();
|
|
5273
5342
|
const evalRes = await page.evaluate(
|
|
5274
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
5343
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
5275
5344
|
try {
|
|
5276
5345
|
const response = await fetch(signinUrl2, {
|
|
5277
5346
|
method: "POST",
|
|
@@ -5283,7 +5352,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5283
5352
|
source: "web",
|
|
5284
5353
|
version: "0.3.11",
|
|
5285
5354
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5286
|
-
timezone:
|
|
5355
|
+
timezone: tz,
|
|
5287
5356
|
"x-request-id": requestId2
|
|
5288
5357
|
},
|
|
5289
5358
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5294,7 +5363,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5294
5363
|
return { ok: false, error: e.message };
|
|
5295
5364
|
}
|
|
5296
5365
|
},
|
|
5297
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
5366
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
5298
5367
|
).catch(() => null);
|
|
5299
5368
|
let data = evalRes?.data;
|
|
5300
5369
|
let signinSuccess = Boolean(
|
|
@@ -5303,7 +5372,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5303
5372
|
if (!signinSuccess && !data?.data?.code) {
|
|
5304
5373
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
5305
5374
|
const fallbackRes = await page.evaluate(
|
|
5306
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
5375
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
5307
5376
|
try {
|
|
5308
5377
|
const response = await fetch(fallbackUrl2, {
|
|
5309
5378
|
method: "POST",
|
|
@@ -5314,7 +5383,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5314
5383
|
"content-type": "application/json",
|
|
5315
5384
|
source: "web",
|
|
5316
5385
|
version: "0.3.11",
|
|
5317
|
-
timezone:
|
|
5386
|
+
timezone: tz,
|
|
5318
5387
|
"x-request-id": requestId2
|
|
5319
5388
|
},
|
|
5320
5389
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5325,7 +5394,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5325
5394
|
return { ok: false, error: e.message };
|
|
5326
5395
|
}
|
|
5327
5396
|
},
|
|
5328
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
5397
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
5329
5398
|
).catch(() => null);
|
|
5330
5399
|
if (fallbackRes?.data) {
|
|
5331
5400
|
data = fallbackRes.data;
|
|
@@ -5657,7 +5726,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5657
5726
|
if (refreshToken) {
|
|
5658
5727
|
try {
|
|
5659
5728
|
const refreshResult = await page.evaluate(
|
|
5660
|
-
async ({ rTok }) => {
|
|
5729
|
+
async ({ rTok, tz }) => {
|
|
5661
5730
|
try {
|
|
5662
5731
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5663
5732
|
method: "GET",
|
|
@@ -5667,7 +5736,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5667
5736
|
source: "web",
|
|
5668
5737
|
version: "0.3.11",
|
|
5669
5738
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5670
|
-
timezone:
|
|
5739
|
+
timezone: tz,
|
|
5671
5740
|
authorization: `Bearer ${rTok}`
|
|
5672
5741
|
}
|
|
5673
5742
|
});
|
|
@@ -5677,7 +5746,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5677
5746
|
return { ok: false, error: e.message };
|
|
5678
5747
|
}
|
|
5679
5748
|
},
|
|
5680
|
-
{ rTok: refreshToken }
|
|
5749
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
5681
5750
|
);
|
|
5682
5751
|
const json = refreshResult?.data;
|
|
5683
5752
|
if (json && json.success === true && json.data) {
|
|
@@ -5715,6 +5784,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5715
5784
|
const cache = getHeaderCache(accountId);
|
|
5716
5785
|
cache.headers.cookie = cookieStr;
|
|
5717
5786
|
cache.lastRefresh = Date.now();
|
|
5787
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5718
5788
|
markAccountHeadersReady(accountId);
|
|
5719
5789
|
saveAuthSession2(accountId, {
|
|
5720
5790
|
cookie: cookieStr,
|
|
@@ -5749,6 +5819,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5749
5819
|
const cache = getHeaderCache(accountId);
|
|
5750
5820
|
cache.headers.cookie = cookieStr;
|
|
5751
5821
|
cache.lastRefresh = Date.now();
|
|
5822
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5752
5823
|
markAccountHeadersReady(accountId);
|
|
5753
5824
|
saveAuthSession2(accountId, {
|
|
5754
5825
|
cookie: cookieStr,
|
|
@@ -6803,11 +6874,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6803
6874
|
const now = Date.now();
|
|
6804
6875
|
const currentUrl = page.url();
|
|
6805
6876
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
6806
|
-
const cookie = await getCookies(accountId);
|
|
6877
|
+
const cookie = await getCookies(accountId, true);
|
|
6807
6878
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
6808
6879
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
6809
|
-
|
|
6810
|
-
|
|
6880
|
+
logger.debug(
|
|
6881
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
6811
6882
|
);
|
|
6812
6883
|
const res = await refreshAccountToken(accountId);
|
|
6813
6884
|
if (res.success) {
|
|
@@ -6815,6 +6886,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6815
6886
|
touchAccountActivity(accountId);
|
|
6816
6887
|
return true;
|
|
6817
6888
|
}
|
|
6889
|
+
logger.warn(
|
|
6890
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
6891
|
+
);
|
|
6818
6892
|
}
|
|
6819
6893
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
6820
6894
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -7052,6 +7126,7 @@ var init_playwright = __esm({
|
|
|
7052
7126
|
init_qwen_headers();
|
|
7053
7127
|
init_paths();
|
|
7054
7128
|
init_jwt();
|
|
7129
|
+
init_timezone();
|
|
7055
7130
|
requireLocal = createRequire(import.meta.url);
|
|
7056
7131
|
contextInitHooks = [];
|
|
7057
7132
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -7065,7 +7140,7 @@ var init_playwright = __esm({
|
|
|
7065
7140
|
headerCaches = /* @__PURE__ */ new Map();
|
|
7066
7141
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
7067
7142
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
7068
|
-
COOKIE_CACHE_TTL =
|
|
7143
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
7069
7144
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
7070
7145
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
7071
7146
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -8437,6 +8512,8 @@ __export(qwen_exports, {
|
|
|
8437
8512
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
8438
8513
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
8439
8514
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
8515
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
8516
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
8440
8517
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
8441
8518
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
8442
8519
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -8450,10 +8527,13 @@ __export(qwen_exports, {
|
|
|
8450
8527
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
8451
8528
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
8452
8529
|
disableNativeTools: () => disableNativeTools,
|
|
8530
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
8453
8531
|
fetchQwenModels: () => fetchQwenModels,
|
|
8454
8532
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
8455
8533
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
8534
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
8456
8535
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
8536
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
8457
8537
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
8458
8538
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
8459
8539
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -8469,6 +8549,8 @@ __export(qwen_exports, {
|
|
|
8469
8549
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
8470
8550
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
8471
8551
|
setToolCapNotice: () => setToolCapNotice,
|
|
8552
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
8553
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
8472
8554
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
8473
8555
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
8474
8556
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -8755,7 +8837,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
8755
8837
|
function getAccountStreamMutex(accountId) {
|
|
8756
8838
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
8757
8839
|
if (!slots2) {
|
|
8758
|
-
slots2 = {
|
|
8840
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
8759
8841
|
accountStreamMutexes.set(accountId, slots2);
|
|
8760
8842
|
}
|
|
8761
8843
|
return slots2;
|
|
@@ -8763,27 +8845,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
8763
8845
|
function streamSlotCapacity() {
|
|
8764
8846
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
8765
8847
|
}
|
|
8766
|
-
function createStreamSlotRelease(slots2) {
|
|
8848
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
8767
8849
|
let released = false;
|
|
8768
8850
|
return () => {
|
|
8769
8851
|
if (released) return;
|
|
8770
8852
|
released = true;
|
|
8771
|
-
slots2.
|
|
8853
|
+
slots2.activeSlots.delete(slotId);
|
|
8772
8854
|
const next = slots2.queue.shift();
|
|
8773
8855
|
if (next) {
|
|
8774
|
-
|
|
8775
|
-
|
|
8856
|
+
const nextId = nextStreamSlotId++;
|
|
8857
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
8858
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
8776
8859
|
}
|
|
8777
8860
|
};
|
|
8778
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
|
+
}
|
|
8779
8903
|
async function acquireAccountStreamLock(accountId) {
|
|
8904
|
+
ensureStaleSlotSweeperStarted();
|
|
8780
8905
|
const slots2 = getAccountStreamMutex(accountId);
|
|
8781
|
-
if (slots2.
|
|
8782
|
-
|
|
8783
|
-
|
|
8906
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
8907
|
+
const slotId = nextStreamSlotId++;
|
|
8908
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
8909
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
8784
8910
|
}
|
|
8785
8911
|
return new Promise((resolve) => {
|
|
8786
|
-
slots2.queue.push(() => resolve(
|
|
8912
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
8787
8913
|
});
|
|
8788
8914
|
}
|
|
8789
8915
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -8857,7 +8983,7 @@ async function withQwenBrowserPage(accountId, fn, targetPath, operationTimeoutMs
|
|
|
8857
8983
|
return fn(page);
|
|
8858
8984
|
},
|
|
8859
8985
|
operationTimeoutMs,
|
|
8860
|
-
Math.
|
|
8986
|
+
Math.max(6e4, operationTimeoutMs),
|
|
8861
8987
|
recoverOnTimeout
|
|
8862
8988
|
);
|
|
8863
8989
|
}
|
|
@@ -8875,11 +9001,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
8875
9001
|
"x-request-id",
|
|
8876
9002
|
"x-accel-buffering"
|
|
8877
9003
|
]);
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
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;
|
|
8883
9011
|
}
|
|
8884
9012
|
async function requestQwenTextInBrowser(accountId, method, path6, headers, body, options = {}) {
|
|
8885
9013
|
const url = qwenUrl(path6);
|
|
@@ -8900,7 +9028,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8900
9028
|
browserHeaders["Content-Type"] = "application/json";
|
|
8901
9029
|
}
|
|
8902
9030
|
const evaluateRequest = (page) => page.evaluate(
|
|
8903
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
9031
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
8904
9032
|
const controller = new AbortController();
|
|
8905
9033
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
8906
9034
|
try {
|
|
@@ -8924,7 +9052,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8924
9052
|
source: "web",
|
|
8925
9053
|
version: "0.3.11",
|
|
8926
9054
|
"x-request-origin": "https://chat.qwen.ai",
|
|
8927
|
-
timezone:
|
|
9055
|
+
timezone: tz,
|
|
8928
9056
|
authorization: `Bearer ${rTok}`
|
|
8929
9057
|
},
|
|
8930
9058
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -8969,7 +9097,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8969
9097
|
headers: browserHeaders,
|
|
8970
9098
|
body,
|
|
8971
9099
|
referrer: options.referrer,
|
|
8972
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
9100
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
9101
|
+
tz: formatAsciiTimezone()
|
|
8973
9102
|
}
|
|
8974
9103
|
);
|
|
8975
9104
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -9647,7 +9776,7 @@ async function disableNativeTools(accountId) {
|
|
|
9647
9776
|
async (page) => {
|
|
9648
9777
|
const requestId = crypto5.randomUUID();
|
|
9649
9778
|
const response = await page.evaluate(
|
|
9650
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
9779
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
9651
9780
|
const controller = new AbortController();
|
|
9652
9781
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9653
9782
|
try {
|
|
@@ -9659,7 +9788,7 @@ async function disableNativeTools(accountId) {
|
|
|
9659
9788
|
accept: "application/json, text/plain, */*",
|
|
9660
9789
|
"content-type": "application/json",
|
|
9661
9790
|
"x-request-id": requestId2,
|
|
9662
|
-
timezone:
|
|
9791
|
+
timezone: tz,
|
|
9663
9792
|
source: "web"
|
|
9664
9793
|
},
|
|
9665
9794
|
body: JSON.stringify(payload2),
|
|
@@ -9671,7 +9800,7 @@ async function disableNativeTools(accountId) {
|
|
|
9671
9800
|
clearTimeout(timeoutId);
|
|
9672
9801
|
}
|
|
9673
9802
|
},
|
|
9674
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
9803
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
9675
9804
|
);
|
|
9676
9805
|
return response;
|
|
9677
9806
|
}
|
|
@@ -9888,7 +10017,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
9888
10017
|
const requestHeaders = {
|
|
9889
10018
|
source: "web",
|
|
9890
10019
|
version: "0.2.89",
|
|
9891
|
-
timezone: (
|
|
10020
|
+
timezone: formatAsciiTimezone(),
|
|
9892
10021
|
"x-request-id": crypto5.randomUUID(),
|
|
9893
10022
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
9894
10023
|
};
|
|
@@ -9932,7 +10061,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
9932
10061
|
}
|
|
9933
10062
|
const requestHeaders = {
|
|
9934
10063
|
version: "0.2.89",
|
|
9935
|
-
timezone: (
|
|
10064
|
+
timezone: formatAsciiTimezone(),
|
|
9936
10065
|
"x-request-id": crypto5.randomUUID(),
|
|
9937
10066
|
Referer: qwenUrl("/settings/chats")
|
|
9938
10067
|
};
|
|
@@ -9984,7 +10113,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9984
10113
|
async (page) => {
|
|
9985
10114
|
const requestId = crypto5.randomUUID();
|
|
9986
10115
|
const response2 = await page.evaluate(
|
|
9987
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
10116
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
9988
10117
|
const controller = new AbortController();
|
|
9989
10118
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9990
10119
|
try {
|
|
@@ -9993,7 +10122,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9993
10122
|
headers: {
|
|
9994
10123
|
accept: "application/json, text/plain, */*",
|
|
9995
10124
|
"x-request-id": requestId2,
|
|
9996
|
-
timezone:
|
|
10125
|
+
timezone: tz,
|
|
9997
10126
|
source: "web"
|
|
9998
10127
|
},
|
|
9999
10128
|
signal: controller.signal
|
|
@@ -10003,7 +10132,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10003
10132
|
clearTimeout(timeoutId);
|
|
10004
10133
|
}
|
|
10005
10134
|
},
|
|
10006
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
10135
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
10007
10136
|
);
|
|
10008
10137
|
return response2;
|
|
10009
10138
|
}
|
|
@@ -10042,7 +10171,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10042
10171
|
bxUa,
|
|
10043
10172
|
bxUmidtoken,
|
|
10044
10173
|
extra: {
|
|
10045
|
-
timezone: (
|
|
10174
|
+
timezone: formatAsciiTimezone()
|
|
10046
10175
|
}
|
|
10047
10176
|
}),
|
|
10048
10177
|
void 0,
|
|
@@ -10805,7 +10934,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
10805
10934
|
clearTimeout(timeoutId);
|
|
10806
10935
|
}
|
|
10807
10936
|
}
|
|
10808
|
-
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;
|
|
10809
10938
|
var init_qwen = __esm({
|
|
10810
10939
|
"src/services/qwen.ts"() {
|
|
10811
10940
|
"use strict";
|
|
@@ -10823,6 +10952,7 @@ var init_qwen = __esm({
|
|
|
10823
10952
|
init_captcha_coordinator();
|
|
10824
10953
|
init_captcha_solver();
|
|
10825
10954
|
init_account_concurrency();
|
|
10955
|
+
init_timezone();
|
|
10826
10956
|
init_qwen_errors();
|
|
10827
10957
|
init_qwen_thread_state();
|
|
10828
10958
|
init_qwen_chat_pool();
|
|
@@ -10852,7 +10982,10 @@ var init_qwen = __esm({
|
|
|
10852
10982
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
10853
10983
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
10854
10984
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
10985
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
10855
10986
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
10987
|
+
nextStreamSlotId = 1;
|
|
10988
|
+
staleSlotSweeperTimer = null;
|
|
10856
10989
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
10857
10990
|
ui: {
|
|
10858
10991
|
autoTags: false,
|