qwenproxy-cli 1.6.10 → 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/login.js CHANGED
@@ -1228,7 +1228,7 @@ var init_config = __esm({
1228
1228
  // Version header on Qwen API requests = the deployed web bundle version
1229
1229
  // (fallback when dynamic DOM detection is not yet completed).
1230
1230
  // Override via env when Qwen ships a new bundle.
1231
- QWEN_WEB_VERSION: z.string().default("0.3.26"),
1231
+ QWEN_WEB_VERSION: z.string().default("0.3.43"),
1232
1232
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
1233
1233
  // (chats/new, settings): those work without them (live-probed). The
1234
1234
  // completions path is the exception — the 0.2.86 HAR shows the real
@@ -1271,6 +1271,7 @@ var init_config = __esm({
1271
1271
  CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
1272
1272
  CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
1273
1273
  CAPTCHA_SOLVER_TIMEOUT_MS: z.string().default("15000"),
1274
+ CAPTCHA_SLIDER_TIMEOUT_MS: z.string().default("8000"),
1274
1275
  CAPTCHA_SOLVER_RETRY_DELAY_MS: z.string().default("1000"),
1275
1276
  CAPTCHA_SOLVER_SETTLE_MS: z.string().default("2000"),
1276
1277
  CAPTCHA_ACCOUNT_COOLDOWN_MS: z.string().default("120000"),
@@ -1422,6 +1423,8 @@ var init_config = __esm({
1422
1423
  enabled: env.CAPTCHA_SOLVER_ENABLED === "true",
1423
1424
  maxAttempts: Math.max(1, Math.min(5, parseInt(env.CAPTCHA_SOLVER_MAX_ATTEMPTS))),
1424
1425
  timeoutMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_TIMEOUT_MS)),
1426
+ /** Max wait for a challenge slider to become visible (slow WAF renders). */
1427
+ sliderTimeoutMs: Math.max(500, parseInt(env.CAPTCHA_SLIDER_TIMEOUT_MS)),
1425
1428
  retryDelayMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_RETRY_DELAY_MS)),
1426
1429
  settleMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_SETTLE_MS)),
1427
1430
  /** Rest an account whose challenge could not be cleared before reusing it. */
@@ -2612,6 +2615,294 @@ function logBaxiaCaptcha(event, fields = {}, important = false) {
2612
2615
  console.log(line);
2613
2616
  }
2614
2617
  }
