qwenproxy-cli 1.4.1 → 1.4.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.
@@ -3852,8 +3852,9 @@ async function loginToQwen(accountId, email, password) {
3852
3852
  }
3853
3853
  if (attempt < maxAttempts) {
3854
3854
  const backoffMs = attempt * 5e3;
3855
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
3855
3856
  console.warn(
3856
- `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha tempor\xE1ria"}), retrying in ${backoffMs / 1e3}s`
3857
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
3857
3858
  );
3858
3859
  await sleep2(backoffMs);
3859
3860
  }
@@ -4035,10 +4036,16 @@ async function loginViaUi(page, email, password) {
4035
4036
  );
4036
4037
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4037
4038
  }
4039
+ await clearVisibleChallenge(page);
4040
+ const findPasswordModeButton = () => {
4041
+ return page.locator('button, [role="button"], a, div').filter({
4042
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
4043
+ }).first();
4044
+ };
4038
4045
  try {
4039
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4040
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4041
- await pwdModeBtn.click().catch(() => {
4046
+ const pwdModeBtn = findPasswordModeButton();
4047
+ if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4048
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4042
4049
  });
4043
4050
  await sleep2(500);
4044
4051
  }
@@ -4047,9 +4054,9 @@ async function loginViaUi(page, email, password) {
4047
4054
  await page.fill(emailSelector, email);
4048
4055
  await sleep2(300);
4049
4056
  try {
4050
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4051
- if (await pwdModeBtn.isVisible({ timeout: 1e3 }).catch(() => false)) {
4052
- await pwdModeBtn.click().catch(() => {
4057
+ const pwdModeBtn = findPasswordModeButton();
4058
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4059
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4053
4060
  });
4054
4061
  await sleep2(500);
4055
4062
  }
@@ -4074,7 +4081,7 @@ async function loginViaUi(page, email, password) {
4074
4081
  }
4075
4082
  await page.fill(passwordSelector, password);
4076
4083
  await sleep2(500);
4077
- const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
4084
+ 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")';
4078
4085
  const submitButton = page.locator(submitSelector).first();
4079
4086
  try {
4080
4087
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -4084,49 +4091,63 @@ async function loginViaUi(page, email, password) {
4084
4091
  } catch {
4085
4092
  await page.keyboard.press("Enter");
4086
4093
  }
4087
- await sleep2(3e3);
4088
- await solveBaxiaCaptcha(page, {
4089
- waitForMs: 2e3,
4090
- maxAttempts: config.captcha.maxAttempts,
4091
- retryDelayMs: config.captcha.retryDelayMs
4092
- }).catch(() => {
4093
- });
4094
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
4095
- const codeInput = page.locator(codeSelector).first();
4096
- if (await codeInput.isVisible().catch(() => false)) {
4097
- return {
4098
- success: false,
4099
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4100
- };
4101
- }
4102
- const errorSelector = [
4103
- ".qwen-chat-v2-toast-text",
4104
- ".qwen-chat-v2-toast-content",
4105
- ".ant-form-item-explain-error",
4106
- ".ant-message-error",
4107
- ".ant-message-notice",
4108
- '[role="alert"]',
4109
- ".qwenchat-auth-error",
4110
- ".auth-error-message"
4111
- ].join(", ");
4112
- const errorEl = page.locator(errorSelector).first();
4113
- if (await errorEl.isVisible().catch(() => false)) {
4114
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
4115
- if (errorText) {
4116
- const classified = classifyQwenAuthError(void 0, errorText);
4094
+ const postSubmitDeadline = Date.now() + 15e3;
4095
+ while (Date.now() < postSubmitDeadline) {
4096
+ if (page.isClosed()) break;
4097
+ if (config.captcha.enabled) {
4098
+ await solveBaxiaCaptcha(page, {
4099
+ waitForMs: 1500,
4100
+ maxAttempts: config.captcha.maxAttempts,
4101
+ retryDelayMs: config.captcha.retryDelayMs
4102
+ }).catch(() => {
4103
+ });
4104
+ }
4105
+ const errorSelector = [
4106
+ ".qwen-chat-v2-toast-text",
4107
+ ".qwen-chat-v2-toast-content",
4108
+ ".ant-form-item-explain-error",
4109
+ ".ant-message-error",
4110
+ ".ant-message-notice",
4111
+ '[role="alert"]',
4112
+ ".qwenchat-auth-error",
4113
+ ".auth-error-message"
4114
+ ].join(", ");
4115
+ const errorEl = page.locator(errorSelector).first();
4116
+ if (await errorEl.isVisible().catch(() => false)) {
4117
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
4118
+ if (errorText) {
4119
+ const classified = classifyQwenAuthError(void 0, errorText);
4120
+ return {
4121
+ success: false,
4122
+ permanentFailure: classified.isPermanent,
4123
+ reason: `Formul\xE1rio: ${classified.reason}`
4124
+ };
4125
+ }
4126
+ }
4127
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
4128
+ const codeInput = page.locator(codeSelector).first();
4129
+ if (await codeInput.isVisible().catch(() => false)) {
4117
4130
  return {
4118
4131
  success: false,
4119
- permanentFailure: classified.isPermanent,
4120
- reason: `Formul\xE1rio: ${classified.reason}`
4132
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4121
4133
  };
4122
4134
  }
4135
+ try {
4136
+ const liveCookies = await page.context().cookies();
4137
+ if (liveCookies.some((c) => c.name === "token")) {
4138
+ return { success: true };
4139
+ }
4140
+ } catch {
4141
+ }
4142
+ const currentUrl = page.url();
4143
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
4144
+ if (await isPageLoggedIn(page, 3e3)) {
4145
+ return { success: true };
4146
+ }
4147
+ }
4148
+ await sleep2(1e3);
4123
4149
  }
4124
- const isLoggedIn = await isPageLoggedIn(page);
4125
- if (isLoggedIn) {
4126
- await page.goto(qwenUrl("/"), {
4127
- waitUntil: "domcontentloaded",
4128
- timeout: config.timeouts.navigation
4129
- });
4150
+ if (await isPageLoggedIn(page, 3e3)) {
4130
4151
  return { success: true };
4131
4152
  }
4132
4153
  return {
@@ -4727,8 +4727,9 @@ async function loginToQwen(accountId, email, password) {
4727
4727
  }
4728
4728
  if (attempt < maxAttempts) {
4729
4729
  const backoffMs = attempt * 5e3;
4730
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
4730
4731
  console.warn(
4731
- `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha tempor\xE1ria"}), retrying in ${backoffMs / 1e3}s`
4732
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
4732
4733
  );
4733
4734
  await sleep2(backoffMs);
4734
4735
  }
@@ -4910,10 +4911,16 @@ async function loginViaUi(page, email, password) {
4910
4911
  );
4911
4912
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4912
4913
  }
4914
+ await clearVisibleChallenge(page);
4915
+ const findPasswordModeButton = () => {
4916
+ return page.locator('button, [role="button"], a, div').filter({
4917
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
4918
+ }).first();
4919
+ };
4913
4920
  try {
4914
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4915
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4916
- await pwdModeBtn.click().catch(() => {
4921
+ const pwdModeBtn = findPasswordModeButton();
4922
+ if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4923
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4917
4924
  });
4918
4925
  await sleep2(500);
4919
4926
  }
@@ -4922,9 +4929,9 @@ async function loginViaUi(page, email, password) {
4922
4929
  await page.fill(emailSelector, email);
4923
4930
  await sleep2(300);
4924
4931
  try {
4925
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4926
- if (await pwdModeBtn.isVisible({ timeout: 1e3 }).catch(() => false)) {
4927
- await pwdModeBtn.click().catch(() => {
4932
+ const pwdModeBtn = findPasswordModeButton();
4933
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4934
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4928
4935
  });
4929
4936
  await sleep2(500);
4930
4937
  }
@@ -4949,7 +4956,7 @@ async function loginViaUi(page, email, password) {
4949
4956
  }
4950
4957
  await page.fill(passwordSelector, password);
4951
4958
  await sleep2(500);
4952
- const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
4959
+ 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")';
4953
4960
  const submitButton = page.locator(submitSelector).first();
4954
4961
  try {
4955
4962
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -4959,49 +4966,63 @@ async function loginViaUi(page, email, password) {
4959
4966
  } catch {
4960
4967
  await page.keyboard.press("Enter");
4961
4968
  }
4962
- await sleep2(3e3);
4963
- await solveBaxiaCaptcha(page, {
4964
- waitForMs: 2e3,
4965
- maxAttempts: config.captcha.maxAttempts,
4966
- retryDelayMs: config.captcha.retryDelayMs
4967
- }).catch(() => {
4968
- });
4969
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
4970
- const codeInput = page.locator(codeSelector).first();
4971
- if (await codeInput.isVisible().catch(() => false)) {
4972
- return {
4973
- success: false,
4974
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4975
- };
4976
- }
4977
- const errorSelector = [
4978
- ".qwen-chat-v2-toast-text",
4979
- ".qwen-chat-v2-toast-content",
4980
- ".ant-form-item-explain-error",
4981
- ".ant-message-error",
4982
- ".ant-message-notice",
4983
- '[role="alert"]',
4984
- ".qwenchat-auth-error",
4985
- ".auth-error-message"
4986
- ].join(", ");
4987
- const errorEl = page.locator(errorSelector).first();
4988
- if (await errorEl.isVisible().catch(() => false)) {
4989
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
4990
- if (errorText) {
4991
- const classified = classifyQwenAuthError(void 0, errorText);
4969
+ const postSubmitDeadline = Date.now() + 15e3;
4970
+ while (Date.now() < postSubmitDeadline) {
4971
+ if (page.isClosed()) break;
4972
+ if (config.captcha.enabled) {
4973
+ await solveBaxiaCaptcha(page, {
4974
+ waitForMs: 1500,
4975
+ maxAttempts: config.captcha.maxAttempts,
4976
+ retryDelayMs: config.captcha.retryDelayMs
4977
+ }).catch(() => {
4978
+ });
4979
+ }
4980
+ const errorSelector = [
4981
+ ".qwen-chat-v2-toast-text",
4982
+ ".qwen-chat-v2-toast-content",
4983
+ ".ant-form-item-explain-error",
4984
+ ".ant-message-error",
4985
+ ".ant-message-notice",
4986
+ '[role="alert"]',
4987
+ ".qwenchat-auth-error",
4988
+ ".auth-error-message"
4989
+ ].join(", ");
4990
+ const errorEl = page.locator(errorSelector).first();
4991
+ if (await errorEl.isVisible().catch(() => false)) {
4992
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
4993
+ if (errorText) {
4994
+ const classified = classifyQwenAuthError(void 0, errorText);
4995
+ return {
4996
+ success: false,
4997
+ permanentFailure: classified.isPermanent,
4998
+ reason: `Formul\xE1rio: ${classified.reason}`
4999
+ };
5000
+ }
5001
+ }
5002
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5003
+ const codeInput = page.locator(codeSelector).first();
5004
+ if (await codeInput.isVisible().catch(() => false)) {
4992
5005
  return {
4993
5006
  success: false,
4994
- permanentFailure: classified.isPermanent,
4995
- reason: `Formul\xE1rio: ${classified.reason}`
5007
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4996
5008
  };
4997
5009
  }
5010
+ try {
5011
+ const liveCookies = await page.context().cookies();
5012
+ if (liveCookies.some((c) => c.name === "token")) {
5013
+ return { success: true };
5014
+ }
5015
+ } catch {
5016
+ }
5017
+ const currentUrl = page.url();
5018
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
5019
+ if (await isPageLoggedIn(page, 3e3)) {
5020
+ return { success: true };
5021
+ }
5022
+ }
5023
+ await sleep2(1e3);
4998
5024
  }
4999
- const isLoggedIn = await isPageLoggedIn(page);
5000
- if (isLoggedIn) {
5001
- await page.goto(qwenUrl("/"), {
5002
- waitUntil: "domcontentloaded",
5003
- timeout: config.timeouts.navigation
5004
- });
5025
+ if (await isPageLoggedIn(page, 3e3)) {
5005
5026
  return { success: true };
5006
5027
  }
5007
5028
  return {
package/dist/index.js CHANGED
@@ -5944,8 +5944,9 @@ async function loginToQwen(accountId, email, password) {
5944
5944
  }
5945
5945
  if (attempt < maxAttempts) {
5946
5946
  const backoffMs = attempt * 5e3;
5947
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
5947
5948
  console.warn(
5948
- `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha tempor\xE1ria"}), retrying in ${backoffMs / 1e3}s`
5949
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
5949
5950
  );
5950
5951
  await sleep2(backoffMs);
5951
5952
  }
@@ -6127,10 +6128,16 @@ async function loginViaUi(page, email, password) {
6127
6128
  );
6128
6129
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
6129
6130
  }
6131
+ await clearVisibleChallenge(page);
6132
+ const findPasswordModeButton = () => {
6133
+ return page.locator('button, [role="button"], a, div').filter({
6134
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
6135
+ }).first();
6136
+ };
6130
6137
  try {
6131
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
6132
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
6133
- await pwdModeBtn.click().catch(() => {
6138
+ const pwdModeBtn = findPasswordModeButton();
6139
+ if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
6140
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
6134
6141
  });
6135
6142
  await sleep2(500);
6136
6143
  }
@@ -6139,9 +6146,9 @@ async function loginViaUi(page, email, password) {
6139
6146
  await page.fill(emailSelector, email);
6140
6147
  await sleep2(300);
6141
6148
  try {
6142
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
6143
- if (await pwdModeBtn.isVisible({ timeout: 1e3 }).catch(() => false)) {
6144
- await pwdModeBtn.click().catch(() => {
6149
+ const pwdModeBtn = findPasswordModeButton();
6150
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
6151
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
6145
6152
  });
6146
6153
  await sleep2(500);
6147
6154
  }
@@ -6166,7 +6173,7 @@ async function loginViaUi(page, email, password) {
6166
6173
  }
6167
6174
  await page.fill(passwordSelector, password);
6168
6175
  await sleep2(500);
6169
- const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
6176
+ 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")';
6170
6177
  const submitButton = page.locator(submitSelector).first();
6171
6178
  try {
6172
6179
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -6176,49 +6183,63 @@ async function loginViaUi(page, email, password) {
6176
6183
  } catch {
6177
6184
  await page.keyboard.press("Enter");
6178
6185
  }
6179
- await sleep2(3e3);
6180
- await solveBaxiaCaptcha(page, {
6181
- waitForMs: 2e3,
6182
- maxAttempts: config.captcha.maxAttempts,
6183
- retryDelayMs: config.captcha.retryDelayMs
6184
- }).catch(() => {
6185
- });
6186
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
6187
- const codeInput = page.locator(codeSelector).first();
6188
- if (await codeInput.isVisible().catch(() => false)) {
6189
- return {
6190
- success: false,
6191
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
6192
- };
6193
- }
6194
- const errorSelector = [
6195
- ".qwen-chat-v2-toast-text",
6196
- ".qwen-chat-v2-toast-content",
6197
- ".ant-form-item-explain-error",
6198
- ".ant-message-error",
6199
- ".ant-message-notice",
6200
- '[role="alert"]',
6201
- ".qwenchat-auth-error",
6202
- ".auth-error-message"
6203
- ].join(", ");
6204
- const errorEl = page.locator(errorSelector).first();
6205
- if (await errorEl.isVisible().catch(() => false)) {
6206
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
6207
- if (errorText) {
6208
- const classified = classifyQwenAuthError(void 0, errorText);
6186
+ const postSubmitDeadline = Date.now() + 15e3;
6187
+ while (Date.now() < postSubmitDeadline) {
6188
+ if (page.isClosed()) break;
6189
+ if (config.captcha.enabled) {
6190
+ await solveBaxiaCaptcha(page, {
6191
+ waitForMs: 1500,
6192
+ maxAttempts: config.captcha.maxAttempts,
6193
+ retryDelayMs: config.captcha.retryDelayMs
6194
+ }).catch(() => {
6195
+ });
6196
+ }
6197
+ const errorSelector = [
6198
+ ".qwen-chat-v2-toast-text",
6199
+ ".qwen-chat-v2-toast-content",
6200
+ ".ant-form-item-explain-error",
6201
+ ".ant-message-error",
6202
+ ".ant-message-notice",
6203
+ '[role="alert"]',
6204
+ ".qwenchat-auth-error",
6205
+ ".auth-error-message"
6206
+ ].join(", ");
6207
+ const errorEl = page.locator(errorSelector).first();
6208
+ if (await errorEl.isVisible().catch(() => false)) {
6209
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
6210
+ if (errorText) {
6211
+ const classified = classifyQwenAuthError(void 0, errorText);
6212
+ return {
6213
+ success: false,
6214
+ permanentFailure: classified.isPermanent,
6215
+ reason: `Formul\xE1rio: ${classified.reason}`
6216
+ };
6217
+ }
6218
+ }
6219
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
6220
+ const codeInput = page.locator(codeSelector).first();
6221
+ if (await codeInput.isVisible().catch(() => false)) {
6209
6222
  return {
6210
6223
  success: false,
6211
- permanentFailure: classified.isPermanent,
6212
- reason: `Formul\xE1rio: ${classified.reason}`
6224
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
6213
6225
  };
6214
6226
  }
6227
+ try {
6228
+ const liveCookies = await page.context().cookies();
6229
+ if (liveCookies.some((c) => c.name === "token")) {
6230
+ return { success: true };
6231
+ }
6232
+ } catch {
6233
+ }
6234
+ const currentUrl = page.url();
6235
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
6236
+ if (await isPageLoggedIn(page, 3e3)) {
6237
+ return { success: true };
6238
+ }
6239
+ }
6240
+ await sleep2(1e3);
6215
6241
  }
6216
- const isLoggedIn = await isPageLoggedIn(page);
6217
- if (isLoggedIn) {
6218
- await page.goto(qwenUrl("/"), {
6219
- waitUntil: "domcontentloaded",
6220
- timeout: config.timeouts.navigation
6221
- });
6242
+ if (await isPageLoggedIn(page, 3e3)) {
6222
6243
  return { success: true };
6223
6244
  }
6224
6245
  return {
@@ -25492,7 +25513,7 @@ function getAppVersion() {
25492
25513
  }
25493
25514
  } catch {
25494
25515
  }
25495
- cachedVersion = "v1.4.1";
25516
+ cachedVersion = "v1.4.3";
25496
25517
  return cachedVersion;
25497
25518
  }
25498
25519
  var cachedVersion;
package/dist/login.js CHANGED
@@ -4812,8 +4812,9 @@ async function loginToQwen(accountId, email, password) {
4812
4812
  }
4813
4813
  if (attempt < maxAttempts) {
4814
4814
  const backoffMs = attempt * 5e3;
4815
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
4815
4816
  console.warn(
4816
- `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha tempor\xE1ria"}), retrying in ${backoffMs / 1e3}s`
4817
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
4817
4818
  );
4818
4819
  await sleep2(backoffMs);
4819
4820
  }
@@ -4995,10 +4996,16 @@ async function loginViaUi(page, email, password) {
4995
4996
  );
4996
4997
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4997
4998
  }
4999
+ await clearVisibleChallenge(page);
5000
+ const findPasswordModeButton = () => {
5001
+ return page.locator('button, [role="button"], a, div').filter({
5002
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
5003
+ }).first();
5004
+ };
4998
5005
  try {
4999
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
5000
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
5001
- await pwdModeBtn.click().catch(() => {
5006
+ const pwdModeBtn = findPasswordModeButton();
5007
+ if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
5008
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
5002
5009
  });
5003
5010
  await sleep2(500);
5004
5011
  }
@@ -5007,9 +5014,9 @@ async function loginViaUi(page, email, password) {
5007
5014
  await page.fill(emailSelector, email);
5008
5015
  await sleep2(300);
5009
5016
  try {
5010
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
5011
- if (await pwdModeBtn.isVisible({ timeout: 1e3 }).catch(() => false)) {
5012
- await pwdModeBtn.click().catch(() => {
5017
+ const pwdModeBtn = findPasswordModeButton();
5018
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
5019
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
5013
5020
  });
5014
5021
  await sleep2(500);
5015
5022
  }
@@ -5034,7 +5041,7 @@ async function loginViaUi(page, email, password) {
5034
5041
  }
5035
5042
  await page.fill(passwordSelector, password);
5036
5043
  await sleep2(500);
5037
- const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
5044
+ 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")';
5038
5045
  const submitButton = page.locator(submitSelector).first();
5039
5046
  try {
5040
5047
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -5044,49 +5051,63 @@ async function loginViaUi(page, email, password) {
5044
5051
  } catch {
5045
5052
  await page.keyboard.press("Enter");
5046
5053
  }
5047
- await sleep2(3e3);
5048
- await solveBaxiaCaptcha(page, {
5049
- waitForMs: 2e3,
5050
- maxAttempts: config.captcha.maxAttempts,
5051
- retryDelayMs: config.captcha.retryDelayMs
5052
- }).catch(() => {
5053
- });
5054
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5055
- const codeInput = page.locator(codeSelector).first();
5056
- if (await codeInput.isVisible().catch(() => false)) {
5057
- return {
5058
- success: false,
5059
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
5060
- };
5061
- }
5062
- const errorSelector = [
5063
- ".qwen-chat-v2-toast-text",
5064
- ".qwen-chat-v2-toast-content",
5065
- ".ant-form-item-explain-error",
5066
- ".ant-message-error",
5067
- ".ant-message-notice",
5068
- '[role="alert"]',
5069
- ".qwenchat-auth-error",
5070
- ".auth-error-message"
5071
- ].join(", ");
5072
- const errorEl = page.locator(errorSelector).first();
5073
- if (await errorEl.isVisible().catch(() => false)) {
5074
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
5075
- if (errorText) {
5076
- const classified = classifyQwenAuthError(void 0, errorText);
5054
+ const postSubmitDeadline = Date.now() + 15e3;
5055
+ while (Date.now() < postSubmitDeadline) {
5056
+ if (page.isClosed()) break;
5057
+ if (config.captcha.enabled) {
5058
+ await solveBaxiaCaptcha(page, {
5059
+ waitForMs: 1500,
5060
+ maxAttempts: config.captcha.maxAttempts,
5061
+ retryDelayMs: config.captcha.retryDelayMs
5062
+ }).catch(() => {
5063
+ });
5064
+ }
5065
+ const errorSelector = [
5066
+ ".qwen-chat-v2-toast-text",
5067
+ ".qwen-chat-v2-toast-content",
5068
+ ".ant-form-item-explain-error",
5069
+ ".ant-message-error",
5070
+ ".ant-message-notice",
5071
+ '[role="alert"]',
5072
+ ".qwenchat-auth-error",
5073
+ ".auth-error-message"
5074
+ ].join(", ");
5075
+ const errorEl = page.locator(errorSelector).first();
5076
+ if (await errorEl.isVisible().catch(() => false)) {
5077
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
5078
+ if (errorText) {
5079
+ const classified = classifyQwenAuthError(void 0, errorText);
5080
+ return {
5081
+ success: false,
5082
+ permanentFailure: classified.isPermanent,
5083
+ reason: `Formul\xE1rio: ${classified.reason}`
5084
+ };
5085
+ }
5086
+ }
5087
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5088
+ const codeInput = page.locator(codeSelector).first();
5089
+ if (await codeInput.isVisible().catch(() => false)) {
5077
5090
  return {
5078
5091
  success: false,
5079
- permanentFailure: classified.isPermanent,
5080
- reason: `Formul\xE1rio: ${classified.reason}`
5092
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
5081
5093
  };
5082
5094
  }
5095
+ try {
5096
+ const liveCookies = await page.context().cookies();
5097
+ if (liveCookies.some((c) => c.name === "token")) {
5098
+ return { success: true };
5099
+ }
5100
+ } catch {
5101
+ }
5102
+ const currentUrl = page.url();
5103
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
5104
+ if (await isPageLoggedIn(page, 3e3)) {
5105
+ return { success: true };
5106
+ }
5107
+ }
5108
+ await sleep2(1e3);
5083
5109
  }
5084
- const isLoggedIn = await isPageLoggedIn(page);
5085
- if (isLoggedIn) {
5086
- await page.goto(qwenUrl("/"), {
5087
- waitUntil: "domcontentloaded",
5088
- timeout: config.timeouts.navigation
5089
- });
5110
+ if (await isPageLoggedIn(page, 3e3)) {
5090
5111
  return { success: true };
5091
5112
  }
5092
5113
  return {
@@ -6440,7 +6461,8 @@ async function addAccountFlow() {
6440
6461
  const { config: config3 } = await Promise.resolve().then(() => (init_config(), config_exports));
6441
6462
  const ok = await validateAccountLogin2(
6442
6463
  { id: tempId, email: trimmedEmail, password: trimmedPassword },
6443
- config3.playwright.headless,
6464
+ false,
6465
+ // Visible browser so user can see and complete challenges if needed
6444
6466
  config3.playwright.browser
6445
6467
  );
6446
6468
  if (ok) {
@@ -4088,8 +4088,9 @@ async function loginToQwen(accountId, email, password) {
4088
4088
  }
4089
4089
  if (attempt < maxAttempts) {
4090
4090
  const backoffMs = attempt * 5e3;
4091
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
4091
4092
  console.warn(
4092
- `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha tempor\xE1ria"}), retrying in ${backoffMs / 1e3}s`
4093
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
4093
4094
  );
4094
4095
  await sleep2(backoffMs);
4095
4096
  }
@@ -4271,10 +4272,16 @@ async function loginViaUi(page, email, password) {
4271
4272
  );
4272
4273
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4273
4274
  }
4275
+ await clearVisibleChallenge(page);
4276
+ const findPasswordModeButton = () => {
4277
+ return page.locator('button, [role="button"], a, div').filter({
4278
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
4279
+ }).first();
4280
+ };
4274
4281
  try {
4275
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4276
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4277
- await pwdModeBtn.click().catch(() => {
4282
+ const pwdModeBtn = findPasswordModeButton();
4283
+ if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4284
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4278
4285
  });
4279
4286
  await sleep2(500);
4280
4287
  }
@@ -4283,9 +4290,9 @@ async function loginViaUi(page, email, password) {
4283
4290
  await page.fill(emailSelector, email);
4284
4291
  await sleep2(300);
4285
4292
  try {
4286
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
4287
- if (await pwdModeBtn.isVisible({ timeout: 1e3 }).catch(() => false)) {
4288
- await pwdModeBtn.click().catch(() => {
4293
+ const pwdModeBtn = findPasswordModeButton();
4294
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4295
+ await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4289
4296
  });
4290
4297
  await sleep2(500);
4291
4298
  }
@@ -4310,7 +4317,7 @@ async function loginViaUi(page, email, password) {
4310
4317
  }
4311
4318
  await page.fill(passwordSelector, password);
4312
4319
  await sleep2(500);
4313
- const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
4320
+ 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")';
4314
4321
  const submitButton = page.locator(submitSelector).first();
4315
4322
  try {
4316
4323
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -4320,49 +4327,63 @@ async function loginViaUi(page, email, password) {
4320
4327
  } catch {
4321
4328
  await page.keyboard.press("Enter");
4322
4329
  }
4323
- await sleep2(3e3);
4324
- await solveBaxiaCaptcha(page, {
4325
- waitForMs: 2e3,
4326
- maxAttempts: config.captcha.maxAttempts,
4327
- retryDelayMs: config.captcha.retryDelayMs
4328
- }).catch(() => {
4329
- });
4330
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
4331
- const codeInput = page.locator(codeSelector).first();
4332
- if (await codeInput.isVisible().catch(() => false)) {
4333
- return {
4334
- success: false,
4335
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4336
- };
4337
- }
4338
- const errorSelector = [
4339
- ".qwen-chat-v2-toast-text",
4340
- ".qwen-chat-v2-toast-content",
4341
- ".ant-form-item-explain-error",
4342
- ".ant-message-error",
4343
- ".ant-message-notice",
4344
- '[role="alert"]',
4345
- ".qwenchat-auth-error",
4346
- ".auth-error-message"
4347
- ].join(", ");
4348
- const errorEl = page.locator(errorSelector).first();
4349
- if (await errorEl.isVisible().catch(() => false)) {
4350
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
4351
- if (errorText) {
4352
- const classified = classifyQwenAuthError(void 0, errorText);
4330
+ const postSubmitDeadline = Date.now() + 15e3;
4331
+ while (Date.now() < postSubmitDeadline) {
4332
+ if (page.isClosed()) break;
4333
+ if (config.captcha.enabled) {
4334
+ await solveBaxiaCaptcha(page, {
4335
+ waitForMs: 1500,
4336
+ maxAttempts: config.captcha.maxAttempts,
4337
+ retryDelayMs: config.captcha.retryDelayMs
4338
+ }).catch(() => {
4339
+ });
4340
+ }
4341
+ const errorSelector = [
4342
+ ".qwen-chat-v2-toast-text",
4343
+ ".qwen-chat-v2-toast-content",
4344
+ ".ant-form-item-explain-error",
4345
+ ".ant-message-error",
4346
+ ".ant-message-notice",
4347
+ '[role="alert"]',
4348
+ ".qwenchat-auth-error",
4349
+ ".auth-error-message"
4350
+ ].join(", ");
4351
+ const errorEl = page.locator(errorSelector).first();
4352
+ if (await errorEl.isVisible().catch(() => false)) {
4353
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
4354
+ if (errorText) {
4355
+ const classified = classifyQwenAuthError(void 0, errorText);
4356
+ return {
4357
+ success: false,
4358
+ permanentFailure: classified.isPermanent,
4359
+ reason: `Formul\xE1rio: ${classified.reason}`
4360
+ };
4361
+ }
4362
+ }
4363
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
4364
+ const codeInput = page.locator(codeSelector).first();
4365
+ if (await codeInput.isVisible().catch(() => false)) {
4353
4366
  return {
4354
4367
  success: false,
4355
- permanentFailure: classified.isPermanent,
4356
- reason: `Formul\xE1rio: ${classified.reason}`
4368
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
4357
4369
  };
4358
4370
  }
4371
+ try {
4372
+ const liveCookies = await page.context().cookies();
4373
+ if (liveCookies.some((c) => c.name === "token")) {
4374
+ return { success: true };
4375
+ }
4376
+ } catch {
4377
+ }
4378
+ const currentUrl = page.url();
4379
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
4380
+ if (await isPageLoggedIn(page, 3e3)) {
4381
+ return { success: true };
4382
+ }
4383
+ }
4384
+ await sleep2(1e3);
4359
4385
  }
4360
- const isLoggedIn = await isPageLoggedIn(page);
4361
- if (isLoggedIn) {
4362
- await page.goto(qwenUrl("/"), {
4363
- waitUntil: "domcontentloaded",
4364
- timeout: config.timeouts.navigation
4365
- });
4386
+ if (await isPageLoggedIn(page, 3e3)) {
4366
4387
  return { success: true };
4367
4388
  }
4368
4389
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
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": {
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.4.1";
20
+ cachedVersion = "v1.4.3";
21
21
  return cachedVersion;
22
22
  }
package/src/login.ts CHANGED
@@ -120,7 +120,7 @@ async function addAccountFlow() {
120
120
 
121
121
  const ok = await validateAccountLogin(
122
122
  { id: tempId, email: trimmedEmail, password: trimmedPassword },
123
- config.playwright.headless,
123
+ false, // Visible browser so user can see and complete challenges if needed
124
124
  config.playwright.browser,
125
125
  );
126
126
 
@@ -2163,8 +2163,9 @@ async function loginToQwen(
2163
2163
 
2164
2164
  if (attempt < maxAttempts) {
2165
2165
  const backoffMs = attempt * 5_000;
2166
+ const failReason = uiResult.reason || apiResult.reason || "falha temporária";
2166
2167
  console.warn(
2167
- `⚠️ [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${apiResult.reason || uiResult.reason || "falha temporária"}), retrying in ${backoffMs / 1000}s`,
2168
+ `⚠️ [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1000}s`,
2168
2169
  );
2169
2170
  await sleep(backoffMs);
2170
2171
  }
@@ -2375,11 +2376,22 @@ async function loginViaUi(
2375
2376
  }
2376
2377
 
2377
2378
  // In Qwen Web, if the page opens on the default email OTP panel, click "Log in with a password"
2378
- // to reveal the standard email + password form.
2379
+ // (supporting English, Portuguese, Spanish, Chinese) to reveal the standard email + password form.
2380
+ await clearVisibleChallenge(page);
2381
+
2382
+ const findPasswordModeButton = () => {
2383
+ return page
2384
+ .locator('button, [role="button"], a, div')
2385
+ .filter({
2386
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i,
2387
+ })
2388
+ .first();
2389
+ };
2390
+
2379
2391
  try {
2380
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
2381
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
2382
- await pwdModeBtn.click().catch(() => {});
2392
+ const pwdModeBtn = findPasswordModeButton();
2393
+ if (await pwdModeBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
2394
+ await pwdModeBtn.click({ timeout: 2000 }).catch(() => {});
2383
2395
  await sleep(500);
2384
2396
  }
2385
2397
  } catch {}
@@ -2390,9 +2402,9 @@ async function loginViaUi(
2390
2402
 
2391
2403
  // If "Log in with a password" button appeared after typing email, click it
2392
2404
  try {
2393
- const pwdModeBtn = page.getByText(/Log in with a password/i).first();
2394
- if (await pwdModeBtn.isVisible({ timeout: 1000 }).catch(() => false)) {
2395
- await pwdModeBtn.click().catch(() => {});
2405
+ const pwdModeBtn = findPasswordModeButton();
2406
+ if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
2407
+ await pwdModeBtn.click({ timeout: 2000 }).catch(() => {});
2396
2408
  await sleep(500);
2397
2409
  }
2398
2410
  } catch {}
@@ -2426,7 +2438,7 @@ async function loginViaUi(
2426
2438
  // Prefer clicking the submit button; fall back to pressing Enter.
2427
2439
  // The button starts disabled and only enables once both fields are filled.
2428
2440
  const submitSelector =
2429
- 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in")';
2441
+ '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ón"), button:has-text("登录")';
2430
2442
  const submitButton = page.locator(submitSelector).first();
2431
2443
  try {
2432
2444
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
@@ -2436,58 +2448,76 @@ async function loginViaUi(
2436
2448
  } catch {
2437
2449
  await page.keyboard.press("Enter");
2438
2450
  }
2439
- await sleep(3000);
2440
-
2441
- // If a slider puzzle captcha appeared after submit, solve it automatically
2442
- await solveBaxiaCaptcha(page, {
2443
- waitForMs: 2000,
2444
- maxAttempts: config.captcha.maxAttempts,
2445
- retryDelayMs: config.captcha.retryDelayMs,
2446
- }).catch(() => {});
2451
+ // Post-submit verification loop: wait up to 15s for captcha, redirect, cookies, or errors
2452
+ const postSubmitDeadline = Date.now() + 15_000;
2453
+ while (Date.now() < postSubmitDeadline) {
2454
+ if (page.isClosed()) break;
2455
+
2456
+ // If a slider puzzle captcha appeared after submit, solve it automatically
2457
+ if (config.captcha.enabled) {
2458
+ await solveBaxiaCaptcha(page, {
2459
+ waitForMs: 1500,
2460
+ maxAttempts: config.captcha.maxAttempts,
2461
+ retryDelayMs: config.captcha.retryDelayMs,
2462
+ }).catch(() => {});
2463
+ }
2447
2464
 
2448
- // Check if an OTP code verification screen appeared (e.g. 2FA / email verification code)
2449
- const codeSelector =
2450
- 'input[placeholder*="code" i], input[placeholder*="código" i], input[name*="code" i]';
2451
- const codeInput = page.locator(codeSelector).first();
2452
- if (await codeInput.isVisible().catch(() => false)) {
2453
- return {
2454
- success: false,
2455
- reason: "Conta requer código de verificação enviado por e-mail (2FA/OTP)",
2456
- };
2457
- }
2465
+ // Check for UI error elements in DOM (Ant Design errors, alerts, toasts)
2466
+ const errorSelector = [
2467
+ ".qwen-chat-v2-toast-text",
2468
+ ".qwen-chat-v2-toast-content",
2469
+ ".ant-form-item-explain-error",
2470
+ ".ant-message-error",
2471
+ ".ant-message-notice",
2472
+ '[role="alert"]',
2473
+ ".qwenchat-auth-error",
2474
+ ".auth-error-message",
2475
+ ].join(", ");
2476
+ const errorEl = page.locator(errorSelector).first();
2477
+ if (await errorEl.isVisible().catch(() => false)) {
2478
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
2479
+ if (errorText) {
2480
+ const classified = classifyQwenAuthError(undefined, errorText);
2481
+ return {
2482
+ success: false,
2483
+ permanentFailure: classified.isPermanent,
2484
+ reason: `Formulário: ${classified.reason}`,
2485
+ };
2486
+ }
2487
+ }
2458
2488
 
2459
- // Check for UI error elements in DOM (Ant Design errors, alerts, toasts)
2460
- const errorSelector = [
2461
- ".qwen-chat-v2-toast-text",
2462
- ".qwen-chat-v2-toast-content",
2463
- ".ant-form-item-explain-error",
2464
- ".ant-message-error",
2465
- ".ant-message-notice",
2466
- '[role="alert"]',
2467
- ".qwenchat-auth-error",
2468
- ".auth-error-message",
2469
- ].join(", ");
2470
- const errorEl = page.locator(errorSelector).first();
2471
- if (await errorEl.isVisible().catch(() => false)) {
2472
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
2473
- if (errorText) {
2474
- const classified = classifyQwenAuthError(undefined, errorText);
2489
+ // Check if an OTP code verification screen appeared (e.g. 2FA / email verification code)
2490
+ const codeSelector =
2491
+ 'input[placeholder*="code" i], input[placeholder*="código" i], input[name*="code" i]';
2492
+ const codeInput = page.locator(codeSelector).first();
2493
+ if (await codeInput.isVisible().catch(() => false)) {
2475
2494
  return {
2476
2495
  success: false,
2477
- permanentFailure: classified.isPermanent,
2478
- reason: `Formulário: ${classified.reason}`,
2496
+ reason: "Conta requer código de verificação enviado por e-mail (2FA/OTP)",
2479
2497
  };
2480
2498
  }
2481
- }
2482
2499
 
2483
- // Check if login was successful
2484
- const isLoggedIn = await isPageLoggedIn(page);
2500
+ // Check if auth cookie 'token' appeared
2501
+ try {
2502
+ const liveCookies = await page.context().cookies();
2503
+ if (liveCookies.some((c) => c.name === "token")) {
2504
+ return { success: true };
2505
+ }
2506
+ } catch {}
2485
2507
 
2486
- if (isLoggedIn) {
2487
- await page.goto(qwenUrl("/"), {
2488
- waitUntil: "domcontentloaded",
2489
- timeout: config.timeouts.navigation,
2490
- });
2508
+ // Check if page redirected away from /auth and is logged in
2509
+ const currentUrl = page.url();
2510
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
2511
+ if (await isPageLoggedIn(page, 3000)) {
2512
+ return { success: true };
2513
+ }
2514
+ }
2515
+
2516
+ await sleep(1000);
2517
+ }
2518
+
2519
+ // Final check
2520
+ if (await isPageLoggedIn(page, 3000)) {
2491
2521
  return { success: true };
2492
2522
  }
2493
2523