qwenproxy-cli 1.6.11 → 1.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
@@ -2200,237 +2564,31 @@ function getAccountCooldownInfo(accountId) {
2200
2564
  err.message
2201
2565
  );
2202
2566
  }
2203
- }
2204
- return null;
2205
- }
2206
- return { onCooldown: true, remainingMs: remaining, reason: entry.reason };
2207
- }
2208
- function markAccountHeadersReady(accountId) {
2209
- if (!accountId || accountId === "global") return;
2210
- headersReadyAccounts.add(accountId);
2211
- }
2212
- function unmarkAccountHeadersReady(accountId) {
2213
- if (!accountId) return;
2214
- headersReadyAccounts.delete(accountId);
2215
- }
2216
- function isAccountHeadersReady(accountId) {
2217
- return headersReadyAccounts.has(accountId);
2218
- }
2219
- var cooldowns, headersReadyAccounts;
2220
- var init_account_manager = __esm({
2221
- "src/core/account-manager.ts"() {
2222
- "use strict";
2223
- init_accounts();
2224
- init_account_priority();
2225
- init_logger();
2226
- cooldowns = /* @__PURE__ */ new Map();
2227
- headersReadyAccounts = /* @__PURE__ */ new Set();
2228
- }
2229
- });
2230
-
2231
- // src/services/fingerprint.ts
2232
- function mulberry32(seed) {
2233
- return () => {
2234
- seed |= 0;
2235
- seed = seed + 1831565813 | 0;
2236
- let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
2237
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2238
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
2239
- };
2240
- }
2241
- function seedFromString(value) {
2242
- let hash = 0;
2243
- for (let i = 0; i < value.length; i++) {
2244
- hash = (hash << 5) - hash + value.charCodeAt(i) | 0;
2245
- }
2246
- return hash >>> 0;
2247
- }
2248
- function pick(rng, values) {
2249
- return values[Math.floor(rng() * values.length)];
2250
- }
2251
- function randInt(rng, min, max) {
2252
- return Math.floor(rng() * (max - min + 1)) + min;
2253
- }
2254
- function updateChromeMajor(major) {
2255
- if (typeof major === "number" && major >= 100) {
2256
- dynamicChromeMajor = major;
2257
- }
2258
- }
2259
- function getChromeMajor() {
2260
- return dynamicChromeMajor;
2261
- }
2262
- function getSystemLocale() {
2263
- return process.env.QWEN_LOCALE || (typeof Intl !== "undefined" && Intl.DateTimeFormat ? Intl.DateTimeFormat().resolvedOptions().locale : "") || "pt-BR";
2264
- }
2265
- function getLanguageProfiles(locale = getSystemLocale()) {
2266
- const primary = locale.split("-")[0];
2267
- if (primary === "en") {
2268
- return [
2269
- ["en-US", "en"],
2270
- ["en-US", "en", "es"],
2271
- ["en", "en-US"],
2272
- ["en-US", "en;q=0.9"]
2273
- ];
2274
- }
2275
- return [
2276
- [locale, primary, "en-US", "en"],
2277
- [locale, primary, "en-US", "en", "es"],
2278
- [locale, "en-US", "en", primary],
2279
- [locale, primary, "en"],
2280
- [locale, `${primary};q=0.9`, "en-US;q=0.8", "en;q=0.7"]
2281
- ];
2282
- }
2283
- function getFingerprintProfile(accountId) {
2284
- const cached = profileCache.get(accountId);
2285
- if (cached) return cached;
2286
- const salt = rotationSalts.get(accountId) ?? 0;
2287
- const seed = seedFromString(salt === 0 ? accountId : `${accountId}#r${salt}`);
2288
- const rng = mulberry32(seed);
2289
- const viewport = pick(rng, VIEWPORTS);
2290
- const webgl = pick(rng, WEBGL_PROFILES);
2291
- const languages = [...pick(rng, getLanguageProfiles())];
2292
- const hardwareConcurrency = pick(rng, HARDWARE_CONCURRENCIES);
2293
- const deviceMemory = pick(rng, DEVICE_MEMORIES);
2294
- const platformInfo = pick(rng, PLATFORM_VERSIONS);
2295
- const notABrand = pick(rng, NOT_A_BRAND_VARIANTS);
2296
- const major = dynamicChromeMajor;
2297
- const build = randInt(rng, 7300, 7600);
2298
- const patch = randInt(rng, 0, 160);
2299
- const chromeVersion = `${major}.0.${build}.${patch}`;
2300
- const brands = [
2301
- { brand: notABrand.brand, version: notABrand.version },
2302
- { brand: "Google Chrome", version: String(major) },
2303
- { brand: "Chromium", version: String(major) }
2304
- ];
2305
- const fullVersionList = [
2306
- { brand: notABrand.brand, version: chromeVersion },
2307
- { brand: "Google Chrome", version: chromeVersion },
2308
- { brand: "Chromium", version: chromeVersion }
2309
- ];
2310
- const secChUa = brands.map((brand) => `"${brand.brand}";v="${brand.version}"`).join(", ");
2311
- const userAgent = `Mozilla/5.0 (Windows NT ${platformInfo.major}.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
2312
- const profile = {
2313
- accountId,
2314
- seed,
2315
- userAgent,
2316
- appVersion: userAgent.replace("Mozilla/", ""),
2317
- chromeMajor: CHROME_MAJOR,
2318
- chromeVersion,
2319
- brands,
2320
- fullVersionList,
2321
- secChUa,
2322
- platform: "Win32",
2323
- platformVersion: platformInfo.platformVersion,
2324
- architecture: "x86",
2325
- bitness: "64",
2326
- languages,
2327
- locale: languages[0].split(";")[0],
2328
- timezoneId: "America/Sao_Paulo",
2329
- viewport,
2330
- hardwareConcurrency,
2331
- deviceMemory,
2332
- webglVendor: webgl.vendor,
2333
- webglRenderer: webgl.renderer,
2334
- colorDepth: 24,
2335
- pixelDepth: 24,
2336
- canvasNoiseSeed: randInt(rng, 1, 2147483647),
2337
- audioNoiseSeed: randInt(rng, 1, 2147483647),
2338
- webglNoiseSeed: randInt(rng, 1, 2147483647),
2339
- outerWidthOffset: randInt(rng, 0, 20),
2340
- outerHeightOffset: randInt(rng, 75, 95)
2341
- };
2342
- profileCache.set(accountId, profile);
2343
- return profile;
2567
+ }
2568
+ return null;
2569
+ }
2570
+ return { onCooldown: true, remainingMs: remaining, reason: entry.reason };
2344
2571
  }
2345
- function clearFingerprintCache(accountId) {
2346
- if (accountId) profileCache.delete(accountId);
2347
- else profileCache.clear();
2572
+ function markAccountHeadersReady(accountId) {
2573
+ if (!accountId || accountId === "global") return;
2574
+ headersReadyAccounts.add(accountId);
2348
2575
  }
2349
- var dynamicChromeMajor, CHROME_MAJOR, VIEWPORTS, WEBGL_PROFILES, HARDWARE_CONCURRENCIES, DEVICE_MEMORIES, PLATFORM_VERSIONS, NOT_A_BRAND_VARIANTS, profileCache, rotationSalts;
2350
- var init_fingerprint = __esm({
2351
- "src/services/fingerprint.ts"() {
2576
+ function unmarkAccountHeadersReady(accountId) {
2577
+ if (!accountId) return;
2578
+ headersReadyAccounts.delete(accountId);
2579
+ }
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"() {
2352
2586
  "use strict";
2353
- dynamicChromeMajor = 151;
2354
- CHROME_MAJOR = 151;
2355
- VIEWPORTS = [
2356
- { width: 1366, height: 768 },
2357
- { width: 1440, height: 900 },
2358
- { width: 1536, height: 864 },
2359
- { width: 1600, height: 900 },
2360
- { width: 1680, height: 1050 },
2361
- { width: 1920, height: 1080 },
2362
- { width: 1920, height: 1200 },
2363
- { width: 1280, height: 720 },
2364
- { width: 1280, height: 800 },
2365
- { width: 1536, height: 864 }
2366
- ];
2367
- WEBGL_PROFILES = [
2368
- {
2369
- vendor: "Google Inc. (Intel)",
2370
- renderer: "ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2371
- },
2372
- {
2373
- vendor: "Google Inc. (Intel)",
2374
- renderer: "ANGLE (Intel, Intel(R) Iris(R) Xe Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)"
2375
- },
2376
- {
2377
- vendor: "Google Inc. (Intel)",
2378
- renderer: "ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2379
- },
2380
- {
2381
- vendor: "Google Inc. (NVIDIA)",
2382
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2383
- },
2384
- {
2385
- vendor: "Google Inc. (NVIDIA)",
2386
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2387
- },
2388
- {
2389
- vendor: "Google Inc. (NVIDIA)",
2390
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 3080 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2391
- },
2392
- {
2393
- vendor: "Google Inc. (NVIDIA)",
2394
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2395
- },
2396
- {
2397
- vendor: "Google Inc. (NVIDIA)",
2398
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2399
- },
2400
- {
2401
- vendor: "Google Inc. (NVIDIA)",
2402
- renderer: "ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)"
2403
- },
2404
- {
2405
- vendor: "Google Inc. (AMD)",
2406
- renderer: "ANGLE (AMD, AMD Radeon RX 6600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2407
- },
2408
- {
2409
- vendor: "Google Inc. (AMD)",
2410
- renderer: "ANGLE (AMD, AMD Radeon RX 7600 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2411
- },
2412
- {
2413
- vendor: "Google Inc. (AMD)",
2414
- renderer: "ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)"
2415
- }
2416
- ];
2417
- HARDWARE_CONCURRENCIES = [4, 6, 8, 8, 8, 12, 16, 16, 24, 32];
2418
- DEVICE_MEMORIES = [4, 8, 8, 8, 16, 16, 32];
2419
- PLATFORM_VERSIONS = [
2420
- { platform: "Windows", platformVersion: "14.0.0", major: "10" },
2421
- { platform: "Windows", platformVersion: "15.0.0", major: "11" },
2422
- { platform: "Windows", platformVersion: "14.0.0", major: "10" },
2423
- { platform: "Windows", platformVersion: "15.0.0", major: "11" }
2424
- ];
2425
- NOT_A_BRAND_VARIANTS = [
2426
- { brand: "Not/A)Brand", version: "99" },
2427
- { brand: "Not)A_Brand", version: "99" },
2428
- { brand: "Not/A)Brand", version: "8" },
2429
- { brand: "Not?A_Brand", version: "24" },
2430
- { brand: "Not/A)Brand", version: "99" }
2431
- ];
2432
- profileCache = /* @__PURE__ */ new Map();
2433
- rotationSalts = /* @__PURE__ */ new Map();
2587
+ init_accounts();
2588
+ init_account_priority();
2589
+ init_logger();
2590
+ cooldowns = /* @__PURE__ */ new Map();
2591
+ headersReadyAccounts = /* @__PURE__ */ new Set();
2434
2592
  }
2435
2593
  });
2436
2594
 
@@ -2513,69 +2671,357 @@ async function subtlePageActivity(page) {
2513
2671
  target.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
2514
2672
  target.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
2515
2673
  }
2516
- } catch {
2674
+ } catch {
2675
+ }
2676
+ }).catch(() => {
2677
+ });
2678
+ } catch (err) {
2679
+ if (page.isClosed()) return;
2680
+ const msg = err instanceof Error ? err.message : String(err);
2681
+ if (msg.includes("Target page, context or browser has been closed") || msg.includes("Browser has been closed") || msg.includes("Target closed") || msg.includes("Target crashed") || msg.includes("Page crashed") || msg.includes("Connection closed")) {
2682
+ return;
2683
+ }
2684
+ throw err;
2685
+ }
2686
+ }
2687
+ var sleep;
2688
+ var init_human_behavior = __esm({
2689
+ "src/services/human-behavior.ts"() {
2690
+ "use strict";
2691
+ sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2692
+ }
2693
+ });
2694
+
2695
+ // src/services/captcha-solver.ts
2696
+ function formatCaptchaLogValue(value) {
2697
+ if (typeof value === "string") {
2698
+ return JSON.stringify(value.replace(/\s+/g, " ").slice(0, 160));
2699
+ }
2700
+ return String(value);
2701
+ }
2702
+ function logBaxiaCaptcha(event, fields = {}, important = false) {
2703
+ if (!important && !captchaDebugEnabled) return;
2704
+ const suffix = Object.entries(fields).map(([key, value]) => `${key}=${formatCaptchaLogValue(value)}`).join(" ");
2705
+ const emoji = CAPTCHA_EVENT_EMOJI[event] ?? "\u2139\uFE0F";
2706
+ const line = `${emoji} [Captcha] ${event}${suffix ? ` | ${suffix}` : ""}`;
2707
+ if (important) {
2708
+ console.warn(line);
2709
+ } else {
2710
+ console.log(line);
2711
+ }
2712
+ }
2713
+ function extractBaxiaChallengeUrl(body, baseUrl) {
2714
+ if (!body) return null;
2715
+ const normalized = body.replace(/\\u002f/gi, "/").replace(/\\\//g, "/").replace(/&amp;/gi, "&");
2716
+ const candidates = normalized.match(
2717
+ /(?:https?:)?\/\/[^\s"'`\\<>()]*(?:_____tmd_____|x5secdata=)[^\s"'`\\<>()]*|\/[^\s"'`\\<>()]*(?:_____tmd_____|x5secdata=)[^\s"'`\\<>()]*/gi
2718
+ );
2719
+ if (!candidates) return null;
2720
+ let base;
2721
+ try {
2722
+ base = new URL(baseUrl);
2723
+ } catch {
2724
+ return null;
2725
+ }
2726
+ for (const candidate of candidates) {
2727
+ let url;
2728
+ try {
2729
+ url = new URL(candidate, base);
2730
+ } catch {
2731
+ continue;
2732
+ }
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);
2517
3002
  }
2518
- }).catch(() => {
2519
- });
2520
- } catch (err) {
2521
- if (page.isClosed()) return;
2522
- const msg = err instanceof Error ? err.message : String(err);
2523
- if (msg.includes("Target page, context or browser has been closed") || msg.includes("Browser has been closed") || msg.includes("Target closed") || msg.includes("Target crashed") || msg.includes("Page crashed") || msg.includes("Connection closed")) {
2524
- return;
2525
3003
  }
2526
- throw err;
2527
- }
2528
- }
2529
- var sleep;
2530
- var init_human_behavior = __esm({
2531
- "src/services/human-behavior.ts"() {
2532
- "use strict";
2533
- sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2534
- }
2535
- });
2536
-
2537
- // src/services/captcha-solver.ts
2538
- function formatCaptchaLogValue(value) {
2539
- if (typeof value === "string") {
2540
- return JSON.stringify(value.replace(/\s+/g, " ").slice(0, 160));
2541
- }
2542
- return String(value);
3004
+ logBaxiaCaptcha(
3005
+ "aliyun_solve_failed",
3006
+ { attempts: maxAttempts, reason: lastReason },
3007
+ true
3008
+ );
3009
+ return false;
3010
+ });
2543
3011
  }
2544
- function logBaxiaCaptcha(event, fields = {}, important = false) {
2545
- if (!important && !captchaDebugEnabled) return;
2546
- const suffix = Object.entries(fields).map(([key, value]) => `${key}=${formatCaptchaLogValue(value)}`).join(" ");
2547
- const emoji = CAPTCHA_EVENT_EMOJI[event] ?? "\u2139\uFE0F";
2548
- const line = `${emoji} [Captcha] ${event}${suffix ? ` | ${suffix}` : ""}`;
2549
- if (important) {
2550
- console.warn(line);
2551
- } else {
2552
- console.log(line);
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;
2553
3019
  }
2554
- }
2555
- function extractBaxiaChallengeUrl(body, baseUrl) {
2556
- if (!body) return null;
2557
- const normalized = body.replace(/\\u002f/gi, "/").replace(/\\\//g, "/").replace(/&amp;/gi, "&");
2558
- const candidates = normalized.match(
2559
- /(?:https?:)?\/\/[^\s"'`\\<>()]*(?:_____tmd_____|x5secdata=)[^\s"'`\\<>()]*|\/[^\s"'`\\<>()]*(?:_____tmd_____|x5secdata=)[^\s"'`\\<>()]*/gi
3020
+ const remainingWaitMs = Math.max(
3021
+ 0,
3022
+ totalWaitMs - (Date.now() - startedAt)
2560
3023
  );
2561
- if (!candidates) return null;
2562
- let base;
2563
- try {
2564
- base = new URL(baseUrl);
2565
- } catch {
2566
- return null;
2567
- }
2568
- for (const candidate of candidates) {
2569
- let url;
2570
- try {
2571
- url = new URL(candidate, base);
2572
- } catch {
2573
- continue;
2574
- }
2575
- if (url.protocol !== base.protocol || url.host !== base.host) continue;
2576
- return url.toString();
2577
- }
2578
- return null;
3024
+ return solveBaxiaCaptcha(page, { ...options, waitForMs: remainingWaitMs });
2579
3025
  }
2580
3026
  async function isVisible(locator) {
2581
3027
  return locator.isVisible().catch(() => false);
@@ -2846,7 +3292,7 @@ function startBaxiaCaptchaWatcher(page, timeoutMs, options = {}) {
2846
3292
  promise
2847
3293
  };
2848
3294
  }
2849
- 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;
2850
3296
  var init_captcha_solver = __esm({
2851
3297
  "src/services/captcha-solver.ts"() {
2852
3298
  "use strict";
@@ -2872,14 +3318,20 @@ var init_captcha_solver = __esm({
2872
3318
  solve_failed: "\u274C",
2873
3319
  recovery_succeeded: "\u2705",
2874
3320
  recovery_not_solved: "\u26A0\uFE0F",
2875
- 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"
2876
3327
  };
2877
3328
  captchaDebugEnabled = process.env.CAPTCHA_DEBUG === "true";
2878
3329
  BAXIA_IFRAME_SELECTORS = [
2879
3330
  `${BAXIA_DIALOG_SELECTOR} ${BAXIA_CONTENT_SELECTOR} iframe`,
2880
3331
  `${BAXIA_CONTENT_SELECTOR} iframe`,
2881
3332
  "iframe#baxia-dialog-content",
2882
- 'iframe[src*="_____tmd_____/punish"]'
3333
+ 'iframe[src*="_____tmd_____/punish"]',
3334
+ 'iframe[src*="nocaptcha"]'
2883
3335
  ];
2884
3336
  BAXIA_DOCUMENT_SELECTORS = [
2885
3337
  "#nocaptcha",
@@ -2887,41 +3339,71 @@ var init_captcha_solver = __esm({
2887
3339
  "#baxia-punish"
2888
3340
  ];
2889
3341
  BAXIA_IFRAME_SELECTOR = BAXIA_IFRAME_SELECTORS.join(", ");
2890
- BAXIA_SLIDER_SELECTOR = "#nc_1_n1z, .nc_1_n1z, .btn_slide, .nc_wrapper .btn_slide, ._nc .btn_slide, .nc-container .btn_slide";
2891
- 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';
2892
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';
2893
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"]';
2894
3346
  DEFAULT_MAX_ATTEMPTS = 3;
2895
3347
  DEFAULT_RETRY_DELAY_MS = 1e3;
2896
3348
  DEFAULT_SETTLE_MS = 2e3;
2897
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;
2898
3359
  captchaMouseLock = Promise.resolve();
2899
3360
  }
2900
3361
  });
2901
3362
 
2902
- // src/services/qwen-url.ts
2903
- function qwenUrl(path6 = "") {
2904
- const normalizedPath = path6.replace(/^\/+/, "");
2905
- return normalizedPath ? `${QWEN_BASE_URL}/${normalizedPath}` : QWEN_BASE_URL;
2906
- }
2907
- function qwenOrigin() {
2908
- return QWEN_ORIGIN;
2909
- }
2910
- var QWEN_BASE_URL, QWEN_ORIGIN;
2911
- var init_qwen_url = __esm({
2912
- "src/services/qwen-url.ts"() {
2913
- "use strict";
2914
- init_config();
2915
- QWEN_BASE_URL = config.qwen.baseUrl.trim().replace(/\/+$/, "");
2916
- QWEN_ORIGIN = new URL(QWEN_BASE_URL).origin;
2917
- }
2918
- });
2919
-
2920
3363
  // src/core/waf-isolation.ts
2921
3364
  function setWafContextResetListener(fn) {
2922
3365
  contextResetListener = fn;
2923
3366
  }
2924
- 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;
2925
3407
  var init_waf_isolation = __esm({
2926
3408
  "src/core/waf-isolation.ts"() {
2927
3409
  "use strict";
@@ -2929,132 +3411,13 @@ var init_waf_isolation = __esm({
2929
3411
  init_logger();
2930
3412
  init_account_manager();
2931
3413
  init_fingerprint();
3414
+ ESCALATION_FACTOR = 2;
3415
+ MAX_CONSECUTIVE_FOR_ESCALATION = 4;
3416
+ states = /* @__PURE__ */ new Map();
2932
3417
  contextResetListener = null;
2933
3418
  }
2934
3419
  });
2935
3420
 
2936
- // src/utils/timezone.ts
2937
- function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
2938
- const dayName = DAYS[date.getDay()];
2939
- const monthName = MONTHS[date.getMonth()];
2940
- const day = String(date.getDate()).padStart(2, "0");
2941
- const year = date.getFullYear();
2942
- const hours = String(date.getHours()).padStart(2, "0");
2943
- const minutes = String(date.getMinutes()).padStart(2, "0");
2944
- const seconds = String(date.getSeconds()).padStart(2, "0");
2945
- const offsetMinutes = -date.getTimezoneOffset();
2946
- const sign = offsetMinutes >= 0 ? "+" : "-";
2947
- const absOffset = Math.abs(offsetMinutes);
2948
- const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
2949
- const offsetMins = String(absOffset % 60).padStart(2, "0");
2950
- const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
2951
- return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
2952
- }
2953
- function sanitizeHeaderForFetch(val) {
2954
- if (typeof val !== "string") return "";
2955
- return val.replace(/[^\x00-\xFF]/g, "");
2956
- }
2957
- var DAYS, MONTHS;
2958
- var init_timezone = __esm({
2959
- "src/utils/timezone.ts"() {
2960
- "use strict";
2961
- DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
2962
- MONTHS = [
2963
- "Jan",
2964
- "Feb",
2965
- "Mar",
2966
- "Apr",
2967
- "May",
2968
- "Jun",
2969
- "Jul",
2970
- "Aug",
2971
- "Sep",
2972
- "Oct",
2973
- "Nov",
2974
- "Dec"
2975
- ];
2976
- }
2977
- });
2978
-
2979
- // src/services/qwen-headers.ts
2980
- import { v4 as uuidv4 } from "uuid";
2981
- function getSystemAcceptLanguage(customLocale) {
2982
- const locale = customLocale || getSystemLocale();
2983
- const primary = locale.split("-")[0];
2984
- if (locale.toLowerCase() === "en-us" || locale.toLowerCase() === "en") {
2985
- return "en-US,en;q=0.9";
2986
- }
2987
- if (locale.toLowerCase().startsWith("pt")) {
2988
- return "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7";
2989
- }
2990
- return `${locale},${primary};q=0.9,en-US;q=0.8,en;q=0.7`;
2991
- }
2992
- function getDefaultQwenUserAgent(major = getChromeMajor()) {
2993
- return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${major}.0.0.0 Safari/537.36`;
2994
- }
2995
- function updateQwenWebVersion(version) {
2996
- if (version && typeof version === "string" && /^\d+\.\d+\.\d+/.test(version)) {
2997
- dynamicWebVersion = version.trim();
2998
- }
2999
- }
3000
- function getQwenWebVersion() {
3001
- return dynamicWebVersion || config.qwen.webVersion;
3002
- }
3003
- function buildQwenRequestHeaders(opts) {
3004
- const headers = {
3005
- ...opts.extra ?? {},
3006
- Accept: "application/json",
3007
- "Accept-Language": opts.extra?.["Accept-Language"] || opts.acceptLanguage || getSystemAcceptLanguage(),
3008
- "Accept-Encoding": "gzip, deflate, br, zstd",
3009
- "Content-Type": "application/json",
3010
- Cookie: opts.cookie,
3011
- Origin: qwenOrigin(),
3012
- Referer: opts.extra?.Referer ?? (opts.chatSessionId ? qwenUrl(`/c/${encodeURIComponent(opts.chatSessionId)}`) : qwenUrl("/")),
3013
- "Sec-Fetch-Dest": "empty",
3014
- "Sec-Fetch-Mode": "cors",
3015
- "Sec-Fetch-Site": "same-origin",
3016
- Connection: "keep-alive",
3017
- "User-Agent": opts.userAgent || getDefaultQwenUserAgent(),
3018
- "X-Request-Id": uuidv4(),
3019
- "bx-v": opts.bxV || "2.5.37",
3020
- source: "web",
3021
- version: opts.version || getQwenWebVersion(),
3022
- timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
3023
- Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
3024
- // Use the real browser client-hints when captured (anti-hardcoded); fall
3025
- // back to the static fingerprint otherwise.
3026
- "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
3027
- "sec-ch-ua-mobile": opts.secChUaMobile || "?0",
3028
- "sec-ch-ua-platform": opts.secChUaPlatform || '"Windows"'
3029
- };
3030
- const tokenMatch = opts.cookie?.match(/(?:^|;\s*)token=([^;]+)/);
3031
- if (tokenMatch && !headers["Authorization"] && !headers["authorization"]) {
3032
- const bearer = decodeURIComponent(tokenMatch[1].trim());
3033
- if (bearer && bearer !== '""' && bearer !== "null") {
3034
- headers["Authorization"] = `Bearer ${bearer}`;
3035
- }
3036
- }
3037
- if (config.qwen.sendBxUa) {
3038
- if (opts.bxUa) headers["bx-ua"] = opts.bxUa;
3039
- if (opts.bxUmidtoken) headers["bx-umidtoken"] = opts.bxUmidtoken;
3040
- }
3041
- return headers;
3042
- }
3043
- var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
3044
- var init_qwen_headers = __esm({
3045
- "src/services/qwen-headers.ts"() {
3046
- "use strict";
3047
- init_qwen_url();
3048
- init_config();
3049
- init_fingerprint();
3050
- init_timezone();
3051
- dynamicWebVersion = null;
3052
- QWEN_WEB_VERSION = config.qwen.webVersion;
3053
- DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
3054
- QWEN_TIMEZONE_HEADER = (/* @__PURE__ */ new Date()).toString().split(" (")[0];
3055
- }
3056
- });
3057
-
3058
3421
  // src/utils/jwt.ts
