qwenproxy-cli 1.4.7 → 1.5.0

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
  }
@@ -1912,7 +1915,15 @@ function defaultCooldownDurationMs() {
1912
1915
  return computeQuotaCooldownMs(Date.now());
1913
1916
  }
1914
1917
  function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
1918
+ if (cooldownMs !== void 0 && cooldownMs <= 0) {
1919
+ clearAccountCooldown(accountId);
1920
+ return;
1921
+ }
1915
1922
  const duration = cooldownMs ?? defaultCooldownDurationMs();
1923
+ if (duration <= 0) {
1924
+ clearAccountCooldown(accountId);
1925
+ return;
1926
+ }
1916
1927
  const until = Date.now() + duration;
1917
1928
  const cooldownReason = reason ?? "RateLimited";
1918
1929
  cooldowns.set(accountId, {
@@ -1935,6 +1946,19 @@ function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
1935
1946
  );
1936
1947
  }
1937
1948
  }
1949
+ function clearAccountCooldown(accountId) {
1950
+ cooldowns.delete(accountId);
1951
+ if (accountId !== "global") {
1952
+ try {
1953
+ updateAccountCooldown(accountId, 0, null);
1954
+ } catch (err) {
1955
+ console.error(
1956
+ `\u274C [AccountManager] Failed to clear cooldown in DB for ${accountId}:`,
1957
+ err.message
1958
+ );
1959
+ }
1960
+ }
1961
+ }
1938
1962
  function markAccountHeadersReady(accountId) {
1939
1963
  if (!accountId || accountId === "global") return;
1940
1964
  headersReadyAccounts.add(accountId);
@@ -3812,6 +3836,7 @@ async function loginToQwen(accountId, email, password) {
3812
3836
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
3813
3837
  version: cache.headers["version"] || void 0,
3814
3838
  tokenExpiresAt: exp || void 0,
3839
+ refreshToken: apiResult.refreshToken,
3815
3840
  capturedAt: Date.now()
3816
3841
  });
3817
3842
  }
@@ -3845,6 +3870,13 @@ async function loginToQwen(accountId, email, password) {
3845
3870
  markAccountHeadersReady(accountId);
3846
3871
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3847
3872
  const exp = parseJwtExpiry(tokenCookie.value);
3873
+ let rTok = uiResult.refreshToken;
3874
+ if (!rTok) {
3875
+ try {
3876
+ rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
3877
+ } catch {
3878
+ }
3879
+ }
3848
3880
  saveAuthSession2(accountId, {
3849
3881
  cookie: cookieStr,
3850
3882
  userAgent: cache.headers["user-agent"] || "",
@@ -3856,6 +3888,7 @@ async function loginToQwen(accountId, email, password) {
3856
3888
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
3857
3889
  version: cache.headers["version"] || void 0,
3858
3890
  tokenExpiresAt: exp || void 0,
3891
+ refreshToken: rTok,
3859
3892
  capturedAt: Date.now()
3860
3893
  });
3861
3894
  }
@@ -4034,7 +4067,7 @@ async function loginViaApi(page, email, password) {
4034
4067
  });
4035
4068
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4036
4069
  });
4037
- return { success: true };
4070
+ return { success: true, token, refreshToken };
4038
4071
  }
4039
4072
  }
4040
4073
  return {
@@ -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
  }
@@ -1975,7 +1978,15 @@ function defaultCooldownDurationMs() {
1975
1978
  return computeQuotaCooldownMs(Date.now());
1976
1979
  }
1977
1980
  function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
1981
+ if (cooldownMs !== void 0 && cooldownMs <= 0) {
1982
+ clearAccountCooldown(accountId);
1983
+ return;
1984
+ }
1978
1985
  const duration = cooldownMs ?? defaultCooldownDurationMs();
1986
+ if (duration <= 0) {
1987
+ clearAccountCooldown(accountId);
1988
+ return;
1989
+ }
1979
1990
  const until = Date.now() + duration;
1980
1991
  const cooldownReason = reason ?? "RateLimited";
1981
1992
  cooldowns.set(accountId, {
@@ -1998,6 +2009,19 @@ function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
1998
2009
  );
1999
2010
  }
2000
2011
  }
