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.
@@ -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 res = await fetch("/api/v1/auths/", { method: "GET" });
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
- await loginToQwen(account.id, account.email, account.password);
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
- const liveCookies = await page.context().cookies();
4268
- const tokenCookie = liveCookies.find((c) => c.name === "token");
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: getQwenWebVersion() || "0.3.26",
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
- { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
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: getQwenWebVersion() || "0.3.26",
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
- { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
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
- await page.evaluate((sel) => {
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({ force: true, timeout: 2e3 }).catch(() => {
4704
+ await submitButton.click({ timeout: 2e3 }).catch(() => {
4653
4705
  });
4706
+ submitted = true;
4654
4707
  }
4655
4708
  } catch {
4656
4709
  }
4657
- const passInput = page.locator(passwordSelector).first();
4658
- await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
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 = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
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
- if (liveCookies.some((c) => c.name === "token")) {
4704
- return { success: true };
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
- } catch {
4707
- }
4708
- const currentUrl = page.url();
4709
- if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
4710
- if (await isPageLoggedIn(page, 3e3)) {
4711
- return { success: true };
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: getQwenWebVersion() || "0.3.26",
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.9",
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": {
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.6.9";
20
+ cachedVersion = "v1.6.10";
21
21
  return cachedVersion;
22
22
  }