qwenproxy-cli 1.6.3 → 1.6.5

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.
@@ -1814,6 +1814,7 @@ var init_mutex = __esm({
1814
1814
  locked = false;
1815
1815
  lockedAt = 0;
1816
1816
  lockedByKey = "";
1817
+ epoch = 0;
1817
1818
  async acquire(timeoutMs = 3e5, key = "") {
1818
1819
  const holdLimitMs = this.maxHoldMs;
1819
1820
  if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
@@ -1824,6 +1825,7 @@ var init_mutex = __esm({
1824
1825
  this.locked = false;
1825
1826
  this.lockedAt = 0;
1826
1827
  this.lockedByKey = "";
1828
+ this.epoch++;
1827
1829
  }
1828
1830
  if (!this.locked) {
1829
1831
  this.locked = true;
@@ -1862,10 +1864,12 @@ var init_mutex = __esm({
1862
1864
  }
1863
1865
  }
1864
1866
  createRelease() {
1867
+ const epoch = this.epoch;
1865
1868
  let released = false;
1866
1869
  return () => {
1867
1870
  if (released) return;
1868
1871
  released = true;
1872
+ if (epoch !== this.epoch) return;
1869
1873
  this.release();
1870
1874
  };
1871
1875
  }
@@ -2673,6 +2677,45 @@ var init_waf_isolation = __esm({
2673
2677
  }
2674
2678
  });
2675
2679
 
2680
+ // src/utils/timezone.ts
2681
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2682
+ const dayName = DAYS[date.getDay()];
2683
+ const monthName = MONTHS[date.getMonth()];
2684
+ const day = String(date.getDate()).padStart(2, "0");
2685
+ const year = date.getFullYear();
2686
+ const hours = String(date.getHours()).padStart(2, "0");
2687
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2688
+ const seconds = String(date.getSeconds()).padStart(2, "0");
2689
+ const offsetMinutes = -date.getTimezoneOffset();
2690
+ const sign = offsetMinutes >= 0 ? "+" : "-";
2691
+ const absOffset = Math.abs(offsetMinutes);
2692
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2693
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
2694
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2695
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2696
+ }
2697
+ var DAYS, MONTHS;
2698
+ var init_timezone = __esm({
2699
+ "src/utils/timezone.ts"() {
2700
+ "use strict";
2701
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2702
+ MONTHS = [
2703
+ "Jan",
2704
+ "Feb",
2705
+ "Mar",
2706
+ "Apr",
2707
+ "May",
2708
+ "Jun",
2709
+ "Jul",
2710
+ "Aug",
2711
+ "Sep",
2712
+ "Oct",
2713
+ "Nov",
2714
+ "Dec"
2715
+ ];
2716
+ }
2717
+ });
2718
+
2676
2719
  // src/services/qwen-headers.ts
2677
2720
  import { v4 as uuidv4 } from "uuid";
2678
2721
  function getDefaultQwenUserAgent(major = getChromeMajor()) {
@@ -2690,6 +2733,7 @@ var init_qwen_headers = __esm({
2690
2733
  init_qwen_url();
2691
2734
  init_config();
2692
2735
  init_fingerprint();
2736
+ init_timezone();
2693
2737
  dynamicWebVersion = null;
2694
2738
  QWEN_WEB_VERSION = config.qwen.webVersion;
2695
2739
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
@@ -2841,9 +2885,10 @@ function getAccountMutex(accountId) {
2841
2885
  async function recoverStuckAccountMutex(accountId, mutex, key) {
2842
2886
  if (accountMutexes.get(accountId) !== mutex) return;
2843
2887
  const lockState = mutex.state();
2844
- if (lockState.heldForMs < PLAYWRIGHT_MUTEX_WAIT_MS || lockState.heldBy.startsWith("init:") || isAccountServingStream(accountId)) {
2888
+ const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
2889
+ if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
2845
2890
  logger.warn(
2846
- `[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${PLAYWRIGHT_MUTEX_WAIT_MS}ms | servingStream=${isAccountServingStream(accountId)}`
2891
+ `[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
2847
2892
  );
2848
2893
  return;
2849
2894
  }
@@ -3757,6 +3802,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
3757
3802
  }
3758
3803
  }
3759
3804
  }
3805
+ const currentCache = getHeaderCache(account.id);
3806
+ const currentCookie = currentCache?.headers?.cookie || "";
3807
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
3808
+ try {
3809
+ const refreshRes = await refreshAccountToken(account.id);
3810
+ if (refreshRes.success) {
3811
+ console.log(
3812
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
3813
+ );
3814
+ }
3815
+ } catch (err) {
3816
+ console.warn(
3817
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
3818
+ );
3819
+ }
3820
+ }
3760
3821
  return;
3761
3822
  }
3762
3823
  const cookies = await acctContext.cookies();
@@ -4014,7 +4075,7 @@ async function loginViaApi(page, email, password) {
4014
4075
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4015
4076
  const requestId = crypto3.randomUUID();
4016
4077
  const evalRes = await page.evaluate(
4017
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4078
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
4018
4079
  try {
4019
4080
  const response = await fetch(signinUrl2, {
4020
4081
  method: "POST",
@@ -4026,7 +4087,7 @@ async function loginViaApi(page, email, password) {
4026
4087
  source: "web",
4027
4088
  version: "0.3.11",
4028
4089
  "x-request-origin": "https://chat.qwen.ai",
4029
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4090
+ timezone: tz,
4030
4091
  "x-request-id": requestId2
4031
4092
  },
4032
4093
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4037,7 +4098,7 @@ async function loginViaApi(page, email, password) {
4037
4098
  return { ok: false, error: e.message };
4038
4099
  }
4039
4100
  },
4040
- { email, password: hashedPassword, signinUrl, requestId }
4101
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
4041
4102
  ).catch(() => null);
4042
4103
  let data = evalRes?.data;
4043
4104
  let signinSuccess = Boolean(
@@ -4046,7 +4107,7 @@ async function loginViaApi(page, email, password) {
4046
4107
  if (!signinSuccess && !data?.data?.code) {
4047
4108
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4048
4109
  const fallbackRes = await page.evaluate(
4049
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4110
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
4050
4111
  try {
4051
4112
  const response = await fetch(fallbackUrl2, {
4052
4113
  method: "POST",
@@ -4057,7 +4118,7 @@ async function loginViaApi(page, email, password) {
4057
4118
  "content-type": "application/json",
4058
4119
  source: "web",
4059
4120
  version: "0.3.11",
4060
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4121
+ timezone: tz,
4061
4122
  "x-request-id": requestId2
4062
4123
  },
4063
4124
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4068,7 +4129,7 @@ async function loginViaApi(page, email, password) {
4068
4129
  return { ok: false, error: e.message };
4069
4130
  }
4070
4131
  },
4071
- { email, password: hashedPassword, fallbackUrl, requestId }
4132
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
4072
4133
  ).catch(() => null);
4073
4134
  if (fallbackRes?.data) {
4074
4135
  data = fallbackRes.data;
@@ -4400,7 +4461,7 @@ async function refreshAccountToken(accountId) {
4400
4461
  if (refreshToken) {
4401
4462
  try {
4402
4463
  const refreshResult = await page.evaluate(
4403
- async ({ rTok }) => {
4464
+ async ({ rTok, tz }) => {
4404
4465
  try {
4405
4466
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4406
4467
  method: "GET",
@@ -4410,7 +4471,7 @@ async function refreshAccountToken(accountId) {
4410
4471
  source: "web",
4411
4472
  version: "0.3.11",
4412
4473
  "x-request-origin": "https://chat.qwen.ai",
4413
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4474
+ timezone: tz,
4414
4475
  authorization: `Bearer ${rTok}`
4415
4476
  }
4416
4477
  });
@@ -4420,7 +4481,7 @@ async function refreshAccountToken(accountId) {
4420
4481
  return { ok: false, error: e.message };
4421
4482
  }
4422
4483
  },
4423
- { rTok: refreshToken }
4484
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
4424
4485
  );
4425
4486
  const json = refreshResult?.data;
4426
4487
  if (json && json.success === true && json.data) {
@@ -4458,6 +4519,7 @@ async function refreshAccountToken(accountId) {
4458
4519
  const cache = getHeaderCache(accountId);
4459
4520
  cache.headers.cookie = cookieStr;
4460
4521
  cache.lastRefresh = Date.now();
4522
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4461
4523
  markAccountHeadersReady(accountId);
4462
4524
  saveAuthSession2(accountId, {
4463
4525
  cookie: cookieStr,
@@ -4492,6 +4554,7 @@ async function refreshAccountToken(accountId) {
4492
4554
  const cache = getHeaderCache(accountId);
4493
4555
  cache.headers.cookie = cookieStr;
4494
4556
  cache.lastRefresh = Date.now();
4557
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4495
4558
  markAccountHeadersReady(accountId);
4496
4559
  saveAuthSession2(accountId, {
4497
4560
  cookie: cookieStr,
@@ -5372,6 +5435,7 @@ var init_playwright = __esm({
5372
5435
  init_qwen_headers();
5373
5436
  init_paths();
5374
5437
  init_jwt();
5438
+ init_timezone();
5375
5439
  requireLocal = createRequire(import.meta.url);
5376
5440
  contextInitHooks = [];
5377
5441
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -5381,7 +5445,7 @@ var init_playwright = __esm({
5381
5445
  inFlightAccountInits = /* @__PURE__ */ new Map();
5382
5446
  headerCaches = /* @__PURE__ */ new Map();
5383
5447
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
5384
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
5448
+ COOKIE_CACHE_TTL = 60 * 1e3;
5385
5449
  cookieCaches = /* @__PURE__ */ new Map();
5386
5450
  lastAccountActivity = /* @__PURE__ */ new Map();
5387
5451
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();