2012
+ function clearAccountCooldown(accountId) {
2013
+ cooldowns.delete(accountId);
2014
+ if (accountId !== "global") {
2015
+ try {
2016
+ updateAccountCooldown(accountId, 0, null);
2017
+ } catch (err) {
2018
+ console.error(
2019
+ `\u274C [AccountManager] Failed to clear cooldown in DB for ${accountId}:`,
2020
+ err.message
2021
+ );
2022
+ }
2023
+ }
2024
+ }
2001
2025
  function markAccountHeadersReady(accountId) {
2002
2026
  if (!accountId || accountId === "global") return;
2003
2027
  headersReadyAccounts.add(accountId);
@@ -2766,7 +2790,8 @@ function buildQwenRequestHeaders(opts) {
2766
2790
  "bx-v": opts.bxV || "2.5.37",
2767
2791
  source: "web",
2768
2792
  version: opts.version || getQwenWebVersion(),
2769
- timezone: opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
2793
+ timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
2794
+ Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
2770
2795
  // Use the real browser client-hints when captured (anti-hardcoded); fall
2771
2796
  // back to the static fingerprint otherwise.
2772
2797
  "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
@@ -3052,6 +3077,7 @@ __export(playwright_exports, {
3052
3077
  parseCookiesForBrowser: () => parseCookiesForBrowser,
3053
3078
  pruneAllPlaywrightProfiles: () => pruneAllPlaywrightProfiles,
3054
3079
  prunePlaywrightProfileCaches: () => prunePlaywrightProfileCaches,
3080
+ refreshAccountToken: () => refreshAccountToken,
3055
3081
  refreshHeaders: () => refreshHeaders,
3056
3082
  refreshHeadersWithProfileReset: () => refreshHeadersWithProfileReset,
3057
3083
  registerPlaywrightAccountForTests: () => registerPlaywrightAccountForTests,
@@ -4687,6 +4713,7 @@ async function loginToQwen(accountId, email, password) {
4687
4713
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
4688
4714
  version: cache.headers["version"] || void 0,
4689
4715
  tokenExpiresAt: exp || void 0,
4716
+ refreshToken: apiResult.refreshToken,
4690
4717
  capturedAt: Date.now()
4691
4718
  });
4692
4719
  }
@@ -4720,6 +4747,13 @@ async function loginToQwen(accountId, email, password) {
4720
4747
  markAccountHeadersReady(accountId);
4721
4748
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4722
4749
  const exp = parseJwtExpiry(tokenCookie.value);
4750
+ let rTok = uiResult.refreshToken;
4751
+ if (!rTok) {
4752
+ try {
4753
+ rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
4754
+ } catch {
4755
+ }
4756
+ }
4723
4757
  saveAuthSession2(accountId, {
4724
4758
  cookie: cookieStr,
4725
4759
  userAgent: cache.headers["user-agent"] || "",
@@ -4731,6 +4765,7 @@ async function loginToQwen(accountId, email, password) {
4731
4765
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
4732
4766
  version: cache.headers["version"] || void 0,
4733
4767
  tokenExpiresAt: exp || void 0,
4768
+ refreshToken: rTok,
4734
4769
  capturedAt: Date.now()
4735
4770
  });
4736
4771
  }
@@ -4909,7 +4944,7 @@ async function loginViaApi(page, email, password) {
4909
4944
  });
4910
4945
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4911
4946
  });
4912
- return { success: true };
4947
+ return { success: true, token, refreshToken };
4913
4948
  }
4914
4949
  }
4915
4950
  return {
@@ -5152,6 +5187,137 @@ async function loginViaUi(page, email, password) {
5152
5187
  return { success: false, reason: err?.message || String(err) };
5153
5188
  }
5154
5189
  }
