meta(tasks): migrate .tasks to v2 (global numbering 87 tasks)

This commit is contained in:
2026-08-23 10:18:28 +03:00
parent 83986e0589
commit 3f4d7474b3
28 changed files with 176 additions and 89 deletions

View File

@@ -0,0 +1,68 @@
# llm-web-proxy-qwen-web
Подзадача [llm-web-proxy] (п.4). Родитель: `.tasks/llm-web-proxy.md`.
## Goal
Добавить комбо `qwen-web` в llm-web-proxy (lwp): web-чат chat.qwen.ai как провайдер
(executor + ключ + конфиг + тесты + live-smoke). Модель — Qwen, эмуляция тулов
(`emulated`, как у deepseek-web). Отдельное комбо, НЕ смешивать с deepseek-комбо.
## Почему (контекст)
- В спеке llm-web-proxy qwen-web заявлен как будущее комбо («позже: qwen-web, gemini-web, kimi-web, grok-web — отдельные комбо, свои executors»).
- Оригинальный блокер: **DNS EAI_AGAIN chat.qwen.ai** — сетевой глюк, не ключ
(.wiki/concepts/omniroute-combo-deepseek-web-tuning.md:43).
- **Проверено 2026-08-22: DNS резолвится** (nslookup chat.qwen.ai → 47.91.78.155 / 47.254.175.31, aliases) — блокер, похоже, снят.
- Ключ есть: wiki `omniroute-combo-deepseek-web-tuning.md:58` — «qwen-web (✅ ключ есть, DNS-глюк)». userToken chat.qwen.ai — в кредах omniroute (не в pass; источник — конфиг/креды omniroute, паттерн deepseek-аккаунтов megan/yulia/vitya).
- Эмуляция тулов у qwen-web: `"emulated"` (webTools.ts парсер зоопарка `<tool>`-форматов — уже в lwp, общий для deepseek-web и qwen-web).
- В lwp сейчас ТОЛЬКО deepseek: 4 комбо (flash/pro ± search), 3 аккаунта. qwen-executor'а в пакете нет (проверено 2026-08-22).
## Что брать из omniroute
- `open-sse/executors/qwen-web.ts` — из docker-образа `diegosouzapw/omniroute:latest` (`/app/`), паттерн вытаскивания deepseek-web.ts (уже отработан в llm-web-proxy).
- Парсеры тулов общие — `webTools.ts` уже есть, переиспользовать.
## Находка 2026-08-22 (вечер): прямой fetch мёртв — нужен браузерный путь
Подтверждено внешним источником (ForgetMeAI/FreeQwenApi, 212⭐): **Node-fetch в принципе не проходит baxia-WAF** — капча привязана к device-фингерпринту (bx-umidtoken минтится JS из sg-wum.alibaba.com в контексте страницы), не к cookie. Рабочий путь — **fetch внутри `page.evaluate`** на живой странице chat.qwen.ai (настоящий umidtoken + canvas-фингерпринт + same-origin).
Референс (закоммичен в lwp): `docs/reference/forgetmeai/` (фингерпринт-сниппет, browser-fetch паттерн, cloakbrowser-заметки). Дизайн решения: `.wiki/concepts/qwen-waf-browser-path.md` (варианты A — браузер-фасад / B — полный браузерный executor).
## Что писать в lwp
1. executor `qwen-web` (по паттерну deepseek-web, но НЕ копипастить логику тул-эмуляции — общий модуль).
2. Ключ userToken chat.qwen.ai → в lwp key-store (паттерн deepseek-аккаунтов).
3. Комбо `qwen-web` в конфиг (аккаунт, модель, timeout).
4. Тесты: TDD — round-robin, failover, `<tool>`-парсинг (зоопарк), smoke против live.
5. Live-smoke: `lwp test qwen-web` / curl `/v1/chat/completions` model=qwen-web.
## Ожидаемый результат
- `lwp combo list` показывает qwen-web.
- `lwp test qwen-web` — 200, стриминг, tool-calls работают.
- Лог lwp: req/upstream/session/stream события для qwen (raw-reply виден).
## Open questions
- [x] 2026-08-22: Актуальный userToken chat.qwen.ai жив? (проверено: executor детектит капчу, не 401 по токену — токен жив, блокирует WAF)
- [ ] Какая модель по умолчанию у qwen-web (qwen3-max? qwen3-flash?) — в lwp стоит `qwen3.7-max` (из executor'а omniroute, alias-таблица в qwen-web-executor.ts)
- [ ] Браузерный путь: какой движок (puppeteer-extra vs playwright), persistent-профиль, ручной проход капчи один раз
## Completed steps
- [x] 2026-08-22: DNS-блокер перепроверен — chat.qwen.ai резолвится (EAI_AGAIN ушёл).
- [x] 2026-08-22: подтверждено, что qwen-executor'а в lwp ещё нет (только deepseek). *(позднее: executor добавлен коммитом 6ef57bc — v2 API, cookie jar + детект капчи)*
- [x] 2026-08-22: живой прогон — лог `waf=captcha-envelope` на completion (детект работает, запрос блокируется WAF).
- [x] 2026-08-22: внешний источник найден (FreeQwenApi): браузерный путь через baxia-WAF, референс в `docs/reference/forgetmeai/`, дизайн в `.wiki/concepts/qwen-waf-browser-path.md`.
- [x] 2026-08-22: **браузерный путь РЕАЛИЗОВАН (lwp 0.1.9, commit 43840ce)** — вариант A (браузер-фасад): `src/lib/qwen-browser.js` (lazy puppeteer-core + системный Chrome, persistent-профиль `<dataDir>/profiles/qwen-browser`, фингерпринт-спуфинг, cleanProfileLocks) + executor `runFlow(fetchImpl)` с blocked-маркером (WAF html / captcha-эневелоп → retry всей цепочки через браузер) + `force`-режим; конфиг `browser` в секрете аккаунта; CLI `lwp account add ... --browser [fallback|force]`; build.mjs external puppeteer-core.
- [x] 2026-08-22: тесты — `qwen-browser.test.ts` (6, fake launch), `qwen-browser-fallback.test.ts` (6, injected browserFactory), `browser-config.test.ts` (3); всего 25/25, регрессов нет.
- [x] 2026-08-22: **LIVE GREEN** — qwen-web HTTP 200 (headless=true): `2+2` → «4», лог `[QWEN-BROWSER] event=browser-fetch status=200` на chats/new + completion. Капча руками не понадобилась.
- [x] 2026-08-22: задеплоено в live-сервис (restart, qwen-main `browser: {enabled:true, force:true, headless:true}`), smoke-ключ ревокнут.
- [ ] (опц.) live-smoke стриминга и tool-calls qwen через браузерный путь.
## Notes
- Родитель llm-web-proxy — 🔴 active (claimed 2026-08-22, vitya). Эта подзадача ⚪ ready, берётся в работу после/параллельно с п.2 (mac/linux autostart).
- «Настоящий» web-чат Qwen без подписки — бесплатный, тул-эмуляция emulated (wiki:58).
- **2026-08-22 вечер: подзадача разблокирована.** Диагноз подтверждён (baxia-WAF привязана к device-фингерпринту, Node-fetch мёртв), решение реализовано: fetch внутри реальной страницы (`page.evaluate`), фингерпринт-спуфинг, persistent-профиль. Live 200. Детали: lwp `.wiki/concepts/qwen-waf-browser-path.md` (статус → implemented).