qwenproxy-cli 1.6.4 → 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.
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);
@@ -4590,6 +4634,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4590
4634
  }
4591
4635
  }
4592
4636
  }
4637
+ const currentCache = getHeaderCache(account.id);
4638
+ const currentCookie = currentCache?.headers?.cookie || "";
4639
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
4640
+ try {
4641
+ const refreshRes = await refreshAccountToken(account.id);
4642
+ if (refreshRes.success) {
4643
+ console.log(
4644
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
4645
+ );
4646
+ }
4647
+ } catch (err) {
4648
+ console.warn(
4649
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
4650
+ );
4651
+ }
4652
+ }
4593
4653
  return;
4594
4654
  }
4595
4655
  const cookies = await acctContext.cookies();
@@ -4877,7 +4937,7 @@ async function startEmailOtpLogin(email, options = {}) {
4877
4937
  email: normalizedEmail,
4878
4938
  requestId,
4879
4939
  version: getQwenWebVersion() || "0.3.12",
4880
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4940
+ timezone: formatAsciiTimezone()
4881
4941
  }
4882
4942
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4883
4943
  if (!evalRes.ok || evalRes.data?.success !== true) {
@@ -4972,7 +5032,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
4972
5032
  code: cleanCode,
4973
5033
  requestId,
4974
5034
  version: getQwenWebVersion() || "0.3.12",
4975
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
5035
+ timezone: formatAsciiTimezone()
4976
5036
  }
4977
5037
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4978
5038
  if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