5190
+ async function refreshAccountToken(accountId) {
5191
+ const page = accountPages.get(accountId);
5192
+ if (!page || page.isClosed()) {
5193
+ return { success: false, error: "Account page not active" };
5194
+ }
5195
+ const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5196
+ const session = getValidAuthSession2(accountId);
5197
+ let refreshToken = session?.refreshToken;
5198
+ if (!refreshToken) {
5199
+ try {
5200
+ refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
5201
+ } catch {
5202
+ }
5203
+ }
5204
+ if (refreshToken) {
5205
+ try {
5206
+ const refreshResult = await page.evaluate(
5207
+ async ({ rTok }) => {
5208
+ try {
5209
+ const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5210
+ method: "GET",
5211
+ credentials: "include",
5212
+ headers: {
5213
+ accept: "application/json, text/plain, */*",
5214
+ source: "web",
5215
+ version: "0.3.11",
5216
+ "x-request-origin": "https://chat.qwen.ai",
5217
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5218
+ authorization: `Bearer ${rTok}`
5219
+ }
5220
+ });
5221
+ const json2 = await response.json().catch(() => null);
5222
+ return { ok: response.ok, status: response.status, data: json2 };
5223
+ } catch (e) {
5224
+ return { ok: false, error: e.message };
5225
+ }
5226
+ },
5227
+ { rTok: refreshToken }
5228
+ );
5229
+ const json = refreshResult?.data;
5230
+ if (json && json.success === true && json.data) {
5231
+ const newAccessToken = json.data.access_token || json.data.token;
5232
+ const newRefreshToken = json.data.refresh_token || refreshToken;
5233
+ if (newAccessToken) {
5234
+ await page.context().addCookies([
5235
+ {
5236
+ name: "token",
5237
+ value: newAccessToken,
5238
+ domain: ".qwen.ai",
5239
+ path: "/",
5240
+ expires: Math.floor(Date.now() / 1e3) + 31536e3,
5241
+ httpOnly: false,
5242
+ secure: true,
5243
+ sameSite: "Lax"
5244
+ }
5245
+ ]);
5246
+ await page.evaluate(
5247
+ ({ tok, rTok }) => {
5248
+ try {
5249
+ localStorage.removeItem("qwen_token_logged_out_marker");
5250
+ localStorage.setItem("token", tok);
5251
+ localStorage.setItem("access_token", tok);
5252
+ if (rTok) localStorage.setItem("refresh_token", rTok);
5253
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
5254
+ } catch {
5255
+ }
5256
+ },
5257
+ { tok: newAccessToken, rTok: newRefreshToken }
5258
+ ).catch(() => {
5259
+ });
5260
+ const liveCookies = await page.context().cookies();
5261
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5262
+ const cache = getHeaderCache(accountId);
5263
+ cache.headers.cookie = cookieStr;
5264
+ cache.lastRefresh = Date.now();
5265
+ markAccountHeadersReady(accountId);
5266
+ saveAuthSession2(accountId, {
5267
+ cookie: cookieStr,
5268
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5269
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
5270
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5271
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5272
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
5273
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
5274
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
5275
+ version: cache.headers["version"] || session?.version,
5276
+ userId: session?.userId,
5277
+ tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
5278
+ refreshToken: newRefreshToken,
5279
+ capturedAt: Date.now()
5280
+ });
5281
+ return { success: true, token: newAccessToken };
5282
+ }
5283
+ }
5284
+ } catch {
5285
+ }
5286
+ }
5287
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5288
+ const creds = getAccountCredentials2(accountId);
5289
+ if (creds && creds.email && creds.password) {
5290
+ const apiResult = await loginViaApi(page, creds.email, creds.password);
5291
+ if (apiResult.success) {
5292
+ const liveCookies = await page.context().cookies();
5293
+ const tokenCookie = liveCookies.find((c) => c.name === "token");
5294
+ if (tokenCookie) {
5295
+ const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5296
+ const cache = getHeaderCache(accountId);
5297
+ cache.headers.cookie = cookieStr;
5298
+ cache.lastRefresh = Date.now();
5299
+ markAccountHeadersReady(accountId);
5300
+ saveAuthSession2(accountId, {
5301
+ cookie: cookieStr,
5302
+ userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5303
+ bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
5304
+ bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5305
+ bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5306
+ secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
5307
+ secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
5308
+ secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
5309
+ version: cache.headers["version"] || session?.version,
5310
+ userId: session?.userId,
5311
+ tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
5312
+ refreshToken: apiResult.refreshToken,
5313
+ capturedAt: Date.now()
5314
+ });
5315
+ return { success: true, token: tokenCookie.value };
5316
+ }
5317
+ }
5318
+ }
5319
+ return { success: false, error: "Could not refresh or re-authenticate token" };
5320
+ }
5155
5321
  async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
5156
5322
  const page = pageOverride ?? accountPages.get(accountId);
5157
5323
  if (!page || page.isClosed()) {
@@ -5668,16 +5834,9 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5668
5834
  }
5669
5835
  };
