qwenproxy-cli 1.4.2 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/login.js CHANGED
@@ -3864,8 +3864,29 @@ function getStealthScript(profile) {
3864
3864
  const _toBlob = HTMLCanvasElement.prototype.toBlob;
3865
3865
  const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
3866
3866
 
3867
+ function isCaptchaCanvas(canvas) {
3868
+ try {
3869
+ if (!canvas) return false;
3870
+ const id = (canvas.id || "").toLowerCase();
3871
+ const cls = (canvas.className || "").toLowerCase();
3872
+ return (
3873
+ id.includes("baxia") ||
3874
+ id.includes("nc_") ||
3875
+ id.includes("captcha") ||
3876
+ id.includes("punish") ||
3877
+ cls.includes("baxia") ||
3878
+ cls.includes("nc_") ||
3879
+ cls.includes("captcha") ||
3880
+ Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
3881
+ );
3882
+ } catch(e) {
3883
+ return false;
3884
+ }
3885
+ }
3886
+
3867
3887
  function addNoise(canvas) {
3868
3888
  try {
3889
+ if (isCaptchaCanvas(canvas)) return;
3869
3890
  const ctx = canvas.getContext('2d');
3870
3891
  if (!ctx) return;
3871
3892
  const style = ctx.fillStyle;
@@ -3889,6 +3910,9 @@ function getStealthScript(profile) {
3889
3910
 
3890
3911
  CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
3891
3912
  const imageData = _getImageData.apply(this, arguments);
3913
+ if (isCaptchaCanvas(this.canvas)) {
3914
+ return imageData;
3915
+ }
3892
3916
  const data = imageData.data;
3893
3917
  const maxPixels = Math.min(data.length / 4, 2500);
3894
3918
  for (let i = 0; i < maxPixels * 4; i += 4) {
@@ -4812,8 +4836,9 @@ async function loginToQwen(accountId, email, password) {
4812
4836
  }
4813
4837
  if (attempt < maxAttempts) {
4814
4838
  const backoffMs = attempt * 5e3;
4839
+ const failReason = uiResult.reason || apiResult.reason || "falha tempor\xE1ria";
4815
4840
  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`
4841
+ `\u26A0\uFE0F [Playwright] Login attempt ${attempt}/${maxAttempts} failed for ${maskEmail(email)} (${failReason}), retrying in ${backoffMs / 1e3}s`
4817
4842
  );
4818
4843
  await sleep2(backoffMs);
4819
4844
  }
@@ -4843,37 +4868,52 @@ async function loginToQwen(accountId, email, password) {
4843
4868
  async function loginViaApi(page, email, password) {
4844
4869
  try {
4845
4870
  const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
4846
- const signinUrl = qwenUrl("/api/v2/auths/signin");
4847
- let signinSuccess = false;
4848
- let data = null;
4849
- if (page.request && typeof page.request.post === "function") {
4850
- try {
4851
- const response = await page.request.post(signinUrl, {
4852
- data: {
4853
- email,
4854
- password: hashedPassword,
4855
- login_type: "email"
4856
- },
4857
- headers: {
4858
- "content-type": "application/json",
4859
- accept: "application/json, text/plain, */*",
4860
- referer: qwenUrl("/auth"),
4861
- origin: qwenOrigin(),
4862
- source: "web"
4863
- },
4864
- timeout: 1e4
4865
- });
4866
- data = await response.json().catch(() => null);
4867
- signinSuccess = Boolean(data && (data.success === true || data.token));
4868
- } catch {
4869
- }
4871
+ if (!page.url().includes("/auth")) {
4872
+ await page.goto(qwenUrl("/auth"), {
4873
+ waitUntil: "domcontentloaded",
4874
+ timeout: config.timeouts.navigation
4875
+ }).catch(() => {
4876
+ });
4877
+ await sleep2(1e3);
4870
4878
  }
4871
- if (!signinSuccess && !data) {
4872
- const requestId = crypto3.randomUUID();
4873
- const evalRes = await page.evaluate(
4874
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4879
+ const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4880
+ const requestId = crypto3.randomUUID();
4881
+ const evalRes = await page.evaluate(
4882
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4883
+ try {
4884
+ const response = await fetch(signinUrl2, {
4885
+ method: "POST",
4886
+ signal: AbortSignal.timeout(15e3),
4887
+ credentials: "include",
4888
+ headers: {
4889
+ accept: "application/json, text/plain, */*",
4890
+ "content-type": "application/json",
4891
+ source: "web",
4892
+ version: "0.3.11",
4893
+ "x-request-origin": "https://chat.qwen.ai",
4894
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4895
+ "x-request-id": requestId2
4896
+ },
4897
+ body: JSON.stringify({ email: email2, password: password2 })
4898
+ });
4899
+ const json = await response.json().catch(() => null);
4900
+ return { ok: response.ok, status: response.status, data: json };
4901
+ } catch (e) {
4902
+ return { ok: false, error: e.message };
4903
+ }
4904
+ },
4905
+ { email, password: hashedPassword, signinUrl, requestId }
4906
+ ).catch(() => null);
4907
+ let data = evalRes?.data;
4908
+ let signinSuccess = Boolean(
4909
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4910
+ );
4911
+ if (!signinSuccess && !data?.data?.code) {
4912
+ const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4913
+ const fallbackRes = await page.evaluate(
4914
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4875
4915
  try {
4876
- const response = await fetch(signinUrl2, {
4916
+ const response = await fetch(fallbackUrl2, {
4877
4917
  method: "POST",
4878
4918
  signal: AbortSignal.timeout(1e4),
4879
4919
  credentials: "include",
@@ -4881,10 +4921,11 @@ async function loginViaApi(page, email, password) {
4881
4921
  accept: "application/json, text/plain, */*",
4882
4922
  "content-type": "application/json",
4883
4923
  source: "web",
4924
+ version: "0.3.11",
4884
4925
  timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4885
4926
  "x-request-id": requestId2
4886
4927
  },
4887
- body: JSON.stringify({ email: email2, password: password2, login_type: "email" })
4928
+ body: JSON.stringify({ email: email2, password: password2 })
4888
4929
  });
4889
4930
  const json = await response.json().catch(() => null);
4890
4931
  return { ok: response.ok, status: response.status, data: json };
@@ -4892,11 +4933,13 @@ async function loginViaApi(page, email, password) {
4892
4933
  return { ok: false, error: e.message };
4893
4934
  }
4894
4935
  },
4895
- { email, password: hashedPassword, signinUrl, requestId }
4936
+ { email, password: hashedPassword, fallbackUrl, requestId }
4896
4937
  ).catch(() => null);
4897
- if (evalRes?.data) {
4898
- data = evalRes.data;
4899
- signinSuccess = Boolean(data && (data.success === true || data.token));
4938
+ if (fallbackRes?.data) {
4939
+ data = fallbackRes.data;
4940
+ signinSuccess = Boolean(
4941
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4942
+ );
4900
4943
  }
4901
4944
  }
4902
4945
  if (data?.success === false) {
@@ -4910,7 +4953,8 @@ async function loginViaApi(page, email, password) {
4910
4953
  };
4911
4954
  }
4912
4955
  if (signinSuccess) {
4913
- const token = data?.data?.token || data?.token;
4956
+ const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
4957
+ const refreshToken = data?.data?.refresh_token || data?.refresh_token;
4914
4958
  if (token) {
4915
4959
  try {
4916
4960
  await page.context().addCookies([
@@ -4934,14 +4978,19 @@ async function loginViaApi(page, email, password) {
4934
4978
  }).catch(() => {
4935
4979
  });
4936
4980
  if (token) {
4937
- await page.evaluate((tok) => {
4938
- try {
4939
- localStorage.removeItem("qwen_token_logged_out_marker");
4940
- localStorage.setItem("token", tok);
4941
- document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4942
- } catch {
4943
- }
4944
- }, token).catch(() => {
4981
+ await page.evaluate(
4982
+ ({ tok, rTok }) => {
4983
+ try {
4984
+ localStorage.removeItem("qwen_token_logged_out_marker");
4985
+ localStorage.setItem("token", tok);
4986
+ localStorage.setItem("access_token", tok);
4987
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4988
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4989
+ } catch {
4990
+ }
4991
+ },
4992
+ { tok: token, rTok: refreshToken }
4993
+ ).catch(() => {
4945
4994
  });
4946
4995
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4947
4996
  });
@@ -4969,11 +5018,23 @@ async function loginViaApi(page, email, password) {
4969
5018
  }
4970
5019
  async function loginViaUi(page, email, password) {
4971
5020
  try {
4972
- await page.goto(qwenUrl("/auth"), {
4973
- waitUntil: "domcontentloaded",
4974
- timeout: config.timeouts.navigation
4975
- });
4976
- await sleep2(1500);
5021
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
5022
+ return { success: true };
5023
+ }
5024
+ try {
5025
+ await page.goto(qwenUrl("/auth"), {
5026
+ waitUntil: "domcontentloaded",
5027
+ timeout: Math.min(config.timeouts.navigation, 15e3)
5028
+ });
5029
+ await sleep2(1e3);
5030
+ } catch (gotoErr) {
5031
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
5032
+ return { success: true };
5033
+ }
5034
+ }
5035
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
5036
+ return { success: true };
5037
+ }
4977
5038
  const emailSelector = [
4978
5039
  'input[type="email"]',
4979
5040
  'input[name="email"]',
@@ -4983,7 +5044,7 @@ async function loginViaUi(page, email, password) {
4983
5044
  ].join(", ");
4984
5045
  try {
4985
5046
  await page.waitForSelector(emailSelector, {
4986
- timeout: Math.min(config.timeouts.page, 8e3)
5047
+ timeout: Math.min(config.timeouts.page, 5e3)
4987
5048
  });
4988
5049
  } catch {
4989
5050
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
@@ -4996,30 +5057,55 @@ async function loginViaUi(page, email, password) {
4996
5057
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4997
5058
  }
4998
5059
  await clearVisibleChallenge(page);
4999
- const findPasswordModeButton = () => {
5000
- return page.locator('button, [role="button"], a, div').filter({
5001
- 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
5002
- }).first();
5060
+ const isPasswordInputPresent = async () => {
5061
+ return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
5003
5062
  };
5004
- try {
5005
- const pwdModeBtn = findPasswordModeButton();
5006
- if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
5007
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
5063
+ const revealPasswordMode = async () => {
5064
+ if (page.url().includes("/forget")) {
5065
+ await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
5008
5066
  });
5009
- await sleep2(500);
5067
+ await sleep2(1e3);
5010
5068
  }
5011
- } catch {
5012
- }
5013
- await page.fill(emailSelector, email);
5014
- await sleep2(300);
5015
- try {
5016
- const pwdModeBtn = findPasswordModeButton();
5017
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
5018
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
5069
+ if (await isPasswordInputPresent()) return;
5070
+ const btn = page.locator("button").filter({
5071
+ 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
5072
+ }).filter({
5073
+ hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
5074
+ }).first();
5075
+ if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
5076
+ await btn.click({ timeout: 2e3 }).catch(() => {
5019
5077
  });
5020
5078
  await sleep2(500);
5021
5079
  }
5080
+ if (await isPasswordInputPresent()) return;
5081
+ await page.evaluate(() => {
5082
+ try {
5083
+ const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
5084
+ for (const b of buttons) {
5085
+ const txt = (b.innerText || "").trim().toLowerCase();
5086
+ if (/(?: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.test(txt) && !txt.includes("esqueci") && !txt.includes("forgot") && !txt.includes("olvid") && !txt.includes("reset")) {
5087
+ b.click();
5088
+ return true;
5089
+ }
5090
+ }
5091
+ } catch {
5092
+ }
5093
+ return false;
5094
+ }).catch(() => false);
5095
+ await sleep2(500);
5096
+ };
5097
+ await revealPasswordMode();
5098
+ try {
5099
+ await page.fill(emailSelector, email, { timeout: 8e3 });
5100
+ await sleep2(300);
5022
5101
  } catch {
5102
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5103
+ return { success: true };
5104
+ }
5105
+ throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5106
+ }
5107
+ if (!await isPasswordInputPresent()) {
5108
+ await revealPasswordMode();
5023
5109
  }
5024
5110
  const passwordSelector = 'input[type="password"], input[name="password"]';
5025
5111
  try {
@@ -5038,61 +5124,108 @@ async function loginViaUi(page, email, password) {
5038
5124
  reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
5039
5125
  };
5040
5126
  }
5041
- await page.fill(passwordSelector, password);
5042
- await sleep2(500);
5127
+ try {
5128
+ await page.fill(passwordSelector, password, { timeout: 8e3 });
5129
+ await sleep2(500);
5130
+ } catch {
5131
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5132
+ return { success: true };
5133
+ }
5134
+ throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5135
+ }
5136
+ await page.evaluate(() => {
5137
+ try {
5138
+ const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
5139
+ const passEl = document.querySelector('input[type="password"], input[name="password"]');
5140
+ if (emailEl) {
5141
+ emailEl.dispatchEvent(new Event("input", { bubbles: true }));
5142
+ emailEl.dispatchEvent(new Event("change", { bubbles: true }));
5143
+ emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
5144
+ }
5145
+ if (passEl) {
5146
+ passEl.dispatchEvent(new Event("input", { bubbles: true }));
5147
+ passEl.dispatchEvent(new Event("change", { bubbles: true }));
5148
+ passEl.dispatchEvent(new Event("blur", { bubbles: true }));
5149
+ }
5150
+ } catch {
5151
+ }
5152
+ }).catch(() => {
5153
+ });
5043
5154
  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")';
5044
5155
  const submitButton = page.locator(submitSelector).first();
5045
5156
  try {
5046
5157
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
5047
- timeout: 5e3
5158
+ timeout: 4e3
5048
5159
  });
5049
5160
  await submitButton.click();
5050
5161
  } catch {
5051
- await page.keyboard.press("Enter");
5052
- }
5053
- await sleep2(3e3);
5054
- await solveBaxiaCaptcha(page, {
5055
- waitForMs: 2e3,
5056
- maxAttempts: config.captcha.maxAttempts,
5057
- retryDelayMs: config.captcha.retryDelayMs
5058
- }).catch(() => {
5059
- });
5060
- const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5061
- const codeInput = page.locator(codeSelector).first();
5062
- if (await codeInput.isVisible().catch(() => false)) {
5063
- return {
5064
- success: false,
5065
- reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
5066
- };
5162
+ const isDisabled = await submitButton.isDisabled().catch(() => true);
5163
+ if (!isDisabled) {
5164
+ await submitButton.click({ force: true }).catch(() => {
5165
+ });
5166
+ } else {
5167
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
5168
+ return { success: true };
5169
+ }
5170
+ }
5067
5171
  }
5068
- const errorSelector = [
5069
- ".qwen-chat-v2-toast-text",
5070
- ".qwen-chat-v2-toast-content",
5071
- ".ant-form-item-explain-error",
5072
- ".ant-message-error",
5073
- ".ant-message-notice",
5074
- '[role="alert"]',
5075
- ".qwenchat-auth-error",
5076
- ".auth-error-message"
5077
- ].join(", ");
5078
- const errorEl = page.locator(errorSelector).first();
5079
- if (await errorEl.isVisible().catch(() => false)) {
5080
- const errorText = (await errorEl.innerText().catch(() => "")).trim();
5081
- if (errorText) {
5082
- const classified = classifyQwenAuthError(void 0, errorText);
5172
+ const postSubmitDeadline = Date.now() + 15e3;
5173
+ while (Date.now() < postSubmitDeadline) {
5174
+ if (page.isClosed()) break;
5175
+ if (config.captcha.enabled) {
5176
+ await solveBaxiaCaptcha(page, {
5177
+ waitForMs: 1500,
5178
+ maxAttempts: config.captcha.maxAttempts,
5179
+ retryDelayMs: config.captcha.retryDelayMs
5180
+ }).catch(() => {
5181
+ });
5182
+ }
5183
+ const errorSelector = [
5184
+ ".qwen-chat-v2-toast-text",
5185
+ ".qwen-chat-v2-toast-content",
5186
+ ".ant-form-item-explain-error",
5187
+ ".ant-message-error",
5188
+ ".ant-message-notice",
5189
+ '[role="alert"]',
5190
+ ".qwenchat-auth-error",
5191
+ ".auth-error-message"
5192
+ ].join(", ");
5193
+ const errorEl = page.locator(errorSelector).first();
5194
+ if (await errorEl.isVisible().catch(() => false)) {
5195
+ const errorText = (await errorEl.innerText().catch(() => "")).trim();
5196
+ if (errorText) {
5197
+ const classified = classifyQwenAuthError(void 0, errorText);
5198
+ return {
5199
+ success: false,
5200
+ permanentFailure: classified.isPermanent,
5201
+ reason: `Formul\xE1rio: ${classified.reason}`
5202
+ };
5203
+ }
5204
+ }
5205
+ const codeSelector = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
5206
+ const codeInput = page.locator(codeSelector).first();
5207
+ if (await codeInput.isVisible().catch(() => false)) {
5083
5208
  return {
5084
5209
  success: false,
5085
- permanentFailure: classified.isPermanent,
5086
- reason: `Formul\xE1rio: ${classified.reason}`
5210
+ reason: "Conta requer c\xF3digo de verifica\xE7\xE3o enviado por e-mail (2FA/OTP)"
5087
5211
  };
5088
5212
  }
5213
+ try {
5214
+ const liveCookies = await page.context().cookies();
5215
+ if (liveCookies.some((c) => c.name === "token")) {
5216
+ return { success: true };
5217
+ }
5218
+ } catch {
5219
+ }
5220
+ const currentUrl = page.url();
5221
+ if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
5222
+ if (await isPageLoggedIn(page, 3e3)) {
5223
+ return { success: true };
5224
+ }
5225
+ }
5226
+ await sleep2(1e3);
5089
5227
  }
5090
- const isLoggedIn = await isPageLoggedIn(page);
5091
- if (isLoggedIn) {
5092
- await page.goto(qwenUrl("/"), {
5093
- waitUntil: "domcontentloaded",
5094
- timeout: config.timeouts.navigation
5095
- });
5228
+ if (await isPageLoggedIn(page, 3e3)) {
5096
5229
  return { success: true };
5097
5230
  }
5098
5231
  return {
@@ -5620,6 +5753,17 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5620
5753
  }
5621
5754
  };
5622
5755
  if (forceReauth) {
5756
+ const alreadyIn = await isPageLoggedIn(page, 2e3).catch(() => false);
5757
+ if (alreadyIn) {
5758
+ const liveCookies = await page.context().cookies();
5759
+ if (liveCookies.some((c) => c.name === "token")) {
5760
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5761
+ cache.headers.cookie = cookieStr;
5762
+ cache.lastRefresh = Date.now();
5763
+ markAccountHeadersReady(accountId);
5764
+ return;
5765
+ }
5766
+ }
5623
5767
  await executeReauth();
5624
5768
  } else {
5625
5769
  try {
@@ -6054,19 +6198,50 @@ async function keepAlivePlaywrightAccount(accountId) {
6054
6198
  const now = Date.now();
6055
6199
  const currentUrl = page.url();
6056
6200
  const cookie = await getCookies(accountId);
6057
- if (cookie && isTokenExpiringSoon(cookie, 45)) {
6058
- console.log(
6059
- `\u{1F493} [SessionKeeper] Account ${accountId} token expires within 45m; proactively renewing session...`
6060
- );
6201
+ if (cookie && isTokenExpiringSoon(cookie, 2)) {
6061
6202
  try {
6062
- await refreshHeadersInternal(accountId, config.timeouts.headers, true);
6063
- lastKeepAliveNavigation.set(accountId, now);
6064
- touchAccountActivity(accountId);
6065
- return true;
6066
- } catch (err) {
6067
- console.warn(
6068
- `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
6203
+ await page.evaluate(async () => {
6204
+ await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
6205
+ method: "GET",
6206
+ credentials: "include"
6207
+ }).catch(() => {
6208
+ });
6209
+ await fetch("/api/v1/auths/", {
6210
+ method: "GET",
6211
+ credentials: "include"
6212
+ }).catch(() => {
6213
+ });
6214
+ }).catch(() => {
6215
+ });
6216
+ const liveCookies = await page.context().cookies();
6217
+ const tokenCookie = liveCookies.find((c) => c.name === "token");
6218
+ if (tokenCookie) {
6219
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
6220
+ const cache = getHeaderCache(accountId);
6221
+ cache.headers.cookie = cookieStr;
6222
+ cache.lastRefresh = Date.now();
6223
+ markAccountHeadersReady(accountId);
6224
+ lastKeepAliveNavigation.set(accountId, now);
6225
+ touchAccountActivity(accountId);
6226
+ return true;
6227
+ }
6228
+ } catch {
6229
+ }
6230
+ const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
6231
+ if (!loggedIn) {
6232
+ console.log(
6233
+ `\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
6069
6234
  );
6235
+ try {
6236
+ await refreshHeadersInternal(accountId, config.timeouts.headers, true);
6237
+ lastKeepAliveNavigation.set(accountId, now);
6238
+ touchAccountActivity(accountId);
6239
+ return true;
6240
+ } catch (err) {
6241
+ console.warn(
6242
+ `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
6243
+ );
6244
+ }
6070
6245
  }
6071
6246
  }
6072
6247
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
@@ -6446,7 +6621,8 @@ async function addAccountFlow() {
6446
6621
  const { config: config3 } = await Promise.resolve().then(() => (init_config(), config_exports));
6447
6622
  const ok = await validateAccountLogin2(
6448
6623
  { id: tempId, email: trimmedEmail, password: trimmedPassword },
6449
- config3.playwright.headless,
6624
+ false,
6625
+ // Visible browser so user can see and complete challenges if needed
6450
6626
  config3.playwright.browser
6451
6627
  );
6452
6628
  if (ok) {