qwenproxy-cli 1.3.3 → 1.3.5
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/README.es.md +6 -0
- package/README.md +6 -0
- package/README.pt-BR.md +6 -0
- package/package.json +1 -1
- package/src/core/config.ts +1 -1
- package/src/services/playwright.ts +40 -13
package/README.es.md
CHANGED
|
@@ -141,6 +141,12 @@ QwenProxy iniciará el servidor proxy en segundo plano y abrirá el panel intera
|
|
|
141
141
|
|
|
142
142
|
### 3. Añadir Cuentas de Qwen
|
|
143
143
|
|
|
144
|
+
> 🌐 **¿Dónde crear las cuentas?**
|
|
145
|
+
> Crea tus cuentas gratuitas directamente en el portal oficial de Qwen: **[https://chat.qwen.ai](https://chat.qwen.ai)**
|
|
146
|
+
> 1. Ingresa a **[chat.qwen.ai](https://chat.qwen.ai)** y haz clic en **Sign up** (Registrarse).
|
|
147
|
+
> 2. Regístrate con tu correo y contraseña (o inicia sesión con Google).
|
|
148
|
+
> 3. *Consejo:* Crea de 3 a 10 cuentas gratuitas. QwenProxy rotará automáticamente entre ellas cuando alcancen la cuota diaria, brindándote uso continuo e ininterrumpido en tus agentes de código.
|
|
149
|
+
|
|
144
150
|
Dentro de la TUI, dirígete a la pestaña **`[5] Contas`** para gestionar credenciales:
|
|
145
151
|
|
|
146
152
|
- **Importación por Lotes (`B`):** Pulsa **`B`** (o haz clic en `[ B ] Em Lote`). Pega tus credenciales en masa (`email:contraseña` por línea, formato `.env` con comas o copiado de hojas de cálculo). El sistema calcula las cuentas válidas en tiempo real, respeta caracteres especiales en contraseñas, omite duplicadas y guarda todo cifrado en SQLite en una sola transacción (<10ms).
|
package/README.md
CHANGED
|
@@ -141,6 +141,12 @@ QwenProxy launches the high-performance proxy server in the background and opens
|
|
|
141
141
|
|
|
142
142
|
### 3. Add Qwen Accounts
|
|
143
143
|
|
|
144
|
+
> 🌐 **Where to create accounts?**
|
|
145
|
+
> Create your free accounts directly on the official Qwen Web portal: **[https://chat.qwen.ai](https://chat.qwen.ai)**
|
|
146
|
+
> 1. Go to **[chat.qwen.ai](https://chat.qwen.ai)** and click **Sign up**.
|
|
147
|
+
> 2. Register with your email and password (or Google login).
|
|
148
|
+
> 3. *Pro Tip:* Create 3 to 10 accounts so QwenProxy can automatically rotate between them when daily quotas are reached, giving you continuous uninterrupted coding!
|
|
149
|
+
|
|
144
150
|
Inside the TUI, navigate to tab **`[5] Accounts`** to manage credentials:
|
|
145
151
|
|
|
146
152
|
- **Batch Import (`B`):** Press **`B`** (or click `[ B ] Em Lote`). Paste your account credentials in bulk (`email:password` per line, raw `.env` string with commas, or spreadsheet paste). The parser calculates valid accounts in real time, handles special password characters safely, skips duplicates, and commits encrypted credentials to SQLite in a single transaction (<10ms).
|
package/README.pt-BR.md
CHANGED
|
@@ -300,6 +300,12 @@ O QwenProxy inicializa o servidor de alta performance em segundo plano e abre a
|
|
|
300
300
|
|
|
301
301
|
### 3. Adicionar Contas Qwen
|
|
302
302
|
|
|
303
|
+
> 🌐 **Onde criar as contas?**
|
|
304
|
+
> Crie suas contas gratuitas diretamente no portal oficial do Qwen: **[https://chat.qwen.ai](https://chat.qwen.ai)**
|
|
305
|
+
> 1. Acesse **[chat.qwen.ai](https://chat.qwen.ai)** e clique no botão **Sign up** (Cadastrar-se).
|
|
306
|
+
> 2. Cadastre-se com seu e-mail e defina uma senha (ou use login via Google).
|
|
307
|
+
> 3. *Dica de Ouro:* Crie de 3 a 10 contas gratuitas. O QwenProxy rotaciona automaticamente entre elas quando a cota diária de uma atinge o limite, garantindo uso contínuo e sem pausas no seu fluxo de desenvolvimento!
|
|
308
|
+
|
|
303
309
|
Na TUI, você pode gerenciar suas contas na aba **`[5] Contas`** de duas formas simples:
|
|
304
310
|
|
|
305
311
|
- **Importação em Lote (`B`):** Pressione a tecla **`B`** (ou clique em `[ B ] Em Lote`). Cole suas contas de uma só vez (aceita formato `email:senha`, formato bruto do `.env` com vírgulas ou copiado de planilhas). O sistema calcula a contagem em tempo real, valida duplicatas e grava tudo no SQLite criptografado em milissegundos.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
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": {
|
package/src/core/config.ts
CHANGED
|
@@ -80,7 +80,7 @@ const envSchema = z
|
|
|
80
80
|
CHAT_TIMEOUT: z.string().default("180000"),
|
|
81
81
|
NAVIGATION_TIMEOUT: z.string().default("60000"),
|
|
82
82
|
PAGE_TIMEOUT: z.string().default("60000"),
|
|
83
|
-
HEADERS_TIMEOUT: z.string().default("
|
|
83
|
+
HEADERS_TIMEOUT: z.string().default("120000"),
|
|
84
84
|
TIME_TO_FIRST_BYTE: z.string().default("60000"),
|
|
85
85
|
IDLE_STREAM_TIMEOUT: z.string().default("60000"),
|
|
86
86
|
// Deadline for the FIRST upstream chunk on thinking models (the reasoning
|
|
@@ -2496,17 +2496,15 @@ export async function captureQwenHeaders(
|
|
|
2496
2496
|
// Unbounded, page.focus would burn its 60s default timeout on every
|
|
2497
2497
|
// attempt, freezing the whole capture and cooling a healthy account with
|
|
2498
2498
|
// AuthInitFailed. A miss marks the attempt for a reload instead.
|
|
2499
|
+
const inputLocator = page.locator(inputSelector).first();
|
|
2499
2500
|
try {
|
|
2500
|
-
await
|
|
2501
|
-
|
|
2502
|
-
.
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
Math.min(CHAT_INPUT_APPEAR_TIMEOUT_MS, remainingBudgetMs()),
|
|
2508
|
-
),
|
|
2509
|
-
});
|
|
2501
|
+
await inputLocator.waitFor({
|
|
2502
|
+
state: "visible",
|
|
2503
|
+
timeout: Math.max(
|
|
2504
|
+
1,
|
|
2505
|
+
Math.min(CHAT_INPUT_APPEAR_TIMEOUT_MS, remainingBudgetMs()),
|
|
2506
|
+
),
|
|
2507
|
+
});
|
|
2510
2508
|
} catch {
|
|
2511
2509
|
if (settled || page.isClosed()) return;
|
|
2512
2510
|
console.warn(
|
|
@@ -2526,9 +2524,27 @@ export async function captureQwenHeaders(
|
|
|
2526
2524
|
for (let interactionTry = 1; interactionTry <= 2; interactionTry++) {
|
|
2527
2525
|
if (settled || page.isClosed()) return;
|
|
2528
2526
|
try {
|
|
2529
|
-
|
|
2527
|
+
if (typeof (inputLocator as any).focus === "function") {
|
|
2528
|
+
await (inputLocator as any).focus({ timeout: inputActionTimeoutMs });
|
|
2529
|
+
} else {
|
|
2530
|
+
await page.focus(inputSelector, { timeout: inputActionTimeoutMs });
|
|
2531
|
+
}
|
|
2530
2532
|
if (settled || page.isClosed()) return;
|
|
2531
|
-
|
|
2533
|
+
if (typeof (inputLocator as any).fill === "function") {
|
|
2534
|
+
await (inputLocator as any).fill("a", { timeout: inputActionTimeoutMs });
|
|
2535
|
+
} else {
|
|
2536
|
+
await page.fill(inputSelector, "a", { timeout: inputActionTimeoutMs });
|
|
2537
|
+
}
|
|
2538
|
+
// Dispatch native React input events and keyboard stroke to guarantee React updates state
|
|
2539
|
+
if (typeof page.keyboard?.press === "function") {
|
|
2540
|
+
await page.keyboard.press("End").catch(() => {});
|
|
2541
|
+
}
|
|
2542
|
+
if (typeof (page.keyboard as any)?.type === "function") {
|
|
2543
|
+
await (page.keyboard as any).type(" ").catch(() => {});
|
|
2544
|
+
}
|
|
2545
|
+
if (typeof page.keyboard?.press === "function") {
|
|
2546
|
+
await page.keyboard.press("Backspace").catch(() => {});
|
|
2547
|
+
}
|
|
2532
2548
|
if (typeof page.evaluate === "function") {
|
|
2533
2549
|
await page.evaluate((sel) => {
|
|
2534
2550
|
const el = document.querySelector(sel) as HTMLTextAreaElement | null;
|
|
@@ -2570,7 +2586,18 @@ export async function captureQwenHeaders(
|
|
|
2570
2586
|
return;
|
|
2571
2587
|
}
|
|
2572
2588
|
if (settled || page.isClosed()) return;
|
|
2573
|
-
|
|
2589
|
+
|
|
2590
|
+
// Wait up to 3s for React to enable the send button after typing
|
|
2591
|
+
try {
|
|
2592
|
+
await page.waitForFunction(() => {
|
|
2593
|
+
const btn = document.querySelector(
|
|
2594
|
+
".message-input-right-button-send .send-button, .chat-prompt-send-button, button.send-button, button[aria-label*='Send' i], button[aria-label*='Enviar' i], .send-button-container button"
|
|
2595
|
+
) as HTMLButtonElement | null;
|
|
2596
|
+
return btn && !btn.disabled && !btn.classList.contains("disabled") && btn.getAttribute("aria-disabled") !== "true";
|
|
2597
|
+
}, { timeout: 3000 });
|
|
2598
|
+
} catch {
|
|
2599
|
+
await sleep(1000);
|
|
2600
|
+
}
|
|
2574
2601
|
if (settled || page.isClosed()) return;
|
|
2575
2602
|
|
|
2576
2603
|
const sendSelectors = [
|