2618
+ async function findVisibleLocatorWithin(page, selectors, timeoutMs) {
2619
+ const deadline = Date.now() + Math.max(0, timeoutMs);
2620
+ while (true) {
2621
+ const found = await findVisibleLocator(page, selectors);
2622
+ if (found) return found;
2623
+ if (Date.now() >= deadline) return null;
2624
+ await sleep(Math.min(150, Math.max(1, deadline - Date.now())));
2625
+ }
2626
+ }
2627
+ async function waitForHidden(locator, timeoutMs) {
2628
+ const deadline = Date.now() + Math.max(0, timeoutMs);
2629
+ while (true) {
2630
+ if (!await isVisible(locator)) return true;
2631
+ if (Date.now() >= deadline) return false;
2632
+ await sleep(Math.min(100, Math.max(1, deadline - Date.now())));
2633
+ }
2634
+ }
2635
+ async function computeAliyunPuzzleOffset(page) {
2636
+ return page.evaluate(() => {
2637
+ const background = document.querySelector(
2638
+ "#aliyunCaptcha-img"
2639
+ );
2640
+ const piece = document.querySelector(
2641
+ "#aliyunCaptcha-puzzle"
2642
+ );
2643
+ if (!background || !piece) {
2644
+ return Promise.resolve({ ok: false, reason: "missing puzzle images" });
2645
+ }
2646
+ const compute = () => {
2647
+ try {
2648
+ const width = background.naturalWidth;
2649
+ const height = background.naturalHeight;
2650
+ const pieceWidth = piece.naturalWidth;
2651
+ const pieceHeight = piece.naturalHeight;
2652
+ if (!width || !height || !pieceWidth || !pieceHeight) {
2653
+ return { ok: false, reason: "image dimensions unavailable" };
2654
+ }
2655
+ const backgroundCanvas = document.createElement("canvas");
2656
+ backgroundCanvas.id = "captcha-solver-canvas";
2657
+ backgroundCanvas.width = width;
2658
+ backgroundCanvas.height = height;
2659
+ const backgroundContext = backgroundCanvas.getContext("2d");
2660
+ if (!backgroundContext) {
2661
+ return { ok: false, reason: "no background context" };
2662
+ }
2663
+ const pieceCanvas = document.createElement("canvas");
2664
+ pieceCanvas.id = "captcha-solver-canvas";
2665
+ pieceCanvas.width = pieceWidth;
2666
+ pieceCanvas.height = pieceHeight;
2667
+ const pieceContext = pieceCanvas.getContext("2d");
2668
+ if (!pieceContext) {
2669
+ return { ok: false, reason: "no piece context" };
2670
+ }
2671
+ try {
2672
+ backgroundContext.drawImage(background, 0, 0);
2673
+ } catch {
2674
+ return { ok: false, reason: "background canvas tainted" };
2675
+ }
2676
+ try {
2677
+ pieceContext.drawImage(piece, 0, 0);
2678
+ } catch {
2679
+ return { ok: false, reason: "puzzle canvas tainted" };
2680
+ }
2681
+ const backgroundData = backgroundContext.getImageData(
2682
+ 0,
2683
+ 0,
2684
+ width,
2685
+ height
2686
+ ).data;
2687
+ const pieceData = pieceContext.getImageData(
2688
+ 0,
2689
+ 0,
2690
+ pieceWidth,
2691
+ pieceHeight
2692
+ ).data;
2693
+ let pieceMinX = pieceWidth;
2694
+ let pieceMaxX = -1;
2695
+ let pieceMinY = pieceHeight;
2696
+ let pieceMaxY = -1;
2697
+ for (let y = 0; y < pieceHeight; y++) {
2698
+ for (let x = 0; x < pieceWidth; x++) {
2699
+ if (pieceData[(y * pieceWidth + x) * 4 + 3] > 100) {
2700
+ if (x < pieceMinX) pieceMinX = x;
2701
+ if (x > pieceMaxX) pieceMaxX = x;
2702
+ if (y < pieceMinY) pieceMinY = y;
2703
+ if (y > pieceMaxY) pieceMaxY = y;
2704
+ }
2705
+ }
2706
+ }
2707
+ const puzzleWidth = pieceMaxX - pieceMinX + 1;
2708
+ if (puzzleWidth <= 0) {
2709
+ return { ok: false, reason: "empty puzzle piece" };
2710
+ }
2711
+ const yStart = Math.max(1, Math.min(height - 2, pieceMinY + 5));
2712
+ const yEnd = Math.max(
2713
+ yStart,
2714
+ Math.min(height - 2, pieceMaxY - 5)
2715
+ );
2716
+ const columnGradients = new Float32Array(width);
2717
+ for (let x = 1; x < width - 1; x++) {
2718
+ let gradient = 0;
2719
+ for (let y = yStart; y <= yEnd; y++) {
2720
+ const left = (y * width + x - 1) * 4;
2721
+ const right = (y * width + x + 1) * 4;
2722
+ gradient += Math.abs(backgroundData[right] - backgroundData[left]) + Math.abs(backgroundData[right + 1] - backgroundData[left + 1]) + Math.abs(backgroundData[right + 2] - backgroundData[left + 2]);
2723
+ }
2724
+ columnGradients[x] = gradient;
2725
+ }
2726
+ let bestX = 0;
2727
+ let bestScore = 0;
2728
+ const minCandidateX = 30;
2729
+ const maxCandidateX = width - puzzleWidth - 20;
2730
+ for (let candidateX = minCandidateX; candidateX <= maxCandidateX; candidateX++) {
2731
+ let leftMax = 0;
2732
+ for (let dx = -2; dx <= 2; dx++) {
2733
+ const value = columnGradients[candidateX + pieceMinX + dx] || 0;
2734
+ if (value > leftMax) leftMax = value;
2735
+ }
2736
+ let rightMax = 0;
2737
+ for (let dx = -3; dx <= 1; dx++) {
2738
+ const value = columnGradients[candidateX + pieceMaxX + dx] || 0;
2739
+ if (value > rightMax) rightMax = value;
2740
+ }
2741
+ const score = Math.sqrt(leftMax * rightMax);
2742
+ if (score > bestScore) {
2743
+ bestScore = score;
2744
+ bestX = candidateX;
2745
+ }
2746
+ }
2747
+ if (bestScore <= 0) {
2748
+ return { ok: false, reason: "no matching hole edge" };
2749
+ }
2750
+ return {
2751
+ ok: true,
2752
+ offsetX: bestX - pieceMinX,
2753
+ naturalWidth: width
2754
+ };
2755
+ } catch (error) {
2756
+ return {
2757
+ ok: false,
2758
+ reason: error instanceof Error ? error.message : String(error)
2759
+ };
2760
+ }
2761
+ };
2762
+ return new Promise((resolve) => {
2763
+ if (background.complete && piece.complete) {
2764
+ resolve(compute());
2765
+ return;
2766
+ }
2767
+ let settled = false;
2768
+ const finish = () => {
2769
+ if (settled) return;
2770
+ settled = true;
2771
+ resolve(compute());
2772
+ };
2773
+ const fail = () => {
2774
+ if (settled) return;
2775
+ settled = true;
2776
+ resolve({ ok: false, reason: "puzzle image load error" });
2777
+ };
2778
+ background.addEventListener("load", finish, { once: true });
2779
+ piece.addEventListener("load", finish, { once: true });
2780
+ background.addEventListener("error", fail, { once: true });
2781
+ piece.addEventListener("error", fail, { once: true });
2782
+ setTimeout(() => {
2783
+ if (settled) return;
2784
+ settled = true;
2785
+ resolve({ ok: false, reason: "puzzle image load timeout" });
2786
+ }, 2e3);
2787
+ });
2788
+ });
2789
+ }
2790
+ async function solveAliyunWafCaptcha(page, options = {}) {
2791
+ const waitForMs = Math.max(0, options.waitForMs ?? 0);
2792
+ const maxAttempts = Math.max(
2793
+ 1,
2794
+ Math.min(5, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS)
2795
+ );
2796
+ const retryDelayMs = Math.max(
2797
+ 0,
2798
+ options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
2799
+ );
2800
+ const settleMs = Math.max(0, options.settleMs ?? DEFAULT_SETTLE_MS);
2801
+ const sliderTimeoutMs = Math.max(
2802
+ 500,
2803
+ options.sliderTimeoutMs ?? DEFAULT_SLIDER_TIMEOUT_MS
2804
+ );
2805
+ const detected = await findVisibleLocatorWithin(
2806
+ page,
2807
+ ALIYUN_WAF_BLOCK_SELECTORS,
2808
+ waitForMs
2809
+ );
2810
+ if (!detected) return false;
2811
+ return withCaptchaMouseLock(async () => {
2812
+ const block = detected.locator;
2813
+ if (!await isVisible(block)) return false;
2814
+ logBaxiaCaptcha("aliyun_detected", { selector: detected.selector });
2815
+ let lastReason = "unknown";
2816
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2817
+ try {
2818
+ const offset = await computeAliyunPuzzleOffset(page);
2819
+ if (!offset.ok || typeof offset.offsetX !== "number" || typeof offset.naturalWidth !== "number" || offset.naturalWidth <= 0) {
2820
+ lastReason = offset.reason || "offset_unavailable";
2821
+ logBaxiaCaptcha("aliyun_offset_failed", {
2822
+ attempt,
2823
+ reason: lastReason
2824
+ });
2825
+ } else {
2826
+ const slider = page.locator(ALIYUN_PUZZLE_SLIDER_SELECTOR).first();
2827
+ const track = page.locator(ALIYUN_PUZZLE_TRACK_SELECTOR).first();
2828
+ await slider.waitFor({ state: "visible", timeout: sliderTimeoutMs });
2829
+ const [sliderBox, trackBox] = await Promise.all([
2830
+ slider.boundingBox(),
2831
+ track.boundingBox()
2832
+ ]);
2833
+ if (!sliderBox || !trackBox || trackBox.width <= 0) {
2834
+ lastReason = "bounds_unavailable";
2835
+ logBaxiaCaptcha("aliyun_offset_failed", {
2836
+ attempt,
2837
+ reason: lastReason
2838
+ });
2839
+ } else {
2840
+ const scale = trackBox.width / offset.naturalWidth;
2841
+ const dragDistance = Math.round(offset.offsetX * scale);
2842
+ logBaxiaCaptcha("aliyun_geometry", {
2843
+ attempt,
2844
+ offset: offset.offsetX,
2845
+ scale: Number(scale.toFixed(2)),
2846
+ distance: dragDistance
2847
+ });
2848
+ if (dragDistance <= 0) {
2849
+ lastReason = "non_positive_distance";
2850
+ } else {
2851
+ const startX = sliderBox.x + sliderBox.width / 2;
2852
+ const startY = sliderBox.y + sliderBox.height / 2;
2853
+ await humanDrag(
2854
+ page,
2855
+ startX,
2856
+ startY,
2857
+ startX + dragDistance,
2858
+ startY
2859
+ );
2860
+ if (await waitForHidden(block, settleMs)) {
2861
+ logBaxiaCaptcha(
2862
+ "aliyun_solve_succeeded",
2863
+ { attempt, distance: dragDistance },
2864
+ true
2865
+ );
2866
+ return true;
2867
+ }
2868
+ lastReason = "not_solved";
2869
+ }
2870
+ }
2871
+ }
2872
+ } catch (error) {
2873
+ lastReason = error instanceof Error ? error.name : "UnknownError";
2874
+ logBaxiaCaptcha("aliyun_offset_failed", {
2875
+ attempt,
2876
+ error: lastReason,
2877
+ detail: sanitizeCaptchaErrorDetail(error)
2878
+ });
2879
+ }
2880
+ if (attempt < maxAttempts && retryDelayMs > 0) {
2881
+ await sleep(retryDelayMs);
2882
+ }
2883
+ }
2884
+ logBaxiaCaptcha(
2885
+ "aliyun_solve_failed",
2886
+ { attempts: maxAttempts, reason: lastReason },
2887
+ true
2888
+ );
2889
+ return false;
2890
+ });
2891
+ }
2892
+ async function solveVisibleCaptcha(page, options = {}) {
2893
+ if (typeof page.isClosed === "function" && page.isClosed()) return false;
2894
+ const totalWaitMs = Math.max(0, options.waitForMs ?? 0);
2895
+ const aliyunWaitMs = Math.min(totalWaitMs, ALIYUN_PROBE_MAX_WAIT_MS);
2896
+ const startedAt = Date.now();
2897
+ if (await solveAliyunWafCaptcha(page, { ...options, waitForMs: aliyunWaitMs })) {
2898
+ return true;
2899
+ }
2900
+ const remainingWaitMs = Math.max(
2901
+ 0,
2902
+ totalWaitMs - (Date.now() - startedAt)
2903
+ );
2904
+ return solveBaxiaCaptcha(page, { ...options, waitForMs: remainingWaitMs });
2905
+ }
2615
2906
  async function isVisible(locator) {
2616
2907
  return locator.isVisible().catch(() => false);
2617
2908
  }
