dzzzr-cli

module
v0.0.0-...-465a0d7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: Apache-2.0

README

dzzzr-cli

Go-библиотека и консольная утилита для движка городской игры «Дозор Классик» (classic.dzzzr.ru). Позволяет играть и администрировать игру без браузера, а также встраивать клиент движка в свой код.

В комплекте: библиотека, CLI, mock-сервер движка для разработки, инструменты для LLM-агентов с MCP-сервером и gomobile-биндинги для мобильного приложения игрока.

Проект повторяет по структуре и функциям encx-cli — такой же клиент для движка Encounter.

Что внутри

Каталог Назначение
dzzzr/ клиент движка: игрок и организатор
cmd/dzzzr/ консольная утилита
cmd/dzzzr-mock/ mock-сервер движка, см. README
agenttools/ каталог инструментов движка для LLM-агентов с политиками доступа
agentmcp/ MCP-сервер над этим каталогом
agentloop/ общее ядро LLM-агента для CLI/web/WASM; протокол и нативные провайдеры PicoClaw
agentfiles/ чтение локальных файлов и сохранение новых JSON-документов агентом
gamesource/, pdfsource/ импорт сценария игры из JSON и PDF
mobile/dzzzrmobile/ gomobile-обёртка для iOS и Android
e2e/ сквозные тесты: собранная утилита против mock-сервера

Установка

go install github.com/skrashevich/dzzzr-cli/cmd/dzzzr@latest
go install github.com/skrashevich/dzzzr-cli/cmd/dzzzr-mock@latest

Библиотека:

go get github.com/skrashevich/dzzzr-cli/dzzzr

Docker:

docker run --rm ghcr.io/skrashevich/dzzzr-cli version
docker run --rm -p 18090:18090 -e DZZZR_MOCK_ADDR=0.0.0.0:18090 ghcr.io/skrashevich/dzzzr-mock

Быстрый старт

По умолчанию используется https://classic.dzzzr.ru/moscow/. Флаг -city меняет город в пути; -base-url или DZZZR_BASE_URL задаёт адрес движка целиком.

# вход: спросит недостающие логин и пароль
dzzzr login -city moscow

# что происходит в игре
dzzzr status

# текст задания и подсказки
dzzzr level
dzzzr hints

# отправить код
dzzzr send-code "(112)D45R92"

Всё это можно попробовать против mock-сервера, ничего не задев на боевом движке:

dzzzr-mock &                       # слушает 0.0.0.0:18090
export DZZZR_BASE_URL=http://127.0.0.1:18090/moscow/
dzzzr login -login demo -password demo -captain demo -pin 1234
dzzzr status
dzzzr send-code 1

Отправка кода завершается кодом выхода 0, если движок код принял, и 2, если отклонил, — удобно в скриптах:

if dzzzr send-code "$code"; then echo принят; else echo мимо; fi

Полный список команд игрока и организатора, флагов и переменных окружения — в docs/cli.md.

dzzzr quickstart

Ещё записи — прохождение уровня, mock-сервер, организаторская часть — на GitHub Pages. Тапки VHS лежат в docs/vhs/, собрать локально: ./docs/vhs/render.sh (нужны vhs, ttyd, ffmpeg).

Авторизация

  1. Аккаунт сайта — логин и пароль. API/login.php меняет их на токен сессии. Этого достаточно, чтобы играть.
  2. Заголовок HTTP Basic — движок требует, чтобы он просто был. Утилита подставляет {город}_{логин} с пустым паролем; логин капитана и PIN (-captain, -pin) нужны только там, где организатор их выдал.
  3. Учётная запись организатора — отдельные логин и пароль от админки, с игровыми никак не связаны.

Утилита хранит всё это в ~/.config/dzzzr/<город>.json с правами 600 ($XDG_CONFIG_HOME/dzzzr, если переменная задана; на Windows — %AppData%\dzzzr, где права файла заменяет наследуемый доступ к профилю). Каталог целиком переопределяется переменной DZZZR_CONFIG_DIR. Файл содержит секреты — не кладите его в общий каталог.

