feat: xterm.js + node-pty architecture for Claude CLI web terminal

Complete rewrite from tmux + JSONL parsing to a clean PTY-based approach.
The proxy spawns Claude CLI in a pseudo-terminal via node-pty and relays
terminal I/O as binary WebSocket frames to the simple-chat backend,
which forwards them to the browser where xterm.js renders a full terminal.

- node-pty PTY manager with 50KB replay buffer per session
- Binary frame protocol with chatId multiplexing
- WebSocket client with auto-reconnection and heartbeat
- Directory listing and session listing for the web UI
- README with setup instructions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 00:24:29 +03:00
commit 4a91896732
17 changed files with 6146 additions and 0 deletions

117
REQUIREMENTS.md Normal file
View File

@ -0,0 +1,117 @@
# Claude CLI Proxy — Требования
## Функциональные требования
### FR-1: WebSocket-соединение с бэкендом
- Подключение к бэкенду simple-chat через WebSocket по настраиваемому URL
- Аутентификация с помощью `PROXY_TOKEN`
- Отправка идентификации устройства при подключении
- Поддержание постоянного соединения с автоматическим переподключением
### FR-2: Управление tmux-сессиями
- Управление одной tmux-сессией (настраиваемое имя, по умолчанию `claude-proxy`)
- Создание новых tmux-окон по запросу (одно на чат)
- Запуск `claude` CLI в новых окнах или `claude --resume <sessionId>` для существующих сессий
- Закрытие окон при завершении чатов
- Отправка нажатий клавиш в окна (сообщения пользователя, клавиши)
- Захват содержимого панели для скриншотов
### FR-3: Мониторинг JSONL-транскриптов
- Отслеживание JSONL-файлов транскриптов Claude CLI на предмет нового содержимого
- Инкрементальное чтение с отслеживанием смещения в байтах (без полного перечитывания)
- Парсинг JSONL-записей: сообщения пользователя, сообщения ассистента, вызовы инструментов, результаты инструментов
- Фильтрация системных/внутренних сообщений (события инициализации, конфигурации)
- Определение правильного пути к файлу через карту сессий и закодированную рабочую директорию
- Интервал опроса: 2 секунды
### FR-4: Мониторинг строки состояния терминала
- Захват последних 3 строк каждой активной tmux-панели
- Парсинг строки состояния для извлечения:
- Названия проекта
- Названия модели и размера контекстного окна
- Процента использования контекста
- Процентов использования сессии и недели
- Режима разрешений
- Обнаружение интерактивных состояний UI (запросы разрешений, запросы ввода)
- Интервал опроса: 1 секунда
### FR-5: Маппинг сессий через хук
- Установка хука SessionStart в настройках Claude CLI (`~/.claude/settings.json`)
- Хук захватывает sessionId и текущий tmux windowId при запуске Claude CLI
- Сохранение маппинга в `~/.claude-proxy/session_map.json`
- Чтение карты сессий для нахождения JSONL-файлов активных окон
### FR-6: Выполнение команд
- Выполнение произвольных команд в tmux-панелях по запросу бэкенда
- Захват вывода команды и возврат на бэкенд (`send_command_capture`)
### FR-7: Список директорий и сессий
- Получение списка доступных рабочих директорий по запросу
- Получение списка возобновляемых сессий Claude CLI по запросу
### FR-8: Скачивание файлов
- Скачивание файлов по URL из MinIO, на которые есть ссылки в сообщениях
- Обеспечение доступа к файлам через веб-интерфейс
### FR-9: Синхронизация состояния
- При переподключении отправка полного состояния всех активных окон на бэкенд (`sync_state`)
- Отчёт о создании окон (`window_ready`) и их закрытии (`window_closed`)
## Нефункциональные требования
### NFR-1: Надёжность
- Переподключение к бэкенду с экспоненциальной задержкой при разрыве соединения
- Возобновление мониторинга после переподключения без потери состояния
- Корректная обработка падений tmux-процессов
- Обработка отсутствующих или повреждённых JSONL-файлов без аварийного завершения
### NFR-2: Производительность
- Опрос терминала с интервалом 1 секунда не должен вызывать заметную нагрузку на CPU
- Опрос JSONL с интервалом 2 секунды использует отслеживание смещения в байтах, чтобы не перечитывать файлы целиком
- Кеш mtime полностью пропускает неизменённые файлы
- Эффективные tmux-операции через прямой `execFile` (без накладных расходов на shell)
### NFR-3: Переносимость
- Работает на macOS (основная машина разработчика)
- Требования: Node.js, tmux, Claude CLI (`claude` в PATH)
- Без Docker, без пайплайна деплоя
### NFR-4: Безопасность
- Аутентификация по токену для WebSocket-соединения
- Отсутствие слушающих портов (только исходящие соединения)
- Карта сессий хранится локально без удалённого доступа
### NFR-5: Поддерживаемость
- TypeScript со строгой типизацией
- Общие конфиги из @vigdorov/dev-configs (ESLint, Prettier, TSConfig)
- Модульная структура: connection, tmux, monitor, hook, files
## Обработка граничных случаев
| Сценарий | Поведение |
|----------|-----------|
| Бэкенд недоступен при запуске | Повтор с экспоненциальной задержкой |
| Бэкенд отключается во время сессии | Переподключение и синхронизация состояния |
| JSONL-файл ещё не создан | Пропуск мониторинга, повтор при следующем опросе |
| JSONL-файл удалён или перемещён | Сброс смещения в байтах, повторное обнаружение |
| tmux-сессия не запущена | Автоматическое создание сессии |
| Процесс tmux-окна завершился | Обнаружение через захват, отчёт `window_closed` |
| Карта сессий отсутствует | Создание пустого файла карты |
| Хук не установлен | Автоустановка при запуске прокси |
| Конфликт нескольких прокси | Один прокси на tmux-сессию по дизайну |
| Большие JSONL-файлы | Инкрементальное чтение через смещение в байтах, без полного перечитывания |
| Claude CLI отсутствует в PATH | Ошибка при создании окна, отчёт на бэкенд |