qwenproxy-cli 1.6.1 → 1.6.3

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.
@@ -52,6 +52,25 @@ var init_process_guard = __esm({
52
52
  });
53
53
 
54
54
  // src/core/paths.ts
55
+ var paths_exports = {};
56
+ __export(paths_exports, {
57
+ ensureDataDirs: () => ensureDataDirs,
58
+ getAccountPriorityPath: () => getAccountPriorityPath,
59
+ getAccountProfilePath: () => getAccountProfilePath,
60
+ getDataDir: () => getDataDir,
61
+ getDbDir: () => getDbDir,
62
+ getDbPath: () => getDbPath,
63
+ getEncryptionKeyPath: () => getEncryptionKeyPath,
64
+ getEnvFilePath: () => getEnvFilePath,
65
+ getLogsDir: () => getLogsDir,
66
+ getOsGlobalDataDir: () => getOsGlobalDataDir,
67
+ getProfilesDir: () => getProfilesDir,
68
+ getServerLogFilePath: () => getServerLogFilePath,
69
+ getSyncStatePath: () => getSyncStatePath,
70
+ getTuiSettingsPath: () => getTuiSettingsPath,
71
+ isRunningUnderNodeTest: () => isRunningUnderNodeTest,
72
+ resolveDataDir: () => resolveDataDir
73
+ });
55
74
  import fs from "node:fs";
56
75
  import path from "node:path";
57
76
  import os from "node:os";
