Files
sechenov/CLAUDE.md

122 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: синяя дата `<span>` + время `<em>`
- `#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`, `<title>Экзаменационный билет (ЦТ)</title>`, 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<token>/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.