qwenproxy-cli 1.6.4 → 1.6.6
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 +84 -11
- package/dist/delete-chats.js +183 -45
- package/dist/index.js +267 -77
- package/dist/login.js +92 -16
- package/dist/reset-cooldowns.js +87 -12
- 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 +66 -31
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +57 -20
- 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);
|
|
@@ -4399,6 +4447,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4399
4447
|
await existingInit;
|
|
4400
4448
|
return;
|
|
4401
4449
|
}
|
|
4450
|
+
let resolveInit;
|
|
4451
|
+
let rejectInit;
|
|
4452
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
4453
|
+
resolveInit = res;
|
|
4454
|
+
rejectInit = rej;
|
|
4455
|
+
});
|
|
4456
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
4402
4457
|
const initPromise = (async () => {
|
|
4403
4458
|
const release = await acquireAccountMutex(
|
|
4404
4459
|
account.id,
|
|
@@ -4613,6 +4668,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4613
4668
|
}
|
|
4614
4669
|
}
|
|
4615
4670
|
}
|
|
4671
|
+
const currentCache = getHeaderCache(account.id);
|
|
4672
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
4673
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
4674
|
+
try {
|
|
4675
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
4676
|
+
if (refreshRes.success) {
|
|
4677
|
+
console.log(
|
|
4678
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
4679
|
+
);
|
|
4680
|
+
}
|
|
4681
|
+
} catch (err) {
|
|
4682
|
+
console.warn(
|
|
4683
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4616
4687
|
return;
|
|
4617
4688
|
}
|
|
4618
4689
|
const cookies = await acctContext.cookies();
|
|
@@ -4667,9 +4738,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4667
4738
|
release();
|
|
4668
4739
|
}
|
|
4669
4740
|
})();
|
|
4670
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
4671
4741
|
try {
|
|
4672
4742
|
await initPromise;
|
|
4743
|
+
resolveInit();
|
|
4744
|
+
} catch (error) {
|
|
4745
|
+
rejectInit(error);
|
|
4746
|
+
throw error;
|
|
4673
4747
|
} finally {
|
|
4674
4748
|
inFlightAccountInits.delete(account.id);
|
|
4675
4749
|
}
|
|
@@ -4900,7 +4974,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
4900
4974
|
email: normalizedEmail,
|
|
4901
4975
|
requestId,
|
|
4902
4976
|
version: getQwenWebVersion() || "0.3.12",
|
|
4903
|
-
timezone: (
|
|
4977
|
+
timezone: formatAsciiTimezone()
|
|
4904
4978
|
}
|
|
4905
4979
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
4906
4980
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -4995,7 +5069,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
4995
5069
|
code: cleanCode,
|
|
4996
5070
|
requestId,
|
|
4997
5071
|
version: getQwenWebVersion() || "0.3.12",
|
|
4998
|
-
timezone: (
|
|
5072
|
+
timezone: formatAsciiTimezone()
|
|
4999
5073
|
}
|
|
5000
5074
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
5001
5075
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -5276,7 +5350,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5276
5350
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
5277
5351
|
const requestId = crypto3.randomUUID();
|
|
5278
5352
|
const evalRes = await page.evaluate(
|
|
5279
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
5353
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
5280
5354
|
try {
|
|
5281
5355
|
const response = await fetch(signinUrl2, {
|
|
5282
5356
|
method: "POST",
|
|
@@ -5288,7 +5362,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5288
5362
|
source: "web",
|
|
5289
5363
|
version: "0.3.11",
|
|
5290
5364
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5291
|
-
timezone:
|
|
5365
|
+
timezone: tz,
|
|
5292
5366
|
"x-request-id": requestId2
|
|
5293
5367
|
},
|
|
5294
5368
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5299,7 +5373,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5299
5373
|
return { ok: false, error: e.message };
|
|
5300
5374
|
}
|
|
5301
5375
|
},
|
|
5302
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
5376
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
5303
5377
|
).catch(() => null);
|
|
5304
5378
|
let data = evalRes?.data;
|
|
5305
5379
|
let signinSuccess = Boolean(
|
|
@@ -5308,7 +5382,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5308
5382
|
if (!signinSuccess && !data?.data?.code) {
|
|
5309
5383
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
5310
5384
|
const fallbackRes = await page.evaluate(
|
|
5311
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
5385
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
5312
5386
|
try {
|
|
5313
5387
|
const response = await fetch(fallbackUrl2, {
|
|
5314
5388
|
method: "POST",
|
|
@@ -5319,7 +5393,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5319
5393
|
"content-type": "application/json",
|
|
5320
5394
|
source: "web",
|
|
5321
5395
|
version: "0.3.11",
|
|
5322
|
-
timezone:
|
|
5396
|
+
timezone: tz,
|
|
5323
5397
|
"x-request-id": requestId2
|
|
5324
5398
|
},
|
|
5325
5399
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -5330,7 +5404,7 @@ async function loginViaApi(page, email, password) {
|
|
|
5330
5404
|
return { ok: false, error: e.message };
|
|
5331
5405
|
}
|
|
5332
5406
|
},
|
|
5333
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
5407
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
5334
5408
|
).catch(() => null);
|
|
5335
5409
|
if (fallbackRes?.data) {
|
|
5336
5410
|
data = fallbackRes.data;
|
|
@@ -5662,7 +5736,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5662
5736
|
if (refreshToken) {
|
|
5663
5737
|
try {
|
|
5664
5738
|
const refreshResult = await page.evaluate(
|
|
5665
|
-
async ({ rTok }) => {
|
|
5739
|
+
async ({ rTok, tz }) => {
|
|
5666
5740
|
try {
|
|
5667
5741
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5668
5742
|
method: "GET",
|
|
@@ -5672,7 +5746,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5672
5746
|
source: "web",
|
|
5673
5747
|
version: "0.3.11",
|
|
5674
5748
|
"x-request-origin": "https://chat.qwen.ai",
|
|
5675
|
-
timezone:
|
|
5749
|
+
timezone: tz,
|
|
5676
5750
|
authorization: `Bearer ${rTok}`
|
|
5677
5751
|
}
|
|
5678
5752
|
});
|
|
@@ -5682,7 +5756,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5682
5756
|
return { ok: false, error: e.message };
|
|
5683
5757
|
}
|
|
5684
5758
|
},
|
|
5685
|
-
{ rTok: refreshToken }
|
|
5759
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
5686
5760
|
);
|
|
5687
5761
|
const json = refreshResult?.data;
|
|
5688
5762
|
if (json && json.success === true && json.data) {
|
|
@@ -5720,6 +5794,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5720
5794
|
const cache = getHeaderCache(accountId);
|
|
5721
5795
|
cache.headers.cookie = cookieStr;
|
|
5722
5796
|
cache.lastRefresh = Date.now();
|
|
5797
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5723
5798
|
markAccountHeadersReady(accountId);
|
|
5724
5799
|
saveAuthSession2(accountId, {
|
|
5725
5800
|
cookie: cookieStr,
|
|
@@ -5754,6 +5829,7 @@ async function refreshAccountToken(accountId) {
|
|
|
5754
5829
|
const cache = getHeaderCache(accountId);
|
|
5755
5830
|
cache.headers.cookie = cookieStr;
|
|
5756
5831
|
cache.lastRefresh = Date.now();
|
|
5832
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
5757
5833
|
markAccountHeadersReady(accountId);
|
|
5758
5834
|
saveAuthSession2(accountId, {
|
|
5759
5835
|
cookie: cookieStr,
|
|
@@ -6808,11 +6884,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6808
6884
|
const now = Date.now();
|
|
6809
6885
|
const currentUrl = page.url();
|
|
6810
6886
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
6811
|
-
const cookie = await getCookies(accountId);
|
|
6887
|
+
const cookie = await getCookies(accountId, true);
|
|
6812
6888
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
6813
6889
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
6814
|
-
|
|
6815
|
-
|
|
6890
|
+
logger.debug(
|
|
6891
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
6816
6892
|
);
|
|
6817
6893
|
const res = await refreshAccountToken(accountId);
|
|
6818
6894
|
if (res.success) {
|
|
@@ -6820,6 +6896,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
6820
6896
|
touchAccountActivity(accountId);
|
|
6821
6897
|
return true;
|
|
6822
6898
|
}
|
|
6899
|
+
logger.warn(
|
|
6900
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
6901
|
+
);
|
|
6823
6902
|
}
|
|
6824
6903
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
6825
6904
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -7057,6 +7136,7 @@ var init_playwright = __esm({
|
|
|
7057
7136
|
init_qwen_headers();
|
|
7058
7137
|
init_paths();
|
|
7059
7138
|
init_jwt();
|
|
7139
|
+
init_timezone();
|
|
7060
7140
|
requireLocal = createRequire(import.meta.url);
|
|
7061
7141
|
contextInitHooks = [];
|
|
7062
7142
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -7070,7 +7150,7 @@ var init_playwright = __esm({
|
|
|
7070
7150
|
headerCaches = /* @__PURE__ */ new Map();
|
|
7071
7151
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
7072
7152
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
7073
|
-
COOKIE_CACHE_TTL =
|
|
7153
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
7074
7154
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
7075
7155
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
7076
7156
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -8442,6 +8522,8 @@ __export(qwen_exports, {
|
|
|
8442
8522
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
8443
8523
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
8444
8524
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
8525
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
8526
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
8445
8527
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
8446
8528
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
8447
8529
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -8455,10 +8537,13 @@ __export(qwen_exports, {
|
|
|
8455
8537
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
8456
8538
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
8457
8539
|
disableNativeTools: () => disableNativeTools,
|
|
8540
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
8458
8541
|
fetchQwenModels: () => fetchQwenModels,
|
|
8459
8542
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
8460
8543
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
8544
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
8461
8545
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
8546
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
8462
8547
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
8463
8548
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
8464
8549
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -8474,6 +8559,8 @@ __export(qwen_exports, {
|
|
|
8474
8559
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
8475
8560
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
8476
8561
|
setToolCapNotice: () => setToolCapNotice,
|
|
8562
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
8563
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
8477
8564
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
8478
8565
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
8479
8566
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -8760,7 +8847,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
8760
8847
|
function getAccountStreamMutex(accountId) {
|
|
8761
8848
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
8762
8849
|
if (!slots2) {
|
|
8763
|
-
slots2 = {
|
|
8850
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
8764
8851
|
accountStreamMutexes.set(accountId, slots2);
|
|
8765
8852
|
}
|
|
8766
8853
|
return slots2;
|
|
@@ -8768,27 +8855,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
8768
8855
|
function streamSlotCapacity() {
|
|
8769
8856
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
8770
8857
|
}
|
|
8771
|
-
function createStreamSlotRelease(slots2) {
|
|
8858
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
8772
8859
|
let released = false;
|
|
8773
8860
|
return () => {
|
|
8774
8861
|
if (released) return;
|
|
8775
8862
|
released = true;
|
|
8776
|
-
slots2.
|
|
8863
|
+
slots2.activeSlots.delete(slotId);
|
|
8777
8864
|
const next = slots2.queue.shift();
|
|
8778
8865
|
if (next) {
|
|
8779
|
-
|
|
8780
|
-
|
|
8866
|
+
const nextId = nextStreamSlotId++;
|
|
8867
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
8868
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
8781
8869
|
}
|
|
8782
8870
|
};
|
|
8783
8871
|
}
|
|
8872
|
+
function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
|
|
8873
|
+
const now = Date.now();
|
|
8874
|
+
let sweptCount = 0;
|
|
8875
|
+
for (const [accountId, slots2] of accountStreamMutexes.entries()) {
|
|
8876
|
+
for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
|
|
8877
|
+
if (now - startedAt >= maxHoldMs) {
|
|
8878
|
+
logger.warn(
|
|
8879
|
+
`[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
|
|
8880
|
+
);
|
|
8881
|
+
slots2.activeSlots.delete(slotId);
|
|
8882
|
+
sweptCount++;
|
|
8883
|
+
const next = slots2.queue.shift();
|
|
8884
|
+
if (next) {
|
|
8885
|
+
const nextId = nextStreamSlotId++;
|
|
8886
|
+
slots2.activeSlots.set(nextId, now);
|
|
8887
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
8888
|
+
}
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8892
|
+
return sweptCount;
|
|
8893
|
+
}
|
|
8894
|
+
function ensureStaleSlotSweeperStarted() {
|
|
8895
|
+
if (staleSlotSweeperTimer) return;
|
|
8896
|
+
staleSlotSweeperTimer = setInterval(() => {
|
|
8897
|
+
try {
|
|
8898
|
+
sweepStaleAccountStreamSlots();
|
|
8899
|
+
} catch (err) {
|
|
8900
|
+
logger.error("[Qwen] Error in stale stream slot sweeper", {
|
|
8901
|
+
error: err instanceof Error ? err.message : String(err)
|
|
8902
|
+
});
|
|
8903
|
+
}
|
|
8904
|
+
}, 6e4);
|
|
8905
|
+
staleSlotSweeperTimer.unref();
|
|
8906
|
+
}
|
|
8907
|
+
function stopStaleSlotSweeperForTests() {
|
|
8908
|
+
if (staleSlotSweeperTimer) {
|
|
8909
|
+
clearInterval(staleSlotSweeperTimer);
|
|
8910
|
+
staleSlotSweeperTimer = null;
|
|
8911
|
+
}
|
|
8912
|
+
}
|
|
8784
8913
|
async function acquireAccountStreamLock(accountId) {
|
|
8914
|
+
ensureStaleSlotSweeperStarted();
|
|
8785
8915
|
const slots2 = getAccountStreamMutex(accountId);
|
|
8786
|
-
if (slots2.
|
|
8787
|
-
|
|
8788
|
-
|
|
8916
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
8917
|
+
const slotId = nextStreamSlotId++;
|
|
8918
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
8919
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
8789
8920
|
}
|
|
8790
8921
|
return new Promise((resolve) => {
|
|
8791
|
-
slots2.queue.push(() => resolve(
|
|
8922
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
8792
8923
|
});
|
|
8793
8924
|
}
|
|
8794
8925
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -8880,11 +9011,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
8880
9011
|
"x-request-id",
|
|
8881
9012
|
"x-accel-buffering"
|
|
8882
9013
|
]);
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
9014
|
+
const result = {};
|
|
9015
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
9016
|
+
if (browserAllowedHeaders.has(name.toLowerCase())) {
|
|
9017
|
+
result[name] = sanitizeHeaderForFetch(String(value ?? ""));
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
return result;
|
|
8888
9021
|
}
|
|
8889
9022
|
async function requestQwenTextInBrowser(accountId, method, path6, headers, body, options = {}) {
|
|
8890
9023
|
const url = qwenUrl(path6);
|
|
@@ -8905,7 +9038,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8905
9038
|
browserHeaders["Content-Type"] = "application/json";
|
|
8906
9039
|
}
|
|
8907
9040
|
const evaluateRequest = (page) => page.evaluate(
|
|
8908
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
9041
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
8909
9042
|
const controller = new AbortController();
|
|
8910
9043
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
8911
9044
|
try {
|
|
@@ -8929,7 +9062,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8929
9062
|
source: "web",
|
|
8930
9063
|
version: "0.3.11",
|
|
8931
9064
|
"x-request-origin": "https://chat.qwen.ai",
|
|
8932
|
-
timezone:
|
|
9065
|
+
timezone: tz,
|
|
8933
9066
|
authorization: `Bearer ${rTok}`
|
|
8934
9067
|
},
|
|
8935
9068
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -8974,7 +9107,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8974
9107
|
headers: browserHeaders,
|
|
8975
9108
|
body,
|
|
8976
9109
|
referrer: options.referrer,
|
|
8977
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
9110
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
9111
|
+
tz: formatAsciiTimezone()
|
|
8978
9112
|
}
|
|
8979
9113
|
);
|
|
8980
9114
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -9652,7 +9786,7 @@ async function disableNativeTools(accountId) {
|
|
|
9652
9786
|
async (page) => {
|
|
9653
9787
|
const requestId = crypto5.randomUUID();
|
|
9654
9788
|
const response = await page.evaluate(
|
|
9655
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
9789
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
9656
9790
|
const controller = new AbortController();
|
|
9657
9791
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9658
9792
|
try {
|
|
@@ -9664,7 +9798,7 @@ async function disableNativeTools(accountId) {
|
|
|
9664
9798
|
accept: "application/json, text/plain, */*",
|
|
9665
9799
|
"content-type": "application/json",
|
|
9666
9800
|
"x-request-id": requestId2,
|
|
9667
|
-
timezone:
|
|
9801
|
+
timezone: tz,
|
|
9668
9802
|
source: "web"
|
|
9669
9803
|
},
|
|
9670
9804
|
body: JSON.stringify(payload2),
|
|
@@ -9676,7 +9810,7 @@ async function disableNativeTools(accountId) {
|
|
|
9676
9810
|
clearTimeout(timeoutId);
|
|
9677
9811
|
}
|
|
9678
9812
|
},
|
|
9679
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
9813
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
9680
9814
|
);
|
|
9681
9815
|
return response;
|
|
9682
9816
|
}
|
|
@@ -9893,7 +10027,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
9893
10027
|
const requestHeaders = {
|
|
9894
10028
|
source: "web",
|
|
9895
10029
|
version: "0.2.89",
|
|
9896
|
-
timezone: (
|
|
10030
|
+
timezone: formatAsciiTimezone(),
|
|
9897
10031
|
"x-request-id": crypto5.randomUUID(),
|
|
9898
10032
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
9899
10033
|
};
|
|
@@ -9937,7 +10071,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
9937
10071
|
}
|
|
9938
10072
|
const requestHeaders = {
|
|
9939
10073
|
version: "0.2.89",
|
|
9940
|
-
timezone: (
|
|
10074
|
+
timezone: formatAsciiTimezone(),
|
|
9941
10075
|
"x-request-id": crypto5.randomUUID(),
|
|
9942
10076
|
Referer: qwenUrl("/settings/chats")
|
|
9943
10077
|
};
|
|
@@ -9989,7 +10123,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9989
10123
|
async (page) => {
|
|
9990
10124
|
const requestId = crypto5.randomUUID();
|
|
9991
10125
|
const response2 = await page.evaluate(
|
|
9992
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
10126
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
9993
10127
|
const controller = new AbortController();
|
|
9994
10128
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
9995
10129
|
try {
|
|
@@ -9998,7 +10132,7 @@ async function fetchQwenModels(accountId) {
|
|
|
9998
10132
|
headers: {
|
|
9999
10133
|
accept: "application/json, text/plain, */*",
|
|
10000
10134
|
"x-request-id": requestId2,
|
|
10001
|
-
timezone:
|
|
10135
|
+
timezone: tz,
|
|
10002
10136
|
source: "web"
|
|
10003
10137
|
},
|
|
10004
10138
|
signal: controller.signal
|
|
@@ -10008,7 +10142,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10008
10142
|
clearTimeout(timeoutId);
|
|
10009
10143
|
}
|
|
10010
10144
|
},
|
|
10011
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
10145
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
10012
10146
|
);
|
|
10013
10147
|
return response2;
|
|
10014
10148
|
}
|
|
@@ -10047,7 +10181,7 @@ async function fetchQwenModels(accountId) {
|
|
|
10047
10181
|
bxUa,
|
|
10048
10182
|
bxUmidtoken,
|
|
10049
10183
|
extra: {
|
|
10050
|
-
timezone: (
|
|
10184
|
+
timezone: formatAsciiTimezone()
|
|
10051
10185
|
}
|
|
10052
10186
|
}),
|
|
10053
10187
|
void 0,
|
|
@@ -10810,7 +10944,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
10810
10944
|
clearTimeout(timeoutId);
|
|
10811
10945
|
}
|
|
10812
10946
|
}
|
|
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;
|
|
10947
|
+
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
10948
|
var init_qwen = __esm({
|
|
10815
10949
|
"src/services/qwen.ts"() {
|
|
10816
10950
|
"use strict";
|
|
@@ -10828,6 +10962,7 @@ var init_qwen = __esm({
|
|
|
10828
10962
|
init_captcha_coordinator();
|
|
10829
10963
|
init_captcha_solver();
|
|
10830
10964
|
init_account_concurrency();
|
|
10965
|
+
init_timezone();
|
|
10831
10966
|
init_qwen_errors();
|
|
10832
10967
|
init_qwen_thread_state();
|
|
10833
10968
|
init_qwen_chat_pool();
|
|
@@ -10857,7 +10992,10 @@ var init_qwen = __esm({
|
|
|
10857
10992
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
10858
10993
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
10859
10994
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
10995
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
10860
10996
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
10997
|
+
nextStreamSlotId = 1;
|
|
10998
|
+
staleSlotSweeperTimer = null;
|
|
10861
10999
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
10862
11000
|
ui: {
|
|
10863
11001
|
autoTags: false,
|