qwenproxy-cli 1.6.4 → 1.6.6

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
@@ -1892,6 +1892,7 @@ var init_mutex = __esm({
1892
1892
  locked = false;
1893
1893
  lockedAt = 0;
1894
1894
  lockedByKey = "";
1895
+ epoch = 0;
1895
1896
  async acquire(timeoutMs = 3e5, key = "") {
1896
1897
  const holdLimitMs = this.maxHoldMs;
1897
1898
  if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
@@ -1902,6 +1903,7 @@ var init_mutex = __esm({
1902
1903
  this.locked = false;
1903
1904
  this.lockedAt = 0;
1904
1905
  this.lockedByKey = "";
1906
+ this.epoch++;
1905
1907
  }
1906
1908
  if (!this.locked) {
1907
1909
  this.locked = true;
@@ -1940,10 +1942,12 @@ var init_mutex = __esm({
1940
1942
  }
1941
1943
  }
1942
1944
  createRelease() {
1945
+ const epoch = this.epoch;
1943
1946
  let released = false;
1944
1947
  return () => {
1945
1948
  if (released) return;
1946
1949
  released = true;
1950
+ if (epoch !== this.epoch) return;
1947
1951
  this.release();
1948
1952
  };
1949
1953
  }
@@ -2934,6 +2938,45 @@ var init_waf_isolation = __esm({
2934
2938
  }
2935
2939
  });
2936
2940
 
2941
+ // src/utils/timezone.ts
2942
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2943
+ const dayName = DAYS[date.getDay()];
2944
+ const monthName = MONTHS[date.getMonth()];
2945
+ const day = String(date.getDate()).padStart(2, "0");
2946
+ const year = date.getFullYear();
2947
+ const hours = String(date.getHours()).padStart(2, "0");
2948
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2949
+ const seconds = String(date.getSeconds()).padStart(2, "0");
2950
+ const offsetMinutes = -date.getTimezoneOffset();
2951
+ const sign = offsetMinutes >= 0 ? "+" : "-";
2952
+ const absOffset = Math.abs(offsetMinutes);
2953
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2954
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
2955
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2956
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2957
+ }
2958
+ var DAYS, MONTHS;
2959
+ var init_timezone = __esm({
2960
+ "src/utils/timezone.ts"() {
2961
+ "use strict";
2962
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2963
+ MONTHS = [
2964
+ "Jan",
2965
+ "Feb",
2966
+ "Mar",
2967
+ "Apr",
2968
+ "May",
2969
+ "Jun",
2970
+ "Jul",
2971
+ "Aug",
2972
+ "Sep",
2973
+ "Oct",
2974
+ "Nov",
2975
+ "Dec"
2976
+ ];
2977
+ }
2978
+ });
2979
+
2937
2980
  // src/services/qwen-headers.ts
2938
2981
  import { v4 as uuidv4 } from "uuid";
2939
2982
  function getDefaultQwenUserAgent(major = getChromeMajor()) {
@@ -2954,6 +2997,7 @@ var init_qwen_headers = __esm({
2954
2997
  init_qwen_url();
2955
2998
  init_config();
2956
2999
  init_fingerprint();
3000
+ init_timezone();
2957
3001
  dynamicWebVersion = null;
2958
3002
  QWEN_WEB_VERSION = config.qwen.webVersion;
2959
3003
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
@@ -4376,6 +4420,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4376
4420
  await existingInit;
4377
4421
  return;
4378
4422
  }
4423
+ let resolveInit;
4424
+ let rejectInit;
4425
+ const inFlightDeferred = new Promise((res, rej) => {
4426
+ resolveInit = res;
4427
+ rejectInit = rej;
4428
+ });
4429
+ inFlightAccountInits.set(account.id, inFlightDeferred);
4379
4430
  const initPromise = (async () => {
4380
4431
  const release = await acquireAccountMutex(
4381
4432
  account.id,
@@ -4590,6 +4641,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4590
4641
  }
4591
4642
  }
4592
4643
  }
4644
+ const currentCache = getHeaderCache(account.id);
4645
+ const currentCookie = currentCache?.headers?.cookie || "";
4646
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
4647
+ try {
4648
+ const refreshRes = await refreshAccountToken(account.id);
4649
+ if (refreshRes.success) {
4650
+ console.log(
4651
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
4652
+ );
4653
+ }
4654
+ } catch (err) {
4655
+ console.warn(
4656
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
4657
+ );
4658
+ }
4659
+ }
4593
4660
  return;
4594
4661
  }
4595
4662
  const cookies = await acctContext.cookies();
@@ -4644,9 +4711,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4644
4711
  release();
4645
4712
  }
4646
4713
  })();
