qwenproxy-cli 1.6.9 → 1.6.11

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/login.js CHANGED
@@ -3492,7 +3492,19 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
3492
3492
  if (localStorage.getItem("qwen_token_logged_out_marker")) {
3493
3493
  return false;
3494
3494
  }
3495
- const res = await fetch("/api/v1/auths/", { method: "GET" });
3495
+ const lsToken = (localStorage.getItem("token") || "").trim();
3496
+ if (lsToken && !/(?:^|;\s*)token=/.test(document.cookie || "")) {
3497
+ try {
3498
+ document.cookie = `token=${encodeURIComponent(lsToken)}; path=/; domain=.qwen.ai; max-age=31536000`;
3499
+ } catch {
3500
+ }
3501
+ }
3502
+ const authHeaders = lsToken ? { Authorization: `Bearer ${lsToken}` } : {};
3503
+ const res = await fetch("/api/v1/auths/", {
3504
+ method: "GET",
3505
+ headers: authHeaders,
3506
+ credentials: "include"
3507
+ });
3496
3508
  if (res.status !== 200) return false;
3497
3509
  const json = await res.json().catch(() => null);
3498
3510
  if (!json) return false;
@@ -3510,6 +3522,7 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
3510
3522
  try {
3511
3523
  const settingsRes = await fetch("/api/v2/users/user/settings", {
3512
3524
  method: "GET",
3525
+ headers: authHeaders,
3513
3526
  credentials: "include",
3514
3527
  signal: AbortSignal.timeout(3e3)
3515
3528
  });
@@ -4297,14 +4310,15 @@ async function getBasicHeaders(accountId) {
4297
4310
  `\u26A0\uFE0F [Playwright] Header refresh failed for ${accountId}; retaining the previous valid cache: ${getErrorMessage(error)}`
4298
4311
  );
4299
4312
  }
4300
- } else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache.refreshInProgress) {
4301
- cache.refreshInProgress = true;
4313
+ } else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache.refreshInProgress && // Header capture types into the chat input and sends a probe message on
4314
+ // the account's page. Doing that while the account is serving a stream
4315
+ // would mutate the live conversation (and can navigate the SPA), so the
4316
+ // proactive refresh waits until the account has no active lease.
4317
+ !hasActiveAccountLease(accountId)) {
4302
4318
  refreshHeadersInternal(accountId).catch((error) => {
4303
4319
  console.warn(
4304
4320
  `\u26A0\uFE0F [Playwright] Background header refresh failed for ${accountId}: ${getErrorMessage(error)}`
4305
4321
  );
4306
- }).finally(() => {
4307
- cache.refreshInProgress = false;
4308
4322
  });
4309
4323
  }