3059
3422
  var jwt_exports = {};
3060
3423
  __export(jwt_exports, {
@@ -3662,11 +4025,12 @@ async function getOrLaunchSharedBrowser(browserType = "chromium", headless = con
3662
4025
  async function clearVisibleChallenge(page) {
3663
4026
  if (page.isClosed()) return;
3664
4027
  if (config.captcha.enabled) {
3665
- await solveBaxiaCaptcha(page, {
4028
+ await solveVisibleCaptcha(page, {
3666
4029
  waitForMs: 0,
3667
4030
  maxAttempts: config.captcha.maxAttempts,
3668
4031
  retryDelayMs: config.captcha.retryDelayMs,
3669
- settleMs: config.captcha.settleMs
4032
+ settleMs: config.captcha.settleMs,
4033
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
3670
4034
  }).catch(() => false);
3671
4035
  }
3672
4036
  try {
@@ -4279,6 +4643,7 @@ async function getBasicHeaders(accountId) {
4279
4643
  mobile = data.mobile ? "?1" : "?0";
4280
4644
  }
4281
4645
  let bundleVersion = null;
4646
+ let baxiaVersion = null;
4282
4647
  try {
4283
4648
  const el = document.querySelector('link[href*="qwen-chat-fe"], script[src*="qwen-chat-fe"], img[src*="qwen-chat-fe"]');
4284
4649
  const src = el ? el.getAttribute("href") || el.getAttribute("src") || "" : "";
@@ -4286,7 +4651,14 @@ async function getBasicHeaders(accountId) {
4286
4651
  if (match) bundleVersion = match[1];
4287
4652
  } catch {
4288
4653
  }
4289
- 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 };
4290
4662
  }),
4291
4663
  config.timeouts.page,
4292
4664
  `User-agent lookup timed out for ${accountId}`
@@ -4295,6 +4667,9 @@ async function getBasicHeaders(accountId) {
4295
4667
  if (nav.bundleVersion) {
4296
4668
  updateQwenWebVersion(nav.bundleVersion);
4297
4669
  }
4670
+ if (nav.baxiaVersion) {
4671
+ updateQwenBxVersion(nav.baxiaVersion);
4672
+ }
4298
4673
  cachedUserAgents.set(accountId, userAgent);
4299
4674
  const hints = getHeaderCache(accountId).headers;
4300
4675
  clientHints = {
@@ -4326,7 +4701,7 @@ async function getBasicHeaders(accountId) {
4326
4701
  markAccountHeadersReady(accountId);
4327
4702
  const bxUa2 = cache.headers["bx-ua"];
4328
4703
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4329
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4704
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4330
4705
  const cookie2 = await getCookies(accountId);
4331
4706
  return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
4332
4707
  }
@@ -4337,7 +4712,7 @@ async function getBasicHeaders(accountId) {
4337
4712
  cookieCaches.set(accountId, { cookie: cookie2, timestamp: Date.now() });
4338
4713
  const bxUa2 = cache.headers["bx-ua"];
4339
4714
  const bxUmidtoken2 = cache.headers["bx-umidtoken"];
4340
- const bxV2 = cache.headers["bx-v"] || "2.5.37";
4715
+ const bxV2 = cache.headers["bx-v"] || getQwenBxVersion();
4341
4716
  cache.lastRefresh = Date.now();
4342
4717
  markAccountHeadersReady(accountId);
4343
4718
  console.log(
@@ -4412,6 +4787,9 @@ async function getBasicHeaders(accountId) {
4412
4787
  if (persisted.version) {
4413
4788
  updateQwenWebVersion(persisted.version);
4414
4789
  }
4790
+ if (persisted.bxV) {
4791
+ updateQwenBxVersion(persisted.bxV);
4792
+ }
4415
4793
  cache.lastRefresh = persisted.capturedAt;
4416
4794
  markAccountHeadersReady(accountId);
4417
4795
  }
@@ -4439,7 +4817,7 @@ async function getBasicHeaders(accountId) {
4439
4817
  markAccountHeadersReady(accountId);
4440
4818
  const bxUa = cache.headers["bx-ua"];
4441
4819
  const bxUmidtoken = cache.headers["bx-umidtoken"];
4442
- const bxV = cache.headers["bx-v"] || "2.5.37";
4820
+ const bxV = cache.headers["bx-v"] || getQwenBxVersion();
4443
4821
  cookieCaches.delete(accountId);
4444
4822
  const cookie = await getCookies(accountId, true);
4445
4823
  if (cache.headers) {
@@ -4684,6 +5062,9 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4684
5062
  if (persisted.version) {
4685
5063
  updateQwenWebVersion(persisted.version);
4686
5064
  }
5065
+ if (persisted.bxV) {
5066
+ updateQwenBxVersion(persisted.bxV);
5067
+ }
4687
5068
  cache.lastRefresh = persisted.capturedAt;
4688
5069
  markAccountHeadersReady(account.id);
4689
5070
  restoredFromDb = true;
@@ -5033,7 +5414,7 @@ async function startEmailOtpLogin(email, options = {}) {
5033
5414
  {
5034
5415
  email: normalizedEmail,
5035
5416
  requestId,
5036
- version: getQwenWebVersion() || "0.3.26",
5417
+ version: getQwenWebVersion(),
5037
5418
  timezone: formatAsciiTimezone()
5038
5419
  }
5039
5420
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5128,7 +5509,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
5128
5509
  email: session.email,
5129
5510
  code: cleanCode,
5130
5511
  requestId,
5131
- version: getQwenWebVersion() || "0.3.26",
5512
+ version: getQwenWebVersion(),
5132
5513
  timezone: formatAsciiTimezone()
5133
5514
  }
5134
5515
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
@@ -5175,8 +5556,8 @@ async function verifyEmailOtpLogin(sessionId, code) {
5175
5556
  saveAuthSession2(session.accountId, {
5176
5557
  cookie: `token=${accessToken}`,
5177
5558
  userAgent: session.fingerprint.userAgent,
5178
- bxV: "2.5.37",
5179
- version: getQwenWebVersion() || "0.3.26",
5559
+ bxV: getQwenBxVersion(),
5560
+ version: getQwenWebVersion(),
5180
5561
  userId: userId || void 0,
5181
5562
  tokenExpiresAt: exp || void 0,
5182
5563
  refreshToken: refreshToken || void 0,
@@ -5286,7 +5667,7 @@ async function loginToQwen(accountId, email, password) {
5286
5667
  saveAuthSession2(accountId, {
5287
5668
  cookie: cookieStr,
5288
5669
  userAgent: cache.headers["user-agent"] || "",
5289
- bxV: cache.headers["bx-v"] || "2.5.37",
5670
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5290
5671
  bxUa: cache.headers["bx-ua"] || "",
5291
5672
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5292
5673
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5358,7 +5739,7 @@ async function loginToQwen(accountId, email, password) {
5358
5739
  saveAuthSession2(accountId, {
5359
5740
  cookie: cookieStr,
5360
5741
  userAgent: cache.headers["user-agent"] || "",
5361
- bxV: cache.headers["bx-v"] || "2.5.37",
5742
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
5362
5743
  bxUa: cache.headers["bx-ua"] || "",
5363
5744
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
5364
5745
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -5429,7 +5810,7 @@ async function loginViaApi(page, email, password) {
5429
5810
  }
5430
5811
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
5431
5812
  const requestId = crypto3.randomUUID();
5432
- const webVersion = getQwenWebVersion() || "0.3.26";
5813
+ const webVersion = getQwenWebVersion();
5433
5814
  const evalRes = await page.evaluate(
5434
5815
  async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
5435
5816
  try {
@@ -5601,6 +5982,15 @@ async function loginViaUi(page, email, password) {
5601
5982
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
5602
5983
  return { success: true };
5603
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
+ }
5604
5994
  const emailSelector = [
5605
5995
  'input[type="email"]',
5606
5996
  'input[name="email"]',
@@ -5756,10 +6146,11 @@ async function loginViaUi(page, email, password) {
5756
6146
  while (Date.now() < postSubmitDeadline) {
5757
6147
  if (page.isClosed()) break;
5758
6148
  if (config.captcha.enabled) {
5759
- await solveBaxiaCaptcha(page, {
6149
+ await solveVisibleCaptcha(page, {
5760
6150
  waitForMs: 1500,
5761
6151
  maxAttempts: config.captcha.maxAttempts,
5762
- retryDelayMs: config.captcha.retryDelayMs
6152
+ retryDelayMs: config.captcha.retryDelayMs,
6153
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
5763
6154
  }).catch(() => {
5764
6155
  });
5765
6156
  }
@@ -5882,7 +6273,7 @@ async function refreshAccountToken(accountId) {
5882
6273
  }
5883
6274
  if (refreshToken) {
5884
6275
  try {
5885
- const webVersion = getQwenWebVersion() || "0.3.26";
6276
+ const webVersion = getQwenWebVersion();
5886
6277
  const refreshResult = await page.evaluate(
5887
6278
  async ({ rTok, tz, version }) => {
5888
6279
  try {
@@ -5947,7 +6338,7 @@ async function refreshAccountToken(accountId) {
5947
6338
  saveAuthSession2(accountId, {
5948
6339
  cookie: cookieStr,
5949
6340
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5950
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6341
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5951
6342
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5952
6343
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5953
6344
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -5982,7 +6373,7 @@ async function refreshAccountToken(accountId) {
5982
6373
  saveAuthSession2(accountId, {
5983
6374
  cookie: cookieStr,
5984
6375
  userAgent: cache.headers["user-agent"] || session?.userAgent || "",
5985
- bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
6376
+ bxV: cache.headers["bx-v"] || session?.bxV || getQwenBxVersion(),
5986
6377
  bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
5987
6378
  bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
5988
6379
  secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
@@ -6130,7 +6521,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6130
6521
  cookie: reqHeaders["cookie"] || "",
6131
6522
  "bx-ua": reqHeaders["bx-ua"] || "",
6132
6523
  "bx-umidtoken": reqHeaders["bx-umidtoken"] || "",
6133
- "bx-v": reqHeaders["bx-v"] || "2.5.37",
6524
+ "bx-v": reqHeaders["bx-v"] || getQwenBxVersion(),
6134
6525
  "user-agent": reqHeaders["user-agent"] || "",
6135
6526
  "x-request-id": reqHeaders["x-request-id"] || "",
6136
6527
  "version": reqHeaders["version"] || "",
@@ -6162,6 +6553,9 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
6162
6553
  if (capturedHeaders["version"]) {
6163
6554
  updateQwenWebVersion(capturedHeaders["version"]);
6164
6555
  }
6556
+ if (capturedHeaders["bx-v"]) {
6557
+ updateQwenBxVersion(capturedHeaders["bx-v"]);
6558
+ }
6165
6559
  markAccountHeadersReady(accountId);
6166
6560
  cache.lastRefresh = Date.now();
6167
6561
  cookieCaches.delete(accountId);
@@ -6619,7 +7013,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
6619
7013
  saveAuthSession2(accountId, {
6620
7014
  cookie: cookieStr,
6621
7015
  userAgent: cache.headers["user-agent"] || "",
6622
- bxV: cache.headers["bx-v"] || "2.5.37",
7016
+ bxV: cache.headers["bx-v"] || getQwenBxVersion(),
6623
7017
  bxUa: cache.headers["bx-ua"] || "",
6624
7018
  bxUmidtoken: cache.headers["bx-umidtoken"] || "",
6625
7019
  secChUa: cache.headers["sec-ch-ua"] || void 0,
@@ -7383,13 +7777,13 @@ async function getBasicHeaders2(accountId) {
7383
7777
  return {
7384
7778
  cookie: "token=mock",
7385
7779
  userAgent: "mock",
7386
- bxV: "2.5.37",
7780
+ bxV: getQwenBxVersion(),
7387
7781
  bxUa: "mock-bx-ua",
7388
7782
  bxUmidtoken: "mock-bx-umidtoken",
7389
7783
  secChUa: "",
7390
7784
  secChUaMobile: "?0",
7391
7785
  secChUaPlatform: "",
7392
- version: "0.3.26"
7786
+ version: getQwenWebVersion()
7393
7787
  };
7394
7788
  }
7395
7789
  const resolvedAccountId = accountId ?? loadAccounts()[0]?.id;
@@ -7450,6 +7844,7 @@ var init_auth_playwright = __esm({
7450
7844
  init_errors();
7451
7845
  init_accounts();
7452
7846
  init_config();
7847
+ init_qwen_headers();
7453
7848
  init_playwright();
7454
7849
  init_jwt();
7455
7850
  }
@@ -7878,9 +8273,10 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
7878
8273
  const solverOptions = {
7879
8274
  maxAttempts: config.captcha.maxAttempts,
7880
8275
  retryDelayMs: config.captcha.retryDelayMs,
7881
- settleMs: config.captcha.settleMs
8276
+ settleMs: config.captcha.settleMs,
8277
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
7882
8278
  };
7883
- if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
8279
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
7884
8280
  return true;
7885
8281
  }
7886
8282
  logBaxiaCaptcha(
@@ -7890,7 +8286,7 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
7890
8286
  );
7891
8287
  await gotoBestEffort(page, challengeUrl ?? qwenUrl("/"));
7892
8288
  try {
7893
- return await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs });
8289
+ return await solveVisibleCaptcha(page, { ...solverOptions, waitForMs });
7894
8290
  } finally {
7895
8291
  if (page.url().includes(CHALLENGE_PATH_MARKER)) {
7896
8292
  await gotoBestEffort(page, qwenUrl("/"));
@@ -7928,9 +8324,10 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
7928
8324
  const solverOptions = {
7929
8325
  maxAttempts: config.captcha.maxAttempts,
7930
8326
  retryDelayMs: config.captcha.retryDelayMs,
7931
- settleMs: config.captcha.settleMs
8327
+ settleMs: config.captcha.settleMs,
8328
+ sliderTimeoutMs: config.captcha.sliderTimeoutMs
7932
8329
  };
7933
- if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
8330
+ if (await solveVisibleCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
7934
8331
  return true;
7935
8332
  }
7936
8333
  const ctx = typeof page.context === "function" ? page.context() : null;
@@ -8447,7 +8844,7 @@ var init_media_generation = __esm({
8447
8844
 
8448
8845
  // src/services/qwen-chat-pool.ts
8449
8846
  import crypto4 from "crypto";
8450
- async function createQwenChatSession(headers, model, accountId, chatMode = "thread") {
8847
+ async function createQwenChatSession(headers, model, accountId, chatMode = "thread", captchaRecoveryAttempts = 1) {
8451
8848
  if (isAuthMockEnabled()) {
8452
8849
  return process.env.TEST_SESSION_ID || "mock-session";
8453
8850
  }
@@ -8475,16 +8872,29 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
8475
8872
  if (!chatId || typeof chatId !== "string") {
8476
8873
  const rawLower = raw.toLowerCase();
8477
8874
  if (looksLikeAntiBotChallengeText(raw)) {
8478
- try {
8479
- const { recoverBaxiaCaptcha: recoverBaxiaCaptcha2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
8480
- const solved = await recoverBaxiaCaptcha2(accountId, "createQwenChatSession", {
8481
- challengeBody: raw
8482
- });
8483
- if (solved) {
8484
- 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 {
8485
8891
  }
8486
- } catch {
8487
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
+ );
8488
8898
  }
8489
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");
8490
8900
  if (isUnauthorized) {
@@ -8661,6 +9071,20 @@ async function refillQwenChatPool(headers, model, accountId) {
8661
9071
  console.warn(
8662
9072
  `[WarmPool] Account ${accountId || "global"} rate-limited during chat creation. Marked for cooldown.`
8663
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
+ }
8664
9088
  }
8665
9089
  }
8666
9090
  if (isToolcallDebugEnabled()) {
@@ -8696,6 +9120,7 @@ var init_qwen_chat_pool = __esm({
8696
9120
  init_config();
8697
9121
  init_logger();
8698
9122
  init_account_manager();
9123
+ init_waf_isolation();
8699
9124
  init_model_alias();
8700
9125
  init_qwen_url();
8701
9126
  init_qwen_errors();
@@ -9265,7 +9690,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9265
9690
  browserHeaders["Content-Type"] = "application/json";
9266
9691
  }
9267
9692
  const evaluateRequest = (page) => page.evaluate(
9268
- 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 }) => {
9269
9694
  const controller = new AbortController();
9270
9695
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
9271
9696
  try {
@@ -9287,7 +9712,7 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9287
9712
  headers: {
9288
9713
  accept: "application/json, text/plain, */*",
9289
9714
  source: "web",
9290
- version: "0.3.11",
9715
+ version,
9291
9716
  "x-request-origin": "https://chat.qwen.ai",
9292
9717
  timezone: tz,
9293
9718
  authorization: `Bearer ${rTok}`
@@ -9335,7 +9760,8 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
9335
9760
  body,
9336
9761
  referrer: options.referrer,
9337
9762
  timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
9338
- tz: formatAsciiTimezone()
9763
+ tz: formatAsciiTimezone(),
9764
+ version: getQwenWebVersion()
9339
9765
  }
9340
9766
  );
9341
9767
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -10292,7 +10718,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
10292
10718
  }
10293
10719
  const requestHeaders = {
10294
10720
  source: "web",
10295
- version: getQwenWebVersion() || "0.3.26",
10721
+ version: getQwenWebVersion(),
10296
10722
  timezone: formatAsciiTimezone(),
10297
10723
  "x-request-id": crypto5.randomUUID(),
10298
10724
  Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
@@ -10336,7 +10762,7 @@ async function fetchRemoteQwenChats(accountId) {
10336
10762
  return [];
10337
10763
  }
10338
10764
  const requestHeaders = {
10339
- version: getQwenWebVersion() || "0.3.26",
10765
+ version: getQwenWebVersion(),
10340
10766
  timezone: formatAsciiTimezone(),
10341
10767
  "x-request-id": crypto5.randomUUID(),
10342
10768
  Referer: qwenUrl("/settings/chats")