ИИ-агент

Движок можно отдать LLM-агенту как каталог инструментов — по MCP или встроенным циклом прямо из утилиты:

dzzzr mcp                                    # MCP-сервер, только чтение
export DZZZR_LLM_API_KEY=sk-…
dzzzr agent "на каком мы уровне и сколько кодов осталось"
dzzzr chat                                   # полноэкранный диалог в терминале
dzzzr web                                    # тот же агент в браузере, рядом — редактор игры

Политика доступа (readonly / approve / full) задаётся флагом -security. Агент умеет импортировать сценарий игры из PDF и читать публичные веб-страницы инструментом fetch_url — HTML приводится к тексту, кодировка распознаётся, непубличные адреса (localhost, локальная сеть, служебные диапазоны) не читаются. Содержимое страницы — данные, а не инструкции. Подробно — в docs/agent.md.

Настройка модели

Переменные окружения не обязательны: при первом запуске dzzzr web или dzzzr editor открывается мастер настройки. Он проходит два шага: подключает модель и выполняет вход в Дозор игроком или организатором. Способы подключения модели:

  • Polza.ai — вход в аккаунт или API-ключ, выбор модели с поддержкой инструментов, проверка баланса;
  • подписка ChatGPT — вход через браузер; если уже выполнен вход в Codex CLI (~/.codex/auth.json), dzzzr использует его;
  • OpenAI-совместимый провайдер — адрес API, ключ и модель (по умолчанию OpenRouter).

Редактору модель не нужна, поэтому её настройку можно отложить кнопкой «Настроить позже». Позже настройки меняются кнопкой ⚙ рядом с названием модели, а мастер открывается снова кнопкой ◈.

Настройки сохраняются в <каталог настроек>/llm/settings.json, вход ChatGPT — в codex/auth.json рядом. Оба файла доступны только владельцу. Их читают и веб, и dzzzr chat, и dzzzr agent. Выход из аккаунта игрока их не удаляет. Переменные окружения (DZZZR_LLM_PROVIDER, DZZZR_LLM_API_KEY, DZZZR_LLM_BASE_URL, DZZZR_LLM_MODEL и их варианты LLM_*, OPENROUTER_*) важнее сохранённого. Панель показывает, какая переменная перекрывает значение.

Веб-интерфейс

dzzzr web поднимает локальный сервер (по умолчанию 127.0.0.1:8788) с тем же агентом, что и dzzzr chat: переписка нескольких чатов, потоковый ответ, согласование действий, вход на сайт города и файлы, которые агент сохранил за время чата. Наружу ничего не публикуется, слушает только петлю.