4310
4324
  if (!hasRequiredQwenHeaders(cache.headers)) {
@@ -4442,8 +4456,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4442
4456
  if (accountPages.has(account.id)) {
4443
4457
  return;
4444
4458
  }
4445
- await evictIdlePlaywrightContextsToLimit().catch(() => {
4446
- });
4459
+ if (config.playwright.maxActiveContexts > 0) {
4460
+ await evictIdlePlaywrightContextsToLimit(
4461
+ Math.max(1, config.playwright.maxActiveContexts - 1)
4462
+ ).catch(() => {
4463
+ });
4464
+ }
4447
4465
  const profilePath = getAccountProfilePath(account.id);
4448
4466
  const fingerprint = getFingerprintProfile(account.id);
4449
4467
  const { engine, channel } = resolveBrowserEngine(browserType);
@@ -4668,8 +4686,15 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4668
4686
  const hasAuthCookie = cookies.some(
4669
4687
  (c) => c.name.toLowerCase().includes("token") || c.name.toLowerCase().includes("session")
4670
4688
  );
4689
+ let initialLoginAttempted = false;
4671
4690
  if (!hasAuthCookie && account.email && account.password) {
4672
- await loginToQwen(account.id, account.email, account.password);
4691
+ initialLoginAttempted = true;
4692
+ const loggedIn = await loginToQwen(account.id, account.email, account.password);
4693
+ if (!loggedIn) {
4694
+ throw new Error(
4695
+ `Authentication failed for account ${maskEmail(account.email)}`
4696
+ );
4697
+ }
4673
4698
  }
4674
4699
  try {
4675
4700
  await acctPage.goto(qwenUrl("/"), {
@@ -4679,13 +4704,21 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4679
4704
  await sleep2(1500);
4680
4705
  const currentUrl = acctPage.url();
4681
4706
  const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
4682
- if (isAuthUrl && account.email && account.password) {
4707
+ if (isAuthUrl && !initialLoginAttempted && account.email && account.password) {
4683
4708
  console.log(
4684
4709
  `[Playwright] Initial login needed for ${maskEmail(account.email)}, authenticating...`
4685
4710
  );
4686
- await loginToQwen(account.id, account.email, account.password);
4711
+ const loggedIn = await loginToQwen(account.id, account.email, account.password);
4712
+ if (!loggedIn) {
4713
+ throw new Error(
4714
+ `Authentication failed for account ${maskEmail(account.email)}`
4715
+ );
4716
+ }
4687
4717
  }
4688
4718
  } catch (err) {
4719
+ if (err?.message?.includes("Authentication failed for account")) {
4720
+ throw err;
4721
+ }
4689
4722
  console.warn(
4690
4723
  `\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
4691
4724
  );
@@ -4697,7 +4730,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4697
4730
  if (!acctPage.isClosed()) {
4698
4731
  try {
4699
4732
  const currentUrl = new URL(acctPage.url());
4700
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
4733
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
4701
4734
  await acctPage.goto(qwenUrl("/"), {
4702
4735
  waitUntil: "domcontentloaded",
4703
4736
  timeout: config.timeouts.navigation
@@ -4715,6 +4748,8 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4715
4748
  } finally {
4716
4749
  release();
4717
4750
  }
4751
+ await evictIdlePlaywrightContextsToLimit().catch(() => {
4752
+ });
4718
4753
  })();
4719
4754
  try {
4720
4755
  await initPromise;
@@ -5234,10 +5269,30 @@ async function loginToQwen(accountId, email, password) {
5234
5269
  const uiResult = await loginViaUi(page, email, password);
5235
5270
  if (uiResult.success) {
5236
5271
  cookieCaches.delete(accountId);
5237
- await saveStorageState(page.context(), accountId);
5238
5272
  try {
5239
- const liveCookies = await page.context().cookies();
5240
- const tokenCookie = liveCookies.find((c) => c.name === "token");
5273
+ let liveCookies = await page.context().cookies();
5274
+ let tokenCookie = liveCookies.find((c) => c.name === "token");
5275
+ if (!tokenCookie) {
5276
+ const lsTok = uiResult.token || await page.evaluate(() => (localStorage.getItem("token") || "").trim()).catch(() => "");
5277
+ if (lsTok) {
5278
+ await page.context().addCookies([
5279
+ {
5280
+ name: "token",
5281
+ value: lsTok,
5282
+ domain: ".qwen.ai",
5283
+ path: "/",
5284
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
5285
+ httpOnly: false,
5286
+ secure: true,
5287
+ sameSite: "Lax"
5288
+ }
5289
+ ]).catch(() => {
5290
+ });
5291
+ liveCookies = await page.context().cookies();
5292
+ tokenCookie = liveCookies.find((c) => c.name === "token");
5293
+ }
5294
+ }
5295
+ await saveStorageState(page.context(), accountId);
5241
5296
  if (tokenCookie) {
5242
5297
  const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5243
5298
  const cache = getHeaderCache(accountId);
@@ -5246,7 +5301,7 @@ async function loginToQwen(accountId, email, password) {
5246
5301
  markAccountHeadersReady(accountId);
5247
5302
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5248
5303
  const exp = parseJwtExpiry(tokenCookie.value);
5249
- let rTok = uiResult.refreshToken;
5304
+ let rTok = uiResult.refreshToken || liveCookies.find((c) => c.name === "refresh_token")?.value;
5250
5305
  if (!rTok) {
5251
5306
  try {
5252
5307
  rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
@@ -5327,8 +5382,9 @@ async function loginViaApi(page, email, password) {
5327
5382
  }
5328
5383
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5329
5384
  const requestId = crypto3.randomUUID();
5385
+ const webVersion = getQwenWebVersion() || "0.3.26";
5330
5386
  const evalRes = await page.evaluate(
5331
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
5387
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
5332
5388
  try {
5333
5389
  const response = await fetch(signinUrl2, {
5334
5390
  method: "POST",
@@ -5338,7 +5394,7 @@ async function loginViaApi(page, email, password) {
5338
5394
  accept: "application/json, text/plain, */*",
5339
5395
  "content-type": "application/json",
5340
5396
  source: "web",
5341
- version: getQwenWebVersion() || "0.3.26",
5397
+ version,
5342
5398
  "x-request-origin": "https://chat.qwen.ai",
5343
5399
  timezone: tz,
5344
5400
  "x-request-id": requestId2
@@ -5351,7 +5407,14 @@ async function loginViaApi(page, email, password) {
5351
5407
  return { ok: false, error: e.message };
5352
5408
  }
5353
5409
  },
5354
- { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
5410
+ {
5411
+ email,
5412
+ password: hashedPassword,
5413
+ signinUrl,
5414
+ requestId,
5415
+ tz: formatAsciiTimezone(),
5416
+ version: webVersion
5417
+ }
5355
5418
  ).catch(() => null);
5356
5419
  let data = evalRes?.data;
5357
5420
  let signinSuccess = Boolean(
@@ -5360,7 +5423,7 @@ async function loginViaApi(page, email, password) {
5360
5423
  if (!signinSuccess && !data?.data?.code) {
5361
5424
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
5362
5425
  const fallbackRes = await page.evaluate(
5363
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
5426
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz, version }) => {
5364
5427
  try {
5365
5428
  const response = await fetch(fallbackUrl2, {
5366
5429
  method: "POST",
@@ -5370,7 +5433,7 @@ async function loginViaApi(page, email, password) {
5370
5433
  accept: "application/json, text/plain, */*",
5371
5434
  "content-type": "application/json",
5372
5435
  source: "web",
5373
- version: getQwenWebVersion() || "0.3.26",
5436
+ version,
5374
5437
  timezone: tz,
5375
5438
  "x-request-id": requestId2
5376
5439
  },
@@ -5382,7 +5445,14 @@ async function loginViaApi(page, email, password) {
5382
5445
  return { ok: false, error: e.message };
5383
5446
  }
5384
5447
  },
5385
- { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
5448
+ {
5449
+ email,
5450
+ password: hashedPassword,
5451
+ fallbackUrl,
5452
+ requestId,
5453
+ tz: formatAsciiTimezone(),
5454
+ version: webVersion
5455
+ }
5386
5456
  ).catch(() => null);
5387
5457
  if (fallbackRes?.data) {
5388
5458
  data = fallbackRes.data;
@@ -5607,28 +5677,34 @@ async function loginViaUi(page, email, password) {
5607
5677
  await sleep2(500);
5608
5678
  const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
5609
5679
  const submitButton = page.locator(submitSelector).first();
5610
- await page.evaluate((sel) => {
5611
- try {
5612
- const btn = document.querySelector(sel);
5613
- if (btn) {
5614
- btn.removeAttribute("disabled");
5615
- btn.classList.remove("ant-btn-disabled");
5616
- btn.click();
5617
- }
5618
- } catch {
5619
- }
5620
- }, submitSelector).catch(() => {
5621
- });
5680
+ let submitted = false;
5622
5681
  try {
5623
5682
  if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
5624
- await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
5683
+ await submitButton.click({ timeout: 2e3 }).catch(() => {
5625
5684
  });
5685
+ submitted = true;
5626
5686
  }
5627
5687
  } catch {
5628
5688
  }
5629
- const passInput = page.locator(passwordSelector).first();
5630
- await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
5631
- });
5689
+ if (!submitted) {
5690
+ await page.evaluate(() => {
5691
+ try {
5692
+ const btn = document.querySelector(
5693
+ 'button.qwenchat-auth-pc-submit-button, button[type="submit"]'
5694
+ );
5695
+ if (btn) {
5696
+ btn.removeAttribute("disabled");
5697
+ btn.classList.remove("ant-btn-disabled");
5698
+ btn.click();
5699
+ }
5700
+ } catch {
5701
+ }
5702
+ }).catch(() => {
5703
+ });
5704
+ const passInput = page.locator(passwordSelector).first();
5705
+ await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
5706
+ });
5707
+ }
5632
5708
  const postSubmitDeadline = Date.now() + 15e3;
5633
5709
  while (Date.now() < postSubmitDeadline) {
5634
5710
  if (page.isClosed()) break;
@@ -5646,9 +5722,12 @@ async function loginViaUi(page, email, password) {
5646
5722
  ".ant-form-item-explain-error",
5647
5723
  ".ant-message-error",
5648
5724
  ".ant-message-notice",
5725
+ ".ant-message-custom-content",
5726
+ ".ant-notification-notice",
5649
5727
  '[role="alert"]',
5650
5728
  ".qwenchat-auth-error",
5651
- ".auth-error-message"
5729
+ ".auth-error-message",
5730
+ ".qwenchat-toast"
5652
5731
  ].join(", ");
5653
5732
  const errorEl = page.locator(errorSelector).first();
5654
5733
  if (await errorEl.isVisible().catch(() => false)) {
@@ -5662,7 +5741,15 @@ async function loginViaUi(page, email, password) {
5662
5741
  };
5663
5742
  }
5664
5743
  }
5665
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5744
+ const codeSelector = [
5745
+ 'input[placeholder*="code" i]',
5746
+ 'input[placeholder*="c\xF3digo" i]',
5747
+ 'input[name*="code" i]',
5748
+ ".qwenchat-verification-code-input",
5749
+ ".qwenchat-verification-code-input-cell",
5750
+ '[aria-label*="Verification code" i]',
5751
+ '[aria-label*="c\xF3digo de verifica\xE7\xE3o" i]'
5752
+ ].join(", ");
5666
5753
  const codeInput = page.locator(codeSelector).first();
5667
5754
  if (await codeInput.isVisible().catch(() => false)) {
5668
5755
  return {
@@ -5672,16 +5759,44 @@ async function loginViaUi(page, email, password) {
5672
5759
  }
5673
5760
  try {
5674
5761
  const liveCookies = await page.context().cookies();
5675
- if (liveCookies.some((c) => c.name === "token")) {
5676
- return { success: true };
5762
+ const rTokCookie = liveCookies.find((c) => c.name === "refresh_token")?.value;
5763
+ const tokCookie = liveCookies.find((c) => c.name === "token")?.value;
5764
+ if (tokCookie) {
5765
+ return { success: true, token: tokCookie, refreshToken: rTokCookie };
5677
5766
  }
5678
- } catch {
5679
- }
5680
- const currentUrl = page.url();
5681
- if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
5682
- if (await isPageLoggedIn(page, 3e3)) {
5683
- return { success: true };
5767
+ const currentUrl = page.url();
5768
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
5769
+ const lsTok = await page.evaluate(() => {
5770
+ const t = (localStorage.getItem("token") || "").trim();
5771
+ if (t) {
5772
+ try {
5773
+ document.cookie = `token=${encodeURIComponent(t)}; path=/; domain=.qwen.ai; max-age=31536000`;
5774
+ } catch {
5775
+ }
5776
+ }
5777
+ return t;
5778
+ }).catch(() => "");
5779
+ if (lsTok) {
5780
+ await page.context().addCookies([
5781
+ {
5782
+ name: "token",
5783
+ value: lsTok,
5784
+ domain: ".qwen.ai",
5785
+ path: "/",
5786
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
5787
+ httpOnly: false,
5788
+ secure: true,
5789
+ sameSite: "Lax"
5790
+ }
5791
+ ]).catch(() => {
5792
+ });
5793
+ return { success: true, token: lsTok, refreshToken: rTokCookie };
5794
+ }
5795
+ if (await isPageLoggedIn(page, 3e3)) {
5796
+ return { success: true, refreshToken: rTokCookie };
5797
+ }
5684
5798
  }
5799
+ } catch {
5685
5800
  }
5686
5801
  await sleep2(1e3);
5687
5802
  }
@@ -5711,10 +5826,18 @@ async function refreshAccountToken(accountId) {
5711
5826
  } catch {
5712
5827
  }
5713
5828
  }
5829
+ if (!refreshToken) {
5830
+ try {
5831
+ const liveCookies = await page.context().cookies();
5832
+ refreshToken = liveCookies.find((c) => c.name === "refresh_token")?.value;
5833
+ } catch {
5834
+ }
5835
+ }
5714
5836
  if (refreshToken) {
5715
5837
  try {
5838
+ const webVersion = getQwenWebVersion() || "0.3.26";
5716
5839
  const refreshResult = await page.evaluate(
5717
- async ({ rTok, tz }) => {
5840
+ async ({ rTok, tz, version }) => {
5718
5841
  try {
5719
5842
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5720
5843
  method: "GET",
@@ -5722,7 +5845,7 @@ async function refreshAccountToken(accountId) {
5722
5845
  headers: {
5723
5846
  accept: "application/json, text/plain, */*",
5724
5847
  source: "web",
5725
- version: getQwenWebVersion() || "0.3.26",
5848
+ version,
5726
5849
  "x-request-origin": "https://chat.qwen.ai",
5727
5850
  timezone: tz,
5728
5851
  authorization: `Bearer ${rTok}`
@@ -5734,7 +5857,7 @@ async function refreshAccountToken(accountId) {
5734
5857
  return { ok: false, error: e.message };
5735
5858
  }
5736
5859
  },
5737
- { rTok: refreshToken, tz: formatAsciiTimezone() }
5860
+ { rTok: refreshToken, tz: formatAsciiTimezone(), version: webVersion }
5738
5861
  );
5739
5862
  const json = refreshResult?.data;
5740
5863
  if (json && json.success === true && json.data) {
@@ -6395,19 +6518,26 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6395
6518
  await executeReauth();
6396
6519
  } else {
6397
6520
  try {
6398
- await page.goto(qwenUrl("/"), {
6399
- waitUntil: "domcontentloaded",
6400
- timeout: Math.min(
6401
- config.timeouts.navigation,
6402
- boundedTimeoutMs,
6403
- SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6404
- )
6405
- });
6406
- await sleep2(2e3);
6407
- const url = page.url();
6521
+ const currentUrl = typeof page.url === "function" ? page.url() : "";
6522
+ const needsNavigation = !currentUrl.startsWith(qwenOrigin()) || currentUrl.includes("/auth") || currentUrl.includes("/login") || currentUrl.includes("_____tmd_____");
6523
+ if (needsNavigation) {
6524
+ await page.goto(qwenUrl("/"), {
6525
+ waitUntil: "domcontentloaded",
6526
+ timeout: Math.min(
6527
+ config.timeouts.navigation,
6528
+ boundedTimeoutMs,
6529
+ SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6530
+ )
6531
+ });
6532
+ await sleep2(2e3);
6533
+ }
6534
+ const url = typeof page.url === "function" ? page.url() : "";
6408
6535
  const isAuthUrl = url.includes("auth") || url.includes("login");
6409
6536
  if (isAuthUrl) {
6410
- await executeReauth();
6537
+ const refreshed = await refreshAccountToken(accountId);
6538
+ if (!refreshed.success) {
6539
+ await executeReauth();
6540
+ }
6411
6541
  }
6412
6542
  } catch (navErr) {
6413
6543
  console.warn(
@@ -6464,7 +6594,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6464
6594
  if (capturedPage && !capturedPage.isClosed()) {
6465
6595
  try {
6466
6596
  const currentUrl = new URL(capturedPage.url());
6467
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
6597
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
6468
6598
  await capturedPage.goto(qwenUrl("/"), {
6469
6599
  waitUntil: "domcontentloaded",
6470
6600
  timeout: Math.min(config.timeouts.navigation, boundedTimeoutMs)
@@ -6780,7 +6910,11 @@ function priorityOrderForEviction(accountIds) {
6780
6910
  const ranked = getAccountsByPriority(accountIds.map((id) => ({ id })));
6781
6911
  const rank = /* @__PURE__ */ new Map();
6782
6912
  for (const [index, account] of ranked.entries()) {
6783
- rank.set(account.id, index);
6913
+ if (getAccountCooldownInfo(account.id)) {
6914
+ rank.set(account.id, Number.MAX_SAFE_INTEGER);
6915
+ } else {
6916
+ rank.set(account.id, index);
6917
+ }
6784
6918
  }
6785
6919
  return orderContextsForEviction(
6786
6920
  accountIds,
@@ -6817,14 +6951,15 @@ async function closeIdlePlaywrightAccounts(idleMs) {
6817
6951
  }
6818
6952
  return closed;
6819
6953
  }
6820
- async function evictIdlePlaywrightContextsToLimit() {
6821
- const max = config.playwright.maxActiveContexts;
6822
- if (max <= 0) return 0;
6954
+ async function evictIdlePlaywrightContextsToLimit(targetLimit) {
6955
+ const configuredMax = config.playwright.maxActiveContexts;
6956
+ if (configuredMax <= 0) return 0;
6957
+ const max = targetLimit !== void 0 ? Math.max(0, targetLimit) : configuredMax;
6823
6958
  if (accountPages.size <= max) return 0;
6824
6959
  const candidates = priorityOrderForEviction(
6825
6960
  Array.from(accountPages.keys()).filter((accountId) => {
6826
6961
  if (isAccountServingStream(accountId)) return false;
6827
- if (isAccountRecentlyActive(accountId, 3e4)) return false;
6962
+ if (hasActiveAccountLease(accountId)) return false;
6828
6963
  const mutex = accountMutexes.get(accountId);
6829
6964
  return mutex?.isIdle();
6830
6965
  })
@@ -6837,6 +6972,7 @@ async function evictIdlePlaywrightContextsToLimit() {
6837
6972
  for (const candidate of candidates) {
6838
6973
  if (accountPages.size <= max) break;
6839
6974
  if (isAccountServingStream(candidate.accountId)) continue;
6975
+ if (hasActiveAccountLease(candidate.accountId)) continue;
6840
6976
  const mutex = accountMutexes.get(candidate.accountId);
6841
6977
  if (!mutex?.isIdle()) continue;
6842
6978
  await closePlaywrightForAccount(candidate.accountId).catch((error) => {