@@ -5253,7 +5313,7 @@ async function loginViaApi(page, email, password) {
5253
5313
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5254
5314
  const requestId = crypto3.randomUUID();
5255
5315
  const evalRes = await page.evaluate(
5256
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
5316
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
5257
5317
  try {
5258
5318
  const response = await fetch(signinUrl2, {
5259
5319
  method: "POST",
@@ -5265,7 +5325,7 @@ async function loginViaApi(page, email, password) {
5265
5325
  source: "web",
5266
5326
  version: "0.3.11",
5267
5327
  "x-request-origin": "https://chat.qwen.ai",
5268
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5328
+ timezone: tz,
5269
5329
  "x-request-id": requestId2
5270
5330
  },
5271
5331
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5276,7 +5336,7 @@ async function loginViaApi(page, email, password) {
5276
5336
  return { ok: false, error: e.message };
5277
5337
  }
5278
5338
  },
5279
- { email, password: hashedPassword, signinUrl, requestId }
5339
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
5280
5340
  ).catch(() => null);
5281
5341
  let data = evalRes?.data;
5282
5342
  let signinSuccess = Boolean(
@@ -5285,7 +5345,7 @@ async function loginViaApi(page, email, password) {
5285
5345
  if (!signinSuccess && !data?.data?.code) {
5286
5346
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
5287
5347
  const fallbackRes = await page.evaluate(
5288
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
5348
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
5289
5349
  try {
5290
5350
  const response = await fetch(fallbackUrl2, {
5291
5351
  method: "POST",
@@ -5296,7 +5356,7 @@ async function loginViaApi(page, email, password) {
5296
5356
  "content-type": "application/json",
5297
5357
  source: "web",
5298
5358
  version: "0.3.11",
5299
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5359
+ timezone: tz,
5300
5360
  "x-request-id": requestId2
5301
5361
  },
5302
5362
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5307,7 +5367,7 @@ async function loginViaApi(page, email, password) {
5307
5367
  return { ok: false, error: e.message };
5308
5368
  }
5309
5369
  },
5310
- { email, password: hashedPassword, fallbackUrl, requestId }
5370
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
5311
5371
  ).catch(() => null);
5312
5372
  if (fallbackRes?.data) {
5313
5373
  data = fallbackRes.data;
@@ -5639,7 +5699,7 @@ async function refreshAccountToken(accountId) {
5639
5699
  if (refreshToken) {
5640
5700
  try {
5641
5701
  const refreshResult = await page.evaluate(
5642
- async ({ rTok }) => {
5702
+ async ({ rTok, tz }) => {
5643
5703
  try {
5644
5704
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5645
5705
  method: "GET",
@@ -5649,7 +5709,7 @@ async function refreshAccountToken(accountId) {
5649
5709
  source: "web",
5650
5710
  version: "0.3.11",
5651
5711
  "x-request-origin": "https://chat.qwen.ai",
5652
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5712
+ timezone: tz,
5653
5713
  authorization: `Bearer ${rTok}`
5654
5714
  }
5655
5715
  });
@@ -5659,7 +5719,7 @@ async function refreshAccountToken(accountId) {
5659
5719
  return { ok: false, error: e.message };
5660
5720
  }
5661
5721
  },
5662
- { rTok: refreshToken }
5722
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
5663
5723
  );
5664
5724
  const json = refreshResult?.data;
5665
5725
  if (json && json.success === true && json.data) {
@@ -5697,6 +5757,7 @@ async function refreshAccountToken(accountId) {
5697
5757
  const cache = getHeaderCache(accountId);
5698
5758
  cache.headers.cookie = cookieStr;
5699
5759
  cache.lastRefresh = Date.now();
5760
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5700
5761
  markAccountHeadersReady(accountId);
5701
5762
  saveAuthSession2(accountId, {
5702
5763
  cookie: cookieStr,
@@ -5731,6 +5792,7 @@ async function refreshAccountToken(accountId) {
5731
5792
  const cache = getHeaderCache(accountId);
5732
5793
  cache.headers.cookie = cookieStr;
5733
5794
  cache.lastRefresh = Date.now();
5795
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5734
5796
  markAccountHeadersReady(accountId);
5735
5797
  saveAuthSession2(accountId, {
5736
5798
  cookie: cookieStr,
@@ -6785,11 +6847,11 @@ async function keepAlivePlaywrightAccount(accountId) {
6785
6847
  const now = Date.now();
6786
6848
  const currentUrl = page.url();
6787
6849
  const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
6788
- const cookie = await getCookies(accountId);
6850
+ const cookie = await getCookies(accountId, true);
6789
6851
  if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
6790
6852
  lastProactiveTokenRefresh.set(accountId, now);
6791
- console.log(
6792
- `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6853
+ logger.debug(
6854
+ `[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6793
6855
  );
6794
6856
  const res = await refreshAccountToken(accountId);
6795
6857
  if (res.success) {
@@ -6797,6 +6859,9 @@ async function keepAlivePlaywrightAccount(accountId) {
6797
6859
  touchAccountActivity(accountId);
6798
6860
  return true;
6799
6861
  }
6862
+ logger.warn(
6863
+ `[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
6864
+ );
6800
6865
  }
6801
6866
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
6802
6867
  const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
@@ -7034,6 +7099,7 @@ var init_playwright = __esm({
7034
7099
  init_qwen_headers();
7035
7100
  init_paths();
7036
7101
  init_jwt();
7102
+ init_timezone();
7037
7103
  requireLocal = createRequire(import.meta.url);
7038
7104
  contextInitHooks = [];
7039
7105
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -7047,7 +7113,7 @@ var init_playwright = __esm({
7047
7113
  headerCaches = /* @__PURE__ */ new Map();
7048
7114
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
7049
7115
  HEADER_REFRESH_THRESHOLD = 0.8;
7050
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
7116
+ COOKIE_CACHE_TTL = 60 * 1e3;
7051
7117
  cookieCaches = /* @__PURE__ */ new Map();
7052
7118
  lastAccountActivity = /* @__PURE__ */ new Map();
7053
7119
  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);
@@ -4021,6 +4065,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4021
4065
  }
4022
4066
  }
4023
4067
  }
4068
+ const currentCache = getHeaderCache(account.id);
4069
+ const currentCookie = currentCache?.headers?.cookie || "";
4070
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
4071
+ try {
4072
+ const refreshRes = await refreshAccountToken(account.id);
4073
+ if (refreshRes.success) {
4074
+ console.log(
4075
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
4076
+ );
4077
+ }
4078
+ } catch (err) {
4079
+ console.warn(
4080
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
4081
+ );
4082
+ }
4083
+ }
4024
4084
  return;
4025
4085
  }
4026
4086
  const cookies = await acctContext.cookies();
@@ -4278,7 +4338,7 @@ async function loginViaApi(page, email, password) {
4278
4338
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4279
4339
  const requestId = crypto3.randomUUID();
4280
4340
  const evalRes = await page.evaluate(
4281
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4341
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
4282
4342
  try {
4283
4343
  const response = await fetch(signinUrl2, {
4284
4344
  method: "POST",
@@ -4290,7 +4350,7 @@ async function loginViaApi(page, email, password) {
4290
4350
  source: "web",
4291
4351
  version: "0.3.11",
4292
4352
  "x-request-origin": "https://chat.qwen.ai",
4293
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4353
+ timezone: tz,
4294
4354
  "x-request-id": requestId2
4295
4355
  },
4296
4356
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4301,7 +4361,7 @@ async function loginViaApi(page, email, password) {
4301
4361
  return { ok: false, error: e.message };
4302
4362
  }
4303
4363
  },
4304
- { email, password: hashedPassword, signinUrl, requestId }
4364
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
4305
4365
  ).catch(() => null);
4306
4366
  let data = evalRes?.data;
4307
4367
  let signinSuccess = Boolean(
@@ -4310,7 +4370,7 @@ async function loginViaApi(page, email, password) {
4310
4370
  if (!signinSuccess && !data?.data?.code) {
4311
4371
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4312
4372
  const fallbackRes = await page.evaluate(
4313
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4373
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
4314
4374
  try {
4315
4375
  const response = await fetch(fallbackUrl2, {
4316
4376
  method: "POST",
@@ -4321,7 +4381,7 @@ async function loginViaApi(page, email, password) {
4321
4381
  "content-type": "application/json",
4322
4382
  source: "web",
4323
4383
  version: "0.3.11",
4324
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4384
+ timezone: tz,
4325
4385
  "x-request-id": requestId2
4326
4386
  },
4327
4387
  body: JSON.stringify({ email: email2, password: password2 })
@@ -4332,7 +4392,7 @@ async function loginViaApi(page, email, password) {
4332
4392
  return { ok: false, error: e.message };
4333
4393
  }
4334
4394
  },
4335
- { email, password: hashedPassword, fallbackUrl, requestId }
4395
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
4336
4396
  ).catch(() => null);
4337
4397
  if (fallbackRes?.data) {
4338
4398
  data = fallbackRes.data;
@@ -4664,7 +4724,7 @@ async function refreshAccountToken(accountId) {
4664
4724
  if (refreshToken) {
4665
4725
  try {
4666
4726
  const refreshResult = await page.evaluate(
4667
- async ({ rTok }) => {
4727
+ async ({ rTok, tz }) => {
4668
4728
  try {
4669
4729
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4670
4730
  method: "GET",
@@ -4674,7 +4734,7 @@ async function refreshAccountToken(accountId) {
4674
4734
  source: "web",
4675
4735
  version: "0.3.11",
4676
4736
  "x-request-origin": "https://chat.qwen.ai",
4677
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4737
+ timezone: tz,
4678
4738
  authorization: `Bearer ${rTok}`
4679
4739
  }
4680
4740
  });
@@ -4684,7 +4744,7 @@ async function refreshAccountToken(accountId) {
4684
4744
  return { ok: false, error: e.message };
4685
4745
  }
4686
4746
  },
4687
- { rTok: refreshToken }
4747
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
4688
4748
  );
4689
4749
  const json = refreshResult?.data;
4690
4750
  if (json && json.success === true && json.data) {
@@ -4722,6 +4782,7 @@ async function refreshAccountToken(accountId) {
4722
4782
  const cache = getHeaderCache(accountId);
4723
4783
  cache.headers.cookie = cookieStr;
4724
4784
  cache.lastRefresh = Date.now();
4785
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4725
4786
  markAccountHeadersReady(accountId);
4726
4787
  saveAuthSession2(accountId, {
4727
4788
  cookie: cookieStr,
@@ -4756,6 +4817,7 @@ async function refreshAccountToken(accountId) {
4756
4817
  const cache = getHeaderCache(accountId);
4757
4818
  cache.headers.cookie = cookieStr;
4758
4819
  cache.lastRefresh = Date.now();
4820
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
4759
4821
  markAccountHeadersReady(accountId);
4760
4822
  saveAuthSession2(accountId, {
4761
4823
  cookie: cookieStr,
@@ -5636,6 +5698,7 @@ var init_playwright = __esm({
5636
5698
  init_qwen_headers();
5637
5699
  init_paths();
5638
5700
  init_jwt();
5701
+ init_timezone();
5639
5702
  requireLocal = createRequire(import.meta.url);
5640
5703
  contextInitHooks = [];
5641
5704
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -5645,7 +5708,7 @@ var init_playwright = __esm({
5645
5708
  inFlightAccountInits = /* @__PURE__ */ new Map();
5646
5709
  headerCaches = /* @__PURE__ */ new Map();
5647
5710
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
5648
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
5711
+ COOKIE_CACHE_TTL = 60 * 1e3;
5649
5712
  cookieCaches = /* @__PURE__ */ new Map();
5650
5713
  lastAccountActivity = /* @__PURE__ */ new Map();
5651
5714
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
@@ -5927,7 +5990,7 @@ function clearPersonalizationDbCache(accountId) {
5927
5990
  return 0;
5928
5991
  }
5929
5992
  }
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;
5993
+ 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
5994
  var init_qwen = __esm({
5932
5995
  "src/services/qwen.ts"() {
5933
5996
  "use strict";
@@ -5945,6 +6008,7 @@ var init_qwen = __esm({
5945
6008
  init_captcha_coordinator();
5946
6009
  init_captcha_solver();
5947
6010
  init_account_concurrency();
6011
+ init_timezone();
5948
6012
  init_qwen_errors();
5949
6013
  init_qwen_thread_state();
5950
6014
  init_qwen_chat_pool();
@@ -5960,6 +6024,7 @@ var init_qwen = __esm({
5960
6024
  MODEL_CACHE_TTL_MS = 60 * 60 * 1e3;
5961
6025
  lastSyncedPersonalizationHashes = /* @__PURE__ */ new Map();
5962
6026
  activePersonalizationByAccount = /* @__PURE__ */ new Map();
6027
+ STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
5963
6028
  QWEN_SAFE_SETTINGS_PATCH = {
5964
6029
  ui: {
5965
6030
  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.5",
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.5";
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) {