qwenproxy-cli 1.6.8 → 1.6.10
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 +163 -44
- package/dist/delete-chats.js +166 -50
- package/dist/index.js +167 -51
- package/dist/login.js +163 -47
- package/dist/reset-cooldowns.js +163 -44
- package/dist/sync-clients.js +1 -1
- package/package.json +1 -1
- package/src/core/config.ts +1 -1
- package/src/core/version.ts +1 -1
- package/src/services/auth-playwright.ts +1 -1
- package/src/services/playwright.ts +174 -49
- package/src/services/qwen.ts +3 -3
package/dist/clean-cache.js
CHANGED
|
@@ -997,7 +997,7 @@ var init_config = __esm({
|
|
|
997
997
|
// Version header on Qwen API requests = the deployed web bundle version
|
|
998
998
|
// (fallback when dynamic DOM detection is not yet completed).
|
|
999
999
|
// Override via env when Qwen ships a new bundle.
|
|
1000
|
-
QWEN_WEB_VERSION: z.string().default("0.
|
|
1000
|
+
QWEN_WEB_VERSION: z.string().default("0.3.26"),
|
|
1001
1001
|
// Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
|
|
1002
1002
|
// (chats/new, settings): those work without them (live-probed). The
|
|
1003
1003
|
// completions path is the exception — the 0.2.86 HAR shows the real
|
|
@@ -2726,6 +2726,9 @@ function updateQwenWebVersion(version) {
|
|
|
2726
2726
|
dynamicWebVersion = version.trim();
|
|
2727
2727
|
}
|
|
2728
2728
|
}
|
|
2729
|
+
function getQwenWebVersion() {
|
|
2730
|
+
return dynamicWebVersion || config.qwen.webVersion;
|
|
2731
|
+
}
|
|
2729
2732
|
var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
|
|
2730
2733
|
var init_qwen_headers = __esm({
|
|
2731
2734
|
"src/services/qwen-headers.ts"() {
|
|
@@ -2987,7 +2990,19 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
2987
2990
|
if (localStorage.getItem("qwen_token_logged_out_marker")) {
|
|
2988
2991
|
return false;
|
|
2989
2992
|
}
|
|
2990
|
-
const
|
|
2993
|
+
const lsToken = (localStorage.getItem("token") || "").trim();
|
|
2994
|
+
if (lsToken && !/(?:^|;\s*)token=/.test(document.cookie || "")) {
|
|
2995
|
+
try {
|
|
2996
|
+
document.cookie = `token=${encodeURIComponent(lsToken)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
2997
|
+
} catch {
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
const authHeaders = lsToken ? { Authorization: `Bearer ${lsToken}` } : {};
|
|
3001
|
+
const res = await fetch("/api/v1/auths/", {
|
|
3002
|
+
method: "GET",
|
|
3003
|
+
headers: authHeaders,
|
|
3004
|
+
credentials: "include"
|
|
3005
|
+
});
|
|
2991
3006
|
if (res.status !== 200) return false;
|
|
2992
3007
|
const json = await res.json().catch(() => null);
|
|
2993
3008
|
if (!json) return false;
|
|
@@ -3005,6 +3020,7 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3005
3020
|
try {
|
|
3006
3021
|
const settingsRes = await fetch("/api/v2/users/user/settings", {
|
|
3007
3022
|
method: "GET",
|
|
3023
|
+
headers: authHeaders,
|
|
3008
3024
|
credentials: "include",
|
|
3009
3025
|
signal: AbortSignal.timeout(3e3)
|
|
3010
3026
|
});
|
|
@@ -3833,8 +3849,15 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3833
3849
|
const hasAuthCookie = cookies.some(
|
|
3834
3850
|
(c) => c.name.toLowerCase().includes("token") || c.name.toLowerCase().includes("session")
|
|
3835
3851
|
);
|
|
3852
|
+
let initialLoginAttempted = false;
|
|
3836
3853
|
if (!hasAuthCookie && account.email && account.password) {
|
|
3837
|
-
|
|
3854
|
+
initialLoginAttempted = true;
|
|
3855
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
3856
|
+
if (!loggedIn) {
|
|
3857
|
+
throw new Error(
|
|
3858
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
3859
|
+
);
|
|
3860
|
+
}
|
|
3838
3861
|
}
|
|
3839
3862
|
try {
|
|
3840
3863
|
await acctPage.goto(qwenUrl("/"), {
|
|
@@ -3844,13 +3867,21 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3844
3867
|
await sleep2(1500);
|
|
3845
3868
|
const currentUrl = acctPage.url();
|
|
3846
3869
|
const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
|
|
3847
|
-
if (isAuthUrl && account.email && account.password) {
|
|
3870
|
+
if (isAuthUrl && !initialLoginAttempted && account.email && account.password) {
|
|
3848
3871
|
console.log(
|
|
3849
3872
|
`[Playwright] Initial login needed for ${maskEmail(account.email)}, authenticating...`
|
|
3850
3873
|
);
|
|
3851
|
-
await loginToQwen(account.id, account.email, account.password);
|
|
3874
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
3875
|
+
if (!loggedIn) {
|
|
3876
|
+
throw new Error(
|
|
3877
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
3878
|
+
);
|
|
3879
|
+
}
|
|
3852
3880
|
}
|
|
3853
3881
|
} catch (err) {
|
|
3882
|
+
if (err?.message?.includes("Authentication failed for account")) {
|
|
3883
|
+
throw err;
|
|
3884
|
+
}
|
|
3854
3885
|
console.warn(
|
|
3855
3886
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
3856
3887
|
);
|
|
@@ -3993,10 +4024,30 @@ async function loginToQwen(accountId, email, password) {
|
|
|
3993
4024
|
const uiResult = await loginViaUi(page, email, password);
|
|
3994
4025
|
if (uiResult.success) {
|
|
3995
4026
|
cookieCaches.delete(accountId);
|
|
3996
|
-
await saveStorageState(page.context(), accountId);
|
|
3997
4027
|
try {
|
|
3998
|
-
|
|
3999
|
-
|
|
4028
|
+
let liveCookies = await page.context().cookies();
|
|
4029
|
+
let tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
4030
|
+
if (!tokenCookie) {
|
|
4031
|
+
const lsTok = uiResult.token || await page.evaluate(() => (localStorage.getItem("token") || "").trim()).catch(() => "");
|
|
4032
|
+
if (lsTok) {
|
|
4033
|
+
await page.context().addCookies([
|
|
4034
|
+
{
|
|
4035
|
+
name: "token",
|
|
4036
|
+
value: lsTok,
|
|
4037
|
+
domain: ".qwen.ai",
|
|
4038
|
+
path: "/",
|
|
4039
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
4040
|
+
httpOnly: false,
|
|
4041
|
+
secure: true,
|
|
4042
|
+
sameSite: "Lax"
|
|
4043
|
+
}
|
|
4044
|
+
]).catch(() => {
|
|
4045
|
+
});
|
|
4046
|
+
liveCookies = await page.context().cookies();
|
|
4047
|
+
tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
await saveStorageState(page.context(), accountId);
|
|
4000
4051
|
if (tokenCookie) {
|
|
4001
4052
|
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
4002
4053
|
const cache = getHeaderCache(accountId);
|
|
@@ -4005,7 +4056,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4005
4056
|
markAccountHeadersReady(accountId);
|
|
4006
4057
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4007
4058
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
4008
|
-
let rTok = uiResult.refreshToken;
|
|
4059
|
+
let rTok = uiResult.refreshToken || liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4009
4060
|
if (!rTok) {
|
|
4010
4061
|
try {
|
|
4011
4062
|
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
@@ -4086,8 +4137,9 @@ async function loginViaApi(page, email, password) {
|
|
|
4086
4137
|
}
|
|
4087
4138
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4088
4139
|
const requestId = crypto3.randomUUID();
|
|
4140
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
4089
4141
|
const evalRes = await page.evaluate(
|
|
4090
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
4142
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
|
|
4091
4143
|
try {
|
|
4092
4144
|
const response = await fetch(signinUrl2, {
|
|
4093
4145
|
method: "POST",
|
|
@@ -4097,7 +4149,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4097
4149
|
accept: "application/json, text/plain, */*",
|
|
4098
4150
|
"content-type": "application/json",
|
|
4099
4151
|
source: "web",
|
|
4100
|
-
version
|
|
4152
|
+
version,
|
|
4101
4153
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4102
4154
|
timezone: tz,
|
|
4103
4155
|
"x-request-id": requestId2
|
|
@@ -4110,7 +4162,14 @@ async function loginViaApi(page, email, password) {
|
|
|
4110
4162
|
return { ok: false, error: e.message };
|
|
4111
4163
|
}
|
|
4112
4164
|
},
|
|
4113
|
-
{
|
|
4165
|
+
{
|
|
4166
|
+
email,
|
|
4167
|
+
password: hashedPassword,
|
|
4168
|
+
signinUrl,
|
|
4169
|
+
requestId,
|
|
4170
|
+
tz: formatAsciiTimezone(),
|
|
4171
|
+
version: webVersion
|
|
4172
|
+
}
|
|
4114
4173
|
).catch(() => null);
|
|
4115
4174
|
let data = evalRes?.data;
|
|
4116
4175
|
let signinSuccess = Boolean(
|
|
@@ -4119,7 +4178,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4119
4178
|
if (!signinSuccess && !data?.data?.code) {
|
|
4120
4179
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4121
4180
|
const fallbackRes = await page.evaluate(
|
|
4122
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
4181
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz, version }) => {
|
|
4123
4182
|
try {
|
|
4124
4183
|
const response = await fetch(fallbackUrl2, {
|
|
4125
4184
|
method: "POST",
|
|
@@ -4129,7 +4188,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4129
4188
|
accept: "application/json, text/plain, */*",
|
|
4130
4189
|
"content-type": "application/json",
|
|
4131
4190
|
source: "web",
|
|
4132
|
-
version
|
|
4191
|
+
version,
|
|
4133
4192
|
timezone: tz,
|
|
4134
4193
|
"x-request-id": requestId2
|
|
4135
4194
|
},
|
|
@@ -4141,7 +4200,14 @@ async function loginViaApi(page, email, password) {
|
|
|
4141
4200
|
return { ok: false, error: e.message };
|
|
4142
4201
|
}
|
|
4143
4202
|
},
|
|
4144
|
-
{
|
|
4203
|
+
{
|
|
4204
|
+
email,
|
|
4205
|
+
password: hashedPassword,
|
|
4206
|
+
fallbackUrl,
|
|
4207
|
+
requestId,
|
|
4208
|
+
tz: formatAsciiTimezone(),
|
|
4209
|
+
version: webVersion
|
|
4210
|
+
}
|
|
4145
4211
|
).catch(() => null);
|
|
4146
4212
|
if (fallbackRes?.data) {
|
|
4147
4213
|
data = fallbackRes.data;
|
|
@@ -4366,28 +4432,34 @@ async function loginViaUi(page, email, password) {
|
|
|
4366
4432
|
await sleep2(500);
|
|
4367
4433
|
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")';
|
|
4368
4434
|
const submitButton = page.locator(submitSelector).first();
|
|
4369
|
-
|
|
4370
|
-
try {
|
|
4371
|
-
const btn = document.querySelector(sel);
|
|
4372
|
-
if (btn) {
|
|
4373
|
-
btn.removeAttribute("disabled");
|
|
4374
|
-
btn.classList.remove("ant-btn-disabled");
|
|
4375
|
-
btn.click();
|
|
4376
|
-
}
|
|
4377
|
-
} catch {
|
|
4378
|
-
}
|
|
4379
|
-
}, submitSelector).catch(() => {
|
|
4380
|
-
});
|
|
4435
|
+
let submitted = false;
|
|
4381
4436
|
try {
|
|
4382
4437
|
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
4383
|
-
await submitButton.click({
|
|
4438
|
+
await submitButton.click({ timeout: 2e3 }).catch(() => {
|
|
4384
4439
|
});
|
|
4440
|
+
submitted = true;
|
|
4385
4441
|
}
|
|
4386
4442
|
} catch {
|
|
4387
4443
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4444
|
+
if (!submitted) {
|
|
4445
|
+
await page.evaluate(() => {
|
|
4446
|
+
try {
|
|
4447
|
+
const btn = document.querySelector(
|
|
4448
|
+
'button.qwenchat-auth-pc-submit-button, button[type="submit"]'
|
|
4449
|
+
);
|
|
4450
|
+
if (btn) {
|
|
4451
|
+
btn.removeAttribute("disabled");
|
|
4452
|
+
btn.classList.remove("ant-btn-disabled");
|
|
4453
|
+
btn.click();
|
|
4454
|
+
}
|
|
4455
|
+
} catch {
|
|
4456
|
+
}
|
|
4457
|
+
}).catch(() => {
|
|
4458
|
+
});
|
|
4459
|
+
const passInput = page.locator(passwordSelector).first();
|
|
4460
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4391
4463
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4392
4464
|
while (Date.now() < postSubmitDeadline) {
|
|
4393
4465
|
if (page.isClosed()) break;
|
|
@@ -4405,9 +4477,12 @@ async function loginViaUi(page, email, password) {
|
|
|
4405
4477
|
".ant-form-item-explain-error",
|
|
4406
4478
|
".ant-message-error",
|
|
4407
4479
|
".ant-message-notice",
|
|
4480
|
+
".ant-message-custom-content",
|
|
4481
|
+
".ant-notification-notice",
|
|
4408
4482
|
'[role="alert"]',
|
|
4409
4483
|
".qwenchat-auth-error",
|
|
4410
|
-
".auth-error-message"
|
|
4484
|
+
".auth-error-message",
|
|
4485
|
+
".qwenchat-toast"
|
|
4411
4486
|
].join(", ");
|
|
4412
4487
|
const errorEl = page.locator(errorSelector).first();
|
|
4413
4488
|
if (await errorEl.isVisible().catch(() => false)) {
|
|
@@ -4421,7 +4496,15 @@ async function loginViaUi(page, email, password) {
|
|
|
4421
4496
|
};
|
|
4422
4497
|
}
|
|
4423
4498
|
}
|
|
4424
|
-
const codeSelector =
|
|
4499
|
+
const codeSelector = [
|
|
4500
|
+
'input[placeholder*="code" i]',
|
|
4501
|
+
'input[placeholder*="c\xF3digo" i]',
|
|
4502
|
+
'input[name*="code" i]',
|
|
4503
|
+
".qwenchat-verification-code-input",
|
|
4504
|
+
".qwenchat-verification-code-input-cell",
|
|
4505
|
+
'[aria-label*="Verification code" i]',
|
|
4506
|
+
'[aria-label*="c\xF3digo de verifica\xE7\xE3o" i]'
|
|
4507
|
+
].join(", ");
|
|
4425
4508
|
const codeInput = page.locator(codeSelector).first();
|
|
4426
4509
|
if (await codeInput.isVisible().catch(() => false)) {
|
|
4427
4510
|
return {
|
|
@@ -4431,16 +4514,44 @@ async function loginViaUi(page, email, password) {
|
|
|
4431
4514
|
}
|
|
4432
4515
|
try {
|
|
4433
4516
|
const liveCookies = await page.context().cookies();
|
|
4434
|
-
|
|
4435
|
-
|
|
4517
|
+
const rTokCookie = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4518
|
+
const tokCookie = liveCookies.find((c) => c.name === "token")?.value;
|
|
4519
|
+
if (tokCookie) {
|
|
4520
|
+
return { success: true, token: tokCookie, refreshToken: rTokCookie };
|
|
4436
4521
|
}
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4522
|
+
const currentUrl = page.url();
|
|
4523
|
+
if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
|
|
4524
|
+
const lsTok = await page.evaluate(() => {
|
|
4525
|
+
const t = (localStorage.getItem("token") || "").trim();
|
|
4526
|
+
if (t) {
|
|
4527
|
+
try {
|
|
4528
|
+
document.cookie = `token=${encodeURIComponent(t)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4529
|
+
} catch {
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
return t;
|
|
4533
|
+
}).catch(() => "");
|
|
4534
|
+
if (lsTok) {
|
|
4535
|
+
await page.context().addCookies([
|
|
4536
|
+
{
|
|
4537
|
+
name: "token",
|
|
4538
|
+
value: lsTok,
|
|
4539
|
+
domain: ".qwen.ai",
|
|
4540
|
+
path: "/",
|
|
4541
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
4542
|
+
httpOnly: false,
|
|
4543
|
+
secure: true,
|
|
4544
|
+
sameSite: "Lax"
|
|
4545
|
+
}
|
|
4546
|
+
]).catch(() => {
|
|
4547
|
+
});
|
|
4548
|
+
return { success: true, token: lsTok, refreshToken: rTokCookie };
|
|
4549
|
+
}
|
|
4550
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
4551
|
+
return { success: true, refreshToken: rTokCookie };
|
|
4552
|
+
}
|
|
4443
4553
|
}
|
|
4554
|
+
} catch {
|
|
4444
4555
|
}
|
|
4445
4556
|
await sleep2(1e3);
|
|
4446
4557
|
}
|
|
@@ -4470,10 +4581,18 @@ async function refreshAccountToken(accountId) {
|
|
|
4470
4581
|
} catch {
|
|
4471
4582
|
}
|
|
4472
4583
|
}
|
|
4584
|
+
if (!refreshToken) {
|
|
4585
|
+
try {
|
|
4586
|
+
const liveCookies = await page.context().cookies();
|
|
4587
|
+
refreshToken = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4588
|
+
} catch {
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4473
4591
|
if (refreshToken) {
|
|
4474
4592
|
try {
|
|
4593
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
4475
4594
|
const refreshResult = await page.evaluate(
|
|
4476
|
-
async ({ rTok, tz }) => {
|
|
4595
|
+
async ({ rTok, tz, version }) => {
|
|
4477
4596
|
try {
|
|
4478
4597
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
4479
4598
|
method: "GET",
|
|
@@ -4481,7 +4600,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4481
4600
|
headers: {
|
|
4482
4601
|
accept: "application/json, text/plain, */*",
|
|
4483
4602
|
source: "web",
|
|
4484
|
-
version
|
|
4603
|
+
version,
|
|
4485
4604
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4486
4605
|
timezone: tz,
|
|
4487
4606
|
authorization: `Bearer ${rTok}`
|
|
@@ -4493,7 +4612,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4493
4612
|
return { ok: false, error: e.message };
|
|
4494
4613
|
}
|
|
4495
4614
|
},
|
|
4496
|
-
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
4615
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone(), version: webVersion }
|
|
4497
4616
|
);
|
|
4498
4617
|
const json = refreshResult?.data;
|
|
4499
4618
|
if (json && json.success === true && json.data) {
|