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.
@@ -1334,7 +1334,7 @@ var init_config = __esm({
1334
1334
  // Version header on Qwen API requests = the deployed web bundle version
1335
1335
  // (fallback when dynamic DOM detection is not yet completed).
1336
1336
  // Override via env when Qwen ships a new bundle.
1337
- QWEN_WEB_VERSION: z.string().default("0.2.91"),
1337
+ QWEN_WEB_VERSION: z.string().default("0.3.26"),
1338
1338
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
1339
1339
  // (chats/new, settings): those work without them (live-probed). The
1340
1340
  // completions path is the exception — the 0.2.86 HAR shows the real
@@ -2768,6 +2768,9 @@ function updateQwenWebVersion(version) {
2768
2768
  dynamicWebVersion = version.trim();
2769
2769
  }
2770
2770
  }
2771
+ function getQwenWebVersion() {
2772
+ return dynamicWebVersion || config.qwen.webVersion;
2773
+ }
2771
2774
  var dynamicWebVersion, QWEN_WEB_VERSION, DEFAULT_QWEN_USER_AGENT, QWEN_TIMEZONE_HEADER;
2772
2775
  var init_qwen_headers = __esm({
2773
2776
  "src/services/qwen-headers.ts"() {
@@ -3253,7 +3256,19 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
3253
3256
  if (localStorage.getItem("qwen_token_logged_out_marker")) {
3254
3257
  return false;
3255
3258
  }
3256
- 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
+ });
3257
3272
  if (res.status !== 200) return false;
3258
3273
  const json = await res.json().catch(() => null);
3259
3274
  if (!json) return false;
@@ -3271,6 +3286,7 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
3271
3286
  try {
3272
3287
  const settingsRes = await fetch("/api/v2/users/user/settings", {
3273
3288
  method: "GET",
3289
+ headers: authHeaders,
3274
3290
  credentials: "include",
3275
3291
  signal: AbortSignal.timeout(3e3)
3276
3292
  });
@@ -4099,8 +4115,15 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4099
4115
  const hasAuthCookie = cookies.some(
4100
4116
  (c) => c.name.toLowerCase().includes("token") || c.name.toLowerCase().includes("session")
4101
4117
  );
4118
+ let initialLoginAttempted = false;
4102
4119
  if (!hasAuthCookie && account.email && account.password) {
4103
- 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
+ }
4104
4127
  }
4105
4128
  try {
4106
4129
  await acctPage.goto(qwenUrl("/"), {
@@ -4110,13 +4133,21 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
4110
4133
  await sleep2(1500);
4111
4134
  const currentUrl = acctPage.url();
4112
4135
  const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
4113
- if (isAuthUrl && account.email && account.password) {
4136
+ if (isAuthUrl && !initialLoginAttempted && account.email && account.password) {
4114
4137
  console.log(
4115
4138
  `[Playwright] Initial login needed for ${maskEmail(account.email)}, authenticating...`
4116
4139
  );
4117
- 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
+ }
4118
4146
  }
4119
4147
  } catch (err) {
4148
+ if (err?.message?.includes("Authentication failed for account")) {
4149
+ throw err;
4150
+ }
4120
4151
  console.warn(
4121
4152
  `\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
4122
4153
  );
@@ -4259,10 +4290,30 @@ async function loginToQwen(accountId, email, password) {
4259
4290
  const uiResult = await loginViaUi(page, email, password);
4260
4291
  if (uiResult.success) {
4261
4292
  cookieCaches.delete(accountId);
4262
- await saveStorageState(page.context(), accountId);
4263
4293
  try {
4264
- const liveCookies = await page.context().cookies();
4265
- 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);
4266
4317
  if (tokenCookie) {
4267
4318
  const cookieStr = liveCookies.map((c) => `${c.name}=${c.value}`).join("; ");
4268
4319
  const cache = getHeaderCache(accountId);
@@ -4271,7 +4322,7 @@ async function loginToQwen(accountId, email, password) {
4271
4322
  markAccountHeadersReady(accountId);
4272
4323
  const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
4273
4324
  const exp = parseJwtExpiry(tokenCookie.value);
4274
- let rTok = uiResult.refreshToken;
4325
+ let rTok = uiResult.refreshToken || liveCookies.find((c) => c.name === "refresh_token")?.value;
4275
4326
  if (!rTok) {
4276
4327
  try {
4277
4328
  rTok = await page.evaluate(() => localStorage.getItem("refresh_token") || "") || void 0;
@@ -4352,8 +4403,9 @@ async function loginViaApi(page, email, password) {
4352
4403
  }
4353
4404
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4354
4405
  const requestId = crypto3.randomUUID();
4406
+ const webVersion = getQwenWebVersion() || "0.3.26";
4355
4407
  const evalRes = await page.evaluate(
4356
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
4408
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz, version }) => {
4357
4409
  try {
4358
4410
  const response = await fetch(signinUrl2, {
4359
4411
  method: "POST",
@@ -4363,7 +4415,7 @@ async function loginViaApi(page, email, password) {
4363
4415
  accept: "application/json, text/plain, */*",
4364
4416
  "content-type": "application/json",
4365
4417
  source: "web",
4366
- version: "0.3.11",
4418
+ version,
4367
4419
  "x-request-origin": "https://chat.qwen.ai",
4368
4420
  timezone: tz,
4369
4421
  "x-request-id": requestId2
@@ -4376,7 +4428,14 @@ async function loginViaApi(page, email, password) {
4376
4428
  return { ok: false, error: e.message };
4377
4429
  }
4378
4430
  },
4379
- { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
4431
+ {
4432
+ email,
4433
+ password: hashedPassword,
4434
+ signinUrl,
4435
+ requestId,
4436
+ tz: formatAsciiTimezone(),
4437
+ version: webVersion
4438
+ }
4380
4439
  ).catch(() => null);
4381
4440
  let data = evalRes?.data;
4382
4441
  let signinSuccess = Boolean(
@@ -4385,7 +4444,7 @@ async function loginViaApi(page, email, password) {
4385
4444
  if (!signinSuccess && !data?.data?.code) {
4386
4445
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4387
4446
  const fallbackRes = await page.evaluate(
4388
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
4447
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz, version }) => {
4389
4448
  try {
4390
4449
  const response = await fetch(fallbackUrl2, {
4391
4450
  method: "POST",
@@ -4395,7 +4454,7 @@ async function loginViaApi(page, email, password) {
4395
4454
  accept: "application/json, text/plain, */*",
4396
4455
  "content-type": "application/json",
4397
4456
  source: "web",
4398
- version: "0.3.11",
4457
+ version,
4399
4458
  timezone: tz,
4400
4459
  "x-request-id": requestId2
4401
4460
  },
@@ -4407,7 +4466,14 @@ async function loginViaApi(page, email, password) {
4407
4466
  return { ok: false, error: e.message };
4408
4467
  }
4409
4468
  },
4410
- { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
4469
+ {
4470
+ email,
4471
+ password: hashedPassword,
4472
+ fallbackUrl,
4473
+ requestId,
4474
+ tz: formatAsciiTimezone(),
4475
+ version: webVersion
4476
+ }
4411
4477
  ).catch(() => null);
4412
4478
  if (fallbackRes?.data) {
4413
4479
  data = fallbackRes.data;
@@ -4632,28 +4698,34 @@ async function loginViaUi(page, email, password) {
4632
4698
  await sleep2(500);
4633
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")';
4634
4700
  const submitButton = page.locator(submitSelector).first();
4635
- await page.evaluate((sel) => {
4636
- try {
4637
- const btn = document.querySelector(sel);
4638
- if (btn) {
4639
- btn.removeAttribute("disabled");
4640
- btn.classList.remove("ant-btn-disabled");
4641
- btn.click();
4642
- }
4643
- } catch {
4644
- }
4645
- }, submitSelector).catch(() => {
4646
- });
4701
+ let submitted = false;
4647
4702
  try {
4648
4703
  if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
4649
- await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
4704
+ await submitButton.click({ timeout: 2e3 }).catch(() => {
4650
4705
  });
4706
+ submitted = true;
4651
4707
  }
4652
4708
  } catch {
4653
4709
  }
4654
- const passInput = page.locator(passwordSelector).first();
4655
- await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
4656
- });
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
+ }
4657
4729
  const postSubmitDeadline = Date.now() + 15e3;
4658
4730
  while (Date.now() < postSubmitDeadline) {
4659
4731
  if (page.isClosed()) break;
@@ -4671,9 +4743,12 @@ async function loginViaUi(page, email, password) {
4671
4743
  ".ant-form-item-explain-error",
4672
4744
  ".ant-message-error",
4673
4745
  ".ant-message-notice",
4746
+ ".ant-message-custom-content",
4747
+ ".ant-notification-notice",
4674
4748
  '[role="alert"]',
4675
4749
  ".qwenchat-auth-error",
4676
- ".auth-error-message"
4750
+ ".auth-error-message",
4751
+ ".qwenchat-toast"
4677
4752
  ].join(", ");
4678
4753
  const errorEl = page.locator(errorSelector).first();
4679
4754
  if (await errorEl.isVisible().catch(() => false)) {
@@ -4687,7 +4762,15 @@ async function loginViaUi(page, email, password) {
4687
4762
  };
4688
4763
  }
4689
4764
  }
4690
- 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(", ");
4691
4774
  const codeInput = page.locator(codeSelector).first();
4692
4775
  if (await codeInput.isVisible().catch(() => false)) {
4693
4776
  return {
@@ -4697,16 +4780,44 @@ async function loginViaUi(page, email, password) {
4697
4780
  }
4698
4781
  try {
4699
4782
  const liveCookies = await page.context().cookies();
4700
- if (liveCookies.some((c) => c.name === "token")) {
4701
- 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 };
4702
4787
  }
4703
- } catch {
4704
- }
4705
- const currentUrl = page.url();
4706
- if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
4707
- if (await isPageLoggedIn(page, 3e3)) {
4708
- 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
+ }
4709
4819
  }
4820
+ } catch {
4710
4821
  }
4711
4822
  await sleep2(1e3);
4712
4823
  }
@@ -4736,10 +4847,18 @@ async function refreshAccountToken(accountId) {
4736
4847
  } catch {
4737
4848
  }
4738
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
+ }
4739
4857
  if (refreshToken) {
4740
4858
  try {
4859
+ const webVersion = getQwenWebVersion() || "0.3.26";
4741
4860
  const refreshResult = await page.evaluate(
4742
- async ({ rTok, tz }) => {
4861
+ async ({ rTok, tz, version }) => {
4743
4862
  try {
4744
4863
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4745
4864
  method: "GET",
@@ -4747,7 +4866,7 @@ async function refreshAccountToken(accountId) {
4747
4866
  headers: {
4748
4867
  accept: "application/json, text/plain, */*",
4749
4868
  source: "web",
4750
- version: "0.3.11",
4869
+ version,
4751
4870
  "x-request-origin": "https://chat.qwen.ai",
4752
4871
  timezone: tz,
4753
4872
  authorization: `Bearer ${rTok}`
@@ -4759,7 +4878,7 @@ async function refreshAccountToken(accountId) {
4759
4878
  return { ok: false, error: e.message };
4760
4879
  }
4761
4880
  },
4762
- { rTok: refreshToken, tz: formatAsciiTimezone() }
4881
+ { rTok: refreshToken, tz: formatAsciiTimezone(), version: webVersion }
4763
4882
  );
4764
4883
  const json = refreshResult?.data;
4765
4884
  if (json && json.success === true && json.data) {
@@ -119,7 +119,7 @@ var envSchema = z.object({
119
119
  // Version header on Qwen API requests = the deployed web bundle version
120
120
  // (fallback when dynamic DOM detection is not yet completed).
121
121
  // Override via env when Qwen ships a new bundle.
122
- QWEN_WEB_VERSION: z.string().default("0.2.91"),
122
+ QWEN_WEB_VERSION: z.string().default("0.3.26"),
123
123
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
124
124
  // (chats/new, settings): those work without them (live-probed). The
125
125
  // completions path is the exception — the 0.2.86 HAR shows the real
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.6.8",
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": {
@@ -34,7 +34,7 @@ const envSchema = z
34
34
  // Version header on Qwen API requests = the deployed web bundle version
35
35
  // (fallback when dynamic DOM detection is not yet completed).
36
36
  // Override via env when Qwen ships a new bundle.
37
- QWEN_WEB_VERSION: z.string().default("0.2.91"),
37
+ QWEN_WEB_VERSION: z.string().default("0.3.26"),
38
38
  // Controls bx-ua/bx-umidtoken injection on the GENERAL API paths
39
39
  // (chats/new, settings): those work without them (live-probed). The
40
40
  // completions path is the exception — the 0.2.86 HAR shows the real
@@ -17,6 +17,6 @@ export function getAppVersion(): string {
17
17
  }
18
18
  }
19
19
  } catch {}
20
- cachedVersion = "v1.6.8";
20
+ cachedVersion = "v1.6.10";
21
21
  return cachedVersion;
22
22
  }
@@ -84,7 +84,7 @@ export async function getBasicHeaders(accountId?: string): Promise<{
84
84
  secChUa: "",
85
85
  secChUaMobile: "?0",
86
86
  secChUaPlatform: "",
87
- version: "0.2.89",
87
+ version: "0.3.26",
88
88
  };
89
89
  }
90
90