qwenproxy-cli 1.6.3 → 1.6.4
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 +3 -2
- package/dist/delete-chats.js +10 -5
- package/dist/index.js +12 -7
- package/dist/login.js +9 -4
- package/dist/reset-cooldowns.js +3 -2
- package/package.json +1 -1
- package/src/core/version.ts +1 -1
- package/src/routes/chat/retry-policy.ts +3 -1
- package/src/services/playwright.ts +14 -6
- package/src/services/qwen.ts +1 -1
package/dist/clean-cache.js
CHANGED
|
@@ -2841,9 +2841,10 @@ function getAccountMutex(accountId) {
|
|
|
2841
2841
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
2842
2842
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
2843
2843
|
const lockState = mutex.state();
|
|
2844
|
-
|
|
2844
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
2845
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
2845
2846
|
logger.warn(
|
|
2846
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
2847
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
2847
2848
|
);
|
|
2848
2849
|
return;
|
|
2849
2850
|
}
|
package/dist/delete-chats.js
CHANGED
|
@@ -3213,6 +3213,7 @@ __export(playwright_exports, {
|
|
|
3213
3213
|
closeIdlePlaywrightAccounts: () => closeIdlePlaywrightAccounts,
|
|
3214
3214
|
closePlaywrightForAccount: () => closePlaywrightForAccount,
|
|
3215
3215
|
evictIdlePlaywrightContextsToLimit: () => evictIdlePlaywrightContextsToLimit,
|
|
3216
|
+
getAccountMutex: () => getAccountMutex,
|
|
3216
3217
|
getActivePlaywrightAccountIds: () => getActivePlaywrightAccountIds,
|
|
3217
3218
|
getBasicHeaders: () => getBasicHeaders,
|
|
3218
3219
|
getCookies: () => getCookies,
|
|
@@ -3362,9 +3363,10 @@ function getAccountMutex(accountId) {
|
|
|
3362
3363
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
3363
3364
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
3364
3365
|
const lockState = mutex.state();
|
|
3365
|
-
|
|
3366
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
3367
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
3366
3368
|
logger.warn(
|
|
3367
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
3369
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
3368
3370
|
);
|
|
3369
3371
|
return;
|
|
3370
3372
|
}
|
|
@@ -4166,7 +4168,8 @@ async function getBasicHeaders(accountId) {
|
|
|
4166
4168
|
}
|
|
4167
4169
|
const release = await acquireAccountMutex(
|
|
4168
4170
|
accountId,
|
|
4169
|
-
`headers:${accountId.substring(0, 12)}
|
|
4171
|
+
`headers:${accountId.substring(0, 12)}`,
|
|
4172
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 3e4)
|
|
4170
4173
|
);
|
|
4171
4174
|
try {
|
|
4172
4175
|
touchAccountActivity(accountId);
|
|
@@ -4259,8 +4262,10 @@ async function getBasicHeaders(accountId) {
|
|
|
4259
4262
|
return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
|
|
4260
4263
|
}
|
|
4261
4264
|
}
|
|
4265
|
+
let refreshedInThisCall = false;
|
|
4262
4266
|
if (headersAge > HEADER_CACHE_TTL && !cache.refreshInProgress) {
|
|
4263
4267
|
try {
|
|
4268
|
+
refreshedInThisCall = true;
|
|
4264
4269
|
await refreshHeadersInternal(accountId);
|
|
4265
4270
|
} catch (error) {
|
|
4266
4271
|
if (!hadUsableHeaders) throw error;
|
|
@@ -4329,7 +4334,7 @@ async function getBasicHeaders(accountId) {
|
|
|
4329
4334
|
} catch {
|
|
4330
4335
|
}
|
|
4331
4336
|
}
|
|
4332
|
-
if (!hasRequiredQwenHeaders(cache.headers)) {
|
|
4337
|
+
if (!hasRequiredQwenHeaders(cache.headers) && !refreshedInThisCall) {
|
|
4333
4338
|
console.log(
|
|
4334
4339
|
`\u{1F504} [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`
|
|
4335
4340
|
);
|
|
@@ -8857,7 +8862,7 @@ async function withQwenBrowserPage(accountId, fn, targetPath, operationTimeoutMs
|
|
|
8857
8862
|
return fn(page);
|
|
8858
8863
|
},
|
|
8859
8864
|
operationTimeoutMs,
|
|
8860
|
-
Math.
|
|
8865
|
+
Math.max(6e4, operationTimeoutMs),
|
|
8861
8866
|
recoverOnTimeout
|
|
8862
8867
|
);
|
|
8863
8868
|
}
|
package/dist/index.js
CHANGED
|
@@ -4323,6 +4323,7 @@ __export(playwright_exports, {
|
|
|
4323
4323
|
closeIdlePlaywrightAccounts: () => closeIdlePlaywrightAccounts,
|
|
4324
4324
|
closePlaywrightForAccount: () => closePlaywrightForAccount,
|
|
4325
4325
|
evictIdlePlaywrightContextsToLimit: () => evictIdlePlaywrightContextsToLimit,
|
|
4326
|
+
getAccountMutex: () => getAccountMutex,
|
|
4326
4327
|
getActivePlaywrightAccountIds: () => getActivePlaywrightAccountIds,
|
|
4327
4328
|
getBasicHeaders: () => getBasicHeaders,
|
|
4328
4329
|
getCookies: () => getCookies,
|
|
@@ -4472,9 +4473,10 @@ function getAccountMutex(accountId) {
|
|
|
4472
4473
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
4473
4474
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
4474
4475
|
const lockState = mutex.state();
|
|
4475
|
-
|
|
4476
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
4477
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
4476
4478
|
logger.warn(
|
|
4477
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
4479
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
4478
4480
|
);
|
|
4479
4481
|
return;
|
|
4480
4482
|
}
|
|
@@ -5276,7 +5278,8 @@ async function getBasicHeaders(accountId) {
|
|
|
5276
5278
|
}
|
|
5277
5279
|
const release = await acquireAccountMutex(
|
|
5278
5280
|
accountId,
|
|
5279
|
-
`headers:${accountId.substring(0, 12)}
|
|
5281
|
+
`headers:${accountId.substring(0, 12)}`,
|
|
5282
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 3e4)
|
|
5280
5283
|
);
|
|
5281
5284
|
try {
|
|
5282
5285
|
touchAccountActivity(accountId);
|
|
@@ -5369,8 +5372,10 @@ async function getBasicHeaders(accountId) {
|
|
|
5369
5372
|
return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
|
|
5370
5373
|
}
|
|
5371
5374
|
}
|
|
5375
|
+
let refreshedInThisCall = false;
|
|
5372
5376
|
if (headersAge > HEADER_CACHE_TTL && !cache3.refreshInProgress) {
|
|
5373
5377
|
try {
|
|
5378
|
+
refreshedInThisCall = true;
|
|
5374
5379
|
await refreshHeadersInternal(accountId);
|
|
5375
5380
|
} catch (error) {
|
|
5376
5381
|
if (!hadUsableHeaders) throw error;
|
|
@@ -5439,7 +5444,7 @@ async function getBasicHeaders(accountId) {
|
|
|
5439
5444
|
} catch {
|
|
5440
5445
|
}
|
|
5441
5446
|
}
|
|
5442
|
-
if (!hasRequiredQwenHeaders(cache3.headers)) {
|
|
5447
|
+
if (!hasRequiredQwenHeaders(cache3.headers) && !refreshedInThisCall) {
|
|
5443
5448
|
console.log(
|
|
5444
5449
|
`\u{1F504} [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`
|
|
5445
5450
|
);
|
|
@@ -9825,7 +9830,7 @@ function isModelNotFoundError(err) {
|
|
|
9825
9830
|
function isNetworkLikeError(err) {
|
|
9826
9831
|
if (err instanceof QwenNetworkError) return true;
|
|
9827
9832
|
const message = errMessage(err).toLowerCase();
|
|
9828
|
-
return message === "network error" || message.includes("failed to fetch") || message.includes("fetch failed") || message.includes("network connection was lost") || message.includes("connection reset") || message.includes("connection closed") || message.includes("socket hang up") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("etimedout");
|
|
9833
|
+
return message === "network error" || message.includes("failed to fetch") || message.includes("fetch failed") || message.includes("network connection was lost") || message.includes("connection reset") || message.includes("connection closed") || message.includes("socket hang up") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("etimedout") || message.includes("stream ended before response headers") || message.includes("timed out waiting for response headers");
|
|
9829
9834
|
}
|
|
9830
9835
|
function isCorruptedChatHistoryError(err) {
|
|
9831
9836
|
const message = errMessage(err).toLowerCase();
|
|
@@ -12047,7 +12052,7 @@ async function withQwenBrowserPage(accountId, fn, targetPath, operationTimeoutMs
|
|
|
12047
12052
|
return fn(page);
|
|
12048
12053
|
},
|
|
12049
12054
|
operationTimeoutMs,
|
|
12050
|
-
Math.
|
|
12055
|
+
Math.max(6e4, operationTimeoutMs),
|
|
12051
12056
|
recoverOnTimeout
|
|
12052
12057
|
);
|
|
12053
12058
|
}
|
|
@@ -26823,7 +26828,7 @@ function getAppVersion() {
|
|
|
26823
26828
|
}
|
|
26824
26829
|
} catch {
|
|
26825
26830
|
}
|
|
26826
|
-
cachedVersion = "v1.6.
|
|
26831
|
+
cachedVersion = "v1.6.4";
|
|
26827
26832
|
return cachedVersion;
|
|
26828
26833
|
}
|
|
26829
26834
|
var cachedVersion;
|
package/dist/login.js
CHANGED
|
@@ -3190,6 +3190,7 @@ __export(playwright_exports, {
|
|
|
3190
3190
|
closeIdlePlaywrightAccounts: () => closeIdlePlaywrightAccounts,
|
|
3191
3191
|
closePlaywrightForAccount: () => closePlaywrightForAccount,
|
|
3192
3192
|
evictIdlePlaywrightContextsToLimit: () => evictIdlePlaywrightContextsToLimit,
|
|
3193
|
+
getAccountMutex: () => getAccountMutex,
|
|
3193
3194
|
getActivePlaywrightAccountIds: () => getActivePlaywrightAccountIds,
|
|
3194
3195
|
getBasicHeaders: () => getBasicHeaders,
|
|
3195
3196
|
getCookies: () => getCookies,
|
|
@@ -3339,9 +3340,10 @@ function getAccountMutex(accountId) {
|
|
|
3339
3340
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
3340
3341
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
3341
3342
|
const lockState = mutex.state();
|
|
3342
|
-
|
|
3343
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
3344
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
3343
3345
|
logger.warn(
|
|
3344
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
3346
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
3345
3347
|
);
|
|
3346
3348
|
return;
|
|
3347
3349
|
}
|
|
@@ -4143,7 +4145,8 @@ async function getBasicHeaders(accountId) {
|
|
|
4143
4145
|
}
|
|
4144
4146
|
const release = await acquireAccountMutex(
|
|
4145
4147
|
accountId,
|
|
4146
|
-
`headers:${accountId.substring(0, 12)}
|
|
4148
|
+
`headers:${accountId.substring(0, 12)}`,
|
|
4149
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 3e4)
|
|
4147
4150
|
);
|
|
4148
4151
|
try {
|
|
4149
4152
|
touchAccountActivity(accountId);
|
|
@@ -4236,8 +4239,10 @@ async function getBasicHeaders(accountId) {
|
|
|
4236
4239
|
return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
|
|
4237
4240
|
}
|
|
4238
4241
|
}
|
|
4242
|
+
let refreshedInThisCall = false;
|
|
4239
4243
|
if (headersAge > HEADER_CACHE_TTL && !cache.refreshInProgress) {
|
|
4240
4244
|
try {
|
|
4245
|
+
refreshedInThisCall = true;
|
|
4241
4246
|
await refreshHeadersInternal(accountId);
|
|
4242
4247
|
} catch (error) {
|
|
4243
4248
|
if (!hadUsableHeaders) throw error;
|
|
@@ -4306,7 +4311,7 @@ async function getBasicHeaders(accountId) {
|
|
|
4306
4311
|
} catch {
|
|
4307
4312
|
}
|
|
4308
4313
|
}
|
|
4309
|
-
if (!hasRequiredQwenHeaders(cache.headers)) {
|
|
4314
|
+
if (!hasRequiredQwenHeaders(cache.headers) && !refreshedInThisCall) {
|
|
4310
4315
|
console.log(
|
|
4311
4316
|
`\u{1F504} [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`
|
|
4312
4317
|
);
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -3104,9 +3104,10 @@ function getAccountMutex(accountId) {
|
|
|
3104
3104
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
3105
3105
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
3106
3106
|
const lockState = mutex.state();
|
|
3107
|
-
|
|
3107
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
3108
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
3108
3109
|
logger.warn(
|
|
3109
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
3110
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
3110
3111
|
);
|
|
3111
3112
|
return;
|
|
3112
3113
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
package/src/core/version.ts
CHANGED
|
@@ -390,7 +390,9 @@ export function isNetworkLikeError(err: unknown): boolean {
|
|
|
390
390
|
message.includes("socket hang up") ||
|
|
391
391
|
message.includes("econnreset") ||
|
|
392
392
|
message.includes("econnrefused") ||
|
|
393
|
-
message.includes("etimedout")
|
|
393
|
+
message.includes("etimedout") ||
|
|
394
|
+
message.includes("stream ended before response headers") ||
|
|
395
|
+
message.includes("timed out waiting for response headers")
|
|
394
396
|
);
|
|
395
397
|
}
|
|
396
398
|
|
|
@@ -166,7 +166,7 @@ export function buildChromiumLaunchArgs(viewport: {
|
|
|
166
166
|
const ACCOUNT_MUTEX_MAX_HOLD_MS = 180_000;
|
|
167
167
|
const accountMutexes = new Map<string, Mutex>();
|
|
168
168
|
|
|
169
|
-
function getAccountMutex(accountId: string): Mutex {
|
|
169
|
+
export function getAccountMutex(accountId: string): Mutex {
|
|
170
170
|
let mutex = accountMutexes.get(accountId);
|
|
171
171
|
if (!mutex) {
|
|
172
172
|
mutex = new Mutex(`playwright:${accountId.substring(0, 8)}`, ACCOUNT_MUTEX_MAX_HOLD_MS);
|
|
@@ -189,15 +189,20 @@ async function recoverStuckAccountMutex(
|
|
|
189
189
|
const lockState = mutex.state();
|
|
190
190
|
// A lock is only considered stuck if it has been held for at least PLAYWRIGHT_MUTEX_WAIT_MS (60s).
|
|
191
191
|
// A waiter with a short timeout (e.g. 5s) timing out does NOT mean the lock holder is stuck!
|
|
192
|
-
// Furthermore, never nuke an account while it is initializing (init: takes 20-30s)
|
|
193
|
-
//
|
|
192
|
+
// Furthermore, never nuke an account while it is initializing (init: takes 20-30s),
|
|
193
|
+
// capturing/refreshing headers (headers/refresh can take up to HEADERS_TIMEOUT),
|
|
194
|
+
// running page operations, or while it is actively serving a stream to a user.
|
|
195
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
194
196
|
if (
|
|
195
|
-
lockState.heldForMs <
|
|
197
|
+
lockState.heldForMs < minLockTimeout ||
|
|
196
198
|
lockState.heldBy.startsWith("init:") ||
|
|
199
|
+
lockState.heldBy.startsWith("headers:") ||
|
|
200
|
+
lockState.heldBy.startsWith("refresh:") ||
|
|
201
|
+
lockState.heldBy.startsWith("profile-reset:") ||
|
|
197
202
|
isAccountServingStream(accountId)
|
|
198
203
|
) {
|
|
199
204
|
logger.warn(
|
|
200
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
205
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`,
|
|
201
206
|
);
|
|
202
207
|
return;
|
|
203
208
|
}
|
|
@@ -1246,6 +1251,7 @@ export async function getBasicHeaders(accountId: string): Promise<{
|
|
|
1246
1251
|
const release = await acquireAccountMutex(
|
|
1247
1252
|
accountId,
|
|
1248
1253
|
`headers:${accountId.substring(0, 12)}`,
|
|
1254
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 30_000),
|
|
1249
1255
|
);
|
|
1250
1256
|
try {
|
|
1251
1257
|
touchAccountActivity(accountId);
|
|
@@ -1369,8 +1375,10 @@ export async function getBasicHeaders(accountId: string): Promise<{
|
|
|
1369
1375
|
|
|
1370
1376
|
// Refresh headers if stale. A valid cached set remains usable when a
|
|
1371
1377
|
// browser recapture transiently fails; a cold/partial cache must fail.
|
|
1378
|
+
let refreshedInThisCall = false;
|
|
1372
1379
|
if (headersAge > HEADER_CACHE_TTL && !cache.refreshInProgress) {
|
|
1373
1380
|
try {
|
|
1381
|
+
refreshedInThisCall = true;
|
|
1374
1382
|
await refreshHeadersInternal(accountId);
|
|
1375
1383
|
} catch (error) {
|
|
1376
1384
|
if (!hadUsableHeaders) throw error;
|
|
@@ -1443,7 +1451,7 @@ export async function getBasicHeaders(accountId: string): Promise<{
|
|
|
1443
1451
|
} catch {}
|
|
1444
1452
|
}
|
|
1445
1453
|
|
|
1446
|
-
if (!hasRequiredQwenHeaders(cache.headers)) {
|
|
1454
|
+
if (!hasRequiredQwenHeaders(cache.headers) && !refreshedInThisCall) {
|
|
1447
1455
|
console.log(
|
|
1448
1456
|
`🔄 [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`,
|
|
1449
1457
|
);
|