qwenproxy-cli 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -296,7 +296,26 @@ function runMigrations(db2) {
296
296
  instruction_hash TEXT NOT NULL,
297
297
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
298
298
  );
299
+
300
+ CREATE TABLE IF NOT EXISTS account_daily_usage (
301
+ account_id TEXT NOT NULL,
302
+ date_utc TEXT NOT NULL,
303
+ prompt_tokens INTEGER NOT NULL DEFAULT 0,
304
+ completion_tokens INTEGER NOT NULL DEFAULT 0,
305
+ weighted_tokens REAL NOT NULL DEFAULT 0,
306
+ total_tokens INTEGER NOT NULL DEFAULT 0,
307
+ turns_count INTEGER NOT NULL DEFAULT 0,
308
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
309
+ PRIMARY KEY (account_id, date_utc)
310
+ );
311
+
312
+ CREATE INDEX IF NOT EXISTS idx_daily_usage_date ON account_daily_usage(date_utc);
299
313
  `);
314
+ try {
315
+ db2.exec(`ALTER TABLE account_daily_usage ADD COLUMN weighted_tokens REAL DEFAULT 0;`);
316
+ } catch (err) {
317
+ if (!isDuplicateColumnError(err)) throw err;
318
+ }
300
319
  try {
301
320
  db2.exec(
302
321
  `ALTER TABLE accounts ADD COLUMN cooldown_until INTEGER DEFAULT 0;`
@@ -397,7 +416,11 @@ function saveAuthSession(accountId, session) {
397
416
  ) VALUES (
398
417
  ?, ?, ?, ?, ?, ?,
399
418
  ?, ?, ?, ?,
400
- ?, ?, ?, ?, datetime('now')
419
+ COALESCE(?, (SELECT user_id FROM qwen_auth_sessions WHERE account_id = ?)),
420
+ ?,
421
+ COALESCE(?, (SELECT refresh_token FROM qwen_auth_sessions WHERE account_id = ?)),
422
+ ?,
423
+ datetime('now')
401
424
  )
402
425
  `);
403
426
  stmt.run(
@@ -412,8 +435,10 @@ function saveAuthSession(accountId, session) {
412
435
  session.secChUaPlatform || null,
413
436
  session.version || null,
414
437
  session.userId || null,
438
+ accountId,
415
439
  session.tokenExpiresAt || null,
416
440
  session.refreshToken || null,
441
+ accountId,
417
442
  session.capturedAt ?? Date.now()
418
443
  );
419
444
  }
