qwenproxy-cli 1.4.3 → 1.4.8
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 +205 -75
- package/dist/delete-chats.js +397 -93
- package/dist/index.js +399 -95
- package/dist/login.js +345 -83
- package/dist/reset-cooldowns.js +205 -75
- package/package.json +1 -1
- package/src/core/database.ts +10 -5
- package/src/core/version.ts +1 -1
- package/src/routes/chat/retry-policy.ts +4 -3
- package/src/services/playwright.ts +408 -83
- package/src/services/qwen-chat-pool.ts +9 -0
- package/src/services/qwen.ts +43 -10
package/dist/delete-chats.js
CHANGED
|
@@ -314,6 +314,7 @@ function runMigrations(db2) {
|
|
|
314
314
|
"sec_ch_ua_mobile TEXT",
|
|
315
315
|
"sec_ch_ua_platform TEXT",
|
|
316
316
|
"version TEXT",
|
|
317
|
+
"refresh_token TEXT",
|
|
317
318
|
"captured_at INTEGER DEFAULT 0"
|
|
318
319
|
];
|
|
319
320
|
for (const col of authSessionCols) {
|
|
@@ -392,11 +393,11 @@ function saveAuthSession(accountId, session) {
|
|
|
392
393
|
INSERT OR REPLACE INTO qwen_auth_sessions (
|
|
393
394
|
account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
394
395
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
395
|
-
user_id, token_expires_at, captured_at, updated_at
|
|
396
|
+
user_id, token_expires_at, refresh_token, captured_at, updated_at
|
|
396
397
|
) VALUES (
|
|
397
398
|
?, ?, ?, ?, ?, ?,
|
|
398
399
|
?, ?, ?, ?,
|
|
399
|
-
?, ?, ?, datetime('now')
|
|
400
|
+
?, ?, ?, ?, datetime('now')
|
|
400
401
|
)
|
|
401
402
|
`);
|
|
402
403
|
stmt.run(
|
|
@@ -412,6 +413,7 @@ function saveAuthSession(accountId, session) {
|
|
|
412
413
|
session.version || null,
|
|
413
414
|
session.userId || null,
|
|
414
415
|
session.tokenExpiresAt || null,
|
|
416
|
+
session.refreshToken || null,
|
|
415
417
|
session.capturedAt ?? Date.now()
|
|
416
418
|
);
|
|
417
419
|
}
|
|
@@ -420,7 +422,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
420
422
|
const row = database.prepare(
|
|
421
423
|
`SELECT account_id, cookie, user_agent, bx_v, bx_ua, bx_umidtoken,
|
|
422
424
|
sec_ch_ua, sec_ch_ua_mobile, sec_ch_ua_platform, version,
|
|
423
|
-
user_id, token_expires_at, captured_at
|
|
425
|
+
user_id, token_expires_at, refresh_token, captured_at
|
|
424
426
|
FROM qwen_auth_sessions WHERE account_id = ?`
|
|
425
427
|
).get(accountId);
|
|
426
428
|
if (!row) return null;
|
|
@@ -428,7 +430,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
428
430
|
if (capturedAt <= 0 || Date.now() - capturedAt > maxAgeMs) {
|
|
429
431
|
return null;
|
|
430
432
|
}
|
|
431
|
-
if (row.token_expires_at) {
|
|
433
|
+
if (!row.refresh_token && row.token_expires_at) {
|
|
432
434
|
const tokenExpMs = Number(row.token_expires_at) * 1e3;
|
|
433
435
|
if (tokenExpMs <= Date.now() + 5 * 60 * 1e3) {
|
|
434
436
|
return null;
|
|
@@ -450,6 +452,7 @@ function getValidAuthSession(accountId, maxAgeMs = 30 * 24 * 60 * 60 * 1e3) {
|
|
|
450
452
|
version: row.version || void 0,
|
|
451
453
|
userId: row.user_id || void 0,
|
|
452
454
|
tokenExpiresAt: row.token_expires_at ? Number(row.token_expires_at) : void 0,
|
|
455
|
+
refreshToken: row.refresh_token || void 0,
|
|
453
456
|
capturedAt
|
|
454
457
|
};
|
|
455
458
|
}
|
|
@@ -3052,6 +3055,7 @@ __export(playwright_exports, {
|
|
|
3052
3055
|
parseCookiesForBrowser: () => parseCookiesForBrowser,
|
|
3053
3056
|
pruneAllPlaywrightProfiles: () => pruneAllPlaywrightProfiles,
|
|
3054
3057
|
prunePlaywrightProfileCaches: () => prunePlaywrightProfileCaches,
|
|
3058
|
+
refreshAccountToken: () => refreshAccountToken,
|
|
3055
3059
|
refreshHeaders: () => refreshHeaders,
|
|
3056
3060
|
refreshHeadersWithProfileReset: () => refreshHeadersWithProfileReset,
|
|
3057
3061
|
registerPlaywrightAccountForTests: () => registerPlaywrightAccountForTests,
|
|
@@ -3779,8 +3783,29 @@ function getStealthScript(profile) {
|
|
|
3779
3783
|
const _toBlob = HTMLCanvasElement.prototype.toBlob;
|
|
3780
3784
|
const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
|
|
3781
3785
|
|
|
3786
|
+
function isCaptchaCanvas(canvas) {
|
|
3787
|
+
try {
|
|
3788
|
+
if (!canvas) return false;
|
|
3789
|
+
const id = (canvas.id || "").toLowerCase();
|
|
3790
|
+
const cls = (canvas.className || "").toLowerCase();
|
|
3791
|
+
return (
|
|
3792
|
+
id.includes("baxia") ||
|
|
3793
|
+
id.includes("nc_") ||
|
|
3794
|
+
id.includes("captcha") ||
|
|
3795
|
+
id.includes("punish") ||
|
|
3796
|
+
cls.includes("baxia") ||
|
|
3797
|
+
cls.includes("nc_") ||
|
|
3798
|
+
cls.includes("captcha") ||
|
|
3799
|
+
Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
|
|
3800
|
+
);
|
|
3801
|
+
} catch(e) {
|
|
3802
|
+
return false;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3782
3806
|
function addNoise(canvas) {
|
|
3783
3807
|
try {
|
|
3808
|
+
if (isCaptchaCanvas(canvas)) return;
|
|
3784
3809
|
const ctx = canvas.getContext('2d');
|
|
3785
3810
|
if (!ctx) return;
|
|
3786
3811
|
const style = ctx.fillStyle;
|
|
@@ -3804,6 +3829,9 @@ function getStealthScript(profile) {
|
|
|
3804
3829
|
|
|
3805
3830
|
CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
|
|
3806
3831
|
const imageData = _getImageData.apply(this, arguments);
|
|
3832
|
+
if (isCaptchaCanvas(this.canvas)) {
|
|
3833
|
+
return imageData;
|
|
3834
|
+
}
|
|
3807
3835
|
const data = imageData.data;
|
|
3808
3836
|
const maxPixels = Math.min(data.length / 4, 2500);
|
|
3809
3837
|
for (let i = 0; i < maxPixels * 4; i += 4) {
|
|
@@ -4663,6 +4691,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4663
4691
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4664
4692
|
version: cache.headers["version"] || void 0,
|
|
4665
4693
|
tokenExpiresAt: exp || void 0,
|
|
4694
|
+
refreshToken: apiResult.refreshToken,
|
|
4666
4695
|
capturedAt: Date.now()
|
|
4667
4696
|
});
|
|
4668
4697
|
}
|
|
@@ -4696,6 +4725,13 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4696
4725
|
markAccountHeadersReady(accountId);
|
|
4697
4726
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4698
4727
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
4728
|
+
let rTok = uiResult.refreshToken;
|
|
4729
|
+
if (!rTok) {
|
|
4730
|
+
try {
|
|
4731
|
+
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
4732
|
+
} catch {
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4699
4735
|
saveAuthSession2(accountId, {
|
|
4700
4736
|
cookie: cookieStr,
|
|
4701
4737
|
userAgent: cache.headers["user-agent"] || "",
|
|
@@ -4707,6 +4743,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4707
4743
|
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || void 0,
|
|
4708
4744
|
version: cache.headers["version"] || void 0,
|
|
4709
4745
|
tokenExpiresAt: exp || void 0,
|
|
4746
|
+
refreshToken: rTok,
|
|
4710
4747
|
capturedAt: Date.now()
|
|
4711
4748
|
});
|
|
4712
4749
|
}
|
|
@@ -4759,37 +4796,52 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4759
4796
|
async function loginViaApi(page, email, password) {
|
|
4760
4797
|
try {
|
|
4761
4798
|
const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
email,
|
|
4770
|
-
password: hashedPassword,
|
|
4771
|
-
login_type: "email"
|
|
4772
|
-
},
|
|
4773
|
-
headers: {
|
|
4774
|
-
"content-type": "application/json",
|
|
4775
|
-
accept: "application/json, text/plain, */*",
|
|
4776
|
-
referer: qwenUrl("/auth"),
|
|
4777
|
-
origin: qwenOrigin(),
|
|
4778
|
-
source: "web"
|
|
4779
|
-
},
|
|
4780
|
-
timeout: 1e4
|
|
4781
|
-
});
|
|
4782
|
-
data = await response.json().catch(() => null);
|
|
4783
|
-
signinSuccess = Boolean(data && (data.success === true || data.token));
|
|
4784
|
-
} catch {
|
|
4785
|
-
}
|
|
4799
|
+
if (!page.url().includes("/auth")) {
|
|
4800
|
+
await page.goto(qwenUrl("/auth"), {
|
|
4801
|
+
waitUntil: "domcontentloaded",
|
|
4802
|
+
timeout: config.timeouts.navigation
|
|
4803
|
+
}).catch(() => {
|
|
4804
|
+
});
|
|
4805
|
+
await sleep2(1e3);
|
|
4786
4806
|
}
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4807
|
+
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4808
|
+
const requestId = crypto3.randomUUID();
|
|
4809
|
+
const evalRes = await page.evaluate(
|
|
4810
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
4811
|
+
try {
|
|
4812
|
+
const response = await fetch(signinUrl2, {
|
|
4813
|
+
method: "POST",
|
|
4814
|
+
signal: AbortSignal.timeout(15e3),
|
|
4815
|
+
credentials: "include",
|
|
4816
|
+
headers: {
|
|
4817
|
+
accept: "application/json, text/plain, */*",
|
|
4818
|
+
"content-type": "application/json",
|
|
4819
|
+
source: "web",
|
|
4820
|
+
version: "0.3.11",
|
|
4821
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
4822
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4823
|
+
"x-request-id": requestId2
|
|
4824
|
+
},
|
|
4825
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4826
|
+
});
|
|
4827
|
+
const json = await response.json().catch(() => null);
|
|
4828
|
+
return { ok: response.ok, status: response.status, data: json };
|
|
4829
|
+
} catch (e) {
|
|
4830
|
+
return { ok: false, error: e.message };
|
|
4831
|
+
}
|
|
4832
|
+
},
|
|
4833
|
+
{ email, password: hashedPassword, signinUrl, requestId }
|
|
4834
|
+
).catch(() => null);
|
|
4835
|
+
let data = evalRes?.data;
|
|
4836
|
+
let signinSuccess = Boolean(
|
|
4837
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4838
|
+
);
|
|
4839
|
+
if (!signinSuccess && !data?.data?.code) {
|
|
4840
|
+
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4841
|
+
const fallbackRes = await page.evaluate(
|
|
4842
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
4791
4843
|
try {
|
|
4792
|
-
const response = await fetch(
|
|
4844
|
+
const response = await fetch(fallbackUrl2, {
|
|
4793
4845
|
method: "POST",
|
|
4794
4846
|
signal: AbortSignal.timeout(1e4),
|
|
4795
4847
|
credentials: "include",
|
|
@@ -4797,10 +4849,11 @@ async function loginViaApi(page, email, password) {
|
|
|
4797
4849
|
accept: "application/json, text/plain, */*",
|
|
4798
4850
|
"content-type": "application/json",
|
|
4799
4851
|
source: "web",
|
|
4852
|
+
version: "0.3.11",
|
|
4800
4853
|
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
4801
4854
|
"x-request-id": requestId2
|
|
4802
4855
|
},
|
|
4803
|
-
body: JSON.stringify({ email: email2, password: password2
|
|
4856
|
+
body: JSON.stringify({ email: email2, password: password2 })
|
|
4804
4857
|
});
|
|
4805
4858
|
const json = await response.json().catch(() => null);
|
|
4806
4859
|
return { ok: response.ok, status: response.status, data: json };
|
|
@@ -4808,11 +4861,13 @@ async function loginViaApi(page, email, password) {
|
|
|
4808
4861
|
return { ok: false, error: e.message };
|
|
4809
4862
|
}
|
|
4810
4863
|
},
|
|
4811
|
-
{ email, password: hashedPassword,
|
|
4864
|
+
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
4812
4865
|
).catch(() => null);
|
|
4813
|
-
if (
|
|
4814
|
-
data =
|
|
4815
|
-
signinSuccess = Boolean(
|
|
4866
|
+
if (fallbackRes?.data) {
|
|
4867
|
+
data = fallbackRes.data;
|
|
4868
|
+
signinSuccess = Boolean(
|
|
4869
|
+
data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
|
|
4870
|
+
);
|
|
4816
4871
|
}
|
|
4817
4872
|
}
|
|
4818
4873
|
if (data?.success === false) {
|
|
@@ -4826,7 +4881,8 @@ async function loginViaApi(page, email, password) {
|
|
|
4826
4881
|
};
|
|
4827
4882
|
}
|
|
4828
4883
|
if (signinSuccess) {
|
|
4829
|
-
const token = data?.data?.token || data?.token;
|
|
4884
|
+
const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
|
|
4885
|
+
const refreshToken = data?.data?.refresh_token || data?.refresh_token;
|
|
4830
4886
|
if (token) {
|
|
4831
4887
|
try {
|
|
4832
4888
|
await page.context().addCookies([
|
|
@@ -4850,18 +4906,23 @@ async function loginViaApi(page, email, password) {
|
|
|
4850
4906
|
}).catch(() => {
|
|
4851
4907
|
});
|
|
4852
4908
|
if (token) {
|
|
4853
|
-
await page.evaluate(
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4909
|
+
await page.evaluate(
|
|
4910
|
+
({ tok, rTok }) => {
|
|
4911
|
+
try {
|
|
4912
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
4913
|
+
localStorage.setItem("token", tok);
|
|
4914
|
+
localStorage.setItem("access_token", tok);
|
|
4915
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
4916
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4917
|
+
} catch {
|
|
4918
|
+
}
|
|
4919
|
+
},
|
|
4920
|
+
{ tok: token, rTok: refreshToken }
|
|
4921
|
+
).catch(() => {
|
|
4861
4922
|
});
|
|
4862
4923
|
await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
|
|
4863
4924
|
});
|
|
4864
|
-
return { success: true };
|
|
4925
|
+
return { success: true, token, refreshToken };
|
|
4865
4926
|
}
|
|
4866
4927
|
}
|
|
4867
4928
|
return {
|
|
@@ -4885,11 +4946,23 @@ async function loginViaApi(page, email, password) {
|
|
|
4885
4946
|
}
|
|
4886
4947
|
async function loginViaUi(page, email, password) {
|
|
4887
4948
|
try {
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4949
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
|
|
4950
|
+
return { success: true };
|
|
4951
|
+
}
|
|
4952
|
+
try {
|
|
4953
|
+
await page.goto(qwenUrl("/auth"), {
|
|
4954
|
+
waitUntil: "domcontentloaded",
|
|
4955
|
+
timeout: Math.min(config.timeouts.navigation, 15e3)
|
|
4956
|
+
});
|
|
4957
|
+
await sleep2(1e3);
|
|
4958
|
+
} catch (gotoErr) {
|
|
4959
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
4960
|
+
return { success: true };
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
|
|
4964
|
+
return { success: true };
|
|
4965
|
+
}
|
|
4893
4966
|
const emailSelector = [
|
|
4894
4967
|
'input[type="email"]',
|
|
4895
4968
|
'input[name="email"]',
|
|
@@ -4899,7 +4972,7 @@ async function loginViaUi(page, email, password) {
|
|
|
4899
4972
|
].join(", ");
|
|
4900
4973
|
try {
|
|
4901
4974
|
await page.waitForSelector(emailSelector, {
|
|
4902
|
-
timeout: Math.min(config.timeouts.page,
|
|
4975
|
+
timeout: Math.min(config.timeouts.page, 5e3)
|
|
4903
4976
|
});
|
|
4904
4977
|
} catch {
|
|
4905
4978
|
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
@@ -4912,30 +4985,55 @@ async function loginViaUi(page, email, password) {
|
|
|
4912
4985
|
return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
|
|
4913
4986
|
}
|
|
4914
4987
|
await clearVisibleChallenge(page);
|
|
4915
|
-
const
|
|
4916
|
-
return page.locator('
|
|
4917
|
-
hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
|
|
4918
|
-
}).first();
|
|
4988
|
+
const isPasswordInputPresent = async () => {
|
|
4989
|
+
return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
|
|
4919
4990
|
};
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
|
|
4991
|
+
const revealPasswordMode = async () => {
|
|
4992
|
+
if (page.url().includes("/forget")) {
|
|
4993
|
+
await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
|
|
4924
4994
|
});
|
|
4925
|
-
await sleep2(
|
|
4995
|
+
await sleep2(1e3);
|
|
4926
4996
|
}
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
if (await
|
|
4934
|
-
await
|
|
4997
|
+
if (await isPasswordInputPresent()) return;
|
|
4998
|
+
const btn = page.locator("button").filter({
|
|
4999
|
+
hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
|
|
5000
|
+
}).filter({
|
|
5001
|
+
hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
|
|
5002
|
+
}).first();
|
|
5003
|
+
if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
|
|
5004
|
+
await btn.click({ timeout: 2e3 }).catch(() => {
|
|
4935
5005
|
});
|
|
4936
5006
|
await sleep2(500);
|
|
4937
5007
|
}
|
|
5008
|
+
if (await isPasswordInputPresent()) return;
|
|
5009
|
+
await page.evaluate(() => {
|
|
5010
|
+
try {
|
|
5011
|
+
const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
|
|
5012
|
+
for (const b of buttons) {
|
|
5013
|
+
const txt = (b.innerText || "").trim().toLowerCase();
|
|
5014
|
+
if (/(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i.test(txt) && !txt.includes("esqueci") && !txt.includes("forgot") && !txt.includes("olvid") && !txt.includes("reset")) {
|
|
5015
|
+
b.click();
|
|
5016
|
+
return true;
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
5019
|
+
} catch {
|
|
5020
|
+
}
|
|
5021
|
+
return false;
|
|
5022
|
+
}).catch(() => false);
|
|
5023
|
+
await sleep2(500);
|
|
5024
|
+
};
|
|
5025
|
+
await revealPasswordMode();
|
|
5026
|
+
try {
|
|
5027
|
+
await page.fill(emailSelector, email, { timeout: 8e3 });
|
|
5028
|
+
await sleep2(300);
|
|
4938
5029
|
} catch {
|
|
5030
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
5031
|
+
return { success: true };
|
|
5032
|
+
}
|
|
5033
|
+
throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
5034
|
+
}
|
|
5035
|
+
if (!await isPasswordInputPresent()) {
|
|
5036
|
+
await revealPasswordMode();
|
|
4939
5037
|
}
|
|
4940
5038
|
const passwordSelector = 'input[type="password"], input[name="password"]';
|
|
4941
5039
|
try {
|
|
@@ -4954,17 +5052,50 @@ async function loginViaUi(page, email, password) {
|
|
|
4954
5052
|
reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
|
|
4955
5053
|
};
|
|
4956
5054
|
}
|
|
4957
|
-
|
|
4958
|
-
|
|
5055
|
+
try {
|
|
5056
|
+
await page.fill(passwordSelector, password, { timeout: 8e3 });
|
|
5057
|
+
await sleep2(500);
|
|
5058
|
+
} catch {
|
|
5059
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
|
|
5060
|
+
return { success: true };
|
|
5061
|
+
}
|
|
5062
|
+
throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
|
|
5063
|
+
}
|
|
5064
|
+
await page.evaluate(() => {
|
|
5065
|
+
try {
|
|
5066
|
+
const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
|
|
5067
|
+
const passEl = document.querySelector('input[type="password"], input[name="password"]');
|
|
5068
|
+
if (emailEl) {
|
|
5069
|
+
emailEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5070
|
+
emailEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5071
|
+
emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5072
|
+
}
|
|
5073
|
+
if (passEl) {
|
|
5074
|
+
passEl.dispatchEvent(new Event("input", { bubbles: true }));
|
|
5075
|
+
passEl.dispatchEvent(new Event("change", { bubbles: true }));
|
|
5076
|
+
passEl.dispatchEvent(new Event("blur", { bubbles: true }));
|
|
5077
|
+
}
|
|
5078
|
+
} catch {
|
|
5079
|
+
}
|
|
5080
|
+
}).catch(() => {
|
|
5081
|
+
});
|
|
4959
5082
|
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")';
|
|
4960
5083
|
const submitButton = page.locator(submitSelector).first();
|
|
4961
5084
|
try {
|
|
4962
5085
|
await page.waitForSelector('button[type="submit"]:not([disabled])', {
|
|
4963
|
-
timeout:
|
|
5086
|
+
timeout: 4e3
|
|
4964
5087
|
});
|
|
4965
5088
|
await submitButton.click();
|
|
4966
5089
|
} catch {
|
|
4967
|
-
await
|
|
5090
|
+
const isDisabled = await submitButton.isDisabled().catch(() => true);
|
|
5091
|
+
if (!isDisabled) {
|
|
5092
|
+
await submitButton.click({ force: true }).catch(() => {
|
|
5093
|
+
});
|
|
5094
|
+
} else {
|
|
5095
|
+
if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
|
|
5096
|
+
return { success: true };
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
4968
5099
|
}
|
|
4969
5100
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4970
5101
|
while (Date.now() < postSubmitDeadline) {
|
|
@@ -5034,6 +5165,137 @@ async function loginViaUi(page, email, password) {
|
|
|
5034
5165
|
return { success: false, reason: err?.message || String(err) };
|
|
5035
5166
|
}
|
|
5036
5167
|
}
|
|
5168
|
+
async function refreshAccountToken(accountId) {
|
|
5169
|
+
const page = accountPages.get(accountId);
|
|
5170
|
+
if (!page || page.isClosed()) {
|
|
5171
|
+
return { success: false, error: "Account page not active" };
|
|
5172
|
+
}
|
|
5173
|
+
const { getValidAuthSession: getValidAuthSession2, saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
5174
|
+
const session = getValidAuthSession2(accountId);
|
|
5175
|
+
let refreshToken = session?.refreshToken;
|
|
5176
|
+
if (!refreshToken) {
|
|
5177
|
+
try {
|
|
5178
|
+
refreshToken = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
5179
|
+
} catch {
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
if (refreshToken) {
|
|
5183
|
+
try {
|
|
5184
|
+
const refreshResult = await page.evaluate(
|
|
5185
|
+
async ({ rTok }) => {
|
|
5186
|
+
try {
|
|
5187
|
+
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
5188
|
+
method: "GET",
|
|
5189
|
+
credentials: "include",
|
|
5190
|
+
headers: {
|
|
5191
|
+
accept: "application/json, text/plain, */*",
|
|
5192
|
+
source: "web",
|
|
5193
|
+
version: "0.3.11",
|
|
5194
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
5195
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
5196
|
+
authorization: `Bearer ${rTok}`
|
|
5197
|
+
}
|
|
5198
|
+
});
|
|
5199
|
+
const json2 = await response.json().catch(() => null);
|
|
5200
|
+
return { ok: response.ok, status: response.status, data: json2 };
|
|
5201
|
+
} catch (e) {
|
|
5202
|
+
return { ok: false, error: e.message };
|
|
5203
|
+
}
|
|
5204
|
+
},
|
|
5205
|
+
{ rTok: refreshToken }
|
|
5206
|
+
);
|
|
5207
|
+
const json = refreshResult?.data;
|
|
5208
|
+
if (json && json.success === true && json.data) {
|
|
5209
|
+
const newAccessToken = json.data.access_token || json.data.token;
|
|
5210
|
+
const newRefreshToken = json.data.refresh_token || refreshToken;
|
|
5211
|
+
if (newAccessToken) {
|
|
5212
|
+
await page.context().addCookies([
|
|
5213
|
+
{
|
|
5214
|
+
name: "token",
|
|
5215
|
+
value: newAccessToken,
|
|
5216
|
+
domain: ".qwen.ai",
|
|
5217
|
+
path: "/",
|
|
5218
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
5219
|
+
httpOnly: false,
|
|
5220
|
+
secure: true,
|
|
5221
|
+
sameSite: "Lax"
|
|
5222
|
+
}
|
|
5223
|
+
]);
|
|
5224
|
+
await page.evaluate(
|
|
5225
|
+
({ tok, rTok }) => {
|
|
5226
|
+
try {
|
|
5227
|
+
localStorage.removeItem("qwen_token_logged_out_marker");
|
|
5228
|
+
localStorage.setItem("token", tok);
|
|
5229
|
+
localStorage.setItem("access_token", tok);
|
|
5230
|
+
if (rTok) localStorage.setItem("refresh_token", rTok);
|
|
5231
|
+
document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
5232
|
+
} catch {
|
|
5233
|
+
}
|
|
5234
|
+
},
|
|
5235
|
+
{ tok: newAccessToken, rTok: newRefreshToken }
|
|
5236
|
+
).catch(() => {
|
|
5237
|
+
});
|
|
5238
|
+
const liveCookies = await page.context().cookies();
|
|
5239
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5240
|
+
const cache = getHeaderCache(accountId);
|
|
5241
|
+
cache.headers.cookie = cookieStr;
|
|
5242
|
+
cache.lastRefresh = Date.now();
|
|
5243
|
+
markAccountHeadersReady(accountId);
|
|
5244
|
+
saveAuthSession2(accountId, {
|
|
5245
|
+
cookie: cookieStr,
|
|
5246
|
+
userAgent: cache.headers["user-agent"] || session?.userAgent || "",
|
|
5247
|
+
bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
5248
|
+
bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
|
|
5249
|
+
bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
5250
|
+
secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
|
|
5251
|
+
secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
5252
|
+
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
5253
|
+
version: cache.headers["version"] || session?.version,
|
|
5254
|
+
userId: session?.userId,
|
|
5255
|
+
tokenExpiresAt: parseJwtExpiry(newAccessToken) || void 0,
|
|
5256
|
+
refreshToken: newRefreshToken,
|
|
5257
|
+
capturedAt: Date.now()
|
|
5258
|
+
});
|
|
5259
|
+
return { success: true, token: newAccessToken };
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
} catch {
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
5266
|
+
const creds = getAccountCredentials2(accountId);
|
|
5267
|
+
if (creds && creds.email && creds.password) {
|
|
5268
|
+
const apiResult = await loginViaApi(page, creds.email, creds.password);
|
|
5269
|
+
if (apiResult.success) {
|
|
5270
|
+
const liveCookies = await page.context().cookies();
|
|
5271
|
+
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
5272
|
+
if (tokenCookie) {
|
|
5273
|
+
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
5274
|
+
const cache = getHeaderCache(accountId);
|
|
5275
|
+
cache.headers.cookie = cookieStr;
|
|
5276
|
+
cache.lastRefresh = Date.now();
|
|
5277
|
+
markAccountHeadersReady(accountId);
|
|
5278
|
+
saveAuthSession2(accountId, {
|
|
5279
|
+
cookie: cookieStr,
|
|
5280
|
+
userAgent: cache.headers["user-agent"] || session?.userAgent || "",
|
|
5281
|
+
bxV: cache.headers["bx-v"] || session?.bxV || "2.5.37",
|
|
5282
|
+
bxUa: cache.headers["bx-ua"] || session?.bxUa || "",
|
|
5283
|
+
bxUmidtoken: cache.headers["bx-umidtoken"] || session?.bxUmidtoken || "",
|
|
5284
|
+
secChUa: cache.headers["sec-ch-ua"] || session?.secChUa,
|
|
5285
|
+
secChUaMobile: cache.headers["sec-ch-ua-mobile"] || session?.secChUaMobile,
|
|
5286
|
+
secChUaPlatform: cache.headers["sec-ch-ua-platform"] || session?.secChUaPlatform,
|
|
5287
|
+
version: cache.headers["version"] || session?.version,
|
|
5288
|
+
userId: session?.userId,
|
|
5289
|
+
tokenExpiresAt: parseJwtExpiry(tokenCookie.value) || void 0,
|
|
5290
|
+
refreshToken: apiResult.refreshToken,
|
|
5291
|
+
capturedAt: Date.now()
|
|
5292
|
+
});
|
|
5293
|
+
return { success: true, token: tokenCookie.value };
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
return { success: false, error: "Could not refresh or re-authenticate token" };
|
|
5298
|
+
}
|
|
5037
5299
|
async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.timeouts.headers, triggerGraceMs = HEADER_CAPTURE_TRIGGER_GRACE_MS) {
|
|
5038
5300
|
const page = pageOverride ?? accountPages.get(accountId);
|
|
5039
5301
|
if (!page || page.isClosed()) {
|
|
@@ -5550,6 +5812,10 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
5550
5812
|
}
|
|
5551
5813
|
};
|
|
5552
5814
|
if (forceReauth) {
|
|
5815
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
5816
|
+
if (refreshed.success) {
|
|
5817
|
+
return;
|
|
5818
|
+
}
|
|
5553
5819
|
await executeReauth();
|
|
5554
5820
|
} else {
|
|
5555
5821
|
try {
|
|
@@ -5984,19 +6250,15 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
5984
6250
|
const now = Date.now();
|
|
5985
6251
|
const currentUrl = page.url();
|
|
5986
6252
|
const cookie = await getCookies(accountId);
|
|
5987
|
-
if (cookie && isTokenExpiringSoon(cookie,
|
|
6253
|
+
if (cookie && isTokenExpiringSoon(cookie, 5)) {
|
|
5988
6254
|
console.log(
|
|
5989
|
-
`\u{1F493} [SessionKeeper] Account ${accountId} token
|
|
6255
|
+
`\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
5990
6256
|
);
|
|
5991
|
-
|
|
5992
|
-
|
|
6257
|
+
const res = await refreshAccountToken(accountId);
|
|
6258
|
+
if (res.success) {
|
|
5993
6259
|
lastKeepAliveNavigation.set(accountId, now);
|
|
5994
6260
|
touchAccountActivity(accountId);
|
|
5995
6261
|
return true;
|
|
5996
|
-
} catch (err) {
|
|
5997
|
-
console.warn(
|
|
5998
|
-
`[SessionKeeper] Proactive session renewal failed for ${accountId}: ${getErrorMessage(err)}`
|
|
5999
|
-
);
|
|
6000
6262
|
}
|
|
6001
6263
|
}
|
|
6002
6264
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
@@ -7362,6 +7624,16 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
|
|
|
7362
7624
|
}
|
|
7363
7625
|
}
|
|
7364
7626
|
if (json?.data?.code === "Unauthorized" || json?.code === "Unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access")) {
|
|
7627
|
+
if (accountId) {
|
|
7628
|
+
try {
|
|
7629
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
7630
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
7631
|
+
if (refreshed.success) {
|
|
7632
|
+
return createQwenChatSession(headers, model, accountId, chatMode);
|
|
7633
|
+
}
|
|
7634
|
+
} catch {
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7365
7637
|
throw new QwenUpstreamError(
|
|
7366
7638
|
`Qwen create chat unauthorized: ${raw.substring(0, 300)}`,
|
|
7367
7639
|
"Unauthorized",
|
|
@@ -8057,16 +8329,28 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8057
8329
|
});
|
|
8058
8330
|
if (response2.status === 401) {
|
|
8059
8331
|
try {
|
|
8060
|
-
const
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8332
|
+
const rTok = localStorage.getItem("refresh_token");
|
|
8333
|
+
if (rTok) {
|
|
8334
|
+
const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
8335
|
+
method: "GET",
|
|
8336
|
+
credentials: "include",
|
|
8337
|
+
headers: {
|
|
8338
|
+
accept: "application/json, text/plain, */*",
|
|
8339
|
+
source: "web",
|
|
8340
|
+
version: "0.3.11",
|
|
8341
|
+
"x-request-origin": "https://chat.qwen.ai",
|
|
8342
|
+
timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
|
|
8343
|
+
authorization: `Bearer ${rTok}`
|
|
8344
|
+
},
|
|
8345
|
+
signal: AbortSignal.timeout(5e3)
|
|
8346
|
+
});
|
|
8066
8347
|
const refreshJson = await refreshRes.json().catch(() => null);
|
|
8067
|
-
|
|
8068
|
-
|
|
8348
|
+
const freshTok = refreshJson?.data?.access_token || refreshJson?.data?.token;
|
|
8349
|
+
const nextRTok = refreshJson?.data?.refresh_token;
|
|
8350
|
+
if (refreshJson?.success === true && freshTok) {
|
|
8069
8351
|
localStorage.setItem("token", freshTok);
|
|
8352
|
+
localStorage.setItem("access_token", freshTok);
|
|
8353
|
+
if (nextRTok) localStorage.setItem("refresh_token", nextRTok);
|
|
8070
8354
|
document.cookie = `token=${encodeURIComponent(freshTok)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
8071
8355
|
if (headers2["authorization"] || headers2["Authorization"]) {
|
|
8072
8356
|
headers2["authorization"] = `Bearer ${freshTok}`;
|
|
@@ -8104,13 +8388,29 @@ async function requestQwenTextInBrowser(accountId, method, path6, headers, body,
|
|
|
8104
8388
|
}
|
|
8105
8389
|
);
|
|
8106
8390
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
8107
|
-
|
|
8391
|
+
let response = await withQwenBrowserPage(
|
|
8108
8392
|
accountId,
|
|
8109
8393
|
evaluateRequest,
|
|
8110
8394
|
void 0,
|
|
8111
8395
|
options.timeoutMs,
|
|
8112
8396
|
recoverOnTimeout
|
|
8113
8397
|
);
|
|
8398
|
+
if (response.status === 401 && accountId) {
|
|
8399
|
+
try {
|
|
8400
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
8401
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
8402
|
+
if (refreshed.success) {
|
|
8403
|
+
response = await withQwenBrowserPage(
|
|
8404
|
+
accountId,
|
|
8405
|
+
evaluateRequest,
|
|
8406
|
+
void 0,
|
|
8407
|
+
options.timeoutMs,
|
|
8408
|
+
recoverOnTimeout
|
|
8409
|
+
);
|
|
8410
|
+
}
|
|
8411
|
+
} catch {
|
|
8412
|
+
}
|
|
8413
|
+
}
|
|
8114
8414
|
return new Response(response.raw, {
|
|
8115
8415
|
status: response.status,
|
|
8116
8416
|
headers: response.contentType ? { "content-type": response.contentType } : void 0
|
|
@@ -8633,7 +8933,11 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
|
|
|
8633
8933
|
);
|
|
8634
8934
|
try {
|
|
8635
8935
|
currentSettings = null;
|
|
8636
|
-
const {
|
|
8936
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
8937
|
+
if (accountId) {
|
|
8938
|
+
await refreshAccountToken2(accountId);
|
|
8939
|
+
}
|
|
8940
|
+
const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
|
|
8637
8941
|
requestHeaders = buildCapturedQwenHeaders(freshHeaders, {
|
|
8638
8942
|
referer: qwenUrl("/settings/personalization")
|
|
8639
8943
|
});
|