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.
@@ -1093,7 +1093,7 @@ var init_config = __esm({
1093
1093
  // Version header on Qwen API requests = the deployed web bundle version
1094
1094
  // (fallback when dynamic DOM detection is not yet completed).
1095
1095
  // Override via env when Qwen ships a new bundle.
1096
- QWEN_WEB_VERSION: z.string().default("0.3.26"),
1096
+ QWEN_WEB_VERSION: z.string().default("0.3.43"),
1097
1097
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
1098
1098
  // (chats/new, settings): those work without them (live-probed). The
1099
1099
  // completions path is the exception — the 0.2.86 HAR shows the real
@@ -1136,6 +1136,7 @@ var init_config = __esm({
1136
1136
  CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
1137
1137
  CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
1138
1138
  CAPTCHA_SOLVER_TIMEOUT_MS: z.string().default("15000"),
1139
+ CAPTCHA_SLIDER_TIMEOUT_MS: z.string().default("8000"),
1139
1140
  CAPTCHA_SOLVER_RETRY_DELAY_MS: z.string().default("1000"),
1140
1141
  CAPTCHA_SOLVER_SETTLE_MS: z.string().default("2000"),
1141
1142
  CAPTCHA_ACCOUNT_COOLDOWN_MS: z.string().default("120000"),
@@ -1287,6 +1288,8 @@ var init_config = __esm({
1287
1288
  enabled: env.CAPTCHA_SOLVER_ENABLED === "true",
1288
1289
  maxAttempts: Math.max(1, Math.min(5, parseInt(env.CAPTCHA_SOLVER_MAX_ATTEMPTS))),
1289
1290
  timeoutMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_TIMEOUT_MS)),
1291
+ /** Max wait for a challenge slider to become visible (slow WAF renders). */
1292
+ sliderTimeoutMs: Math.max(500, parseInt(env.CAPTCHA_SLIDER_TIMEOUT_MS)),
1290
1293
  retryDelayMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_RETRY_DELAY_MS)),
1291
1294
  settleMs: Math.max(0, parseInt(env.CAPTCHA_SOLVER_SETTLE_MS)),
1292
1295
  /** Rest an account whose challenge could not be cleared before reusing it. */
@@ -1446,6 +1449,367 @@ var init_config = __esm({
1446
1449
  }
1447
1450
  });
1448
1451
 
1452
+ // src/services/qwen-url.ts
1453
+ function qwenUrl(path6 = "") {
1454
+ const normalizedPath = path6.replace(/^\/+/, "");
1455
+ return normalizedPath ? `${QWEN_BASE_URL}/${normalizedPath}` : QWEN_BASE_URL;
1456
+ }
1457
+ function qwenOrigin() {
1458
+ return QWEN_ORIGIN;
1459
+ }
1460
+ var QWEN_BASE_URL, QWEN_ORIGIN;
1461
+ var init_qwen_url = __esm({
1462
+ "src/services/qwen-url.ts"() {
1463
+ "use strict";
1464
+ init_config();
1465
+ QWEN_BASE_URL = config.qwen.baseUrl.trim().replace(/\/+$/, "");
1466
+ QWEN_ORIGIN = new URL(QWEN_BASE_URL).origin;
1467
+ }
1468
+ });
1469
+
1470
+ // src/services/fingerprint.ts
1471
+ function mulberry32(seed) {
1472
+ return () => {
1473
+ seed |= 0;
1474
+ seed = seed + 1831565813 | 0;
1475
+ let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
1476
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
1477
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1478
+ };
1479
+ }
1480
+ function seedFromString(value) {
1481
+ let hash = 0;
1482
+ for (let i = 0; i < value.length; i++) {
1483
+ hash = (hash << 5) - hash + value.charCodeAt(i) | 0;
1484
+ }
1485
+ return hash >>> 0;
1486
+ }
1487
+ function pick(rng, values) {
1488
+ return values[Math.floor(rng() * values.length)];
1489
+ }
1490
+ function randInt(rng, min, max) {
1491
+ return Math.floor(rng() * (max - min + 1)) + min;
1492
+ }
1493
+ function updateChromeMajor(major) {
1494
+ if (typeof major === "number" && major >= 100) {
1495
+ dynamicChromeMajor = major;
1496
+ }
1497
+ }
1498
+ function getChromeMajor() {
1499
+ return dynamicChromeMajor;
1500
+ }
1501
+ function getSystemLocale() {
1502
+ return process.env.QWEN_LOCALE || (typeof Intl !== "undefined" && Intl.DateTimeFormat ? Intl.DateTimeFormat().resolvedOptions().locale : "") || "pt-BR";
1503
+ }
1504
+ function getLanguageProfiles(locale = getSystemLocale()) {
1505
+ const primary = locale.split("-")[0];
1506
+ if (primary === "en") {
1507
+ return [
1508
+ ["en-US", "en"],
1509
+ ["en-US", "en", "es"],
1510
+ ["en", "en-US"],
1511
+ ["en-US", "en;q=0.9"]
1512
+ ];
1513
+ }
1514
+ return [
1515
+ [locale, primary, "en-US", "en"],
1516
+ [locale, primary, "en-US", "en", "es"],
1517
+ [locale, "en-US", "en", primary],
1518
+ [locale, primary, "en"],
1519
+ [locale, `${primary};q=0.9`, "en-US;q=0.8", "en;q=0.7"]
1520
+ ];
1521
+ }
1522
+ function rotateFingerprintSeed(accountId) {
1523
+ const next = (rotationSalts.get(accountId) ?? 0) + 1;
1524
+ rotationSalts.set(accountId, next);
1525
+ profileCache.delete(accountId);
1526
+ return next;
1527
+ }
1528
+ function getFingerprintProfile(accountId) {
1529
+ const cached = profileCache.get(accountId);
1530
+ if (cached) return cached;
1531
+ const salt = rotationSalts.get(accountId) ?? 0;
1532
+ const seed = seedFromString(salt === 0 ? accountId : `${accountId}#r${salt}`);
1533
+ const rng = mulberry32(seed);
1534
+ const viewport = pick(rng, VIEWPORTS);
1535
+ const webgl = pick(rng, WEBGL_PROFILES);
1536
+ const languages = [...pick(rng, getLanguageProfiles())];
1537
+ const hardwareConcurrency = pick(rng, HARDWARE_CONCURRENCIES);
1538
+ const deviceMemory = pick(rng, DEVICE_MEMORIES);
1539
+ const platformInfo = pick(rng, PLATFORM_VERSIONS);
1540
+ const notABrand = pick(rng, NOT_A_BRAND_VARIANTS);
1541
+ const major = dynamicChromeMajor;
1542
+ const build = randInt(rng, 7300, 7600);
1543
+ const patch = randInt(rng, 0, 160);
1544
+ const chromeVersion = `${major}.0.${build}.${patch}`;
1545
+ const brands = [
1546
+ { brand: notABrand.brand, version: notABrand.version },
1547
+ { brand: "Google Chrome", version: String(major) },
1548
+ { brand: "Chromium", version: String(major) }
1549
+ ];
1550
+ const fullVersionList = [
1551
+ { brand: notABrand.brand, version: chromeVersion },
1552
+ { brand: "Google Chrome", version: chromeVersion },
1553
+ { brand: "Chromium", version: chromeVersion }
1554
+ ];
1555
+ const secChUa = brands.map((brand) => `"${brand.brand}";v="${brand.version}"`).join(", ");
1556
+ const userAgent = `Mozilla/5.0 (Windows NT ${platformInfo.major}.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
1557
+ const profile = {
1558
+ accountId,
1559
+ seed,
1560
+ userAgent,
1561
+ appVersion: userAgent.replace("Mozilla/", ""),
1562
+ chromeMajor: CHROME_MAJOR,
1563
+ chromeVersion,
1564
+ brands,
1565
+ fullVersionList,
1566
+ secChUa,
1567
+ platform: "Win32",
1568
+ platformVersion: platformInfo.platformVersion,
1569
+ architecture: "x86",
1570
+ bitness: "64",
1571
+ languages,
1572
+ locale: languages[0].split(";")[0],
1573
+ timezoneId: "America/Sao_Paulo",
1574
+ viewport,
1575
+ hardwareConcurrency,
1576
+ deviceMemory,
1577
+ webglVendor: webgl.vendor,
1578
+ webglRenderer: webgl.renderer,
1579
+ colorDepth: 24,
1580
+ pixelDepth: 24,
1581
+ canvasNoiseSeed: randInt(rng, 1, 2147483647),
1582
+ audioNoiseSeed: randInt(rng, 1, 2147483647),
1583
+ webglNoiseSeed: randInt(rng, 1, 2147483647),
1584
+ outerWidthOffset: randInt(rng, 0, 20),
1585
+ outerHeightOffset: randInt(rng, 75, 95)
1586
+ };
1587
+ profileCache.set(accountId, profile);
1588
+ return profile;
1589
+ }
1590
+ function clearFingerprintCache(accountId) {
1591
+ if (accountId) profileCache.delete(accountId);
1592
+ else profileCache.clear();
1593
+ }
1594
+ var dynamicChromeMajor, CHROME_MAJOR, VIEWPORTS, WEBGL_PROFILES, HARDWARE_CONCURRENCIES, DEVICE_MEMORIES, PLATFORM_VERSIONS, NOT_A_BRAND_VARIANTS, profileCache, rotationSalts;
1595
+ var init_fingerprint = __esm({
1596
+ "src/services/fingerprint.ts"() {
1597
+ "use strict";
1598
+ dynamicChromeMajor = 151;
1599
+ CHROME_MAJOR = 151;
1600
+ VIEWPORTS = [
1601
+ { width: 1366, height: 768 },
1602
+ { width: 1440, height: 900 },
1603
+ { width: 1536, height: 864 },
1604
+ { width: 1600, height: 900 },
1605
+ { width: 1680, height: 1050 },
1606
+ { width: 1920, height: 1080 },
1607
+ { width: 1920, height: 1200 },
1608
+ { width: 1280, height: 720 },
1609
+ { width: 1280, height: 800 },
1610
+ { width: 1536, height: 864 }
1611
+ ];
1612
+ WEBGL_PROFILES = [
1613
+ {
1614
+ vendor: "Google Inc. (Intel)",
1615
+ renderer: "ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1616
+ },
1617
+ {
1618
+ vendor: "Google Inc. (Intel)",
1619
+ renderer: "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)"
1620
+ },
1621
+ {
1622
+ vendor: "Google Inc. (Intel)",
1623
+ renderer: "ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1624
+ },
1625
+ {
1626
+ vendor: "Google Inc. (NVIDIA)",
1627
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1628
+ },
1629
+ {
1630
+ vendor: "Google Inc. (NVIDIA)",
1631
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1632
+ },
1633
+ {
1634
+ vendor: "Google Inc. (NVIDIA)",
1635
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1636
+ },
1637
+ {
1638
+ vendor: "Google Inc. (NVIDIA)",
1639
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1640
+ },
1641
+ {
1642
+ vendor: "Google Inc. (NVIDIA)",
1643
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1644
+ },
1645
+ {
1646
+ vendor: "Google Inc. (NVIDIA)",
1647
+ renderer: "ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)"
1648
+ },
1649
+ {
1650
+ vendor: "Google Inc. (AMD)",
1651
+ renderer: "ANGLE (AMD, AMD Radeon RX 6600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1652
+ },
1653
+ {
1654
+ vendor: "Google Inc. (AMD)",
1655
+ renderer: "ANGLE (AMD, AMD Radeon RX 7600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1656
+ },
1657
+ {
1658
+ vendor: "Google Inc. (AMD)",
1659
+ renderer: "ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)"
1660
+ }
1661
+ ];
1662
+ HARDWARE_CONCURRENCIES = [4, 6, 8, 8, 8, 12, 16, 16, 24, 32];
1663
+ DEVICE_MEMORIES = [4, 8, 8, 8, 16, 16, 32];
1664
+ PLATFORM_VERSIONS = [
1665
+ { platform: "Windows", platformVersion: "14.0.0", major: "10" },
1666
+ { platform: "Windows", platformVersion: "15.0.0", major: "11" },
1667
+ { platform: "Windows", platformVersion: "14.0.0", major: "10" },
1668
+ { platform: "Windows", platformVersion: "15.0.0", major: "11" }
1669
+ ];
1670
+ NOT_A_BRAND_VARIANTS = [
1671
+ { brand: "Not/A)Brand", version: "99" },
1672
+ { brand: "Not)A_Brand", version: "99" },
1673
+ { brand: "Not/A)Brand", version: "8" },
1674
+ { brand: "Not?A_Brand", version: "24" },
1675
+ { brand: "Not/A)Brand", version: "99" }
1676
+ ];
1677
+ profileCache = /* @__PURE__ */ new Map();
1678
+ rotationSalts = /* @__PURE__ */ new Map();
1679
+ }
1680
+ });
1681
+
1682
+ // src/utils/timezone.ts
1683
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
1684
+ const dayName = DAYS[date.getDay()];
1685
+ const monthName = MONTHS[date.getMonth()];
1686
+ const day = String(date.getDate()).padStart(2, "0");
1687
+ const year = date.getFullYear();
1688
+ const hours = String(date.getHours()).padStart(2, "0");
1689
+ const minutes = String(date.getMinutes()).padStart(2, "0");
1690
+ const seconds = String(date.getSeconds()).padStart(2, "0");
1691
+ const offsetMinutes = -date.getTimezoneOffset();
1692
+ const sign = offsetMinutes >= 0 ? "+" : "-";
1693
+ const absOffset = Math.abs(offsetMinutes);
1694
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
1695
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
1696
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
1697
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
1698
+ }
1699
+ function sanitizeHeaderForFetch(val) {
1700
+ if (typeof val !== "string") return "";
1701
+ return val.replace(/[^\x00-\xFF]/g, "");
1702
+ }
1703
+ var DAYS, MONTHS;
1704
+ var init_timezone = __esm({
1705
+ "src/utils/timezone.ts"() {
1706
+ "use strict";
1707
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1708
+ MONTHS = [
1709
+ "Jan",
1710
+ "Feb",
1711
+ "Mar",
1712
+ "Apr",
1713
+ "May",
1714
+ "Jun",
1715
+ "Jul",
1716
+ "Aug",
1717
+ "Sep",
1718
+ "Oct",
1719
+ "Nov",
1720
+ "Dec"
1721
+ ];
1722
+ }
1723
+ });
1724
+
1725
+ // src/services/qwen-headers.ts
1726
+ import { v4 as uuidv4 } from "uuid";
1727
+ function getSystemAcceptLanguage(customLocale) {
1728
+ const locale = customLocale || getSystemLocale();
1729
+ const primary = locale.split("-")[0];
1730
+ if (locale.toLowerCase() === "en-us" || locale.toLowerCase() === "en") {
1731
+ return "en-US,en;q=0.9";
1732
+ }
1733
+ if (locale.toLowerCase().startsWith("pt")) {
1734
+ return "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7";
1735
+ }
1736
+ return `${locale},${primary};q=0.9,en-US;q=0.8,en;q=0.7`;
1737
+ }
1738
+ function getDefaultQwenUserAgent(major = getChromeMajor()) {
1739
+ return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${major}.0.0.0 Safari/537.36`;
1740
+ }
1741
+ function updateQwenWebVersion(version) {
1742
+ if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
1743
+ dynamicWebVersion = version.trim();
1744
+ }
1745
+ }
1746
+ function getQwenWebVersion() {
1747
+ return dynamicWebVersion || config.qwen.webVersion;
1748
+ }
1749
+ function updateQwenBxVersion(version) {
1750
+ if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
1751
+ dynamicBxVersion = version.trim();
1752
+ }
1753
+ }
1754
+ function getQwenBxVersion() {
1755
+ return dynamicBxVersion || config.auth.bxV;
1756
+ }
1757
+ function buildQwenRequestHeaders(opts) {
1758
+ const headers = {
1759
+ ...opts.extra ?? {},
1760
+ Accept: "application/json",
1761
+ "Accept-Language": opts.extra?.["Accept-Language"] || opts.acceptLanguage || getSystemAcceptLanguage(),
1762
+ "Accept-Encoding": "gzip, deflate, br, zstd",
1763
+ "Content-Type": "application/json",
1764
+ Cookie: opts.cookie,
1765
+ Origin: qwenOrigin(),
1766
+ Referer: opts.extra?.Referer ?? (opts.chatSessionId ? qwenUrl(`/c/${encodeURIComponent(opts.chatSessionId)}`) : qwenUrl("/")),
1767
+ "Sec-Fetch-Dest": "empty",
1768
+ "Sec-Fetch-Mode": "cors",
1769
+ "Sec-Fetch-Site": "same-origin",
1770
+ Connection: "keep-alive",
1771
+ "User-Agent": opts.userAgent || getDefaultQwenUserAgent(),
1772
+ "X-Request-Id": uuidv4(),
1773
+ "bx-v": opts.bxV || getQwenBxVersion(),
1774
+ source: "web",
1775
+ version: opts.version || getQwenWebVersion(),
1776
+ timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
1777
+ Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
1778
+ // Use the real browser client-hints when captured (anti-hardcoded); fall
1779
+ // back to the static fingerprint otherwise.
1780
+ "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
1781
+ "sec-ch-ua-mobile": opts.secChUaMobile || "?0",
1782
+ "sec-ch-ua-platform": opts.secChUaPlatform || '"Windows"'
1783
+ };
1784
+ const tokenMatch = opts.cookie?.match(/(?:^|;\s*)token=([^;]+)/);
1785
+ if (tokenMatch && !headers["Authorization"] && !headers["authorization"]) {
1786
+ const bearer = decodeURIComponent(tokenMatch[1].trim());
1787
+ if (bearer && bearer !== '""' && bearer !== "null") {
1788
+ headers["Authorization"] = `Bearer ${bearer}`;
1789
+ }
1790
+ }
1791
+ if (config.qwen.sendBxUa) {
1792
+ if (opts.bxUa) headers["bx-ua"] = opts.bxUa;
1793
+ if (opts.bxUmidtoken) headers["bx-umidtoken"] = opts.bxUmidtoken;
1794
+ }
1795
+ return headers;
1796
+ }
1797
+ var dynamicWebVersion, dynamicBxVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
1798
+ var init_qwen_headers = __esm({
1799
+ "src/services/qwen-headers.ts"() {
1800
+ "use strict";
1801
+ init_qwen_url();
1802
+ init_config();
1803
+ init_fingerprint();
1804
+ init_timezone();
1805
+ dynamicWebVersion = null;
1806
+ dynamicBxVersion = null;
1807
+ QWEN_WEB_VERSION = config.qwen.webVersion;
1808
+ DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
1809
+ QWEN_TIMEZONE_HEADER = (/* @__PURE__ */ new Date()).toString().split(" (")[0];
1810
+ }
1811
+ });
1812
+
1449
1813
  // src/core/logger.ts
