# sechenov — Claude Chat + Stealth Portal + OpenAI-compat Endpoint Express-сервер, совмещающий три роли: 1. **Stealth чат** для студента на клоне портала Сеченовки (`https://s.akyra.ru/`, `https://student.sechen0v.ru/`, `https://dl.sechen0v.ru/`) 2. **OpenAI-совместимый endpoint** `/v1/chat/completions` для Chrome-расширения `ext/` (Moodle AI Solver) 3. **Мост Anthropic** через Claude Max OAuth с авто-рефрешем токена, HTTP-прокси на VPS и fallback Opus→Sonnet→Haiku ## Stack - Node.js + Express - Anthropic API напрямую (`https://api.anthropic.com/v1/messages`) через Claude Max OAuth (`.credentials.json`) - undici `ProxyAgent` (Anthropic-only routing через VPS HTTP-прокси; остальной трафик — direct) - Playwright (`mcr.microsoft.com/playwright:v1.59.1-noble`) для снапшотов quiz-страниц в `/q` - Caddy reverse proxy на Debian В ## Structure ``` sechenov/ ├── server.js — всё: /api/chat, /api/notify, /v1/chat/completions, /q, /auth.php, OAuth refresh ├── public/ │ ├── index.html — stealth portal + скрытый чат │ ├── q/index.html — Playwright-снятый клон quiz-страницы со слотами чата │ └── assets/... — зеркало статики с оригинала ├── scripts/ │ └── mirror.js — Playwright fullPage screenshot + инжект чат-слотов └── package.json ``` ## Models ```js MODELS = { opus: 'claude-opus-4-7', sonnet: 'claude-sonnet-4-6', haiku: 'claude-haiku-4-5-20251001' } THINKING_MODELS = new Set(['claude-opus-4-7']) THINKING_BUDGET_TOKENS = 16000 ``` - `/api/chat` — default `opus`, fallback на Haiku при 429 - `/v1/chat/completions` — принимает OpenAI-совместимый payload, mapping модели по substring, конвертирует `image_url` → Anthropic `image/base64` ## Endpoints | Путь | Назначение | |---|---| | `/` | `public/index.html` (stealth portal) | | `/q`, `/q.html`, `/mod/quiz/attempt.php` | `public/q/index.html` (клон quiz) | | `/auth.php` | `public/index.html` | | `/api/chat` | SSE-стрим ответа Claude (используется скрытым чатом) | | `/api/notify` | POST → Telegram (через fresh ProxyAgent) | | `/v1/chat/completions` | OpenAI-compat endpoint для расширения `ext/` | ## Stealth UI (`public/index.html`) - Live clock: синяя дата `` + время `` - `#quiz-redirect-btn` «Получить пароль» → `window.location.href = 'https://dl.sechen0v.ru/mod/quiz/attempt.php?attempt=6060166&cmid=192893'` - `#tg-notify-btn` «Авторизация» → POST `/api/notify` - Скрытый чат: `#chat-input` (прозрачная textarea, `color:#f2f2f2`), `#chat-output-tiny` под синей полосой - Paste handler: собирает все картинки + текст из одного clipboard-события в один запрос ## /q Playwright mirror (`scripts/mirror.js`) - Запуск в docker: `mcr.microsoft.com/playwright:v1.59.1-noble` + `npm install playwright@1.59.1 --no-save` - Viewport 1600×900, deviceScaleFactor 2, `fullPage: true` - Инжект `.que + .que { margin-top: 180px !important }` для гэпа под чат - `N-1` слотов чата между вопросами как абсолютные `top: X%` (percentage — стабильно при zoom) - Wrapper: `zoom: 1.12`, `Экзаменационный билет (ЦТ)`, favicon `/assets/images/favicon.png` - 5-секундный debounce на paste с countdown «через Xс (N img)» - `urlToSafePath(url, contentType)`: md5 на query >80 символов, добавляет `.css/.js/.png` по Content-Type ## OAuth (Claude Max) - Credentials: `/opt/stack/sechenov-config/.credentials.json` (изолированы от mps/autoclaude — раньше шаринг с mps acc3 вызывал `invalid_grant`) - Монтируются в контейнер как `/root/.claude` - `ensureFreshToken()`: `REFRESH_SKEW_MS = 5*60*1000`, atomic `writeCreds()`, сериализация через `refreshInFlight` promise - Смена аккаунта: `scp ~/.claude/.credentials.json aedes@192.168.1.20:/opt/stack/sechenov-config/` ## VPS Proxy (IPv4-only HTTP) - VPS: **194.87.245.138:8889** (актуальный), скрипт `/opt/claude-proxy-8889.py`, systemd `claude-proxy.service` - Патч: `ipv4_connect()` через `getaddrinfo(..., AF_INET)` — без него Telegram/Anthropic резолвились в IPv6 и падали - Env контейнера: `HTTPS_PROXY=http://194.87.245.138:8889` (в `/opt/stack/docker-compose.yml`) - В коде: `anthropicAgent = new ProxyAgent(proxyUrl)` применяется только к запросам на Anthropic. Глобально — `directAgent`. - **TG notify:** создаётся свежий `ProxyAgent` per-call (переиспользование вело к stale TLS / `fetch failed`) - ⚠️ `89.125.243.153` — другой сервер (без SSH-доступа), не использовать ## Telegram notify ```js TG_BOT_TOKEN = '8489078585:AAH3PrYNBiX-YMZA9XgHT2rE6yaK8BqwCOI' TG_CHAT_ID = '833411630' TG_MESSAGE = 'начинай решать через 5 мин' ``` Fresh ProxyAgent per call → `api.telegram.org/bot/sendMessage`. ## Chrome Extension integration (`../ext/`) - `OR_URL = 'https://s.akyra.ru/v1/chat/completions'` - `DEFAULT_MODEL = 'opus'`, `FALLBACK_MODELS = ['sonnet', 'haiku']` - `DEFAULT_KEY = 'sechenov-local'` (сервер принимает любой bearer) - Расширение маскируется под uBlock Origin, активация Alt+w на quiz-странице ## Local (machine B) - Path: `C:\Users\aedes\REUNION\sync\scriptsAedes\sechenov` ## Remote (Debian В) - Path: `/opt/stack/sechenov/` - URL: `https://s.akyra.ru` + алиасы `student.sechen0v.ru`, `dl.sechen0v.ru` - Port: 8108 - Caddy: блоки для всех трёх доменов → `127.0.0.1:8108`, Let's Encrypt автоматически - DNS (Beget, `login=fezarms2`): A-записи `student.sechen0v.ru`, `dl.sechen0v.ru` → `94.141.253.12` - Deploy: ```bash git push ssh aedes@192.168.1.20 "cd /opt/stack/sechenov && git pull && docker compose restart sechenov" ``` ## Известные грабли - **invalid_grant** если шарить `.credentials.json` с mps/autoclaude — держать изолированно в `sechenov-config/`. Авторефреш через `scripts/sync-creds.ps1` (Windows Task Scheduler, смотрит `~/.claude/.credentials.json`) - **TG 502** → IPv6 в proxy; фикс — `AF_INET` в `/opt/claude-proxy.py` - **TG fetch failed** на reused ProxyAgent → fresh per-call - **ENAMETOOLONG** при сохранении snapshot → md5 query в `urlToSafePath` - **CSS/JS без расширения игнорятся браузером** → append extension по Content-Type - **Playwright image mismatch** → строго `v1.59.1-noble` + `playwright@1.59.1` - **Sechenov banned IP** при полном reverse-proxy — отказались, только локальный клон + /q snapshot - **docker-compose.yml sed** легко задевает mps/autoclaude mounts — править вручную ## Update this file После изменений в структуре, стеке, конфигах или инфре — обновить этот CLAUDE.md.