qwenproxy-cli 1.6.11 → 1.6.12

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/index.js CHANGED
@@ -266,7 +266,7 @@ var init_config = __esm({
266
266
  // Version header on Qwen API requests = the deployed web bundle version
267
267
  // (fallback when dynamic DOM detection is not yet completed).
268
268
  // Override via env when Qwen ships a new bundle.
269
- QWEN_WEB_VERSION: z.string().default("0.3.26"),
269
+ QWEN_WEB_VERSION: z.string().default("0.3.43"),
270
270
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
271
271
  // (chats/new, settings): those work without them (live-probed). The
272
272
  // completions path is the exception — the 0.2.86 HAR shows the real
@@ -309,6 +309,7 @@ var init_config = __esm({
309
309
  CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
310
310
  CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
311
311
  CAPTCHA_SOLVER_TIMEOUT_MS: z.string().default("15000"),
312
+ CAPTCHA_SLIDER_TIMEOUT_MS: z.string().default("8000"),
312
313
  CAPTCHA_SOLVER_RETRY_DELAY_MS: z.string().default("1000"),
313
314
  CAPTCHA_SOLVER_SETTLE_MS: z.string().default("2000"),
314
315
  CAPTCHA_ACCOUNT_COOLDOWN_MS: z.string().default("120000"),
@@ -460,6 +461,8 @@ var init_config = __esm({
460
461
  enabled: env.CAPTCHA_SOLVER_ENABLED === "true",
461
462
  maxAttempts: Math.max(1, Math.min(5, parseInt(env.CAPTCHA_SOLVER_MAX_ATTEMPTS))),
462
463
  timeoutMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_TIMEOUT_MS)),
464
+ /** Max wait for a challenge slider to become visible (slow WAF renders). */
465
+ sliderTimeoutMs: Math.max(500, parseInt(env.CAPTCHA_SLIDER_TIMEOUT_MS)),
463
466
  retryDelayMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_RETRY_DELAY_MS)),
464
467
  settleMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_SETTLE_MS)),
465
468
  /** Rest an account whose challenge could not be cleared before reusing it. */
@@ -3619,6 +3622,294 @@ function extractBaxiaChallengeUrl(body, baseUrl) {
3619
3622
  }
3620
3623
  return null;
3621
3624
  }