1450
1814
  import "dotenv/config";
1451
1815
  function maskEmail(email) {
@@ -2185,232 +2549,46 @@ function clearAccountCooldown(accountId) {
2185
2549
  }
2186
2550
  }
2187
2551
  }
2188
- function markAccountHeadersReady(accountId) {
2189
- if (!accountId || accountId === "global") return;
2190
- headersReadyAccounts.add(accountId);
2191
- }
2192
- function unmarkAccountHeadersReady(accountId) {
2193
- if (!accountId) return;
2194
- headersReadyAccounts.delete(accountId);
2195
- }
2196
- function isAccountHeadersReady(accountId) {
2197
- return headersReadyAccounts.has(accountId);
2198
- }
2199
- var cooldowns, headersReadyAccounts;
2200
- var init_account_manager = __esm({
2201
- "src/core/account-manager.ts"() {
2202
- "use strict";
2203
- init_accounts();
2204
- init_account_priority();
2205
- init_logger();
2206
- cooldowns = /* @__PURE__ */ new Map();
2207
- headersReadyAccounts = /* @__PURE__ */ new Set();
2208
- }
2209
- });
2210
-
2211
- // src/services/fingerprint.ts
2212
- function mulberry32(seed) {
2213
- return () => {
2214
- seed |= 0;
2215
- seed = seed + 1831565813 | 0;
2216
- let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
2217
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2218
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
2219
- };
2220
- }
2221
- function seedFromString(value) {
2222
- let hash = 0;
2223
- for (let i = 0; i < value.length; i++) {
2224
- hash = (hash << 5) - hash + value.charCodeAt(i) | 0;
2225
- }
2226
- return hash >>> 0;
2227
- }
2228
- function pick(rng, values) {
2229
- return values[Math.floor(rng() * values.length)];
2230
- }
2231
- function randInt(rng, min, max) {
2232
- return Math.floor(rng() * (max - min + 1)) + min;
2233
- }
2234
- function updateChromeMajor(major) {
2235
- if (typeof major === "number" && major >= 100) {
2236
- dynamicChromeMajor = major;
2237
- }
2238
- }
2239
- function getChromeMajor() {
2240
- return dynamicChromeMajor;
2241
- }
2242
- function getSystemLocale() {
2243
- return process.env.QWEN_LOCALE || (typeof Intl !== "undefined" && Intl.DateTimeFormat ? Intl.DateTimeFormat().resolvedOptions().locale : "") || "pt-BR";
2244
- }
2245
- function getLanguageProfiles(locale = getSystemLocale()) {
2246
- const primary = locale.split("-")[0];
2247
- if (primary === "en") {
2248
- return [
2249
- ["en-US", "en"],
2250
- ["en-US", "en", "es"],
2251
- ["en", "en-US"],
2252
- ["en-US", "en;q=0.9"]
2253
- ];
2552
+ function getAccountCooldownInfo(accountId) {
2553
+ const entry = cooldowns.get(accountId);
2554
+ if (!entry) return null;
2555
+ const remaining = entry.until - Date.now();
2556
+ if (remaining <= 0) {
2557
+ cooldowns.delete(accountId);
2558
+ if (accountId !== "global") {
2559
+ try {
2560
+ updateAccountCooldown(accountId, 0, null);
2561
+ } catch (err) {
2562
+ console.error(
2563
+ `\u274C [AccountManager] Failed to clear expired cooldown in DB:`,
2564
+ err.message
2565
+ );
2566
+ }
2567
+ }
2568
+ return null;
2254
2569
  }
2255
- return [
2256
- [locale, primary, "en-US", "en"],
2257
- [locale, primary, "en-US", "en", "es"],
2258
- [locale, "en-US", "en", primary],
2259
- [locale, primary, "en"],
2260
- [locale, `${primary};q=0.9`, "en-US;q=0.8", "en;q=0.7"]
2261
- ];
2570
+ return { onCooldown: true, remainingMs: remaining, reason: entry.reason };
2262
2571
  }
2263
- function getFingerprintProfile(accountId) {
2264
- const cached = profileCache.get(accountId);
2265
- if (cached) return cached;
2266
- const salt = rotationSalts.get(accountId) ?? 0;
2267
- const seed = seedFromString(salt === 0 ? accountId : `${accountId}#r${salt}`);
2268
- const rng = mulberry32(seed);
2269
- const viewport = pick(rng, VIEWPORTS);
2270
- const webgl = pick(rng, WEBGL_PROFILES);
2271
- const languages = [...pick(rng, getLanguageProfiles())];
2272
- const hardwareConcurrency = pick(rng, HARDWARE_CONCURRENCIES);
2273
- const deviceMemory = pick(rng, DEVICE_MEMORIES);
2274
- const platformInfo = pick(rng, PLATFORM_VERSIONS);
2275
- const notABrand = pick(rng, NOT_A_BRAND_VARIANTS);
2276
- const major = dynamicChromeMajor;
2277
- const build = randInt(rng, 7300, 7600);
2278
- const patch = randInt(rng, 0, 160);
2279
- const chromeVersion = `${major}.0.${build}.${patch}`;
2280
- const brands = [
2281
- { brand: notABrand.brand, version: notABrand.version },
2282
- { brand: "Google Chrome", version: String(major) },
2283
- { brand: "Chromium", version: String(major) }
2284
- ];
2285
- const fullVersionList = [
2286
- { brand: notABrand.brand, version: chromeVersion },
2287
- { brand: "Google Chrome", version: chromeVersion },
2288
- { brand: "Chromium", version: chromeVersion }
2289
- ];
2290
- const secChUa = brands.map((brand) => `"${brand.brand}";v="${brand.version}"`).join(", ");
2291
- const userAgent = `Mozilla/5.0 (Windows NT ${platformInfo.major}.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
2292
- const profile = {
2293
- accountId,
2294
- seed,
2295
- userAgent,
2296
- appVersion: userAgent.replace("Mozilla/", ""),
2297
- chromeMajor: CHROME_MAJOR,
2298
- chromeVersion,
2299
- brands,
2300
- fullVersionList,
2301
- secChUa,
2302
- platform: "Win32",
2303
- platformVersion: platformInfo.platformVersion,
2304
- architecture: "x86",
2305
- bitness: "64",
2306
- languages,
2307
- locale: languages[0].split(";")[0],
2308
- timezoneId: "America/Sao_Paulo",
2309
- viewport,
2310
- hardwareConcurrency,
2311
- deviceMemory,
2312
- webglVendor: webgl.vendor,
2313
- webglRenderer: webgl.renderer,
2314
- colorDepth: 24,
2315
- pixelDepth: 24,
2316
- canvasNoiseSeed: randInt(rng, 1, 2147483647),
2317
- audioNoiseSeed: randInt(rng, 1, 2147483647),
2318
- webglNoiseSeed: randInt(rng, 1, 2147483647),
2319
- outerWidthOffset: randInt(rng, 0, 20),
2320
- outerHeightOffset: randInt(rng, 75, 95)
2321
- };
2322
- profileCache.set(accountId, profile);
2323
- return profile;
2572
+ function markAccountHeadersReady(accountId) {
2573
+ if (!accountId || accountId === "global") return;
2574
+ headersReadyAccounts.add(accountId);
2324
2575
  }
2325
- function clearFingerprintCache(accountId) {
2326
- if (accountId) profileCache.delete(accountId);
2327
- else profileCache.clear();
2576
+ function unmarkAccountHeadersReady(accountId) {
2577
+ if (!accountId) return;
2578
+ headersReadyAccounts.delete(accountId);
2328
2579
  }
2329
- var dynamicChromeMajor, CHROME_MAJOR, VIEWPORTS, WEBGL_PROFILES, HARDWARE_CONCURRENCIES, DEVICE_MEMORIES, PLATFORM_VERSIONS, NOT_A_BRAND_VARIANTS, profileCache, rotationSalts;
2330
- var init_fingerprint = __esm({
2331
- "src/services/fingerprint.ts"() {
2580
+ function isAccountHeadersReady(accountId) {
2581
+ return headersReadyAccounts.has(accountId);
2582
+ }
2583
+ var cooldowns, headersReadyAccounts;
2584
+ var init_account_manager = __esm({
2585
+ "src/core/account-manager.ts"() {
2332
2586
  "use strict";
2333
- dynamicChromeMajor = 151;
2334
- CHROME_MAJOR = 151;
2335
- VIEWPORTS = [
2336
- { width: 1366, height: 768 },
2337
- { width: 1440, height: 900 },
2338
- { width: 1536, height: 864 },
2339
- { width: 1600, height: 900 },
2340
- { width: 1680, height: 1050 },
2341
- { width: 1920, height: 1080 },
2342
- { width: 1920, height: 1200 },
2343
- { width: 1280, height: 720 },
2344
- { width: 1280, height: 800 },
2345
- { width: 1536, height: 864 }
2346
- ];
2347
- WEBGL_PROFILES = [
2348
- {
2349
- vendor: "Google Inc. (Intel)",
2350
- renderer: "ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2351
- },
2352
- {
2353
- vendor: "Google Inc. (Intel)",
2354
- renderer: "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)"
2355
- },
2356
- {
2357
- vendor: "Google Inc. (Intel)",
2358
- renderer: "ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2359
- },
2360
- {
2361
- vendor: "Google Inc. (NVIDIA)",
2362
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2363
- },
2364
- {
2365
- vendor: "Google Inc. (NVIDIA)",
2366
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2367
- },
2368
- {
2369
- vendor: "Google Inc. (NVIDIA)",
2370
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2371
- },
2372
- {
2373
- vendor: "Google Inc. (NVIDIA)",
2374
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2375
- },
2376
- {
2377
- vendor: "Google Inc. (NVIDIA)",
2378
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2379
- },
2380
- {
2381
- vendor: "Google Inc. (NVIDIA)",
2382
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)"
2383
- },
2384
- {
2385
- vendor: "Google Inc. (AMD)",
2386
- renderer: "ANGLE (AMD, AMD Radeon RX 6600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2387
- },
2388
- {
2389
- vendor: "Google Inc. (AMD)",
2390
- renderer: "ANGLE (AMD, AMD Radeon RX 7600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2391
- },
2392
- {
2393
- vendor: "Google Inc. (AMD)",
2394
- renderer: "ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2395
- }
2396
- ];
2397
- HARDWARE_CONCURRENCIES = [4, 6, 8, 8, 8, 12, 16, 16, 24, 32];
2398
- DEVICE_MEMORIES = [4, 8, 8, 8, 16, 16, 32];
2399
- PLATFORM_VERSIONS = [
2400
- { platform: "Windows", platformVersion: "14.0.0", major: "10" },
2401
- { platform: "Windows", platformVersion: "15.0.0", major: "11" },
2402
- { platform: "Windows", platformVersion: "14.0.0", major: "10" },
2403
- { platform: "Windows", platformVersion: "15.0.0", major: "11" }
2404
- ];
2405
- NOT_A_BRAND_VARIANTS = [
2406
- { brand: "Not/A)Brand", version: "99" },
2407
- { brand: "Not)A_Brand", version: "99" },
2408
- { brand: "Not/A)Brand", version: "8" },
2409
- { brand: "Not?A_Brand", version: "24" },
2410
- { brand: "Not/A)Brand", version: "99" }
2411
- ];
2412
- profileCache = /* @__PURE__ */ new Map();
2413
- rotationSalts = /* @__PURE__ */ new Map();
2587
+ init_accounts();
2588
+ init_account_priority();
2589
+ init_logger();
2590
+ cooldowns = /* @__PURE__ */ new Map();
2591
+ headersReadyAccounts = /* @__PURE__ */ new Set();
2414
2592
  }
2415
2593
  });
2416
2594
 
@@ -2552,10 +2730,298 @@ function extractBaxiaChallengeUrl(body, baseUrl) {
2552
2730
  } catch {
2553
2731
  continue;
2554
2732
  }
2555
- if (url.protocol !== base.protocol || url.host !== base.host) continue;
2556
- return url.toString();
2733
+ if (url.protocol !== base.protocol || url.host !== base.host) continue;
2734
+ return url.toString();
2735
+ }
2736
+ return null;
2737
+ }
2738
+ async function findVisibleLocatorWithin(page, selectors, timeoutMs) {
2739
+ const deadline = Date.now() + Math.max(0, timeoutMs);
2740
+ while (true) {
2741
+ const found = await findVisibleLocator(page, selectors);
2742
+ if (found) return found;
2743
+ if (Date.now() >= deadline) return null;
2744
+ await sleep(Math.min(150, Math.max(1, deadline - Date.now())));
2745
+ }
2746
+ }
2747
+ async function waitForHidden(locator, timeoutMs) {
2748
+ const deadline = Date.now() + Math.max(0, timeoutMs);
2749
+ while (true) {
2750
+ if (!await isVisible(locator)) return true;
2751
+ if (Date.now() >= deadline) return false;
2752
+ await sleep(Math.min(100, Math.max(1, deadline - Date.now())));
2753
+ }
2754
+ }
2755
+ async function computeAliyunPuzzleOffset(page) {
2756
+ return page.evaluate(() => {
2757
+ const background = document.querySelector(
2758
+ "#aliyunCaptcha-img"
2759
+ );
2760
+ const piece = document.querySelector(
2761
+ "#aliyunCaptcha-puzzle"
2762
+ );
2763
+ if (!background || !piece) {
2764
+ return Promise.resolve({ ok: false, reason: "missing puzzle images" });
2765
+ }
2766
+ const compute = () => {
2767
+ try {
2768
+ const width = background.naturalWidth;
2769
+ const height = background.naturalHeight;
2770
+ const pieceWidth = piece.naturalWidth;
2771
+ const pieceHeight = piece.naturalHeight;
2772
+ if (!width || !height || !pieceWidth || !pieceHeight) {
2773
+ return { ok: false, reason: "image dimensions unavailable" };
2774
+ }
2775
+ const backgroundCanvas = document.createElement("canvas");
2776
+ backgroundCanvas.id = "captcha-solver-canvas";
2777
+ backgroundCanvas.width = width;
2778
+ backgroundCanvas.height = height;
2779
+ const backgroundContext = backgroundCanvas.getContext("2d");
2780
+ if (!backgroundContext) {
2781
+ return { ok: false, reason: "no background context" };
2782
+ }
2783
+ const pieceCanvas = document.createElement("canvas");
2784
+ pieceCanvas.id = "captcha-solver-canvas";
2785
+ pieceCanvas.width = pieceWidth;
2786
+ pieceCanvas.height = pieceHeight;
2787
+ const pieceContext = pieceCanvas.getContext("2d");
2788
+ if (!pieceContext) {
2789
+ return { ok: false, reason: "no piece context" };
2790
+ }
2791
+ try {
2792
+ backgroundContext.drawImage(background, 0, 0);
2793
+ } catch {
2794
+ return { ok: false, reason: "background canvas tainted" };
2795
+ }
2796
+ try {
2797
+ pieceContext.drawImage(piece, 0, 0);
2798
+ } catch {
2799
+ return { ok: false, reason: "puzzle canvas tainted" };
2800
+ }
2801
+ const backgroundData = backgroundContext.getImageData(
2802
+ 0,
2803
+ 0,
2804
+ width,
2805
+ height
2806
+ ).data;
2807
+ const pieceData = pieceContext.getImageData(
2808
+ 0,
2809
+ 0,
2810
+ pieceWidth,
2811
+ pieceHeight
2812
+ ).data;
2813
+ let pieceMinX = pieceWidth;
2814
+ let pieceMaxX = -1;
2815
+ let pieceMinY = pieceHeight;
2816
+ let pieceMaxY = -1;
2817
+ for (let y = 0; y < pieceHeight; y++) {
2818
+ for (let x = 0; x < pieceWidth; x++) {
2819
+ if (pieceData[(y * pieceWidth + x) * 4 + 3] > 100) {
2820
+ if (x < pieceMinX) pieceMinX = x;
2821
+ if (x > pieceMaxX) pieceMaxX = x;
2822
+ if (y < pieceMinY) pieceMinY = y;
2823
+ if (y > pieceMaxY) pieceMaxY = y;
2824
+ }
2825
+ }
2826
+ }
2827
+ const puzzleWidth = pieceMaxX - pieceMinX + 1;
2828
+ if (puzzleWidth <= 0) {
2829
+ return { ok: false, reason: "empty puzzle piece" };
2830
+ }
2831
+ const yStart = Math.max(1, Math.min(height - 2, pieceMinY + 5));
2832
+ const yEnd = Math.max(
2833
+ yStart,
2834
+ Math.min(height - 2, pieceMaxY - 5)
2835
+ );
2836
+ const columnGradients = new Float32Array(width);
2837
+ for (let x = 1; x < width - 1; x++) {
2838
+ let gradient = 0;
2839
+ for (let y = yStart; y <= yEnd; y++) {
2840
+ const left = (y * width + x - 1) * 4;
2841
+ const right = (y * width + x + 1) * 4;
2842
+ gradient += Math.abs(backgroundData[right] - backgroundData[left]) + Math.abs(backgroundData[right + 1] - backgroundData[left + 1]) + Math.abs(backgroundData[right + 2] - backgroundData[left + 2]);
2843
+ }
2844
+ columnGradients[x] = gradient;
2845
+ }
2846
+ let bestX = 0;
2847
+ let bestScore = 0;
2848
+ const minCandidateX = 30;
2849
+ const maxCandidateX = width - puzzleWidth - 20;
2850
+ for (let candidateX = minCandidateX; candidateX <= maxCandidateX; candidateX++) {
2851
+ let leftMax = 0;
2852
+ for (let dx = -2; dx <= 2; dx++) {
2853
+ const value = columnGradients[candidateX + pieceMinX + dx] || 0;
2854
+ if (value > leftMax) leftMax = value;
2855
+ }
2856
+ let rightMax = 0;
2857
+ for (let dx = -3; dx <= 1; dx++) {
2858
+ const value = columnGradients[candidateX + pieceMaxX + dx] || 0;
2859
+ if (value > rightMax) rightMax = value;
2860
+ }
2861
+ const score = Math.sqrt(leftMax * rightMax);
2862
+ if (score > bestScore) {
2863
+ bestScore = score;
2864
+ bestX = candidateX;
2865
+ }
2866
+ }
2867
+ if (bestScore <= 0) {
2868
+ return { ok: false, reason: "no matching hole edge" };
2869
+ }
2870
+ return {
2871
+ ok: true,
2872
+ offsetX: bestX - pieceMinX,
2873
+ naturalWidth: width
2874
+ };
2875
+ } catch (error) {
2876
+ return {
2877
+ ok: false,
2878
+ reason: error instanceof Error ? error.message : String(error)
2879
+ };
2880
+ }
2881
+ };
2882
+ return new Promise((resolve) => {
2883
+ if (background.complete && piece.complete) {
2884
+ resolve(compute());
2885
+ return;
2886
+ }
2887
+ let settled = false;
2888
+ const finish = () => {
2889
+ if (settled) return;
2890
+ settled = true;
2891
+ resolve(compute());
2892
+ };
2893
+ const fail = () => {
2894
+ if (settled) return;
2895
+ settled = true;
2896
+ resolve({ ok: false, reason: "puzzle image load error" });
2897
+ };
2898
+ background.addEventListener("load", finish, { once: true });
2899
+ piece.addEventListener("load", finish, { once: true });
2900
+ background.addEventListener("error", fail, { once: true });
2901
+ piece.addEventListener("error", fail, { once: true });
2902
+ setTimeout(() => {
2903
+ if (settled) return;
2904
+ settled = true;
2905
+ resolve({ ok: false, reason: "puzzle image load timeout" });
2906
+ }, 2e3);
2907
+ });
2908
+ });
2909
+ }
2910
+ async function solveAliyunWafCaptcha(page, options = {}) {
2911
+ const waitForMs = Math.max(0, options.waitForMs ?? 0);
2912
+ const maxAttempts = Math.max(
2913
+ 1,
2914
+ Math.min(5, options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS)
2915
+ );
2916
+ const retryDelayMs = Math.max(
2917
+ 0,
2918
+ options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
2919
+ );
2920
+ const settleMs = Math.max(0, options.settleMs ?? DEFAULT_SETTLE_MS);
2921
+ const sliderTimeoutMs = Math.max(
2922
+ 500,
2923
+ options.sliderTimeoutMs ?? DEFAULT_SLIDER_TIMEOUT_MS
2924
+ );
2925
+ const detected = await findVisibleLocatorWithin(
2926
+ page,
2927
+ ALIYUN_WAF_BLOCK_SELECTORS,
2928
+ waitForMs
2929
+ );
2930
+ if (!detected) return false;
2931
+ return withCaptchaMouseLock(async () => {
2932
+ const block = detected.locator;
2933
+ if (!await isVisible(block)) return false;
2934
+ logBaxiaCaptcha("aliyun_detected", { selector: detected.selector });
2935
+ let lastReason = "unknown";
2936
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2937
+ try {
2938
+ const offset = await computeAliyunPuzzleOffset(page);
2939
+ if (!offset.ok || typeof offset.offsetX !== "number" || typeof offset.naturalWidth !== "number" || offset.naturalWidth <= 0) {
2940
+ lastReason = offset.reason || "offset_unavailable";
2941
+ logBaxiaCaptcha("aliyun_offset_failed", {
2942
+ attempt,
2943
+ reason: lastReason
2944
+ });
2945
+ } else {
2946
+ const slider = page.locator(ALIYUN_PUZZLE_SLIDER_SELECTOR).first();
2947
+ const track = page.locator(ALIYUN_PUZZLE_TRACK_SELECTOR).first();
2948
+ await slider.waitFor({ state: "visible", timeout: sliderTimeoutMs });
2949
+ const [sliderBox, trackBox] = await Promise.all([
2950
+ slider.boundingBox(),
2951
+ track.boundingBox()
2952
+ ]);
2953
+ if (!sliderBox || !trackBox || trackBox.width <= 0) {
2954
+ lastReason = "bounds_unavailable";
2955
+ logBaxiaCaptcha("aliyun_offset_failed", {
2956
+ attempt,
2957
+ reason: lastReason
2958
+ });
2959
+ } else {
2960
+ const scale = trackBox.width / offset.naturalWidth;
2961
+ const dragDistance = Math.round(offset.offsetX * scale);
2962
+ logBaxiaCaptcha("aliyun_geometry", {
2963
+ attempt,
2964
+ offset: offset.offsetX,
2965
+ scale: Number(scale.toFixed(2)),
2966
+ distance: dragDistance
2967
+ });
2968
+ if (dragDistance <= 0) {
2969
+ lastReason = "non_positive_distance";
2970
+ } else {
2971
+ const startX = sliderBox.x + sliderBox.width / 2;
2972
+ const startY = sliderBox.y + sliderBox.height / 2;
2973
+ await humanDrag(
2974
+ page,
2975
+ startX,
2976
+ startY,
2977
+ startX + dragDistance,
2978
+ startY
2979
+ );
2980
+ if (await waitForHidden(block, settleMs)) {
2981
+ logBaxiaCaptcha(
2982
+ "aliyun_solve_succeeded",
2983
+ { attempt, distance: dragDistance },
2984
+ true
2985
+ );
2986
+ return true;
2987
+ }
2988
+ lastReason = "not_solved";
2989
+ }
2990
+ }
2991
+ }
2992
+ } catch (error) {
2993
+ lastReason = error instanceof Error ? error.name : "UnknownError";
2994
+ logBaxiaCaptcha("aliyun_offset_failed", {
2995
+ attempt,
2996
+ error: lastReason,
2997
+ detail: sanitizeCaptchaErrorDetail(error)
2998
+ });
2999
+ }
3000
+ if (attempt < maxAttempts && retryDelayMs > 0) {
3001
+ await sleep(retryDelayMs);
3002
+ }
3003
+ }
3004
+ logBaxiaCaptcha(
3005
+ "aliyun_solve_failed",
3006
+ { attempts: maxAttempts, reason: lastReason },
3007
+ true
3008
+ );
3009
+ return false;
3010
+ });
3011
+ }
3012
+ async function solveVisibleCaptcha(page, options = {}) {
3013
+ if (typeof page.isClosed === "function" && page.isClosed()) return false;
3014
+ const totalWaitMs = Math.max(0, options.waitForMs ?? 0);
3015
+ const aliyunWaitMs = Math.min(totalWaitMs, ALIYUN_PROBE_MAX_WAIT_MS);
3016
+ const startedAt = Date.now();
3017
+ if (await solveAliyunWafCaptcha(page, { ...options, waitForMs: aliyunWaitMs })) {
3018
+ return true;
2557
3019
  }
2558
- return null;
3020
+ const remainingWaitMs = Math.max(
3021
+ 0,
3022
+ totalWaitMs - (Date.now() - startedAt)
3023
+ );
3024
+ return solveBaxiaCaptcha(page, { ...options, waitForMs: remainingWaitMs });
2559
3025
  }
2560
3026
  async function isVisible(locator) {
2561
3027
  return locator.isVisible().catch(() => false);
@@ -2826,7 +3292,7 @@ function startBaxiaCaptchaWatcher(page, timeoutMs, options = {}) {
2826
3292
  promise
2827
3293
  };
2828
3294
  }
2829
- 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;
3295
+ 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;
2830
3296
  var init_captcha_solver = __esm({
2831
3297
  "src/services/captcha-solver.ts"() {
2832
3298
  "use strict";
@@ -2852,14 +3318,20 @@ var init_captcha_solver = __esm({
2852
3318
  solve_failed: "\u274C",
2853
3319
  recovery_succeeded: "\u2705",
2854
3320
  recovery_not_solved: "\u26A0\uFE0F",
2855
- recovery_failed: "\u274C"
3321
+ recovery_failed: "\u274C",
3322
+ aliyun_detected: "\u{1F9E9}",
3323
+ aliyun_offset_failed: "\u{1F4D0}",
3324
+ aliyun_geometry: "\u{1F4D0}",
3325
+ aliyun_solve_succeeded: "\u2705",
3326
+ aliyun_solve_failed: "\u274C"
2856
3327
  };
2857
3328
  captchaDebugEnabled = process.env.CAPTCHA_DEBUG === "true";
2858
3329
  BAXIA_IFRAME_SELECTORS = [
2859
3330
  `${BAXIA_DIALOG_SELECTOR} ${BAXIA_CONTENT_SELECTOR} iframe`,
2860
3331
  `${BAXIA_CONTENT_SELECTOR} iframe`,
2861
3332
  "iframe#baxia-dialog-content",
2862
- 'iframe[src*="_____tmd_____/punish"]'
3333
+ 'iframe[src*="_____tmd_____/punish"]',
3334
+ 'iframe[src*="nocaptcha"]'
2863
3335
  ];
2864
3336
  BAXIA_DOCUMENT_SELECTORS = [
2865
3337
  "#nocaptcha",
@@ -2867,41 +3339,71 @@ var init_captcha_solver = __esm({
2867
3339
  "#baxia-punish"
2868
3340
  ];
2869
3341
  BAXIA_IFRAME_SELECTOR = BAXIA_IFRAME_SELECTORS.join(", ");
2870
- BAXIA_SLIDER_SELECTOR = "#nc_1_n1z, .nc_1_n1z, .btn_slide, .nc_wrapper .btn_slide, ._nc .btn_slide, .nc-container .btn_slide";
2871
- BAXIA_TRACK_SELECTOR = "#nc_1_n1t, .nc_scale, .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale";
3342
+ 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';
3343
+ BAXIA_TRACK_SELECTOR = '#nc_1_n1t, .nc_scale, span[id*="_n1t"], .nc_wrapper .nc_scale, ._nc .nc_scale, .nc-container .nc_scale';
2872
3344
  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';
2873
3345
  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"]';
2874
3346
  DEFAULT_MAX_ATTEMPTS = 3;
2875
3347
  DEFAULT_RETRY_DELAY_MS = 1e3;
2876
3348
  DEFAULT_SETTLE_MS = 2e3;
2877
3349
  DEFAULT_SLIDER_TIMEOUT_MS = 8e3;
3350
+ ALIYUN_WAF_CAPTCHA_SELECTOR = "#waf_nc_block";
3351
+ ALIYUN_WAF_CAPTCHA_WIDGET = "#aliyunCaptcha-window-embed";
3352
+ ALIYUN_WAF_BLOCK_SELECTORS = [
3353
+ ALIYUN_WAF_CAPTCHA_SELECTOR,
3354
+ ALIYUN_WAF_CAPTCHA_WIDGET
3355
+ ];
3356
+ ALIYUN_PUZZLE_SLIDER_SELECTOR = "#aliyunCaptcha-sliding-slider";
3357
+ ALIYUN_PUZZLE_TRACK_SELECTOR = "#aliyunCaptcha-sliding-body";
3358
+ ALIYUN_PROBE_MAX_WAIT_MS = 2e3;
2878
3359
  captchaMouseLock = Promise.resolve();
2879
3360
  }
2880
3361
  });
2881
3362
 
2882
- // src/services/qwen-url.ts
2883
- function qwenUrl(path6 = "") {
2884
- const normalizedPath = path6.replace(/^\/+/, "");
2885
- return normalizedPath ? `${QWEN_BASE_URL}/${normalizedPath}` : QWEN_BASE_URL;
2886
- }
2887
- function qwenOrigin() {
2888
- return QWEN_ORIGIN;
2889
- }
2890
- var QWEN_BASE_URL, QWEN_ORIGIN;
2891
- var init_qwen_url = __esm({
2892
- "src/services/qwen-url.ts"() {
2893
- "use strict";
2894
- init_config();
2895
- QWEN_BASE_URL = config.qwen.baseUrl.trim().replace(/\/+$/, "");
2896
- QWEN_ORIGIN = new URL(QWEN_BASE_URL).origin;
2897
- }
2898
- });
2899
-
2900
3363
  // src/core/waf-isolation.ts
2901
3364
  function setWafContextResetListener(fn) {
2902
3365
  contextResetListener = fn;
2903
3366
  }
2904
- var contextResetListener;
3367
+ function getState(accountId) {
3368
+ let state = states.get(accountId);
3369
+ if (!state) {
3370
+ state = { consecutiveHardBlocks: 0, lastBlockAt: 0 };
3371
+ states.set(accountId, state);
3372
+ }
3373
+ return state;
3374
+ }
3375
+ function recordWafHardBlock(accountId) {
3376
+ const state = getState(accountId);
3377
+ state.consecutiveHardBlocks += 1;
3378
+ state.lastBlockAt = Date.now();
3379
+ const base = config.captcha.accountCooldownMs;
3380
+ const cap = config.captcha.hardBlockMaxCooldownMs;
3381
+ const exponent = Math.min(
3382
+ state.consecutiveHardBlocks - 1,
3383
+ MAX_CONSECUTIVE_FOR_ESCALATION
3384
+ );
3385
+ const cooldownMs = Math.min(cap, base * Math.pow(ESCALATION_FACTOR, exponent));
3386
+ if (cooldownMs > 0) {
3387
+ markAccountRateLimited(accountId, cooldownMs, "WafChallenge");
3388
+ }
3389
+ rotateFingerprintSeed(accountId);
3390
+ logger.warn(
3391
+ `[WafIsolation] Hard WAF block on ${accountId}: fingerprint rotated (streak ${state.consecutiveHardBlocks}), quarantined ${Math.round(cooldownMs / 1e3)}s`
3392
+ );
3393
+ if (contextResetListener) {
3394
+ void Promise.resolve(contextResetListener(accountId)).catch((error) => {
3395
+ logger.warn(
3396
+ `[WafIsolation] Context reset listener failed for ${accountId}: ${error instanceof Error ? error.message : String(error)}`
3397
+ );
3398
+ });
3399
+ }
3400
+ return {
3401
+ cooldownMs,
3402
+ fingerprintRotated: true,
3403
+ escalated: state.consecutiveHardBlocks > 1
3404
+ };
3405
+ }
3406
+ var ESCALATION_FACTOR, MAX_CONSECUTIVE_FOR_ESCALATION, states, contextResetListener;
2905
3407
  var init_waf_isolation = __esm({
2906
3408
  "src/core/waf-isolation.ts"() {
2907
3409
  "use strict";
@@ -2909,132 +3411,13 @@ var init_waf_isolation = __esm({
2909
3411
  init_logger();
2910
3412
  init_account_manager();
2911
3413
  init_fingerprint();
3414
+ ESCALATION_FACTOR = 2;
3415
+ MAX_CONSECUTIVE_FOR_ESCALATION = 4;
3416
+ states = /* @__PURE__ */ new Map();
2912
3417
  contextResetListener = null;
2913
3418
  }
2914
3419
  });
2915
3420
 
2916
- // src/utils/timezone.ts
2917
- function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2918
- const dayName = DAYS[date.getDay()];
2919
- const monthName = MONTHS[date.getMonth()];
2920
- const day = String(date.getDate()).padStart(2, "0");
2921
- const year = date.getFullYear();
2922
- const hours = String(date.getHours()).padStart(2, "0");
2923
- const minutes = String(date.getMinutes()).padStart(2, "0");
2924
- const seconds = String(date.getSeconds()).padStart(2, "0");
2925
- const offsetMinutes = -date.getTimezoneOffset();
2926
- const sign = offsetMinutes >= 0 ? "+" : "-";
2927
- const absOffset = Math.abs(offsetMinutes);
2928
- const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2929
- const offsetMins = String(absOffset % 60).padStart(2, "0");
2930
- const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2931
- return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2932
- }
2933
- function sanitizeHeaderForFetch(val) {
2934
- if (typeof val !== "string") return "";
2935
- return val.replace(/[^\x00-\xFF]/g, "");
2936
- }
2937
- var DAYS, MONTHS;
2938
- var init_timezone = __esm({
2939
- "src/utils/timezone.ts"() {
2940
- "use strict";
2941
- DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2942
- MONTHS = [
2943
- "Jan",
2944
- "Feb",
2945
- "Mar",
2946
- "Apr",
2947
- "May",
2948
- "Jun",
2949
- "Jul",
2950
- "Aug",
2951
- "Sep",
2952
- "Oct",
2953
- "Nov",
2954
- "Dec"
2955
- ];
2956
- }
2957
- });
2958
-
2959
- // src/services/qwen-headers.ts
2960
- import { v4 as uuidv4 } from "uuid";
2961
- function getSystemAcceptLanguage(customLocale) {
2962
- const locale = customLocale || getSystemLocale();
2963
- const primary = locale.split("-")[0];
2964
- if (locale.toLowerCase() === "en-us" || locale.toLowerCase() === "en") {
2965
- return "en-US,en;q=0.9";
2966
- }
2967
- if (locale.toLowerCase().startsWith("pt")) {
2968
- return "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7";
2969
- }
2970
- return `${locale},${primary};q=0.9,en-US;q=0.8,en;q=0.7`;
2971
- }
2972
- function getDefaultQwenUserAgent(major = getChromeMajor()) {
2973
- return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${major}.0.0.0 Safari/537.36`;
2974
- }
2975
- function updateQwenWebVersion(version) {
2976
- if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
2977
- dynamicWebVersion = version.trim();
2978
- }
2979
- }
2980
- function getQwenWebVersion() {
2981
- return dynamicWebVersion || config.qwen.webVersion;
2982
- }
2983
- function buildQwenRequestHeaders(opts) {
2984
- const headers = {
2985
- ...opts.extra ?? {},
2986
- Accept: "application/json",
2987
- "Accept-Language": opts.extra?.["Accept-Language"] || opts.acceptLanguage || getSystemAcceptLanguage(),
2988
- "Accept-Encoding": "gzip, deflate, br, zstd",
2989
- "Content-Type": "application/json",
2990
- Cookie: opts.cookie,
2991
- Origin: qwenOrigin(),
2992
- Referer: opts.extra?.Referer ?? (opts.chatSessionId ? qwenUrl(`/c/${encodeURIComponent(opts.chatSessionId)}`) : qwenUrl("/")),
2993
- "Sec-Fetch-Dest": "empty",
2994
- "Sec-Fetch-Mode": "cors",
2995
- "Sec-Fetch-Site": "same-origin",
2996
- Connection: "keep-alive",
2997
- "User-Agent": opts.userAgent || getDefaultQwenUserAgent(),
2998
- "X-Request-Id": uuidv4(),
2999
- "bx-v": opts.bxV || "2.5.37",
3000
- source: "web",
3001
- version: opts.version || getQwenWebVersion(),
3002
- timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
3003
- Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
3004
- // Use the real browser client-hints when captured (anti-hardcoded); fall
3005
- // back to the static fingerprint otherwise.
3006
- "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
3007
- "sec-ch-ua-mobile": opts.secChUaMobile || "?0",
3008
- "sec-ch-ua-platform": opts.secChUaPlatform || '"Windows"'
3009
- };
3010
- const tokenMatch = opts.cookie?.match(/(?:^|;\s*)token=([^;]+)/);
3011
- if (tokenMatch && !headers["Authorization"] && !headers["authorization"]) {
3012
- const bearer = decodeURIComponent(tokenMatch[1].trim());
3013
- if (bearer && bearer !== '""' && bearer !== "null") {
3014
- headers["Authorization"] = `Bearer ${bearer}`;
3015
- }
3016
- }
3017
- if (config.qwen.sendBxUa) {
3018
- if (opts.bxUa) headers["bx-ua"] = opts.bxUa;
3019
- if (opts.bxUmidtoken) headers["bx-umidtoken"] = opts.bxUmidtoken;
3020
- }
3021
- return headers;
3022
- }
3023
- var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
3024
- var init_qwen_headers = __esm({
3025
- "src/services/qwen-headers.ts"() {
3026
- "use strict";
3027
- init_qwen_url();
3028
- init_config();
3029
- init_fingerprint();
3030
- init_timezone();
3031
- dynamicWebVersion = null;
3032
- QWEN_WEB_VERSION = config.qwen.webVersion;
3033
- DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
3034
- QWEN_TIMEZONE_HEADER = (/* @__PURE__ */ new Date()).toString().split(" (")[0];
3035
- }
3036
- });
3037
-
3038
3421
  // src/utils/jwt.ts
3039
3422
  var jwt_exports = {};
3040
3423
  __export(jwt_exports, {
@@ -3642,11 +4025,12 @@ async function getOrLaunchSharedBrowser(browserType = "chromium", headless = con
3642
4025
  async function clearVisibleChallenge(page) {
3643
4026
  if (page.isClosed()) return;
3644
4027
  if (config.captcha.enabled) {
3645
- await solveBaxiaCaptcha(page, {
4028
+ await solveVisibleCaptcha(page, {
3646
4029
  waitForMs: 0,
3647
4030
  maxAttempts: config.captcha.maxAttempts,
3648
4031
  retryDelayMs: config.captcha.retryDelayMs,
3649
- settleMs: config.captcha.settleMs
4032
+ settleMs: config.captcha.settleMs,
4033
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
3650
4034
  }).catch(() => false);
3651
4035
  }
3652
4036
  try {
@@ -4259,6 +4643,7 @@ async function getBasicHeaders(accountId) {
4259
4643
  mobile = data.mobile ? "?1" : "?0";
4260
4644
  }
4261
4645
  let bundleVersion = null;
4646
+ let baxiaVersion = null;
4262
4647
  try {
4263
4648
  const el = document.querySelector('link[href*="qwen-chat-fe"], script[src*="qwen-chat-fe"], img[src*="qwen-chat-fe"]');
4264
4649
  const src = el ? el.getAttribute("href") || el.getAttribute("src") || "" : "";
@@ -4266,7 +4651,14 @@ async function getBasicHeaders(accountId) {
4266
4651
  if (match) bundleVersion = match[1];
4267
4652
  } catch {
4268
4653
  }
4269
- return { ua, secChUa, platform, mobile, bundleVersion };
4654
+ try {
4655
+ const baxia = window.baxiaCommon;
4656
+ if (typeof baxia?.version === "string") {
4657
+ baxiaVersion = baxia.version;
4658
+ }
4659
+ } catch {
4660
+ }
4661
+ return { ua, secChUa, platform, mobile, bundleVersion, baxiaVersion };
4270
4662
  }),
4271
4663
  config.timeouts.page,
4272
4664
  `User-agent lookup timed out for ${accountId}`
@@ -4275,6 +4667,9 @@ async function getBasicHeaders(accountId) {
4275
4667
  if (nav.bundleVersion) {
4276
4668
  updateQwenWebVersion(nav.bundleVersion);
4277
4669
  }
4670
+ if (nav.baxiaVersion) {
4671
+ updateQwenBxVersion(nav.baxiaVersion);
4672
+ }
4278
4673
  cachedUserAgents.set(accountId, userAgent);
4279
4674
  const hints = getHeaderCache(accountId).headers;
4280
4675
  clientHints = {
@@ -4306,7 +4701,7 @@ async function getBasicHeaders(accountId) {
4306
4701
  markAccountHeadersReady(accountId);
4307
4702
  const bxUa2 = cache.headers["bx-ua"];
4308
4703
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4309
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4704
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4310
4705
  const cookie2 = await getCookies(accountId);
4311
4706
  return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
4312
4707
  }
@@ -4317,7 +4712,7 @@ async function getBasicHeaders(accountId) {
4317
4712
  cookieCaches.set(accountId, { cookie: cookie2, timestamp: Date.now() });
4318
4713
  const bxUa2 = cache.headers["bx-ua"];
4319
4714
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4320
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4715
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4321
4716
  cache.lastRefresh = Date.now();
4322
4717
  markAccountHeadersReady(accountId);
4323
4718
  console.log(
@@ -4337,14 +4732,15 @@ async function getBasicHeaders(accountId) {
4337
4732
  `\u26A0\uFE0F [Playwright] Header refresh failed for ${accountId}; retaining the previous valid cache: ${getErrorMessage(error)}`
4338
4733
  );
4339
4734
  }
4340
- } else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache.refreshInProgress) {
4341
- cache.refreshInProgress = true;
4735
+ } 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
4736
+ // the account's page. Doing that while the account is serving a stream
4737
+ // would mutate the live conversation (and can navigate the SPA), so the
4738
+ // proactive refresh waits until the account has no active lease.
4739
+ !hasActiveAccountLease(accountId)) {
4342
4740
  refreshHeadersInternal(accountId).catch((error) => {
4343
4741
  console.warn(
4344
4742
  `\u26A0\uFE0F [Playwright] Background header refresh failed for ${accountId}: ${getErrorMessage(error)}`
4345
4743
  );
4346
- }).finally(() => {
4347
- cache.refreshInProgress = false;
4348
4744
  });
4349
4745
  }
4350
4746
  if (!hasRequiredQwenHeaders(cache.headers)) {
@@ -4391,6 +4787,9 @@ async function getBasicHeaders(accountId) {
4391
4787
  if (persisted.version) {
4392
4788
  updateQwenWebVersion(persisted.version);
4393
4789
  }
4790
+ if (persisted.bxV) {
4791
+ updateQwenBxVersion(persisted.bxV);
4792
+ }
4394
4793
  cache.lastRefresh = persisted.capturedAt;
4395
4794
  markAccountHeadersReady(accountId);
4396
4795
  }
@@ -4418,7 +4817,7 @@ async function getBasicHeaders(accountId) {
4418
4817
  markAccountHeadersReady(accountId);
4419
4818
  const bxUa = cache.headers["bx-ua"];
4420
4819
  const bxUmidtoken = cache.headers["bx-umidtoken"];
4421
- const bxV = cache.headers["bx-v"] || "2.5.37";
4820
+ const bxV = cache.headers["bx-v"] || getQwenBxVersion();
4422
4821
  cookieCaches.delete(accountId);
4423
4822
  const cookie = await getCookies(accountId, true);
4424
4823
  if (cache.headers) {
@@ -4482,8 +4881,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4482
4881
  if (accountPages.has(account.id)) {
4483
4882
  return;
4484
4883
  }
4485
- await evictIdlePlaywrightContextsToLimit().catch(() => {
4486
- });
4884
+ if (config.playwright.maxActiveContexts > 0) {
4885
+ await evictIdlePlaywrightContextsToLimit(
4886
+ Math.max(1, config.playwright.maxActiveContexts - 1)
4887
+ ).catch(() => {
4888
+ });
4889
+ }
4487
4890
  const profilePath = getAccountProfilePath(account.id);
4488
4891
  const fingerprint = getFingerprintProfile(account.id);
4489
4892
  const { engine, channel } = resolveBrowserEngine(browserType);
@@ -4659,6 +5062,9 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4659
5062
  if (persisted.version) {
4660
5063
  updateQwenWebVersion(persisted.version);
4661
5064
  }
5065
+ if (persisted.bxV) {
5066
+ updateQwenBxVersion(persisted.bxV);
5067
+ }
4662
5068
  cache.lastRefresh = persisted.capturedAt;
4663
5069
  markAccountHeadersReady(account.id);
4664
5070
  restoredFromDb = true;
@@ -4752,7 +5158,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4752
5158
  if (!acctPage.isClosed()) {
4753
5159
  try {
4754
5160
  const currentUrl = new URL(acctPage.url());
4755
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
5161
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
4756
5162
  await acctPage.goto(qwenUrl("/"), {
4757
5163
  waitUntil: "domcontentloaded",
4758
5164
  timeout: config.timeouts.navigation
@@ -4770,6 +5176,8 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4770
5176
  } finally {
4771
5177
  release();
4772
5178
  }
5179
+ await evictIdlePlaywrightContextsToLimit().catch(() => {
5180
+ });
4773
5181
  })();
4774
5182
  try {
4775
5183
  await initPromise;
@@ -5006,7 +5414,7 @@ async function startEmailOtpLogin(email, options = {}) {
5006
5414
  {
5007
5415
  email: normalizedEmail,
5008
5416
  requestId,
5009
- version: getQwenWebVersion() || "0.3.26",
5417
+ version: getQwenWebVersion(),
5010
5418
  timezone: formatAsciiTimezone()
5011
5419
  }
5012
5420
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5101,7 +5509,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
5101
5509
  email: session.email,
5102
5510
  code: cleanCode,
5103
5511
  requestId,
5104
- version: getQwenWebVersion() || "0.3.26",
5512
+ version: getQwenWebVersion(),
5105
5513
  timezone: formatAsciiTimezone()
5106
5514
  }
5107
5515
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5148,8 +5556,8 @@ async function verifyEmailOtpLogin(sessionId, code) {
5148
5556
  saveAuthSession2(session.accountId, {
5149
5557
  cookie: `token=${accessToken}`,
5150
5558
  userAgent: session.fingerprint.userAgent,
5151
- bxV: "2.5.37",
5152
- version: getQwenWebVersion() || "0.3.26",
5559
+ bxV: getQwenBxVersion(),
5560
+ version: getQwenWebVersion(),
5153
5561
  userId: userId || void 0,
5154
5562
  tokenExpiresAt: exp || void 0,
5155
5563
  refreshToken: refreshToken || void 0,
@@ -5259,7 +5667,7 @@ async function loginToQwen(accountId, email, password) {
5259
5667
  saveAuthSession2(accountId, {
5260
5668
  cookie: cookieStr,
5261
5669
  userAgent: cache.headers["user-agent"] || "",
5262
- bxV: cache.headers["bx-v"] || "2.5.37",
5670
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5263
5671
  bxUa: cache.headers["bx-ua"] || "",
5264
5672
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5265
5673
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5331,7 +5739,7 @@ async function loginToQwen(accountId, email, password) {
5331
5739
  saveAuthSession2(accountId, {
5332
5740
  cookie: cookieStr,
5333
5741
  userAgent: cache.headers["user-agent"] || "",
5334
- bxV: cache.headers["bx-v"] || "2.5.37",
5742
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5335
5743
  bxUa: cache.headers["bx-ua"] || "",
5336
5744
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5337
5745
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5402,7 +5810,7 @@ async function loginViaApi(page, email, password) {
5402
5810
  }
5403
5811
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5404
5812
  const requestId = crypto3.randomUUID();
5405
- const webVersion = getQwenWebVersion() || "0.3.26";
5813
+ const webVersion = getQwenWebVersion();
5406
5814
  const evalRes = await page.evaluate(
5407
5815
  async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
5408
5816
  try {
@@ -5574,6 +5982,15 @@ async function loginViaUi(page, email, password) {
5574
5982
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
5575
5983
  return { success: true };
5576
5984
  }
5985
+ if (config.captcha.enabled) {
5986
+ await solveVisibleCaptcha(page, {
5987
+ waitForMs: 1500,
5988
+ maxAttempts: config.captcha.maxAttempts,
5989
+ retryDelayMs: config.captcha.retryDelayMs,
5990
+ settleMs: config.captcha.settleMs,
5991
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
5992
+ }).catch(() => false);
5993
+ }
5577
5994
  const emailSelector = [
5578
5995
  'input[type="email"]',
5579
5996
  'input[name="email"]',
@@ -5729,10 +6146,11 @@ async function loginViaUi(page, email, password) {
5729
6146
  while (Date.now() < postSubmitDeadline) {
5730
6147
  if (page.isClosed()) break;
5731
6148
  if (config.captcha.enabled) {
5732
- await solveBaxiaCaptcha(page, {
6149
+ await solveVisibleCaptcha(page, {
5733
6150
  waitForMs: 1500,
5734
6151
  maxAttempts: config.captcha.maxAttempts,
5735
- retryDelayMs: config.captcha.retryDelayMs
6152
+ retryDelayMs: config.captcha.retryDelayMs,
6153
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
5736
6154
  }).catch(() => {
5737
6155
  });
5738
6156
  }
@@ -5855,7 +6273,7 @@ async function refreshAccountToken(accountId) {
5855
6273
  }
5856
6274
  if (refreshToken) {
5857
6275
  try {
5858
- const webVersion = getQwenWebVersion() || "0.3.26";
6276
+ const webVersion = getQwenWebVersion();
5859
6277
  const refreshResult = await page.evaluate(
5860
6278
  async ({ rTok, tz, version }) => {
5861
6279
  try {
@@ -5920,7 +6338,7 @@ async function refreshAccountToken(accountId) {
5920
6338
  saveAuthSession2(accountId, {
5921
6339
  cookie: cookieStr,
5922
6340
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5923
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6341
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5924
6342
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5925
6343
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5926
6344
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -5955,7 +6373,7 @@ async function refreshAccountToken(accountId) {
5955
6373
  saveAuthSession2(accountId, {
5956
6374
  cookie: cookieStr,
5957
6375
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5958
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6376
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5959
6377
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5960
6378
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5961
6379
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -6103,7 +6521,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6103
6521
  cookie: reqHeaders["cookie"] || "",
6104
6522
  "bx-ua": reqHeaders["bx-ua"] || "",
6105
6523
  "bx-umidtoken": reqHeaders["bx-umidtoken"] || "",
6106
- "bx-v": reqHeaders["bx-v"] || "2.5.37",
6524
+ "bx-v": reqHeaders["bx-v"] || getQwenBxVersion(),
6107
6525
  "user-agent": reqHeaders["user-agent"] || "",
6108
6526
  "x-request-id": reqHeaders["x-request-id"] || "",
6109
6527
  "version": reqHeaders["version"] || "",
@@ -6135,6 +6553,9 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6135
6553
  if (capturedHeaders["version"]) {
6136
6554
  updateQwenWebVersion(capturedHeaders["version"]);
6137
6555
  }
6556
+ if (capturedHeaders["bx-v"]) {
6557
+ updateQwenBxVersion(capturedHeaders["bx-v"]);
6558
+ }
6138
6559
  markAccountHeadersReady(accountId);
6139
6560
  cache.lastRefresh = Date.now();
6140
6561
  cookieCaches.delete(accountId);
@@ -6538,19 +6959,26 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6538
6959
  await executeReauth();
6539
6960
  } else {
6540
6961
  try {
6541
- await page.goto(qwenUrl("/"), {
6542
- waitUntil: "domcontentloaded",
6543
- timeout: Math.min(
6544
- config.timeouts.navigation,
6545
- boundedTimeoutMs,
6546
- SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6547
- )
6548
- });
6549
- await sleep2(2e3);
6550
- const url = page.url();
6962
+ const currentUrl = typeof page.url === "function" ? page.url() : "";
6963
+ const needsNavigation = !currentUrl.startsWith(qwenOrigin()) || currentUrl.includes("/auth") || currentUrl.includes("/login") || currentUrl.includes("_____tmd_____");
6964
+ if (needsNavigation) {
6965
+ await page.goto(qwenUrl("/"), {
6966
+ waitUntil: "domcontentloaded",
6967
+ timeout: Math.min(
6968
+ config.timeouts.navigation,
6969
+ boundedTimeoutMs,
6970
+ SESSION_PROBE_NAVIGATION_TIMEOUT_MS
6971
+ )
6972
+ });
6973
+ await sleep2(2e3);
6974
+ }
6975
+ const url = typeof page.url === "function" ? page.url() : "";
6551
6976
  const isAuthUrl = url.includes("auth") || url.includes("login");
6552
6977
  if (isAuthUrl) {
6553
- await executeReauth();
6978
+ const refreshed = await refreshAccountToken(accountId);
6979
+ if (!refreshed.success) {
6980
+ await executeReauth();
6981
+ }
6554
6982
  }
6555
6983
  } catch (navErr) {
6556
6984
  console.warn(
@@ -6585,7 +7013,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6585
7013
  saveAuthSession2(accountId, {
6586
7014
  cookie: cookieStr,
6587
7015
  userAgent: cache.headers["user-agent"] || "",
6588
- bxV: cache.headers["bx-v"] || "2.5.37",
7016
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
6589
7017
  bxUa: cache.headers["bx-ua"] || "",
6590
7018
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
6591
7019
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -6607,7 +7035,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6607
7035
  if (capturedPage && !capturedPage.isClosed()) {
6608
7036
  try {
6609
7037
  const currentUrl = new URL(capturedPage.url());
6610
- if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname !== "/") {
7038
+ if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
6611
7039
  await capturedPage.goto(qwenUrl("/"), {
6612
7040
  waitUntil: "domcontentloaded",
6613
7041
  timeout: Math.min(config.timeouts.navigation, boundedTimeoutMs)
@@ -6923,7 +7351,11 @@ function priorityOrderForEviction(accountIds) {
6923
7351
  const ranked = getAccountsByPriority(accountIds.map((id) => ({ id })));
6924
7352
  const rank = /* @__PURE__ */ new Map();
6925
7353
  for (const [index, account] of ranked.entries()) {
6926
- rank.set(account.id, index);
7354
+ if (getAccountCooldownInfo(account.id)) {
7355
+ rank.set(account.id, Number.MAX_SAFE_INTEGER);
7356
+ } else {
7357
+ rank.set(account.id, index);
7358
+ }
6927
7359
  }
6928
7360
  return orderContextsForEviction(
6929
7361
  accountIds,
@@ -6960,14 +7392,15 @@ async function closeIdlePlaywrightAccounts(idleMs) {
6960
7392
  }
6961
7393
  return closed;
6962
7394
  }
6963
- async function evictIdlePlaywrightContextsToLimit() {
6964
- const max = config.playwright.maxActiveContexts;
6965
- if (max <= 0) return 0;
7395
+ async function evictIdlePlaywrightContextsToLimit(targetLimit) {
7396
+ const configuredMax = config.playwright.maxActiveContexts;
7397
+ if (configuredMax <= 0) return 0;
7398
+ const max = targetLimit !== void 0 ? Math.max(0, targetLimit) : configuredMax;
6966
7399
  if (accountPages.size <= max) return 0;
6967
7400
  const candidates = priorityOrderForEviction(
6968
7401
  Array.from(accountPages.keys()).filter((accountId) => {
6969
7402
  if (isAccountServingStream(accountId)) return false;
6970
- if (isAccountRecentlyActive(accountId, 3e4)) return false;
7403
+ if (hasActiveAccountLease(accountId)) return false;
6971
7404
  const mutex = accountMutexes.get(accountId);
6972
7405
  return mutex?.isIdle();
6973
7406
  })
@@ -6980,6 +7413,7 @@ async function evictIdlePlaywrightContextsToLimit() {
6980
7413
  for (const candidate of candidates) {
6981
7414
  if (accountPages.size <= max) break;
6982
7415
  if (isAccountServingStream(candidate.accountId)) continue;
7416
+ if (hasActiveAccountLease(candidate.accountId)) continue;
6983
7417
  const mutex = accountMutexes.get(candidate.accountId);
6984
7418
  if (!mutex?.isIdle()) continue;
6985
7419
  await closePlaywrightForAccount(candidate.accountId).catch((error) => {
@@ -7343,13 +7777,13 @@ async function getBasicHeaders2(accountId) {
7343
7777
  return {
7344
7778
  cookie: "token=mock",
7345
7779
  userAgent: "mock",
7346
- bxV: "2.5.37",
7780
+ bxV: getQwenBxVersion(),
7347
7781
  bxUa: "mock-bx-ua",
7348
7782
  bxUmidtoken: "mock-bx-umidtoken",
7349
7783
  secChUa: "",
7350
7784
  secChUaMobile: "?0",
7351
7785
  secChUaPlatform: "",
7352
- version: "0.3.26"
7786
+ version: getQwenWebVersion()
7353
7787
  };
7354
7788
  }
7355
7789
  const resolvedAccountId = accountId ?? loadAccounts()[0]?.id;
@@ -7410,6 +7844,7 @@ var init_auth_playwright = __esm({
7410
7844
  init_errors();
7411
7845
  init_accounts();
7412
7846
  init_config();
7847
+ init_qwen_headers();
7413
7848
  init_playwright();
7414
7849
  init_jwt();
7415
7850
  }
@@ -7818,6 +8253,7 @@ var init_model_registry = __esm({
7818
8253
  var captcha_coordinator_exports = {};
7819
8254
  __export(captcha_coordinator_exports, {
7820
8255
  isCaptchaRecoveryActive: () => isCaptchaRecoveryActive,
8256
+ isCaptchaRecoveryInBackoff: () => isCaptchaRecoveryInBackoff,
7821
8257
  recoverBaxiaCaptcha: () => recoverBaxiaCaptcha,
7822
8258
  solveChallengeOnPage: () => solveChallengeOnPage
7823
8259
  });
@@ -7837,9 +8273,10 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
7837
8273
  const solverOptions = {
7838
8274
  maxAttempts: config.captcha.maxAttempts,
7839
8275
  retryDelayMs: config.captcha.retryDelayMs,
7840
- settleMs: config.captcha.settleMs
8276
+ settleMs: config.captcha.settleMs,
8277
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
7841
8278
  };
7842
- if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
8279
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
7843
8280
  return true;
7844
8281
  }
7845
8282
  logBaxiaCaptcha(
@@ -7849,13 +8286,18 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
7849
8286
  );
7850
8287
  await gotoBestEffort(page, challengeUrl ?? qwenUrl("/"));
7851
8288
  try {
7852
- return await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs });
8289
+ return await solveVisibleCaptcha(page, { ...solverOptions, waitForMs });
7853
8290
  } finally {
7854
8291
  if (page.url().includes(CHALLENGE_PATH_MARKER)) {
7855
8292
  await gotoBestEffort(page, qwenUrl("/"));
7856
8293
  }
7857
8294
  }
7858
8295
  }
8296
+ function isCaptchaRecoveryInBackoff(accountId) {
8297
+ if (!accountId) return false;
8298
+ const lastFailure = lastFailedRecoveryAt.get(accountId) ?? 0;
8299
+ return Date.now() - lastFailure < FAILED_RECOVERY_BACKOFF_MS;
8300
+ }
7859
8301
  async function recoverBaxiaCaptcha(accountId, label, options = {}) {
7860
8302
  if (!config.captcha.enabled || !accountId) return false;
7861
8303
  const solver = "baxia";
@@ -7873,17 +8315,35 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
7873
8315
  const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
7874
8316
  activeCaptchaRecoveries.add(accountId);
7875
8317
  try {
7876
- const { abortActiveBrowserStreamsForAccount: abortActiveBrowserStreamsForAccount2 } = await Promise.resolve().then(() => (init_qwen(), qwen_exports));
7877
- abortActiveBrowserStreamsForAccount2(
7878
- accountId,
7879
- "Account stream interrupted by captcha challenge recovery"
7880
- );
7881
- } catch {
7882
- }
7883
- try {
8318
+ const { hasActiveBrowserStreamsForAccount: hasActiveBrowserStreamsForAccount2 } = await Promise.resolve().then(() => (init_qwen(), qwen_exports));
8319
+ const hasConcurrentStream = hasActiveBrowserStreamsForAccount2(accountId);
7884
8320
  const solved = await withAccountPage(
7885
8321
  accountId,
7886
- (page) => solveChallengeOnPage(page, challengeUrl),
8322
+ async (page) => {
8323
+ if (hasConcurrentStream) {
8324
+ const solverOptions = {
8325
+ maxAttempts: config.captcha.maxAttempts,
8326
+ retryDelayMs: config.captcha.retryDelayMs,
8327
+ settleMs: config.captcha.settleMs,
8328
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
8329
+ };
8330
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
8331
+ return true;
8332
+ }
8333
+ const ctx = typeof page.context === "function" ? page.context() : null;
8334
+ if (ctx && typeof ctx.newPage === "function") {
8335
+ const tempPage = await ctx.newPage();
8336
+ try {
8337
+ return await solveChallengeOnPage(tempPage, challengeUrl);
8338
+ } finally {
8339
+ await tempPage.close({ runBeforeUnload: false }).catch(() => {
8340
+ });
8341
+ }
8342
+ }
8343
+ return false;
8344
+ }
8345
+ return await solveChallengeOnPage(page, challengeUrl);
8346
+ },
7887
8347
  solverOperationTimeoutMs,
7888
8348
  solverOperationTimeoutMs,
7889
8349
  false
@@ -8384,7 +8844,7 @@ var init_media_generation = __esm({
8384
8844
 
8385
8845
  // src/services/qwen-chat-pool.ts
8386
8846
  import crypto4 from "crypto";
8387
- async function createQwenChatSession(headers, model, accountId, chatMode = "thread") {
8847
+ async function createQwenChatSession(headers, model, accountId, chatMode = "thread", captchaRecoveryAttempts = 1) {
8388
8848
  if (isAuthMockEnabled()) {
8389
8849
  return process.env.TEST_SESSION_ID || "mock-session";
8390
8850
  }
@@ -8412,16 +8872,29 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
8412
8872
  if (!chatId || typeof chatId !== "string") {
8413
8873
  const rawLower = raw.toLowerCase();
8414
8874
  if (looksLikeAntiBotChallengeText(raw)) {
8415
- try {
8416
- const { recoverBaxiaCaptcha: recoverBaxiaCaptcha2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
8417
- const solved = await recoverBaxiaCaptcha2(accountId, "createQwenChatSession", {
8418
- challengeBody: raw
8419
- });
8420
- if (solved) {
8421
- return createQwenChatSession(headers, model, accountId, chatMode);
8875
+ if (captchaRecoveryAttempts > 0) {
8876
+ try {
8877
+ const { recoverBaxiaCaptcha: recoverBaxiaCaptcha2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
8878
+ const solved = await recoverBaxiaCaptcha2(accountId, "createQwenChatSession", {
8879
+ challengeBody: raw
8880
+ });
8881
+ if (solved) {
8882
+ return createQwenChatSession(
8883
+ headers,
8884
+ model,
8885
+ accountId,
8886
+ chatMode,
8887
+ captchaRecoveryAttempts - 1
8888
+ );
8889
+ }
8890
+ } catch {
8422
8891
  }
8423
- } catch {
8424
8892
  }
8893
+ throw new QwenUpstreamError(
8894
+ `Qwen create chat blocked by WAF challenge: ${raw.substring(0, 300)}`,
8895
+ "FAIL_SYS_USER_VALIDATE",
8896
+ 403
8897
+ );
8425
8898
  }
8426
8899
  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");
8427
8900
  if (isUnauthorized) {
@@ -8598,6 +9071,20 @@ async function refillQwenChatPool(headers, model, accountId) {
8598
9071
  console.warn(
8599
9072
  `[WarmPool] Account ${accountId || "global"} rate-limited during chat creation. Marked for cooldown.`
8600
9073
  );
9074
+ } else if (accountId && (err.upstreamCode === "FAIL_SYS_USER_VALIDATE" || err.upstreamCode === "RGV587_ERROR" || /_____tmd_____|rgv587_error|fail_sys_user_validate/i.test(
9075
+ err.message
9076
+ ))) {
9077
+ const existing = getAccountCooldownInfo(accountId);
9078
+ if (existing?.reason === "WafChallenge") {
9079
+ console.warn(
9080
+ `[WarmPool] Account ${accountId} still on WAF quarantine (${Math.ceil(existing.remainingMs / 1e3)}s left); skipping re-escalation.`
9081
+ );
9082
+ } else {
9083
+ const block = recordWafHardBlock(accountId);
9084
+ console.warn(
9085
+ `[WarmPool] Unsolved WAF challenge on ${accountId} during chat creation; quarantined ${Math.ceil(block.cooldownMs / 1e3)}s and rotated the fingerprint.`
9086
+ );
9087
+ }
8601
9088
  }
8602
9089
  }
8603
9090
  if (isToolcallDebugEnabled()) {
@@ -8633,6 +9120,7 @@ var init_qwen_chat_pool = __esm({
8633
9120
  init_config();
8634
9121
  init_logger();
8635
9122
  init_account_manager();
9123
+ init_waf_isolation();
8636
9124
  init_model_alias();
8637
9125
  init_qwen_url();
8638
9126
  init_qwen_errors();
@@ -8682,6 +9170,7 @@ __export(qwen_exports, {
8682
9170
  getLogicalThreadState: () => getLogicalThreadState,
8683
9171
  getQwenErrorCode: () => getQwenErrorCode,
8684
9172
  getSessionParent: () => getSessionParent,
9173
+ hasActiveBrowserStreamsForAccount: () => hasActiveBrowserStreamsForAccount,
8685
9174
  invalidateLogicalThreadParent: () => invalidateLogicalThreadParent,
8686
9175
  isChatSessionActive: () => isChatSessionActive,
8687
9176
  isRetryableFetchErrorMessage: () => isRetryableFetchErrorMessage,
@@ -8703,6 +9192,14 @@ __export(qwen_exports, {
8703
9192
  });
8704
9193
  import crypto5 from "crypto";
8705
9194
  import { v4 as uuidv43 } from "uuid";
9195
+ function hasActiveBrowserStreamsForAccount(accountId) {
9196
+ for (const state of browserStreamStates.values()) {
9197
+ if (state.accountId === accountId && !state.done) {
9198
+ return true;
9199
+ }
9200
+ }
9201
+ return false;
9202
+ }
8706
9203
  function abortActiveBrowserStreamsForAccount(accountId, reason = "Account browser stream interrupted (page context unloaded)") {
8707
9204
  let count = 0;
8708
9205
  for (const [requestId, state] of browserStreamStates) {
@@ -9193,7 +9690,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9193
9690
  browserHeaders["Content-Type"] = "application/json";
9194
9691
  }
9195
9692
  const evaluateRequest = (page) => page.evaluate(
9196
- async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
9693
+ async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz, version }) => {
9197
9694
  const controller = new AbortController();
9198
9695
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
9199
9696
  try {
@@ -9215,7 +9712,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9215
9712
  headers: {
9216
9713
  accept: "application/json, text/plain, */*",
9217
9714
  source: "web",
9218
- version: "0.3.11",
9715
+ version,
9219
9716
  "x-request-origin": "https://chat.qwen.ai",
9220
9717
  timezone: tz,
9221
9718
  authorization: `Bearer ${rTok}`
@@ -9263,7 +9760,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9263
9760
  body,
9264
9761
  referrer: options.referrer,
9265
9762
  timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
9266
- tz: formatAsciiTimezone()
9763
+ tz: formatAsciiTimezone(),
9764
+ version: getQwenWebVersion()
9267
9765
  }
9268
9766
  );
9269
9767
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -9480,7 +9978,7 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
9480
9978
  const onFrameNavigated = (frame) => {
9481
9979
  if (!settled && typeof page.mainFrame === "function" && frame === page.mainFrame()) {
9482
9980
  const url2 = typeof frame.url === "function" ? frame.url() : String(frame.url || "");
9483
- if (url2.startsWith(qwenOrigin()) && !url2.includes("/auth") && !url2.includes("_____tmd_____") && !url2.includes("/punish")) {
9981
+ if (url2.startsWith(qwenOrigin()) && !url2.includes("/auth") && !url2.includes("/login") && !url2.includes("_____tmd_____") && !url2.includes("/punish")) {
9484
9982
  return;
9485
9983
  }
9486
9984
  handleBrowserStreamEvent(requestId, {
@@ -9501,10 +9999,12 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
9501
9999
  };
9502
10000
  page.on("framenavigated", onFrameNavigated);
9503
10001
  page.on("close", onPageClosed);
10002
+ page.on("crash", onPageClosed);
9504
10003
  pageCleanup = () => {
9505
10004
  try {
9506
10005
  page.off?.("framenavigated", onFrameNavigated);
9507
10006
  page.off?.("close", onPageClosed);
10007
+ page.off?.("crash", onPageClosed);
9508
10008
  } catch {
9509
10009
  }
9510
10010
  };
@@ -10218,7 +10718,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
10218
10718
  }
10219
10719
  const requestHeaders = {
10220
10720
  source: "web",
10221
- version: getQwenWebVersion() || "0.3.26",
10721
+ version: getQwenWebVersion(),
10222
10722
  timezone: formatAsciiTimezone(),
10223
10723
  "x-request-id": crypto5.randomUUID(),
10224
10724
  Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
@@ -10262,7 +10762,7 @@ async function fetchRemoteQwenChats(accountId) {
10262
10762
  return [];
10263
10763
  }
10264
10764
  const requestHeaders = {
10265
- version: getQwenWebVersion() || "0.3.26",
10765
+ version: getQwenWebVersion(),
10266
10766
  timezone: formatAsciiTimezone(),
10267
10767
  "x-request-id": crypto5.randomUUID(),
10268
10768
  Referer: qwenUrl("/settings/chats")
@@ -11292,6 +11792,7 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
11292
11792
  const account = accounts[i];
11293
11793
  const currentIdx = i + 1;
11294
11794
  const totalCount = accounts.length;
11795
+ const wasInitialized = isPlaywrightInitialized(account.id);
11295
11796
  try {
11296
11797
  const ok = await deleteChatsForAccount(account, currentIdx, totalCount);
11297
11798
  if (ok) succeeded++;
@@ -11301,7 +11802,7 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
11301
11802
  error instanceof Error ? error.message : String(error)
11302
11803
  );
11303
11804
  } finally {
11304
- if (!keepBrowserOpen) {
11805
+ if ((!keepBrowserOpen || !wasInitialized) && !isAuthMockEnabled()) {
11305
11806
  const { closePlaywrightForAccount: closePlaywrightForAccount2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
11306
11807
  await closePlaywrightForAccount2(account.id).catch(() => {
11307
11808
  });