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.
@@ -1915,6 +1915,7 @@ var init_mutex = __esm({
1915
1915
  locked = false;
1916
1916
  lockedAt = 0;
1917
1917
  lockedByKey = "";
1918
+ epoch = 0;
1918
1919
  async acquire(timeoutMs = 3e5, key = "") {
1919
1920
  const holdLimitMs = this.maxHoldMs;
1920
1921
  if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
@@ -1925,6 +1926,7 @@ var init_mutex = __esm({
1925
1926
  this.locked = false;
1926
1927
  this.lockedAt = 0;
1927
1928
  this.lockedByKey = "";
1929
+ this.epoch++;
1928
1930
  }
1929
1931
  if (!this.locked) {
1930
1932
  this.locked = true;
@@ -1963,10 +1965,12 @@ var init_mutex = __esm({
1963
1965
  }
1964
1966
  }
1965
1967
  createRelease() {
1968
+ const epoch = this.epoch;
1966
1969
  let released = false;
1967
1970
  return () => {
1968
1971
  if (released) return;
1969
1972
  released = true;
1973
+ if (epoch !== this.epoch) return;
1970
1974
  this.release();
1971
1975
  };
1972
1976
  }
@@ -2906,6 +2910,49 @@ var init_waf_isolation = __esm({
2906
2910
  }
2907
2911
  });
2908
2912
 
2913
+ // src/utils/timezone.ts
2914
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2915
+ const dayName = DAYS[date.getDay()];
2916
+ const monthName = MONTHS[date.getMonth()];
2917
+ const day = String(date.getDate()).padStart(2, "0");
2918
+ const year = date.getFullYear();
2919
+ const hours = String(date.getHours()).padStart(2, "0");
2920
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2921
+ const seconds = String(date.getSeconds()).padStart(2, "0");
2922
+ const offsetMinutes = -date.getTimezoneOffset();
2923
+ const sign = offsetMinutes >= 0 ? "+" : "-";
2924
+ const absOffset = Math.abs(offsetMinutes);
2925
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2926
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
2927
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2928
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2929
+ }
2930
+ function sanitizeHeaderForFetch(val) {
2931
+ if (typeof val !== "string") return "";
2932
+ return val.replace(/[^\x00-\xFF]/g, "");
2933
+ }
2934
+ var DAYS, MONTHS;
2935
+ var init_timezone = __esm({
2936
+ "src/utils/timezone.ts"() {
2937
+ "use strict";
2938
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2939
+ MONTHS = [
2940
+ "Jan",
2941
+ "Feb",
2942
+ "Mar",
2943
+ "Apr",
2944
+ "May",
2945
+ "Jun",
2946
+ "Jul",
2947
+ "Aug",
2948
+ "Sep",
2949
+ "Oct",
2950
+ "Nov",
2951
+ "Dec"
2952
+ ];
2953
+ }
2954
+ });
2955
+
2909
2956
  // src/services/qwen-headers.ts
2910
2957
  import { v4 as uuidv4 } from "uuid";
2911
2958
  function getSystemAcceptLanguage(customLocale) {
@@ -2949,8 +2996,8 @@ function buildQwenRequestHeaders(opts) {
2949
2996
  "bx-v": opts.bxV || "2.5.37",
2950
2997
  source: "web",
2951
2998
  version: opts.version || getQwenWebVersion(),
2952
- timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
2953
- Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
2999
+ timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
3000
+ Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
2954
3001
  // Use the real browser client-hints when captured (anti-hardcoded); fall
2955
3002
  // back to the static fingerprint otherwise.
2956
3003
  "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
@@ -2977,6 +3024,7 @@ var init_qwen_headers = __esm({
2977
3024
  init_qwen_url();
2978
3025
  init_config();
2979
3026
  init_fingerprint();
3027
+ init_timezone();
2980
3028
  dynamicWebVersion = null;
2981
3029
  QWEN_WEB_VERSION = config.qwen.webVersion;
2982
3030
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
@@ -4399,6 +4447,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4399
4447
  await existingInit;
4400
4448
  return;
4401
4449
  }
4450
+ let resolveInit;
4451
+ let rejectInit;
4452
+ const inFlightDeferred = new Promise((res, rej) => {
4453
+ resolveInit = res;
4454
+ rejectInit = rej;
4455
+ });
4456
+ inFlightAccountInits.set(account.id, inFlightDeferred);
4402
4457
  const initPromise = (async () => {
4403
4458
  const release = await acquireAccountMutex(
4404
4459
  account.id,
@@ -4613,6 +4668,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4613
4668
  }
4614
4669
  }
4615
4670
  }
4671
+ const currentCache = getHeaderCache(account.id);
4672
+ const currentCookie = currentCache?.headers?.cookie || "";
4673
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
4674
+ try {
4675
+ const refreshRes = await refreshAccountToken(account.id);
4676
+ if (refreshRes.success) {
4677
+ console.log(
4678
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
4679
+ );
4680
+ }
4681
+ } catch (err) {
4682
+ console.warn(
4683
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
4684
+ );
4685
+ }
4686
+ }
4616
4687
  return;
4617
4688
  }
4618
4689
  const cookies = await acctContext.cookies();
@@ -4667,9 +4738,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4667
4738
  release();
4668
4739
  }
4669
4740
  })();
4670
- inFlightAccountInits.set(account.id, initPromise);
4671
4741
  try {
4672
4742
  await initPromise;
4743
+ resolveInit();
4744
+ } catch (error) {
4745
+ rejectInit(error);
4746
+ throw error;
4673
4747
  } finally {
4674
4748
  inFlightAccountInits.delete(account.id);
4675
4749
  }
@@ -4900,7 +4974,7 @@ async function startEmailOtpLogin(email, options = {}) {
4900
4974
  email: normalizedEmail,
4901
4975
  requestId,
4902
4976
  version: getQwenWebVersion() || "0.3.12",
4903
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
4977
+ timezone: formatAsciiTimezone()
4904
4978
  }
4905
4979
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
4906
4980
  if (!evalRes.ok || evalRes.data?.success !== true) {
@@ -4995,7 +5069,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
4995
5069
  code: cleanCode,
4996
5070
  requestId,
4997
5071
  version: getQwenWebVersion() || "0.3.12",
4998
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
5072
+ timezone: formatAsciiTimezone()
4999
5073
  }
5000
5074
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
5001
5075
  if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
@@ -5276,7 +5350,7 @@ async function loginViaApi(page, email, password) {
5276
5350
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5277
5351
  const requestId = crypto3.randomUUID();
5278
5352
  const evalRes = await page.evaluate(
5279
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
5353
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
5280
5354
  try {
5281
5355
  const response = await fetch(signinUrl2, {
5282
5356
  method: "POST",
@@ -5288,7 +5362,7 @@ async function loginViaApi(page, email, password) {
5288
5362
  source: "web",
5289
5363
  version: "0.3.11",
5290
5364
  "x-request-origin": "https://chat.qwen.ai",
5291
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5365
+ timezone: tz,
5292
5366
  "x-request-id": requestId2
5293
5367
  },
5294
5368
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5299,7 +5373,7 @@ async function loginViaApi(page, email, password) {
5299
5373
  return { ok: false, error: e.message };
5300
5374
  }
5301
5375
  },
5302
- { email, password: hashedPassword, signinUrl, requestId }
5376
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
5303
5377
  ).catch(() => null);
5304
5378
  let data = evalRes?.data;
5305
5379
  let signinSuccess = Boolean(
@@ -5308,7 +5382,7 @@ async function loginViaApi(page, email, password) {
5308
5382
  if (!signinSuccess && !data?.data?.code) {
5309
5383
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
5310
5384
  const fallbackRes = await page.evaluate(
5311
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
5385
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
5312
5386
  try {
5313
5387
  const response = await fetch(fallbackUrl2, {
5314
5388
  method: "POST",
@@ -5319,7 +5393,7 @@ async function loginViaApi(page, email, password) {
5319
5393
  "content-type": "application/json",
5320
5394
  source: "web",
5321
5395
  version: "0.3.11",
5322
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5396
+ timezone: tz,
5323
5397
  "x-request-id": requestId2
5324
5398
  },
5325
5399
  body: JSON.stringify({ email: email2, password: password2 })
@@ -5330,7 +5404,7 @@ async function loginViaApi(page, email, password) {
5330
5404
  return { ok: false, error: e.message };
5331
5405
  }
5332
5406
  },
5333
- { email, password: hashedPassword, fallbackUrl, requestId }
5407
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
5334
5408
  ).catch(() => null);
5335
5409
  if (fallbackRes?.data) {
5336
5410
  data = fallbackRes.data;
@@ -5662,7 +5736,7 @@ async function refreshAccountToken(accountId) {
5662
5736
  if (refreshToken) {
5663
5737
  try {
5664
5738
  const refreshResult = await page.evaluate(
5665
- async ({ rTok }) => {
5739
+ async ({ rTok, tz }) => {
5666
5740
  try {
5667
5741
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
5668
5742
  method: "GET",
@@ -5672,7 +5746,7 @@ async function refreshAccountToken(accountId) {
5672
5746
  source: "web",
5673
5747
  version: "0.3.11",
5674
5748
  "x-request-origin": "https://chat.qwen.ai",
5675
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
5749
+ timezone: tz,
5676
5750
  authorization: `Bearer ${rTok}`
5677
5751
  }
5678
5752
  });
@@ -5682,7 +5756,7 @@ async function refreshAccountToken(accountId) {
5682
5756
  return { ok: false, error: e.message };
5683
5757
  }
5684
5758
  },
5685
- { rTok: refreshToken }
5759
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
5686
5760
  );
5687
5761
  const json = refreshResult?.data;
5688
5762
  if (json && json.success === true && json.data) {
@@ -5720,6 +5794,7 @@ async function refreshAccountToken(accountId) {
5720
5794
  const cache = getHeaderCache(accountId);
5721
5795
  cache.headers.cookie = cookieStr;
5722
5796
  cache.lastRefresh = Date.now();
5797
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5723
5798
  markAccountHeadersReady(accountId);
5724
5799
  saveAuthSession2(accountId, {
5725
5800
  cookie: cookieStr,
@@ -5754,6 +5829,7 @@ async function refreshAccountToken(accountId) {
5754
5829
  const cache = getHeaderCache(accountId);
5755
5830
  cache.headers.cookie = cookieStr;
5756
5831
  cache.lastRefresh = Date.now();
5832
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
5757
5833
  markAccountHeadersReady(accountId);
5758
5834
  saveAuthSession2(accountId, {
5759
5835
  cookie: cookieStr,
@@ -6808,11 +6884,11 @@ async function keepAlivePlaywrightAccount(accountId) {
6808
6884
  const now = Date.now();
6809
6885
  const currentUrl = page.url();
6810
6886
  const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
6811
- const cookie = await getCookies(accountId);
6887
+ const cookie = await getCookies(accountId, true);
6812
6888
  if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
6813
6889
  lastProactiveTokenRefresh.set(accountId, now);
6814
- console.log(
6815
- `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6890
+ logger.debug(
6891
+ `[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6816
6892
  );
6817
6893
  const res = await refreshAccountToken(accountId);
6818
6894
  if (res.success) {
@@ -6820,6 +6896,9 @@ async function keepAlivePlaywrightAccount(accountId) {
6820
6896
  touchAccountActivity(accountId);
6821
6897
  return true;
6822
6898
  }
6899
+ logger.warn(
6900
+ `[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
6901
+ );
6823
6902
  }
6824
6903
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
6825
6904
  const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
@@ -7057,6 +7136,7 @@ var init_playwright = __esm({
7057
7136
  init_qwen_headers();
7058
7137
  init_paths();
7059
7138
  init_jwt();
7139
+ init_timezone();
7060
7140
  requireLocal = createRequire(import.meta.url);
7061
7141
  contextInitHooks = [];
7062
7142
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -7070,7 +7150,7 @@ var init_playwright = __esm({
7070
7150
  headerCaches = /* @__PURE__ */ new Map();
7071
7151
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
7072
7152
  HEADER_REFRESH_THRESHOLD = 0.8;
7073
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
7153
+ COOKIE_CACHE_TTL = 60 * 1e3;
7074
7154
  cookieCaches = /* @__PURE__ */ new Map();
7075
7155
  lastAccountActivity = /* @__PURE__ */ new Map();
7076
7156
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
@@ -8442,6 +8522,8 @@ __export(qwen_exports, {
8442
8522
  QwenUpstreamError: () => QwenUpstreamError,
8443
8523
  QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
8444
8524
  RetryableQwenStreamError: () => RetryableQwenStreamError,
8525
+ STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
8526
+ acquireAccountStreamLock: () => acquireAccountStreamLock,
8445
8527
  acquireNewQwenChatSession: () => acquireNewQwenChatSession,
8446
8528
  buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
8447
8529
  buildChatNewBody: () => buildChatNewBody,
@@ -8455,10 +8537,13 @@ __export(qwen_exports, {
8455
8537
  deleteAllQwenChats: () => deleteAllQwenChats,
8456
8538
  deleteSingleQwenChat: () => deleteSingleQwenChat,
8457
8539
  disableNativeTools: () => disableNativeTools,
8540
+ ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
8458
8541
  fetchQwenModels: () => fetchQwenModels,
8459
8542
  fetchRemoteQwenChats: () => fetchRemoteQwenChats,
8460
8543
  flushLogicalThreadState: () => flushLogicalThreadState,
8544
+ getAccountStreamMutex: () => getAccountStreamMutex,
8461
8545
  getAnyCachedQwenModels: () => getAnyCachedQwenModels,
8546
+ getBrowserFetchHeaders: () => getBrowserFetchHeaders,
8462
8547
  getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
8463
8548
  getLogicalThreadState: () => getLogicalThreadState,
8464
8549
  getQwenErrorCode: () => getQwenErrorCode,
@@ -8474,6 +8559,8 @@ __export(qwen_exports, {
8474
8559
  requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
8475
8560
  requestQwenTextInBrowser: () => requestQwenTextInBrowser,
8476
8561
  setToolCapNotice: () => setToolCapNotice,
8562
+ stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
8563
+ sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
8477
8564
  syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
8478
8565
  updateLogicalThreadParent: () => updateLogicalThreadParent,
8479
8566
  updateLogicalThreadState: () => updateLogicalThreadState,
@@ -8760,7 +8847,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
8760
8847
  function getAccountStreamMutex(accountId) {
8761
8848
  let slots2 = accountStreamMutexes.get(accountId);
8762
8849
  if (!slots2) {
8763
- slots2 = { active: 0, queue: [] };
8850
+ slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
8764
8851
  accountStreamMutexes.set(accountId, slots2);
8765
8852
  }
8766
8853
  return slots2;
@@ -8768,27 +8855,71 @@ function getAccountStreamMutex(accountId) {
8768
8855
  function streamSlotCapacity() {
8769
8856
  return Math.max(1, config.concurrency.maxStreamsPerAccount);
8770
8857
  }
8771
- function createStreamSlotRelease(slots2) {
8858
+ function createStreamSlotRelease(slots2, slotId) {
8772
8859
  let released = false;
8773
8860
  return () => {
8774
8861
  if (released) return;
8775
8862
  released = true;
8776
- slots2.active -= 1;
8863
+ slots2.activeSlots.delete(slotId);
8777
8864
  const next = slots2.queue.shift();
8778
8865
  if (next) {
8779
- slots2.active += 1;
8780
- next();
8866
+ const nextId = nextStreamSlotId++;
8867
+ slots2.activeSlots.set(nextId, Date.now());
8868
+ next(createStreamSlotRelease(slots2, nextId));
8781
8869
  }
8782
8870
  };
8783
8871
  }
8872
+ function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
8873
+ const now = Date.now();
8874
+ let sweptCount = 0;
8875
+ for (const [accountId, slots2] of accountStreamMutexes.entries()) {
8876
+ for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
8877
+ if (now - startedAt >= maxHoldMs) {
8878
+ logger.warn(
8879
+ `[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
8880
+ );
8881
+ slots2.activeSlots.delete(slotId);
8882
+ sweptCount++;
8883
+ const next = slots2.queue.shift();
8884
+ if (next) {
8885
+ const nextId = nextStreamSlotId++;
8886
+ slots2.activeSlots.set(nextId, now);
8887
+ next(createStreamSlotRelease(slots2, nextId));
8888
+ }
8889
+ }
8890
+ }
8891
+ }
8892
+ return sweptCount;
8893
+ }
8894
+ function ensureStaleSlotSweeperStarted() {
8895
+ if (staleSlotSweeperTimer) return;
8896
+ staleSlotSweeperTimer = setInterval(() => {
8897
+ try {
8898
+ sweepStaleAccountStreamSlots();
8899
+ } catch (err) {
8900
+ logger.error("[Qwen] Error in stale stream slot sweeper", {
8901
+ error: err instanceof Error ? err.message : String(err)
8902
+ });
8903
+ }
8904
+ }, 6e4);
8905
+ staleSlotSweeperTimer.unref();
8906
+ }
8907
+ function stopStaleSlotSweeperForTests() {
8908
+ if (staleSlotSweeperTimer) {
8909
+ clearInterval(staleSlotSweeperTimer);
8910
+ staleSlotSweeperTimer = null;
8911
+ }
8912
+ }
8784
8913
  async function acquireAccountStreamLock(accountId) {
8914
+ ensureStaleSlotSweeperStarted();
8785
8915
  const slots2 = getAccountStreamMutex(accountId);
8786
- if (slots2.active < streamSlotCapacity()) {
8787
- slots2.active += 1;
8788
- return Promise.resolve(createStreamSlotRelease(slots2));
8916
+ if (slots2.activeSlots.size < streamSlotCapacity()) {
8917
+ const slotId = nextStreamSlotId++;
8918
+ slots2.activeSlots.set(slotId, Date.now());
8919
+ return Promise.resolve(createStreamSlotRelease(slots2, slotId));
8789
8920
  }
8790
8921
  return new Promise((resolve) => {
8791
- slots2.queue.push(() => resolve(createStreamSlotRelease(slots2)));
8922
+ slots2.queue.push((releaseFn) => resolve(releaseFn));
8792
8923
  });
8793
8924
  }
8794
8925
  function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
@@ -8880,11 +9011,13 @@ function getBrowserFetchHeaders(headers) {
8880
9011
  "x-request-id",
8881
9012
  "x-accel-buffering"
8882
9013
  ]);
8883
- return Object.fromEntries(
8884
- Object.entries(headers).filter(
8885
- ([name]) => browserAllowedHeaders.has(name.toLowerCase())
8886
- )
8887
- );
9014
+ const result = {};
9015
+ for (const [name, value] of Object.entries(headers)) {
9016
+ if (browserAllowedHeaders.has(name.toLowerCase())) {
9017
+ result[name] = sanitizeHeaderForFetch(String(value ?? ""));
9018
+ }
9019
+ }
9020
+ return result;
8888
9021
  }
8889
9022
  async function requestQwenTextInBrowser(accountId, method, path6, headers, body, options = {}) {
8890
9023
  const url = qwenUrl(path6);
@@ -8905,7 +9038,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8905
9038
  browserHeaders["Content-Type"] = "application/json";
8906
9039
  }
8907
9040
  const evaluateRequest = (page) => page.evaluate(
8908
- async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
9041
+ async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
8909
9042
  const controller = new AbortController();
8910
9043
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
8911
9044
  try {
@@ -8929,7 +9062,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8929
9062
  source: "web",
8930
9063
  version: "0.3.11",
8931
9064
  "x-request-origin": "https://chat.qwen.ai",
8932
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
9065
+ timezone: tz,
8933
9066
  authorization: `Bearer ${rTok}`
8934
9067
  },
8935
9068
  signal: AbortSignal.timeout(5e3)
@@ -8974,7 +9107,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
8974
9107
  headers: browserHeaders,
8975
9108
  body,
8976
9109
  referrer: options.referrer,
8977
- timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
9110
+ timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
9111
+ tz: formatAsciiTimezone()
8978
9112
  }
8979
9113
  );
8980
9114
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -9652,7 +9786,7 @@ async function disableNativeTools(accountId) {
9652
9786
  async (page) => {
9653
9787
  const requestId = crypto5.randomUUID();
9654
9788
  const response = await page.evaluate(
9655
- async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
9789
+ async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
9656
9790
  const controller = new AbortController();
9657
9791
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
9658
9792
  try {
@@ -9664,7 +9798,7 @@ async function disableNativeTools(accountId) {
9664
9798
  accept: "application/json, text/plain, */*",
9665
9799
  "content-type": "application/json",
9666
9800
  "x-request-id": requestId2,
9667
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
9801
+ timezone: tz,
9668
9802
  source: "web"
9669
9803
  },
9670
9804
  body: JSON.stringify(payload2),
@@ -9676,7 +9810,7 @@ async function disableNativeTools(accountId) {
9676
9810
  clearTimeout(timeoutId);
9677
9811
  }
9678
9812
  },
9679
- { payload, timeoutMs: config.timeouts.http, requestId }
9813
+ { payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
9680
9814
  );
9681
9815
  return response;
9682
9816
  }
@@ -9893,7 +10027,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
9893
10027
  const requestHeaders = {
9894
10028
  source: "web",
9895
10029
  version: "0.2.89",
9896
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
10030
+ timezone: formatAsciiTimezone(),
9897
10031
  "x-request-id": crypto5.randomUUID(),
9898
10032
  Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
9899
10033
  };
@@ -9937,7 +10071,7 @@ async function fetchRemoteQwenChats(accountId) {
9937
10071
  }
9938
10072
  const requestHeaders = {
9939
10073
  version: "0.2.89",
9940
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
10074
+ timezone: formatAsciiTimezone(),
9941
10075
  "x-request-id": crypto5.randomUUID(),
9942
10076
  Referer: qwenUrl("/settings/chats")
9943
10077
  };
@@ -9989,7 +10123,7 @@ async function fetchQwenModels(accountId) {
9989
10123
  async (page) => {
9990
10124
  const requestId = crypto5.randomUUID();
9991
10125
  const response2 = await page.evaluate(
9992
- async ({ timeoutMs, requestId: requestId2 }) => {
10126
+ async ({ timeoutMs, requestId: requestId2, tz }) => {
9993
10127
  const controller = new AbortController();
9994
10128
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
9995
10129
  try {
@@ -9998,7 +10132,7 @@ async function fetchQwenModels(accountId) {
9998
10132
  headers: {
9999
10133
  accept: "application/json, text/plain, */*",
10000
10134
  "x-request-id": requestId2,
10001
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
10135
+ timezone: tz,
10002
10136
  source: "web"
10003
10137
  },
10004
10138
  signal: controller.signal
@@ -10008,7 +10142,7 @@ async function fetchQwenModels(accountId) {
10008
10142
  clearTimeout(timeoutId);
10009
10143
  }
10010
10144
  },
10011
- { timeoutMs: config.timeouts.http, requestId }
10145
+ { timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
10012
10146
  );
10013
10147
  return response2;
10014
10148
  }
@@ -10047,7 +10181,7 @@ async function fetchQwenModels(accountId) {
10047
10181
  bxUa,
10048
10182
  bxUmidtoken,
10049
10183
  extra: {
10050
- timezone: (/* @__PURE__ */ new Date()).toString()
10184
+ timezone: formatAsciiTimezone()
10051
10185
  }
10052
10186
  }),
10053
10187
  void 0,
@@ -10810,7 +10944,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10810
10944
  clearTimeout(timeoutId);
10811
10945
  }
10812
10946
  }
10813
- var sleep4, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, accountStreamMutexes, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
10947
+ var sleep4, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, accountStreamMutexes, nextStreamSlotId, staleSlotSweeperTimer, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
10814
10948
  var init_qwen = __esm({
10815
10949
  "src/services/qwen.ts"() {
10816
10950
  "use strict";
@@ -10828,6 +10962,7 @@ var init_qwen = __esm({
10828
10962
  init_captcha_coordinator();
10829
10963
  init_captcha_solver();
10830
10964
  init_account_concurrency();
10965
+ init_timezone();
10831
10966
  init_qwen_errors();
10832
10967
  init_qwen_thread_state();
10833
10968
  init_qwen_chat_pool();
@@ -10857,7 +10992,10 @@ var init_qwen = __esm({
10857
10992
  directSettingsFetchBlocked = /* @__PURE__ */ new Set();
10858
10993
  DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
10859
10994
  activePersonalizationByAccount = /* @__PURE__ */ new Map();
10995
+ STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
10860
10996
  accountStreamMutexes = /* @__PURE__ */ new Map();
10997
+ nextStreamSlotId = 1;
10998
+ staleSlotSweeperTimer = null;
10861
10999
  QWEN_SAFE_SETTINGS_PATCH = {
10862
11000
  ui: {
10863
11001
  autoTags: false,