Handbooks
Руководства

Studio — веб-интерфейс

Весь тулчейн во вкладке браузера, с живыми логами и откатом в один клик. Только localhost — намеренно.

handbook studio                 # → http://127.0.0.1:4860
handbook studio --port 5000     # or: pnpm studio --port 5000

Те же кодовые пути, что и у CLI, то же разрешение конфигурации, те же артефакты на диске — просто другой способ управления.

Нулевой шаг сборки. UI — это один написанный вручную HTML-файл со встроенным CSS и ванильным JS. Ни бандлера, ни фреймворка, ничего с CDN. Он загружается мгновенно и работает с выдернутым сетевым кабелем.

Что в нём можно делать

ОбластьЧто она делает
РепозиторииЗарегистрировать дерево исходников + рабочий каталог под URL-безопасным именем
ГенерацияПолный набор параметров, живая трансляция логов по SSE, отмена посреди запуска
Просмотр handbookЧитать отрендеренный handbook на месте
Граф влиянияКакие файлы принадлежат этапу, кто вызывает его и кого вызывает он
Просмотр исходниковОткрыть реальный файл за любой карточкой, на процитированной строке
ПланированиеВвести запрос, наблюдать за работой read-only-агента, прочитать план
Применение / откатПробный прогон, применение, список всех резервных копий, откат в один клик
ResyncПрокатить handbook вперёд по живому дереву — не собирая каталог кейса
ИсторияЭволюция по каждому репозиторию: что изменил каждый запуск и когда

Задачи

Генерация, планирование и resync выполняются как фоновые задачи с захваченным логом, отдаваемым по Server-Sent Events.

  • Одна задача на репозиторий одновременно. Артефакты конвейера небезопасны для конкурентных писателей в одном рабочем каталоге; второй запуск отклоняется с внятным сообщением.
  • Отменяемо. У каждой задачи есть AbortController, чей сигнал доходит до LLM-запросов в полёте. Отмена означает отмену, а не «перестань показывать лог».
  • Статусы: runningsucceeded | failed | cancelled. Полный лог сохраняется, так что можно прочитать, что произошло, уже после завершения.

Конфигурация

Studio разрешает свои настройки из тех же уровней, что и любая другая команда, — флаги, окружение, каскад .env, handbook.config.yaml, значения по умолчанию:

handbook studio --model gpt-4o --base-url https://my-proxy/v1 --port 5000
handbook --env prod studio

Задача генерации, запущенная из UI, видит тот же слой конфигурационного файла, что и CLI, поэтому detail, narrateLang, readWorkers и всё остальное работают из YAML.

Модель безопасности

Studio — локальный инструмент. Он не укреплён для внешнего доступа и не притворяется укреплённым.

  • По умолчанию слушает 127.0.0.1.
  • CSRF-защита проверяет заголовок запроса Host, а не сокет. Проходят только loopback-имена хостов.
  • POST требует application/json, что блокирует классическую кросс-доменную атаку через HTML-форму.
  • Имена репозиториев валидируются по ^[A-Za-z0-9][A-Za-z0-9._-]*$ до того, как коснутся файловой системы, а пути нормализуются через realpath.
  • Раздача файлов исходников и handbook заперта в песочнице зарегистрированных корней.

В контейнере

pnpm run docker:studio    # docker compose up --build studio

Контейнер обязан слушать 0.0.0.0, чтобы опубликованный порт вообще был достижим (HANDBOOK_STUDIO_HOST=0.0.0.0 в docker-compose.yml).

Работает только http://localhost:4860

Не IP из LAN и не имя контейнера. Обращение из браузера на хосте по-прежнему отправляет Host: localhost:4860 и проходит; запрос с IP из LAN или именем хоста контейнера отклоняется с 403так задумано. Удалённый доступ — намеренно нереализованная отдельная возможность (ей понадобился бы явный список разрешённых), а не пробел в этой защите.

Состояние

~/.handbook-studio/
  studio.json        the repository registry (schema-validated on read)
  work/<name>/       auto-created work dirs for repos that did not bring their own

--state-dir перемещает его. Всё остальное — артефакты handbook, история эволюции — живёт в рабочем каталоге каждого репозитория, поэтому удаление каталога состояния теряет реестр и ничего по-настоящему важного.

Скриптование поверх него

UI — просто клиент. HTTP API достаточно стабилен, чтобы писать скрипты:

curl -s http://localhost:4860/api/repos | jq

curl -s -X POST http://localhost:4860/api/repos \
  -H 'content-type: application/json' \
  -d '{"name":"api","sourceRoot":"/Users/me/code/api","workDir":"/Users/me/work/api"}'

curl -s -X POST http://localhost:4860/api/repos/api \
  -H 'content-type: application/json' \
  -d '{"action":"analyze"}'

curl -N http://localhost:4860/api/jobs/<job-id>    # SSE log stream

Полная таблица маршрутов — в README пакета.

На этой странице