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.
@@ -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.2.91"),
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 res = await fetch("/api/v1/auths/", { method: "GET" });
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
- await loginToQwen(account.id, account.email, account.password);
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
- const liveCookies = await page.context().cookies();
3999
- const tokenCookie = liveCookies.find((c) => c.name === "token");
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: "0.3.11",
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
- { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
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: "0.3.11",
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
- { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
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
- await page.evaluate((sel) => {
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({ force: true, timeout: 2e3 }).catch(() => {
4438
+ await submitButton.click({ timeout: 2e3 }).catch(() => {
4384
4439
  });
4440
+ submitted = true;
4385
4441
  }
4386
4442
  } catch {
4387
4443
  }
4388
- const passInput = page.locator(passwordSelector).first();
4389
- await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
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 = 'input[placeholder*="code" i], input[placeholder*="c\xF3digo" i], input[name*="code" i]';
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
- if (liveCookies.some((c) => c.name === "token")) {
4435
- return { success: true };
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
- } catch {
4438
- }
4439
- const currentUrl = page.url();
4440
- if (!currentUrl.includes("/auth") && !currentUrl.includes("/login")) {
4441
- if (await isPageLoggedIn(page, 3e3)) {
4442
- return { success: true };
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: "0.3.11",
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) {