@@ -4039,6 +4064,11 @@ async function getBasicHeaders(accountId) {
4039
4064
  if (!page) {
4040
4065
  throw new Error(`Playwright not initialized for account: ${accountId}`);
4041
4066
  }
4067
+ const inFlightInit = inFlightAccountInits.get(accountId);
4068
+ if (inFlightInit) {
4069
+ await inFlightInit.catch(() => {
4070
+ });
4071
+ }
4042
4072
  const release = await acquireAccountMutex(
4043
4073
  accountId,
4044
4074
  `headers:${accountId.substring(0, 12)}`
@@ -5513,6 +5543,11 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
5513
5543
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5514
5544
  const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
5515
5545
  const tokenExpiry = parseJwtExpiry2(capturedHeaders.cookie);
5546
+ let rTok;
5547
+ try {
5548
+ rTok = (await page.evaluate(() => localStorage.getItem("refresh_token") || "")).trim() || void 0;
5549
+ } catch {
5550
+ }
5516
5551
  saveAuthSession2(accountId, {
5517
5552
  cookie: capturedHeaders.cookie,
5518
5553
  userAgent: capturedHeaders["user-agent"],
@@ -5524,6 +5559,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
5524
5559
  secChUaPlatform: capturedHeaders["sec-ch-ua-platform"],
5525
5560
  version: capturedHeaders["version"],
5526
5561
  tokenExpiresAt: tokenExpiry || void 0,
5562
+ refreshToken: rTok,
5527
5563
  capturedAt: Date.now()
5528
5564
  });
5529
5565
  } catch {
@@ -5555,38 +5591,57 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
5555
5591
  const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
5556
5592
  const isSuspectedGuest = isAuthUrl || attempt >= 2 && !await isPageLoggedIn(page, 5e3);
5557
5593
  if (isSuspectedGuest) {
5558
- const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5559
- const creds = getAccountCredentials2(accountId);
5560
- if (creds && creds.email && creds.password) {
5561
- console.warn(
5562
- `\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
5594
+ const silentRefresh = await refreshAccountToken(accountId);
5595
+ if (silentRefresh.success) {
5596
+ console.log(
5597
+ `\u26A1 [Playwright] Token refreshed silently during header capture for ${accountId}`
5563
5598
  );
5564
- armOverallDeadline();
5565
- const ok = await loginToQwen(accountId, creds.email, creds.password);
5566
- if (ok) {
5599
+ if (isAuthUrl) {
5567
5600
  await page.goto(qwenUrl("/"), {
5568
5601
  waitUntil: "domcontentloaded",
5569
5602
  timeout: Math.min(config.timeouts.navigation, timeoutMs)
5570
5603
  });
5571
- await sleep2(2e3);
5604
+ await sleep2(1500);
5572
5605
  }
5573
- if (!ok || !await isPageLoggedIn(page, 6e3)) {
5606
+ if (await isPageLoggedIn(page, 5e3)) {
5607
+ armOverallDeadline();
5608
+ }
5609
+ }
5610
+ const stillSuspected = !silentRefresh.success || !await isPageLoggedIn(page, 2e3);
5611
+ if (stillSuspected) {
5612
+ const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
5613
+ const creds = getAccountCredentials2(accountId);
5614
+ if (creds && creds.email && creds.password) {
5615
+ console.warn(
5616
+ `\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
5617
+ );
5618
+ armOverallDeadline();
5619
+ const ok = await loginToQwen(accountId, creds.email, creds.password);
5620
+ if (ok) {
5621
+ await page.goto(qwenUrl("/"), {
5622
+ waitUntil: "domcontentloaded",
5623
+ timeout: Math.min(config.timeouts.navigation, timeoutMs)
5624
+ });
5625
+ await sleep2(2e3);
5626
+ }
5627
+ if (!ok || !await isPageLoggedIn(page, 6e3)) {
5628
+ settle(
5629
+ new Error(
5630
+ `Header capture failed for ${accountId}: re-login after session expiry did not succeed`
5631
+ )
5632
+ );
5633
+ return;
5634
+ }
5635
+ armOverallDeadline();
5636
+ if (settled || page.isClosed()) return;
5637
+ } else {
5574
5638
  settle(
5575
5639
  new Error(
5576
- `Header capture failed for ${accountId}: re-login after session expiry did not succeed`
5640
+ `Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
5577
5641
  )
5578
5642
  );
5579
5643
  return;
5580
5644
  }
5581
- armOverallDeadline();
5582
- if (settled || page.isClosed()) return;
5583
- } else {
5584
- settle(
5585
- new Error(
5586
- `Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
5587
- )
5588
- );
5589
- return;
5590
5645
  }
5591
5646
  }
5592
5647
  if (settled || page.isClosed()) return;
@@ -5902,7 +5957,13 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5902
5957
  `[Playwright] Navigation check failed during refresh for ${accountId}:`,
5903
5958
  navErr.message
5904
5959
  );
5905
- await executeReauth();
5960
+ if (await isPageLoggedIn(page, 3e3)) {
5961
+ } else {
5962
+ const refreshed = await refreshAccountToken(accountId);
5963
+ if (!refreshed.success) {
5964
+ await executeReauth();
5965
+ }
5966
+ }
5906
5967
  }
5907
5968
  }