4647
- inFlightAccountInits.set(account.id, initPromise);
4648
4714
  try {
4649
4715
  await initPromise;
4716
+ resolveInit();
4717
+ } catch (error) {
4718
+ rejectInit(error);
4719
+ throw error;
4650
4720
  } finally {
4651
4721
  inFlightAccountInits.delete(account.id);
4652
4722
  }
@@ -4877,7 +4947,7 @@ async function startEmailOtpLogin(email, options = {}) {
4877
4947
  email: normalizedEmail,
4878
4948
  requestId,
4879
4949
  version: getQwenWebVersion() || "0.3.12",
4880
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4950
+ timezone: formatAsciiTimezone()
4881
4951
  }
4882
4952
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4883
4953
  if (!evalRes.ok || evalRes.data?.success !== true) {
@@ -4972,7 +5042,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
4972
5042
  code: cleanCode,
4973
5043
  requestId,
4974
5044
  version: getQwenWebVersion() || "0.3.12",
4975
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
5045
+ timezone: formatAsciiTimezone()
4976
5046
  }
4977
5047
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4978
5048
  if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
@@ -5253,7 +5323,7 @@ async function loginViaApi(page, email, password) {
5253
5323
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5254
5324
  const requestId = crypto3.randomUUID();
5255
5325
  const evalRes = await page.evaluate(
5256
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
5326
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
5257
5327
  try {
5258
5328
  const response = await fetch(signinUrl2, {
5259
5329
  method: "POST",
@@ -5265,7 +5335,7 @@ async function loginViaApi(page, email, password) {
5265
5335
  source: "web",
5266
5336
  version: "0.3.11",
5267
5337
  "x-request-origin": "https://chat.qwen.ai",
5268
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5338
+ timezone: tz,
5269
5339
  "x-request-id": requestId2
5270
5340
  },
5271
5341
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5276,7 +5346,7 @@ async function loginViaApi(page, email, password) {
5276
5346
  return { ok: false, error: e.message };
5277
5347
  }
5278
5348
  },
5279
- { email, password: hashedPassword, signinUrl, requestId }
5349
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
5280
5350
  ).catch(() => null);
5281
5351
  let data = evalRes?.data;
5282
5352
  let signinSuccess = Boolean(
@@ -5285,7 +5355,7 @@ async function loginViaApi(page, email, password) {
5285
5355
  if (!signinSuccess && !data?.data?.code) {
5286
5356
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
5287
5357
  const fallbackRes = await page.evaluate(
5288
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
5358
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
5289
5359
  try {
5290
5360
  const response = await fetch(fallbackUrl2, {
5291
5361
  method: "POST",
@@ -5296,7 +5366,7 @@ async function loginViaApi(page, email, password) {
5296
5366
  "content-type": "application/json",
5297
5367
  source: "web",
5298
5368
  version: "0.3.11",
5299
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5369
+ timezone: tz,
5300
5370
  "x-request-id": requestId2
5301
5371
  },
5302
5372
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5307,7 +5377,7 @@ async function loginViaApi(page, email, password) {
5307
5377
  return { ok: false, error: e.message };
5308
5378
  }
5309
5379
  },
5310
- { email, password: hashedPassword, fallbackUrl, requestId }
5380
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
5311
5381
  ).catch(() => null);
5312
5382
  if (fallbackRes?.data) {
5313
5383
  data = fallbackRes.data;
@@ -5639,7 +5709,7 @@ async function refreshAccountToken(accountId) {
5639
5709
  if (refreshToken) {
5640
5710
  try {
5641
5711
  const refreshResult = await page.evaluate(
5642
- async ({ rTok }) => {
5712
+ async ({ rTok, tz }) => {
5643
5713
  try {
5644
5714
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5645
5715
  method: "GET",
@@ -5649,7 +5719,7 @@ async function refreshAccountToken(accountId) {
5649
5719
  source: "web",
5650
5720
  version: "0.3.11",
5651
5721
  "x-request-origin": "https://chat.qwen.ai",
5652
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5722
+ timezone: tz,
5653
5723
  authorization: `Bearer ${rTok}`
5654
5724
  }
5655
5725
  });
@@ -5659,7 +5729,7 @@ async function refreshAccountToken(accountId) {
5659
5729
  return { ok: false, error: e.message };
5660
5730
  }
5661
5731
  },
5662
- { rTok: refreshToken }
5732
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
5663
5733
  );
5664
5734
  const json = refreshResult?.data;
5665
5735
  if (json && json.success === true && json.data) {
@@ -5697,6 +5767,7 @@ async function refreshAccountToken(accountId) {
5697
5767
  const cache = getHeaderCache(accountId);
5698
5768
  cache.headers.cookie = cookieStr;
5699
5769
  cache.lastRefresh = Date.now();
5770
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5700
5771
  markAccountHeadersReady(accountId);
5701
5772
  saveAuthSession2(accountId, {
5702
5773
  cookie: cookieStr,
@@ -5731,6 +5802,7 @@ async function refreshAccountToken(accountId) {
5731
5802
  const cache = getHeaderCache(accountId);
5732
5803
  cache.headers.cookie = cookieStr;
5733
5804
  cache.lastRefresh = Date.now();
5805
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5734
5806
  markAccountHeadersReady(accountId);
5735
5807
  saveAuthSession2(accountId, {
5736
5808
  cookie: cookieStr,
@@ -6785,11 +6857,11 @@ async function keepAlivePlaywrightAccount(accountId) {
6785
6857
  const now = Date.now();
6786
6858
  const currentUrl = page.url();
6787
6859
  const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
6788
- const cookie = await getCookies(accountId);
6860
+ const cookie = await getCookies(accountId, true);
6789
6861
  if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
6790
6862
  lastProactiveTokenRefresh.set(accountId, now);
6791
- console.log(
6792
- `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6863
+ logger.debug(
6864
+ `[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6793
6865
  );
6794
6866
  const res = await refreshAccountToken(accountId);
6795
6867
  if (res.success) {
@@ -6797,6 +6869,9 @@ async function keepAlivePlaywrightAccount(accountId) {
6797
6869
  touchAccountActivity(accountId);
6798
6870
  return true;
6799
6871
  }
6872
+ logger.warn(
6873
+ `[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
6874
+ );
6800
6875
  }
6801
6876
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
6802
6877
  const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
@@ -7034,6 +7109,7 @@ var init_playwright = __esm({
7034
7109
  init_qwen_headers();
7035
7110
  init_paths();
7036
7111
  init_jwt();
7112
+ init_timezone();
7037
7113
  requireLocal = createRequire(import.meta.url);
7038
7114
  contextInitHooks = [];
7039
7115
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -7047,7 +7123,7 @@ var init_playwright = __esm({
7047
7123
  headerCaches = /* @__PURE__ */ new Map();
7048
7124
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
7049
7125
  HEADER_REFRESH_THRESHOLD = 0.8;
7050
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
7126
+ COOKIE_CACHE_TTL = 60 * 1e3;
7051
7127
  cookieCaches = /* @__PURE__ */ new Map();
7052
7128
  lastAccountActivity = /* @__PURE__ */ new Map();
7053
7129
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
@@ -2001,6 +2001,7 @@ var init_mutex = __esm({
2001
2001
  locked = false;
2002
2002
  lockedAt = 0;
2003
2003
  lockedByKey = "";
2004
+ epoch = 0;
2004
2005
  async acquire(timeoutMs = 3e5, key = "") {
2005
2006
  const holdLimitMs = this.maxHoldMs;
2006
2007
  if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
@@ -2011,6 +2012,7 @@ var init_mutex = __esm({
2011
2012
  this.locked = false;
2012
2013
  this.lockedAt = 0;
2013
2014
  this.lockedByKey = "";
2015
+ this.epoch++;
2014
2016
  }
2015
2017
  if (!this.locked) {
2016
2018
  this.locked = true;
@@ -2049,10 +2051,12 @@ var init_mutex = __esm({
2049
2051
  }
2050
2052
  }
2051
2053
  createRelease() {
2054
+ const epoch = this.epoch;
2052
2055
  let released = false;
2053
2056
  return () => {
2054
2057
  if (released) return;
2055
2058
  released = true;
2059
+ if (epoch !== this.epoch) return;
2056
2060
  this.release();
2057
2061
  };
2058
2062
  }
@@ -2715,6 +2719,45 @@ var init_waf_isolation = __esm({
2715
2719
  }
2716
2720
  });
2717
2721
 
2722
+ // src/utils/timezone.ts
2723
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2724
+ const dayName = DAYS[date.getDay()];
2725
+ const monthName = MONTHS[date.getMonth()];
2726
+ const day = String(date.getDate()).padStart(2, "0");
2727
+ const year = date.getFullYear();
2728
+ const hours = String(date.getHours()).padStart(2, "0");
2729
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2730
+ const seconds = String(date.getSeconds()).padStart(2, "0");
2731
+ const offsetMinutes = -date.getTimezoneOffset();
2732
+ const sign = offsetMinutes >= 0 ? "+" : "-";
2733
+ const absOffset = Math.abs(offsetMinutes);
2734
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2735
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
2736
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2737
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2738
+ }
2739
+ var DAYS, MONTHS;
2740
+ var init_timezone = __esm({
2741
+ "src/utils/timezone.ts"() {
2742
+ "use strict";
2743
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2744
+ MONTHS = [
2745
+ "Jan",
2746
+ "Feb",
2747
+ "Mar",
2748
+ "Apr",
2749
+ "May",
2750
+ "Jun",
2751
+ "Jul",
2752
+ "Aug",
2753
+ "Sep",
2754
+ "Oct",
2755
+ "Nov",
2756
+ "Dec"
2757
+ ];
2758
+ }
2759
+ });
2760
+
2718
2761
  // src/services/qwen-headers.ts
2719
2762
  import { v4 as uuidv4 } from "uuid";
2720
2763
  function getDefaultQwenUserAgent(major = getChromeMajor()) {
@@ -2732,6 +2775,7 @@ var init_qwen_headers = __esm({
2732
2775
  init_qwen_url();
2733
2776
  init_config();
2734
2777
  init_fingerprint();
2778
+ init_timezone();
2735
2779
  dynamicWebVersion = null;
2736
2780
  QWEN_WEB_VERSION = config.qwen.webVersion;
2737
2781
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
@@ -3807,6 +3851,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
3807
3851
  await existingInit;
3808
3852
  return;
3809
3853
  }
3854
+ let resolveInit;
3855
+ let rejectInit;
3856
+ const inFlightDeferred = new Promise((res, rej) => {
3857
+ resolveInit = res;
3858
+ rejectInit = rej;
3859
+ });
3860
+ inFlightAccountInits.set(account.id, inFlightDeferred);
3810
3861
  const initPromise = (async () => {
3811
3862
  const release = await acquireAccountMutex(
3812
3863
  account.id,
@@ -4021,6 +4072,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4021
4072
  }
4022
4073
  }
4023
4074
  }
4075
+ const currentCache = getHeaderCache(account.id);
4076
+ const currentCookie = currentCache?.headers?.cookie || "";
4077
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
4078
+ try {
4079
+ const refreshRes = await refreshAccountToken(account.id);
4080
+ if (refreshRes.success) {
4081
+ console.log(
4082
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
4083
+ );
4084
+ }
4085
+ } catch (err) {
4086
+ console.warn(
4087
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
4088
+ );
4089
+ }
4090
+ }
4024
4091
  return;
4025
4092
  }
4026
4093
  const cookies = await acctContext.cookies();
@@ -4075,9 +4142,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4075
4142
  release();
4076
4143
  }
4077
4144
  })();
4078
- inFlightAccountInits.set(account.id, initPromise);
4079
4145
  try {
4080
4146
  await initPromise;
4147
+ resolveInit();
4148
+ } catch (error) {
4149
+ rejectInit(error);
4150
+ throw error;
4081
4151
  } finally {
4082
4152
  inFlightAccountInits.delete(account.id);
4083
4153
  }
@@ -4278,7 +4348,7 @@ async function loginViaApi(page, email, password) {
4278
4348
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4279
4349
  const requestId = crypto3.randomUUID();
4280
4350
  const evalRes = await page.evaluate(
4281
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4351
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
4282
4352
  try {
4283
4353
  const response = await fetch(signinUrl2, {
4284
4354
  method: "POST",
@@ -4290,7 +4360,7 @@ async function loginViaApi(page, email, password) {
4290
4360
  source: "web",
4291
4361
  version: "0.3.11",
4292
4362
  "x-request-origin": "https://chat.qwen.ai",
4293
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4363
+ timezone: tz,
4294
4364
  "x-request-id": requestId2
4295
4365
  },
4296
4366
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4301,7 +4371,7 @@ async function loginViaApi(page, email, password) {
4301
4371
  return { ok: false, error: e.message };
4302
4372
  }
4303
4373
  },
4304
- { email, password: hashedPassword, signinUrl, requestId }
4374
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
4305
4375
  ).catch(() => null);
4306
4376
  let data = evalRes?.data;
4307
4377
  let signinSuccess = Boolean(
@@ -4310,7 +4380,7 @@ async function loginViaApi(page, email, password) {
4310
4380
  if (!signinSuccess && !data?.data?.code) {
4311
4381
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4312
4382
  const fallbackRes = await page.evaluate(
4313
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4383
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
4314
4384
  try {
4315
4385
  const response = await fetch(fallbackUrl2, {
4316
4386
  method: "POST",
@@ -4321,7 +4391,7 @@ async function loginViaApi(page, email, password) {
4321
4391
  "content-type": "application/json",
4322
4392
  source: "web",
4323
4393
  version: "0.3.11",
4324
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4394
+ timezone: tz,
4325
4395
  "x-request-id": requestId2
4326
4396
  },
4327
4397
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4332,7 +4402,7 @@ async function loginViaApi(page, email, password) {
4332
4402
  return { ok: false, error: e.message };
4333
4403
  }
4334
4404
  },
4335
- { email, password: hashedPassword, fallbackUrl, requestId }
4405
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
4336
4406
  ).catch(() => null);
4337
4407
  if (fallbackRes?.data) {
4338
4408
  data = fallbackRes.data;
@@ -4664,7 +4734,7 @@ async function refreshAccountToken(accountId) {
4664
4734
  if (refreshToken) {
4665
4735
  try {
4666
4736
  const refreshResult = await page.evaluate(
4667
- async ({ rTok }) => {
4737
+ async ({ rTok, tz }) => {
4668
4738
  try {
4669
4739
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4670
4740
  method: "GET",
@@ -4674,7 +4744,7 @@ async function refreshAccountToken(accountId) {
4674
4744
  source: "web",
4675
4745
  version: "0.3.11",
4676
4746
  "x-request-origin": "https://chat.qwen.ai",
4677
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4747
+ timezone: tz,
4678
4748
  authorization: `Bearer ${rTok}`
4679
4749
  }
4680
4750
  });
@@ -4684,7 +4754,7 @@ async function refreshAccountToken(accountId) {
4684
4754
  return { ok: false, error: e.message };
4685
4755
  }
4686
4756
  },
4687
- { rTok: refreshToken }
4757
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
4688
4758
  );
4689
4759
  const json = refreshResult?.data;
4690
4760
  if (json && json.success === true && json.data) {
@@ -4722,6 +4792,7 @@ async function refreshAccountToken(accountId) {
4722
4792
  const cache = getHeaderCache(accountId);
4723
4793
  cache.headers.cookie = cookieStr;
4724
4794
  cache.lastRefresh = Date.now();
4795
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4725
4796
  markAccountHeadersReady(accountId);
4726
4797
  saveAuthSession2(accountId, {
4727
4798
  cookie: cookieStr,
@@ -4756,6 +4827,7 @@ async function refreshAccountToken(accountId) {
4756
4827
  const cache = getHeaderCache(accountId);
4757
4828
  cache.headers.cookie = cookieStr;
4758
4829
  cache.lastRefresh = Date.now();
4830
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4759
4831
  markAccountHeadersReady(accountId);
4760
4832
  saveAuthSession2(accountId, {
4761
4833
  cookie: cookieStr,
@@ -5636,6 +5708,7 @@ var init_playwright = __esm({
5636
5708
  init_qwen_headers();
5637
5709
  init_paths();
5638
5710
  init_jwt();
5711
+ init_timezone();
5639
5712
  requireLocal = createRequire(import.meta.url);
5640
5713
  contextInitHooks = [];
5641
5714
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -5645,7 +5718,7 @@ var init_playwright = __esm({
5645
5718
  inFlightAccountInits = /* @__PURE__ */ new Map();
5646
5719
  headerCaches = /* @__PURE__ */ new Map();
5647
5720
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
5648
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
5721
+ COOKIE_CACHE_TTL = 60 * 1e3;
5649
5722
  cookieCaches = /* @__PURE__ */ new Map();
5650
5723
  lastAccountActivity = /* @__PURE__ */ new Map();
5651
5724
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
@@ -5927,7 +6000,7 @@ function clearPersonalizationDbCache(accountId) {
5927
6000
  return 0;
5928
6001
  }
5929
6002
  }
5930
- var BROWSER_STREAM_BINDING, browserStreamStates, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, lastSyncedPersonalizationHashes, activePersonalizationByAccount, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, UPSTREAM_RESPONSE_PREVIEW_BYTES;
6003
+ var BROWSER_STREAM_BINDING, browserStreamStates, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, lastSyncedPersonalizationHashes, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, UPSTREAM_RESPONSE_PREVIEW_BYTES;
5931
6004
  var init_qwen = __esm({
5932
6005
  "src/services/qwen.ts"() {
5933
6006
  "use strict";
@@ -5945,6 +6018,7 @@ var init_qwen = __esm({
5945
6018
  init_captcha_coordinator();
5946
6019
  init_captcha_solver();
5947
6020
  init_account_concurrency();
6021
+ init_timezone();
5948
6022
  init_qwen_errors();
5949
6023
  init_qwen_thread_state();
5950
6024
  init_qwen_chat_pool();
@@ -5960,6 +6034,7 @@ var init_qwen = __esm({
5960
6034
  MODEL_CACHE_TTL_MS = 60 * 60 * 1e3;
5961
6035
  lastSyncedPersonalizationHashes = /* @__PURE__ */ new Map();
5962
6036
  activePersonalizationByAccount = /* @__PURE__ */ new Map();
6037
+ STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
5963
6038
  QWEN_SAFE_SETTINGS_PATCH = {
5964
6039
  ui: {
5965
6040
  autoTags: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
package/src/core/mutex.ts CHANGED
@@ -14,6 +14,7 @@ export class Mutex {
14
14
  private locked = false;
15
15
  private lockedAt = 0;
16
16
  private lockedByKey = "";
17
+ private epoch = 0;
17
18
 
18
19
  constructor(
19
20
  public readonly name: string = "unnamed",
@@ -35,6 +36,7 @@ export class Mutex {
35
36
  this.locked = false;
36
37
  this.lockedAt = 0;
37
38
  this.lockedByKey = "";
39
+ this.epoch++;
38
40
  }
39
41
 
40
42
  if (!this.locked) {
@@ -78,10 +80,12 @@ export class Mutex {
78
80
  }
79
81
 
80
82
  private createRelease(): () => void {
83
+ const epoch = this.epoch;
81
84
  let released = false;
82
85
  return () => {
83
86
  if (released) return;
84
87
  released = true;
88
+ if (epoch !== this.epoch) return;
85
89
  this.release();
86
90
  };
87
91
  }
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.6.4";
20
+ cachedVersion = "v1.6.6";
21
21
  return cachedVersion;
22
22
  }
@@ -316,9 +316,14 @@ export function translateOpenAIToAnthropic(
316
316
  };
317
317
 
318
318
  const hasToolUse = content.some((b) => b.type === "tool_use");
319
- const stopReason = hasToolUse
320
- ? "tool_use"
321
- : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
319
+ const stopReason =
320
+ choice.finish_reason === "length"
321
+ ? "max_tokens"
322
+ : choice.finish_reason === "content_filter"
323
+ ? "end_turn"
324
+ : hasToolUse
325
+ ? "tool_use"
326
+ : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
322
327
 
323
328
  return {
324
329
  id: generateMessageId(),
@@ -498,11 +503,14 @@ export function translateStreamChunk(
498
503
  state.currentBlockType = null;
499
504
  }
500
505
 
501
- const stopReason = state.hasEmittedToolUse
502
- ? "tool_use"
503
- : choice.finish_reason === "length"
506
+ const stopReason =
507
+ choice.finish_reason === "length"
504
508
  ? "max_tokens"
505
- : "end_turn";
509
+ : choice.finish_reason === "content_filter"
510
+ ? "end_turn"
511
+ : state.hasEmittedToolUse
512
+ ? "tool_use"
513
+ : "end_turn";
506
514
 
507
515
  events.push(
508
516
  JSON.stringify({
@@ -231,8 +231,10 @@ export function applyUpstreamUsage(
231
231
  if (!candidate || typeof candidate !== "object") return;
232
232
 
233
233
  const usage = candidate as Record<string, unknown>;
234
- const promptTokens = asFiniteNumber(usage.input_tokens);
235
- const completionTokens = asFiniteNumber(usage.output_tokens);
234
+ const promptTokens =
235
+ asFiniteNumber(usage.input_tokens) ?? asFiniteNumber(usage.prompt_tokens);
236
+ const completionTokens =
237
+ asFiniteNumber(usage.output_tokens) ?? asFiniteNumber(usage.completion_tokens);
236
238
  const totalTokens = asFiniteNumber(usage.total_tokens);
237
239
 
238
240
  if (promptTokens !== null) {