@@ -116,6 +135,12 @@ function getAccountProfilePath(accountId, customDataDir) {
116
135
  function getAccountPriorityPath(customDataDir) {
117
136
  return path.join(customDataDir || getDataDir(), "account-priority.json");
118
137
  }
138
+ function getSyncStatePath(customDataDir) {
139
+ return path.join(customDataDir || getDataDir(), "sync-state.json");
140
+ }
141
+ function getTuiSettingsPath(customDataDir) {
142
+ return path.join(customDataDir || getDataDir(), "tui-settings.json");
143
+ }
119
144
  function getEnvFilePath(customDataDir) {
120
145
  if (isRunningUnderNodeTest()) {
121
146
  return path.join(customDataDir || getDataDir(), ".env");
@@ -126,6 +151,26 @@ function getEnvFilePath(customDataDir) {
126
151
  }
127
152
  return path.join(customDataDir || getDataDir(), ".env");
128
153
  }
154
+ function getLogsDir(customDataDir) {
155
+ return path.join(customDataDir || getDataDir(), "logs");
156
+ }
157
+ function getServerLogFilePath(customDataDir) {
158
+ return path.join(getLogsDir(customDataDir), "server.log");
159
+ }
160
+ function ensureDataDirs(targetDataDir) {
161
+ const root = targetDataDir || getDataDir();
162
+ const dbDir = getDbDir(root);
163
+ const profilesDir = getProfilesDir(root);
164
+ const logsDir = getLogsDir(root);
165
+ try {
166
+ if (!fs.existsSync(root)) fs.mkdirSync(root, { recursive: true });
167
+ if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir, { recursive: true });
168
+ if (!fs.existsSync(profilesDir)) fs.mkdirSync(profilesDir, { recursive: true });
169
+ if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
170
+ } catch (err) {
171
+ console.error(`[Paths] Error ensuring data directories at ${root}:`, err?.message || String(err));
172
+ }
173
+ }
129
174
  var init_paths = __esm({
130
175
  "src/core/paths.ts"() {
131
176
  "use strict";
@@ -819,7 +864,7 @@ function addAccountsBatch(entries) {
819
864
  }
820
865
  return { added, skipped, invalid };
821
866
  }
822
- function addAccount(email, password, id) {
867
+ function addAccount(email, password = "", id) {
823
868
  if (!email || typeof email !== "string" || email.trim().length === 0) {
824
869
  throw new Error("Email is required");
825
870
  }
@@ -3160,6 +3205,7 @@ var playwright_exports = {};
3160
3205
  __export(playwright_exports, {
3161
3206
  assertAntiBotHeaders: () => assertAntiBotHeaders,
3162
3207
  buildChromiumLaunchArgs: () => buildChromiumLaunchArgs,
3208
+ cancelEmailOtpLogin: () => cancelEmailOtpLogin,
3163
3209
  captureQwenHeaders: () => captureQwenHeaders,
3164
3210
  classifyQwenAuthError: () => classifyQwenAuthError,
3165
3211
  cleanupOrphanProfiles: () => cleanupOrphanProfiles,
@@ -3200,8 +3246,10 @@ __export(playwright_exports, {
3200
3246
  removePlaywrightProfile: () => removePlaywrightProfile,
3201
3247
  saveStorageState: () => saveStorageState,
3202
3248
  schedulePlaywrightProfileReset: () => schedulePlaywrightProfileReset,
3249
+ startEmailOtpLogin: () => startEmailOtpLogin,
3203
3250
  unregisterPlaywrightAccountForTests: () => unregisterPlaywrightAccountForTests,
3204
3251
  validateAccountLogin: () => validateAccountLogin,
3252
+ verifyEmailOtpLogin: () => verifyEmailOtpLogin,
3205
3253
  withAccountPage: () => withAccountPage
3206
3254
  });
3207
3255
  import { chromium as chromium2 } from "patchright";
@@ -3543,6 +3591,10 @@ async function clearVisibleChallenge(page) {
3543
3591
  "button[aria-label*='Close' i]",
3544
3592
  "button[aria-label*='Fechar' i]",
3545
3593
  ".index-module__whats-new-modal-close",
3594
+ ".age-confirmation-modal button",
3595
+ ".age-confirmation-modal .qwen-chat-v2-btn-black",
3596
+ "button:has-text('Continuar')",
3597
+ "button:has-text('Continue')",
3546
3598
  "button:has-text('Cancel')",
3547
3599
  "button:has-text('Cancelar')",
3548
3600
  "button:has-text('I understand')",
@@ -4737,6 +4789,292 @@ async function validateAccountLogin(rawAccount, headless = true, browserType = "
4737
4789
  release();
4738
4790
  }
4739
4791
  }
4792
+ async function startEmailOtpLogin(email, options = {}) {
4793
+ const normalizedEmail = email.trim().toLowerCase();
4794
+ if (!normalizedEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
4795
+ return { success: false, error: "Formato de e-mail inv\xE1lido" };
4796
+ }
4797
+ const { listAccounts: listAccounts2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4798
+ if (listAccounts2().some((a) => a.email.toLowerCase() === normalizedEmail)) {
4799
+ return {
4800
+ success: false,
4801
+ error: `Uma conta com o e-mail "${normalizedEmail}" j\xE1 est\xE1 cadastrada.`
4802
+ };
4803
+ }
4804
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4805
+ const mockSessionId = "mock-otp-" + Date.now();
4806
+ mockOtpSessions.set(mockSessionId, normalizedEmail);
4807
+ return { success: true, sessionId: mockSessionId };
4808
+ }
4809
+ const accountId = crypto3.randomUUID();
4810
+ const sessionId = accountId;
4811
+ const profilePath = getAccountProfilePath(accountId);
4812
+ const fingerprint = getFingerprintProfile(accountId);
4813
+ const browserType = options.browserType ?? config.playwright.browser;
4814
+ const { engine, channel } = resolveBrowserEngine(browserType);
4815
+ const headless = options.headless ?? config.playwright.headless;
4816
+ const launchOptions = {
4817
+ headless,
4818
+ channel,
4819
+ userAgent: fingerprint.userAgent,
4820
+ locale: fingerprint.locale,
4821
+ timezoneId: fingerprint.timezoneId,
4822
+ viewport: fingerprint.viewport,
4823
+ screen: fingerprint.viewport,
4824
+ deviceScaleFactor: 1,
4825
+ isMobile: false,
4826
+ hasTouch: false,
4827
+ colorScheme: "light",
4828
+ extraHTTPHeaders: {
4829
+ "sec-ch-ua": fingerprint.secChUa,
4830
+ "sec-ch-ua-mobile": "?0",
4831
+ "sec-ch-ua-platform": '"Windows"'
4832
+ },
4833
+ ignoreDefaultArgs: ["--enable-automation", "--enable-blink-features"],
4834
+ args: buildChromiumLaunchArgs(fingerprint.viewport)
4835
+ };
4836
+ let acctContext;
4837
+ try {
4838
+ acctContext = await withTimeout(
4839
+ engine.launchPersistentContext(profilePath, launchOptions),
4840
+ 3e4,
4841
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4842
+ );
4843
+ } catch (launchErr) {
4844
+ if (launchErr?.message?.includes("Executable doesn't exist")) {
4845
+ autoInstallPlaywrightChromium();
4846
+ acctContext = await withTimeout(
4847
+ engine.launchPersistentContext(profilePath, launchOptions),
4848
+ 3e4,
4849
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4850
+ );
4851
+ } else {
4852
+ return { success: false, error: launchErr?.message || String(launchErr) };
4853
+ }
4854
+ }
4855
+ try {
4856
+ await acctContext.addInitScript(getStealthScript(fingerprint));
4857
+ const existingPages = acctContext.pages().filter((p) => !p.isClosed());
4858
+ const acctPage = existingPages.find((p) => p.url().startsWith(qwenOrigin())) ?? existingPages[0] ?? await acctContext.newPage();
4859
+ await acctPage.goto(qwenUrl("/auth?mode=register"), {
4860
+ waitUntil: "domcontentloaded",
4861
+ timeout: Math.min(config.timeouts.navigation, 2e4)
4862
+ }).catch(() => {
4863
+ });
4864
+ await sleep2(1e3);
4865
+ await clearVisibleChallenge(acctPage);
4866
+ const requestId = crypto3.randomUUID();
4867
+ const evalRes = await acctPage.evaluate(
4868
+ async ({ email: email2, requestId: requestId2, version, timezone }) => {
4869
+ try {
4870
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/request", {
4871
+ method: "POST",
4872
+ credentials: "include",
4873
+ headers: {
4874
+ accept: "application/json, text/plain, */*",
4875
+ "content-type": "application/json",
4876
+ source: "web",
4877
+ version,
4878
+ "x-request-origin": "https://chat.qwen.ai",
4879
+ timezone,
4880
+ "x-request-id": requestId2
4881
+ },
4882
+ body: JSON.stringify({
4883
+ identifier: email2,
4884
+ accept_terms: true,
4885
+ language: "pt-BR"
4886
+ })
4887
+ });
4888
+ const json = await response.json().catch(() => null);
4889
+ return { ok: response.ok, status: response.status, data: json };
4890
+ } catch (e) {
4891
+ return { ok: false, error: e.message };
4892
+ }
4893
+ },
4894
+ {
4895
+ email: normalizedEmail,
4896
+ requestId,
4897
+ version: getQwenWebVersion() || "0.3.12",
4898
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4899
+ }
4900
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4901
+ if (!evalRes.ok || evalRes.data?.success !== true) {
4902
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "Falha ao solicitar c\xF3digo de verifica\xE7\xE3o";
4903
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4904
+ });
4905
+ cleanupPlaywrightAccountState(accountId);
4906
+ return { success: false, error: errorMsg };
4907
+ }
4908
+ const timer = setTimeout(async () => {
4909
+ activeOtpSessions.delete(sessionId);
4910
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4911
+ });
4912
+ cleanupPlaywrightAccountState(accountId);
4913
+ }, 5 * 60 * 1e3);
4914
+ timer.unref?.();
4915
+ activeOtpSessions.set(sessionId, {
4916
+ sessionId,
4917
+ accountId,
4918
+ email: normalizedEmail,
4919
+ context: acctContext,
4920
+ page: acctPage,
4921
+ fingerprint,
4922
+ createdAt: Date.now(),
4923
+ timer
4924
+ });
4925
+ return { success: true, sessionId };
4926
+ } catch (err) {
4927
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4928
+ });
4929
+ cleanupPlaywrightAccountState(accountId);
4930
+ return { success: false, error: err?.message || String(err) };
4931
+ }
4932
+ }
4933
+ async function verifyEmailOtpLogin(sessionId, code) {
4934
+ const cleanCode = code.trim();
4935
+ if (!cleanCode) {
4936
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o \xE9 obrigat\xF3rio" };
4937
+ }
4938
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4939
+ const mockEmail = mockOtpSessions.get(sessionId);
4940
+ if (!mockEmail) {
4941
+ return { success: false, error: "Sess\xE3o OTP inv\xE1lida ou expirada" };
4942
+ }
4943
+ if (cleanCode === "000000") {
4944
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado" };
4945
+ }
4946
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4947
+ const mockAccId = "mock-otp-acc-" + Date.now();
4948
+ const newAccount = addAccount2(mockEmail, "", mockAccId);
4949
+ mockOtpSessions.delete(sessionId);
4950
+ return { success: true, account: newAccount };
4951
+ }
4952
+ const session = activeOtpSessions.get(sessionId);
4953
+ if (!session) {
4954
+ return { success: false, error: "Sess\xE3o OTP expirada ou n\xE3o encontrada. Solicite um novo c\xF3digo." };
4955
+ }
4956
+ if (session.page.isClosed()) {
4957
+ activeOtpSessions.delete(sessionId);
4958
+ return { success: false, error: "Navegador foi fechado. Solicite um novo c\xF3digo." };
4959
+ }
4960
+ const requestId = crypto3.randomUUID();
4961
+ const evalRes = await session.page.evaluate(
4962
+ async ({ email, code: code2, requestId: requestId2, version, timezone }) => {
4963
+ try {
4964
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/verify", {
4965
+ method: "POST",
4966
+ credentials: "include",
4967
+ headers: {
4968
+ accept: "application/json, text/plain, */*",
4969
+ "content-type": "application/json",
4970
+ source: "web",
4971
+ version,
4972
+ "x-request-origin": "https://chat.qwen.ai",
4973
+ timezone,
4974
+ "x-request-id": requestId2
4975
+ },
4976
+ body: JSON.stringify({
4977
+ identifier: email,
4978
+ code: code2,
4979
+ name: null
4980
+ })
4981
+ });
4982
+ const json = await response.json().catch(() => null);
4983
+ return { ok: response.ok, status: response.status, data: json };
4984
+ } catch (e) {
4985
+ return { ok: false, error: e.message };
4986
+ }
4987
+ },
4988
+ {
4989
+ email: session.email,
4990
+ code: cleanCode,
4991
+ requestId,
4992
+ version: getQwenWebVersion() || "0.3.12",
4993
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4994
+ }
4995
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4996
+ if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
4997
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado";
4998
+ return { success: false, error: errorMsg };
4999
+ }
5000
+ const resData = evalRes.data.data;
5001
+ const accessToken = resData.access_token || resData.token;
5002
+ const refreshToken = resData.refresh_token;
5003
+ const userId = resData.id;
5004
+ try {
5005
+ await session.page.evaluate(({ token, rTok }) => {
5006
+ try {
5007
+ localStorage.setItem("token", token);
5008
+ localStorage.setItem("access_token", token);
5009
+ if (rTok) localStorage.setItem("refresh_token", rTok);
5010
+ document.cookie = `token=${encodeURIComponent(token)}; path=/; domain=.qwen.ai; max-age=31536000`;
5011
+ } catch {
5012
+ }
5013
+ }, { token: accessToken, rTok: refreshToken });
5014
+ await session.context.addCookies([
5015
+ {
5016
+ name: "token",
5017
+ value: accessToken,
5018
+ domain: ".qwen.ai",
5019
+ path: "/",
5020
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
5021
+ httpOnly: false,
5022
+ secure: true,
5023
+ sameSite: "Lax"
5024
+ }
5025
+ ]);
5026
+ await session.page.goto(qwenUrl("/"), {
5027
+ waitUntil: "domcontentloaded",
5028
+ timeout: Math.min(config.timeouts.navigation, 15e3)
5029
+ }).catch(() => {
5030
+ });
5031
+ await sleep2(1e3);
5032
+ await saveStorageState(session.context, session.accountId);
5033
+ const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5034
+ const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
5035
+ const exp = parseJwtExpiry2(accessToken);
5036
+ saveAuthSession2(session.accountId, {
5037
+ cookie: `token=${accessToken}`,
5038
+ userAgent: session.fingerprint.userAgent,
5039
+ bxV: "2.5.37",
5040
+ version: getQwenWebVersion() || "0.3.12",
5041
+ userId: userId || void 0,
5042
+ tokenExpiresAt: exp || void 0,
5043
+ refreshToken: refreshToken || void 0,
5044
+ capturedAt: Date.now()
5045
+ });
5046
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5047
+ const newAccount = addAccount2(session.email, "", session.accountId);
5048
+ clearTimeout(session.timer);
5049
+ activeOtpSessions.delete(sessionId);
5050
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5051
+ });
5052
+ cleanupPlaywrightAccountState(session.accountId);
5053
+ return { success: true, account: newAccount };
5054
+ } catch (err) {
5055
+ clearTimeout(session.timer);
5056
+ activeOtpSessions.delete(sessionId);
5057
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5058
+ });
5059
+ cleanupPlaywrightAccountState(session.accountId);
5060
+ return { success: false, error: err?.message || String(err) };
5061
+ }
5062
+ }
5063
+ async function cancelEmailOtpLogin(sessionId) {
5064
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
5065
+ mockOtpSessions.delete(sessionId);
5066
+ return;
5067
+ }
5068
+ const session = activeOtpSessions.get(sessionId);
5069
+ if (!session) return;
5070
+ clearTimeout(session.timer);
5071
+ activeOtpSessions.delete(sessionId);
5072
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5073
+ });
5074
+ cleanupPlaywrightAccountState(session.accountId);
5075
+ const { wipeAccountSessionFiles: wipeAccountSessionFiles2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5076
+ wipeAccountSessionFiles2(session.accountId);
5077
+ }
4740
5078
  function classifyQwenAuthError(code, details) {
4741
5079
  const c = (code || "").trim().toLowerCase();
4742
5080
  const d = (details || "").trim().toLowerCase();
@@ -5434,7 +5772,24 @@ async function refreshAccountToken(accountId) {
5434
5772
  return { success: false, error: "Could not refresh or re-authenticate token" };
5435
5773
  }
5436
5774
  async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
5437
- const page = pageOverride ?? accountPages.get(accountId);
5775
+ let page = pageOverride ?? accountPages.get(accountId);
5776
+ if (!page || page.isClosed()) {
5777
+ const { isRunningUnderNodeTest: isRunningUnderNodeTest4 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
5778
+ if (!isRunningUnderNodeTest4()) {
5779
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5780
+ const creds = getAccountCredentials2(accountId);
5781
+ if (creds) {
5782
+ await initPlaywrightForAccount(
5783
+ creds,
5784
+ config.playwright.headless,
5785
+ config.playwright.browser,
5786
+ { skipHeaderCapture: true }
5787
+ ).catch(() => {
5788
+ });
5789
+ page = accountPages.get(accountId);
5790
+ }
5791
+ }
5792
+ }
5438
5793
  if (!page || page.isClosed()) {
5439
5794
  throw new Error(`Playwright page unavailable for header capture: ${accountId}`);
5440
5795
  }
@@ -6077,16 +6432,38 @@ async function refreshHeaders(accountId, timeoutMs = config.timeouts.headers, fo
6077
6432
  release();
6078
6433
  }
6079
6434
  }
6080
- async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, mutexTimeoutMs = PLAYWRIGHT_MUTEX_WAIT_MS, recoverOnTimeout = true) {
6435
+ async function ensureAccountPageAvailable(accountId) {
6081
6436
  const inFlightInit = inFlightAccountInits.get(accountId);
6082
6437
  if (inFlightInit) {
6083
6438
  await inFlightInit.catch(() => {
6084
6439
  });
6085
6440
  }
6086
- const page = accountPages.get(accountId);
6441
+ let page = accountPages.get(accountId);
6442
+ if (page && !page.isClosed()) {
6443
+ return page;
6444
+ }
6445
+ const { isRunningUnderNodeTest: isRunningUnderNodeTest4 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
6446
+ if (isRunningUnderNodeTest4()) {
6447
+ throw new Error(`Playwright page unavailable for account: ${accountId}`);
6448
+ }
6449
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
6450
+ const creds = getAccountCredentials2(accountId);
6451
+ if (!creds) {
6452
+ throw new Error(`Playwright page unavailable for account: ${accountId}`);
6453
+ }
6454
+ await initPlaywrightForAccount(
6455
+ creds,
6456
+ config.playwright.headless,
6457
+ config.playwright.browser
6458
+ );
6459
+ page = accountPages.get(accountId);
6087
6460
  if (!page || page.isClosed()) {
6088
6461
  throw new Error(`Playwright page unavailable for account: ${accountId}`);
6089
6462
  }
6463
+ return page;
6464
+ }
6465
+ async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, mutexTimeoutMs = PLAYWRIGHT_MUTEX_WAIT_MS, recoverOnTimeout = true) {
6466
+ const page = await ensureAccountPageAvailable(accountId);
6090
6467
  const release = await acquireAccountMutex(
6091
6468
  accountId,
6092
6469
  `page:${accountId.substring(0, 12)}`,
@@ -6105,7 +6482,7 @@ async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION
6105
6482
  return result;
6106
6483
  } catch (error) {
6107
6484
  const message = getErrorMessage(error);
6108
- if (recoverOnTimeout && message.includes("Playwright page operation timed out")) {
6485
+ if (recoverOnTimeout && message.includes("Playwright page operation timed out") && (!page || page.isClosed())) {
6109
6486
  console.warn(
6110
6487
  `\u23F1\uFE0F [Playwright] Resetting account context after a stuck page operation: ${accountId}`
6111
6488
  );
@@ -6387,8 +6764,10 @@ async function evictIdlePlaywrightContextsToLimit() {
6387
6764
  if (accountPages.size <= max) return 0;
6388
6765
  const candidates = priorityOrderForEviction(
6389
6766
  Array.from(accountPages.keys()).filter((accountId) => {
6767
+ if (isAccountServingStream(accountId)) return false;
6768
+ if (isAccountRecentlyActive(accountId, 3e4)) return false;
6390
6769
  const mutex = accountMutexes.get(accountId);
6391
- return mutex?.isIdle() && !isAccountServingStream(accountId);
6770
+ return mutex?.isIdle();
6392
6771
  })
6393
6772
  ).map((accountId) => ({
6394
6773
  accountId,
@@ -6654,7 +7033,7 @@ async function getTokenDiagnostics(accountId) {
6654
7033
  }
6655
7034
  };
6656
7035
  }
6657
- var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
7036
+ var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, activeOtpSessions, mockOtpSessions, PROFILE_RESET_TIMEOUT_MS;
6658
7037
  var init_playwright = __esm({
6659
7038
  "src/services/playwright.ts"() {
6660
7039
  "use strict";
@@ -6699,11 +7078,13 @@ var init_playwright = __esm({
6699
7078
  PLAYWRIGHT_MUTEX_WAIT_MS = 6e4;
6700
7079
  ACCOUNT_PAGE_OPERATION_TIMEOUT_MS = config.timeouts.page;
6701
7080
  SESSION_PROBE_NAVIGATION_TIMEOUT_MS = 15e3;
6702
- HEADER_CAPTURE_TRIGGER_GRACE_MS = 15e3;
6703
- FIRST_TRIGGER_GRACE_MS = 8e3;
7081
+ HEADER_CAPTURE_TRIGGER_GRACE_MS = 2e4;
7082
+ FIRST_TRIGGER_GRACE_MS = 12e3;
6704
7083
  HEADER_CAPTURE_TRIGGER_ATTEMPTS = 3;
6705
7084
  CHAT_INPUT_APPEAR_TIMEOUT_MS = 15e3;
6706
7085
  CHAT_INPUT_ACTION_TIMEOUT_MS = 1e4;
7086
+ activeOtpSessions = /* @__PURE__ */ new Map();
7087
+ mockOtpSessions = /* @__PURE__ */ new Map();
6707
7088
  PROFILE_RESET_TIMEOUT_MS = Math.max(9e4, config.timeouts.headers);
6708
7089
  setWafContextResetListener((accountId) => {
6709
7090
  if (!accountPages.has(accountId)) return;
@@ -7715,6 +8096,15 @@ var init_helpers = __esm({
7715
8096
  }
7716
8097
  });
7717
8098
 
8099
+ // src/utils/overload-detector.ts
8100
+ var OVERLOAD_COOLDOWN_MS;
8101
+ var init_overload_detector = __esm({
8102
+ "src/utils/overload-detector.ts"() {
8103
+ "use strict";
8104
+ OVERLOAD_COOLDOWN_MS = 5 * 60 * 1e3;
8105
+ }
8106
+ });
8107
+
7718
8108
  // src/routes/chat/retry-policy.ts
7719
8109
  var init_retry_policy = __esm({
7720
8110
  "src/routes/chat/retry-policy.ts"() {
@@ -7725,6 +8115,7 @@ var init_retry_policy = __esm({
7725
8115
  init_qwen();
7726
8116
  init_errors();
7727
8117
  init_helpers();
8118
+ init_overload_detector();
7728
8119
  init_account_manager();
7729
8120
  }
7730
8121
  });
@@ -8688,7 +9079,8 @@ async function requestQwenPersonalizationInBrowser(accountId, method, path6, hea
8688
9079
  payload === void 0 ? void 0 : JSON.stringify(payload),
8689
9080
  {
8690
9081
  settingsPage: true,
8691
- referrer: qwenUrl("/settings/personalization")
9082
+ referrer: qwenUrl("/settings/personalization"),
9083
+ noMutexRecovery: true
8692
9084
  }
8693
9085
  );
8694
9086
  const { raw, json } = await readJsonTextResponse(response);
@@ -9189,8 +9581,9 @@ async function syncQwenRequestPersonalizationInternal(instruction, accountId, me
9189
9581
  const returnedInstruction = json?.data?.personalization?.instruction;
9190
9582
  const returned = textSize(returnedInstruction);
9191
9583
  let stored = { chars: null, bytes: null, hash: null };
9584
+ const matchReturned = isEmptyInstruction && (!returnedInstruction || returned.chars === 0) || returned.hash !== null && returned.hash === sent.hash;
9192
9585
  let verifyData = null;
9193
- if (config.qwen.personalizationVerifyGet) {
9586
+ if (config.qwen.personalizationVerifyGet && !matchReturned) {
9194
9587
  const { json: verifyJson } = await requestQwenPersonalizationInBrowser(
9195
9588
  accountId,
9196
9589
  "GET",
@@ -9200,7 +9593,6 @@ async function syncQwenRequestPersonalizationInternal(instruction, accountId, me
9200
9593
  verifyData = verifyJson?.data?.personalization;
9201
9594
  stored = textSize(verifyData?.instruction);
9202
9595
  }
9203
- const matchReturned = isEmptyInstruction && (!returnedInstruction || returned.chars === 0) || returned.hash !== null && returned.hash === sent.hash;
9204
9596
  const matchStored = stored.hash === null ? null : isEmptyInstruction && (!verifyData?.instruction || stored.chars === 0) || stored.hash === sent.hash;
9205
9597
  const applied = matchReturned || matchStored === true;
9206
9598
  if (syncHash && applied) {
@@ -9574,6 +9966,17 @@ async function fetchQwenModels(accountId) {
9574
9966
  );
9575
9967
  return cached.models;
9576
9968
  }
9969
+ if (accountId && !isAuthMockEnabled() && !isPlaywrightInitialized(accountId)) {
9970
+ for (const [, entry] of modelsCache.entries()) {
9971
+ if (entry && now - entry.fetchedAt < MODEL_CACHE_TTL_MS * 4) {
9972
+ syncModelMetadata(
9973
+ entry.models,
9974
+ accountId
9975
+ );
9976
+ return entry.models;
9977
+ }
9978
+ }
9979
+ }
9577
9980
  if (accountId && !isAuthMockEnabled() && isAccountBusy(accountId)) {
9578
9981
  try {
9579
9982
  const result = await withAccountPage(