qwenproxy-cli 1.6.0 → 1.6.2

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.
@@ -781,7 +781,7 @@ function addAccountsBatch(entries) {
781
781
  }
782
782
  return { added, skipped, invalid };
783
783
  }
784
- function addAccount(email, password, id) {
784
+ function addAccount(email, password = "", id) {
785
785
  if (!email || typeof email !== "string" || email.trim().length === 0) {
786
786
  throw new Error("Email is required");
787
787
  }
@@ -1016,11 +1016,11 @@ var init_config = __esm({
1016
1016
  // nothing in this window is dead and should fail fast, retryable).
1017
1017
  QWEN_FIRST_CHUNK_TIMEOUT: z.string().default("60000"),
1018
1018
  TOTAL_REQUEST_TIMEOUT: z.string().default("600000"),
1019
- // Mid-stream silence window for thinking models: 3 min with ZERO upstream
1019
+ // Mid-stream silence window for thinking models: 5 min with ZERO upstream
1020
1020
  // bytes is a dead stream (WAF swallow / dropped connection) — fail fast and
1021
1021
  // let the retry policy rotate accounts. Flowing reasoning chunks RESET this
1022
1022
  // timer, so legitimate slow thinking is never cut; only total silence is.
1023
- REASONING_MODEL_TIMEOUT: z.string().default("180000"),
1023
+ REASONING_MODEL_TIMEOUT: z.string().default("300000"),
1024
1024
  CACHE_TTL: z.string().default("3600"),
1025
1025
  RESPONSE_TTL: z.string().default("1800"),
1026
1026
  CACHE_COMPRESSION_ENABLED: z.string().default("true"),
@@ -2848,15 +2848,18 @@ function loadStorageState(accountId) {
2848
2848
  return void 0;
2849
2849
  }
2850
2850
  }
2851
- async function saveStorageState(context, accountId, timeoutMs = 5e3) {
2851
+ async function saveStorageState(context, accountId, timeoutMs = 1e4) {
2852
2852
  try {
2853
2853
  const stateFile = getStorageStatePath(accountId);
2854
2854
  const dir = path4.dirname(stateFile);
2855
2855
  if (!fs7.existsSync(dir)) {
2856
2856
  fs7.mkdirSync(dir, { recursive: true });
2857
2857
  }
2858
+ const rawPromise = context.storageState({ path: stateFile }).catch(() => {
2859
+ return void 0;
2860
+ });
2858
2861
  await withTimeout(
2859
- context.storageState({ path: stateFile }),
2862
+ rawPromise,
2860
2863
  timeoutMs,
2861
2864
  `storageState timed out after ${timeoutMs}ms`
2862
2865
  );
@@ -2960,6 +2963,10 @@ async function clearVisibleChallenge(page) {
2960
2963
  "button[aria-label*='Close' i]",
2961
2964
  "button[aria-label*='Fechar' i]",
2962
2965
  ".index-module__whats-new-modal-close",
2966
+ ".age-confirmation-modal button",
2967
+ ".age-confirmation-modal .qwen-chat-v2-btn-black",
2968
+ "button:has-text('Continuar')",
2969
+ "button:has-text('Continue')",
2963
2970
  "button:has-text('Cancel')",
2964
2971
  "button:has-text('Cancelar')",
2965
2972
  "button:has-text('I understand')",
@@ -5319,8 +5326,8 @@ var init_playwright = __esm({
5319
5326
  PLAYWRIGHT_MUTEX_WAIT_MS = 6e4;
5320
5327
  ACCOUNT_PAGE_OPERATION_TIMEOUT_MS = config.timeouts.page;
5321
5328
  SESSION_PROBE_NAVIGATION_TIMEOUT_MS = 15e3;
5322
- HEADER_CAPTURE_TRIGGER_GRACE_MS = 15e3;
5323
- FIRST_TRIGGER_GRACE_MS = 8e3;
5329
+ HEADER_CAPTURE_TRIGGER_GRACE_MS = 2e4;
5330
+ FIRST_TRIGGER_GRACE_MS = 12e3;
5324
5331
  HEADER_CAPTURE_TRIGGER_ATTEMPTS = 3;
5325
5332
  CHAT_INPUT_APPEAR_TIMEOUT_MS = 15e3;
5326
5333
  CHAT_INPUT_ACTION_TIMEOUT_MS = 1e4;
@@ -5336,6 +5343,44 @@ var init_playwright = __esm({
5336
5343
  }
5337
5344
  });
5338
5345
 
5346
+ // src/core/process-guard.ts
5347
+ function isPlaywrightAlreadyClosedMessage(message) {
5348
+ if (!message) return false;
5349
+ return message.includes("Target page, context or browser has been closed") || message.includes("Browser has been closed") || message.includes("Target closed") || message.includes("Target crashed") || message.includes("Page crashed") || message.includes("Assertion error") || message.includes("Cannot find parent object") || message.includes("Connection closed") || message.includes("session closed") || message.includes("Session closed") || message.includes("Network.setCacheDisabled") || message.includes("Protocol error") || message.includes("storageState timed out");
5350
+ }
5351
+ function isBenignTeardownError(error) {
5352
+ if (!error) return false;
5353
+ const message = error instanceof Error ? error.message : typeof error === "object" && "message" in error ? String(error.message) : String(error);
5354
+ return isPlaywrightAlreadyClosedMessage(message);
5355
+ }
5356
+ function installProcessTeardownGuards(label = "Process") {
5357
+ if (guardsInstalled) return;
5358
+ guardsInstalled = true;
5359
+ process.on("uncaughtException", (error) => {
5360
+ if (isBenignTeardownError(error)) {
5361
+ const msg = error instanceof Error ? error.message : typeof error === "object" && error !== null && "message" in error ? String(error.message) : String(error);
5362
+ console.warn(`\u26A0\uFE0F [${label}] Handled benign driver teardown exception: ${msg}`);
5363
+ return;
5364
+ }
5365
+ console.error(`\u274C [${label}] Uncaught Exception:`, error);
5366
+ });
5367
+ process.on("unhandledRejection", (reason) => {
5368
+ if (isBenignTeardownError(reason)) {
5369
+ const msg = reason instanceof Error ? reason.message : typeof reason === "object" && reason !== null && "message" in reason ? String(reason.message) : String(reason);
5370
+ console.warn(`\u26A0\uFE0F [${label}] Handled benign driver teardown rejection: ${msg}`);
5371
+ return;
5372
+ }
5373
+ console.error(`\u274C [${label}] Unhandled Rejection:`, reason);
5374
+ });
5375
+ }
5376
+ var guardsInstalled;
5377
+ var init_process_guard = __esm({
5378
+ "src/core/process-guard.ts"() {
5379
+ "use strict";
5380
+ guardsInstalled = false;
5381
+ }
5382
+ });
5383
+
5339
5384
  // src/clean-cache.ts
5340
5385
  var clean_cache_exports = {};
5341
5386
  __export(clean_cache_exports, {
@@ -5503,6 +5548,8 @@ var isDirectRun;
5503
5548
  var init_clean_cache = __esm({
5504
5549
  "src/clean-cache.ts"() {
5505
5550
  init_playwright();
5551
+ init_process_guard();
5552
+ installProcessTeardownGuards("CleanCache");
5506
5553
  isDirectRun = process.argv[1] && (process.argv[1].endsWith("clean-cache.ts") || process.argv[1].endsWith("clean-cache.js"));
5507
5554
  if (isDirectRun) {
5508
5555
  main().catch((err) => {
@@ -13,6 +13,44 @@ var __export = (target, all) => {
13
13
  __defProp(target, name, { get: all[name], enumerable: true });
14
14
  };
15
15
 
16
+ // src/core/process-guard.ts
17
+ function isPlaywrightAlreadyClosedMessage(message) {
18
+ if (!message) return false;
19
+ return message.includes("Target page, context or browser has been closed") || message.includes("Browser has been closed") || message.includes("Target closed") || message.includes("Target crashed") || message.includes("Page crashed") || message.includes("Assertion error") || message.includes("Cannot find parent object") || message.includes("Connection closed") || message.includes("session closed") || message.includes("Session closed") || message.includes("Network.setCacheDisabled") || message.includes("Protocol error") || message.includes("storageState timed out");
20
+ }
21
+ function isBenignTeardownError(error) {
22
+ if (!error) return false;
23
+ const message = error instanceof Error ? error.message : typeof error === "object" && "message" in error ? String(error.message) : String(error);
24
+ return isPlaywrightAlreadyClosedMessage(message);
25
+ }
26
+ function installProcessTeardownGuards(label = "Process") {
27
+ if (guardsInstalled) return;
28
+ guardsInstalled = true;
29
+ process.on("uncaughtException", (error) => {
30
+ if (isBenignTeardownError(error)) {
31
+ const msg = error instanceof Error ? error.message : typeof error === "object" && error !== null && "message" in error ? String(error.message) : String(error);
32
+ console.warn(`\u26A0\uFE0F [${label}] Handled benign driver teardown exception: ${msg}`);
33
+ return;
34
+ }
35
+ console.error(`\u274C [${label}] Uncaught Exception:`, error);
36
+ });
37
+ process.on("unhandledRejection", (reason) => {
38
+ if (isBenignTeardownError(reason)) {
39
+ const msg = reason instanceof Error ? reason.message : typeof reason === "object" && reason !== null && "message" in reason ? String(reason.message) : String(reason);
40
+ console.warn(`\u26A0\uFE0F [${label}] Handled benign driver teardown rejection: ${msg}`);
41
+ return;
42
+ }
43
+ console.error(`\u274C [${label}] Unhandled Rejection:`, reason);
44
+ });
45
+ }
46
+ var guardsInstalled;
47
+ var init_process_guard = __esm({
48
+ "src/core/process-guard.ts"() {
49
+ "use strict";
50
+ guardsInstalled = false;
51
+ }
52
+ });
53
+
16
54
  // src/core/paths.ts
17
55
  import fs from "node:fs";
18
56
  import path from "node:path";
@@ -781,7 +819,7 @@ function addAccountsBatch(entries) {
781
819
  }
782
820
  return { added, skipped, invalid };
783
821
  }
784
- function addAccount(email, password, id) {
822
+ function addAccount(email, password = "", id) {
785
823
  if (!email || typeof email !== "string" || email.trim().length === 0) {
786
824
  throw new Error("Email is required");
787
825
  }
@@ -1071,11 +1109,11 @@ var init_config = __esm({
1071
1109
  // nothing in this window is dead and should fail fast, retryable).
1072
1110
  QWEN_FIRST_CHUNK_TIMEOUT: z.string().default("60000"),
1073
1111
  TOTAL_REQUEST_TIMEOUT: z.string().default("600000"),
1074
- // Mid-stream silence window for thinking models: 3 min with ZERO upstream
1112
+ // Mid-stream silence window for thinking models: 5 min with ZERO upstream
1075
1113
  // bytes is a dead stream (WAF swallow / dropped connection) — fail fast and
1076
1114
  // let the retry policy rotate accounts. Flowing reasoning chunks RESET this
1077
1115
  // timer, so legitimate slow thinking is never cut; only total silence is.
1078
- REASONING_MODEL_TIMEOUT: z.string().default("180000"),
1116
+ REASONING_MODEL_TIMEOUT: z.string().default("300000"),
1079
1117
  CACHE_TTL: z.string().default("3600"),
1080
1118
  RESPONSE_TTL: z.string().default("1800"),
1081
1119
  CACHE_COMPRESSION_ENABLED: z.string().default("true"),
@@ -3105,6 +3143,8 @@ var init_clean_cache = __esm({
3105
3143
  "src/clean-cache.ts"() {
3106
3144
  "use strict";
3107
3145
  init_playwright();
3146
+ init_process_guard();
3147
+ installProcessTeardownGuards("CleanCache");
3108
3148
  isDirectRun = process.argv[1] && (process.argv[1].endsWith("clean-cache.ts") || process.argv[1].endsWith("clean-cache.js"));
3109
3149
  if (isDirectRun) {
3110
3150
  main().catch((err) => {
@@ -3120,6 +3160,7 @@ var playwright_exports = {};
3120
3160
  __export(playwright_exports, {
3121
3161
  assertAntiBotHeaders: () => assertAntiBotHeaders,
3122
3162
  buildChromiumLaunchArgs: () => buildChromiumLaunchArgs,
3163
+ cancelEmailOtpLogin: () => cancelEmailOtpLogin,
3123
3164
  captureQwenHeaders: () => captureQwenHeaders,
3124
3165
  classifyQwenAuthError: () => classifyQwenAuthError,
3125
3166
  cleanupOrphanProfiles: () => cleanupOrphanProfiles,
@@ -3160,8 +3201,10 @@ __export(playwright_exports, {
3160
3201
  removePlaywrightProfile: () => removePlaywrightProfile,
3161
3202
  saveStorageState: () => saveStorageState,
3162
3203
  schedulePlaywrightProfileReset: () => schedulePlaywrightProfileReset,
3204
+ startEmailOtpLogin: () => startEmailOtpLogin,
3163
3205
  unregisterPlaywrightAccountForTests: () => unregisterPlaywrightAccountForTests,
3164
3206
  validateAccountLogin: () => validateAccountLogin,
3207
+ verifyEmailOtpLogin: () => verifyEmailOtpLogin,
3165
3208
  withAccountPage: () => withAccountPage
3166
3209
  });
3167
3210
  import { chromium as chromium2 } from "patchright";
@@ -3329,15 +3372,18 @@ function loadStorageState(accountId) {
3329
3372
  return void 0;
3330
3373
  }
3331
3374
  }
3332
- async function saveStorageState(context, accountId, timeoutMs = 5e3) {
3375
+ async function saveStorageState(context, accountId, timeoutMs = 1e4) {
3333
3376
  try {
3334
3377
  const stateFile = getStorageStatePath(accountId);
3335
3378
  const dir = path5.dirname(stateFile);
3336
3379
  if (!fs8.existsSync(dir)) {
3337
3380
  fs8.mkdirSync(dir, { recursive: true });
3338
3381
  }
3382
+ const rawPromise = context.storageState({ path: stateFile }).catch(() => {
3383
+ return void 0;
3384
+ });
3339
3385
  await withTimeout(
3340
- context.storageState({ path: stateFile }),
3386
+ rawPromise,
3341
3387
  timeoutMs,
3342
3388
  `storageState timed out after ${timeoutMs}ms`
3343
3389
  );
@@ -3500,6 +3546,10 @@ async function clearVisibleChallenge(page) {
3500
3546
  "button[aria-label*='Close' i]",
3501
3547
  "button[aria-label*='Fechar' i]",
3502
3548
  ".index-module__whats-new-modal-close",
3549
+ ".age-confirmation-modal button",
3550
+ ".age-confirmation-modal .qwen-chat-v2-btn-black",
3551
+ "button:has-text('Continuar')",
3552
+ "button:has-text('Continue')",
3503
3553
  "button:has-text('Cancel')",
3504
3554
  "button:has-text('Cancelar')",
3505
3555
  "button:has-text('I understand')",
@@ -4694,6 +4744,292 @@ async function validateAccountLogin(rawAccount, headless = true, browserType = "
4694
4744
  release();
4695
4745
  }
4696
4746
  }
4747
+ async function startEmailOtpLogin(email, options = {}) {
4748
+ const normalizedEmail = email.trim().toLowerCase();
4749
+ if (!normalizedEmail || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedEmail)) {
4750
+ return { success: false, error: "Formato de e-mail inv\xE1lido" };
4751
+ }
4752
+ const { listAccounts: listAccounts2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4753
+ if (listAccounts2().some((a) => a.email.toLowerCase() === normalizedEmail)) {
4754
+ return {
4755
+ success: false,
4756
+ error: `Uma conta com o e-mail "${normalizedEmail}" j\xE1 est\xE1 cadastrada.`
4757
+ };
4758
+ }
4759
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4760
+ const mockSessionId = "mock-otp-" + Date.now();
4761
+ mockOtpSessions.set(mockSessionId, normalizedEmail);
4762
+ return { success: true, sessionId: mockSessionId };
4763
+ }
4764
+ const accountId = crypto3.randomUUID();
4765
+ const sessionId = accountId;
4766
+ const profilePath = getAccountProfilePath(accountId);
4767
+ const fingerprint = getFingerprintProfile(accountId);
4768
+ const browserType = options.browserType ?? config.playwright.browser;
4769
+ const { engine, channel } = resolveBrowserEngine(browserType);
4770
+ const headless = options.headless ?? config.playwright.headless;
4771
+ const launchOptions = {
4772
+ headless,
4773
+ channel,
4774
+ userAgent: fingerprint.userAgent,
4775
+ locale: fingerprint.locale,
4776
+ timezoneId: fingerprint.timezoneId,
4777
+ viewport: fingerprint.viewport,
4778
+ screen: fingerprint.viewport,
4779
+ deviceScaleFactor: 1,
4780
+ isMobile: false,
4781
+ hasTouch: false,
4782
+ colorScheme: "light",
4783
+ extraHTTPHeaders: {
4784
+ "sec-ch-ua": fingerprint.secChUa,
4785
+ "sec-ch-ua-mobile": "?0",
4786
+ "sec-ch-ua-platform": '"Windows"'
4787
+ },
4788
+ ignoreDefaultArgs: ["--enable-automation", "--enable-blink-features"],
4789
+ args: buildChromiumLaunchArgs(fingerprint.viewport)
4790
+ };
4791
+ let acctContext;
4792
+ try {
4793
+ acctContext = await withTimeout(
4794
+ engine.launchPersistentContext(profilePath, launchOptions),
4795
+ 3e4,
4796
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4797
+ );
4798
+ } catch (launchErr) {
4799
+ if (launchErr?.message?.includes("Executable doesn't exist")) {
4800
+ autoInstallPlaywrightChromium();
4801
+ acctContext = await withTimeout(
4802
+ engine.launchPersistentContext(profilePath, launchOptions),
4803
+ 3e4,
4804
+ `O navegador n\xE3o iniciou em 30s para a conta ${maskEmail(normalizedEmail)}.`
4805
+ );
4806
+ } else {
4807
+ return { success: false, error: launchErr?.message || String(launchErr) };
4808
+ }
4809
+ }
4810
+ try {
4811
+ await acctContext.addInitScript(getStealthScript(fingerprint));
4812
+ const existingPages = acctContext.pages().filter((p) => !p.isClosed());
4813
+ const acctPage = existingPages.find((p) => p.url().startsWith(qwenOrigin())) ?? existingPages[0] ?? await acctContext.newPage();
4814
+ await acctPage.goto(qwenUrl("/auth?mode=register"), {
4815
+ waitUntil: "domcontentloaded",
4816
+ timeout: Math.min(config.timeouts.navigation, 2e4)
4817
+ }).catch(() => {
4818
+ });
4819
+ await sleep2(1e3);
4820
+ await clearVisibleChallenge(acctPage);
4821
+ const requestId = crypto3.randomUUID();
4822
+ const evalRes = await acctPage.evaluate(
4823
+ async ({ email: email2, requestId: requestId2, version, timezone }) => {
4824
+ try {
4825
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/request", {
4826
+ method: "POST",
4827
+ credentials: "include",
4828
+ headers: {
4829
+ accept: "application/json, text/plain, */*",
4830
+ "content-type": "application/json",
4831
+ source: "web",
4832
+ version,
4833
+ "x-request-origin": "https://chat.qwen.ai",
4834
+ timezone,
4835
+ "x-request-id": requestId2
4836
+ },
4837
+ body: JSON.stringify({
4838
+ identifier: email2,
4839
+ accept_terms: true,
4840
+ language: "pt-BR"
4841
+ })
4842
+ });
4843
+ const json = await response.json().catch(() => null);
4844
+ return { ok: response.ok, status: response.status, data: json };
4845
+ } catch (e) {
4846
+ return { ok: false, error: e.message };
4847
+ }
4848
+ },
4849
+ {
4850
+ email: normalizedEmail,
4851
+ requestId,
4852
+ version: getQwenWebVersion() || "0.3.12",
4853
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4854
+ }
4855
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4856
+ if (!evalRes.ok || evalRes.data?.success !== true) {
4857
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "Falha ao solicitar c\xF3digo de verifica\xE7\xE3o";
4858
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4859
+ });
4860
+ cleanupPlaywrightAccountState(accountId);
4861
+ return { success: false, error: errorMsg };
4862
+ }
4863
+ const timer = setTimeout(async () => {
4864
+ activeOtpSessions.delete(sessionId);
4865
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4866
+ });
4867
+ cleanupPlaywrightAccountState(accountId);
4868
+ }, 5 * 60 * 1e3);
4869
+ timer.unref?.();
4870
+ activeOtpSessions.set(sessionId, {
4871
+ sessionId,
4872
+ accountId,
4873
+ email: normalizedEmail,
4874
+ context: acctContext,
4875
+ page: acctPage,
4876
+ fingerprint,
4877
+ createdAt: Date.now(),
4878
+ timer
4879
+ });
4880
+ return { success: true, sessionId };
4881
+ } catch (err) {
4882
+ await closePlaywrightContextBestEffort(accountId, acctContext).catch(() => {
4883
+ });
4884
+ cleanupPlaywrightAccountState(accountId);
4885
+ return { success: false, error: err?.message || String(err) };
4886
+ }
4887
+ }
4888
+ async function verifyEmailOtpLogin(sessionId, code) {
4889
+ const cleanCode = code.trim();
4890
+ if (!cleanCode) {
4891
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o \xE9 obrigat\xF3rio" };
4892
+ }
4893
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
4894
+ const mockEmail = mockOtpSessions.get(sessionId);
4895
+ if (!mockEmail) {
4896
+ return { success: false, error: "Sess\xE3o OTP inv\xE1lida ou expirada" };
4897
+ }
4898
+ if (cleanCode === "000000") {
4899
+ return { success: false, error: "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado" };
4900
+ }
4901
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
4902
+ const mockAccId = "mock-otp-acc-" + Date.now();
4903
+ const newAccount = addAccount2(mockEmail, "", mockAccId);
4904
+ mockOtpSessions.delete(sessionId);
4905
+ return { success: true, account: newAccount };
4906
+ }
4907
+ const session = activeOtpSessions.get(sessionId);
4908
+ if (!session) {
4909
+ return { success: false, error: "Sess\xE3o OTP expirada ou n\xE3o encontrada. Solicite um novo c\xF3digo." };
4910
+ }
4911
+ if (session.page.isClosed()) {
4912
+ activeOtpSessions.delete(sessionId);
4913
+ return { success: false, error: "Navegador foi fechado. Solicite um novo c\xF3digo." };
4914
+ }
4915
+ const requestId = crypto3.randomUUID();
4916
+ const evalRes = await session.page.evaluate(
4917
+ async ({ email, code: code2, requestId: requestId2, version, timezone }) => {
4918
+ try {
4919
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/otp/email/verify", {
4920
+ method: "POST",
4921
+ credentials: "include",
4922
+ headers: {
4923
+ accept: "application/json, text/plain, */*",
4924
+ "content-type": "application/json",
4925
+ source: "web",
4926
+ version,
4927
+ "x-request-origin": "https://chat.qwen.ai",
4928
+ timezone,
4929
+ "x-request-id": requestId2
4930
+ },
4931
+ body: JSON.stringify({
4932
+ identifier: email,
4933
+ code: code2,
4934
+ name: null
4935
+ })
4936
+ });
4937
+ const json = await response.json().catch(() => null);
4938
+ return { ok: response.ok, status: response.status, data: json };
4939
+ } catch (e) {
4940
+ return { ok: false, error: e.message };
4941
+ }
4942
+ },
4943
+ {
4944
+ email: session.email,
4945
+ code: cleanCode,
4946
+ requestId,
4947
+ version: getQwenWebVersion() || "0.3.12",
4948
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4949
+ }
4950
+ ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4951
+ if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
4952
+ const errorMsg = evalRes.data?.data?.details || evalRes.data?.data?.message || evalRes.data?.message || evalRes.error || "C\xF3digo de verifica\xE7\xE3o incorreto ou expirado";
4953
+ return { success: false, error: errorMsg };
4954
+ }
4955
+ const resData = evalRes.data.data;
4956
+ const accessToken = resData.access_token || resData.token;
4957
+ const refreshToken = resData.refresh_token;
4958
+ const userId = resData.id;
4959
+ try {
4960
+ await session.page.evaluate(({ token, rTok }) => {
4961
+ try {
4962
+ localStorage.setItem("token", token);
4963
+ localStorage.setItem("access_token", token);
4964
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4965
+ document.cookie = `token=${encodeURIComponent(token)}; path=/; domain=.qwen.ai; max-age=31536000`;
4966
+ } catch {
4967
+ }
4968
+ }, { token: accessToken, rTok: refreshToken });
4969
+ await session.context.addCookies([
4970
+ {
4971
+ name: "token",
4972
+ value: accessToken,
4973
+ domain: ".qwen.ai",
4974
+ path: "/",
4975
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
4976
+ httpOnly: false,
4977
+ secure: true,
4978
+ sameSite: "Lax"
4979
+ }
4980
+ ]);
4981
+ await session.page.goto(qwenUrl("/"), {
4982
+ waitUntil: "domcontentloaded",
4983
+ timeout: Math.min(config.timeouts.navigation, 15e3)
4984
+ }).catch(() => {
4985
+ });
4986
+ await sleep2(1e3);
4987
+ await saveStorageState(session.context, session.accountId);
4988
+ const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4989
+ const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
4990
+ const exp = parseJwtExpiry2(accessToken);
4991
+ saveAuthSession2(session.accountId, {
4992
+ cookie: `token=${accessToken}`,
4993
+ userAgent: session.fingerprint.userAgent,
4994
+ bxV: "2.5.37",
4995
+ version: getQwenWebVersion() || "0.3.12",
4996
+ userId: userId || void 0,
4997
+ tokenExpiresAt: exp || void 0,
4998
+ refreshToken: refreshToken || void 0,
4999
+ capturedAt: Date.now()
5000
+ });
5001
+ const { addAccount: addAccount2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5002
+ const newAccount = addAccount2(session.email, "", session.accountId);
5003
+ clearTimeout(session.timer);
5004
+ activeOtpSessions.delete(sessionId);
5005
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5006
+ });
5007
+ cleanupPlaywrightAccountState(session.accountId);
5008
+ return { success: true, account: newAccount };
5009
+ } catch (err) {
5010
+ clearTimeout(session.timer);
5011
+ activeOtpSessions.delete(sessionId);
5012
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5013
+ });
5014
+ cleanupPlaywrightAccountState(session.accountId);
5015
+ return { success: false, error: err?.message || String(err) };
5016
+ }
5017
+ }
5018
+ async function cancelEmailOtpLogin(sessionId) {
5019
+ if (process.env.TEST_MOCK_QWEN_AUTH === "true" && process.env.NODE_ENV !== "production") {
5020
+ mockOtpSessions.delete(sessionId);
5021
+ return;
5022
+ }
5023
+ const session = activeOtpSessions.get(sessionId);
5024
+ if (!session) return;
5025
+ clearTimeout(session.timer);
5026
+ activeOtpSessions.delete(sessionId);
5027
+ await closePlaywrightContextBestEffort(session.accountId, session.context).catch(() => {
5028
+ });
5029
+ cleanupPlaywrightAccountState(session.accountId);
5030
+ const { wipeAccountSessionFiles: wipeAccountSessionFiles2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5031
+ wipeAccountSessionFiles2(session.accountId);
5032
+ }
4697
5033
  function classifyQwenAuthError(code, details) {
4698
5034
  const c = (code || "").trim().toLowerCase();
4699
5035
  const d = (details || "").trim().toLowerCase();
@@ -6062,7 +6398,7 @@ async function withAccountPage(accountId, fn, timeoutMs = ACCOUNT_PAGE_OPERATION
6062
6398
  return result;
6063
6399
  } catch (error) {
6064
6400
  const message = getErrorMessage(error);
6065
- if (recoverOnTimeout && message.includes("Playwright page operation timed out")) {
6401
+ if (recoverOnTimeout && message.includes("Playwright page operation timed out") && (!page || page.isClosed())) {
6066
6402
  console.warn(
6067
6403
  `\u23F1\uFE0F [Playwright] Resetting account context after a stuck page operation: ${accountId}`
6068
6404
  );
@@ -6611,7 +6947,7 @@ async function getTokenDiagnostics(accountId) {
6611
6947
  }
6612
6948
  };
6613
6949
  }
6614
- 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;
6950
+ 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;
6615
6951
  var init_playwright = __esm({
6616
6952
  "src/services/playwright.ts"() {
6617
6953
  "use strict";
@@ -6656,11 +6992,13 @@ var init_playwright = __esm({
6656
6992
  PLAYWRIGHT_MUTEX_WAIT_MS = 6e4;
6657
6993
  ACCOUNT_PAGE_OPERATION_TIMEOUT_MS = config.timeouts.page;
6658
6994
  SESSION_PROBE_NAVIGATION_TIMEOUT_MS = 15e3;
6659
- HEADER_CAPTURE_TRIGGER_GRACE_MS = 15e3;
6660
- FIRST_TRIGGER_GRACE_MS = 8e3;
6995
+ HEADER_CAPTURE_TRIGGER_GRACE_MS = 2e4;
6996
+ FIRST_TRIGGER_GRACE_MS = 12e3;
6661
6997
  HEADER_CAPTURE_TRIGGER_ATTEMPTS = 3;
6662
6998
  CHAT_INPUT_APPEAR_TIMEOUT_MS = 15e3;
6663
6999
  CHAT_INPUT_ACTION_TIMEOUT_MS = 1e4;
7000
+ activeOtpSessions = /* @__PURE__ */ new Map();
7001
+ mockOtpSessions = /* @__PURE__ */ new Map();
6664
7002
  PROFILE_RESET_TIMEOUT_MS = Math.max(9e4, config.timeouts.headers);
6665
7003
  setWafContextResetListener((accountId) => {
6666
7004
  if (!accountPages.has(accountId)) return;
@@ -7672,6 +8010,15 @@ var init_helpers = __esm({
7672
8010
  }
7673
8011
  });
7674
8012
 
8013
+ // src/utils/overload-detector.ts
8014
+ var OVERLOAD_COOLDOWN_MS;
8015
+ var init_overload_detector = __esm({
8016
+ "src/utils/overload-detector.ts"() {
8017
+ "use strict";
8018
+ OVERLOAD_COOLDOWN_MS = 5 * 60 * 1e3;
8019
+ }
8020
+ });
8021
+
7675
8022
  // src/routes/chat/retry-policy.ts
7676
8023
  var init_retry_policy = __esm({
7677
8024
  "src/routes/chat/retry-policy.ts"() {
@@ -7682,6 +8029,7 @@ var init_retry_policy = __esm({
7682
8029
  init_qwen();
7683
8030
  init_errors();
7684
8031
  init_helpers();
8032
+ init_overload_detector();
7685
8033
  init_account_manager();
7686
8034
  }
7687
8035
  });
@@ -8645,7 +8993,8 @@ async function requestQwenPersonalizationInBrowser(accountId, method, path6, hea
8645
8993
  payload === void 0 ? void 0 : JSON.stringify(payload),
8646
8994
  {
8647
8995
  settingsPage: true,
8648
- referrer: qwenUrl("/settings/personalization")
8996
+ referrer: qwenUrl("/settings/personalization"),
8997
+ noMutexRecovery: true
8649
8998
  }
8650
8999
  );
8651
9000
  const { raw, json } = await readJsonTextResponse(response);
@@ -9146,8 +9495,9 @@ async function syncQwenRequestPersonalizationInternal(instruction, accountId, me
9146
9495
  const returnedInstruction = json?.data?.personalization?.instruction;
9147
9496
  const returned = textSize(returnedInstruction);
9148
9497
  let stored = { chars: null, bytes: null, hash: null };
9498
+ const matchReturned = isEmptyInstruction && (!returnedInstruction || returned.chars === 0) || returned.hash !== null && returned.hash === sent.hash;
9149
9499
  let verifyData = null;
9150
- if (config.qwen.personalizationVerifyGet) {
9500
+ if (config.qwen.personalizationVerifyGet && !matchReturned) {
9151
9501
  const { json: verifyJson } = await requestQwenPersonalizationInBrowser(
9152
9502
  accountId,
9153
9503
  "GET",
@@ -9157,7 +9507,6 @@ async function syncQwenRequestPersonalizationInternal(instruction, accountId, me
9157
9507
  verifyData = verifyJson?.data?.personalization;
9158
9508
  stored = textSize(verifyData?.instruction);
9159
9509
  }
9160
- const matchReturned = isEmptyInstruction && (!returnedInstruction || returned.chars === 0) || returned.hash !== null && returned.hash === sent.hash;
9161
9510
  const matchStored = stored.hash === null ? null : isEmptyInstruction && (!verifyData?.instruction || stored.chars === 0) || stored.hash === sent.hash;
9162
9511
  const applied = matchReturned || matchStored === true;
9163
9512
  if (syncHash && applied) {
@@ -9531,6 +9880,17 @@ async function fetchQwenModels(accountId) {
9531
9880
  );
9532
9881
  return cached.models;
9533
9882
  }
9883
+ if (accountId && !isAuthMockEnabled() && !isPlaywrightInitialized(accountId)) {
9884
+ for (const [, entry] of modelsCache.entries()) {
9885
+ if (entry && now - entry.fetchedAt < MODEL_CACHE_TTL_MS * 4) {
9886
+ syncModelMetadata(
9887
+ entry.models,
9888
+ accountId
9889
+ );
9890
+ return entry.models;
9891
+ }
9892
+ }
9893
+ }
9534
9894
  if (accountId && !isAuthMockEnabled() && isAccountBusy(accountId)) {
9535
9895
  try {
9536
9896
  const result = await withAccountPage(
@@ -10454,6 +10814,7 @@ var init_qwen = __esm({
10454
10814
  });
10455
10815
 
10456
10816
  // src/delete-chats.ts
10817
+ init_process_guard();
10457
10818
  import "dotenv/config";
10458
10819
 
10459
10820
  // src/services/chat-cleanup.ts
@@ -10517,6 +10878,12 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
10517
10878
  `\u274C [DeleteChats] [${currentIdx}/${totalCount}] Falha ao apagar conversas de ${maskEmail(account.email)}:`,
10518
10879
  error instanceof Error ? error.message : String(error)
10519
10880
  );
10881
+ } finally {
10882
+ if (!keepBrowserOpen) {
10883
+ const { closePlaywrightForAccount: closePlaywrightForAccount2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10884
+ await closePlaywrightForAccount2(account.id).catch(() => {
10885
+ });
10886
+ }
10520
10887
  }
10521
10888
  }
10522
10889
  } finally {
@@ -10537,6 +10904,7 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
10537
10904
  }
10538
10905
 
10539
10906
  // src/delete-chats.ts
10907
+ installProcessTeardownGuards("DeleteChats");
10540
10908
  async function run() {
10541
10909
  console.log("\u{1F5D1}\uFE0F [DeleteChats] Using Playwright sessions");
10542
10910
  const result = await deleteChatsForConfiguredAccounts();