qwenproxy-cli 1.4.3 → 1.4.8

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.
@@ -314,6 +314,7 @@ function runMigrations(db2) {
314
314
  "sec_ch_ua_mobile TEXT",
315
315
  "sec_ch_ua_platform TEXT",
316
316
  "version TEXT",
317
+ "refresh_token TEXT",
317
318
  "captured_at INTEGER DEFAULT 0"
318
319
  ];
319
320
  for (const col of authSessionCols) {
@@ -392,11 +393,11 @@ function saveAuthSession(accountId, session) {
392
393
  INSERT OR REPLACE INTO qwen_auth_sessions (
393
394
  account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
394
395
  sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
395
- user_id, token_expires_at, captured_at, updated_at
396
+ user_id, token_expires_at, refresh_token, captured_at, updated_at
396
397
  ) VALUES (
397
398
  ?, ?, ?, ?, ?, ?,
398
399
  ?, ?, ?, ?,
399
- ?, ?, ?, datetime('now')
400
+ ?, ?, ?, ?, datetime('now')
400
401
  )
401
402
  `);
402
403
  stmt.run(
@@ -412,6 +413,7 @@ function saveAuthSession(accountId, session) {
412
413
  session.version || null,
413
414
  session.userId || null,
414
415
  session.tokenExpiresAt || null,
416
+ session.refreshToken || null,
415
417
  session.capturedAt ?? Date.now()
416
418
  );
417
419
  }
@@ -420,7 +422,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
420
422
  const row = database.prepare(
421
423
  `SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
422
424
  sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
423
- user_id, token_expires_at, captured_at
425
+ user_id, token_expires_at, refresh_token, captured_at
424
426
  FROM qwen_auth_sessions WHERE account_id = ?`
425
427
  ).get(accountId);
426
428
  if (!row) return null;
@@ -428,7 +430,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
428
430
  if (capturedAt <= 0 || Date.now() - capturedAt > maxAgeMs) {
429
431
  return null;
430
432
  }
431
- if (row.token_expires_at) {
433
+ if (!row.refresh_token && row.token_expires_at) {
432
434
  const tokenExpMs = Number(row.token_expires_at) * 1e3;
433
435
  if (tokenExpMs <= Date.now() + 5 * 60 * 1e3) {
434
436
  return null;
@@ -450,6 +452,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
450
452
  version: row.version || void 0,
451
453
  userId: row.user_id || void 0,
452
454
  tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : void 0,
455
+ refreshToken: row.refresh_token || void 0,
453
456
  capturedAt
454
457
  };
455
458
  }
@@ -3052,6 +3055,7 @@ __export(playwright_exports, {
3052
3055
  parseCookiesForBrowser: () => parseCookiesForBrowser,
3053
3056
  pruneAllPlaywrightProfiles: () => pruneAllPlaywrightProfiles,
3054
3057
  prunePlaywrightProfileCaches: () => prunePlaywrightProfileCaches,
3058
+ refreshAccountToken: () => refreshAccountToken,
3055
3059
  refreshHeaders: () => refreshHeaders,
3056
3060
  refreshHeadersWithProfileReset: () => refreshHeadersWithProfileReset,
3057
3061
  registerPlaywrightAccountForTests: () => registerPlaywrightAccountForTests,
@@ -3779,8 +3783,29 @@ function getStealthScript(profile) {
3779
3783
  const _toBlob = HTMLCanvasElement.prototype.toBlob;
3780
3784
  const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
3781
3785
 
3786
+ function isCaptchaCanvas(canvas) {
3787
+ try {
3788
+ if (!canvas) return false;
3789
+ const id = (canvas.id || "").toLowerCase();
3790
+ const cls = (canvas.className || "").toLowerCase();
3791
+ return (
3792
+ id.includes("baxia") ||
3793
+ id.includes("nc_") ||
3794
+ id.includes("captcha") ||
3795
+ id.includes("punish") ||
3796
+ cls.includes("baxia") ||
3797
+ cls.includes("nc_") ||
3798
+ cls.includes("captcha") ||
3799
+ Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
3800
+ );
3801
+ } catch(e) {
3802
+ return false;
3803
+ }
3804
+ }
3805
+
3782
3806
  function addNoise(canvas) {
3783
3807
  try {
3808
+ if (isCaptchaCanvas(canvas)) return;
3784
3809
  const ctx = canvas.getContext('2d');
3785
3810
  if (!ctx) return;
3786
3811
  const style = ctx.fillStyle;
@@ -3804,6 +3829,9 @@ function getStealthScript(profile) {
3804
3829
 
3805
3830
  CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
3806
3831
  const imageData = _getImageData.apply(this, arguments);
3832
+ if (isCaptchaCanvas(this.canvas)) {
3833
+ return imageData;
3834
+ }
3807
3835
  const data = imageData.data;
3808
3836
  const maxPixels = Math.min(data.length / 4, 2500);
3809
3837
  for (let i = 0; i < maxPixels * 4; i += 4) {
@@ -4663,6 +4691,7 @@ async function loginToQwen(accountId, email, password) {
4663
4691
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
4664
4692
  version: cache.headers["version"] || void 0,
4665
4693
  tokenExpiresAt: exp || void 0,
4694
+ refreshToken: apiResult.refreshToken,
4666
4695
  capturedAt: Date.now()
4667
4696
  });
4668
4697
  }
@@ -4696,6 +4725,13 @@ async function loginToQwen(accountId, email, password) {
4696
4725
  markAccountHeadersReady(accountId);
4697
4726
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4698
4727
  const exp = parseJwtExpiry(tokenCookie.value);
4728
+ let rTok = uiResult.refreshToken;
4729
+ if (!rTok) {
4730
+ try {
4731
+ rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
4732
+ } catch {
4733
+ }
4734
+ }
4699
4735
  saveAuthSession2(accountId, {
4700
4736
  cookie: cookieStr,
4701
4737
  userAgent: cache.headers["user-agent"] || "",
@@ -4707,6 +4743,7 @@ async function loginToQwen(accountId, email, password) {
4707
4743
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
4708
4744
  version: cache.headers["version"] || void 0,
4709
4745
  tokenExpiresAt: exp || void 0,
4746
+ refreshToken: rTok,
4710
4747
  capturedAt: Date.now()
4711
4748
  });
4712
4749
  }
@@ -4759,37 +4796,52 @@ async function loginToQwen(accountId, email, password) {
4759
4796
  async function loginViaApi(page, email, password) {
4760
4797
  try {
4761
4798
  const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
4762
- const signinUrl = qwenUrl("/api/v2/auths/signin");
4763
- let signinSuccess = false;
4764
- let data = null;
4765
- if (page.request && typeof page.request.post === "function") {
4766
- try {
4767
- const response = await page.request.post(signinUrl, {
4768
- data: {
4769
- email,
4770
- password: hashedPassword,
4771
- login_type: "email"
4772
- },
4773
- headers: {
4774
- "content-type": "application/json",
4775
- accept: "application/json, text/plain, */*",
4776
- referer: qwenUrl("/auth"),
4777
- origin: qwenOrigin(),
4778
- source: "web"
4779
- },
4780
- timeout: 1e4
4781
- });
4782
- data = await response.json().catch(() => null);
4783
- signinSuccess = Boolean(data && (data.success === true || data.token));
4784
- } catch {
4785
- }
4799
+ if (!page.url().includes("/auth")) {
4800
+ await page.goto(qwenUrl("/auth"), {
4801
+ waitUntil: "domcontentloaded",
4802
+ timeout: config.timeouts.navigation
4803
+ }).catch(() => {
4804
+ });
4805
+ await sleep2(1e3);
4786
4806
  }
4787
- if (!signinSuccess && !data) {
4788
- const requestId = crypto3.randomUUID();
4789
- const evalRes = await page.evaluate(
4790
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4807
+ const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4808
+ const requestId = crypto3.randomUUID();
4809
+ const evalRes = await page.evaluate(
4810
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4811
+ try {
4812
+ const response = await fetch(signinUrl2, {
4813
+ method: "POST",
4814
+ signal: AbortSignal.timeout(15e3),
4815
+ credentials: "include",
4816
+ headers: {
4817
+ accept: "application/json, text/plain, */*",
4818
+ "content-type": "application/json",
4819
+ source: "web",
4820
+ version: "0.3.11",
4821
+ "x-request-origin": "https://chat.qwen.ai",
4822
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4823
+ "x-request-id": requestId2
4824
+ },
4825
+ body: JSON.stringify({ email: email2, password: password2 })
4826
+ });
4827
+ const json = await response.json().catch(() => null);
4828
+ return { ok: response.ok, status: response.status, data: json };
4829
+ } catch (e) {
4830
+ return { ok: false, error: e.message };
4831
+ }
4832
+ },
4833
+ { email, password: hashedPassword, signinUrl, requestId }
4834
+ ).catch(() => null);
4835
+ let data = evalRes?.data;
4836
+ let signinSuccess = Boolean(
4837
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4838
+ );
4839
+ if (!signinSuccess && !data?.data?.code) {
4840
+ const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4841
+ const fallbackRes = await page.evaluate(
4842
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4791
4843
  try {
4792
- const response = await fetch(signinUrl2, {
4844
+ const response = await fetch(fallbackUrl2, {
4793
4845
  method: "POST",
4794
4846
  signal: AbortSignal.timeout(1e4),
4795
4847
  credentials: "include",
@@ -4797,10 +4849,11 @@ async function loginViaApi(page, email, password) {
4797
4849
  accept: "application/json, text/plain, */*",
4798
4850
  "content-type": "application/json",
4799
4851
  source: "web",
4852
+ version: "0.3.11",
4800
4853
  timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4801
4854
  "x-request-id": requestId2
4802
4855
  },
4803
- body: JSON.stringify({ email: email2, password: password2, login_type: "email" })
4856
+ body: JSON.stringify({ email: email2, password: password2 })
4804
4857
  });
4805
4858
  const json = await response.json().catch(() => null);
4806
4859
  return { ok: response.ok, status: response.status, data: json };
@@ -4808,11 +4861,13 @@ async function loginViaApi(page, email, password) {
4808
4861
  return { ok: false, error: e.message };
4809
4862
  }
4810
4863
  },
4811
- { email, password: hashedPassword, signinUrl, requestId }
4864
+ { email, password: hashedPassword, fallbackUrl, requestId }
4812
4865
  ).catch(() => null);
4813
- if (evalRes?.data) {
4814
- data = evalRes.data;
4815
- signinSuccess = Boolean(data && (data.success === true || data.token));
4866
+ if (fallbackRes?.data) {
4867
+ data = fallbackRes.data;
4868
+ signinSuccess = Boolean(
4869
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4870
+ );
4816
4871
  }
4817
4872
  }
4818
4873
  if (data?.success === false) {
@@ -4826,7 +4881,8 @@ async function loginViaApi(page, email, password) {
4826
4881
  };
4827
4882
  }
4828
4883
  if (signinSuccess) {
4829
- const token = data?.data?.token || data?.token;
4884
+ const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
4885
+ const refreshToken = data?.data?.refresh_token || data?.refresh_token;
4830
4886
  if (token) {
4831
4887
  try {
4832
4888
  await page.context().addCookies([
@@ -4850,18 +4906,23 @@ async function loginViaApi(page, email, password) {
4850
4906
  }).catch(() => {
4851
4907
  });
4852
4908
  if (token) {
4853
- await page.evaluate((tok) => {
4854
- try {
4855
- localStorage.removeItem("qwen_token_logged_out_marker");
4856
- localStorage.setItem("token", tok);
4857
- document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4858
- } catch {
4859
- }
4860
- }, token).catch(() => {
4909
+ await page.evaluate(
4910
+ ({ tok, rTok }) => {
4911
+ try {
4912
+ localStorage.removeItem("qwen_token_logged_out_marker");
4913
+ localStorage.setItem("token", tok);
4914
+ localStorage.setItem("access_token", tok);
4915
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4916
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4917
+ } catch {
4918
+ }
4919
+ },
4920
+ { tok: token, rTok: refreshToken }
4921
+ ).catch(() => {
4861
4922
  });
4862
4923
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4863
4924
  });
4864
- return { success: true };
4925
+ return { success: true, token, refreshToken };
4865
4926
  }
4866
4927
  }
4867
4928
  return {
@@ -4885,11 +4946,23 @@ async function loginViaApi(page, email, password) {
4885
4946
  }
4886
4947
  async function loginViaUi(page, email, password) {
4887
4948
  try {
4888
- await page.goto(qwenUrl("/auth"), {
4889
- waitUntil: "domcontentloaded",
4890
- timeout: config.timeouts.navigation
4891
- });
4892
- await sleep2(1500);
4949
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
4950
+ return { success: true };
4951
+ }
4952
+ try {
4953
+ await page.goto(qwenUrl("/auth"), {
4954
+ waitUntil: "domcontentloaded",
4955
+ timeout: Math.min(config.timeouts.navigation, 15e3)
4956
+ });
4957
+ await sleep2(1e3);
4958
+ } catch (gotoErr) {
4959
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4960
+ return { success: true };
4961
+ }
4962
+ }
4963
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4964
+ return { success: true };
4965
+ }
4893
4966
  const emailSelector = [
4894
4967
  'input[type="email"]',
4895
4968
  'input[name="email"]',
@@ -4899,7 +4972,7 @@ async function loginViaUi(page, email, password) {
4899
4972
  ].join(", ");
4900
4973
  try {
4901
4974
  await page.waitForSelector(emailSelector, {
4902
- timeout: Math.min(config.timeouts.page, 8e3)
4975
+ timeout: Math.min(config.timeouts.page, 5e3)
4903
4976
  });
4904
4977
  } catch {
4905
4978
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
@@ -4912,30 +4985,55 @@ async function loginViaUi(page, email, password) {
4912
4985
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4913
4986
  }
4914
4987
  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();
4988
+ const isPasswordInputPresent = async () => {
4989
+ return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
4919
4990
  };
4920
- try {
4921
- const pwdModeBtn = findPasswordModeButton();
4922
- if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4923
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4991
+ const revealPasswordMode = async () => {
4992
+ if (page.url().includes("/forget")) {
4993
+ await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
4924
4994
  });
4925
- await sleep2(500);
4995
+ await sleep2(1e3);
4926
4996
  }
4927
- } catch {
4928
- }
4929
- await page.fill(emailSelector, email);
4930
- await sleep2(300);
4931
- try {
4932
- const pwdModeBtn = findPasswordModeButton();
4933
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4934
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4997
+ if (await isPasswordInputPresent()) return;
4998
+ const btn = page.locator("button").filter({
4999
+ 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
5000
+ }).filter({
5001
+ hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
5002
+ }).first();
5003
+ if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
5004
+ await btn.click({ timeout: 2e3 }).catch(() => {
4935
5005
  });
4936
5006
  await sleep2(500);
4937
5007
  }
5008
+ if (await isPasswordInputPresent()) return;
5009
+ await page.evaluate(() => {
5010
+ try {
5011
+ const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
5012
+ for (const b of buttons) {
5013
+ const txt = (b.innerText || "").trim().toLowerCase();
5014
+ 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")) {
5015
+ b.click();
5016
+ return true;
5017
+ }
5018
+ }
5019
+ } catch {
5020
+ }
5021
+ return false;
5022
+ }).catch(() => false);
5023
+ await sleep2(500);
5024
+ };
5025
+ await revealPasswordMode();
5026
+ try {
5027
+ await page.fill(emailSelector, email, { timeout: 8e3 });
5028
+ await sleep2(300);
4938
5029
  } catch {
5030
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5031
+ return { success: true };
5032
+ }
5033
+ throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5034
+ }
5035
+ if (!await isPasswordInputPresent()) {
5036
+ await revealPasswordMode();
4939
5037
  }
4940
5038
  const passwordSelector = 'input[type="password"], input[name="password"]';
4941
5039
  try {
@@ -4954,17 +5052,50 @@ async function loginViaUi(page, email, password) {
4954
5052
  reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
4955
5053
  };
4956
5054
  }
4957
- await page.fill(passwordSelector, password);
4958
- await sleep2(500);
5055
+ try {
5056
+ await page.fill(passwordSelector, password, { timeout: 8e3 });
5057
+ await sleep2(500);
5058
+ } catch {
5059
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
5060
+ return { success: true };
5061
+ }
5062
+ throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
5063
+ }
5064
+ await page.evaluate(() => {
5065
+ try {
5066
+ const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
5067
+ const passEl = document.querySelector('input[type="password"], input[name="password"]');
5068
+ if (emailEl) {
5069
+ emailEl.dispatchEvent(new Event("input", { bubbles: true }));
5070
+ emailEl.dispatchEvent(new Event("change", { bubbles: true }));
5071
+ emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
5072
+ }
5073
+ if (passEl) {
5074
+ passEl.dispatchEvent(new Event("input", { bubbles: true }));
5075
+ passEl.dispatchEvent(new Event("change", { bubbles: true }));
5076
+ passEl.dispatchEvent(new Event("blur", { bubbles: true }));
5077
+ }
5078
+ } catch {
5079
+ }
5080
+ }).catch(() => {
5081
+ });
4959
5082
  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")';
4960
5083
  const submitButton = page.locator(submitSelector).first();
4961
5084
  try {
4962
5085
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
4963
- timeout: 5e3
5086
+ timeout: 4e3
4964
5087
  });
4965
5088
  await submitButton.click();
4966
5089
  } catch {
4967
- await page.keyboard.press("Enter");
5090
+ const isDisabled = await submitButton.isDisabled().catch(() => true);
5091
+ if (!isDisabled) {
5092
+ await submitButton.click({ force: true }).catch(() => {
5093
+ });
5094
+ } else {
5095
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
5096
+ return { success: true };
5097
+ }
5098
+ }
4968
5099
  }
4969
5100
  const postSubmitDeadline = Date.now() + 15e3;
4970
5101
  while (Date.now() < postSubmitDeadline) {
@@ -5034,6 +5165,137 @@ async function loginViaUi(page, email, password) {
5034
5165
  return { success: false, reason: err?.message || String(err) };
5035
5166
  }
5036
5167
  }
5168
+ async function refreshAccountToken(accountId) {
5169
+ const page = accountPages.get(accountId);
5170
+ if (!page || page.isClosed()) {
5171
+ return { success: false, error: "Account page not active" };
5172
+ }
5173
+ const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5174
+ const session = getValidAuthSession2(accountId);
5175
+ let refreshToken = session?.refreshToken;
5176
+ if (!refreshToken) {
5177
+ try {
5178
+ refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
5179
+ } catch {
5180
+ }
5181
+ }
5182
+ if (refreshToken) {
5183
+ try {
5184
+ const refreshResult = await page.evaluate(
5185
+ async ({ rTok }) => {
5186
+ try {
5187
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5188
+ method: "GET",
5189
+ credentials: "include",
5190
+ headers: {
5191
+ accept: "application/json, text/plain, */*",
5192
+ source: "web",
5193
+ version: "0.3.11",
5194
+ "x-request-origin": "https://chat.qwen.ai",
5195
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5196
+ authorization: `Bearer ${rTok}`
5197
+ }
5198
+ });
5199
+ const json2 = await response.json().catch(() => null);
5200
+ return { ok: response.ok, status: response.status, data: json2 };
5201
+ } catch (e) {
5202
+ return { ok: false, error: e.message };
5203
+ }
5204
+ },
5205
+ { rTok: refreshToken }
5206
+ );
5207
+ const json = refreshResult?.data;
5208
+ if (json && json.success === true && json.data) {
5209
+ const newAccessToken = json.data.access_token || json.data.token;
5210
+ const newRefreshToken = json.data.refresh_token || refreshToken;
5211
+ if (newAccessToken) {
5212
+ await page.context().addCookies([
5213
+ {
5214
+ name: "token",
5215
+ value: newAccessToken,
5216
+ domain: ".qwen.ai",
5217
+ path: "/",
5218
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
5219
+ httpOnly: false,
5220
+ secure: true,
5221
+ sameSite: "Lax"
5222
+ }
5223
+ ]);
5224
+ await page.evaluate(
5225
+ ({ tok, rTok }) => {
5226
+ try {
5227
+ localStorage.removeItem("qwen_token_logged_out_marker");
5228
+ localStorage.setItem("token", tok);
5229
+ localStorage.setItem("access_token", tok);
5230
+ if (rTok) localStorage.setItem("refresh_token", rTok);
5231
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
5232
+ } catch {
5233
+ }
5234
+ },
5235
+ { tok: newAccessToken, rTok: newRefreshToken }
5236
+ ).catch(() => {
5237
+ });
5238
+ const liveCookies = await page.context().cookies();
5239
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5240
+ const cache = getHeaderCache(accountId);
5241
+ cache.headers.cookie = cookieStr;
5242
+ cache.lastRefresh = Date.now();
5243
+ markAccountHeadersReady(accountId);
5244
+ saveAuthSession2(accountId, {
5245
+ cookie: cookieStr,
5246
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5247
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
5248
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5249
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5250
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
5251
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
5252
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
5253
+ version: cache.headers["version"] || session?.version,
5254
+ userId: session?.userId,
5255
+ tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
5256
+ refreshToken: newRefreshToken,
5257
+ capturedAt: Date.now()
5258
+ });
5259
+ return { success: true, token: newAccessToken };
5260
+ }
5261
+ }
5262
+ } catch {
5263
+ }
5264
+ }
5265
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5266
+ const creds = getAccountCredentials2(accountId);
5267
+ if (creds && creds.email && creds.password) {
5268
+ const apiResult = await loginViaApi(page, creds.email, creds.password);
5269
+ if (apiResult.success) {
5270
+ const liveCookies = await page.context().cookies();
5271
+ const tokenCookie = liveCookies.find((c) => c.name === "token");
5272
+ if (tokenCookie) {
5273
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5274
+ const cache = getHeaderCache(accountId);
5275
+ cache.headers.cookie = cookieStr;
5276
+ cache.lastRefresh = Date.now();
5277
+ markAccountHeadersReady(accountId);
5278
+ saveAuthSession2(accountId, {
5279
+ cookie: cookieStr,
5280
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5281
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
5282
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5283
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5284
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
5285
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
5286
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
5287
+ version: cache.headers["version"] || session?.version,
5288
+ userId: session?.userId,
5289
+ tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
5290
+ refreshToken: apiResult.refreshToken,
5291
+ capturedAt: Date.now()
5292
+ });
5293
+ return { success: true, token: tokenCookie.value };
5294
+ }
5295
+ }
5296
+ }
5297
+ return { success: false, error: "Could not refresh or re-authenticate token" };
5298
+ }
5037
5299
  async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
5038
5300
  const page = pageOverride ?? accountPages.get(accountId);
5039
5301
  if (!page || page.isClosed()) {
@@ -5550,6 +5812,10 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5550
5812
  }
5551
5813
  };
5552
5814
  if (forceReauth) {
5815
+ const refreshed = await refreshAccountToken(accountId);
5816
+ if (refreshed.success) {
5817
+ return;
5818
+ }
5553
5819
  await executeReauth();
5554
5820
  } else {
5555
5821
  try {
@@ -5984,19 +6250,15 @@ async function keepAlivePlaywrightAccount(accountId) {
5984
6250
  const now = Date.now();
5985
6251
  const currentUrl = page.url();
5986
6252
  const cookie = await getCookies(accountId);
5987
- if (cookie && isTokenExpiringSoon(cookie, 45)) {
6253
+ if (cookie && isTokenExpiringSoon(cookie, 5)) {
5988
6254
  console.log(
5989
- `\u{1F493} [SessionKeeper] Account ${accountId} token expires within 45m; proactively renewing session...`
6255
+ `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
5990
6256
  );
5991
- try {
5992
- await refreshHeadersInternal(accountId, config.timeouts.headers, true);
6257
+ const res = await refreshAccountToken(accountId);
6258
+ if (res.success) {
5993
6259
  lastKeepAliveNavigation.set(accountId, now);
5994
6260
  touchAccountActivity(accountId);
5995
6261
  return true;
5996
- } catch (err) {
5997
- console.warn(
5998
- `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
5999
- );
6000
6262
  }
6001
6263
  }
6002
6264
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
@@ -7362,6 +7624,16 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
7362
7624
  }
7363
7625
  }
7364
7626
  if (json?.data?.code === "Unauthorized" || json?.code === "Unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access")) {
7627
+ if (accountId) {
7628
+ try {
7629
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
7630
+ const refreshed = await refreshAccountToken2(accountId);
7631
+ if (refreshed.success) {
7632
+ return createQwenChatSession(headers, model, accountId, chatMode);
7633
+ }
7634
+ } catch {
7635
+ }
7636
+ }
7365
7637
  throw new QwenUpstreamError(
7366
7638
  `Qwen create chat unauthorized: ${raw.substring(0, 300)}`,
7367
7639
  "Unauthorized",
@@ -8057,16 +8329,28 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8057
8329
  });
8058
8330
  if (response2.status === 401) {
8059
8331
  try {
8060
- const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
8061
- method: "GET",
8062
- credentials: "include",
8063
- signal: AbortSignal.timeout(3e3)
8064
- });
8065
- if (refreshRes.status === 200) {
8332
+ const rTok = localStorage.getItem("refresh_token");
8333
+ if (rTok) {
8334
+ const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
8335
+ method: "GET",
8336
+ credentials: "include",
8337
+ headers: {
8338
+ accept: "application/json, text/plain, */*",
8339
+ source: "web",
8340
+ version: "0.3.11",
8341
+ "x-request-origin": "https://chat.qwen.ai",
8342
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
8343
+ authorization: `Bearer ${rTok}`
8344
+ },
8345
+ signal: AbortSignal.timeout(5e3)
8346
+ });
8066
8347
  const refreshJson = await refreshRes.json().catch(() => null);
8067
- if (refreshJson && refreshJson.success === true && refreshJson.data?.token) {
8068
- const freshTok = refreshJson.data.token;
8348
+ const freshTok = refreshJson?.data?.access_token || refreshJson?.data?.token;
8349
+ const nextRTok = refreshJson?.data?.refresh_token;
8350
+ if (refreshJson?.success === true && freshTok) {
8069
8351
  localStorage.setItem("token", freshTok);
8352
+ localStorage.setItem("access_token", freshTok);
8353
+ if (nextRTok) localStorage.setItem("refresh_token", nextRTok);
8070
8354
  document.cookie = `token=${encodeURIComponent(freshTok)}; path=/; domain=.qwen.ai; max-age=31536000`;
8071
8355
  if (headers2["authorization"] || headers2["Authorization"]) {
8072
8356
  headers2["authorization"] = `Bearer ${freshTok}`;
@@ -8104,13 +8388,29 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8104
8388
  }
8105
8389
  );
8106
8390
  const recoverOnTimeout = !options.noMutexRecovery;
8107
- const response = await withQwenBrowserPage(
8391
+ let response = await withQwenBrowserPage(
8108
8392
  accountId,
8109
8393
  evaluateRequest,
8110
8394
  void 0,
8111
8395
  options.timeoutMs,
8112
8396
  recoverOnTimeout
8113
8397
  );
8398
+ if (response.status === 401 && accountId) {
8399
+ try {
8400
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
8401
+ const refreshed = await refreshAccountToken2(accountId);
8402
+ if (refreshed.success) {
8403
+ response = await withQwenBrowserPage(
8404
+ accountId,
8405
+ evaluateRequest,
8406
+ void 0,
8407
+ options.timeoutMs,
8408
+ recoverOnTimeout
8409
+ );
8410
+ }
8411
+ } catch {
8412
+ }
8413
+ }
8114
8414
  return new Response(response.raw, {
8115
8415
  status: response.status,
8116
8416
  headers: response.contentType ? { "content-type": response.contentType } : void 0
@@ -8633,7 +8933,11 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
8633
8933
  );
8634
8934
  try {
8635
8935
  currentSettings = null;
8636
- const { headers: freshHeaders } = await getQwenHeaders(true, accountId, true);
8936
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
8937
+ if (accountId) {
8938
+ await refreshAccountToken2(accountId);
8939
+ }
8940
+ const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
8637
8941
  requestHeaders = buildCapturedQwenHeaders(freshHeaders, {
8638
8942
  referer: qwenUrl("/settings/personalization")
8639
8943
  });