5908
5969
  if (hasRequiredQwenHeaders(cache.headers)) {
@@ -5916,6 +5977,11 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5916
5977
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
5917
5978
  const tokenCookie = liveCookies.find((c) => c.name === "token");
5918
5979
  const exp = tokenCookie ? parseJwtExpiry(tokenCookie.value) : void 0;
5980
+ let rTok;
5981
+ try {
5982
+ rTok = (await page.evaluate(() => localStorage.getItem("refresh_token") || "")).trim() || void 0;
5983
+ } catch {
5984
+ }
5919
5985
  saveAuthSession2(accountId, {
5920
5986
  cookie: cookieStr,
5921
5987
  userAgent: cache.headers["user-agent"] || "",
@@ -5927,6 +5993,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
5927
5993
  secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
5928
5994
  version: cache.headers["version"] || void 0,
5929
5995
  tokenExpiresAt: exp || void 0,
5996
+ refreshToken: rTok,
5930
5997
  capturedAt: Date.now()
5931
5998
  });
5932
5999
  } catch {
@@ -6313,8 +6380,10 @@ async function keepAlivePlaywrightAccount(accountId) {
6313
6380
  if (!page || page.isClosed()) return false;
6314
6381
  const now = Date.now();
6315
6382
  const currentUrl = page.url();
6383
+ const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
6316
6384
  const cookie = await getCookies(accountId);
6317
- if (cookie && isTokenExpiringSoon(cookie, 5)) {
6385
+ if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
6386
+ lastProactiveTokenRefresh.set(accountId, now);
6318
6387
  console.log(
6319
6388
  `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
6320
6389
  );
@@ -6358,6 +6427,7 @@ function cleanupPlaywrightAccountState(accountId) {
6358
6427
  cookieCaches.delete(accountId);
6359
6428
  lastAccountActivity.delete(accountId);
6360
6429
  lastKeepAliveNavigation.delete(accountId);
6430
+ lastProactiveTokenRefresh.delete(accountId);
6361
6431
  clearFingerprintCache(accountId);
6362
6432
  unmarkAccountHeadersReady(accountId);
6363
6433
  }
@@ -6541,7 +6611,7 @@ async function getTokenDiagnostics(accountId) {
6541
6611
  }
6542
6612
  };
6543
6613
  }
6544
- var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
6614
+ var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
6545
6615
  var init_playwright = __esm({
6546
6616
  "src/services/playwright.ts"() {
6547
6617
  "use strict";
@@ -6577,6 +6647,7 @@ var init_playwright = __esm({
6577
6647
  cookieCaches = /* @__PURE__ */ new Map();
6578
6648
  lastAccountActivity = /* @__PURE__ */ new Map();
6579
6649
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
6650
+ lastProactiveTokenRefresh = /* @__PURE__ */ new Map();
6580
6651
  profileResetQueue = /* @__PURE__ */ new Map();
6581
6652
  profileResetChain = Promise.resolve();
6582
6653
  closingAllPlaywright = false;
@@ -7112,9 +7183,13 @@ var init_model_registry = __esm({
7112
7183
  // src/services/captcha-coordinator.ts
7113
7184
  var captcha_coordinator_exports = {};
7114
7185
  __export(captcha_coordinator_exports, {
7186
+ isCaptchaRecoveryActive: () => isCaptchaRecoveryActive,
7115
7187
  recoverBaxiaCaptcha: () => recoverBaxiaCaptcha,
7116
7188
  solveChallengeOnPage: () => solveChallengeOnPage
7117
7189
  });
7190
+ function isCaptchaRecoveryActive(accountId) {
7191
+ return activeCaptchaRecoveries.has(accountId);
7192
+ }
7118
7193
  async function gotoBestEffort(page, url) {
7119
7194
  await page.goto(url, {
7120
7195
  waitUntil: "domcontentloaded",
@@ -7157,11 +7232,12 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
7157
7232
  return false;
7158
7233
  }
7159
7234
  metrics.increment("captcha.challenges.detected", 1, { solver });
7160
- const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
7161
7235
  const solverOperationTimeoutMs = Math.max(
7162
7236
  15e3,
7163
7237
  config.captcha.timeoutMs + config.captcha.maxAttempts * (3e3 + config.captcha.retryDelayMs + config.captcha.settleMs) + 2 * CHALLENGE_NAVIGATION_TIMEOUT_MS
7164
7238
  );
7239
+ const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
7240
+ activeCaptchaRecoveries.add(accountId);
7165
7241
  try {
7166
7242
  const solved = await withAccountPage(
7167
7243
  accountId,
@@ -7194,9 +7270,11 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
7194
7270
  detail
7195
7271
  });
7196
7272
  return false;
7273
+ } finally {
7274
+ activeCaptchaRecoveries.delete(accountId);
7197
7275
  }
7198
7276
  }
7199
- var CHALLENGE_NAVIGATION_TIMEOUT_MS, CHALLENGE_PATH_MARKER, FAILED_RECOVERY_BACKOFF_MS, lastFailedRecoveryAt;
7277
+ var CHALLENGE_NAVIGATION_TIMEOUT_MS, CHALLENGE_PATH_MARKER, FAILED_RECOVERY_BACKOFF_MS, lastFailedRecoveryAt, activeCaptchaRecoveries;
7200
7278
  var init_captcha_coordinator = __esm({
7201
7279
  "src/services/captcha-coordinator.ts"() {
7202
7280
  "use strict";
@@ -7209,6 +7287,7 @@ var init_captcha_coordinator = __esm({
7209
7287
  CHALLENGE_PATH_MARKER = "_____tmd_____";
7210
7288
  FAILED_RECOVERY_BACKOFF_MS = 3e4;
7211
7289
  lastFailedRecoveryAt = /* @__PURE__ */ new Map();
7290
+ activeCaptchaRecoveries = /* @__PURE__ */ new Set();
7212
7291
  }
7213
7292
  });
7214
7293
 
@@ -7941,6 +8020,7 @@ __export(qwen_exports, {
7941
8020
  fetchRemoteQwenChats: () => fetchRemoteQwenChats,
7942
8021
  flushLogicalThreadState: () => flushLogicalThreadState,
7943
8022
  getAnyCachedQwenModels: () => getAnyCachedQwenModels,
8023
+ getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
7944
8024
  getLogicalThreadState: () => getLogicalThreadState,
7945
8025
  getQwenErrorCode: () => getQwenErrorCode,
7946
8026
  getSessionParent: () => getSessionParent,
@@ -8873,12 +8953,51 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
8873
8953
  captchaWatcher?.stop();
8874
8954
  }
8875
8955
  }
8956
+ function getInFlightPersonalizationCount() {
8957
+ return inFlightPersonalizationSyncs.size;
8958
+ }
8876
8959
  async function syncQwenRequestPersonalization(instruction, accountId, metadata = {}) {
8877
- if (isAuthMockEnabled()) {
8878
- if (process.env.TEST_PERSONALIZATION_SYNC_FAIL === "true") return false;
8879
- return true;
8880
- }
8881
8960
  const cacheKey = accountId || "global";
8961
+ const sent = textSize(instruction);
8962
+ const syncHash = sent.hash ? `${sent.hash}:${QWEN_SAFE_SETTINGS_HASH}` : null;
8963
+ const inFlightKey = `${cacheKey}:${syncHash || "empty"}`;
8964
+ const existingInFlight = inFlightPersonalizationSyncs.get(inFlightKey);
8965
+ if (existingInFlight) {
8966
+ logger.debug("[Qwen] Personalization sync already in flight, sharing promise", {
8967
+ accountId: cacheKey
8968
+ });
8969
+ return existingInFlight;
8970
+ }
8971
+ let syncPromise;
8972
+ syncPromise = (async () => {
8973
+ await Promise.resolve();
8974
+ try {
8975
+ if (isAuthMockEnabled()) {
8976
+ const delay = parseInt(process.env.TEST_PERSONALIZATION_DELAY_MS || "0", 10);
8977
+ if (delay > 0) {
8978
+ await sleep4(delay);
8979
+ }
8980
+ if (process.env.TEST_PERSONALIZATION_SYNC_FAIL === "true") return false;
8981
+ return true;
8982
+ }
8983
+ return await syncQwenRequestPersonalizationInternal(
8984
+ instruction,
8985
+ accountId,
8986
+ metadata,
8987
+ cacheKey,
8988
+ sent,
8989
+ syncHash
8990
+ );
8991
+ } finally {
8992
+ if (inFlightPersonalizationSyncs.get(inFlightKey) === syncPromise) {
8993
+ inFlightPersonalizationSyncs.delete(inFlightKey);
8994
+ }
8995
+ }
8996
+ })();
8997
+ inFlightPersonalizationSyncs.set(inFlightKey, syncPromise);
8998
+ return syncPromise;
8999
+ }
9000
+ async function syncQwenRequestPersonalizationInternal(instruction, accountId, metadata, cacheKey, sent, syncHash) {
8882
9001
  let forceRefresh = false;
8883
9002
  try {
8884
9003
  const basic = await getBasicHeaders2(accountId);
@@ -8896,8 +9015,6 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
8896
9015
  });
8897
9016
  let currentSettings = null;
8898
9017
  let payload = buildQwenSettingsUpdatePayload(currentSettings, instruction);
8899
- const sent = textSize(instruction);
8900
- const syncHash = sent.hash ? `${sent.hash}:${QWEN_SAFE_SETTINGS_HASH}` : null;
8901
9018
  const bypassCache = metadata.forceSync === true;
8902
9019
  const cachedHash = lastSyncedPersonalizationHashes.get(cacheKey);
8903
9020
  if (!bypassCache && syncHash && cachedHash === syncHash) {
@@ -9567,9 +9684,13 @@ function parseQwenJsonError(raw, status, accountId) {
9567
9684
  }
9568
9685
  if (errorJson?.success === false) {
9569
9686
  const code = errorJson.data?.code || errorJson.code || "UpstreamError";
9570
- if (status === 401 || code === "Unauthorized" || typeof details === "string" && (details.includes("login") || details.includes("session"))) {
9687
+ const detailsStr = typeof details === "string" ? details : "";
9688
+ const codeStr = String(code).toLowerCase();
9689
+ const detailsLower = detailsStr.toLowerCase();
9690
+ const isAuthExpired = status === 401 || codeStr.includes("unauthorized") || detailsLower.includes("token has expired") || detailsLower.includes("log in") || detailsLower.includes("login") || detailsLower.includes("session") || detailsLower.includes("n\xE3o autorizado") || detailsLower.includes("unauthorized");
9691
+ if (isAuthExpired) {
9571
9692
  return new QwenSessionExpiredError(
9572
- `Session expired: ${details}`,
9693
+ `Session expired: ${detailsStr || codeStr}`,
9573
9694
  accountId || "global"
9574
9695
  );
9575
9696
  }
@@ -10108,8 +10229,24 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10108
10229
  )
10109
10230
  );
10110
10231
  }
10232
+ const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
10233
+ if (parsedJsonErr instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
10234
+ retriedNonSseResponse = true;
10235
+ logger.warn(
10236
+ "[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
10237
+ { accountId, chatId: chatSessionId ?? "new" }
10238
+ );
10239
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10240
+ const refreshed = await refreshAccountToken2(accountId);
10241
+ if (refreshed.success) {
10242
+ const fresh = await getQwenHeaders(false, accountId);
10243
+ activeHeaders = fresh.headers;
10244
+ response = await fetchCompletion(activeHeaders);
10245
+ continue;
10246
+ }
10247
+ }
10111
10248
  throw withCreatedChatMetadata(
10112
- parseQwenJsonError(errText, response.status, accountId) ?? new QwenUpstreamError(
10249
+ parsedJsonErr ?? new QwenUpstreamError(
10113
10250
  `Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
10114
10251
  "NonStreamJsonResponse",
10115
10252
  502
@@ -10160,6 +10297,31 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10160
10297
  response.status,
10161
10298
  accountId
10162
10299
  );
10300
+ if (parsedError instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
10301
+ retriedNonSseResponse = true;
10302
+ logger.warn(
10303
+ "[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
10304
+ { accountId, chatId: chatSessionId ?? "new", status: response.status }
10305
+ );
10306
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10307
+ const refreshed = await refreshAccountToken2(accountId);
10308
+ if (refreshed.success) {
10309
+ const fresh = await getQwenHeaders(false, accountId);
10310
+ activeHeaders = fresh.headers;
10311
+ response = await fetchCompletion(activeHeaders);
10312
+ if (response.ok && response.body) {
10313
+ return {
10314
+ stream: wrapUpstreamStream(response.body, controller),
10315
+ headers: activeHeaders,
10316
+ uiSessionId: chatSessionId || "",
10317
+ controller,
10318
+ accountId: accountId ?? "global",
10319
+ createdNewChat,
10320
+ tokenEstimationContext
10321
+ };
10322
+ }
10323
+ }
10324
+ }
10163
10325
  if (parsedError) {
10164
10326
  throw withCreatedChatMetadata(parsedError);
10165
10327
  }
@@ -10197,7 +10359,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
10197
10359
  clearTimeout(timeoutId);
10198
10360
  }
10199
10361
  }
10200
- 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, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
10362
+ 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;
10201
10363
  var init_qwen = __esm({
10202
10364
  "src/services/qwen.ts"() {
10203
10365
  "use strict";
@@ -10284,6 +10446,7 @@ var init_qwen = __esm({
10284
10446
  }
10285
10447
  };
10286
10448
  QWEN_SAFE_SETTINGS_HASH = crypto5.createHash("sha256").update(JSON.stringify(QWEN_SAFE_SETTINGS_PATCH)).digest("hex").slice(0, 12);
10449
+ inFlightPersonalizationSyncs = /* @__PURE__ */ new Map();
10287
10450
  DISABLE_TOOLS_MAX_RETRIES = 3;
10288
10451
  DISABLE_TOOLS_BACKOFF_MS = 2e3;
10289
10452
  UPSTREAM_RESPONSE_PREVIEW_BYTES = 8 * 1024;