qwenproxy-cli 1.4.3 → 1.4.7

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.
@@ -3523,8 +3523,29 @@ function getStealthScript(profile) {
3523
3523
  const _toBlob = HTMLCanvasElement.prototype.toBlob;
3524
3524
  const _getImageData = CanvasRenderingContext2D.prototype.getImageData;
3525
3525
 
3526
+ function isCaptchaCanvas(canvas) {
3527
+ try {
3528
+ if (!canvas) return false;
3529
+ const id = (canvas.id || "").toLowerCase();
3530
+ const cls = (canvas.className || "").toLowerCase();
3531
+ return (
3532
+ id.includes("baxia") ||
3533
+ id.includes("nc_") ||
3534
+ id.includes("captcha") ||
3535
+ id.includes("punish") ||
3536
+ cls.includes("baxia") ||
3537
+ cls.includes("nc_") ||
3538
+ cls.includes("captcha") ||
3539
+ Boolean(canvas.closest?.("#baxia-dialog, #baxia-punish, [class*='baxia'], [class*='nc_'], .auth-layout"))
3540
+ );
3541
+ } catch(e) {
3542
+ return false;
3543
+ }
3544
+ }
3545
+
3526
3546
  function addNoise(canvas) {
3527
3547
  try {
3548
+ if (isCaptchaCanvas(canvas)) return;
3528
3549
  const ctx = canvas.getContext('2d');
3529
3550
  if (!ctx) return;
3530
3551
  const style = ctx.fillStyle;
@@ -3548,6 +3569,9 @@ function getStealthScript(profile) {
3548
3569
 
3549
3570
  CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
3550
3571
  const imageData = _getImageData.apply(this, arguments);
3572
+ if (isCaptchaCanvas(this.canvas)) {
3573
+ return imageData;
3574
+ }
3551
3575
  const data = imageData.data;
3552
3576
  const maxPixels = Math.min(data.length / 4, 2500);
3553
3577
  for (let i = 0; i < maxPixels * 4; i += 4) {
@@ -4120,37 +4144,52 @@ async function loginToQwen(accountId, email, password) {
4120
4144
  async function loginViaApi(page, email, password) {
4121
4145
  try {
4122
4146
  const hashedPassword = crypto3.createHash("sha256").update(password).digest("hex");
4123
- const signinUrl = qwenUrl("/api/v2/auths/signin");
4124
- let signinSuccess = false;
4125
- let data = null;
4126
- if (page.request && typeof page.request.post === "function") {
4127
- try {
4128
- const response = await page.request.post(signinUrl, {
4129
- data: {
4130
- email,
4131
- password: hashedPassword,
4132
- login_type: "email"
4133
- },
4134
- headers: {
4135
- "content-type": "application/json",
4136
- accept: "application/json, text/plain, */*",
4137
- referer: qwenUrl("/auth"),
4138
- origin: qwenOrigin(),
4139
- source: "web"
4140
- },
4141
- timeout: 1e4
4142
- });
4143
- data = await response.json().catch(() => null);
4144
- signinSuccess = Boolean(data && (data.success === true || data.token));
4145
- } catch {
4146
- }
4147
+ if (!page.url().includes("/auth")) {
4148
+ await page.goto(qwenUrl("/auth"), {
4149
+ waitUntil: "domcontentloaded",
4150
+ timeout: config.timeouts.navigation
4151
+ }).catch(() => {
4152
+ });
4153
+ await sleep2(1e3);
4147
4154
  }
4148
- if (!signinSuccess && !data) {
4149
- const requestId = crypto3.randomUUID();
4150
- const evalRes = await page.evaluate(
4151
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4155
+ const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
4156
+ const requestId = crypto3.randomUUID();
4157
+ const evalRes = await page.evaluate(
4158
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
4159
+ try {
4160
+ const response = await fetch(signinUrl2, {
4161
+ method: "POST",
4162
+ signal: AbortSignal.timeout(15e3),
4163
+ credentials: "include",
4164
+ headers: {
4165
+ accept: "application/json, text/plain, */*",
4166
+ "content-type": "application/json",
4167
+ source: "web",
4168
+ version: "0.3.11",
4169
+ "x-request-origin": "https://chat.qwen.ai",
4170
+ timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4171
+ "x-request-id": requestId2
4172
+ },
4173
+ body: JSON.stringify({ email: email2, password: password2 })
4174
+ });
4175
+ const json = await response.json().catch(() => null);
4176
+ return { ok: response.ok, status: response.status, data: json };
4177
+ } catch (e) {
4178
+ return { ok: false, error: e.message };
4179
+ }
4180
+ },
4181
+ { email, password: hashedPassword, signinUrl, requestId }
4182
+ ).catch(() => null);
4183
+ let data = evalRes?.data;
4184
+ let signinSuccess = Boolean(
4185
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4186
+ );
4187
+ if (!signinSuccess && !data?.data?.code) {
4188
+ const fallbackUrl = qwenUrl("/api/v2/auths/signin");
4189
+ const fallbackRes = await page.evaluate(
4190
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
4152
4191
  try {
4153
- const response = await fetch(signinUrl2, {
4192
+ const response = await fetch(fallbackUrl2, {
4154
4193
  method: "POST",
4155
4194
  signal: AbortSignal.timeout(1e4),
4156
4195
  credentials: "include",
@@ -4158,10 +4197,11 @@ async function loginViaApi(page, email, password) {
4158
4197
  accept: "application/json, text/plain, */*",
4159
4198
  "content-type": "application/json",
4160
4199
  source: "web",
4200
+ version: "0.3.11",
4161
4201
  timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4162
4202
  "x-request-id": requestId2
4163
4203
  },
4164
- body: JSON.stringify({ email: email2, password: password2, login_type: "email" })
4204
+ body: JSON.stringify({ email: email2, password: password2 })
4165
4205
  });
4166
4206
  const json = await response.json().catch(() => null);
4167
4207
  return { ok: response.ok, status: response.status, data: json };
@@ -4169,11 +4209,13 @@ async function loginViaApi(page, email, password) {
4169
4209
  return { ok: false, error: e.message };
4170
4210
  }
4171
4211
  },
4172
- { email, password: hashedPassword, signinUrl, requestId }
4212
+ { email, password: hashedPassword, fallbackUrl, requestId }
4173
4213
  ).catch(() => null);
4174
- if (evalRes?.data) {
4175
- data = evalRes.data;
4176
- signinSuccess = Boolean(data && (data.success === true || data.token));
4214
+ if (fallbackRes?.data) {
4215
+ data = fallbackRes.data;
4216
+ signinSuccess = Boolean(
4217
+ data && (data.success === true || data.token || data.data?.access_token || data.data?.token)
4218
+ );
4177
4219
  }
4178
4220
  }
4179
4221
  if (data?.success === false) {
@@ -4187,7 +4229,8 @@ async function loginViaApi(page, email, password) {
4187
4229
  };
4188
4230
  }
4189
4231
  if (signinSuccess) {
4190
- const token = data?.data?.token || data?.token;
4232
+ const token = data?.data?.access_token || data?.data?.token || data?.access_token || data?.token;
4233
+ const refreshToken = data?.data?.refresh_token || data?.refresh_token;
4191
4234
  if (token) {
4192
4235
  try {
4193
4236
  await page.context().addCookies([
@@ -4211,14 +4254,19 @@ async function loginViaApi(page, email, password) {
4211
4254
  }).catch(() => {
4212
4255
  });
4213
4256
  if (token) {
4214
- await page.evaluate((tok) => {
4215
- try {
4216
- localStorage.removeItem("qwen_token_logged_out_marker");
4217
- localStorage.setItem("token", tok);
4218
- document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4219
- } catch {
4220
- }
4221
- }, token).catch(() => {
4257
+ await page.evaluate(
4258
+ ({ tok, rTok }) => {
4259
+ try {
4260
+ localStorage.removeItem("qwen_token_logged_out_marker");
4261
+ localStorage.setItem("token", tok);
4262
+ localStorage.setItem("access_token", tok);
4263
+ if (rTok) localStorage.setItem("refresh_token", rTok);
4264
+ document.cookie = `token=${encodeURIComponent(tok)}; path=/; domain=.qwen.ai; max-age=31536000`;
4265
+ } catch {
4266
+ }
4267
+ },
4268
+ { tok: token, rTok: refreshToken }
4269
+ ).catch(() => {
4222
4270
  });
4223
4271
  await page.reload({ waitUntil: "domcontentloaded" }).catch(() => {
4224
4272
  });
@@ -4246,11 +4294,23 @@ async function loginViaApi(page, email, password) {
4246
4294
  }
4247
4295
  async function loginViaUi(page, email, password) {
4248
4296
  try {
4249
- await page.goto(qwenUrl("/auth"), {
4250
- waitUntil: "domcontentloaded",
4251
- timeout: config.timeouts.navigation
4252
- });
4253
- await sleep2(1500);
4297
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3).catch(() => false)) {
4298
+ return { success: true };
4299
+ }
4300
+ try {
4301
+ await page.goto(qwenUrl("/auth"), {
4302
+ waitUntil: "domcontentloaded",
4303
+ timeout: Math.min(config.timeouts.navigation, 15e3)
4304
+ });
4305
+ await sleep2(1e3);
4306
+ } catch (gotoErr) {
4307
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4308
+ return { success: true };
4309
+ }
4310
+ }
4311
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3).catch(() => false)) {
4312
+ return { success: true };
4313
+ }
4254
4314
  const emailSelector = [
4255
4315
  'input[type="email"]',
4256
4316
  'input[name="email"]',
@@ -4260,7 +4320,7 @@ async function loginViaUi(page, email, password) {
4260
4320
  ].join(", ");
4261
4321
  try {
4262
4322
  await page.waitForSelector(emailSelector, {
4263
- timeout: Math.min(config.timeouts.page, 8e3)
4323
+ timeout: Math.min(config.timeouts.page, 5e3)
4264
4324
  });
4265
4325
  } catch {
4266
4326
  if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
@@ -4273,30 +4333,55 @@ async function loginViaUi(page, email, password) {
4273
4333
  return { success: false, reason: "Campo de e-mail n\xE3o encontrado (poss\xEDvel captcha)" };
4274
4334
  }
4275
4335
  await clearVisibleChallenge(page);
4276
- const findPasswordModeButton = () => {
4277
- return page.locator('button, [role="button"], a, div').filter({
4278
- hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
4279
- }).first();
4336
+ const isPasswordInputPresent = async () => {
4337
+ return await page.locator('input[type="password"], input[name="password"]').count().catch(() => 0) > 0;
4280
4338
  };
4281
- try {
4282
- const pwdModeBtn = findPasswordModeButton();
4283
- if (await pwdModeBtn.isVisible({ timeout: 2e3 }).catch(() => false)) {
4284
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4339
+ const revealPasswordMode = async () => {
4340
+ if (page.url().includes("/forget")) {
4341
+ await page.goto(qwenUrl("/auth"), { waitUntil: "domcontentloaded", timeout: 1e4 }).catch(() => {
4285
4342
  });
4286
- await sleep2(500);
4343
+ await sleep2(1e3);
4287
4344
  }
4288
- } catch {
4289
- }
4290
- await page.fill(emailSelector, email);
4291
- await sleep2(300);
4292
- try {
4293
- const pwdModeBtn = findPasswordModeButton();
4294
- if (await pwdModeBtn.isVisible({ timeout: 1500 }).catch(() => false)) {
4295
- await pwdModeBtn.click({ timeout: 2e3 }).catch(() => {
4345
+ if (await isPasswordInputPresent()) return;
4346
+ const btn = page.locator("button").filter({
4347
+ hasText: /(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i
4348
+ }).filter({
4349
+ hasNotText: /(?:esqueci|forgot|olvid|reset|recover)/i
4350
+ }).first();
4351
+ if (await btn.isVisible({ timeout: 1500 }).catch(() => false)) {
4352
+ await btn.click({ timeout: 2e3 }).catch(() => {
4296
4353
  });
4297
4354
  await sleep2(500);
4298
4355
  }
4356
+ if (await isPasswordInputPresent()) return;
4357
+ await page.evaluate(() => {
4358
+ try {
4359
+ const buttons = Array.from(document.querySelectorAll("button, [role='button'], a"));
4360
+ for (const b of buttons) {
4361
+ const txt = (b.innerText || "").trim().toLowerCase();
4362
+ if (/(?:log\s*in\s*with\s*(?:a\s*)?password|fazer\s*login\s*com\s*senha|entrar\s*com\s*(?:uma\s*)?senha|iniciar\s*sesi[oó]n\s*con\s*contrase[nñ]a|密码登录)/i.test(txt) && !txt.includes("esqueci") && !txt.includes("forgot") && !txt.includes("olvid") && !txt.includes("reset")) {
4363
+ b.click();
4364
+ return true;
4365
+ }
4366
+ }
4367
+ } catch {
4368
+ }
4369
+ return false;
4370
+ }).catch(() => false);
4371
+ await sleep2(500);
4372
+ };
4373
+ await revealPasswordMode();
4374
+ try {
4375
+ await page.fill(emailSelector, email, { timeout: 8e3 });
4376
+ await sleep2(300);
4299
4377
  } catch {
4378
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
4379
+ return { success: true };
4380
+ }
4381
+ throw new Error(`Campo de e-mail n\xE3o encontrado na tela de autentica\xE7\xE3o`);
4382
+ }
4383
+ if (!await isPasswordInputPresent()) {
4384
+ await revealPasswordMode();
4300
4385
  }
4301
4386
  const passwordSelector = 'input[type="password"], input[name="password"]';
4302
4387
  try {
@@ -4315,17 +4400,50 @@ async function loginViaUi(page, email, password) {
4315
4400
  reason: "Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o"
4316
4401
  };
4317
4402
  }
4318
- await page.fill(passwordSelector, password);
4319
- await sleep2(500);
4403
+ try {
4404
+ await page.fill(passwordSelector, password, { timeout: 8e3 });
4405
+ await sleep2(500);
4406
+ } catch {
4407
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 3e3)) {
4408
+ return { success: true };
4409
+ }
4410
+ throw new Error(`Campo de senha n\xE3o encontrado na tela de autentica\xE7\xE3o`);
4411
+ }
4412
+ await page.evaluate(() => {
4413
+ try {
4414
+ const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
4415
+ const passEl = document.querySelector('input[type="password"], input[name="password"]');
4416
+ if (emailEl) {
4417
+ emailEl.dispatchEvent(new Event("input", { bubbles: true }));
4418
+ emailEl.dispatchEvent(new Event("change", { bubbles: true }));
4419
+ emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
4420
+ }
4421
+ if (passEl) {
4422
+ passEl.dispatchEvent(new Event("input", { bubbles: true }));
4423
+ passEl.dispatchEvent(new Event("change", { bubbles: true }));
4424
+ passEl.dispatchEvent(new Event("blur", { bubbles: true }));
4425
+ }
4426
+ } catch {
4427
+ }
4428
+ }).catch(() => {
4429
+ });
4320
4430
  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")';
4321
4431
  const submitButton = page.locator(submitSelector).first();
4322
4432
  try {
4323
4433
  await page.waitForSelector('button[type="submit"]:not([disabled])', {
4324
- timeout: 5e3
4434
+ timeout: 4e3
4325
4435
  });
4326
4436
  await submitButton.click();
4327
4437
  } catch {
4328
- await page.keyboard.press("Enter");
4438
+ const isDisabled = await submitButton.isDisabled().catch(() => true);
4439
+ if (!isDisabled) {
4440
+ await submitButton.click({ force: true }).catch(() => {
4441
+ });
4442
+ } else {
4443
+ if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
4444
+ return { success: true };
4445
+ }
4446
+ }
4329
4447
  }
4330
4448
  const postSubmitDeadline = Date.now() + 15e3;
4331
4449
  while (Date.now() < postSubmitDeadline) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.4.3",
3
+ "version": "1.4.7",
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.4.3";
20
+ cachedVersion = "v1.4.7";
21
21
  return cachedVersion;
22
22
  }