@@ -2854,7 +3145,7 @@ async function solveBaxiaCaptchaUnlocked(page, options = {}) {
2854
3145
  );
2855
3146
  return false;
2856
3147
  }
2857
- 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;
3148
+ 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;
2858
3149
  var init_captcha_solver = __esm({
2859
3150
  "src/services/captcha-solver.ts"() {
2860
3151
  "use strict";
@@ -2880,14 +3171,20 @@ var init_captcha_solver = __esm({
2880
3171
  solve_failed: "\u274C",
2881
3172
  recovery_succeeded: "\u2705",
2882
3173
  recovery_not_solved: "\u26A0\uFE0F",
2883
- recovery_failed: "\u274C"
3174
+ recovery_failed: "\u274C",
3175
+ aliyun_detected: "\u{1F9E9}",
3176
+ aliyun_offset_failed: "\u{1F4D0}",
3177
+ aliyun_geometry: "\u{1F4D0}",
3178
+ aliyun_solve_succeeded: "\u2705",
3179
+ aliyun_solve_failed: "\u274C"
2884
3180
  };
2885
3181
  captchaDebugEnabled = process.env.CAPTCHA_DEBUG === "true";
2886
3182
  BAXIA_IFRAME_SELECTORS = [
2887
3183
  `${BAXIA_DIALOG_SELECTOR} ${BAXIA_CONTENT_SELECTOR} iframe`,
2888
3184
  `${BAXIA_CONTENT_SELECTOR} iframe`,
2889
3185
  "iframe#baxia-dialog-content",
2890
- 'iframe[src*="_____tmd_____/punish"]'
3186
+ 'iframe[src*="_____tmd_____/punish"]',
3187
+ 'iframe[src*="nocaptcha"]'
2891
3188
  ];
2892
3189
  BAXIA_DOCUMENT_SELECTORS = [
2893
3190
  "#nocaptcha",
@@ -2895,14 +3192,23 @@ var init_captcha_solver = __esm({
2895
3192
  "#baxia-punish"
2896
3193
  ];
2897
3194
  BAXIA_IFRAME_SELECTOR = BAXIA_IFRAME_SELECTORS.join(", ");
2898
- BAXIA_SLIDER_SELECTOR = "#nc_1_n1z, .nc_1_n1z, .btn_slide, .nc_wrapper .btn_slide, ._nc .btn_slide, .nc-container .btn_slide";
2899
- BAXIA_TRACK_SELECTOR = "#nc_1_n1t, .nc_scale, .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale";
3195
+ 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';
3196
+ BAXIA_TRACK_SELECTOR = '#nc_1_n1t, .nc_scale, span[id*="_n1t"], .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale';
2900
3197
  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';
2901
3198
  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"]';
2902
3199
  DEFAULT_MAX_ATTEMPTS = 3;
2903
3200
  DEFAULT_RETRY_DELAY_MS = 1e3;
2904
3201
  DEFAULT_SETTLE_MS = 2e3;
2905
3202
  DEFAULT_SLIDER_TIMEOUT_MS = 8e3;
3203
+ ALIYUN_WAF_CAPTCHA_SELECTOR = "#waf_nc_block";
3204
+ ALIYUN_WAF_CAPTCHA_WIDGET = "#aliyunCaptcha-window-embed";
3205
+ ALIYUN_WAF_BLOCK_SELECTORS = [
3206
+ ALIYUN_WAF_CAPTCHA_SELECTOR,
3207
+ ALIYUN_WAF_CAPTCHA_WIDGET
3208
+ ];
3209
+ ALIYUN_PUZZLE_SLIDER_SELECTOR = "#aliyunCaptcha-sliding-slider";
3210
+ ALIYUN_PUZZLE_TRACK_SELECTOR = "#aliyunCaptcha-sliding-body";
3211
+ ALIYUN_PROBE_MAX_WAIT_MS = 2e3;
2906
3212
  captchaMouseLock = Promise.resolve();
2907
3213
  }
2908
3214
  });
@@ -2993,7 +3299,15 @@ function updateQwenWebVersion(version) {
2993
3299
  function getQwenWebVersion() {
2994
3300
  return dynamicWebVersion || config.qwen.webVersion;
2995
3301
  }
2996
- var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
3302
+ function updateQwenBxVersion(version) {
3303
+ if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
3304
+ dynamicBxVersion = version.trim();
3305
+ }
3306
+ }
3307
+ function getQwenBxVersion() {
3308
+ return dynamicBxVersion || config.auth.bxV;
3309
+ }
3310
+ var dynamicWebVersion, dynamicBxVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
2997
3311
  var init_qwen_headers = __esm({
2998
3312
  "src/services/qwen-headers.ts"() {
2999
3313
  "use strict";
@@ -3002,6 +3316,7 @@ var init_qwen_headers = __esm({
3002
3316
  init_fingerprint();
3003
3317
  init_timezone();
3004
3318
  dynamicWebVersion = null;
3319
+ dynamicBxVersion = null;
3005
3320
  QWEN_WEB_VERSION = config.qwen.webVersion;
3006
3321
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
3007
3322
  QWEN_TIMEZONE_HEADER = (/* @__PURE__ */ new Date()).toString().split(" (")[0];
@@ -3615,11 +3930,12 @@ async function getOrLaunchSharedBrowser(browserType = "chromium", headless = con
3615
3930
  async function clearVisibleChallenge(page) {
3616
3931
  if (page.isClosed()) return;
3617
3932
  if (config.captcha.enabled) {
3618
- await solveBaxiaCaptcha(page, {
3933
+ await solveVisibleCaptcha(page, {
3619
3934
  waitForMs: 0,
3620
3935
  maxAttempts: config.captcha.maxAttempts,
3621
3936
  retryDelayMs: config.captcha.retryDelayMs,
3622
- settleMs: config.captcha.settleMs
3937
+ settleMs: config.captcha.settleMs,
3938
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
3623
3939
  }).catch(() => false);
3624
3940
  }
3625
3941
  try {
@@ -4232,6 +4548,7 @@ async function getBasicHeaders(accountId) {
4232
4548
  mobile = data.mobile ? "?1" : "?0";
4233
4549
  }
4234
4550
  let bundleVersion = null;
4551
+ let baxiaVersion = null;
4235
4552
  try {
4236
4553
  const el = document.querySelector('link[href*="qwen-chat-fe"], script[src*="qwen-chat-fe"], img[src*="qwen-chat-fe"]');
4237
4554
  const src = el ? el.getAttribute("href") || el.getAttribute("src") || "" : "";
@@ -4239,7 +4556,14 @@ async function getBasicHeaders(accountId) {
4239
4556
  if (match) bundleVersion = match[1];
4240
4557
  } catch {
4241
4558
  }
4242
- return { ua, secChUa, platform, mobile, bundleVersion };
4559
+ try {
4560
+ const baxia = window.baxiaCommon;
4561
+ if (typeof baxia?.version === "string") {
4562
+ baxiaVersion = baxia.version;
4563
+ }
4564
+ } catch {
4565
+ }
4566
+ return { ua, secChUa, platform, mobile, bundleVersion, baxiaVersion };
4243
4567
  }),
4244
4568
  config.timeouts.page,
4245
4569
  `User-agent lookup timed out for ${accountId}`
@@ -4248,6 +4572,9 @@ async function getBasicHeaders(accountId) {
4248
4572
  if (nav.bundleVersion) {
4249
4573
  updateQwenWebVersion(nav.bundleVersion);
4250
4574
  }
4575
+ if (nav.baxiaVersion) {
4576
+ updateQwenBxVersion(nav.baxiaVersion);
4577
+ }
4251
4578
  cachedUserAgents.set(accountId, userAgent);
4252
4579
  const hints = getHeaderCache(accountId).headers;
4253
4580
  clientHints = {
@@ -4279,7 +4606,7 @@ async function getBasicHeaders(accountId) {
4279
4606
  markAccountHeadersReady(accountId);
4280
4607
  const bxUa2 = cache.headers["bx-ua"];
4281
4608
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4282
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4609
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4283
4610
  const cookie2 = await getCookies(accountId);
4284
4611
  return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
4285
4612
  }
@@ -4290,7 +4617,7 @@ async function getBasicHeaders(accountId) {
4290
4617
  cookieCaches.set(accountId, { cookie: cookie2, timestamp: Date.now() });
4291
4618
  const bxUa2 = cache.headers["bx-ua"];
4292
4619
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4293
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4620
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4294
4621
  cache.lastRefresh = Date.now();
4295
4622
  markAccountHeadersReady(accountId);
4296
4623
  console.log(
@@ -4310,14 +4637,15 @@ async function getBasicHeaders(accountId) {
4310
4637
  `\u26A0\uFE0F [Playwright] Header refresh failed for ${accountId}; retaining the previous valid cache: ${getErrorMessage(error)}`
4311
4638
  );
4312
4639
  }
4313
- } else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache.refreshInProgress) {
4314
- cache.refreshInProgress = true;
4640
+ } else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache.refreshInProgress && // Header capture types into the chat input and sends a probe message on
4641
+ // the account's page. Doing that while the account is serving a stream
4642
+ // would mutate the live conversation (and can navigate the SPA), so the
4643
+ // proactive refresh waits until the account has no active lease.
4644
+ !hasActiveAccountLease(accountId)) {
4315
4645
  refreshHeadersInternal(accountId).catch((error) => {
4316
4646
  console.warn(
4317
4647
  `\u26A0\uFE0F [Playwright] Background header refresh failed for ${accountId}: ${getErrorMessage(error)}`
4318
4648
  );
4319
- }).finally(() => {
4320
- cache.refreshInProgress = false;
4321
4649
  });
4322
4650
  }
4323
4651
  if (!hasRequiredQwenHeaders(cache.headers)) {
@@ -4364,6 +4692,9 @@ async function getBasicHeaders(accountId) {
4364
4692
  if (persisted.version) {
4365
4693
  updateQwenWebVersion(persisted.version);
4366
4694
  }
4695
+ if (persisted.bxV) {
4696
+ updateQwenBxVersion(persisted.bxV);
4697
+ }
4367
4698
  cache.lastRefresh = persisted.capturedAt;
4368
4699
  markAccountHeadersReady(accountId);
4369
4700
  }
@@ -4391,7 +4722,7 @@ async function getBasicHeaders(accountId) {
4391
4722
  markAccountHeadersReady(accountId);
4392
4723
  const bxUa = cache.headers["bx-ua"];
4393
4724
  const bxUmidtoken = cache.headers["bx-umidtoken"];
4394
- const bxV = cache.headers["bx-v"] || "2.5.37";
4725
+ const bxV = cache.headers["bx-v"] || getQwenBxVersion();
4395
4726
  cookieCaches.delete(accountId);
4396
4727
  const cookie = await getCookies(accountId, true);
4397
4728
  if (cache.headers) {
@@ -4455,8 +4786,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4455
4786
  if (accountPages.has(account.id)) {
4456
4787
  return;
4457
4788
  }
4458
- await evictIdlePlaywrightContextsToLimit().catch(() => {
4459
- });
4789
+ if (config.playwright.maxActiveContexts > 0) {
4790
+ await evictIdlePlaywrightContextsToLimit(
4791
+ Math.max(1, config.playwright.maxActiveContexts - 1)
4792
+ ).catch(() => {
4793
+ });
4794
+ }
4460
4795
  const profilePath = getAccountProfilePath(account.id);
4461
4796
  const fingerprint = getFingerprintProfile(account.id);
4462
4797
  const { engine, channel } = resolveBrowserEngine(browserType);
@@ -4632,6 +4967,9 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4632
4967
  if (persisted.version) {
4633
4968
  updateQwenWebVersion(persisted.version);
4634
4969
  }
4970
+ if (persisted.bxV) {
4971
+ updateQwenBxVersion(persisted.bxV);
4972
+ }
4635
4973
  cache.lastRefresh = persisted.capturedAt;
4636
4974
  markAccountHeadersReady(account.id);
4637
4975
  restoredFromDb = true;
@@ -4725,7 +5063,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4725
5063
  if (!acctPage.isClosed()) {
4726
5064
  try {
4727
5065
  const currentUrl = new URL(acctPage.url());
4728
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
5066
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
4729
5067
  await acctPage.goto(qwenUrl("/"), {
4730
5068
  waitUntil: "domcontentloaded",
4731
5069
  timeout: config.timeouts.navigation
@@ -4743,6 +5081,8 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4743
5081
  } finally {
4744
5082
  release();
4745
5083
  }
5084
+ await evictIdlePlaywrightContextsToLimit().catch(() => {
5085
+ });
4746
5086
  })();
4747
5087
  try {
4748
5088
  await initPromise;
@@ -4979,7 +5319,7 @@ async function startEmailOtpLogin(email, options = {}) {
4979
5319
  {
4980
5320
  email: normalizedEmail,
4981
5321
  requestId,
4982
- version: getQwenWebVersion() || "0.3.26",
5322
+ version: getQwenWebVersion(),
4983
5323
  timezone: formatAsciiTimezone()
4984
5324
  }
4985
5325
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5074,7 +5414,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
5074
5414
  email: session.email,
5075
5415
  code: cleanCode,
5076
5416
  requestId,
5077
- version: getQwenWebVersion() || "0.3.26",
5417
+ version: getQwenWebVersion(),
5078
5418
  timezone: formatAsciiTimezone()
5079
5419
  }
5080
5420
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5121,8 +5461,8 @@ async function verifyEmailOtpLogin(sessionId, code) {
5121
5461
  saveAuthSession2(session.accountId, {
5122
5462
  cookie: `token=${accessToken}`,
5123
5463
  userAgent: session.fingerprint.userAgent,
5124
- bxV: "2.5.37",
5125
- version: getQwenWebVersion() || "0.3.26",
5464
+ bxV: getQwenBxVersion(),
5465
+ version: getQwenWebVersion(),
5126
5466
  userId: userId || void 0,
5127
5467
  tokenExpiresAt: exp || void 0,
5128
5468
  refreshToken: refreshToken || void 0,
@@ -5232,7 +5572,7 @@ async function loginToQwen(accountId, email, password) {
5232
5572
  saveAuthSession2(accountId, {
5233
5573
  cookie: cookieStr,
5234
5574
  userAgent: cache.headers["user-agent"] || "",
5235
- bxV: cache.headers["bx-v"] || "2.5.37",
5575
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5236
5576
  bxUa: cache.headers["bx-ua"] || "",
5237
5577
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5238
5578
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5304,7 +5644,7 @@ async function loginToQwen(accountId, email, password) {
5304
5644
  saveAuthSession2(accountId, {
5305
5645
  cookie: cookieStr,
5306
5646
  userAgent: cache.headers["user-agent"] || "",
5307
- bxV: cache.headers["bx-v"] || "2.5.37",
5647
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5308
5648
  bxUa: cache.headers["bx-ua"] || "",
5309
5649
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5310
5650
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5375,7 +5715,7 @@ async function loginViaApi(page, email, password) {
5375
5715
  }
5376
5716
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5377
5717
  const requestId = crypto3.randomUUID();
5378
- const webVersion = getQwenWebVersion() || "0.3.26";
5718
+ const webVersion = getQwenWebVersion();
5379
5719
  const evalRes = await page.evaluate(
5380
5720
  async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
5381
5721
  try {
@@ -5547,6 +5887,15 @@ async function loginViaUi(page, email, password) {
5547
5887
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
5548
5888
  return { success: true };
5549
5889
  }
5890
+ if (config.captcha.enabled) {
5891
+ await solveVisibleCaptcha(page, {
5892
+ waitForMs: 1500,
5893
+ maxAttempts: config.captcha.maxAttempts,
5894
+ retryDelayMs: config.captcha.retryDelayMs,
5895
+ settleMs: config.captcha.settleMs,
5896
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
5897
+ }).catch(() => false);
5898
+ }
5550
5899
  const emailSelector = [
5551
5900
  'input[type="email"]',
5552
5901
  'input[name="email"]',
@@ -5702,10 +6051,11 @@ async function loginViaUi(page, email, password) {
5702
6051
  while (Date.now() < postSubmitDeadline) {
5703
6052
  if (page.isClosed()) break;
5704
6053
  if (config.captcha.enabled) {
5705
- await solveBaxiaCaptcha(page, {
6054
+ await solveVisibleCaptcha(page, {
5706
6055
  waitForMs: 1500,
5707
6056
  maxAttempts: config.captcha.maxAttempts,
5708
- retryDelayMs: config.captcha.retryDelayMs
6057
+ retryDelayMs: config.captcha.retryDelayMs,
6058
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
5709
6059
  }).catch(() => {
5710
6060
  });
5711
6061
  }
@@ -5828,7 +6178,7 @@ async function refreshAccountToken(accountId) {
5828
6178
  }
5829
6179
  if (refreshToken) {
5830
6180
  try {
5831
- const webVersion = getQwenWebVersion() || "0.3.26";
6181
+ const webVersion = getQwenWebVersion();
5832
6182
  const refreshResult = await page.evaluate(
5833
6183
  async ({ rTok, tz, version }) => {
5834
6184
  try {
@@ -5893,7 +6243,7 @@ async function refreshAccountToken(accountId) {
5893
6243
  saveAuthSession2(accountId, {
5894
6244
  cookie: cookieStr,
5895
6245
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5896
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6246
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5897
6247
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5898
6248
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5899
6249
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -5928,7 +6278,7 @@ async function refreshAccountToken(accountId) {
5928
6278
  saveAuthSession2(accountId, {
5929
6279
  cookie: cookieStr,
5930
6280
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5931
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6281
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5932
6282
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5933
6283
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5934
6284
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -6076,7 +6426,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6076
6426
  cookie: reqHeaders["cookie"] || "",
6077
6427
  "bx-ua": reqHeaders["bx-ua"] || "",
6078
6428
  "bx-umidtoken": reqHeaders["bx-umidtoken"] || "",
6079
- "bx-v": reqHeaders["bx-v"] || "2.5.37",
6429
+ "bx-v": reqHeaders["bx-v"] || getQwenBxVersion(),
6080
6430
  "user-agent": reqHeaders["user-agent"] || "",
6081
6431
  "x-request-id": reqHeaders["x-request-id"] || "",
6082
6432
  "version": reqHeaders["version"] || "",
@@ -6108,6 +6458,9 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6108
6458
  if (capturedHeaders["version"]) {
6109
6459
  updateQwenWebVersion(capturedHeaders["version"]);
6110
6460
  }
6461
+ if (capturedHeaders["bx-v"]) {
6462
+ updateQwenBxVersion(capturedHeaders["bx-v"]);
6463
+ }
6111
6464
  markAccountHeadersReady(accountId);
6112
6465
  cache.lastRefresh = Date.now();
6113
6466
  cookieCaches.delete(accountId);
@@ -6511,19 +6864,26 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6511
6864
  await executeReauth();
6512
6865
  } else {
6513
6866
  try {
6514
- await page.goto(qwenUrl("/"), {
6515
- waitUntil: "domcontentloaded",
6516
- timeout: Math.min(
6517
- config.timeouts.navigation,
6518
- boundedTimeoutMs,
6519
- SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6520
- )
6521
- });
6522
- await sleep2(2e3);
6523
- const url = page.url();
6867
+ const currentUrl = typeof page.url === "function" ? page.url() : "";
6868
+ const needsNavigation = !currentUrl.startsWith(qwenOrigin()) || currentUrl.includes("/auth") || currentUrl.includes("/login") || currentUrl.includes("_____tmd_____");
6869
+ if (needsNavigation) {
6870
+ await page.goto(qwenUrl("/"), {
6871
+ waitUntil: "domcontentloaded",
6872
+ timeout: Math.min(
6873
+ config.timeouts.navigation,
6874
+ boundedTimeoutMs,
6875
+ SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6876
+ )
6877
+ });
6878
+ await sleep2(2e3);
6879
+ }
6880
+ const url = typeof page.url === "function" ? page.url() : "";
6524
6881
  const isAuthUrl = url.includes("auth") || url.includes("login");
6525
6882
  if (isAuthUrl) {
6526
- await executeReauth();
6883
+ const refreshed = await refreshAccountToken(accountId);
6884
+ if (!refreshed.success) {
6885
+ await executeReauth();
6886
+ }
6527
6887
  }
6528
6888
  } catch (navErr) {
6529
6889
  console.warn(
@@ -6558,7 +6918,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6558
6918
  saveAuthSession2(accountId, {
6559
6919
  cookie: cookieStr,
6560
6920
  userAgent: cache.headers["user-agent"] || "",
6561
- bxV: cache.headers["bx-v"] || "2.5.37",
6921
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
6562
6922
  bxUa: cache.headers["bx-ua"] || "",
6563
6923
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
6564
6924
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -6580,7 +6940,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6580
6940
  if (capturedPage && !capturedPage.isClosed()) {
6581
6941
  try {
6582
6942
  const currentUrl = new URL(capturedPage.url());
6583
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
6943
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
6584
6944
  await capturedPage.goto(qwenUrl("/"), {
6585
6945
  waitUntil: "domcontentloaded",
6586
6946
  timeout: Math.min(config.timeouts.navigation, boundedTimeoutMs)
@@ -6896,7 +7256,11 @@ function priorityOrderForEviction(accountIds) {
6896
7256
  const ranked = getAccountsByPriority(accountIds.map((id) => ({ id })));
6897
7257
  const rank = /* @__PURE__ */ new Map();
6898
7258
  for (const [index, account] of ranked.entries()) {
6899
- rank.set(account.id, index);
7259
+ if (getAccountCooldownInfo(account.id)) {
7260
+ rank.set(account.id, Number.MAX_SAFE_INTEGER);
7261
+ } else {
7262
+ rank.set(account.id, index);
7263
+ }
6900
7264
  }
6901
7265
  return orderContextsForEviction(
6902
7266
  accountIds,
@@ -6933,14 +7297,15 @@ async function closeIdlePlaywrightAccounts(idleMs) {
6933
7297
  }
6934
7298
  return closed;
6935
7299
  }
6936
- async function evictIdlePlaywrightContextsToLimit() {
6937
- const max = config.playwright.maxActiveContexts;
6938
- if (max <= 0) return 0;
7300
+ async function evictIdlePlaywrightContextsToLimit(targetLimit) {
7301
+ const configuredMax = config.playwright.maxActiveContexts;
7302
+ if (configuredMax <= 0) return 0;
7303
+ const max = targetLimit !== void 0 ? Math.max(0, targetLimit) : configuredMax;
6939
7304
  if (accountPages.size <= max) return 0;
6940
7305
  const candidates = priorityOrderForEviction(
6941
7306
  Array.from(accountPages.keys()).filter((accountId) => {
6942
7307
  if (isAccountServingStream(accountId)) return false;
6943
- if (isAccountRecentlyActive(accountId, 3e4)) return false;
7308
+ if (hasActiveAccountLease(accountId)) return false;
6944
7309
  const mutex = accountMutexes.get(accountId);
6945
7310
  return mutex?.isIdle();
6946
7311
  })
@@ -6953,6 +7318,7 @@ async function evictIdlePlaywrightContextsToLimit() {
6953
7318
  for (const candidate of candidates) {
6954
7319
  if (accountPages.size <= max) break;
6955
7320
  if (isAccountServingStream(candidate.accountId)) continue;
7321
+ if (hasActiveAccountLease(candidate.accountId)) continue;
6956
7322
  const mutex = accountMutexes.get(candidate.accountId);
6957
7323
  if (!mutex?.isIdle()) continue;
6958
7324
  await closePlaywrightForAccount(candidate.accountId).catch((error) => {