5670
5836
  if (forceReauth) {
5671
- const alreadyIn = await isPageLoggedIn(page, 2e3).catch(() => false);
5672
- if (alreadyIn) {
5673
- const liveCookies = await page.context().cookies();
5674
- if (liveCookies.some((c) => c.name === "token")) {
5675
- const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
5676
- cache.headers.cookie = cookieStr;
5677
- cache.lastRefresh = Date.now();
5678
- markAccountHeadersReady(accountId);
5679
- return;
5680
- }
5837
+ const refreshed = await refreshAccountToken(accountId);
5838
+ if (refreshed.success) {
5839
+ return;
5681
5840
  }
5682
5841
  await executeReauth();
5683
5842
  } else {
@@ -6113,50 +6272,15 @@ async function keepAlivePlaywrightAccount(accountId) {
6113
6272
  const now = Date.now();
6114
6273
  const currentUrl = page.url();
6115
6274
  const cookie = await getCookies(accountId);
6116
- if (cookie && isTokenExpiringSoon(cookie, 2)) {
6117
- try {
6118
- await page.evaluate(async () => {
6119
- await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
6120
- method: "GET",
6121
- credentials: "include"
6122
- }).catch(() => {
6123
- });
6124
- await fetch("/api/v1/auths/", {
6125
- method: "GET",
6126
- credentials: "include"
6127
- }).catch(() => {
6128
- });
6129
- }).catch(() => {
6130
- });
6131
- const liveCookies = await page.context().cookies();
6132
- const tokenCookie = liveCookies.find((c) => c.name === "token");
6133
- if (tokenCookie) {
6134
- const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
6135
- const cache = getHeaderCache(accountId);
6136
- cache.headers.cookie = cookieStr;
6137
- cache.lastRefresh = Date.now();
6138
- markAccountHeadersReady(accountId);
6139
- lastKeepAliveNavigation.set(accountId, now);
6140
- touchAccountActivity(accountId);
6141
- return true;
6142
- }
6143
- } catch {
6144
- }
6145
- const loggedIn = await isPageLoggedIn(page, 2e3).catch(() => false);
6146
- if (!loggedIn) {
6147
- console.log(
6148
- `\u{1F493} [SessionKeeper] Account ${accountId} session expired; renewing session...`
6149
- );
6150
- try {
6151
- await refreshHeadersInternal(accountId, config.timeouts.headers, true);
6152
- lastKeepAliveNavigation.set(accountId, now);
6153
- touchAccountActivity(accountId);
6154
- return true;
6155
- } catch (err) {
6156
- console.warn(
6157
- `[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
6158
- );
6159
- }
6275
+ if (cookie && isTokenExpiringSoon(cookie, 5)) {
6276
+ console.log(
6277
+ `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6278
+ );
6279
+ const res = await refreshAccountToken(accountId);
6280
+ if (res.success) {
6281
+ lastKeepAliveNavigation.set(accountId, now);
6282
+ touchAccountActivity(accountId);
6283
+ return true;
6160
6284
  }
6161
6285
  }
6162
6286
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
@@ -7521,7 +7645,19 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
7521
7645
  } catch {
7522
7646
  }
7523
7647
  }
7524
- if (json?.data?.code === "Unauthorized" || json?.code === "Unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access")) {
7648
+ const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
7649
+ if (isUnauthorized) {
7650
+ if (accountId) {
7651
+ try {
7652
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
7653
+ const refreshed = await refreshAccountToken2(accountId);
7654
+ if (refreshed.success) {
7655
+ const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
7656
+ return createQwenChatSession(freshHeaders, model, accountId, chatMode);
7657
+ }
7658
+ } catch {
7659
+ }
7660
+ }
7525
7661
  throw new QwenUpstreamError(
7526
7662
  `Qwen create chat unauthorized: ${raw.substring(0, 300)}`,
7527
7663
  "Unauthorized",
@@ -8217,16 +8353,28 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8217
8353
  });
8218
8354
  if (response2.status === 401) {
8219
8355
  try {
8220
- const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
8221
- method: "GET",
8222
- credentials: "include",
8223
- signal: AbortSignal.timeout(3e3)
8224
- });
8225
- if (refreshRes.status === 200) {
8356
+ const rTok = localStorage.getItem("refresh_token");
8357
+ if (rTok) {
8358
+ const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
8359
+ method: "GET",
8360
+ credentials: "include",
8361
+ headers: {
8362
+ accept: "application/json, text/plain, */*",
8363
+ source: "web",
8364
+ version: "0.3.11",
8365
+ "x-request-origin": "https://chat.qwen.ai",
8366
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
8367
+ authorization: `Bearer ${rTok}`
8368
+ },
8369
+ signal: AbortSignal.timeout(5e3)
8370
+ });
8226
8371
  const refreshJson = await refreshRes.json().catch(() => null);
8227
- if (refreshJson && refreshJson.success === true && refreshJson.data?.token) {
8228
- const freshTok = refreshJson.data.token;
8372
+ const freshTok = refreshJson?.data?.access_token || refreshJson?.data?.token;
8373
+ const nextRTok = refreshJson?.data?.refresh_token;
8374
+ if (refreshJson?.success === true && freshTok) {
8229
8375
  localStorage.setItem("token", freshTok);
8376
+ localStorage.setItem("access_token", freshTok);
8377
+ if (nextRTok) localStorage.setItem("refresh_token", nextRTok);
8230
8378
  document.cookie = `token=${encodeURIComponent(freshTok)}; path=/; domain=.qwen.ai; max-age=31536000`;
8231
8379
  if (headers2["authorization"] || headers2["Authorization"]) {
8232
8380
  headers2["authorization"] = `Bearer ${freshTok}`;
@@ -8264,13 +8412,29 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8264
8412
  }
8265
8413
  );
8266
8414
  const recoverOnTimeout = !options.noMutexRecovery;
8267
- const response = await withQwenBrowserPage(
8415
+ let response = await withQwenBrowserPage(
8268
8416
  accountId,
8269
8417
  evaluateRequest,
8270
8418
  void 0,
8271
8419
  options.timeoutMs,
8272
8420
  recoverOnTimeout
8273
8421
  );
8422
+ if (response.status === 401 && accountId) {
8423
+ try {
8424
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
8425
+ const refreshed = await refreshAccountToken2(accountId);
8426
+ if (refreshed.success) {
8427
+ response = await withQwenBrowserPage(
8428
+ accountId,
8429
+ evaluateRequest,
8430
+ void 0,
8431
+ options.timeoutMs,
8432
+ recoverOnTimeout
8433
+ );
8434
+ }
8435
+ } catch {
8436
+ }
8437
+ }
8274
8438
  return new Response(response.raw, {
8275
8439
  status: response.status,
8276
8440
  headers: response.contentType ? { "content-type": response.contentType } : void 0
@@ -8793,7 +8957,11 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
8793
8957
  );
8794
8958
  try {
8795
8959
  currentSettings = null;
8796
- const { headers: freshHeaders } = await getQwenHeaders(true, accountId, true);
8960
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
8961
+ if (accountId) {
8962
+ await refreshAccountToken2(accountId);
8963
+ }
8964
+ const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
8797
8965
  requestHeaders = buildCapturedQwenHeaders(freshHeaders, {
8798
8966
  referer: qwenUrl("/settings/personalization")
8799
8967
  });
@@ -9479,8 +9647,18 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
9479
9647
  );
9480
9648
  }
9481
9649
  };
9650
+ let forceRefresh = options?.forceNewChat === true;
9651
+ if (!forceRefresh && accountId) {
9652
+ try {
9653
+ const basic = await getBasicHeaders2(accountId);
9654
+ if (isTokenExpiringSoon(basic.cookie, 3)) {
9655
+ forceRefresh = true;
9656
+ }
9657
+ } catch {
9658
+ }
9659
+ }
9482
9660
  const captured = await getQwenHeaders(
9483
- options?.forceNewChat === true,
9661
+ forceRefresh,
9484
9662
  accountId
9485
9663
  );
9486
9664
  ensureNotAborted();
@@ -9494,7 +9672,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
9494
9672
  if (options && "chatSessionId" in options) {
9495
9673
  if (options.chatSessionId === null || options.chatSessionId === "") {
9496
9674
  const acquired = await acquireNewQwenChatSession(
9497
- headers,
9675
+ activeHeaders,
9498
9676
  model,
9499
9677
  accountId,
9500
9678
  options?.chatMode ?? "thread"
@@ -9502,6 +9680,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
9502
9680
  chatSessionId = acquired.chatId;
9503
9681
  leasedWarmChat = acquired.leasedFromPool;
9504
9682
  createdNewChat = true;
9683
+ try {
9684
+ const fresh = await getQwenHeaders(false, accountId);
9685
+ activeHeaders = fresh.headers;
9686
+ } catch {
9687
+ }
9505
9688
  } else {
9506
9689
  chatSessionId = options.chatSessionId;
9507
9690
  }
@@ -9509,7 +9692,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
9509
9692
  chatSessionId = captured.chatSessionId;
9510
9693
  if (!chatSessionId) {
9511
9694
  const acquired = await acquireNewQwenChatSession(
9512
- headers,
9695
+ activeHeaders,
9513
9696
  model,
9514
9697
  accountId,
9515
9698
  options?.chatMode ?? "thread"
@@ -9517,6 +9700,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
9517
9700
  chatSessionId = acquired.chatId;
9518
9701
  leasedWarmChat = acquired.leasedFromPool;
9519
9702
  createdNewChat = true;
9703
+ try {
9704
+ const fresh = await getQwenHeaders(false, accountId);
9705
+ activeHeaders = fresh.headers;
9706
+ } catch {
9707
+ }
9520
9708
  }
9521
9709
  }
9522
9710
  ensureNotAborted();