3625
+ async function findVisibleLocatorWithin(page, selectors, timeoutMs) {
3626
+ const deadline = Date.now() + Math.max(0, timeoutMs);
3627
+ while (true) {
3628
+ const found = await findVisibleLocator(page, selectors);
3629
+ if (found) return found;
3630
+ if (Date.now() >= deadline) return null;
3631
+ await sleep(Math.min(150, Math.max(1, deadline - Date.now())));
3632
+ }
3633
+ }
3634
+ async function waitForHidden(locator, timeoutMs) {
3635
+ const deadline = Date.now() + Math.max(0, timeoutMs);
3636
+ while (true) {
3637
+ if (!await isVisible(locator)) return true;
3638
+ if (Date.now() >= deadline) return false;
3639
+ await sleep(Math.min(100, Math.max(1, deadline - Date.now())));
3640
+ }
3641
+ }
3642
+ async function computeAliyunPuzzleOffset(page) {
3643
+ return page.evaluate(() => {
3644
+ const background = document.querySelector(
3645
+ "#aliyunCaptcha-img"
3646
+ );
3647
+ const piece = document.querySelector(
3648
+ "#aliyunCaptcha-puzzle"
3649
+ );
3650
+ if (!background || !piece) {
3651
+ return Promise.resolve({ ok: false, reason: "missing puzzle images" });
3652
+ }
3653
+ const compute = () => {
3654
+ try {
3655
+ const width = background.naturalWidth;
3656
+ const height = background.naturalHeight;
3657
+ const pieceWidth = piece.naturalWidth;
3658
+ const pieceHeight = piece.naturalHeight;
3659
+ if (!width || !height || !pieceWidth || !pieceHeight) {
3660
+ return { ok: false, reason: "image dimensions unavailable" };
3661
+ }
3662
+ const backgroundCanvas = document.createElement("canvas");
3663
+ backgroundCanvas.id = "captcha-solver-canvas";
3664
+ backgroundCanvas.width = width;
3665
+ backgroundCanvas.height = height;
3666
+ const backgroundContext = backgroundCanvas.getContext("2d");
3667
+ if (!backgroundContext) {
3668
+ return { ok: false, reason: "no background context" };
3669
+ }
3670
+ const pieceCanvas = document.createElement("canvas");
3671
+ pieceCanvas.id = "captcha-solver-canvas";
3672
+ pieceCanvas.width = pieceWidth;
3673
+ pieceCanvas.height = pieceHeight;
3674
+ const pieceContext = pieceCanvas.getContext("2d");
3675
+ if (!pieceContext) {
3676
+ return { ok: false, reason: "no piece context" };
3677
+ }
3678
+ try {
3679
+ backgroundContext.drawImage(background, 0, 0);
3680
+ } catch {
3681
+ return { ok: false, reason: "background canvas tainted" };
3682
+ }
3683
+ try {
3684
+ pieceContext.drawImage(piece, 0, 0);
3685
+ } catch {
3686
+ return { ok: false, reason: "puzzle canvas tainted" };
3687
+ }
3688
+ const backgroundData = backgroundContext.getImageData(
3689
+ 0,
3690
+ 0,
3691
+ width,
3692
+ height
3693
+ ).data;
3694
+ const pieceData = pieceContext.getImageData(
3695
+ 0,
3696
+ 0,
3697
+ pieceWidth,
3698
+ pieceHeight
3699
+ ).data;
3700
+ let pieceMinX = pieceWidth;
3701
+ let pieceMaxX = -1;
3702
+ let pieceMinY = pieceHeight;
3703
+ let pieceMaxY = -1;
3704
+ for (let y = 0; y < pieceHeight; y++) {
3705
+ for (let x = 0; x < pieceWidth; x++) {
3706
+ if (pieceData[(y * pieceWidth + x) * 4 + 3] > 100) {
3707
+ if (x < pieceMinX) pieceMinX = x;
3708
+ if (x > pieceMaxX) pieceMaxX = x;
3709
+ if (y < pieceMinY) pieceMinY = y;
3710
+ if (y > pieceMaxY) pieceMaxY = y;
3711
+ }
3712
+ }
3713
+ }
3714
+ const puzzleWidth = pieceMaxX - pieceMinX + 1;
3715
+ if (puzzleWidth <= 0) {
3716
+ return { ok: false, reason: "empty puzzle piece" };
3717
+ }
3718
+ const yStart = Math.max(1, Math.min(height - 2, pieceMinY + 5));
3719
+ const yEnd = Math.max(
3720
+ yStart,
3721
+ Math.min(height - 2, pieceMaxY - 5)
3722
+ );
3723
+ const columnGradients = new Float32Array(width);
3724
+ for (let x = 1; x < width - 1; x++) {
3725
+ let gradient = 0;
3726
+ for (let y = yStart; y <= yEnd; y++) {
3727
+ const left = (y * width + x - 1) * 4;
3728
+ const right = (y * width + x + 1) * 4;
3729
+ gradient += Math.abs(backgroundData[right] - backgroundData[left]) + Math.abs(backgroundData[right + 1] - backgroundData[left + 1]) + Math.abs(backgroundData[right + 2] - backgroundData[left + 2]);
3730
+ }
3731
+ columnGradients[x] = gradient;
3732
+ }
3733
+ let bestX = 0;
3734
+ let bestScore = 0;
3735
+ const minCandidateX = 30;
3736
+ const maxCandidateX = width - puzzleWidth - 20;
3737
+ for (let candidateX = minCandidateX; candidateX <= maxCandidateX; candidateX++) {
3738
+ let leftMax = 0;
3739
+ for (let dx = -2; dx <= 2; dx++) {
3740
+ const value = columnGradients[candidateX + pieceMinX + dx] || 0;
3741
+ if (value > leftMax) leftMax = value;
3742
+ }
3743
+ let rightMax = 0;
3744
+ for (let dx = -3; dx <= 1; dx++) {
3745
+ const value = columnGradients[candidateX + pieceMaxX + dx] || 0;
3746
+ if (value > rightMax) rightMax = value;
3747
+ }
3748
+ const score = Math.sqrt(leftMax * rightMax);
3749
+ if (score > bestScore) {
3750
+ bestScore = score;
3751
+ bestX = candidateX;
3752
+ }
3753
+ }
3754
+ if (bestScore <= 0) {
3755
+ return { ok: false, reason: "no matching hole edge" };
3756
+ }
3757
+ return {
3758
+ ok: true,
3759
+ offsetX: bestX - pieceMinX,
3760
+ naturalWidth: width
3761
+ };
3762
+ } catch (error) {
3763
+ return {
3764
+ ok: false,
3765
+ reason: error instanceof Error ? error.message : String(error)
3766
+ };
3767
+ }
3768
+ };
3769
+ return new Promise((resolve) => {
3770
+ if (background.complete && piece.complete) {
3771
+ resolve(compute());
3772
+ return;
3773
+ }
3774
+ let settled = false;
3775
+ const finish = () => {
3776
+ if (settled) return;
3777
+ settled = true;
3778
+ resolve(compute());
3779
+ };
3780
+ const fail = () => {
3781
+ if (settled) return;
3782
+ settled = true;
3783
+ resolve({ ok: false, reason: "puzzle image load error" });
3784
+ };
3785
+ background.addEventListener("load", finish, { once: true });
3786
+ piece.addEventListener("load", finish, { once: true });
3787
+ background.addEventListener("error", fail, { once: true });
3788
+ piece.addEventListener("error", fail, { once: true });
3789
+ setTimeout(() => {
3790
+ if (settled) return;
3791
+ settled = true;
3792
+ resolve({ ok: false, reason: "puzzle image load timeout" });
3793
+ }, 2e3);
3794
+ });
3795
+ });
3796
+ }
3797
+ async function solveAliyunWafCaptcha(page, options = {}) {
3798
+ const waitForMs = Math.max(0, options.waitForMs ?? 0);
3799
+ const maxAttempts = Math.max(
3800
+ 1,
3801
+ Math.min(5, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS)
3802
+ );
3803
+ const retryDelayMs = Math.max(
3804
+ 0,
3805
+ options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
3806
+ );
3807
+ const settleMs = Math.max(0, options.settleMs ?? DEFAULT_SETTLE_MS);
3808
+ const sliderTimeoutMs = Math.max(
3809
+ 500,
3810
+ options.sliderTimeoutMs ?? DEFAULT_SLIDER_TIMEOUT_MS
3811
+ );
3812
+ const detected = await findVisibleLocatorWithin(
3813
+ page,
3814
+ ALIYUN_WAF_BLOCK_SELECTORS,
3815
+ waitForMs
3816
+ );
3817
+ if (!detected) return false;
3818
+ return withCaptchaMouseLock(async () => {
3819
+ const block = detected.locator;
3820
+ if (!await isVisible(block)) return false;
3821
+ logBaxiaCaptcha("aliyun_detected", { selector: detected.selector });
3822
+ let lastReason = "unknown";
3823
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3824
+ try {
3825
+ const offset = await computeAliyunPuzzleOffset(page);
3826
+ if (!offset.ok || typeof offset.offsetX !== "number" || typeof offset.naturalWidth !== "number" || offset.naturalWidth <= 0) {
3827
+ lastReason = offset.reason || "offset_unavailable";
3828
+ logBaxiaCaptcha("aliyun_offset_failed", {
3829
+ attempt,
3830
+ reason: lastReason
3831
+ });
3832
+ } else {
3833
+ const slider = page.locator(ALIYUN_PUZZLE_SLIDER_SELECTOR).first();
3834
+ const track = page.locator(ALIYUN_PUZZLE_TRACK_SELECTOR).first();
3835
+ await slider.waitFor({ state: "visible", timeout: sliderTimeoutMs });
3836
+ const [sliderBox, trackBox] = await Promise.all([
3837
+ slider.boundingBox(),
3838
+ track.boundingBox()
3839
+ ]);
3840
+ if (!sliderBox || !trackBox || trackBox.width <= 0) {
3841
+ lastReason = "bounds_unavailable";
3842
+ logBaxiaCaptcha("aliyun_offset_failed", {
3843
+ attempt,
3844
+ reason: lastReason
3845
+ });
3846
+ } else {
3847
+ const scale = trackBox.width / offset.naturalWidth;
3848
+ const dragDistance = Math.round(offset.offsetX * scale);
3849
+ logBaxiaCaptcha("aliyun_geometry", {
3850
+ attempt,
3851
+ offset: offset.offsetX,
3852
+ scale: Number(scale.toFixed(2)),
3853
+ distance: dragDistance
3854
+ });
3855
+ if (dragDistance <= 0) {
3856
+ lastReason = "non_positive_distance";
3857
+ } else {
3858
+ const startX = sliderBox.x + sliderBox.width / 2;
3859
+ const startY = sliderBox.y + sliderBox.height / 2;
3860
+ await humanDrag(
3861
+ page,
3862
+ startX,
3863
+ startY,
3864
+ startX + dragDistance,
3865
+ startY
3866
+ );
3867
+ if (await waitForHidden(block, settleMs)) {
3868
+ logBaxiaCaptcha(
3869
+ "aliyun_solve_succeeded",
3870
+ { attempt, distance: dragDistance },
3871
+ true
3872
+ );
3873
+ return true;
3874
+ }
3875
+ lastReason = "not_solved";
3876
+ }
3877
+ }
3878
+ }
3879
+ } catch (error) {
3880
+ lastReason = error instanceof Error ? error.name : "UnknownError";
3881
+ logBaxiaCaptcha("aliyun_offset_failed", {
3882
+ attempt,
3883
+ error: lastReason,
3884
+ detail: sanitizeCaptchaErrorDetail(error)
3885
+ });
3886
+ }
3887
+ if (attempt < maxAttempts && retryDelayMs > 0) {
3888
+ await sleep(retryDelayMs);
3889
+ }
3890
+ }
3891
+ logBaxiaCaptcha(
3892
+ "aliyun_solve_failed",
3893
+ { attempts: maxAttempts, reason: lastReason },
3894
+ true
3895
+ );
3896
+ return false;
3897
+ });
3898
+ }
3899
+ async function solveVisibleCaptcha(page, options = {}) {
3900
+ if (typeof page.isClosed === "function" && page.isClosed()) return false;
3901
+ const totalWaitMs = Math.max(0, options.waitForMs ?? 0);
3902
+ const aliyunWaitMs = Math.min(totalWaitMs, ALIYUN_PROBE_MAX_WAIT_MS);
3903
+ const startedAt = Date.now();
3904
+ if (await solveAliyunWafCaptcha(page, { ...options, waitForMs: aliyunWaitMs })) {
3905
+ return true;
3906
+ }
3907
+ const remainingWaitMs = Math.max(
3908
+ 0,
3909
+ totalWaitMs - (Date.now() - startedAt)
3910
+ );
3911
+ return solveBaxiaCaptcha(page, { ...options, waitForMs: remainingWaitMs });
3912
+ }
3622
3913
  async function isVisible(locator) {
3623
3914
  return locator.isVisible().catch(() => false);
3624
3915
  }
