qwenproxy-cli 1.0.31 → 1.1.0
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 +269 -0
- package/README.md +175 -782
- package/README.pt-BR.md +1033 -0
- package/package.json +4 -4
- package/src/api/server.ts +18 -2
- package/src/core/accounts.ts +130 -0
- package/src/core/config.ts +95 -4
- package/src/index.ts +3 -0
- package/src/routes/chat/context.ts +9 -9
- package/src/routes/chat/index.ts +15 -6
- package/src/services/qwen-chat-pool.ts +4 -5
- package/src/services/qwen.ts +2 -5
- package/src/sync/index.ts +124 -80
- package/src/tui/app.ts +8 -9
- package/src/tui/index.ts +3 -0
- package/src/tui/proxy-client.ts +3 -2
- package/src/tui/screen.ts +10 -3
- package/src/tui/settings.ts +2 -0
- package/src/tui/theme.ts +1 -0
- package/src/tui/types.ts +1 -0
- package/src/tui/views/accounts-view.ts +343 -23
- package/src/tui/views/chat-view.ts +209 -14
- package/src/tui/views/status-view.ts +34 -3
package/README.pt-BR.md
ADDED
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
|
|
3
|
+
<img src="docs/banner.webp" alt="QwenProxy" width="100%">
|
|
4
|
+
|
|
5
|
+
</p>
|
|
6
|
+
<p align="center">
|
|
7
|
+
<a href="README.md">English</a> ·
|
|
8
|
+
<b>Português</b> ·
|
|
9
|
+
<a href="README.es.md">Español</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Gateway e API de alta performance compatível com **OpenAI** e **Anthropic** que conecta clientes e agentes (Codex, Claude Code CLI, Grok, Cursor) ao **Qwen (`chat.qwen.ai`)** com suporte a múltiplas contas, failover inteligente, tool calling robusto, thread-native, geração de fotos e vídeos, **Responses API completa com memória persistente** e sessões persistentes. Inclui Playwright com stealth, retries para erros transitórios, variantes públicas base/`-fast`/`-thinking`, cache comprimido, registro de capabilities por modelo e observabilidade.
|
|
14
|
+
|
|
15
|
+
[](https://github.com/johngbl/QwenProxy/actions/workflows/ci.yml)
|
|
16
|
+
[](https://www.npmjs.com/package/qwenproxy-cli)
|
|
17
|
+
[](https://www.typescriptlang.org/)
|
|
18
|
+
[](https://hono.dev/)
|
|
19
|
+
[](https://github.com/kaliiiiiiiiii/patchright)
|
|
20
|
+
[](LICENSE)
|
|
21
|
+
[](https://github.com/sponsors/johngbl)
|
|
22
|
+
[](https://ko-fi.com/johngbl)
|
|
23
|
+
|
|
24
|
+
## ❤️ Apoie o projeto
|
|
25
|
+
|
|
26
|
+
Se o **QwenProxy** está sendo útil para você ou sua equipe e você deseja incentivar o desenvolvimento contínuo, novas integrações, testes ao vivo e atualizações rápidas, considere apoiar voluntariamente:
|
|
27
|
+
|
|
28
|
+
<a href="https://github.com/sponsors/johngbl" target="_blank"><img src="https://img.shields.io/badge/Sponsor%20no%20GitHub-ea4aaa?style=for-the-badge&logo=githubsponsors&logoColor=white" alt="GitHub Sponsors"></a> <a href="https://ko-fi.com/johngbl" target="_blank"><img src="https://img.shields.io/badge/Apoiar%20via%20Ko--fi-ff5e5b?style=for-the-badge&logo=kofi&logoColor=white" alt="Ko-fi"></a>
|
|
29
|
+
|
|
30
|
+
Toda contribuição é muito bem-vinda e ajuda a cobrir custos de infraestrutura e contas de teste!
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Principais funcionalidades
|
|
35
|
+
|
|
36
|
+
- **Compatibilidade Nativa OpenAI & Anthropic** — `/v1/chat/completions`, `/v1/models`, `/v1/messages` (**Anthropic Messages API nativa** para **Claude Code CLI** e SDK oficial), `/v1/messages/count_tokens`, **OpenAI Responses API** (`/v1/responses`) e `/v1/completions` (legado).
|
|
37
|
+
- **Matriz de 4 Modos de Conversação** — `thread` (padrão persistente), `thread-temp` (delta ~1KB efêmero, recomendado para agentes), `stateless-temp` (OpenAI oficial completo, efêmero) e `stateless` (OpenAI oficial completo, salvo na conta).
|
|
38
|
+
- **Controle Dinâmico de Modos em Tempo Real** — Alterne o modo da API global instantaneamente pela TUI (tecla `M` na tela inicial ou `F4` no Chat) ou via endpoint `/v1/chat/mode`, sem reiniciar o proxy.
|
|
39
|
+
- **Dashboard TUI Completo no Terminal (`qpx`)** — Interface visual com suporte total a mouse (hover, clique, arrasto e scroll), seleção vertical de modelos e modos, e título de terminal nativo `QwenProxy`.
|
|
40
|
+
- **Importação de Contas em Lote (`B`)** — Cole dezenas de contas de uma vez (`email:senha`, formato `.env`, tab, pipe). Criptografia at-rest em transação SQLite única (<10ms) com deduplicação e contagem em tempo real.
|
|
41
|
+
- **Rolagem Dinâmica de Viewport** — Navegação suave em listas de 50+ contas sem estourar o tamanho da janela nem desalinhamento de colunas.
|
|
42
|
+
- **Sincronizador Automático de Clientes (`qpx sync`)** — Configuração em 1 clique para Claude Code, OpenAI Codex, OpenCode, Cline, OMP, Zed, Kilo Code e Hermes com backup e restauração.
|
|
43
|
+
- **Instância Única de Chromium Ultra-Leve** — 1 único processo de navegador com aceleração WebGL ativa e isolamento seguro de `BrowserContext` (~200MB de RAM para todas as contas, economia >65%).
|
|
44
|
+
- **Startup Sob Demanda & Multi-Conta** — Sobe instantaneamente com a primeira conta pronta; contas reservas permanecem em *Standby* e inicializam sem esforço sob demanda (failover ou rotação).
|
|
45
|
+
- **Sincronização de Personalization Limpa** — System prompts e tools são sincronizados diretamente na personalização da conta (`/settings/personalization`), imitando 100% o cliente web real e evitando gatilhos de WAF/bot.
|
|
46
|
+
- **Parser de Tool Calling com Auto-Cura** — Suporta streaming fragmentado, reparo de JSON quebrado, tags unificadas `<qpx_call>`, fuzzy matching de nomes (`readFile` → `read_file`) e auto-retry inteligente.
|
|
47
|
+
- **Geração de Fotos e Vídeos** — Endpoints dedicados `/v1/images/generations` e `/v1/videos/generations` com modelos de ponta (`qwen-image-3.0-pro`, `wan3.0-video`, `wan2.7-image-pro`).
|
|
48
|
+
- **Observabilidade & Métricas** — Monitoramento em tempo real em `/health`, `/metrics` (Prometheus), watchdog de memória RSS e logs unificados por turno.
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Arquitetura
|
|
52
|
+
|
|
53
|
+
```mermaid
|
|
54
|
+
flowchart TD
|
|
55
|
+
Client["Cliente OpenAI / Claude Code / Codex / Grok"] -->|HTTP| Proxy["QwenProxy - Hono"]
|
|
56
|
+
Proxy --> Chat["/v1/chat/completions"]
|
|
57
|
+
Proxy --> Anthropic["/v1/messages"]
|
|
58
|
+
Proxy --> Completions["/v1/completions (legado)"]
|
|
59
|
+
Proxy --> Responses["/v1/responses"]
|
|
60
|
+
Proxy --> Media["/v1/images | /v1/videos"]
|
|
61
|
+
Proxy --> Models["/v1/models"]
|
|
62
|
+
Proxy --> Upload["/v1/upload"]
|
|
63
|
+
Anthropic --> Chat
|
|
64
|
+
Completions --> Chat
|
|
65
|
+
Responses --> Chat
|
|
66
|
+
Responses --> Effort["Effort normalization"]
|
|
67
|
+
Responses --> State[("SQLite responses_store")]
|
|
68
|
+
Chat --> Context["Thread-native context"]
|
|
69
|
+
Chat --> Accounts["Account manager"]
|
|
70
|
+
Accounts --> DB[("SQLite encrypted")]
|
|
71
|
+
Accounts --> Playwright["Playwright + Stealth"]
|
|
72
|
+
Playwright --> Fingerprint["Fingerprint / session keeper"]
|
|
73
|
+
Chat --> Parser["Tool-call parser"]
|
|
74
|
+
Chat --> Personalization["Settings + personalization sync"]
|
|
75
|
+
Chat --> BrowserTransport["Playwright page fetch + SSE bridge"]
|
|
76
|
+
BrowserTransport --> Qwen["chat.qwen.ai"]
|
|
77
|
+
Media --> BrowserTransport
|
|
78
|
+
Upload --> OSS["Qwen OSS"]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Autenticação
|
|
84
|
+
|
|
85
|
+
Se `API_KEY` estiver definido, as rotas `/v1/*` (e `/metrics`) exigem uma das formas:
|
|
86
|
+
|
|
87
|
+
- `Authorization: Bearer <API_KEY>` (OpenAI / Responses)
|
|
88
|
+
- `x-api-key: <API_KEY>` (clients bearer-style)
|
|
89
|
+
|
|
90
|
+
QwenProxy utiliza **Patchright com arquitetura de navegador compartilhado**. Um único processo Chromium é mantido aberto com aceleração WebGL ativa, enquanto cada conta opera em um `BrowserContext` isolado com persistência leve em `storage_state.json` (~200MB de RAM para todas as contas).
|
|
91
|
+
|
|
92
|
+
```env
|
|
93
|
+
PLAYWRIGHT_HEADLESS=true
|
|
94
|
+
PLAYWRIGHT_BROWSER=chromium
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Instalação do navegador:**
|
|
98
|
+
O Chromium é instalado automaticamente na primeira execução de `qpx`. Se desejar instalar manualmente:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx patchright install chromium
|
|
102
|
+
```
|
|
103
|
+
Senhas das contas são armazenadas **criptografadas** no SQLite (`data/`).
|
|
104
|
+
|
|
105
|
+
### Transporte upstream e streaming
|
|
106
|
+
|
|
107
|
+
No fluxo textual principal, as chamadas ao Qwen são feitas pelo `fetch` executado dentro da página Chromium da conta. Isso mantém cookies, User-Agent, TLS, Origin e fingerprint no mesmo contexto do navegador.
|
|
108
|
+
|
|
109
|
+
A resposta de completion é consumida com `ReadableStream.getReader()` e encaminhada em chunks ao Bridge. O corpo SSE não é acumulado inteiro antes de ser entregue ao cliente. Personalization usa a página `/settings/personalization`; os demais endpoints de chat usam o mesmo contexto autenticado.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Modelos e contexto
|
|
114
|
+
|
|
115
|
+
Modelos e janelas de contexto são sincronizados em tempo real pelo catálogo `/api/models` do Qwen, separadamente para cada conta. O QwenProxy não mantém uma tabela de nomes/capabilities: modelos novos aparecem automaticamente em `/v1/models`, e o objeto `info.meta` recebido do upstream é preservado.
|
|
116
|
+
|
|
117
|
+
Exemplos do catálogo atual (podem mudar sem release do proxy):
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
| Modelo | Contexto | Output máximo | Thinking | Vision |
|
|
121
|
+
| ------------------------- | -------------: | -------------: | :--------: | :------: |
|
|
122
|
+
| `qwen3.8-max` | 1.000.000 | 131.072 | ✅ | ✅ |
|
|
123
|
+
| `qwen3.7-plus` | 1.000.000 | 65.536 | ✅ | ✅ |
|
|
124
|
+
| `qwen3.7-max` | 1.000.000 | 65.536 | ✅ | ❌ |
|
|
125
|
+
| **Fallback desconhecido** | **1.048.576** | **65.536** | — | — |
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
O fallback é usado somente quando a conta ainda não sincronizou o catálogo ou o endpoint upstream está indisponível. Depois da sincronização, contexto, output, thinking, modalidades, `think_skip`, `chat_type`, `mcp`, status ativo e demais metadata vêm do Qwen.
|
|
129
|
+
|
|
130
|
+
> **Nota:** O endpoint `/v1/models` retorna capabilities dinâmicas (formato OpenAI).
|
|
131
|
+
|
|
132
|
+
### Capabilities
|
|
133
|
+
|
|
134
|
+
Cada modelo tem um registro `ModelCapabilities` em `src/core/model-registry.ts`:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
interface ModelCapabilities {
|
|
138
|
+
maxOutputTokens: number;
|
|
139
|
+
maxThinkingTokens: number;
|
|
140
|
+
supportsThinking: boolean;
|
|
141
|
+
supportsVision: boolean;
|
|
142
|
+
canSkipThinking: boolean;
|
|
143
|
+
supportsDocument: boolean;
|
|
144
|
+
supportsAudio: boolean;
|
|
145
|
+
supportsVideo: boolean;
|
|
146
|
+
supportsCitations: boolean;
|
|
147
|
+
supportsCodeExecution: boolean;
|
|
148
|
+
supportsStructuredOutputs: boolean;
|
|
149
|
+
modalities: string[];
|
|
150
|
+
chatTypes: string[];
|
|
151
|
+
mcp: string[];
|
|
152
|
+
isActive: boolean;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Destaque `qwen3.8-max`**: modelo flagship com suporte a visão (o `qwen3.7-max` não suporta). Permite desativar thinking (`canSkipThinking: true`).
|
|
157
|
+
|
|
158
|
+
### Variantes sintéticas
|
|
159
|
+
|
|
160
|
+
- modelo base — modo **Auto** (o Qwen decide se raciocina), ex.: `qwen3.7-plus`
|
|
161
|
+
- `-fast` — Fast com thinking desativado, ex.: `qwen3.7-plus-fast`
|
|
162
|
+
- `-thinking` — Thinking forçado, ex.: `qwen3.7-plus-thinking`
|
|
163
|
+
|
|
164
|
+
As variantes usam a mesma janela de contexto e o mesmo modelo upstream do modelo base; o modo de raciocínio é selecionado pelo `feature_config` do Qwen (Auto/Fast/Thinking). O ID antigo `-no-thinking` não é publicado; é apenas normalizado internamente para `-fast` por compatibilidade legada.
|
|
165
|
+
|
|
166
|
+
### `reasoning_effort` no Chat Completions
|
|
167
|
+
|
|
168
|
+
O campo OpenAI `reasoning_effort` (`none`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`) também é aceito em `/v1/chat/completions`:
|
|
169
|
+
|
|
170
|
+
- `low`/`none`/`minimal` → força Fast (thinking OFF) quando o modelo **não** tem sufixo
|
|
171
|
+
- `medium`/`high`/`xhigh`/`max` → mantém Auto (o Qwen decide, como hoje)
|
|
172
|
+
- **Precedência:** um sufixo explícito no modelo (`-fast`/`-thinking`) sempre vence o `reasoning_effort`; ausente o campo, comportamento idêntico ao anterior (no-op)
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Responses API (`/v1/responses`)
|
|
177
|
+
|
|
178
|
+
Implementação completa da OpenAI Responses API com extensões para clientes agentic (Codex, Grok CLI, Cursor).
|
|
179
|
+
|
|
180
|
+
### Features
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
| Feature | Descrição |
|
|
184
|
+
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
185
|
+
| **SSE fiel** | `event: <type>` + `data: {...}` com `sequence_number` incremental em todos os eventos |
|
|
186
|
+
| **Memória persistente** | `previous_response_id` com store SQLite durável (sobrevive restarts, TTL 7 dias) |
|
|
187
|
+
| `**last_response_id**` | Retornado em toda response para encadeamento pelo cliente |
|
|
188
|
+
| **Reasoning effort** | `reasoning.effort` aceita qualquer string; normaliza `xhigh`/`max`/`fast`/`none`/numérico para thinking ON/OFF |
|
|
189
|
+
| **Multimodal** | `input_image` → `image_url`, `input_file` → `file_url` no chat interno |
|
|
190
|
+
| **Usage real** | `stream_options.include_usage: true`; upstream sobrescreve estimativas; `input_tokens_details` e `output_tokens_details` **sempre** presentes (fix Grok/serde) |
|
|
191
|
+
| **Reasoning lifecycle** | `reasoning_summary_part.added` → `reasoning_summary_text.delta` → `reasoning_summary_text.done` → `reasoning_summary_part.done` |
|
|
192
|
+
| **Error envelope** | Formato OpenAI: `{ error: { message, type, param, code } }` |
|
|
193
|
+
| **Store** | `store: false` desativa persistência; GET/DELETE `/v1/responses/:id` para recuperar/remover |
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
### Reasoning effort mapping
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
| Client effort | Normalizado | Qwen `feature_config` |
|
|
200
|
+
| ------------------------------------------------------ | ----------- | -------------------------------------------------------------------- |
|
|
201
|
+
| `max`, `high`, `xhigh`, `thinking`, `ultra`, `deep` | high | `thinking_enabled: true`, `thinking_mode: "Thinking"` |
|
|
202
|
+
| `medium`, `med`, `default` | medium | thinking ON (mesmo que high) |
|
|
203
|
+
| `fast`, `none`, `low`, `off`, `minimal`, `no-thinking` | low | `thinking_enabled: false`, `thinking_mode: "Fast"` e modelo `*-fast` |
|
|
204
|
+
| numérico 0–33 | low | thinking OFF |
|
|
205
|
+
| numérico 34–66 | medium | thinking ON |
|
|
206
|
+
| numérico 67–100 | high | thinking ON |
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
> **Nota:** effort `low` sempre seleciona a variante pública `*-fast`; o catálogo pode informar `think_skip`, mas esse metadado não limita a publicação da variante.
|
|
210
|
+
|
|
211
|
+
### Exemplo: Responses API com memória
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Primeira request
|
|
215
|
+
curl http://localhost:7936/v1/responses \
|
|
216
|
+
-H "Authorization: Bearer local" \
|
|
217
|
+
-H "Content-Type: application/json" \
|
|
218
|
+
-d '{"model":"qwen3.8-max","input":"Meu nome é João","stream":true}'
|
|
219
|
+
|
|
220
|
+
# Resposta inclui last_response_id: "resp_abc123..."
|
|
221
|
+
|
|
222
|
+
# Segunda request com memória
|
|
223
|
+
curl http://localhost:7936/v1/responses \
|
|
224
|
+
-H "Authorization: Bearer local" \
|
|
225
|
+
-H "Content-Type: application/json" \
|
|
226
|
+
-d '{"model":"qwen3.8-max","input":"Qual meu nome?","previous_response_id":"resp_abc123...","stream":true}'
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Exemplo: effort com Codex/Grok
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
curl http://localhost:7936/v1/responses \
|
|
233
|
+
-H "Authorization: Bearer local" \
|
|
234
|
+
-H "Content-Type: application/json" \
|
|
235
|
+
-d '{"model":"qwen3.7-max","input":"hi","reasoning":{"effort":"xhigh"},"max_output_tokens":30}'
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
## 🔄 Modos de conversação
|
|
240
|
+
|
|
241
|
+
O QwenProxy oferece uma matriz completa de **4 modos de operação**, permitindo equilibrar economia de tokens, velocidade e organização do histórico:
|
|
242
|
+
|
|
243
|
+
| Modo | Formato do Envio | Upstream Qwen | Persistência na Conta Web | Caso de Uso Ideal |
|
|
244
|
+
| :--- | :--- | :--- | :---: | :--- |
|
|
245
|
+
| **`thread-temp`** ⭐ | **Delta (~1KB)** | `chat_mode: "local"` | ❌ Não (Zero poluição) | **O melhor para o dia a dia.** Recomendado para Claude Code, Codex, OpenCode e Cursor. Máxima velocidade, TTFB ultra-baixo e não enche sua conta de chats descartáveis. |
|
|
246
|
+
| **`stateless-temp`** | **Histórico Completo** | `chat_mode: "local"` | ❌ Não (Zero poluição) | **Padrão Oficial das APIs (OpenAI/Anthropic).** Envia todas as mensagens a cada turno. Ideal se você costuma editar ou reordenar mensagens antigas durante a sessão. |
|
|
247
|
+
| **`thread`** *(Padrão)* | **Delta (~1KB)** | `chat_mode: "normal"` | ✅ Sim (Salva no site) | Ideal se você fizer questão de abrir o site `chat.qwen.ai` no celular ou navegador depois para reler o histórico da conversa. |
|
|
248
|
+
| **`stateless`** | **Histórico Completo** | `chat_mode: "normal"` | ✅ Sim (Salva no site) | Envia o histórico completo e mantém as conversas salvas na conta do Qwen. |
|
|
249
|
+
|
|
250
|
+
### Como alternar os modos:
|
|
251
|
+
|
|
252
|
+
1. **Pela TUI (Em Tempo Real para todo o Proxy):**
|
|
253
|
+
- **Na tela `[1] Status`:** Pressione a tecla **`M`** (ou clique em `[ M ] Alternar Modo`) para ciclar o modo da API global na hora.
|
|
254
|
+
- **Na tela `[2] Chat`:** Pressione **`F4`** (ou clique no indicador `[ Modo ]`) para abrir o modal de seleção vertical.
|
|
255
|
+
2. **Via Endpoint HTTP (Controle Remoto Dinâmico):**
|
|
256
|
+
```bash
|
|
257
|
+
# Inspecionar modo ativo:
|
|
258
|
+
curl http://127.0.0.1:7936/v1/chat/mode
|
|
259
|
+
|
|
260
|
+
# Alterar modo globalmente em tempo real:
|
|
261
|
+
curl -X POST http://127.0.0.1:7936/v1/chat/mode \
|
|
262
|
+
-H "Content-Type: application/json" \
|
|
263
|
+
-d '{"mode":"thread-temp"}'
|
|
264
|
+
```
|
|
265
|
+
3. **Por Requisição (Header HTTP Individual):**
|
|
266
|
+
Envie o header `X-QwenProxy-Chat-Mode: thread-temp` (ou `stateless-temp`, `thread`, `stateless`) em chamadas individuais.
|
|
267
|
+
4. **No arquivo `.env` (Padrão de Inicialização):**
|
|
268
|
+
```env
|
|
269
|
+
QWEN_CHAT_MODE=thread
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 📖 Passo a passo: Como começar do zero
|
|
275
|
+
|
|
276
|
+
### 1. Instalação
|
|
277
|
+
|
|
278
|
+
Instale o CLI do QwenProxy globalmente no seu sistema:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Via npm:
|
|
282
|
+
npm install -g qwenproxy-cli
|
|
283
|
+
|
|
284
|
+
# Ou via pnpm / bun:
|
|
285
|
+
pnpm add -g qwenproxy-cli
|
|
286
|
+
# bun add -g qwenproxy-cli
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 2. Iniciar o Dashboard Interativo (TUI)
|
|
290
|
+
|
|
291
|
+
Abra o terminal e execute:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
qpx
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
O QwenProxy inicializa o servidor de alta performance em segundo plano e abre a interface interativa no terminal. O título da janela do terminal será automaticamente definido como **`QwenProxy`**.
|
|
298
|
+
|
|
299
|
+
### 3. Adicionar Contas Qwen
|
|
300
|
+
|
|
301
|
+
Na TUI, você pode gerenciar suas contas na aba **`[5] Contas`** de duas formas simples:
|
|
302
|
+
|
|
303
|
+
- **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.
|
|
304
|
+
- **Adição Individual (`A`):** Pressione a tecla **`A`** para digitar o e-mail e a senha de uma conta específica.
|
|
305
|
+
- **Login Manual no Navegador:** Se preferir fazer login visual com captcha manual, execute no terminal: `qpx login`.
|
|
306
|
+
|
|
307
|
+
### 4. Sincronizar com seus Agentes de IA
|
|
308
|
+
|
|
309
|
+
Para configurar automaticamente seus editores e CLIs favoritos para usarem o QwenProxy:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# Sincroniza todos os clientes detectados na sua máquina:
|
|
313
|
+
qpx sync
|
|
314
|
+
|
|
315
|
+
# Ou sincronize clientes específicos:
|
|
316
|
+
qpx sync claude codex opencode
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
O sincronizador detecta e configura automaticamente:
|
|
320
|
+
- **Claude Code CLI** (`~/.claude/settings.json`) — Usa o protocolo nativo Anthropic (`/v1/messages`).
|
|
321
|
+
- **OpenAI Codex CLI** (`~/.codex/config.toml`) — Usa o protocolo nativo Responses (`/v1/responses`).
|
|
322
|
+
- **OpenCode** (`~/.config/opencode/opencode.jsonc`) — Configura provider OpenAI-compatible.
|
|
323
|
+
- **Cline, OMP, Zed, Kilo Code e Hermes Agent**.
|
|
324
|
+
|
|
325
|
+
> **Dica de Rollback:** Se quiser desfazer a configuração e restaurar os arquivos originais a qualquer momento, execute `qpx sync -- --restore`.
|
|
326
|
+
|
|
327
|
+
### 5. Pronto para Trabalhar!
|
|
328
|
+
|
|
329
|
+
Agora basta abrir o seu agente favorito normalmente:
|
|
330
|
+
```bash
|
|
331
|
+
# Usar o Claude Code:
|
|
332
|
+
claude
|
|
333
|
+
|
|
334
|
+
# Usar o Codex CLI:
|
|
335
|
+
codex
|
|
336
|
+
|
|
337
|
+
# Usar o OpenCode:
|
|
338
|
+
opencode
|
|
339
|
+
```
|
|
340
|
+
Todas as requisições fluem com máxima velocidade, failover automático entre contas e sem custos de API externa!
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 💡 Dicas de uso e produção
|
|
345
|
+
|
|
346
|
+
1. **Use `thread-temp` para Programar no Dia a Dia:**
|
|
347
|
+
Agentes de desenvolvimento geram dezenas de turnos e chamadas de ferramenta por minuto. Usar o modo `thread-temp` evita que centenas de conversas descartáveis entulhem a sua conta pessoal no `chat.qwen.ai`, mantendo o TTFB na faixa de ~0.6s a 1.2s.
|
|
348
|
+
2. **Multi-Contas para Quota Diária Alta:**
|
|
349
|
+
Adicione 2 ou mais contas no QwenProxy. As cotas do Qwen Web resetam pontualmente às **00:00 UTC**. Se uma conta atingir o limite diário, o proxy a coloca em cooldown automaticamente e faz o failover instantâneo para a próxima conta saudável.
|
|
350
|
+
3. **Limpeza Periódica de Perfis (`qpx clean`):**
|
|
351
|
+
Com o tempo de uso contínuo, o Chromium acumula cache de renderização V8/GPU. Execute `qpx clean` para purgar caches descartáveis, reduzindo o tamanho de cada perfil de ~300MB para apenas **~4.5MB**, preservando 100% os cookies e sessões ativas.
|
|
352
|
+
4. **Zerar Cooldowns na TUI:**
|
|
353
|
+
Se você quiser forçar a revalidação imediata de contas em cooldown, vá até a aba **`[1] Status`** e pressione **`Z`** (ou use `qpx reset`).
|
|
354
|
+
|
|
355
|
+
## Pré-requisitos
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
| Dependência | Versão mínima | Observação |
|
|
359
|
+
| ----------- | -------------: | ------------------------------------ |
|
|
360
|
+
| Node.js | 22+ | Conforme `engines` do `package.json` |
|
|
361
|
+
| npm | 9+ | Incluído com Node |
|
|
362
|
+
| Playwright | - | `npx playwright install chromium` |
|
|
363
|
+
| Docker | opcional | Deploy em container |
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Instalação e Execução
|
|
369
|
+
|
|
370
|
+
O QwenProxy pode ser instalado globalmente, executado instantaneamente via `npx`/`bunx`, ou clonado localmente:
|
|
371
|
+
|
|
372
|
+
### Opção 1: Instalação Global (Recomendado)
|
|
373
|
+
Instale uma única vez para ter acesso ao comando rápido **`qpx`** de qualquer lugar do terminal:
|
|
374
|
+
```bash
|
|
375
|
+
# Via npm:
|
|
376
|
+
npm install -g qwenproxy-cli
|
|
377
|
+
|
|
378
|
+
# Ou via pnpm:
|
|
379
|
+
pnpm add -g qwenproxy-cli
|
|
380
|
+
|
|
381
|
+
# Ou via bun:
|
|
382
|
+
bun add -g qwenproxy-cli
|
|
383
|
+
```
|
|
384
|
+
Após instalar, basta abrir o terminal e digitar:
|
|
385
|
+
```bash
|
|
386
|
+
qpx
|
|
387
|
+
# ou: qwenproxy
|
|
388
|
+
```
|
|
389
|
+
*(Abre diretamente o dashboard interativo da TUI com o servidor e proxy integrados).*
|
|
390
|
+
|
|
391
|
+
#### Comandos Rápidos do CLI (`qpx`)
|
|
392
|
+
|
|
393
|
+
| Comando | Descrição |
|
|
394
|
+
| :--- | :--- |
|
|
395
|
+
| `qpx` *(ou `qwenproxy`)* | Abre o dashboard visual interativo da TUI com servidor proxy integrado |
|
|
396
|
+
| `qpx start` *(ou `--server`)* | Inicia apenas o servidor HTTP/SSE em modo headless (sem interface gráfica) |
|
|
397
|
+
| `qpx update` | Verifica e atualiza o QwenProxy automaticamente via npm/pnpm/bun |
|
|
398
|
+
| `qpx login` | Abre navegador visível para autenticar novas contas interativamente |
|
|
399
|
+
| `qpx sync` | Configura e sincroniza clientes (Claude Code, Codex, OpenCode, OMP) |
|
|
400
|
+
| `qpx clean` | Limpa caches temporários dos perfis Chromium (~4.5MB por conta) |
|
|
401
|
+
| `qpx clean:all` | Limpa caches e remove versões antigas de navegadores órfãos em disco |
|
|
402
|
+
| `qpx purge` | Limpa o histórico de conversas remotas no Qwen de todas as contas |
|
|
403
|
+
| `qpx reset` | Reseta cooldowns e rate limits salvos no banco de dados |
|
|
404
|
+
### Opção 2: Execução Instantânea (Zero Instalação)
|
|
405
|
+
Experimente ou execute pontualmente sem instalar nada permanentemente:
|
|
406
|
+
```bash
|
|
407
|
+
npx qwenproxy-cli
|
|
408
|
+
# ou: bunx qwenproxy-cli
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Opção 3: Clonando o Código (Desenvolvimento)
|
|
412
|
+
```bash
|
|
413
|
+
git clone https://github.com/johngbl/qwenproxy.git
|
|
414
|
+
cd qwenproxy
|
|
415
|
+
npm install
|
|
416
|
+
npm run tui # Abre a TUI interativa
|
|
417
|
+
# ou: npm start # Inicia apenas o servidor HTTP headless
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Opção 4: Via Docker
|
|
421
|
+
```bash
|
|
422
|
+
docker-compose up -d
|
|
423
|
+
```
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Início rápido
|
|
427
|
+
|
|
428
|
+
Crie um `.env` na raiz (use `.env.example` como base).
|
|
429
|
+
|
|
430
|
+
### Exemplo mínimo
|
|
431
|
+
|
|
432
|
+
```env
|
|
433
|
+
QWEN_ACCOUNTS=user1@example.com:senha1;user2@example.com:senha2
|
|
434
|
+
API_KEY=sua-chave-local
|
|
435
|
+
HOST=127.0.0.1
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
> **Dica:** use `;` como separador de contas (`,` legado ainda funciona).
|
|
439
|
+
> Senhas com `:`, `#` e espaços são aceitas.
|
|
440
|
+
|
|
441
|
+
### Iniciar
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
npm start
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
> **Nota:** o servidor não inicia sem pelo menos uma conta configurada (via `.env`/`QWEN_ACCOUNTS`, `npm run login` ou banco de contas).
|
|
448
|
+
|
|
449
|
+
1. O servidor inicia instantaneamente com a **primeira conta pronta** para responder requisições imediatamente.
|
|
450
|
+
2. Com `PLAYWRIGHT_PREPARE_ALL_ON_STARTUP=false` (padrão econômico), as contas adicionais permanecem em **Standby**, com credenciais validadas no banco, sendo inicializadas apenas sob demanda (failover ou rotação), poupando RAM e CPU.
|
|
451
|
+
3. Todas as contas ativas compartilham a mesma instância Chromium única, mantendo sessões isoladas via `BrowserContext` e arquivos leves de estado `storage_state.json`.
|
|
452
|
+
4. O watchdog RSS do sistema monitora a pressão de memória e fecha contextos ociosos automaticamente.
|
|
453
|
+
Exemplo de log:
|
|
454
|
+
|
|
455
|
+
```text
|
|
456
|
+
✅ [Server] Account ready (1/6): us***@example.com
|
|
457
|
+
🪶 [Server] Preparing 5 standby account(s) in background
|
|
458
|
+
✅ [Server] Account ready (2/6): us***@example.com
|
|
459
|
+
...
|
|
460
|
+
|
|
461
|
+
+----------------------------------------------------------+
|
|
462
|
+
| QwenProxy |
|
|
463
|
+
| OpenAI & Anthropic Compatible API |
|
|
464
|
+
| Endpoint http://127.0.0.1:7936/v1 |
|
|
465
|
+
| Accounts 1/6 warm |
|
|
466
|
+
| Status ● Online |
|
|
467
|
+
+----------------------------------------------------------+
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Testes
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
npm test # mock + live
|
|
476
|
+
npm run test:mock # suite mock (sem browser real de contas)
|
|
477
|
+
npm run test:live # stress/concurrency reais
|
|
478
|
+
npm run typecheck # tipos
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## Variáveis de ambiente
|
|
484
|
+
|
|
485
|
+
### Rede e segurança
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
| Variável | Default | Descrição |
|
|
489
|
+
| --------- | --------- | -------------------------------- |
|
|
490
|
+
| `PORT` | `7936` | Porta HTTP (padrão QWEN: 7936). Configurável via .env |
|
|
491
|
+
| `HOST` | `0.0.0.0` | Bind host. Local: `127.0.0.1` |
|
|
492
|
+
| `API_KEY` | vazio | Protege `/v1/*` com Bearer token |
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
### Contas e sessão
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
| Variável | Default | Descrição |
|
|
499
|
+
| ----------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
500
|
+
| `QWEN_ACCOUNTS` | vazio | `email1:senha1;email2:senha2` |
|
|
501
|
+
| `DELETE_ALL_CHATS_ON_SHUTDOWN` | `false` | Limpa chats no shutdown |
|
|
502
|
+
| `QWEN_PERSONALIZATION_FROM_REQUEST` | `true` | Envia system + tools via `/settings/personalization` |
|
|
503
|
+
| `QWEN_PERSONALIZATION_VERIFY_GET` | `true` | Confirma personalization com GET |
|
|
504
|
+
| `QWEN_MAX_PERSONALIZATION_BYTES` | `200000` | Teto UTF-8 para personalization por request; acima disso as instruções seguem inline |
|
|
505
|
+
| `QWEN_CHAT_POOL_SIZE` | `1` | Warm pool de chats por modelo; fica desativado quando personalization por request está ativa |
|
|
506
|
+
| `QWEN_CHAT_POOL_MODELS` | `qwen3.7-plus` | Modelos aquecidos no warm pool |
|
|
507
|
+
| `QWEN_CHAT_MODE` | `thread` | Modo de conversa padrão: `thread`, `thread-temp`, `stateless` ou `stateless-temp`. Override dinâmico via TUI (`M`/`F4`), HTTP (`/v1/chat/mode`) ou header `X-QwenProxy-Chat-Mode`. |
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
### Playwright / processos
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
| Variável | Default | Descrição |
|
|
514
|
+
| ------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
515
|
+
| `PLAYWRIGHT_HEADLESS` | `true` | Browser sem janela |
|
|
516
|
+
| `PLAYWRIGHT_BROWSER` | `chromium` | `chromium` / `chrome` / `edge` |
|
|
517
|
+
| `PLAYWRIGHT_INIT_BATCH_SIZE` | `1` | Contas em paralelo no background init |
|
|
518
|
+
| `PLAYWRIGHT_PREPARE_ALL_ON_STARTUP` | `false` | Prepara somente a primeira conta no boot (`false` = modo econômico sob demanda; `true` = aquece todas) |
|
|
519
|
+
| `PLAYWRIGHT_MAX_ACTIVE_CONTEXTS` | `2` | Contextos idle mantidos quentes ({principal + reserva}); streams ativos nunca são fechados; uso simultâneo abre mais. Contas em cooldown (rate limit) ficam idle e são evictadas |
|
|
520
|
+
| `PLAYWRIGHT_CONTEXT_CLOSE_TIMEOUT_MS` | `10000` | Timeout de close antes do kill |
|
|
521
|
+
| `PLAYWRIGHT_IDLE_CONTEXT_TTL_MS` | `60000` | Fecha contextos idle acima do cap (`0` desativa) |
|
|
522
|
+
| `PLAYWRIGHT_JS_HEAP_MB` | `256` | Cap V8 do Chromium (`--max-old-space-size`) |
|
|
523
|
+
| `PLAYWRIGHT_LOW_MEMORY_FLAGS` | `true` | Flags de baixa RAM (heap cap, cache mínimo, renderer limit) |
|
|
524
|
+
| `OSS_MULTIPART_THRESHOLD_MB` | `5` | Acima disso usa multipart OSS; abaixo `putStream` |
|
|
525
|
+
| `SESSION_KEEP_ALIVE_ENABLED` | `true` | Simula navegações leves periódicas a cada 3min para manter sessões ativas sem desconectar |
|
|
526
|
+
| `SESSION_KEEP_ALIVE_INTERVAL_MS` | `180000` | Intervalo do ciclo de keep-alive/cleanup |
|
|
527
|
+
| `SESSION_KEEP_ALIVE_IDLE_MS` | `120000` | Idle mínimo para keep-alive |
|
|
528
|
+
| `SESSION_KEEP_ALIVE_NAVIGATION_INTERVAL_MS` | `480000` | Intervalo de navegação leve |
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
### CAPTCHA automático
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
| Variável | Default | Descrição |
|
|
535
|
+
| ------------------------------- | -------- | -------------------------------------------------------------------------------------- |
|
|
536
|
+
| `CAPTCHA_SOLVER_ENABLED` | `true` | Solver Baxia/TMD ativo por padrão; use `false` somente como desligamento de emergência |
|
|
537
|
+
| `CAPTCHA_SOLVER_MAX_ATTEMPTS` | `3` | Máximo de arrastos por challenge |
|
|
538
|
+
| `CAPTCHA_SOLVER_TIMEOUT_MS` | `15000` | Tempo para o iframe Baxia aparecer |
|
|
539
|
+
| `CAPTCHA_SOLVER_RETRY_DELAY_MS` | `1000` | Espera entre tentativas do slider |
|
|
540
|
+
| `CAPTCHA_SOLVER_SETTLE_MS` | `2000` | Tempo para confirmar cookies/DOM após o arrasto |
|
|
541
|
+
| `CAPTCHA_ACCOUNT_COOLDOWN_MS` | `120000` | Cooldown da conta quando o desafio não pôde ser resolvido; `0` desliga |
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
### Headers anti-bot
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
| Variável | Default | Descrição |
|
|
548
|
+
| ----------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
|
|
549
|
+
| `USER_AGENT` | Chrome 149 Windows | UA fallback |
|
|
550
|
+
| `QWEN_BX_V` | `2.5.37` | `bx-v` fallback; `bx-ua`/`bx-umidtoken` **não** são enviados como headers (o cliente real os carrega como cookies WAF) |
|
|
551
|
+
| `QWEN_SEND_BX_UA` | `false` | `true` restaura o comportamento legado de injetar `bx-ua`/`bx-umidtoken` capturados como headers |
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
Fingerprint estável por conta (UA, locale, viewport, hardware/WebGL) é aplicado automaticamente.
|
|
555
|
+
|
|
556
|
+
### Delays e retry
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
| Variável | Default | Descrição |
|
|
560
|
+
| ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
|
|
561
|
+
| `RETRY_BASE_DELAY_MS` | `1000` | Base do exponential backoff |
|
|
562
|
+
| `RETRY_MAX_DELAY_MS` | `10000` | Cap do backoff |
|
|
563
|
+
| `RETRY_MAX_ATTEMPTS` | `3` | Tentativas por request (create-stream + mid-stream) |
|
|
564
|
+
| `RETRY_MAX_ACCOUNT_SWITCHES` | `2` | Máximo de trocas de conta por request |
|
|
565
|
+
| `RETRY_ON_UNKNOWN_UPSTREAM` | `true` | Retry/troca automática em erros upstream desconhecidos (denylist só para erros locais terminais) |
|
|
566
|
+
| `RETRY_AUTO_MALFORMED_TOOLS` | `true` | Auto-retry quando todos os tool calls da resposta vêm malformados |
|
|
567
|
+
| `RETRY_AUTO_MALFORMED_TOOLS_MAX` | `2` | Máximo de retries de tool calls malformados por resposta |
|
|
568
|
+
| `MAX_TOOL_CALLS_PER_TURN` | `8` | Teto de tool calls por turno (0 desativa); calls duplicadas idênticas também são descartadas |
|
|
569
|
+
| `CHAT_IN_PROGRESS_RETRY_DELAY_MS` | `2000` | Espera antes de repetir no mesmo chat após `chat_in_progress` |
|
|
570
|
+
| `CHAT_IN_PROGRESS_BUSY_MS` | `4000` | Janela busy da conta após `chat_in_progress` (absorve o settle do upstream) |
|
|
571
|
+
| `MID_STREAM_FAILOVER_THRESHOLD` | `2` | Falhas de rede mid-stream nesta janela marcam a conta temporarily busy |
|
|
572
|
+
| `MID_STREAM_FAILOVER_BUSY_MS` | `60000` | Duração do busy após o threshold mid-stream |
|
|
573
|
+
| `ACQUIRE_DEADLINE_MS` | `120000` | Deadline por tentativa de acquire do stream (falha visível → troca de conta) |
|
|
574
|
+
| `ACCOUNT_QUEUE_WAIT_FOREVER_CAP_MS` | `120000` | Cap de espera na fila "sem deadline" de contas |
|
|
575
|
+
| `ACCOUNT_LEASE_MAX_DURATION_MS` | `600000` | Vida máxima de uma lease de conta |
|
|
576
|
+
| `ACCOUNT_INIT_FAILURE_COOLDOWN_MS` | `300000` | Cooldown após falha de init de conta |
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
### Timeouts
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
| Variável | Default | Descrição |
|
|
583
|
+
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
|
|
584
|
+
| `HTTP_TIMEOUT` | `10000` | HTTP genérico |
|
|
585
|
+
| `CHAT_TIMEOUT` | `120000` | Timeout de chat |
|
|
586
|
+
| `NAVIGATION_TIMEOUT` | `60000` | Navegação Playwright |
|
|
587
|
+
| `PAGE_TIMEOUT` | `60000` | Operações de página |
|
|
588
|
+
| `HEADERS_TIMEOUT` | `60000` | Captura de headers |
|
|
589
|
+
| `TIME_TO_FIRST_BYTE` | `60000` | Janela de primeiro byte (teto com piso de 15s no metadata) |
|
|
590
|
+
| `IDLE_STREAM_TIMEOUT` | `60000` | Stream sem dados (modelos não-reasoning) |
|
|
591
|
+
| `TOTAL_REQUEST_TIMEOUT` | `600000` | Teto de geração |
|
|
592
|
+
| `REASONING_MODEL_TIMEOUT` | `180000` | Silêncio mid-stream para modelos reasoning (chunks fluidos resetam; zero bytes por 3min = morto) |
|
|
593
|
+
| `QWEN_FIRST_CHUNK_TIMEOUT` | `180000` | Deadline do PRIMEIRO chunk (thought = 0 bytes por 3min aborta retryável) |
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
**Nota:** timeouts dinâmicos de payload: **modelos reasoning** usam `REASONING_MODEL_TIMEOUT` (180s default) + 30s por MB; **modelos não-reasoning** usam `IDLE_STREAM_TIMEOUT` (60s default) + 30s por MB.
|
|
597
|
+
|
|
598
|
+
### Cache e contexto
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
| Variável | Default | Descrição |
|
|
602
|
+
| ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
603
|
+
| `CACHE_TTL` | `3600` | TTL do cache (s) |
|
|
604
|
+
| `CACHE_COMPRESSION_ENABLED` | `true` | Compressão Brotli |
|
|
605
|
+
| `QWEN_MAX_PROMPT_BYTES` | `0` | Teto opcional local UTF-8 do prompt (`0` desativa); não é a janela de tokens. O payload total continua limitado a 50 MiB |
|
|
606
|
+
| `CONTEXT_METER_ENABLED` | `true` | Medição do histórico completo, delta/replay, payload Qwen e percentuais de contexto; já vem ativa por padrão |
|
|
607
|
+
| `CONTEXT_METER_WINDOW_TOKENS` | `0` | Janela usada pelo medidor (`0` usa a janela real registrada para o modelo) |
|
|
608
|
+
| `CONTEXT_METER_REPORT_USAGE` | `true` | Reporta em `usage.prompt_tokens` o valor real `input_tokens` do Qwen quando disponível; só usa a estimativa como fallback |
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
O medidor de contexto é padrão e não exige nenhuma variável no `.env`. Ele não é um tokenizer nativo do Zed/Cline nem substitui o tokenizer privado do Qwen: calcula uma estimativa local do histórico completo recebido pelo proxy, registra o prompt delta/replay efetivamente enviado e preserva `usage.context_meter` com `measurementSource=qwen` quando o Qwen devolve `input_tokens`, ou `measurementSource=local_estimate` quando não devolve. A janela do modelo é sincronizada automaticamente pelo `/api/models`, e são emitidos headers `X-QwenProxy-Context-*` e logs estruturados. As três variáveis podem ser usadas somente como overrides avançados; por padrão o valor real do Qwen é preferido e a estimativa só é fallback.
|
|
612
|
+
|
|
613
|
+
### Observabilidade
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
| Variável | Default | Descrição |
|
|
617
|
+
| --------------------- | -------- | ------------------------------------------------------------------------------- |
|
|
618
|
+
| `CHAT_REQUEST_LOG` | `false` | Logs detalhados de request |
|
|
619
|
+
| `LOG_LEVEL` | `warn` | Nível do logger (`debug`/`info`/`warn`/`error`); `TOOLCALL_DEBUG=1` força debug |
|
|
620
|
+
| `METRICS_INTERVAL` | `10000` | Intervalo de métricas |
|
|
621
|
+
| `WATCHDOG_INTERVAL` | `5000` | Intervalo do watchdog |
|
|
622
|
+
| `RAM_WARNING` | `80` | % RSS warning (RSS / totalmem) |
|
|
623
|
+
| `RAM_CRITICAL` | `95` | % RSS critical (RSS / totalmem) |
|
|
624
|
+
| `RATE_LIMIT_REQUESTS` | `5000` | Header estático `x-ratelimit-limit-requests` (não impõe quota) |
|
|
625
|
+
| `RATE_LIMIT_TOKENS` | `200000` | Header estático `x-ratelimit-limit-tokens` (não impõe quota) |
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Retries e resiliência
|
|
631
|
+
|
|
632
|
+
O proxy tenta recuperar erros transitórios sem quebrar thread-native/tools:
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
| Situação | Comportamento |
|
|
636
|
+
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
637
|
+
| `502` / `503` / `504` | Retry com delay curto |
|
|
638
|
+
| `fetch failed`, `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND` | Retry de rede |
|
|
639
|
+
| Anti-bot (`FAIL_SYS_USER_VALIDATE`, captcha, WAF HTML, etc.) | Com solver Baxia habilitado: preserva a página, tenta resolver uma vez, atualiza headers e repete na mesma conta; sem solver, mantém o retry simples |
|
|
640
|
+
| Quota / rate limit | Cooldown categorizado (`RateLimited`, `RateLimitTemporary`, …) |
|
|
641
|
+
| `invalid_input` (“entrada ou anexo inválido”) | Retry forçando **novo chat** + contexto completo |
|
|
642
|
+
| Chat not exist / session stale | Força novo chat na sessão lógica |
|
|
643
|
+
| Tool calls malformados (todos inválidos) | Reparo local do JSON; se não resolver, auto-retry na mesma conta com novo chat e correção enviada ao modelo (até `RETRY_AUTO_MALFORMED_TOOLS_MAX`) |
|
|
644
|
+
| `INVALID_FIRST_MSG` / histórico corrompido | Novo chat + contexto completo na **mesma conta** (a corrupção é da cadeia de parent, não da conta); o thread lógico contaminado é invalidado |
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
Settings seguras aplicadas no sync de personalization (sem reescrever tudo da conta):
|
|
648
|
+
|
|
649
|
+
```json
|
|
650
|
+
{
|
|
651
|
+
"ui": { "autoTags": false, "largeTextAsFile": false, "splitLargeChunks": false },
|
|
652
|
+
"mcp_remind": false,
|
|
653
|
+
"memory": { "enable_memory": false, "enable_history_memory": false },
|
|
654
|
+
"tools_enabled": { "web_search": false, "code_interpreter": false }
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## Anti-bot
|
|
661
|
+
|
|
662
|
+
Detecta, entre outros:
|
|
663
|
+
|
|
664
|
+
- `FAIL_SYS_USER_VALIDATE`
|
|
665
|
+
- `RGV587_ERROR`
|
|
666
|
+
- mensagens de captcha / human verification
|
|
667
|
+
|
|
668
|
+
**Fluxo:**
|
|
669
|
+
|
|
670
|
+
1. Identifica o WAF/captcha sem expor o HTML do desafio ao cliente
|
|
671
|
+
2. Se `CAPTCHA_SOLVER_ENABLED=true`, detecta o diálogo Baxia já visível e procura o iframe aninhado, o iframe legado ou o documento NC diretamente na página da mesma conta
|
|
672
|
+
3. Se nada estiver visível — o caso normal, porque o completion roda como `fetch` em background e o WAF responde o documento de punish ao XHR em vez de renderizar algo — extrai a URL do desafio do corpo da resposta e abre essa URL na própria página da conta; sem URL utilizável, recarrega a página de chat para forçar o desafio a aparecer. Só a origem configurada em `QWEN_BASE_URL` pode ser aberta
|
|
673
|
+
4. Executa o slider com limite de tentativas e volta a página para `/c/new-chat`
|
|
674
|
+
5. Após sucesso, captura novamente cookies/headers e repete a requisição original na mesma conta
|
|
675
|
+
6. Se o solver falhar, a conta entra em cooldown por `CAPTCHA_ACCOUNT_COOLDOWN_MS` e a requisição é encaminhada para **uma** outra conta; percorrer o pool inteiro apenas faria o WAF desafiar todas as contas
|
|
676
|
+
7. Uma recuperação que falhou é ignorada por 30s na mesma conta, para o retry loop não gastar o orçamento do solver em cada tentativa
|
|
677
|
+
|
|
678
|
+
Com Playwright, cada conta usa fingerprint e headers capturados do browser real.
|
|
679
|
+
|
|
680
|
+
O solver Baxia/TMD fica ativo por padrão e cobre o slider NC visível em iframe ou documento direto. O proxy não salva HTML, screenshot, cookies ou tokens do challenge; desafios não suportados continuam no fluxo sanitizado de retry na mesma conta.
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Compatibilidade real das rotas
|
|
685
|
+
|
|
686
|
+
O README descreve o uso operacional. Para detalhes técnicos da API (schemas, exemplos, headers), veja:
|
|
687
|
+
|
|
688
|
+
- [`docs/openapi.yaml`](docs/openapi.yaml) — OpenAPI 3.1 spec com todas as rotas (Chat, Completions, Responses, Models, Upload, Health)
|
|
689
|
+
|
|
690
|
+
> **Nota:** A spec OpenAPI é mantida atualizada com as mudanças recentes (auth Bearer + x-api-key, health heap detalhado).
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## Endpoints
|
|
695
|
+
|
|
696
|
+
### OpenAI Compatible
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
| Rota | Método | Descrição |
|
|
700
|
+
| --------------------------- | ------ | ----------------------------------------- |
|
|
701
|
+
| `/v1/chat/completions` | POST | Chat completions (stream + non-stream) |
|
|
702
|
+
| `/v1/completions` | POST | Completions legado (adapter sobre o chat) |
|
|
703
|
+
| `/v1/chat/completions/stop` | POST | Abortar geração |
|
|
704
|
+
| `/v1/models` | GET | Listar modelos |
|
|
705
|
+
| `/v1/models/:id` | GET | Modelo específico |
|
|
706
|
+
| `/v1/responses` | POST | OpenAI Responses API |
|
|
707
|
+
| `/v1/responses/:id` | GET | Recuperar response armazenada |
|
|
708
|
+
| `/v1/responses/:id` | DELETE | Deletar response |
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
### Anthropic Compatible (Claude Code CLI / Anthropic SDK)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
| Rota | Método | Descrição |
|
|
715
|
+
| --------------------------- | ------ | ------------------------------------------------------------- |
|
|
716
|
+
| `/v1/messages` | POST | Anthropic Messages API (stream, thinking, tools, Claude Code) |
|
|
717
|
+
| `/v1/messages/count_tokens` | POST | Contagem de tokens compatível com Anthropic |
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
### Geração de Mídia (Fotos e Vídeos)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
| Rota | Método | Descrição |
|
|
724
|
+
| -------------------------- | ------ | ------------------------------------------------------------------------------------ |
|
|
725
|
+
| `/v1/images/generations` | POST | Geração de fotos/imagens (`qwen-image-3.0-pro`, `wan2.7-image-pro`, `z-image-turbo`) |
|
|
726
|
+
| `/v1/videos/generations` | POST | Geração de vídeos (`wan3.0-video` até 30s em 1080P, `wan2.7-t2v` com áudio) |
|
|
727
|
+
| `/v1/tasks/status/:taskId` | GET | Consulta de status e download da tarefa de vídeo |
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
### Utilidades
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
| Rota | Método | Descrição |
|
|
734
|
+
| ------------ | ------ | ------------------------------------------------- |
|
|
735
|
+
| `/health` | GET | Health check |
|
|
736
|
+
| `/metrics` | GET | Prometheus (protegido por API key se configurada) |
|
|
737
|
+
| `/v1/upload` | POST | Upload multimodal |
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
> Rotas sem o prefixo `/v1` (ex.: `/chat/completions`) são redirecionadas com 308 preservando método e corpo. Respostas incluem headers OpenAI (`openai-version`, `openai-processing-ms`, `x-ratelimit-*`).
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Exemplos de uso
|
|
745
|
+
|
|
746
|
+
### OpenAI SDK (Node.js)
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
import OpenAI from "openai";
|
|
750
|
+
|
|
751
|
+
const client = new OpenAI({
|
|
752
|
+
baseURL: "http://localhost:7936/v1",
|
|
753
|
+
apiKey: "sua-api-key",
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
const completion = await client.chat.completions.create({
|
|
757
|
+
model: "qwen3.7-plus",
|
|
758
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
console.log(completion.choices[0].message.content);
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
### Anthropic SDK / Claude Code CLI
|
|
765
|
+
|
|
766
|
+
O proxy é 100% compatível com o **Claude Code CLI** e o **Anthropic SDK**:
|
|
767
|
+
|
|
768
|
+
```bash
|
|
769
|
+
# Configuração para Claude Code CLI
|
|
770
|
+
export ANTHROPIC_BASE_URL="http://localhost:7936"
|
|
771
|
+
export ANTHROPIC_API_KEY="sua-api-key"
|
|
772
|
+
|
|
773
|
+
# Iniciar Claude Code
|
|
774
|
+
claude
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
779
|
+
|
|
780
|
+
const anthropic = new Anthropic({
|
|
781
|
+
baseURL: "http://localhost:7936",
|
|
782
|
+
apiKey: "sua-api-key",
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
const message = await anthropic.messages.create({
|
|
786
|
+
model: "claude-3-7-sonnet-20250219", // ou "qwen3.8-max"
|
|
787
|
+
max_tokens: 1024,
|
|
788
|
+
messages: [{ role: "user", content: "Olá!" }],
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
console.log(message.content[0]);
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### OpenAI Responses API (Codex / Grok CLI)
|
|
795
|
+
|
|
796
|
+
```typescript
|
|
797
|
+
import OpenAI from "openai";
|
|
798
|
+
|
|
799
|
+
const client = new OpenAI({
|
|
800
|
+
baseURL: "http://localhost:7936/v1",
|
|
801
|
+
apiKey: "sua-api-key",
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
// Streaming com reasoning effort
|
|
805
|
+
const stream = await client.responses.create({
|
|
806
|
+
model: "qwen3.8-max",
|
|
807
|
+
input: "Explique computação quântica",
|
|
808
|
+
reasoning: { effort: "high" },
|
|
809
|
+
stream: true,
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
for await (const event of stream) {
|
|
813
|
+
if (event.type === "response.output_text.delta") {
|
|
814
|
+
process.stdout.write(event.delta);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Geração de Imagens (`/v1/images/generations`)
|
|
820
|
+
|
|
821
|
+
```bash
|
|
822
|
+
curl http://localhost:7936/v1/images/generations \
|
|
823
|
+
-H "Content-Type: application/json" \
|
|
824
|
+
-H "Authorization: Bearer sua-api-key" \
|
|
825
|
+
-d '{
|
|
826
|
+
"model": "qwen-image-3.0-pro",
|
|
827
|
+
"prompt": "A futuristic cyberpunk city in the rain, ultra-detailed, cinematic lighting",
|
|
828
|
+
"size": "16:9"
|
|
829
|
+
}'
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
### Geração de Vídeos (`/v1/videos/generations`)
|
|
833
|
+
|
|
834
|
+
```bash
|
|
835
|
+
curl http://localhost:7936/v1/videos/generations \
|
|
836
|
+
-H "Content-Type: application/json" \
|
|
837
|
+
-H "Authorization: Bearer sua-api-key" \
|
|
838
|
+
-d '{
|
|
839
|
+
"model": "wan3.0-video",
|
|
840
|
+
"prompt": "Drone shot flying over a misty pine forest at sunrise",
|
|
841
|
+
"size": "16:9",
|
|
842
|
+
"wait": true
|
|
843
|
+
}'
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
### cURL
|
|
849
|
+
|
|
850
|
+
```bash
|
|
851
|
+
curl http://localhost:7936/v1/chat/completions \
|
|
852
|
+
-H "Content-Type: application/json" \
|
|
853
|
+
-H "Authorization: Bearer sua-api-key" \
|
|
854
|
+
-d '{
|
|
855
|
+
"model": "qwen3.7-plus",
|
|
856
|
+
"messages": [{"role": "user", "content": "Hello!"}],
|
|
857
|
+
"stream": true
|
|
858
|
+
}'
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Grok CLI (config)
|
|
862
|
+
|
|
863
|
+
```toml
|
|
864
|
+
[model.qwen38-max]
|
|
865
|
+
api_backend = "responses"
|
|
866
|
+
base_url = "http://127.0.0.1:7936/v1"
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## Tool calling
|
|
872
|
+
|
|
873
|
+
O parser suporta:
|
|
874
|
+
|
|
875
|
+
- tags `<tool_call>...</tool_call>` e variantes Qwen `<tool_calls>...</tool_call(s)>` (fechamentos case-insensitive)
|
|
876
|
+
- formato Hermes/XML (`<parameter name="...">`)
|
|
877
|
+
- JSON malformado / recovery (aspas/braces faltando)
|
|
878
|
+
- JSON **duplamente escapado** em arguments
|
|
879
|
+
- stream fragmentado / tool call sem open tag
|
|
880
|
+
- **fuzzy match** seguro de nomes (`readFile` → `read_file`) quando há match único
|
|
881
|
+
- tool names não declarados: podem ser preservados como texto literal (evita quebrar exemplos)
|
|
882
|
+
- **auto-retry**: se todos os tool calls vierem malformados, o proxy tenta reparo local e, se necessário, repete a geração em novo chat informando o erro ao modelo (até `RETRY_AUTO_MALFORMED_TOOLS_MAX`)
|
|
883
|
+
|
|
884
|
+
Tools internas da conta Qwen (web_search, code interpreter, etc.) ficam desligadas; o proxy usa as tools do cliente.
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## Modelos
|
|
889
|
+
|
|
890
|
+
O proxy envia o id do modelo ao Qwen **como está**. Apenas os sufixos de raciocínio são normalizados antes de subir (via `stripThinkingSuffix`):
|
|
891
|
+
|
|
892
|
+
- `qwen3.7-plus` → base (Auto: o Qwen decide)
|
|
893
|
+
- `qwen3.7-plus-fast` → base + thinking OFF
|
|
894
|
+
- `qwen3.7-plus-thinking` → base + thinking ON
|
|
895
|
+
- `qwen3.7-plus-no-thinking` → base + thinking OFF (compat legado)
|
|
896
|
+
|
|
897
|
+
---
|
|
898
|
+
|
|
899
|
+
## Deploy com Docker
|
|
900
|
+
|
|
901
|
+
```yaml
|
|
902
|
+
services:
|
|
903
|
+
qwenproxy:
|
|
904
|
+
build: .
|
|
905
|
+
container_name: qwenproxy
|
|
906
|
+
ports:
|
|
907
|
+
- "${PORT:-7936}:7936"
|
|
908
|
+
env_file:
|
|
909
|
+
- .env
|
|
910
|
+
volumes:
|
|
911
|
+
- ./data:/app/data
|
|
912
|
+
restart: unless-stopped
|
|
913
|
+
logging:
|
|
914
|
+
driver: "json-file"
|
|
915
|
+
options:
|
|
916
|
+
max-size: "10m"
|
|
917
|
+
max-file: "3"
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
O container ajusta permissões de `data/db` e `data/qwen_profiles` no startup.
|
|
921
|
+
|
|
922
|
+
---
|
|
923
|
+
|
|
924
|
+
## Estrutura do projeto
|
|
925
|
+
|
|
926
|
+
```
|
|
927
|
+
QwenProxy/
|
|
928
|
+
├── src/
|
|
929
|
+
│ ├── api/ # Server Hono, models, errors
|
|
930
|
+
│ ├── benchmarks/ # Baseline de latência do proxy
|
|
931
|
+
│ ├── cache/ # Memory cache + Brotli
|
|
932
|
+
│ ├── core/ # Config, accounts, DB, metrics, cooldowns, model-registry
|
|
933
|
+
│ ├── routes/
|
|
934
|
+
│ │ ├── chat/ # Completions, streaming, account acquire, retry-policy
|
|
935
|
+
│ │ └── responses/ # OpenAI Responses API (state, streaming, adapter)
|
|
936
|
+
│ ├── services/
|
|
937
|
+
│ │ ├── playwright.ts # Browser + headers + cleanup
|
|
938
|
+
│ │ ├── qwen.ts # Upstream Qwen + personalization + idle timeout
|
|
939
|
+
│ │ ├── session-keeper.ts
|
|
940
|
+
│ │ ├── fingerprint.ts
|
|
941
|
+
│ │ └── human-behavior.ts
|
|
942
|
+
│ ├── tools/ # Parser e instruções de tools
|
|
943
|
+
│ ├── tests/
|
|
944
|
+
│ └── utils/
|
|
945
|
+
├── data/ # SQLite, key e profiles (gitignored)
|
|
946
|
+
├── Dockerfile
|
|
947
|
+
├── docker-compose.yml
|
|
948
|
+
└── package.json
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
## Scripts úteis
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
| Comando | Descrição |
|
|
957
|
+
| ------------------- | ----------------------------------------------------------------------- |
|
|
958
|
+
| `qpx` *(ou `npm run tui`)* | Abrir o dashboard interativo da TUI com servidor proxy integrado |
|
|
959
|
+
| `qpx start` *(ou `npm start`)* | Iniciar apenas o servidor HTTP/SSE em modo headless (sem interface) |
|
|
960
|
+
| `qpx sync` *(ou `npm run sync`)* | Sincronizar clientes (Claude Code, Codex, OpenCode, Cline, OMP) |
|
|
961
|
+
| `qpx clean` | Purgar caches temporários dos perfis Chromium (~4.5MB por conta) |
|
|
962
|
+
| `qpx clean:all` | Purgar caches e remover navegadores órfãos antigos em disco (~4GB) |
|
|
963
|
+
| `qpx reset` | Zerar cooldowns de contas no banco de dados |
|
|
964
|
+
| `qpx login` | Adicionar e autenticar contas visualmente no navegador |
|
|
965
|
+
| `qpx purge` | Limpar chats remotos do Qwen nas contas configuradas |
|
|
966
|
+
| `qpx update` | Atualizar o QwenProxy automaticamente para a versão mais recente |
|
|
967
|
+
| `npm test` | Executar a suíte completa de testes automatizados |
|
|
968
|
+
| `npm run typecheck` | Checagem estrita de tipos do TypeScript (zero erros) |
|
|
969
|
+
---
|
|
970
|
+
|
|
971
|
+
## Scripts de instalação, início e atualização
|
|
972
|
+
|
|
973
|
+
A pasta `scripts/` contém atalhos para instalar, iniciar e atualizar o projeto sem digitar os comandos manualmente.
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
| Script | Windows | Linux/macOS | O que faz |
|
|
977
|
+
| ----------- | --------------------- | ---------------------- | -------------------------------------------------------------------------------------------- |
|
|
978
|
+
| Instalador | `scripts\install.bat` | `./scripts/install.sh` | Verifica Node 22+, roda `npm install`, cria `.env` a partir de `.env.example` se não existir |
|
|
979
|
+
| Iniciador | `scripts\start.bat` | `./scripts/start.sh` | Verifica dependências e `.env`, inicia o servidor com `npm start` |
|
|
980
|
+
| Atualizador | `scripts\update.bat` | `./scripts/update.sh` | `git pull` (se for repositório), `npm install` e `npx playwright install chromium` |
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
No Linux/macOS, dê permissão de execução na primeira vez:
|
|
984
|
+
|
|
985
|
+
```bash
|
|
986
|
+
chmod +x scripts/*.sh
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
---
|
|
990
|
+
|
|
991
|
+
## Troubleshooting
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
| Problema | Solução |
|
|
995
|
+
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
996
|
+
| Anti-bot / captcha | Solver Baxia automático por padrão (`CAPTCHA_SOLVER_ENABLED=true`); se falhar, a conta entra em cooldown (`CAPTCHA_ACCOUNT_COOLDOWN_MS`) e a request roda em outra conta |
|
|
997
|
+
| Quota exceeded | Mais contas ou esperar cooldown |
|
|
998
|
+
| `502 Bad Gateway` / `fetch failed` | Normalmente upstream/rede; o proxy faz retry automático |
|
|
999
|
+
| `invalid_input` (anexo inválido) | Retry com chat novo; settings `largeTextAsFile=false` ajudam |
|
|
1000
|
+
| `context_length_exceeded` | O proxy bloqueou o prompt localmente antes de qualquer retry; reduza/resuma o histórico ou ajuste `QWEN_MAX_PROMPT_BYTES` |
|
|
1001
|
+
| HTML/WAF no lugar do stream | O Bridge identifica o desafio e aciona o solver; se persistir, reduza o tamanho/frequência do payload e verifique a sessão |
|
|
1002
|
+
| `Model not found` | Use um id do catálogo de `/v1/models` (ex.: `qwen3.8-max`) |
|
|
1003
|
+
| Vários Chromes abertos / RAM alta | `SESSION_KEEP_ALIVE_ENABLED=false`, idle cleanup on, `PLAYWRIGHT_INIT_BATCH_SIZE=1`, `PLAYWRIGHT_JS_HEAP_MB`, watchdog RSS fecha idle sob pressão |
|
|
1004
|
+
| Watchdog “RAM critical” falso | Baseado em RSS (`memory.rss.usage_percent`); confira `/health` |
|
|
1005
|
+
| Timeout em requests grandes | Aumente `TOTAL_REQUEST_TIMEOUT` / `REASONING_MODEL_TIMEOUT` |
|
|
1006
|
+
| `stream_aborted` em modelo reasoning | Idle timeout: zero bytes por `REASONING_MODEL_TIMEOUT` (180s default) fecha o stream retryável; aumente se necessário |
|
|
1007
|
+
| `canSkipThinking: false` | O catálogo não informa `think_skip`; a variante pública `-fast` continua disponível e usa o payload Fast do Qwen |
|
|
1008
|
+
| Grok CLI `missing field input_tokens_details` | Corrigido: usage sempre inclui `input_tokens_details` e `output_tokens_details` |
|
|
1009
|
+
| Responses `previous_response_id` not found | Store SQLite com TTL 7 dias; verifique se `store: false` não foi enviado |
|
|
1010
|
+
| Playwright não inicia | `npx playwright install chromium` |
|
|
1011
|
+
| Porta em uso | Altere `PORT` no `.env` |
|
|
1012
|
+
| Sessão expirada | `npm run login` ou deixe o refresh automático reautenticar |
|
|
1013
|
+
| API aberta em `0.0.0.0` sem key | Defina `API_KEY` e/ou `HOST=127.0.0.1` |
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
---
|
|
1017
|
+
|
|
1018
|
+
## Créditos e Agradecimentos
|
|
1019
|
+
|
|
1020
|
+
O **QwenProxy** é desenvolvido e mantido por **johngbl**, construído sobre as fundações de código aberto originalmente criadas por **Pedro Farias** sob a licença [ISC](LICENSE).
|
|
1021
|
+
|
|
1022
|
+
---
|
|
1023
|
+
## Disclaimer
|
|
1024
|
+
|
|
1025
|
+
**Software fornecido *as is*, sem qualquer garantia (expressa ou implícita), incluindo as de comerciabilidade, adequação a um fim, funcionamento contínuo, correção de erros ou suporte.**
|
|
1026
|
+
|
|
1027
|
+
- **Sem afiliação:** o QwenProxy não é afiliado, endossado nem patrocinado pela Alibaba/Qwen, OpenAI, Anthropic ou qualquer provedor citado. Marcas pertencem aos seus titulares.
|
|
1028
|
+
- **Uso por sua conta e risco:** destinado a fins educacionais e de estudo técnico. Cabe exclusivamente a você cumprir os Termos de Uso e limites do serviço upstream, usar contas e credenciais próprias e verificar a legalidade do uso na sua jurisdição.
|
|
1029
|
+
- **Responsabilidade integral do usuário:** você é o único responsável por bloqueio, suspensão ou encerramento de contas, desafios anti-bot, perda de dados ou histórico, custos imprevistos, indisponibilidade e por qualquer prompt, saída, arquivo ou resultado de mídia gerado.
|
|
1030
|
+
- **Exclusão total do mantenedor:** nos limites máximos da lei, o autor e os contribuidores **não respondem** por quaisquer danos diretos, indiretos, incidentais, especiais ou consequenciais (perda de lucros, dados, contas, quota ou receita) decorrentes do uso ou da incapacidade de uso deste software, nem por mudanças, quebras ou bloqueios do serviço upstream, que podem ocorrer a qualquer momento e sem aviso prévio.
|
|
1031
|
+
- **Sem prestação de serviço:** projeto voluntário, sem SLA e sem obrigação de atualizar ou corrigir.
|
|
1032
|
+
|
|
1033
|
+
**Ao baixar, compilar ou executar este software você declara ter lido e aceito integralmente este aviso, isentando o mantenedor de qualquer obrigação, reclamação, ação, custo ou despesa (incluindo honorários advocatícios). Se não concordar, não utilize.** Acompanha e não substitui a licença [ISC](LICENSE).
|