qwenproxy-cli 1.6.9 → 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 +159 -43
- package/dist/delete-chats.js +159 -43
- package/dist/index.js +160 -44
- package/dist/login.js +159 -43
- package/dist/reset-cooldowns.js +159 -43
- package/package.json +1 -1
- package/src/core/version.ts +1 -1
- package/src/services/playwright.ts +171 -46
package/dist/reset-cooldowns.js
CHANGED
|
@@ -3256,7 +3256,19 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3256
3256
|
if (localStorage.getItem("qwen_token_logged_out_marker")) {
|
|
3257
3257
|
return false;
|
|
3258
3258
|
}
|
|
3259
|
-
const
|
|
3259
|
+
const lsToken = (localStorage.getItem("token") || "").trim();
|
|
3260
|
+
if (lsToken && !/(?:^|;\s*)token=/.test(document.cookie || "")) {
|
|
3261
|
+
try {
|
|
3262
|
+
document.cookie = `token=${encodeURIComponent(lsToken)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
3263
|
+
} catch {
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
const authHeaders = lsToken ? { Authorization: `Bearer ${lsToken}` } : {};
|
|
3267
|
+
const res = await fetch("/api/v1/auths/", {
|
|
3268
|
+
method: "GET",
|
|
3269
|
+
headers: authHeaders,
|
|
3270
|
+
credentials: "include"
|
|
3271
|
+
});
|
|
3260
3272
|
if (res.status !== 200) return false;
|
|
3261
3273
|
const json = await res.json().catch(() => null);
|
|
3262
3274
|
if (!json) return false;
|
|
@@ -3274,6 +3286,7 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
|
|
|
3274
3286
|
try {
|
|
3275
3287
|
const settingsRes = await fetch("/api/v2/users/user/settings", {
|
|
3276
3288
|
method: "GET",
|
|
3289
|
+
headers: authHeaders,
|
|
3277
3290
|
credentials: "include",
|
|
3278
3291
|
signal: AbortSignal.timeout(3e3)
|
|
3279
3292
|
});
|
|
@@ -4102,8 +4115,15 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4102
4115
|
const hasAuthCookie = cookies.some(
|
|
4103
4116
|
(c) => c.name.toLowerCase().includes("token") || c.name.toLowerCase().includes("session")
|
|
4104
4117
|
);
|
|
4118
|
+
let initialLoginAttempted = false;
|
|
4105
4119
|
if (!hasAuthCookie && account.email && account.password) {
|
|
4106
|
-
|
|
4120
|
+
initialLoginAttempted = true;
|
|
4121
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
4122
|
+
if (!loggedIn) {
|
|
4123
|
+
throw new Error(
|
|
4124
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
4125
|
+
);
|
|
4126
|
+
}
|
|
4107
4127
|
}
|
|
4108
4128
|
try {
|
|
4109
4129
|
await acctPage.goto(qwenUrl("/"), {
|
|
@@ -4113,13 +4133,21 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4113
4133
|
await sleep2(1500);
|
|
4114
4134
|
const currentUrl = acctPage.url();
|
|
4115
4135
|
const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
|
|
4116
|
-
if (isAuthUrl && account.email && account.password) {
|
|
4136
|
+
if (isAuthUrl && !initialLoginAttempted && account.email && account.password) {
|
|
4117
4137
|
console.log(
|
|
4118
4138
|
`[Playwright] Initial login needed for ${maskEmail(account.email)}, authenticating...`
|
|
4119
4139
|
);
|
|
4120
|
-
await loginToQwen(account.id, account.email, account.password);
|
|
4140
|
+
const loggedIn = await loginToQwen(account.id, account.email, account.password);
|
|
4141
|
+
if (!loggedIn) {
|
|
4142
|
+
throw new Error(
|
|
4143
|
+
`Authentication failed for account ${maskEmail(account.email)}`
|
|
4144
|
+
);
|
|
4145
|
+
}
|
|
4121
4146
|
}
|
|
4122
4147
|
} catch (err) {
|
|
4148
|
+
if (err?.message?.includes("Authentication failed for account")) {
|
|
4149
|
+
throw err;
|
|
4150
|
+
}
|
|
4123
4151
|
console.warn(
|
|
4124
4152
|
`\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
|
|
4125
4153
|
);
|
|
@@ -4262,10 +4290,30 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4262
4290
|
const uiResult = await loginViaUi(page, email, password);
|
|
4263
4291
|
if (uiResult.success) {
|
|
4264
4292
|
cookieCaches.delete(accountId);
|
|
4265
|
-
await saveStorageState(page.context(), accountId);
|
|
4266
4293
|
try {
|
|
4267
|
-
|
|
4268
|
-
|
|
4294
|
+
let liveCookies = await page.context().cookies();
|
|
4295
|
+
let tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
4296
|
+
if (!tokenCookie) {
|
|
4297
|
+
const lsTok = uiResult.token || await page.evaluate(() => (localStorage.getItem("token") || "").trim()).catch(() => "");
|
|
4298
|
+
if (lsTok) {
|
|
4299
|
+
await page.context().addCookies([
|
|
4300
|
+
{
|
|
4301
|
+
name: "token",
|
|
4302
|
+
value: lsTok,
|
|
4303
|
+
domain: ".qwen.ai",
|
|
4304
|
+
path: "/",
|
|
4305
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
4306
|
+
httpOnly: false,
|
|
4307
|
+
secure: true,
|
|
4308
|
+
sameSite: "Lax"
|
|
4309
|
+
}
|
|
4310
|
+
]).catch(() => {
|
|
4311
|
+
});
|
|
4312
|
+
liveCookies = await page.context().cookies();
|
|
4313
|
+
tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
await saveStorageState(page.context(), accountId);
|
|
4269
4317
|
if (tokenCookie) {
|
|
4270
4318
|
const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
4271
4319
|
const cache = getHeaderCache(accountId);
|
|
@@ -4274,7 +4322,7 @@ async function loginToQwen(accountId, email, password) {
|
|
|
4274
4322
|
markAccountHeadersReady(accountId);
|
|
4275
4323
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
4276
4324
|
const exp = parseJwtExpiry(tokenCookie.value);
|
|
4277
|
-
let rTok = uiResult.refreshToken;
|
|
4325
|
+
let rTok = uiResult.refreshToken || liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4278
4326
|
if (!rTok) {
|
|
4279
4327
|
try {
|
|
4280
4328
|
rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
|
|
@@ -4355,8 +4403,9 @@ async function loginViaApi(page, email, password) {
|
|
|
4355
4403
|
}
|
|
4356
4404
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
4357
4405
|
const requestId = crypto3.randomUUID();
|
|
4406
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
4358
4407
|
const evalRes = await page.evaluate(
|
|
4359
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
4408
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
|
|
4360
4409
|
try {
|
|
4361
4410
|
const response = await fetch(signinUrl2, {
|
|
4362
4411
|
method: "POST",
|
|
@@ -4366,7 +4415,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4366
4415
|
accept: "application/json, text/plain, */*",
|
|
4367
4416
|
"content-type": "application/json",
|
|
4368
4417
|
source: "web",
|
|
4369
|
-
version
|
|
4418
|
+
version,
|
|
4370
4419
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4371
4420
|
timezone: tz,
|
|
4372
4421
|
"x-request-id": requestId2
|
|
@@ -4379,7 +4428,14 @@ async function loginViaApi(page, email, password) {
|
|
|
4379
4428
|
return { ok: false, error: e.message };
|
|
4380
4429
|
}
|
|
4381
4430
|
},
|
|
4382
|
-
{
|
|
4431
|
+
{
|
|
4432
|
+
email,
|
|
4433
|
+
password: hashedPassword,
|
|
4434
|
+
signinUrl,
|
|
4435
|
+
requestId,
|
|
4436
|
+
tz: formatAsciiTimezone(),
|
|
4437
|
+
version: webVersion
|
|
4438
|
+
}
|
|
4383
4439
|
).catch(() => null);
|
|
4384
4440
|
let data = evalRes?.data;
|
|
4385
4441
|
let signinSuccess = Boolean(
|
|
@@ -4388,7 +4444,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4388
4444
|
if (!signinSuccess && !data?.data?.code) {
|
|
4389
4445
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
4390
4446
|
const fallbackRes = await page.evaluate(
|
|
4391
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
4447
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz, version }) => {
|
|
4392
4448
|
try {
|
|
4393
4449
|
const response = await fetch(fallbackUrl2, {
|
|
4394
4450
|
method: "POST",
|
|
@@ -4398,7 +4454,7 @@ async function loginViaApi(page, email, password) {
|
|
|
4398
4454
|
accept: "application/json, text/plain, */*",
|
|
4399
4455
|
"content-type": "application/json",
|
|
4400
4456
|
source: "web",
|
|
4401
|
-
version
|
|
4457
|
+
version,
|
|
4402
4458
|
timezone: tz,
|
|
4403
4459
|
"x-request-id": requestId2
|
|
4404
4460
|
},
|
|
@@ -4410,7 +4466,14 @@ async function loginViaApi(page, email, password) {
|
|
|
4410
4466
|
return { ok: false, error: e.message };
|
|
4411
4467
|
}
|
|
4412
4468
|
},
|
|
4413
|
-
{
|
|
4469
|
+
{
|
|
4470
|
+
email,
|
|
4471
|
+
password: hashedPassword,
|
|
4472
|
+
fallbackUrl,
|
|
4473
|
+
requestId,
|
|
4474
|
+
tz: formatAsciiTimezone(),
|
|
4475
|
+
version: webVersion
|
|
4476
|
+
}
|
|
4414
4477
|
).catch(() => null);
|
|
4415
4478
|
if (fallbackRes?.data) {
|
|
4416
4479
|
data = fallbackRes.data;
|
|
@@ -4635,28 +4698,34 @@ async function loginViaUi(page, email, password) {
|
|
|
4635
4698
|
await sleep2(500);
|
|
4636
4699
|
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")';
|
|
4637
4700
|
const submitButton = page.locator(submitSelector).first();
|
|
4638
|
-
|
|
4639
|
-
try {
|
|
4640
|
-
const btn = document.querySelector(sel);
|
|
4641
|
-
if (btn) {
|
|
4642
|
-
btn.removeAttribute("disabled");
|
|
4643
|
-
btn.classList.remove("ant-btn-disabled");
|
|
4644
|
-
btn.click();
|
|
4645
|
-
}
|
|
4646
|
-
} catch {
|
|
4647
|
-
}
|
|
4648
|
-
}, submitSelector).catch(() => {
|
|
4649
|
-
});
|
|
4701
|
+
let submitted = false;
|
|
4650
4702
|
try {
|
|
4651
4703
|
if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
|
|
4652
|
-
await submitButton.click({
|
|
4704
|
+
await submitButton.click({ timeout: 2e3 }).catch(() => {
|
|
4653
4705
|
});
|
|
4706
|
+
submitted = true;
|
|
4654
4707
|
}
|
|
4655
4708
|
} catch {
|
|
4656
4709
|
}
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4710
|
+
if (!submitted) {
|
|
4711
|
+
await page.evaluate(() => {
|
|
4712
|
+
try {
|
|
4713
|
+
const btn = document.querySelector(
|
|
4714
|
+
'button.qwenchat-auth-pc-submit-button, button[type="submit"]'
|
|
4715
|
+
);
|
|
4716
|
+
if (btn) {
|
|
4717
|
+
btn.removeAttribute("disabled");
|
|
4718
|
+
btn.classList.remove("ant-btn-disabled");
|
|
4719
|
+
btn.click();
|
|
4720
|
+
}
|
|
4721
|
+
} catch {
|
|
4722
|
+
}
|
|
4723
|
+
}).catch(() => {
|
|
4724
|
+
});
|
|
4725
|
+
const passInput = page.locator(passwordSelector).first();
|
|
4726
|
+
await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4660
4729
|
const postSubmitDeadline = Date.now() + 15e3;
|
|
4661
4730
|
while (Date.now() < postSubmitDeadline) {
|
|
4662
4731
|
if (page.isClosed()) break;
|
|
@@ -4674,9 +4743,12 @@ async function loginViaUi(page, email, password) {
|
|
|
4674
4743
|
".ant-form-item-explain-error",
|
|
4675
4744
|
".ant-message-error",
|
|
4676
4745
|
".ant-message-notice",
|
|
4746
|
+
".ant-message-custom-content",
|
|
4747
|
+
".ant-notification-notice",
|
|
4677
4748
|
'[role="alert"]',
|
|
4678
4749
|
".qwenchat-auth-error",
|
|
4679
|
-
".auth-error-message"
|
|
4750
|
+
".auth-error-message",
|
|
4751
|
+
".qwenchat-toast"
|
|
4680
4752
|
].join(", ");
|
|
4681
4753
|
const errorEl = page.locator(errorSelector).first();
|
|
4682
4754
|
if (await errorEl.isVisible().catch(() => false)) {
|
|
@@ -4690,7 +4762,15 @@ async function loginViaUi(page, email, password) {
|
|
|
4690
4762
|
};
|
|
4691
4763
|
}
|
|
4692
4764
|
}
|
|
4693
|
-
const codeSelector =
|
|
4765
|
+
const codeSelector = [
|
|
4766
|
+
'input[placeholder*="code" i]',
|
|
4767
|
+
'input[placeholder*="c\xF3digo" i]',
|
|
4768
|
+
'input[name*="code" i]',
|
|
4769
|
+
".qwenchat-verification-code-input",
|
|
4770
|
+
".qwenchat-verification-code-input-cell",
|
|
4771
|
+
'[aria-label*="Verification code" i]',
|
|
4772
|
+
'[aria-label*="c\xF3digo de verifica\xE7\xE3o" i]'
|
|
4773
|
+
].join(", ");
|
|
4694
4774
|
const codeInput = page.locator(codeSelector).first();
|
|
4695
4775
|
if (await codeInput.isVisible().catch(() => false)) {
|
|
4696
4776
|
return {
|
|
@@ -4700,16 +4780,44 @@ async function loginViaUi(page, email, password) {
|
|
|
4700
4780
|
}
|
|
4701
4781
|
try {
|
|
4702
4782
|
const liveCookies = await page.context().cookies();
|
|
4703
|
-
|
|
4704
|
-
|
|
4783
|
+
const rTokCookie = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4784
|
+
const tokCookie = liveCookies.find((c) => c.name === "token")?.value;
|
|
4785
|
+
if (tokCookie) {
|
|
4786
|
+
return { success: true, token: tokCookie, refreshToken: rTokCookie };
|
|
4705
4787
|
}
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4788
|
+
const currentUrl = page.url();
|
|
4789
|
+
if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
|
|
4790
|
+
const lsTok = await page.evaluate(() => {
|
|
4791
|
+
const t = (localStorage.getItem("token") || "").trim();
|
|
4792
|
+
if (t) {
|
|
4793
|
+
try {
|
|
4794
|
+
document.cookie = `token=${encodeURIComponent(t)}; path=/; domain=.qwen.ai; max-age=31536000`;
|
|
4795
|
+
} catch {
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
return t;
|
|
4799
|
+
}).catch(() => "");
|
|
4800
|
+
if (lsTok) {
|
|
4801
|
+
await page.context().addCookies([
|
|
4802
|
+
{
|
|
4803
|
+
name: "token",
|
|
4804
|
+
value: lsTok,
|
|
4805
|
+
domain: ".qwen.ai",
|
|
4806
|
+
path: "/",
|
|
4807
|
+
expires: Math.floor(Date.now() / 1e3) + 31536e3,
|
|
4808
|
+
httpOnly: false,
|
|
4809
|
+
secure: true,
|
|
4810
|
+
sameSite: "Lax"
|
|
4811
|
+
}
|
|
4812
|
+
]).catch(() => {
|
|
4813
|
+
});
|
|
4814
|
+
return { success: true, token: lsTok, refreshToken: rTokCookie };
|
|
4815
|
+
}
|
|
4816
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
4817
|
+
return { success: true, refreshToken: rTokCookie };
|
|
4818
|
+
}
|
|
4712
4819
|
}
|
|
4820
|
+
} catch {
|
|
4713
4821
|
}
|
|
4714
4822
|
await sleep2(1e3);
|
|
4715
4823
|
}
|
|
@@ -4739,10 +4847,18 @@ async function refreshAccountToken(accountId) {
|
|
|
4739
4847
|
} catch {
|
|
4740
4848
|
}
|
|
4741
4849
|
}
|
|
4850
|
+
if (!refreshToken) {
|
|
4851
|
+
try {
|
|
4852
|
+
const liveCookies = await page.context().cookies();
|
|
4853
|
+
refreshToken = liveCookies.find((c) => c.name === "refresh_token")?.value;
|
|
4854
|
+
} catch {
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4742
4857
|
if (refreshToken) {
|
|
4743
4858
|
try {
|
|
4859
|
+
const webVersion = getQwenWebVersion() || "0.3.26";
|
|
4744
4860
|
const refreshResult = await page.evaluate(
|
|
4745
|
-
async ({ rTok, tz }) => {
|
|
4861
|
+
async ({ rTok, tz, version }) => {
|
|
4746
4862
|
try {
|
|
4747
4863
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
4748
4864
|
method: "GET",
|
|
@@ -4750,7 +4866,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4750
4866
|
headers: {
|
|
4751
4867
|
accept: "application/json, text/plain, */*",
|
|
4752
4868
|
source: "web",
|
|
4753
|
-
version
|
|
4869
|
+
version,
|
|
4754
4870
|
"x-request-origin": "https://chat.qwen.ai",
|
|
4755
4871
|
timezone: tz,
|
|
4756
4872
|
authorization: `Bearer ${rTok}`
|
|
@@ -4762,7 +4878,7 @@ async function refreshAccountToken(accountId) {
|
|
|
4762
4878
|
return { ok: false, error: e.message };
|
|
4763
4879
|
}
|
|
4764
4880
|
},
|
|
4765
|
-
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
4881
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone(), version: webVersion }
|
|
4766
4882
|
);
|
|
4767
4883
|
const json = refreshResult?.data;
|
|
4768
4884
|
if (json && json.success === true && json.data) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.10",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|