qwenproxy-cli 1.6.9 → 1.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clean-cache.js +199 -55
- package/dist/delete-chats.js +269 -78
- package/dist/index.js +290 -88
- package/dist/login.js +203 -67
- package/dist/reset-cooldowns.js +199 -55
- package/package.json +2 -2
- package/src/api/server.ts +8 -4
- package/src/core/version.ts +1 -1
- package/src/core/watchdog.ts +4 -3
- package/src/routes/chat/account.ts +12 -0
- package/src/routes/chat/streaming.ts +18 -2
- package/src/services/captcha-coordinator.ts +36 -8
- package/src/services/chat-cleanup.ts +11 -2
- package/src/services/playwright.ts +244 -74
- package/src/services/qwen.ts +29 -3
package/dist/index.js
CHANGED
|
@@ -4629,7 +4629,19 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
4629
4629
|
if (localStorage.getItem("qwen_token_logged_out_marker")) {
|
|
4630
4630
|
return false;
|
|
4631
4631
|
}
|
|
4632
|
-
const
|
|
4632
|
+
const lsToken = (localStorage.getItem("token") || "").trim();
|
|
4633
|
+
if (lsToken && !/(?:^|;\s*)token=/.test(document.cookie || "")) {
|
|
4634
|
+
try {
|
|
4635
|
+
document.cookie = `token=${encodeURIComponent(lsToken)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4636
|
+
} catch {
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
const authHeaders = lsToken ? { Authorization: `Bearer ${lsToken}` } : {};
|
|
4640
|
+
const res = await fetch("/api/v1/auths/", {
|
|
4641
|
+
method: "GET",
|
|
4642
|
+
headers: authHeaders,
|
|
4643
|
+
credentials: "include"
|
|
4644
|
+
});
|
|
4633
4645
|
if (res.status !== 200) return false;
|
|
4634
4646
|
const json = await res.json().catch(() => null);
|
|
4635
4647
|
if (!json) return false;
|
|
@@ -4647,6 +4659,7 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
4647
4659
|
try {
|
|
4648
4660
|
const settingsRes = await fetch("/api/v2/users/user/settings", {
|
|
4649
4661
|
method: "GET",
|
|
4662
|
+
headers: authHeaders,
|
|
4650
4663
|
credentials: "include",
|
|
4651
4664
|
signal: AbortSignal.timeout(3e3)
|
|
4652
4665
|
});
|
|
@@ -5434,14 +5447,15 @@ async function getBasicHeaders(accountId) {
|
|
|
5434
5447
|
`\u26A0\uFE0F [Playwright] Header refresh failed for ${accountId}; retaining the previous valid cache: ${getErrorMessage(error)}`
|
|
5435
5448
|
);
|
|
5436
5449
|
}
|
|
5437
|
-
} else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache3.refreshInProgress
|
|
5438
|
-
|
|
5450
|
+
} else if (hadUsableHeaders && headersAge > HEADER_CACHE_TTL * HEADER_REFRESH_THRESHOLD && !cache3.refreshInProgress && // Header capture types into the chat input and sends a probe message on
|
|
5451
|
+
// the account's page. Doing that while the account is serving a stream
|
|
5452
|
+
// would mutate the live conversation (and can navigate the SPA), so the
|
|
5453
|
+
// proactive refresh waits until the account has no active lease.
|
|
5454
|
+
!hasActiveAccountLease(accountId)) {
|
|
5439
5455
|
refreshHeadersInternal(accountId).catch((error) => {
|
|
5440
5456
|
console.warn(
|
|
5441
5457
|
`\u26A0\uFE0F [Playwright] Background header refresh failed for ${accountId}: ${getErrorMessage(error)}`
|
|
5442
5458
|
);
|
|
5443
|
-
}).finally(() => {
|
|
5444
|
-
cache3.refreshInProgress = false;
|
|
5445
5459
|
});
|
|
5446
5460
|
}
|
|
5447
5461
|
if (!hasRequiredQwenHeaders(cache3.headers)) {
|
|
@@ -5579,8 +5593,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5579
5593
|
if (accountPages.has(account.id)) {
|
|
5580
5594
|
return;
|
|
5581
5595
|
}
|
|
5582
|
-
|
|
5583
|
-
|
|
5596
|
+
if (config.playwright.maxActiveContexts > 0) {
|
|
5597
|
+
await evictIdlePlaywrightContextsToLimit(
|
|
5598
|
+
Math.max(1, config.playwright.maxActiveContexts - 1)
|
|
5599
|
+
).catch(() => {
|
|
5600
|
+
});
|
|
5601
|
+
}
|
|
5584
5602
|
const profilePath = getAccountProfilePath(account.id);
|
|
5585
5603
|
const fingerprint = getFingerprintProfile(account.id);
|
|
5586
5604
|
const { engine, channel } = resolveBrowserEngine(browserType);
|
|
@@ -5805,8 +5823,15 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5805
5823
|
const hasAuthCookie = cookies.some(
|
|
5806
5824
|
(c) => c.name.toLowerCase().includes("token") || c.name.toLowerCase().includes("session")
|
|
5807
5825
|
);
|
|
5826
|
+
let initialLoginAttempted = false;
|
|
5808
5827
|
if (!hasAuthCookie && account.email && account.password) {
|
|
5809
|
-
|
|
5828
|
+
initialLoginAttempted = true;
|
|
5829
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
5830
|
+
if (!loggedIn) {
|
|
5831
|
+
throw new Error(
|
|
5832
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
5833
|
+
);
|
|
5834
|
+
}
|
|
5810
5835
|
}
|
|
5811
5836
|
try {
|
|
5812
5837
|
await acctPage.goto(qwenUrl("/"), {
|
|
@@ -5816,13 +5841,21 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5816
5841
|
await sleep2(1500);
|
|
5817
5842
|
const currentUrl = acctPage.url();
|
|
5818
5843
|
const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
|
|
5819
|
-
if (isAuthUrl && account.email && account.password) {
|
|
5844
|
+
if (isAuthUrl && !initialLoginAttempted && account.email && account.password) {
|
|
5820
5845
|
console.log(
|
|
5821
5846
|
`[Playwright] Initial login needed for ${maskEmail(account.email)}, authenticating...`
|
|
5822
5847
|
);
|
|
5823
|
-
await loginToQwen(account.id, account.email, account.password);
|
|
5848
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
5849
|
+
if (!loggedIn) {
|
|
5850
|
+
throw new Error(
|
|
5851
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
5852
|
+
);
|
|
5853
|
+
}
|
|
5824
5854
|
}
|
|
5825
5855
|
} catch (err) {
|
|
5856
|
+
if (err?.message?.includes("Authentication failed for account")) {
|
|
5857
|
+
throw err;
|
|
5858
|
+
}
|
|
5826
5859
|
console.warn(
|
|
5827
5860
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
5828
5861
|
);
|
|
@@ -5834,7 +5867,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5834
5867
|
if (!acctPage.isClosed()) {
|
|
5835
5868
|
try {
|
|
5836
5869
|
const currentUrl = new URL(acctPage.url());
|
|
5837
|
-
if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname
|
|
5870
|
+
if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
|
|
5838
5871
|
await acctPage.goto(qwenUrl("/"), {
|
|
5839
5872
|
waitUntil: "domcontentloaded",
|
|
5840
5873
|
timeout: config.timeouts.navigation
|
|
@@ -5852,6 +5885,8 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5852
5885
|
} finally {
|
|
5853
5886
|
release();
|
|
5854
5887
|
}
|
|
5888
|
+
await evictIdlePlaywrightContextsToLimit().catch(() => {
|
|
5889
|
+
});
|
|
5855
5890
|
})();
|
|
5856
5891
|
try {
|
|
5857
5892
|
await initPromise;
|
|
@@ -6371,10 +6406,30 @@ async function loginToQwen(accountId, email, password) {
|
|
|
6371
6406
|
const uiResult = await loginViaUi(page, email, password);
|
|
6372
6407
|
if (uiResult.success) {
|
|
6373
6408
|
cookieCaches.delete(accountId);
|
|
6374
|
-
await saveStorageState(page.context(), accountId);
|
|
6375
6409
|
try {
|
|
6376
|
-
|
|
6377
|
-
|
|
6410
|
+
let liveCookies = await page.context().cookies();
|
|
6411
|
+
let tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
6412
|
+
if (!tokenCookie) {
|
|
6413
|
+
const lsTok = uiResult.token || await page.evaluate(() => (localStorage.getItem("token") || "").trim()).catch(() => "");
|
|
6414
|
+
if (lsTok) {
|
|
6415
|
+
await page.context().addCookies([
|
|
6416
|
+
{
|
|
6417
|
+
name: "token",
|
|
6418
|
+
value: lsTok,
|
|
6419
|
+
domain: ".qwen.ai",
|
|
6420
|
+
path: "/",
|
|
6421
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
6422
|
+
httpOnly: false,
|
|
6423
|
+
secure: true,
|
|
6424
|
+
sameSite: "Lax"
|
|
6425
|
+
}
|
|
6426
|
+
]).catch(() => {
|
|
6427
|
+
});
|
|
6428
|
+
liveCookies = await page.context().cookies();
|
|
6429
|
+
tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
6430
|
+
}
|
|
6431
|
+
}
|
|
6432
|
+
await saveStorageState(page.context(), accountId);
|
|
6378
6433
|
if (tokenCookie) {
|
|
6379
6434
|
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
6380
6435
|
const cache3 = getHeaderCache(accountId);
|
|
@@ -6383,7 +6438,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
6383
6438
|
markAccountHeadersReady(accountId);
|
|
6384
6439
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6385
6440
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
6386
|
-
let rTok = uiResult.refreshToken;
|
|
6441
|
+
let rTok = uiResult.refreshToken || liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
6387
6442
|
if (!rTok) {
|
|
6388
6443
|
try {
|
|
6389
6444
|
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
@@ -6464,8 +6519,9 @@ async function loginViaApi(page, email, password) {
|
|
|
6464
6519
|
}
|
|
6465
6520
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
6466
6521
|
const requestId = crypto5.randomUUID();
|
|
6522
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
6467
6523
|
const evalRes = await page.evaluate(
|
|
6468
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
6524
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
|
|
6469
6525
|
try {
|
|
6470
6526
|
const response = await fetch(signinUrl2, {
|
|
6471
6527
|
method: "POST",
|
|
@@ -6475,7 +6531,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6475
6531
|
accept: "application/json, text/plain, */*",
|
|
6476
6532
|
"content-type": "application/json",
|
|
6477
6533
|
source: "web",
|
|
6478
|
-
version
|
|
6534
|
+
version,
|
|
6479
6535
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6480
6536
|
timezone: tz,
|
|
6481
6537
|
"x-request-id": requestId2
|
|
@@ -6488,7 +6544,14 @@ async function loginViaApi(page, email, password) {
|
|
|
6488
6544
|
return { ok: false, error: e.message };
|
|
6489
6545
|
}
|
|
6490
6546
|
},
|
|
6491
|
-
{
|
|
6547
|
+
{
|
|
6548
|
+
email,
|
|
6549
|
+
password: hashedPassword,
|
|
6550
|
+
signinUrl,
|
|
6551
|
+
requestId,
|
|
6552
|
+
tz: formatAsciiTimezone(),
|
|
6553
|
+
version: webVersion
|
|
6554
|
+
}
|
|
6492
6555
|
).catch(() => null);
|
|
6493
6556
|
let data = evalRes?.data;
|
|
6494
6557
|
let signinSuccess = Boolean(
|
|
@@ -6497,7 +6560,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6497
6560
|
if (!signinSuccess && !data?.data?.code) {
|
|
6498
6561
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
6499
6562
|
const fallbackRes = await page.evaluate(
|
|
6500
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
6563
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz, version }) => {
|
|
6501
6564
|
try {
|
|
6502
6565
|
const response = await fetch(fallbackUrl2, {
|
|
6503
6566
|
method: "POST",
|
|
@@ -6507,7 +6570,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6507
6570
|
accept: "application/json, text/plain, */*",
|
|
6508
6571
|
"content-type": "application/json",
|
|
6509
6572
|
source: "web",
|
|
6510
|
-
version
|
|
6573
|
+
version,
|
|
6511
6574
|
timezone: tz,
|
|
6512
6575
|
"x-request-id": requestId2
|
|
6513
6576
|
},
|
|
@@ -6519,7 +6582,14 @@ async function loginViaApi(page, email, password) {
|
|
|
6519
6582
|
return { ok: false, error: e.message };
|
|
6520
6583
|
}
|
|
6521
6584
|
},
|
|
6522
|
-
{
|
|
6585
|
+
{
|
|
6586
|
+
email,
|
|
6587
|
+
password: hashedPassword,
|
|
6588
|
+
fallbackUrl,
|
|
6589
|
+
requestId,
|
|
6590
|
+
tz: formatAsciiTimezone(),
|
|
6591
|
+
version: webVersion
|
|
6592
|
+
}
|
|
6523
6593
|
).catch(() => null);
|
|
6524
6594
|
if (fallbackRes?.data) {
|
|
6525
6595
|
data = fallbackRes.data;
|
|
@@ -6744,28 +6814,34 @@ async function loginViaUi(page, email, password) {
|
|
|
6744
6814
|
await sleep2(500);
|
|
6745
6815
|
const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
|
|
6746
6816
|
const submitButton = page.locator(submitSelector).first();
|
|
6747
|
-
|
|
6748
|
-
try {
|
|
6749
|
-
const btn = document.querySelector(sel);
|
|
6750
|
-
if (btn) {
|
|
6751
|
-
btn.removeAttribute("disabled");
|
|
6752
|
-
btn.classList.remove("ant-btn-disabled");
|
|
6753
|
-
btn.click();
|
|
6754
|
-
}
|
|
6755
|
-
} catch {
|
|
6756
|
-
}
|
|
6757
|
-
}, submitSelector).catch(() => {
|
|
6758
|
-
});
|
|
6817
|
+
let submitted = false;
|
|
6759
6818
|
try {
|
|
6760
6819
|
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
6761
|
-
await submitButton.click({
|
|
6820
|
+
await submitButton.click({ timeout: 2e3 }).catch(() => {
|
|
6762
6821
|
});
|
|
6822
|
+
submitted = true;
|
|
6763
6823
|
}
|
|
6764
6824
|
} catch {
|
|
6765
6825
|
}
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6826
|
+
if (!submitted) {
|
|
6827
|
+
await page.evaluate(() => {
|
|
6828
|
+
try {
|
|
6829
|
+
const btn = document.querySelector(
|
|
6830
|
+
'button.qwenchat-auth-pc-submit-button, button[type="submit"]'
|
|
6831
|
+
);
|
|
6832
|
+
if (btn) {
|
|
6833
|
+
btn.removeAttribute("disabled");
|
|
6834
|
+
btn.classList.remove("ant-btn-disabled");
|
|
6835
|
+
btn.click();
|
|
6836
|
+
}
|
|
6837
|
+
} catch {
|
|
6838
|
+
}
|
|
6839
|
+
}).catch(() => {
|
|
6840
|
+
});
|
|
6841
|
+
const passInput = page.locator(passwordSelector).first();
|
|
6842
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6769
6845
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
6770
6846
|
while (Date.now() < postSubmitDeadline) {
|
|
6771
6847
|
if (page.isClosed()) break;
|
|
@@ -6783,9 +6859,12 @@ async function loginViaUi(page, email, password) {
|
|
|
6783
6859
|
".ant-form-item-explain-error",
|
|
6784
6860
|
".ant-message-error",
|
|
6785
6861
|
".ant-message-notice",
|
|
6862
|
+
".ant-message-custom-content",
|
|
6863
|
+
".ant-notification-notice",
|
|
6786
6864
|
'[role="alert"]',
|
|
6787
6865
|
".qwenchat-auth-error",
|
|
6788
|
-
".auth-error-message"
|
|
6866
|
+
".auth-error-message",
|
|
6867
|
+
".qwenchat-toast"
|
|
6789
6868
|
].join(", ");
|
|
6790
6869
|
const errorEl = page.locator(errorSelector).first();
|
|
6791
6870
|
if (await errorEl.isVisible().catch(() => false)) {
|
|
@@ -6799,7 +6878,15 @@ async function loginViaUi(page, email, password) {
|
|
|
6799
6878
|
};
|
|
6800
6879
|
}
|
|
6801
6880
|
}
|
|
6802
|
-
const codeSelector =
|
|
6881
|
+
const codeSelector = [
|
|
6882
|
+
'input[placeholder*="code" i]',
|
|
6883
|
+
'input[placeholder*="c\xF3digo" i]',
|
|
6884
|
+
'input[name*="code" i]',
|
|
6885
|
+
".qwenchat-verification-code-input",
|
|
6886
|
+
".qwenchat-verification-code-input-cell",
|
|
6887
|
+
'[aria-label*="Verification code" i]',
|
|
6888
|
+
'[aria-label*="c\xF3digo de verifica\xE7\xE3o" i]'
|
|
6889
|
+
].join(", ");
|
|
6803
6890
|
const codeInput = page.locator(codeSelector).first();
|
|
6804
6891
|
if (await codeInput.isVisible().catch(() => false)) {
|
|
6805
6892
|
return {
|
|
@@ -6809,17 +6896,45 @@ async function loginViaUi(page, email, password) {
|
|
|
6809
6896
|
}
|
|
6810
6897
|
try {
|
|
6811
6898
|
const liveCookies = await page.context().cookies();
|
|
6812
|
-
|
|
6813
|
-
|
|
6899
|
+
const rTokCookie = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
6900
|
+
const tokCookie = liveCookies.find((c) => c.name === "token")?.value;
|
|
6901
|
+
if (tokCookie) {
|
|
6902
|
+
return { success: true, token: tokCookie, refreshToken: rTokCookie };
|
|
6903
|
+
}
|
|
6904
|
+
const currentUrl = page.url();
|
|
6905
|
+
if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
|
|
6906
|
+
const lsTok = await page.evaluate(() => {
|
|
6907
|
+
const t = (localStorage.getItem("token") || "").trim();
|
|
6908
|
+
if (t) {
|
|
6909
|
+
try {
|
|
6910
|
+
document.cookie = `token=${encodeURIComponent(t)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
6911
|
+
} catch {
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6914
|
+
return t;
|
|
6915
|
+
}).catch(() => "");
|
|
6916
|
+
if (lsTok) {
|
|
6917
|
+
await page.context().addCookies([
|
|
6918
|
+
{
|
|
6919
|
+
name: "token",
|
|
6920
|
+
value: lsTok,
|
|
6921
|
+
domain: ".qwen.ai",
|
|
6922
|
+
path: "/",
|
|
6923
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
6924
|
+
httpOnly: false,
|
|
6925
|
+
secure: true,
|
|
6926
|
+
sameSite: "Lax"
|
|
6927
|
+
}
|
|
6928
|
+
]).catch(() => {
|
|
6929
|
+
});
|
|
6930
|
+
return { success: true, token: lsTok, refreshToken: rTokCookie };
|
|
6931
|
+
}
|
|
6932
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
6933
|
+
return { success: true, refreshToken: rTokCookie };
|
|
6934
|
+
}
|
|
6814
6935
|
}
|
|
6815
6936
|
} catch {
|
|
6816
6937
|
}
|
|
6817
|
-
const currentUrl = page.url();
|
|
6818
|
-
if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
|
|
6819
|
-
if (await isPageLoggedIn(page, 3e3)) {
|
|
6820
|
-
return { success: true };
|
|
6821
|
-
}
|
|
6822
|
-
}
|
|
6823
6938
|
await sleep2(1e3);
|
|
6824
6939
|
}
|
|
6825
6940
|
if (await isPageLoggedIn(page, 3e3)) {
|
|
@@ -6848,10 +6963,18 @@ async function refreshAccountToken(accountId) {
|
|
|
6848
6963
|
} catch {
|
|
6849
6964
|
}
|
|
6850
6965
|
}
|
|
6966
|
+
if (!refreshToken) {
|
|
6967
|
+
try {
|
|
6968
|
+
const liveCookies = await page.context().cookies();
|
|
6969
|
+
refreshToken = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
6970
|
+
} catch {
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6851
6973
|
if (refreshToken) {
|
|
6852
6974
|
try {
|
|
6975
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
6853
6976
|
const refreshResult = await page.evaluate(
|
|
6854
|
-
async ({ rTok, tz }) => {
|
|
6977
|
+
async ({ rTok, tz, version }) => {
|
|
6855
6978
|
try {
|
|
6856
6979
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6857
6980
|
method: "GET",
|
|
@@ -6859,7 +6982,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6859
6982
|
headers: {
|
|
6860
6983
|
accept: "application/json, text/plain, */*",
|
|
6861
6984
|
source: "web",
|
|
6862
|
-
version
|
|
6985
|
+
version,
|
|
6863
6986
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6864
6987
|
timezone: tz,
|
|
6865
6988
|
authorization: `Bearer ${rTok}`
|
|
@@ -6871,7 +6994,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6871
6994
|
return { ok: false, error: e.message };
|
|
6872
6995
|
}
|
|
6873
6996
|
},
|
|
6874
|
-
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
6997
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone(), version: webVersion }
|
|
6875
6998
|
);
|
|
6876
6999
|
const json = refreshResult?.data;
|
|
6877
7000
|
if (json && json.success === true && json.data) {
|
|
@@ -7532,19 +7655,26 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
7532
7655
|
await executeReauth();
|
|
7533
7656
|
} else {
|
|
7534
7657
|
try {
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7658
|
+
const currentUrl = typeof page.url === "function" ? page.url() : "";
|
|
7659
|
+
const needsNavigation = !currentUrl.startsWith(qwenOrigin()) || currentUrl.includes("/auth") || currentUrl.includes("/login") || currentUrl.includes("_____tmd_____");
|
|
7660
|
+
if (needsNavigation) {
|
|
7661
|
+
await page.goto(qwenUrl("/"), {
|
|
7662
|
+
waitUntil: "domcontentloaded",
|
|
7663
|
+
timeout: Math.min(
|
|
7664
|
+
config.timeouts.navigation,
|
|
7665
|
+
boundedTimeoutMs,
|
|
7666
|
+
SESSION_PROBE_NAVIGATION_TIMEOUT_MS
|
|
7667
|
+
)
|
|
7668
|
+
});
|
|
7669
|
+
await sleep2(2e3);
|
|
7670
|
+
}
|
|
7671
|
+
const url = typeof page.url === "function" ? page.url() : "";
|
|
7545
7672
|
const isAuthUrl = url.includes("auth") || url.includes("login");
|
|
7546
7673
|
if (isAuthUrl) {
|
|
7547
|
-
await
|
|
7674
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
7675
|
+
if (!refreshed.success) {
|
|
7676
|
+
await executeReauth();
|
|
7677
|
+
}
|
|
7548
7678
|
}
|
|
7549
7679
|
} catch (navErr) {
|
|
7550
7680
|
console.warn(
|
|
@@ -7601,7 +7731,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
7601
7731
|
if (capturedPage && !capturedPage.isClosed()) {
|
|
7602
7732
|
try {
|
|
7603
7733
|
const currentUrl = new URL(capturedPage.url());
|
|
7604
|
-
if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname
|
|
7734
|
+
if (currentUrl.origin !== qwenOrigin() || currentUrl.pathname.includes("/auth") || currentUrl.pathname.includes("/login") || currentUrl.pathname.includes("_____tmd_____")) {
|
|
7605
7735
|
await capturedPage.goto(qwenUrl("/"), {
|
|
7606
7736
|
waitUntil: "domcontentloaded",
|
|
7607
7737
|
timeout: Math.min(config.timeouts.navigation, boundedTimeoutMs)
|
|
@@ -7917,7 +8047,11 @@ function priorityOrderForEviction(accountIds) {
|
|
|
7917
8047
|
const ranked = getAccountsByPriority(accountIds.map((id) => ({ id })));
|
|
7918
8048
|
const rank = /* @__PURE__ */ new Map();
|
|
7919
8049
|
for (const [index, account] of ranked.entries()) {
|
|
7920
|
-
|
|
8050
|
+
if (getAccountCooldownInfo(account.id)) {
|
|
8051
|
+
rank.set(account.id, Number.MAX_SAFE_INTEGER);
|
|
8052
|
+
} else {
|
|
8053
|
+
rank.set(account.id, index);
|
|
8054
|
+
}
|
|
7921
8055
|
}
|
|
7922
8056
|
return orderContextsForEviction(
|
|
7923
8057
|
accountIds,
|
|
@@ -7954,14 +8088,15 @@ async function closeIdlePlaywrightAccounts(idleMs) {
|
|
|
7954
8088
|
}
|
|
7955
8089
|
return closed;
|
|
7956
8090
|
}
|
|
7957
|
-
async function evictIdlePlaywrightContextsToLimit() {
|
|
7958
|
-
const
|
|
7959
|
-
if (
|
|
8091
|
+
async function evictIdlePlaywrightContextsToLimit(targetLimit) {
|
|
8092
|
+
const configuredMax = config.playwright.maxActiveContexts;
|
|
8093
|
+
if (configuredMax <= 0) return 0;
|
|
8094
|
+
const max = targetLimit !== void 0 ? Math.max(0, targetLimit) : configuredMax;
|
|
7960
8095
|
if (accountPages.size <= max) return 0;
|
|
7961
8096
|
const candidates = priorityOrderForEviction(
|
|
7962
8097
|
Array.from(accountPages.keys()).filter((accountId) => {
|
|
7963
8098
|
if (isAccountServingStream(accountId)) return false;
|
|
7964
|
-
if (
|
|
8099
|
+
if (hasActiveAccountLease(accountId)) return false;
|
|
7965
8100
|
const mutex = accountMutexes.get(accountId);
|
|
7966
8101
|
return mutex?.isIdle();
|
|
7967
8102
|
})
|
|
@@ -7974,6 +8109,7 @@ async function evictIdlePlaywrightContextsToLimit() {
|
|
|
7974
8109
|
for (const candidate of candidates) {
|
|
7975
8110
|
if (accountPages.size <= max) break;
|
|
7976
8111
|
if (isAccountServingStream(candidate.accountId)) continue;
|
|
8112
|
+
if (hasActiveAccountLease(candidate.accountId)) continue;
|
|
7977
8113
|
const mutex = accountMutexes.get(candidate.accountId);
|
|
7978
8114
|
if (!mutex?.isIdle()) continue;
|
|
7979
8115
|
await closePlaywrightForAccount(candidate.accountId).catch((error) => {
|
|
@@ -8404,12 +8540,13 @@ var init_watchdog = __esm({
|
|
|
8404
8540
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
8405
8541
|
this.emit("recovery:ram:freed");
|
|
8406
8542
|
try {
|
|
8407
|
-
const { closeIdlePlaywrightAccounts: closeIdlePlaywrightAccounts2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
8543
|
+
const { closeIdlePlaywrightAccounts: closeIdlePlaywrightAccounts2, evictIdlePlaywrightContextsToLimit: evictIdlePlaywrightContextsToLimit2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
8544
|
+
const evicted = await evictIdlePlaywrightContextsToLimit2(1);
|
|
8408
8545
|
const closed = await closeIdlePlaywrightAccounts2(
|
|
8409
8546
|
config.sessionKeeper.idleMs
|
|
8410
8547
|
);
|
|
8411
|
-
if (closed > 0) {
|
|
8412
|
-
this.emit("recovery:ram:contexts-closed", closed);
|
|
8548
|
+
if (evicted + closed > 0) {
|
|
8549
|
+
this.emit("recovery:ram:contexts-closed", evicted + closed);
|
|
8413
8550
|
}
|
|
8414
8551
|
} catch {
|
|
8415
8552
|
}
|
|
@@ -9092,6 +9229,7 @@ var init_model_registry = __esm({
|
|
|
9092
9229
|
var captcha_coordinator_exports = {};
|
|
9093
9230
|
__export(captcha_coordinator_exports, {
|
|
9094
9231
|
isCaptchaRecoveryActive: () => isCaptchaRecoveryActive,
|
|
9232
|
+
isCaptchaRecoveryInBackoff: () => isCaptchaRecoveryInBackoff,
|
|
9095
9233
|
recoverBaxiaCaptcha: () => recoverBaxiaCaptcha,
|
|
9096
9234
|
solveChallengeOnPage: () => solveChallengeOnPage
|
|
9097
9235
|
});
|
|
@@ -9130,6 +9268,11 @@ async function solveChallengeOnPage(page, challengeUrl, waitForMs = config.captc
|
|
|
9130
9268
|
}
|
|
9131
9269
|
}
|
|
9132
9270
|
}
|
|
9271
|
+
function isCaptchaRecoveryInBackoff(accountId) {
|
|
9272
|
+
if (!accountId) return false;
|
|
9273
|
+
const lastFailure = lastFailedRecoveryAt.get(accountId) ?? 0;
|
|
9274
|
+
return Date.now() - lastFailure < FAILED_RECOVERY_BACKOFF_MS;
|
|
9275
|
+
}
|
|
9133
9276
|
async function recoverBaxiaCaptcha(accountId, label, options = {}) {
|
|
9134
9277
|
if (!config.captcha.enabled || !accountId) return false;
|
|
9135
9278
|
const solver = "baxia";
|
|
@@ -9147,17 +9290,34 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
|
|
|
9147
9290
|
const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
|
|
9148
9291
|
activeCaptchaRecoveries.add(accountId);
|
|
9149
9292
|
try {
|
|
9150
|
-
const {
|
|
9151
|
-
|
|
9152
|
-
accountId,
|
|
9153
|
-
"Account stream interrupted by captcha challenge recovery"
|
|
9154
|
-
);
|
|
9155
|
-
} catch {
|
|
9156
|
-
}
|
|
9157
|
-
try {
|
|
9293
|
+
const { hasActiveBrowserStreamsForAccount: hasActiveBrowserStreamsForAccount2 } = await Promise.resolve().then(() => (init_qwen(), qwen_exports));
|
|
9294
|
+
const hasConcurrentStream = hasActiveBrowserStreamsForAccount2(accountId);
|
|
9158
9295
|
const solved = await withAccountPage(
|
|
9159
9296
|
accountId,
|
|
9160
|
-
(page) =>
|
|
9297
|
+
async (page) => {
|
|
9298
|
+
if (hasConcurrentStream) {
|
|
9299
|
+
const solverOptions = {
|
|
9300
|
+
maxAttempts: config.captcha.maxAttempts,
|
|
9301
|
+
retryDelayMs: config.captcha.retryDelayMs,
|
|
9302
|
+
settleMs: config.captcha.settleMs
|
|
9303
|
+
};
|
|
9304
|
+
if (await solveBaxiaCaptcha(page, { ...solverOptions, waitForMs: 0 })) {
|
|
9305
|
+
return true;
|
|
9306
|
+
}
|
|
9307
|
+
const ctx = typeof page.context === "function" ? page.context() : null;
|
|
9308
|
+
if (ctx && typeof ctx.newPage === "function") {
|
|
9309
|
+
const tempPage = await ctx.newPage();
|
|
9310
|
+
try {
|
|
9311
|
+
return await solveChallengeOnPage(tempPage, challengeUrl);
|
|
9312
|
+
} finally {
|
|
9313
|
+
await tempPage.close({ runBeforeUnload: false }).catch(() => {
|
|
9314
|
+
});
|
|
9315
|
+
}
|
|
9316
|
+
}
|
|
9317
|
+
return false;
|
|
9318
|
+
}
|
|
9319
|
+
return await solveChallengeOnPage(page, challengeUrl);
|
|
9320
|
+
},
|
|
9161
9321
|
solverOperationTimeoutMs,
|
|
9162
9322
|
solverOperationTimeoutMs,
|
|
9163
9323
|
false
|
|
@@ -11756,6 +11916,7 @@ __export(qwen_exports, {
|
|
|
11756
11916
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
11757
11917
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
11758
11918
|
getSessionParent: () => getSessionParent,
|
|
11919
|
+
hasActiveBrowserStreamsForAccount: () => hasActiveBrowserStreamsForAccount,
|
|
11759
11920
|
invalidateLogicalThreadParent: () => invalidateLogicalThreadParent,
|
|
11760
11921
|
isChatSessionActive: () => isChatSessionActive,
|
|
11761
11922
|
isRetryableFetchErrorMessage: () => isRetryableFetchErrorMessage,
|
|
@@ -11777,6 +11938,14 @@ __export(qwen_exports, {
|
|
|
11777
11938
|
});
|
|
11778
11939
|
import crypto7 from "crypto";
|
|
11779
11940
|
import { v4 as uuidv43 } from "uuid";
|
|
11941
|
+
function hasActiveBrowserStreamsForAccount(accountId) {
|
|
11942
|
+
for (const state of browserStreamStates.values()) {
|
|
11943
|
+
if (state.accountId === accountId && !state.done) {
|
|
11944
|
+
return true;
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
return false;
|
|
11948
|
+
}
|
|
11780
11949
|
function abortActiveBrowserStreamsForAccount(accountId, reason = "Account browser stream interrupted (page context unloaded)") {
|
|
11781
11950
|
let count = 0;
|
|
11782
11951
|
for (const [requestId, state] of browserStreamStates) {
|
|
@@ -12554,7 +12723,7 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
|
|
|
12554
12723
|
const onFrameNavigated = (frame) => {
|
|
12555
12724
|
if (!settled && typeof page.mainFrame === "function" && frame === page.mainFrame()) {
|
|
12556
12725
|
const url2 = typeof frame.url === "function" ? frame.url() : String(frame.url || "");
|
|
12557
|
-
if (url2.startsWith(qwenOrigin()) && !url2.includes("/auth") && !url2.includes("_____tmd_____") && !url2.includes("/punish")) {
|
|
12726
|
+
if (url2.startsWith(qwenOrigin()) && !url2.includes("/auth") && !url2.includes("/login") && !url2.includes("_____tmd_____") && !url2.includes("/punish")) {
|
|
12558
12727
|
return;
|
|
12559
12728
|
}
|
|
12560
12729
|
handleBrowserStreamEvent(requestId, {
|
|
@@ -12575,10 +12744,12 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
|
|
|
12575
12744
|
};
|
|
12576
12745
|
page.on("framenavigated", onFrameNavigated);
|
|
12577
12746
|
page.on("close", onPageClosed);
|
|
12747
|
+
page.on("crash", onPageClosed);
|
|
12578
12748
|
pageCleanup = () => {
|
|
12579
12749
|
try {
|
|
12580
12750
|
page.off?.("framenavigated", onFrameNavigated);
|
|
12581
12751
|
page.off?.("close", onPageClosed);
|
|
12752
|
+
page.off?.("crash", onPageClosed);
|
|
12582
12753
|
} catch {
|
|
12583
12754
|
}
|
|
12584
12755
|
};
|
|
@@ -16727,7 +16898,16 @@ async function deleteChatsForAccountId(accountId) {
|
|
|
16727
16898
|
if (!credentials) {
|
|
16728
16899
|
throw new Error(`Credenciais da conta ${account.email} n\xE3o encontradas.`);
|
|
16729
16900
|
}
|
|
16730
|
-
|
|
16901
|
+
const wasInitialized = isPlaywrightInitialized(account.id);
|
|
16902
|
+
try {
|
|
16903
|
+
return await deleteChatsForAccount(credentials);
|
|
16904
|
+
} finally {
|
|
16905
|
+
if (!wasInitialized && !isAuthMockEnabled()) {
|
|
16906
|
+
const { closePlaywrightForAccount: closePlaywrightForAccount2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
16907
|
+
await closePlaywrightForAccount2(account.id).catch(() => {
|
|
16908
|
+
});
|
|
16909
|
+
}
|
|
16910
|
+
}
|
|
16731
16911
|
}
|
|
16732
16912
|
async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
|
|
16733
16913
|
const accounts = loadAccounts();
|
|
@@ -16742,6 +16922,7 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
|
|
|
16742
16922
|
const account = accounts[i];
|
|
16743
16923
|
const currentIdx = i + 1;
|
|
16744
16924
|
const totalCount = accounts.length;
|
|
16925
|
+
const wasInitialized = isPlaywrightInitialized(account.id);
|
|
16745
16926
|
try {
|
|
16746
16927
|
const ok = await deleteChatsForAccount(account, currentIdx, totalCount);
|
|
16747
16928
|
if (ok) succeeded++;
|
|
@@ -16751,7 +16932,7 @@ async function deleteChatsForConfiguredAccounts(keepBrowserOpen = false) {
|
|
|
16751
16932
|
error instanceof Error ? error.message : String(error)
|
|
16752
16933
|
);
|
|
16753
16934
|
} finally {
|
|
16754
|
-
if (!keepBrowserOpen) {
|
|
16935
|
+
if ((!keepBrowserOpen || !wasInitialized) && !isAuthMockEnabled()) {
|
|
16755
16936
|
const { closePlaywrightForAccount: closePlaywrightForAccount2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
16756
16937
|
await closePlaywrightForAccount2(account.id).catch(() => {
|
|
16757
16938
|
});
|
|
@@ -18038,6 +18219,9 @@ ${loopReminder}` : truncation.prompt;
|
|
|
18038
18219
|
if (policy.reason === "corrupted_chat_history") {
|
|
18039
18220
|
invalidateLogicalThreadParent(params.sessionId);
|
|
18040
18221
|
}
|
|
18222
|
+
if (policy.reason === "anti_bot" && isCaptchaRecoveryInBackoff(currentAccountId)) {
|
|
18223
|
+
return { success: false, error: err };
|
|
18224
|
+
}
|
|
18041
18225
|
const retryInvalidInputOnSameAccount = shouldRetryInvalidInputOnSameAccount(
|
|
18042
18226
|
policy.reason,
|
|
18043
18227
|
invalidInputSameAccountRetried
|
|
@@ -18226,6 +18410,7 @@ var init_account = __esm({
|
|
|
18226
18410
|
init_account_manager();
|
|
18227
18411
|
init_account_priority();
|
|
18228
18412
|
init_waf_isolation();
|
|
18413
|
+
init_captcha_coordinator();
|
|
18229
18414
|
init_accounts();
|
|
18230
18415
|
init_config();
|
|
18231
18416
|
init_errors();
|
|
@@ -21849,8 +22034,8 @@ async function processStreamingResponse(params) {
|
|
|
21849
22034
|
);
|
|
21850
22035
|
};
|
|
21851
22036
|
const emitAnswerText = async (textChunk) => {
|
|
21852
|
-
if (textChunk) emittedModelOutput = true;
|
|
21853
22037
|
if (!toolParser) {
|
|
22038
|
+
if (textChunk) emittedModelOutput = true;
|
|
21854
22039
|
finalContent += textChunk;
|
|
21855
22040
|
writeDeltaEvent({ content: textChunk });
|
|
21856
22041
|
return;
|
|
@@ -22226,6 +22411,23 @@ async function processStreamingResponse(params) {
|
|
|
22226
22411
|
if (fastMatch) {
|
|
22227
22412
|
const unescaped = fastMatch[1].replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
22228
22413
|
if (unescaped) {
|
|
22414
|
+
if (!emittedModelOutput && isOverloadMessage(unescaped)) {
|
|
22415
|
+
const overloadErr = toRetryableStreamError(
|
|
22416
|
+
"server_overloaded",
|
|
22417
|
+
unescaped,
|
|
22418
|
+
{
|
|
22419
|
+
switchAccount: true,
|
|
22420
|
+
forceNewChat: true,
|
|
22421
|
+
reason: "server_busy",
|
|
22422
|
+
accountCooldownMs: OVERLOAD_COOLDOWN_MS,
|
|
22423
|
+
accountCooldownReason: "ServerOverloaded"
|
|
22424
|
+
}
|
|
22425
|
+
);
|
|
22426
|
+
if (await recoverFromStreamError(overloadErr)) {
|
|
22427
|
+
break;
|
|
22428
|
+
}
|
|
22429
|
+
throw overloadErr;
|
|
22430
|
+
}
|
|
22229
22431
|
const result = getIncrementalDelta(
|
|
22230
22432
|
lastRawContent,
|
|
22231
22433
|
unescaped,
|
|
@@ -22360,7 +22562,6 @@ async function processStreamingResponse(params) {
|
|
|
22360
22562
|
if (foundStr && vStr !== "") {
|
|
22361
22563
|
if (vStr === "FINISHED") continue;
|
|
22362
22564
|
if (isThinkingChunk) {
|
|
22363
|
-
emittedModelOutput = true;
|
|
22364
22565
|
reasoningBuffer += vStr;
|
|
22365
22566
|
writeDeltaEvent({ reasoning_content: vStr });
|
|
22366
22567
|
} else {
|
|
@@ -27095,7 +27296,7 @@ function getAppVersion() {
|
|
|
27095
27296
|
}
|
|
27096
27297
|
} catch {
|
|
27097
27298
|
}
|
|
27098
|
-
cachedVersion = "v1.6.
|
|
27299
|
+
cachedVersion = "v1.6.11";
|
|
27099
27300
|
return cachedVersion;
|
|
27100
27301
|
}
|
|
27101
27302
|
var cachedVersion;
|
|
@@ -27501,10 +27702,11 @@ async function startServer(options) {
|
|
|
27501
27702
|
} else if (remainingAccounts.length > 0) {
|
|
27502
27703
|
void (async () => {
|
|
27503
27704
|
const { ensureAccountInPriority: ensureAccountInPriority2 } = await Promise.resolve().then(() => (init_account_priority(), account_priority_exports));
|
|
27504
|
-
const
|
|
27705
|
+
const maxWarmCap = config.playwright.maxActiveContexts > 0 ? Math.min(
|
|
27505
27706
|
config.playwright.startupWarmAccounts,
|
|
27506
|
-
|
|
27507
|
-
);
|
|
27707
|
+
config.playwright.maxActiveContexts
|
|
27708
|
+
) : config.playwright.startupWarmAccounts;
|
|
27709
|
+
const targetWarmTotal = Math.min(maxWarmCap, totalAccounts);
|
|
27508
27710
|
let currentReadyCount = readyAccountIds.size;
|
|
27509
27711
|
if (currentReadyCount < targetWarmTotal && remainingAccounts.length > 0) {
|
|
27510
27712
|
for (const reserveAccount of remainingAccounts) {
|