Статистика по выгрузке журнала открывается на вкладке «Статистика» рядом с чатом и редактором (#/stats) или отдельной командой (учётные данные движка и LLM не требуются):

dzzzr log-stat                         # выбрать XLSX/JSON/CSV в браузере
dzzzr log-stat /path/to/log-1566.xlsx   # сразу открыть выбранный журнал
dzzzr log-stat /path/to/log-1566.json   # JSON-выгрузка журнала
dzzzr log-stat /path/to/log-1566.xlsx -json > statistics.json

Автор исходного алгоритма расчёта статистики — Sergey <sergey@luberg.me> Luberg. Алгоритм портирован на Go;

Пакет gamestats на Go читает XLSX/XLSM, JSON и CSV/TSV (UTF-8 или Windows-1251), восстанавливает прохождение, таймауты, отказы и зачёты организатора, считает штрафы, бонусы, места и отставания. Браузер отображает таблицу в формате DozoR, детализацию команд и уровней и отправляет изменения параметров на пересчёт в бэкенд. Экспорт XLSX с листами «Статистика» и «Детали» также выполняется в Go. Файл обрабатывается локальным сервером. Просмотр статистики не отправляет журнал в движок или LLM; при обсуждении агент получает запрошенные им результаты и события. Максимальный размер файла — 32 МБ. JSON принимается как объект с номерами строк ({"1":["Время",...],"2":[...]}) или массив строк ([["Время",...],[...]]), со столбцами в том же порядке, что в XLSX. Числовые ключи сортируются по номеру строки; пустые ячейки могут быть null.

Лимиты восстанавливаются из событий, неизвестные параметры можно исправить в «Параметрах подсчёта». Размеры бонусов в журнале отсутствуют. Для игры 1566 сохранён набор параметров из предоставленного HTML-образца, с пояснением предположений в интерфейсе; к другим играм этот набор не применяется. В общем web UI журнал и параметры сохраняются в <каталог настроек>/web/statistics. Ссылка /?stats=<log_id>#/stats восстанавливает расчёт после перезагрузки. В отдельной команде log-stat журнал не сохраняется на диск: после перезагрузки его нужно выбрать заново, параметры восстанавливаются из браузера.

Кнопка «Обсудить с агентом» открывает чат с идентификатором текущего журнала. Агент работает с тем же расчётом: stats_load получает полный журнал из движка по game_id или читает локальный XLSX/JSON/CSV; stats_report возвращает итоги, ячейки и замечания; stats_search ищет исходные события по команде, уровню, игроку, действию, тексту и времени с пагинацией; stats_configure меняет параметры локального расчёта, не затрагивая движок. Поддерживаются и вложения текущего чата. При возвращении во вкладку статистики (или по кнопке «Обновить от агента») UI читает актуальные параметры. Конфликтующие версии не перезаписываются. В CLI/MCP журналы инструментов хранятся в .dzzzr-statistics внутри DZZZR_FILES_ROOT. Замечания об аномалиях — поводы проверить исходные события, не доказательства нарушений. Время трактуется как часы движка, независимо от часового пояса компьютера.

Кнопка «скачать .html для офлайна» сохраняет самостоятельный интерактивный отчёт. В просмотрщиках с отключёнными скриптами доступны заранее рассчитанная таблица, подробности команд, события и параметры уровней. Изменять расчёт там нельзя. В современном браузере без сервера и интернета доступны подробности команд и уровней, замечания по журналу, изменение лимитов, подсказок, бонусов и штрафов, скрытие команд/уровней и экспорт XLSX. HTML содержит полный исходный журнал и выбранные параметры. Повторный экспорт HTML сохраняет новый вариант расчёта; «Сбросить параметры» возвращает исходные настройки алгоритма.

Для офлайн-пересчёта тот же пакет gamestats компилируется в WebAssembly; формулы не дублируются в JavaScript. Обычный web UI продолжает рассчитывать статистику на Go-бэкенде. Встроенный модуль хранится в репозитории, поэтому готовый бинарник не требует установленного Go. При изменении gamestats, зависимостей или версии Go пересоберите его: go generate ./internal/statsoffline. Тест проверяет актуальность исходников и, при наличии Node.js, совпадение результатов WASM и обычного Go.

Рядом с чатом — переключатель «Редактор» (dzzzr editor открывает браузер сразу в нём): ручная заливка игры для автора, которому проще заполнить форму, чем диктовать уровень модели. Тот же сервер, тот же клиент движка, та же организаторская сессия. Формы игры и задания рисуются по той же JSON Schema, которую принимают инструменты агента admin_create_game и admin_create_level, поэтому поле, добавленное агенту, появляется и в форме: тест не даст им разойтись. В боковой панели — игры организатора и задания выбранной игры с добавлением, удалением и перестановкой; в главной области — поля по группам, таблицы основных, бонусных, ложных и спойлерных кодов со вставкой списком, локальная проверка перед записью, выгрузка и заливка сценария целиком и кнопка «Спросить агента», открывающая постоянный чат общего черновика игры.

Лист кодов вставляется не только из буфера: «Из файла» читает .txt, .csv или .tsv — windows-1251 распознаётся сама, — и кладёт прочитанное в то же поле вставки, чтобы автор увидел разбор до записи. Непрочитанное разбор называет вслух, а не подставляет вместо него ноль или пустую сложность. Поля автоматически сохраняются в локальный черновик. Переключение в чат передаёт агенту актуальные несохранённые правки, а при возврате в редактор виден результат его работы. Повторный переход открывает тот же чат. Новые задания и черновик восстанавливаются после перезагрузки страницы. Разные поля объединяются автоматически; при одновременной правке одного поля интерфейс предлагает выбрать версию. В движок поля записываются только кнопкой «Сохранить в игру» для открытой игры или задания. В привязанном чате агент использует draft_*; инструменты изменения движка ему не выдаются.

Задание, подсказки и легенда — то, что движок показывает игрокам как HTML, — правятся с панелью форматирования и переключателем на исходную разметку; картинка кнопкой загружается в файлы самой игры, а относительные ссылки движка разрешаются только для показа, так что на движок уходит ровно то, что в поле.

Организатором входят в самом редакторе: логин и пароль проверяются на админке и сохраняются в тот же файл сессии, где их оставил бы -admin-login. Этот вход — общий: после него в чате становятся доступны инструменты admin_*, потому что набор инструментов собирается заново на каждый ход из того же клиента. Ни один из двух видов не главный. Игровая сессия редактору не нужна — запуск dzzzr -admin-login … -admin-password … web открывает только организаторскую часть и сразу в редакторе. Адрес страницы (#/editor/game/4242/level/901) называет открытую игру и задание, так что перезагрузка и «назад» возвращают туда же, а без адреса страница открывает вид, в котором работали последним. Подробно — в docs/webui.md.

На Windows запуск dzzzr.exe без команды из Проводника открывает браузер с редактором организатора. Из cmd.exe, PowerShell и других терминалов запуск остаётся консольным: без команды выводится справка. Чтобы открыть редактор из терминала явно, используйте dzzzr editor.

Способ запуска определяется по родительскому процессу; для неизвестных запускателей используется число процессов в консоли. Если автоматически запущенный сервер завершился с ошибкой, окно удерживается до нажатия Enter. Любое непустое значение DZZZR_NO_AUTO_WEB выключает автозапуск — в том числе DZZZR_NO_AUTO_WEB=0.

Диалог с агентом Тёмная тема
Переписка, вызовы инструментов, выбор прав агента Тёмная тема
Ход агента Согласование действия
Статус хода, инструменты, потоковый ответ Согласование действия под политикой approve
Файлы сессии Вход на сайт
Файлы, сохранённые агентом за чат Вход на сайт одного города
Редактор задания Редактор, тёмная тема
Задания игры и форма уровня по группам полей Тёмная тема
Вставка кодов списком
Лист кодов вставляется одним блоком

Скриншоты и GIF ниже пересобираются в CI (.github/workflows/media.yml) из docs/shots/ и docs/vhs/ и публикуются на GitHub Pages.

Мобильные биндинги

mobile/dzzzrmobile — gomobile-обёртка: методы возвращают JSON-строки, все целые int64, контекста нет.

./mobile/bind-ios.sh        # → mobile/build/dzzzr.xcframework
./mobile/bind-android.sh    # → mobile/build/dzzzr.aar (нужны ANDROID_HOME и NDK)

Разработка

go build ./...
go vet ./...
go test ./... -count=1
go test -race ./... -count=1
go test -tags e2e ./e2e -count=1     # собранная утилита против mock-сервера
golangci-lint run ./...              # нужен golangci-lint v2

Живые тесты против боевого движка включаются переменными DZZZR_INTEGRATION=1, DZZZR_E2E_CITY, DZZZR_E2E_LOGIN, DZZZR_E2E_PASSWORD, DZZZR_E2E_CAPTAIN, DZZZR_E2E_PIN. Без них они пропускаются.

Демо-медиа для README собираются локально теми же скриптами, что и в CI:

./docs/vhs/render.sh      # GIF CLI → docs/gifs/   (нужны vhs, ttyd, ffmpeg)
./docs/shots/render.sh    # скриншоты web-ui → docs/screenshots/   (нужен Node)

Оба каталога в .gitignore: workflow media.yml пересобирает их при изменениях в docs/vhs/, docs/shots/, cmd/dzzzr/ или cmd/dzzzr-mock/ и публикует на GitHub Pages. Скриншоты снимаются с настоящего dzzzr web поверх dzzzr-mock; переписка в чатах — фикстуры из docs/shots/fixtures/.

Документация

Файл О чём
docs/cli.md все команды CLI, флаги и переменные окружения
docs/library.md использование пакета dzzzr как библиотеки
docs/agent.md инструменты для ИИ-агентов, MCP, dzzzr chat, импорт сценария из PDF
docs/webui.md dzzzr web: редактор игры в браузере, его HTTP-поверхность и общая с агентом сессия
docs/engine-notes.md как на самом деле ведёт себя движок «Дозор Классик»
docs/design.md дизайн проекта
docs/source-helper-parity.md сверка возможностей заливки сценария с DzrSourceHelper
cmd/dzzzr-mock/README.md mock-сервер движка и его -quirks

Лицензия

Apache 2.0, см. LICENSE.

Браузерная версия (WebAssembly)

Открыть приложение на GitHub Pages. Тот же Go-код работает в браузере: статистика XLSX/JSON/CSV, поиск событий, HTML/XLSX-экспорт, извлечение и индексирование PDF, применение PDF mapping, валидация полных сценариев и пакетов уровней.

Раздел «Редактор» использует формы нативного web-ui: игра, задания, HTML-тексты, подсказки, основные/бонусные/ложные коды, спойлеры, секторы и координаты. Можно создавать и копировать игры, добавлять, удалять и переставлять уровни, импортировать и экспортировать полный сценарий с вложениями. JSON-экспорт включает актуальные черновики, сохраняет исходный HTML, source и extensions. Кнопка «Сохранить локально» записывает документ в локальную игру.

Агент работает через общее с CLI и нативным web-ui ядро agentloop: до 200 шагов, повторные попытки, таймаут каждого запроса, отмена, проверка аргументов инструментов, сохранение исходного контекста PDF, события инструментов и отчёт о токенах/времени. «Спросить агента» связывает диалог с открытым черновиком. draft_read, draft_open_level, draft_update, draft_add_level читают и меняют те же документы, что формы; проверка revision защищает правки автора. scenario_create, scenario_import, scenario_list, scenario_read, scenario_export работают с локальными играми. Доступны режимы чтения, локального редактирования и подтверждения каждого изменения. В настройках LLM задаются лимит шагов, таймаут и дополнительные поля OpenAI-совместимого запроса.

LLM подключается напрямую к указанному HTTPS API; провайдер должен разрешать CORS. API key хранится только в памяти вкладки. Модель получает сообщения и результаты выбранных инструментов. Игры, черновики и текст диалогов сохраняются в IndexedDB; рабочее пространство можно скачать и восстановить из JSON. Загруженные файлы, журнал и ключ нужно загрузить заново после перезагрузки. Очистка данных сайта удаляет локальные игры, поэтому экспортируйте сценарии или резервную копию. После первого успешного кэширования редактор и локальные операции доступны офлайн. Для LLM нужен интернет. OCR, вход и публикация в движок, MCP-сервер, shell и доступ к произвольным файлам ОС доступны в нативном CLI/web UI.

Сборка: go run ./cmd/build-browser -out dist/browser. Для локальной проверки раздайте каталог через HTTP, например python3 -m http.server --directory dist/browser. Workflow Demo media публикует приложение в /app/, сохраняя демостраницу и медиа. В артефакт включаются только явно перечисленные ресурсы, без пользовательских журналов, сценариев, настроек или ключей. Формулы статистики на JavaScript не дублируются.

Directories

Path Synopsis
Package agentfiles lets an agent read files the team keeps on this machine: notes from a briefing, a scenario draft, a script that decodes something.
Package agentfiles lets an agent read files the team keeps on this machine: notes from a briefing, a scenario draft, a script that decodes something.
Package agentloop runs the shared native and browser LLM agent.
Package agentloop runs the shared native and browser LLM agent.
Package agentmcp serves the Dozor Classic toolset over the Model Context Protocol, so any MCP client can read the game and, when allowed, play it through the same tools the CLI and the mobile agent use.
Package agentmcp serves the Dozor Classic toolset over the Model Context Protocol, so any MCP client can read the game and, when allowed, play it through the same tools the CLI and the mobile agent use.
Package agentprotocol is the portable LLM contract shared by native and WASM agents.
Package agentprotocol is the portable LLM contract shared by native and WASM agents.
Package agenttools exposes the Dozor Classic engine to an LLM agent as a catalog of typed tools.
Package agenttools exposes the Dozor Classic engine to an LLM agent as a catalog of typed tools.
Package browserapp exposes portable dzzzr capabilities without filesystem or engine credentials.
Package browserapp exposes portable dzzzr capabilities without filesystem or engine credentials.
cmd
build-browser command
build-browser assembles only explicitly listed public assets; no local logs, credentials or scenario files are copied into the Pages artifact.
build-browser assembles only explicitly listed public assets; no local logs, credentials or scenario files are copied into the Pages artifact.
dzzzr command
Command dzzzr is a command-line client for the Dozor Classic city-game engine (classic.dzzzr.ru).
Command dzzzr is a command-line client for the Dozor Classic city-game engine (classic.dzzzr.ru).
dzzzr-mock command
Command dzzzr-mock serves a small imitation of the Dozor Classic engine.
Command dzzzr-mock serves a small imitation of the Dozor Classic engine.
dzzzr-wasm command
Package dzzzr is an HTTP client for the Dozor Classic city-quest game engine (classic.dzzzr.ru).
Package dzzzr is an HTTP client for the Dozor Classic city-quest game engine (classic.dzzzr.ru).
Package editorform shares editor layouts and validation between web and WASM.
Package editorform shares editor layouts and validation between web and WASM.
Package gamesource validates and applies ordered JSON batches of game levels.
Package gamesource validates and applies ordered JSON batches of game levels.
Statistics algorithm ported from dozor_stats.html.
Statistics algorithm ported from dozor_stats.html.
internal
statsoffline
Package statsoffline embeds the Go calculator for self-contained HTML reports.
Package statsoffline embeds the Go calculator for self-contained HTML reports.
statsoffline/generate command
Generates the embedded calculator using the current Go toolchain.
Generates the embedded calculator using the current Go toolchain.
statsoffline/wasm command
The offline report runs the same Go package as the HTTP backend.
The offline report runs the same Go package as the HTTP backend.
mobile
dzzzrmobile
Package dzzzrmobile provides gomobile-compatible bindings for the dzzzr client, so an iOS or Android app can play Dozor Classic through the same code the CLI uses.
Package dzzzrmobile provides gomobile-compatible bindings for the dzzzr client, so an iOS or Android app can play Dozor Classic through the same code the CLI uses.
Package pdfsource extracts local PDF text, hyperlinks and embedded images in pure Go.
Package pdfsource extracts local PDF text, hyperlinks and embedded images in pure Go.
Package statstore keeps local game logs and versioned calculation settings.
Package statstore keeps local game logs and versioned calculation settings.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL