Files
sechenov/CLAUDE.md

7.6 KiB
Raw Permalink Blame History

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

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

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.ru94.141.253.12
  • Deploy:
    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.