@@ -3888,7 +4179,7 @@ function startBaxiaCaptchaWatcher(page, timeoutMs, options = {}) {
3888
4179
  promise
3889
4180
  };
3890
4181
  }
3891
- var BAXIA_DIALOG_SELECTOR, BAXIA_CONTENT_SELECTOR, CAPTCHA_EVENT_EMOJI, captchaDebugEnabled, BAXIA_IFRAME_SELECTORS, BAXIA_DOCUMENT_SELECTORS, BAXIA_IFRAME_SELECTOR, BAXIA_SLIDER_SELECTOR, BAXIA_TRACK_SELECTOR, BAXIA_SUCCESS_SELECTOR, BAXIA_RELOAD_SELECTOR, DEFAULT_MAX_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_SETTLE_MS, DEFAULT_SLIDER_TIMEOUT_MS, captchaMouseLock;
4182
+ var BAXIA_DIALOG_SELECTOR, BAXIA_CONTENT_SELECTOR, CAPTCHA_EVENT_EMOJI, captchaDebugEnabled, BAXIA_IFRAME_SELECTORS, BAXIA_DOCUMENT_SELECTORS, BAXIA_IFRAME_SELECTOR, BAXIA_SLIDER_SELECTOR, BAXIA_TRACK_SELECTOR, BAXIA_SUCCESS_SELECTOR, BAXIA_RELOAD_SELECTOR, DEFAULT_MAX_ATTEMPTS, DEFAULT_RETRY_DELAY_MS, DEFAULT_SETTLE_MS, DEFAULT_SLIDER_TIMEOUT_MS, ALIYUN_WAF_CAPTCHA_SELECTOR, ALIYUN_WAF_CAPTCHA_WIDGET, ALIYUN_WAF_BLOCK_SELECTORS, ALIYUN_PUZZLE_SLIDER_SELECTOR, ALIYUN_PUZZLE_TRACK_SELECTOR, ALIYUN_PROBE_MAX_WAIT_MS, captchaMouseLock;
3892
4183
  var init_captcha_solver = __esm({
3893
4184
  "src/services/captcha-solver.ts"() {
3894
4185
  "use strict";
@@ -3914,14 +4205,20 @@ var init_captcha_solver = __esm({
3914
4205
  solve_failed: "\u274C",
3915
4206
  recovery_succeeded: "\u2705",
3916
4207
  recovery_not_solved: "\u26A0\uFE0F",
3917
- recovery_failed: "\u274C"
4208
+ recovery_failed: "\u274C",
4209
+ aliyun_detected: "\u{1F9E9}",
4210
+ aliyun_offset_failed: "\u{1F4D0}",
4211
+ aliyun_geometry: "\u{1F4D0}",
4212
+ aliyun_solve_succeeded: "\u2705",
4213
+ aliyun_solve_failed: "\u274C"
3918
4214
  };
3919
4215
  captchaDebugEnabled = process.env.CAPTCHA_DEBUG === "true";
3920
4216
  BAXIA_IFRAME_SELECTORS = [
3921
4217
  `${BAXIA_DIALOG_SELECTOR} ${BAXIA_CONTENT_SELECTOR} iframe`,
3922
4218
  `${BAXIA_CONTENT_SELECTOR} iframe`,
3923
4219
  "iframe#baxia-dialog-content",
3924
- 'iframe[src*="_____tmd_____/punish"]'
4220
+ 'iframe[src*="_____tmd_____/punish"]',
4221
+ 'iframe[src*="nocaptcha"]'
3925
4222
  ];
3926
4223
  BAXIA_DOCUMENT_SELECTORS = [
3927
4224
  "#nocaptcha",
@@ -3929,14 +4226,23 @@ var init_captcha_solver = __esm({
3929
4226
  "#baxia-punish"
3930
4227
  ];
3931
4228
  BAXIA_IFRAME_SELECTOR = BAXIA_IFRAME_SELECTORS.join(", ");
3932
- BAXIA_SLIDER_SELECTOR = "#nc_1_n1z, .nc_1_n1z, .btn_slide, .nc_wrapper .btn_slide, ._nc .btn_slide, .nc-container .btn_slide";
3933
- BAXIA_TRACK_SELECTOR = "#nc_1_n1t, .nc_scale, .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale";
4229
+ BAXIA_SLIDER_SELECTOR = '#nc_1_n1z, .nc_1_n1z, .btn_slide, .nc_iconfont.btn_slide, span[id*="_n1z"], .nc_wrapper .btn_slide, ._nc .btn_slide, .nc-container .btn_slide';
4230
+ BAXIA_TRACK_SELECTOR = '#nc_1_n1t, .nc_scale, span[id*="_n1t"], .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale';
3934
4231
  BAXIA_SUCCESS_SELECTOR = '.btn_ok, .nc_ok, .nc_success, .nc_result, .nc_wrapper.nc-success, .nc_wrapper.success, [data-nc-lang="SUCCESS"], [data-nc-lang="success"], #nc-loading-circle';
3935
4232
  BAXIA_RELOAD_SELECTOR = '#nc_1_refresh1, .errloading a, .nc-container .errloading a, .btn_refresh, .clickCaptcha_text .btn_refresh, [data-nc-lang="REFRESH"], a[id*="refresh"]';
3936
4233
  DEFAULT_MAX_ATTEMPTS = 3;
3937
4234
  DEFAULT_RETRY_DELAY_MS = 1e3;
3938
4235
  DEFAULT_SETTLE_MS = 2e3;
3939
4236
  DEFAULT_SLIDER_TIMEOUT_MS = 8e3;
4237
+ ALIYUN_WAF_CAPTCHA_SELECTOR = "#waf_nc_block";
4238
+ ALIYUN_WAF_CAPTCHA_WIDGET = "#aliyunCaptcha-window-embed";
4239
+ ALIYUN_WAF_BLOCK_SELECTORS = [
4240
+ ALIYUN_WAF_CAPTCHA_SELECTOR,
4241
+ ALIYUN_WAF_CAPTCHA_WIDGET
4242
+ ];
4243
+ ALIYUN_PUZZLE_SLIDER_SELECTOR = "#aliyunCaptcha-sliding-slider";
4244
+ ALIYUN_PUZZLE_TRACK_SELECTOR = "#aliyunCaptcha-sliding-body";
4245
+ ALIYUN_PROBE_MAX_WAIT_MS = 2e3;
3940
4246
  captchaMouseLock = Promise.resolve();
3941
4247
  }
3942
4248
  });
@@ -4090,6 +4396,14 @@ function updateQwenWebVersion(version) {
4090
4396
  function getQwenWebVersion() {
4091
4397
  return dynamicWebVersion || config.qwen.webVersion;
4092
4398
  }
4399
+ function updateQwenBxVersion(version) {
4400
+ if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
4401
+ dynamicBxVersion = version.trim();
4402
+ }
4403
+ }
4404
+ function getQwenBxVersion() {
4405
+ return dynamicBxVersion || config.auth.bxV;
4406
+ }
4093
4407
  function buildQwenRequestHeaders(opts) {
4094
4408
  const headers = {
4095
4409
  ...opts.extra ?? {},
@@ -4106,7 +4420,7 @@ function buildQwenRequestHeaders(opts) {
4106
4420
  Connection: "keep-alive",
4107
4421
  "User-Agent": opts.userAgent || getDefaultQwenUserAgent(),
4108
4422
  "X-Request-Id": uuidv4(),
4109
- "bx-v": opts.bxV || "2.5.37",
4423
+ "bx-v": opts.bxV || getQwenBxVersion(),
4110
4424
  source: "web",
4111
4425
  version: opts.version || getQwenWebVersion(),
4112
4426
  timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
@@ -4130,7 +4444,7 @@ function buildQwenRequestHeaders(opts) {
4130
4444
  }
4131
4445
  return headers;
4132
4446
  }
4133
- var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
4447
+ var dynamicWebVersion, dynamicBxVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
4134
4448
  var init_qwen_headers = __esm({
4135
4449
  "src/services/qwen-headers.ts"() {
4136
4450
  "use strict";
@@ -4139,6 +4453,7 @@ var init_qwen_headers = __esm({
4139
4453
  init_fingerprint();
4140
4454
  init_timezone();
4141
4455
  dynamicWebVersion = null;
4456
+ dynamicBxVersion = null;
4142
4457
  QWEN_WEB_VERSION = config.qwen.webVersion;
4143
4458
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
4144
4459
  QWEN_TIMEZONE_HEADER = (/* @__PURE__ */ new Date()).toString().split(" (")[0];
@@ -4752,11 +5067,12 @@ async function getOrLaunchSharedBrowser(browserType = "chromium", headless = con
4752
5067
  async function clearVisibleChallenge(page) {
4753
5068
  if (page.isClosed()) return;
4754
5069
  if (config.captcha.enabled) {
4755
- await solveBaxiaCaptcha(page, {
5070
+ await solveVisibleCaptcha(page, {
4756
5071
  waitForMs: 0,
4757
5072
  maxAttempts: config.captcha.maxAttempts,
4758
5073
  retryDelayMs: config.captcha.retryDelayMs,
4759
- settleMs: config.captcha.settleMs
5074
+ settleMs: config.captcha.settleMs,
5075
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
4760
5076
  }).catch(() => false);
4761
5077
  }
4762
5078
  try {
@@ -5369,6 +5685,7 @@ async function getBasicHeaders(accountId) {
5369
5685
  mobile = data.mobile ? "?1" : "?0";
5370
5686
  }
5371
5687
  let bundleVersion = null;
5688
+ let baxiaVersion = null;
5372
5689
  try {
5373
5690
  const el = document.querySelector('link[href*="qwen-chat-fe"], script[src*="qwen-chat-fe"], img[src*="qwen-chat-fe"]');
5374
5691
  const src = el ? el.getAttribute("href") || el.getAttribute("src") || "" : "";
@@ -5376,7 +5693,14 @@ async function getBasicHeaders(accountId) {
5376
5693
  if (match) bundleVersion = match[1];
5377
5694
  } catch {
5378
5695
  }
5379
- return { ua, secChUa, platform, mobile, bundleVersion };
5696
+ try {
5697
+ const baxia = window.baxiaCommon;
5698
+ if (typeof baxia?.version === "string") {
5699
+ baxiaVersion = baxia.version;
5700
+ }
5701
+ } catch {
5702
+ }
5703
+ return { ua, secChUa, platform, mobile, bundleVersion, baxiaVersion };
5380
5704
  }),
5381
5705
  config.timeouts.page,
5382
5706
  `User-agent lookup timed out for ${accountId}`
@@ -5385,6 +5709,9 @@ async function getBasicHeaders(accountId) {
5385
5709
  if (nav.bundleVersion) {
5386
5710
  updateQwenWebVersion(nav.bundleVersion);
5387
5711
  }
5712
+ if (nav.baxiaVersion) {
5713
+ updateQwenBxVersion(nav.baxiaVersion);
5714
+ }
5388
5715
  cachedUserAgents.set(accountId, userAgent);
5389
5716
  const hints = getHeaderCache(accountId).headers;
5390
5717
  clientHints = {
@@ -5416,7 +5743,7 @@ async function getBasicHeaders(accountId) {
5416
5743
  markAccountHeadersReady(accountId);
5417
5744
  const bxUa2 = cache3.headers["bx-ua"];
5418
5745
  const bxUmidtoken2 = cache3.headers["bx-umidtoken"];
5419
- const bxV2 = cache3.headers["bx-v"] || "2.5.37";
5746
+ const bxV2 = cache3.headers["bx-v"] || getQwenBxVersion();
5420
5747
  const cookie2 = await getCookies(accountId);
5421
5748
  return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
5422
5749
  }
@@ -5427,7 +5754,7 @@ async function getBasicHeaders(accountId) {
5427
5754
  cookieCaches.set(accountId, { cookie: cookie2, timestamp: Date.now() });
5428
5755
  const bxUa2 = cache3.headers["bx-ua"];
5429
5756
  const bxUmidtoken2 = cache3.headers["bx-umidtoken"];
5430
- const bxV2 = cache3.headers["bx-v"] || "2.5.37";
5757
+ const bxV2 = cache3.headers["bx-v"] || getQwenBxVersion();
5431
5758
  cache3.lastRefresh = Date.now();
5432
5759
  markAccountHeadersReady(accountId);
5433
5760
  console.log(
@@ -5502,6 +5829,9 @@ async function getBasicHeaders(accountId) {
5502
5829
  if (persisted.version) {
5503
5830
  updateQwenWebVersion(persisted.version);
5504
5831
  }
5832
+ if (persisted.bxV) {
5833
+ updateQwenBxVersion(persisted.bxV);
5834
+ }
5505
5835
  cache3.lastRefresh = persisted.capturedAt;
5506
5836
  markAccountHeadersReady(accountId);
5507
5837
  }
@@ -5529,7 +5859,7 @@ async function getBasicHeaders(accountId) {
5529
5859
  markAccountHeadersReady(accountId);
5530
5860
  const bxUa = cache3.headers["bx-ua"];
5531
5861
  const bxUmidtoken = cache3.headers["bx-umidtoken"];
5532
- const bxV = cache3.headers["bx-v"] || "2.5.37";
5862
+ const bxV = cache3.headers["bx-v"] || getQwenBxVersion();
5533
5863
  cookieCaches.delete(accountId);
5534
5864
  const cookie = await getCookies(accountId, true);
5535
5865
  if (cache3.headers) {
@@ -5774,6 +6104,9 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5774
6104
  if (persisted.version) {
5775
6105
  updateQwenWebVersion(persisted.version);
5776
6106
  }
6107
+ if (persisted.bxV) {
6108
+ updateQwenBxVersion(persisted.bxV);
6109
+ }
5777
6110
  cache3.lastRefresh = persisted.capturedAt;
5778
6111
  markAccountHeadersReady(account.id);
5779
6112
  restoredFromDb = true;
@@ -6123,7 +6456,7 @@ async function startEmailOtpLogin(email, options = {}) {
6123
6456
  {
6124
6457
  email: normalizedEmail,
6125
6458
  requestId,
6126
- version: getQwenWebVersion() || "0.3.26",
6459
+ version: getQwenWebVersion(),
6127
6460
  timezone: formatAsciiTimezone()
6128
6461
  }
6129
6462
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -6218,7 +6551,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
6218
6551
  email: session.email,
6219
6552
  code: cleanCode,
6220
6553
  requestId,
6221
- version: getQwenWebVersion() || "0.3.26",
6554
+ version: getQwenWebVersion(),
6222
6555
  timezone: formatAsciiTimezone()
6223
6556
  }
6224
6557
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -6265,8 +6598,8 @@ async function verifyEmailOtpLogin(sessionId, code) {
6265
6598
  saveAuthSession2(session.accountId, {
6266
6599
  cookie: `token=${accessToken}`,
6267
6600
  userAgent: session.fingerprint.userAgent,
6268
- bxV: "2.5.37",
6269
- version: getQwenWebVersion() || "0.3.26",
6601
+ bxV: getQwenBxVersion(),
6602
+ version: getQwenWebVersion(),
6270
6603
  userId: userId || void 0,
6271
6604
  tokenExpiresAt: exp || void 0,
6272
6605
  refreshToken: refreshToken || void 0,
@@ -6376,7 +6709,7 @@ async function loginToQwen(accountId, email, password) {
6376
6709
  saveAuthSession2(accountId, {
6377
6710
  cookie: cookieStr,
6378
6711
  userAgent: cache3.headers["user-agent"] || "",
6379
- bxV: cache3.headers["bx-v"] || "2.5.37",
6712
+ bxV: cache3.headers["bx-v"] || getQwenBxVersion(),
6380
6713
  bxUa: cache3.headers["bx-ua"] || "",
6381
6714
  bxUmidtoken: cache3.headers["bx-umidtoken"] || "",
6382
6715
  secChUa: cache3.headers["sec-ch-ua"] || void 0,
@@ -6448,7 +6781,7 @@ async function loginToQwen(accountId, email, password) {
6448
6781
  saveAuthSession2(accountId, {
6449
6782
  cookie: cookieStr,
6450
6783
  userAgent: cache3.headers["user-agent"] || "",
6451
- bxV: cache3.headers["bx-v"] || "2.5.37",
6784
+ bxV: cache3.headers["bx-v"] || getQwenBxVersion(),
6452
6785
  bxUa: cache3.headers["bx-ua"] || "",
6453
6786
  bxUmidtoken: cache3.headers["bx-umidtoken"] || "",
6454
6787
  secChUa: cache3.headers["sec-ch-ua"] || void 0,
@@ -6519,7 +6852,7 @@ async function loginViaApi(page, email, password) {
6519
6852
  }
6520
6853
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
6521
6854
  const requestId = crypto5.randomUUID();
6522
- const webVersion = getQwenWebVersion() || "0.3.26";
6855
+ const webVersion = getQwenWebVersion();
6523
6856
  const evalRes = await page.evaluate(
6524
6857
  async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
6525
6858
  try {
@@ -6691,6 +7024,15 @@ async function loginViaUi(page, email, password) {
6691
7024
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
6692
7025
  return { success: true };
6693
7026
  }
7027
+ if (config.captcha.enabled) {
7028
+ await solveVisibleCaptcha(page, {
7029
+ waitForMs: 1500,
7030
+ maxAttempts: config.captcha.maxAttempts,
7031
+ retryDelayMs: config.captcha.retryDelayMs,
7032
+ settleMs: config.captcha.settleMs,
7033
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
7034
+ }).catch(() => false);
7035
+ }
6694
7036
  const emailSelector = [
6695
7037
  'input[type="email"]',
6696
7038
  'input[name="email"]',
@@ -6846,10 +7188,11 @@ async function loginViaUi(page, email, password) {
6846
7188
  while (Date.now() < postSubmitDeadline) {
6847
7189
  if (page.isClosed()) break;
6848
7190
  if (config.captcha.enabled) {
6849
- await solveBaxiaCaptcha(page, {
7191
+ await solveVisibleCaptcha(page, {
6850
7192
  waitForMs: 1500,
6851
7193
  maxAttempts: config.captcha.maxAttempts,
6852
- retryDelayMs: config.captcha.retryDelayMs
7194
+ retryDelayMs: config.captcha.retryDelayMs,
7195
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
6853
7196
  }).catch(() => {
6854
7197
  });
6855
7198
  }
@@ -6972,7 +7315,7 @@ async function refreshAccountToken(accountId) {
6972
7315
  }
6973
7316
  if (refreshToken) {
6974
7317
  try {
6975
- const webVersion = getQwenWebVersion() || "0.3.26";
7318
+ const webVersion = getQwenWebVersion();
6976
7319
  const refreshResult = await page.evaluate(
6977
7320
  async ({ rTok, tz, version }) => {
6978
7321
  try {
@@ -7037,7 +7380,7 @@ async function refreshAccountToken(accountId) {
7037
7380
  saveAuthSession2(accountId, {
7038
7381
  cookie: cookieStr,
7039
7382
  userAgent: cache3.headers["user-agent"] || session?.userAgent || "",
7040
- bxV: cache3.headers["bx-v"] || session?.bxV || "2.5.37",
7383
+ bxV: cache3.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
7041
7384
  bxUa: cache3.headers["bx-ua"] || session?.bxUa || "",
7042
7385
  bxUmidtoken: cache3.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
7043
7386
  secChUa: cache3.headers["sec-ch-ua"] || session?.secChUa,
@@ -7072,7 +7415,7 @@ async function refreshAccountToken(accountId) {
7072
7415
  saveAuthSession2(accountId, {
7073
7416
  cookie: cookieStr,
7074
7417
  userAgent: cache3.headers["user-agent"] || session?.userAgent || "",
7075
- bxV: cache3.headers["bx-v"] || session?.bxV || "2.5.37",
7418
+ bxV: cache3.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
7076
7419
  bxUa: cache3.headers["bx-ua"] || session?.bxUa || "",
7077
7420
  bxUmidtoken: cache3.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
7078
7421
  secChUa: cache3.headers["sec-ch-ua"] || session?.secChUa,
@@ -7220,7 +7563,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
7220
7563
  cookie: reqHeaders["cookie"] || "",
7221
7564
  "bx-ua": reqHeaders["bx-ua"] || "",
7222
7565
  "bx-umidtoken": reqHeaders["bx-umidtoken"] || "",
7223
- "bx-v": reqHeaders["bx-v"] || "2.5.37",
7566
+ "bx-v": reqHeaders["bx-v"] || getQwenBxVersion(),
7224
7567
  "user-agent": reqHeaders["user-agent"] || "",
7225
7568
  "x-request-id": reqHeaders["x-request-id"] || "",
7226
7569
  "version": reqHeaders["version"] || "",
@@ -7252,6 +7595,9 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
7252
7595
  if (capturedHeaders["version"]) {
7253
7596
  updateQwenWebVersion(capturedHeaders["version"]);
7254
7597
  }
7598
+ if (capturedHeaders["bx-v"]) {
7599
+ updateQwenBxVersion(capturedHeaders["bx-v"]);
7600
+ }
7255
7601
  markAccountHeadersReady(accountId);
7256
7602
  cache3.lastRefresh = Date.now();
7257
7603
  cookieCaches.delete(accountId);
@@ -7709,7 +8055,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
7709
8055
  saveAuthSession2(accountId, {
7710
8056
  cookie: cookieStr,
7711
8057
  userAgent: cache3.headers["user-agent"] || "",
7712
- bxV: cache3.headers["bx-v"] || "2.5.37",
8058
+ bxV: cache3.headers["bx-v"] || getQwenBxVersion(),
7713
8059
  bxUa: cache3.headers["bx-ua"] || "",
7714
8060
  bxUmidtoken: cache3.headers["bx-umidtoken"] || "",
7715
8061
  secChUa: cache3.headers["sec-ch-ua"] || void 0,
@@ -8712,13 +9058,13 @@ async function getBasicHeaders2(accountId) {
8712
9058
  return {
8713
9059
  cookie: "token=mock",
8714
9060
  userAgent: "mock",
8715
- bxV: "2.5.37",
9061
+ bxV: getQwenBxVersion(),
8716
9062
  bxUa: "mock-bx-ua",
8717
9063
  bxUmidtoken: "mock-bx-umidtoken",
8718
9064
  secChUa: "",
8719
9065
  secChUaMobile: "?0",
8720
9066
  secChUaPlatform: "",
8721
- version: "0.3.26"
9067
+ version: getQwenWebVersion()
8722
9068
  };
8723
9069
  }
8724
9070
  const resolvedAccountId = accountId ?? loadAccounts()[0]?.id;
@@ -8779,6 +9125,7 @@ var init_auth_playwright = __esm({
8779
9125
  init_errors();
8780
9126
  init_accounts();
8781
9127
  init_config();
9128
+ init_qwen_headers();
8782
9129
  init_playwright();
8783
9130
  init_jwt();
8784
9131
  }
@@ -8903,6 +9250,7 @@ var init_model_alias = __esm({
8903
9250
  DEFAULT_FALLBACK_MODELS = [
8904
9251
  "qwen3.8-max",
8905
9252
  "qwen3.7-plus",
9253
+ "qwen4.0-max-preview",
8906
9254
  "qwen3.8-omni-flash",
8907
9255
  "qwen3.7-max",
8908
9256
  "qwen3.6-plus",
@@ -9249,9 +9597,10 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
9249
9597
  const solverOptions = {
9250
9598
  maxAttempts: config.captcha.maxAttempts,
9251
9599
  retryDelayMs: config.captcha.retryDelayMs,
9252
- settleMs: config.captcha.settleMs
9600
+ settleMs: config.captcha.settleMs,
9601
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
9253
9602
  };
9254
- if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
9603
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
9255
9604
  return true;
9256
9605
  }
9257
9606
  logBaxiaCaptcha(
@@ -9261,7 +9610,7 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
9261
9610
  );
9262
9611
  await gotoBestEffort(page, challengeUrl ?? qwenUrl("/"));
9263
9612
  try {
9264
- return await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs });
9613
+ return await solveVisibleCaptcha(page, { ...solverOptions, waitForMs });
9265
9614
  } finally {
9266
9615
  if (page.url().includes(CHALLENGE_PATH_MARKER)) {
9267
9616
  await gotoBestEffort(page, qwenUrl("/"));
@@ -9299,9 +9648,10 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
9299
9648
  const solverOptions = {
9300
9649
  maxAttempts: config.captcha.maxAttempts,
9301
9650
  retryDelayMs: config.captcha.retryDelayMs,
9302
- settleMs: config.captcha.settleMs
9651
+ settleMs: config.captcha.settleMs,
9652
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
9303
9653
  };
9304
- if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
9654
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
9305
9655
  return true;
9306
9656
  }
9307
9657
  const ctx = typeof page.context === "function" ? page.context() : null;
@@ -11618,7 +11968,7 @@ var init_media_generation = __esm({
11618
11968
 
11619
11969
  // src/services/qwen-chat-pool.ts
11620
11970
  import crypto6 from "crypto";
11621
- async function createQwenChatSession(headers, model, accountId, chatMode = "thread") {
11971
+ async function createQwenChatSession(headers, model, accountId, chatMode = "thread", captchaRecoveryAttempts = 1) {
11622
11972
  if (isAuthMockEnabled()) {
11623
11973
  return process.env.TEST_SESSION_ID || "mock-session";
11624
11974
  }
@@ -11646,16 +11996,29 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
11646
11996
  if (!chatId || typeof chatId !== "string") {
11647
11997
  const rawLower = raw.toLowerCase();
11648
11998
  if (looksLikeAntiBotChallengeText(raw)) {
11649
- try {
11650
- const { recoverBaxiaCaptcha: recoverBaxiaCaptcha2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
11651
- const solved = await recoverBaxiaCaptcha2(accountId, "createQwenChatSession", {
11652
- challengeBody: raw
11653
- });
11654
- if (solved) {
11655
- return createQwenChatSession(headers, model, accountId, chatMode);
11999
+ if (captchaRecoveryAttempts > 0) {
12000
+ try {
12001
+ const { recoverBaxiaCaptcha: recoverBaxiaCaptcha2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
12002
+ const solved = await recoverBaxiaCaptcha2(accountId, "createQwenChatSession", {
12003
+ challengeBody: raw
12004
+ });
12005
+ if (solved) {
12006
+ return createQwenChatSession(
12007
+ headers,
12008
+ model,
12009
+ accountId,
12010
+ chatMode,
12011
+ captchaRecoveryAttempts - 1
12012
+ );
12013
+ }
12014
+ } catch {
11656
12015
  }
11657
- } catch {
11658
12016
  }
12017
+ throw new QwenUpstreamError(
12018
+ `Qwen create chat blocked by WAF challenge: ${raw.substring(0, 300)}`,
12019
+ "FAIL_SYS_USER_VALIDATE",
12020
+ 403
12021
+ );
11659
12022
  }
11660
12023
  const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
11661
12024
  if (isUnauthorized) {
@@ -11832,6 +12195,20 @@ async function refillQwenChatPool(headers, model, accountId) {
11832
12195
  console.warn(
11833
12196
  `[WarmPool] Account ${accountId || "global"} rate-limited during chat creation. Marked for cooldown.`
11834
12197
  );
12198
+ } else if (accountId && (err.upstreamCode === "FAIL_SYS_USER_VALIDATE" || err.upstreamCode === "RGV587_ERROR" || /_____tmd_____|rgv587_error|fail_sys_user_validate/i.test(
12199
+ err.message
12200
+ ))) {
12201
+ const existing = getAccountCooldownInfo(accountId);
12202
+ if (existing?.reason === "WafChallenge") {
12203
+ console.warn(
12204
+ `[WarmPool] Account ${accountId} still on WAF quarantine (${Math.ceil(existing.remainingMs / 1e3)}s left); skipping re-escalation.`
12205
+ );
12206
+ } else {
12207
+ const block = recordWafHardBlock(accountId);
12208
+ console.warn(
12209
+ `[WarmPool] Unsolved WAF challenge on ${accountId} during chat creation; quarantined ${Math.ceil(block.cooldownMs / 1e3)}s and rotated the fingerprint.`
12210
+ );
12211
+ }
11835
12212
  }
11836
12213
  }
11837
12214
  if (isToolcallDebugEnabled()) {
@@ -11867,6 +12244,7 @@ var init_qwen_chat_pool = __esm({
11867
12244
  init_config();
11868
12245
  init_logger();
11869
12246
  init_account_manager();
12247
+ init_waf_isolation();
11870
12248
  init_model_alias();
11871
12249
  init_qwen_url();
11872
12250
  init_qwen_errors();
@@ -12436,7 +12814,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12436
12814
  browserHeaders["Content-Type"] = "application/json";
12437
12815
  }
12438
12816
  const evaluateRequest = (page) => page.evaluate(
12439
- async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
12817
+ async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz, version }) => {
12440
12818
  const controller = new AbortController();
12441
12819
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
12442
12820
  try {
@@ -12458,7 +12836,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12458
12836
  headers: {
12459
12837
  accept: "application/json, text/plain, */*",
12460
12838
  source: "web",
12461
- version: "0.3.11",
12839
+ version,
12462
12840
  "x-request-origin": "https://chat.qwen.ai",
12463
12841
  timezone: tz,
12464
12842
  authorization: `Bearer ${rTok}`
@@ -12506,7 +12884,8 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12506
12884
  body,
12507
12885
  referrer: options.referrer,
12508
12886
  timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
12509
- tz: formatAsciiTimezone()
12887
+ tz: formatAsciiTimezone(),
12888
+ version: getQwenWebVersion()
12510
12889
  }
12511
12890
  );
12512
12891
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -13463,7 +13842,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
13463
13842
  }
13464
13843
  const requestHeaders = {
13465
13844
  source: "web",
13466
- version: getQwenWebVersion() || "0.3.26",
13845
+ version: getQwenWebVersion(),
13467
13846
  timezone: formatAsciiTimezone(),
13468
13847
  "x-request-id": crypto7.randomUUID(),
13469
13848
  Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
@@ -13507,7 +13886,7 @@ async function fetchRemoteQwenChats(accountId) {
13507
13886
  return [];
13508
13887
  }
13509
13888
  const requestHeaders = {
13510
- version: getQwenWebVersion() || "0.3.26",
13889
+ version: getQwenWebVersion(),
13511
13890
  timezone: formatAsciiTimezone(),
13512
13891
  "x-request-id": crypto7.randomUUID(),
13513
13892
  Referer: qwenUrl("/settings/chats")
@@ -27196,11 +27575,42 @@ var init_server_log_buffer = __esm({
27196
27575
  // src/services/session-keeper.ts
27197
27576
  var session_keeper_exports = {};
27198
27577
  __export(session_keeper_exports, {
27578
+ computeSessionKeeperBackoffMs: () => computeSessionKeeperBackoffMs,
27579
+ isSessionKeeperAccountBackedOff: () => isSessionKeeperAccountBackedOff,
27199
27580
  isSessionKeeperRunning: () => isSessionKeeperRunning,
27581
+ resetSessionKeeperRetryStateForTests: () => resetSessionKeeperRetryStateForTests,
27200
27582
  runSessionKeeperOnceForTesting: () => runSessionKeeperOnceForTesting,
27201
27583
  startSessionKeeper: () => startSessionKeeper,
27202
27584
  stopSessionKeeper: () => stopSessionKeeper
27203
27585
  });
27586
+ function computeSessionKeeperBackoffMs(failures) {
27587
+ const exponent = Math.max(0, Math.min(failures - 1, 8));
27588
+ return Math.min(
27589
+ KEEP_ALIVE_BACKOFF_MAX_MS,
27590
+ KEEP_ALIVE_BACKOFF_BASE_MS * 2 ** exponent
27591
+ );
27592
+ }
27593
+ function isKeepAliveBackedOff(accountId) {
27594
+ const state = keepAliveRetries.get(accountId);
27595
+ return Boolean(state && Date.now() < state.nextAttemptAt);
27596
+ }
27597
+ function recordKeepAliveFailure(accountId) {
27598
+ const state = keepAliveRetries.get(accountId) ?? {
27599
+ failures: 0,
27600
+ nextAttemptAt: 0
27601
+ };
27602
+ state.failures += 1;
27603
+ const backoffMs = computeSessionKeeperBackoffMs(state.failures);
27604
+ state.nextAttemptAt = Date.now() + backoffMs;
27605
+ keepAliveRetries.set(accountId, state);
27606
+ return backoffMs;
27607
+ }
27608
+ function resetSessionKeeperRetryStateForTests() {
27609
+ keepAliveRetries.clear();
27610
+ }
27611
+ function isSessionKeeperAccountBackedOff(accountId) {
27612
+ return isKeepAliveBackedOff(accountId);
27613
+ }
27204
27614
  function isSessionKeeperRunning() {
27205
27615
  return running;
27206
27616
  }
@@ -27211,13 +27621,25 @@ async function runKeepAliveCycle() {
27211
27621
  if (config.sessionKeeper.enabled) {
27212
27622
  const accountIds = getActivePlaywrightAccountIds();
27213
27623
  for (const accountId of accountIds) {
27214
- await keepAlivePlaywrightAccount(accountId).catch((error) => {
27215
- if (isPlaywrightAlreadyClosedError(error)) return;
27216
- const message = error instanceof Error ? error.message : String(error);
27217
- console.warn(
27218
- `[SessionKeeper] Keep-alive failed for ${accountId}: ${message}`
27219
- );
27220
- });
27624
+ if (isKeepAliveBackedOff(accountId)) {
27625
+ continue;
27626
+ }
27627
+ try {
27628
+ const keptAlive = await keepAlivePlaywrightAccount(accountId);
27629
+ if (keptAlive) {
27630
+ keepAliveRetries.delete(accountId);
27631
+ }
27632
+ } catch (error) {
27633
+ if (isPlaywrightAlreadyClosedError(error)) {
27634
+ keepAliveRetries.delete(accountId);
27635
+ } else {
27636
+ const message = error instanceof Error ? error.message : String(error);
27637
+ const backoffMs = recordKeepAliveFailure(accountId);
27638
+ console.warn(
27639
+ `[SessionKeeper] Keep-alive failed for ${accountId}: ${message} (retry in ${Math.round(backoffMs / 1e3)}s)`
27640
+ );
27641
+ }
27642
+ }
27221
27643
  await sleep(humanDelay(250, 900));
27222
27644
  }
27223
27645
  }
@@ -27257,11 +27679,12 @@ function stopSessionKeeper() {
27257
27679
  intervalId = null;
27258
27680
  }
27259
27681
  cycleInProgress = false;
27682
+ keepAliveRetries.clear();
27260
27683
  }
27261
27684
  async function runSessionKeeperOnceForTesting() {
27262
27685
  await runKeepAliveCycle();
27263
27686
  }
27264
- var running, intervalId, cycleInProgress;
27687
+ var running, intervalId, cycleInProgress, KEEP_ALIVE_BACKOFF_BASE_MS, KEEP_ALIVE_BACKOFF_MAX_MS, keepAliveRetries;
27265
27688
  var init_session_keeper = __esm({
27266
27689
  "src/services/session-keeper.ts"() {
27267
27690
  "use strict";
@@ -27271,6 +27694,9 @@ var init_session_keeper = __esm({
27271
27694
  running = false;
27272
27695
  intervalId = null;
27273
27696
  cycleInProgress = false;
27697
+ KEEP_ALIVE_BACKOFF_BASE_MS = 2e3;
27698
+ KEEP_ALIVE_BACKOFF_MAX_MS = 5 * 6e4;
27699
+ keepAliveRetries = /* @__PURE__ */ new Map();
27274
27700
  }
27275
27701
  });
27276
27702
 
@@ -27296,7 +27722,7 @@ function getAppVersion() {
27296
27722
  }
27297
27723
  } catch {
27298
27724
  }
27299
- cachedVersion = "v1.6.11";
27725
+ cachedVersion = "v1.6.12";
27300
27726
  return cachedVersion;
27301
27727
  }
27302
27728
  var cachedVersion;