README
¶
encx-cli
encx-cli — это Go-клиент и CLI для API движка городских квестов Encounter (en.cx).
Если короче: здесь есть и библиотека для встраивания в свой код, и консольная утилита, которой можно быстро залогиниться, посмотреть игру, прочитать задания и отправить код без браузера.
- Модуль:
github.com/skrashevich/encx-cli - CLI:
cmd/encli - Пакет-библиотека:
encx - Тестовый домен для примеров и интеграционных тестов:
tech.en.cx
Скриншоты
Общий вид: боковая панель с историей чатов, выбор домена и игры, авторизация, поле ввода и режим агента (только чтение / с согласованием / полный доступ). В левом верхнем углу — текущая LLM-модель.

Чат с агентом: запрос на естественном языке и пошаговые вызовы инструментов (admin API, чтение уровней и т.д.).

Тёмная тема (переключатель ◐ в верхней панели).

Что внутри
Проект пригодится в двух сценариях:
- хотите написать свой тулинг поверх Encounter API — берите пакет
encx; - хотите просто работать из терминала — ставьте
encli.
Инструменты движка для ИИ-агентов
Пакет agenttools превращает движок Encounter в каталог инструментов для
LLM-агента, а agentmcp отдаёт этот каталог наружу по MCP:
encli mcp -domain tech.en.cx # только чтение (по умолчанию)
encli mcp -domain tech.en.cx -security approve # мутации подтверждаются клиентом
Тот же каталог встроен в Encx.xcframework как агент PicoClaw
(MIT) — см. EncClient.NewAgentSession в mobile/encxmobile. Каталог, политики доступа
(readonly / approve / full) и конфигурация внешнего PicoClaw описаны в
docs/agent-tools.md.
encli --llm и encli -web также работают на runtime и HTTP-провайдере PicoClaw.
Их расширенный CLI-каталог (админские команды, локальные файлы и Wikipedia)
подключён к tools.ToolRegistry через адаптер совместимости.
Установка
Готовые бинарники
Подписанные и нотаризованные бинарники для macOS, Linux и Windows доступны на странице Releases. macOS-бинарники подписаны сертификатом Developer ID и прошли нотаризацию Apple — Gatekeeper не покажет предупреждение о недоверенном разработчике.
CLI
go install github.com/skrashevich/encx-cli/cmd/encli@latest
go install github.com/skrashevich/encx-cli/cmd/encx-mock@latest
Mock-сервер
Для локальной разработки и ручной проверки CLI в репозитории есть encx-mock — небольшой HTTP-сервер, который имитирует домен Encounter без похода в реальный tech.en.cx.


Скрипты VHS: docs/vhs/. GIF генерируются в CI и публикуются на GitHub Pages.
# запустить mock-сервер на 0.0.0.0:18080
encx-mock
# или публичный demo (HTTPS, без локального сервера)
encli login -domain encounter.exe.xyz -login demo -password demo
encli game-list -domain encounter.exe.xyz
# или через Docker
docker run --rm -p 18080:18080 -e ENCX_MOCK_ADDR=0.0.0.0:18080 ghcr.io/skrashevich/encx-mock
# подключиться к нему через encli
encli login -domain 127.0.0.1:18080 -http -login demo -password demo
encli game-list -domain 127.0.0.1:18080 -http
encli status -domain 127.0.0.1:18080 -http -game-id 424242
encli send-code -domain 127.0.0.1:18080 -http -game-id 424242 "CODE-1"
Особенности mock-сервера:
- слушает адрес из
ENCX_MOCK_ADDRили0.0.0.0:18080по умолчанию; - принимает любой логин/пароль, кроме
fail:fail; - поднимает тестовую игру
424242с тремя уровнями и кодами секторов1–12; - умеет загружать экспорт
Game scenario.htmlчерез-scenarioилиENCX_MOCK_SCENARIO; - умеет через
ENCX_MOCK_HARпотоково извлекать из всего HAR только очищенные формы и варианты протокольных ответов; raw HAR, cookies, ответы, идентификаторы и игровой контент в репозиторий не добавляются; - код
PZDCвключает минутную "потерю сети" для текущей пары логин/пароль, чтобы проверять retry/timeout-логику клиента.
Подробное описание режимов, кодов и ограничений: cmd/encx-mock/README.md.
Docker
docker run --rm ghcr.io/skrashevich/encx-cli -v
docker run --rm ghcr.io/skrashevich/encx-cli games -domain tech.en.cx
docker run --rm -p 18080:18080 -e ENCX_MOCK_ADDR=0.0.0.0:18080 ghcr.io/skrashevich/encx-mock
# LLM-режим: передайте ключ и модель через -e
docker run --rm \
-e ENCX_LOGIN=user -e ENCX_PASSWORD=secret -e ENCX_GAME_ID=12345 \
-e LLM_API_KEY=sk-or-v1-... \
-e LLM_MODEL=anthropic/claude-sonnet-4 \
ghcr.io/skrashevich/encx-cli -game-id 12345 --llm "покажи уровни"
Библиотека
go get github.com/skrashevich/encx-cli/encx
Использование библиотеки
Ниже минимальный пример: логинимся, смотрим список игр, читаем состояние и пробуем отправить код.
package main
import (
"context"
"fmt"
"log"
"github.com/skrashevich/encx-cli/encx"
)
func main() {
client := encx.New("tech.en.cx", encx.WithInsecureTLS())
ctx := context.Background()
resp, err := client.Login(ctx, "user", "password")
if err != nil {
log.Fatal(err)
}
if resp.Error != 0 {
log.Fatalf("Login error %d: %s", resp.Error, encx.LoginErrorText(resp.Error))
}
// Список игр
list, _ := client.GetGameList(ctx)
for _, g := range list.ActiveGames {
fmt.Printf("%d: %s\n", g.GameID, g.Title)
}
// Состояние игры
model, _ := client.GetGameModel(ctx, 12345)
if model.Level != nil {
fmt.Printf("Уровень %d: %s\n", model.Level.Number, model.Level.Name)
}
// Отправка кода
result, _ := client.SendCode(ctx, 12345, model.Level.LevelId, model.Level.Number, "КОД123")
if result.EngineAction != nil && result.EngineAction.LevelAction != nil &&
result.EngineAction.LevelAction.IsCorrectAnswer != nil &&
*result.EngineAction.LevelAction.IsCorrectAnswer {
fmt.Println("Верный код!")
}
// Список игр с пагинацией
page2, _ := client.GetGameList(ctx, 2)
for _, g := range page2.ComingGames {
fmt.Printf("%d: %s (levels: %d)\n", g.GameID, g.Title, g.LevelNumber)
}
// Статистика игры
stats, _ := client.GetGameStatistics(ctx, 12345)
if stats.Game != nil {
fmt.Printf("Игра: %s, Уровней: %d\n", stats.Game.Title, len(stats.Levels))
for _, l := range stats.Levels {
fmt.Printf(" Уровень %d: %s\n", l.LevelNumber, l.LevelName)
}
}
}
Опции клиента
Можно подкрутить поведение клиента через опции:
| Опция | Описание |
|---|---|
WithInsecureTLS() |
Пропустить проверку TLS-сертификата |
WithHTTP() |
Использовать HTTP вместо HTTPS |
WithTimeout(d) |
Установить таймаут HTTP-клиента |
WithUserAgent(ua) |
Установить User-Agent |
WithLang(lang) |
Язык запросов (по умолчанию: ru) |
WithEngine(mode) |
Движок Encounter: EngineAuto (по умолчанию), EngineLegacy, EngineNew |
WithAPIBaseURL(url) |
Хост нового движка (по умолчанию выводится из домена: tech.en.cx → api.en.cx) |
Старый и новый движок
Encounter переезжает с ASP.NET на новый REST-бэкенд. encx реализует оба и
переключается между ними прозрачно: сигнатуры методов и возвращаемые типы
одинаковы, меняется только то, кто отвечает на запрос.
По умолчанию движок определяется автоматически: клиент один раз спрашивает
GET {api}/sites/domain/{domain} — новый бэкенд отвечает описанием сайта для
доменов, которые уже переехали, и 404 domain_unregistered для остальных.
Вопрос именно про домен, а не про хост: один API-хост обслуживает всю зону и
отвечает на любой запрос, поэтому проверка доступности хоста объявила бы
переехавшими вообще все сайты.
c := encx.New("tech.en.cx") // auto: определит сам
fmt.Println(c.Engine()) // legacy | new
c := encx.New("demo.en.cx", encx.WithEngine(encx.EngineNew)) // принудительно
c := encx.New("tech.en.cx", encx.WithEngine(encx.EngineLegacy))
То же самое даёт переменная окружения ENCX_ENGINE=legacy|new|auto и флаг
encli -engine. Хост нового движка выводится из домена (demo.en.cx →
api.en.cx); для локального mock-сервера или своей инсталляции его задают
через encx.WithAPIBaseURL(...), флаг -api-base-url или ENCX_API_BASE_URL.
Выбор можно не повторять при каждом запуске: мастер первого запуска и Web UI
сохраняют его в ~/.config/encli/engine/settings.json (путь переопределяется
через ENCLI_ENGINE_SETTINGS_FILE). Файл — самый младший источник: флаг и
переменная окружения по-прежнему старше него. Испорченный файл не роняет
команды, а игнорируется с сообщением в -debug: умолчание auto всё равно
опрашивает хост и исправляет себя само.
Хост нового движка выводится только для зон Encounter (en.cx, encounter.cx,
encounter.ru, en-world.org, quest.ua), и сайт в ответе обязан сам назвать
запрошенный домен — иначе на неподтверждённый хост не уходят ни заголовок
сайта, ни учётные данные. Для домена вне этих зон хост не выводится: в auto
клиент останется на старом движке, а при явном new вернёт ошибку с просьбой
указать -api-base-url.
Спецификация нового API, протокол WebSocket движка и матрица паритета методов — в docs/newengine.
iOS
Нативное приложение вынесено в отдельный репозиторий enkapp. Здесь остаётся только gomobile-библиотека и сборка Encx.xcframework.
Пакет mobile/encxmobile — gomobile-обёртка над encx. API возвращает JSON-строки, которые декодируются в Swift через JSONDecoder.
Сборка Encx.xcframework
# из корня репозитория
./mobile/bind-ios.sh
# результат: mobile/build/Encx.xcframework
Скрипт устанавливает gomobile, прогоняет тесты и собирает фреймворк для устройства и симулятора.
Подключение в Xcode
См. также enkapp — готовый Xcode-проект с этим фреймворком.
- Перетащите
Encx.xcframeworkв проект (Embed & Sign). - Добавьте в Info.plist разрешение на сеть (если ещё нет):
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Для production лучше настроить ATS exceptions только для нужных доменов (
*.en.cx).
Пример (Swift)
import Encx
guard let client = EncxmobileNewClient("tech.en.cx", true) else { return }
// Авторизация
var loginErr: NSError?
let loginJSON = client.login("user", password: "secret", error: &loginErr)
if let err = loginErr { print(err); return }
struct LoginResponse: Decodable {
let Error: Int
let Message: String
}
let login = try JSONDecoder().decode(LoginResponse.self, from: loginJSON.data(using: .utf8)!)
guard login.Error == 0 else {
print(EncxmobileLoginErrorText(login.Error))
return
}
// Сохранение сессии (Keychain / UserDefaults)
if let cookies = client.exportCookies(&loginErr) {
UserDefaults.standard.set(cookies, forKey: "encx_cookies")
}
// Состояние игры
let gameJSON = client.getGameModel(12345, error: &loginErr)
// decode GameModel from gameJSON...
// Отправка кода
let resultJSON = client.sendCode(12345, levelID: 67890, levelNumber: 1, code: "КОД123", error: &loginErr)
Доступные методы
| Go (encxmobile) | Описание |
|---|---|
NewClient / NewClientWithOptions |
Создание клиента |
Login / LoginWithCaptcha |
Авторизация |
GetGameModel |
Состояние игры |
SendCode |
Отправка кодов |
GetPenaltyHint |
Штрафная подсказка |
GetGameList / GetDomainGames |
Список игр |
GetGameStatistics |
Статистика |
EnterGame |
Вступление в игру |
GetProfile |
Профиль пользователя |
ExportCookies / ImportCookies |
Сохранение сессии |
LoginErrorText / EventText |
Тексты ошибок |
PHP
Тот же пакет mobile/encxmobile доступен из PHP: он собирается в разделяемую библиотеку
(-buildmode=c-shared), которую PHP вызывает через FFI. Клиент живёт на стороне Go, поэтому
сессия и куки сохраняются между вызовами.
require __DIR__ . '/bindings/php/autoload.php';
$client = Encx\Client::newClient('demo.en.cx', false);
$client->login('user', 'password');
$model = json_decode($client->getGameModel(82448), true);
Обёртки не пишутся руками: cgo-экспорты, C-заголовок и PHP-классы генерируются из Go-исходника
командой go generate ./bindings/..., а отставание ловят drift-тест, CI и pre-commit hook.
Сборка, установка и полный список связанных методов — в bindings/php/README.md.
CLI: encli
encli полезен, когда нужно быстро дернуть API руками и не городить под это отдельный код.

Демо использует публичный mock-сервер encounter.exe.xyz. GIF обновляются автоматически через GitHub Pages.

Типичный поток такой:
- залогиниться;
- выбрать игру;
- смотреть статус, задания и сообщения;
- отправлять коды и запрашивать подсказки.
# Авторизация (интерактивный ввод пароля)
encli login -domain tech.en.cx -insecure
# Список игр
encli games
encli game-list
# Статус игры
encli status -game-id 12345
# Задание текущего уровня
encli level -game-id 12345
# Все уровни с прогрессом
encli levels -game-id 12345
# Бонусы текущего уровня
encli bonuses -game-id 12345
# Подсказки (обычные и штрафные)
encli hints -game-id 12345
# Секторы текущего уровня
encli sectors -game-id 12345
# Лог пробитий кодов
encli log -game-id 12345
# Сообщения от организаторов
encli messages -game-id 12345
# Вступить в игру
encli enter -game-id 12345
# Отправка кода уровня/сектора
encli send-code -game-id 12345 "КОД123"
# Отправка бонусного кода
encli send-bonus -game-id 12345 "БОНУС123"
# Запрос штрафной подсказки
encli hint -game-id 12345 42
# Статистика игры
encli game-stats -game-id 12345
# Профиль
encli profile
# Версия
encli -v
# LLM-агент (кратко; подробнее — раздел «LLM-агент и OpenRouter» ниже)
export LLM_API_KEY=sk-or-v1-... # ключ с https://openrouter.ai/keys
export LLM_MODEL=anthropic/claude-sonnet-4 # любая модель из каталога OpenRouter
encli -game-id 12345 --llm "создай 3 уровня с бонусами и подсказками"
encli -game-id 12345 --llm "пройдись по уровням, проверь ответы и предложи исправления"
encli -readonly -game-id 12345 --llm "покажи содержимое всех уровней" # без записи в игру
# Web UI с тем же ключом и моделью
encli -web
# Выход
encli logout
# --- Admin-команды ---
# Список авторских игр
encli admin-games
# Список уровней с ID
encli admin-levels -game-id 12345
# Создать 3 уровня
encli admin-create-levels -game-id 12345 3
# Удалить уровень №5
encli admin-delete-level -game-id 12345 5
# Переименовать уровень (ID из admin-levels)
encli admin-rename-level -game-id 12345 67890 "Новое название"
# Установить автопереход 1ч 30мин с штрафом 15мин
encli admin-set-autopass -game-id 12345 1 1:30:00 0:15:00
# Блокировка: 3 попытки за 1 минуту, на игрока
encli admin-set-block -game-id 12345 1 3 0:01:00 player
# Создать бонус (уровень 1, level-id 67890, название, ответы)
encli admin-create-bonus -game-id 12345 1 67890 "Бонус 1" "ответ1" "ответ2"
# Удалить бонус
# bonus-id берите из admin-level-content
# там бонусы печатаются как [bonus <id>]
encli admin-delete-bonus -game-id 12345 1 <bonus-id>
# Создать сектор
encli admin-create-sector -game-id 12345 1 "Сектор А" "код1" "код2"
# Посмотреть содержимое уровня и найти sector-id
encli admin-level-content -game-id 12345 1
# Удалить сектор
# sector-id берите из admin-level-content
# там секторы печатаются как [sector <id>]
encli admin-delete-sector -game-id 12345 1 <sector-id>
# Обновить сектор
# sector-id берите из admin-level-content
# пример: переименовать сектор и заменить список ответов
encli admin-update-sector -game-id 12345 1 <sector-id> name="Сектор Б" answers="код3,код4"
# Создать подсказку (откроется через 30 минут)
encli admin-create-hint -game-id 12345 1 0:30:00 "Текст подсказки"
# Удалить подсказку
# hint-id берите из admin-level-content
# там подсказки печатаются как [hint <id>]
encli admin-delete-hint -game-id 12345 1 <hint-id>
# Создать задание
encli admin-create-task -game-id 12345 1 "Текст задания уровня"
# Обновить задание
# task-id берите из admin-level-content
# там задания печатаются как [task <id>]
encli admin-update-task -game-id 12345 1 <task-id> "Новый текст задания"
# Установить имя и комментарий уровня
encli admin-set-comment -game-id 12345 1 "Название" "Комментарий для орга"
# Список команд в игре
encli admin-teams -game-id 12345
# Начисления бонусного/штрафного времени
encli admin-corrections -game-id 12345
encli admin-add-correction -game-id 12345 "Team Name" bonus 0:10:00 0 "за красоту"
encli admin-delete-correction -game-id 12345 444
# Чтение содержимого уровня (задание, секторы, бонусы, подсказки, настройки)
encli admin-level-content -game-id 12345 1
# Сообщения уровня
# сначала посмотрите список сообщений и их message-id
encli admin-messages -game-id 12345 1
# создать сообщение
# здесь 67890 — это level-id, его берите из admin-levels
encli admin-create-message -game-id 12345 67890 "Текст сообщения"
# обновить сообщение
# message-id берите из admin-messages
encli admin-update-message -game-id 12345 1 <message-id> text="Новый текст" mode=chosen levels=67890
# удалить сообщение
# message-id берите из admin-messages
encli admin-delete-message -game-id 12345 1 <message-id>
# Создать новую игру (title, start, finish обязательны; даты в RFC3339)
encli admin-create-game title="Новая игра" start="2026-09-10T18:00:00+03:00" finish="2026-09-11T18:00:00+03:00"
# Информация об игре (название, авторы, описание, даты, модерация заявок)
encli admin-game-info -game-id 12345
# Обновить настройки игры
# Ключи: title, authors, description, prize, start, finish, request_last_date, moderated
# Не указанные ключи сохраняют текущее значение
encli admin-update-game -game-id 12345 title="Новое название" description="Описание"
# Перенести старт и включить автоприём заявок (moderated=false)
# Даты в RFC3339; старый движок понимает и DD.MM.YYYY HH:MM:SS
# Старт нельзя изменить после начала игры
encli admin-update-game -game-id 12345 start="2026-09-10T18:00:00+03:00" moderated=false
# Полная очистка игры (обнуление)
encli admin-wipe-game -game-id 67890
# Удаление игры целиком (безвозвратно; ID повторяется как подтверждение)
encli admin-delete-game -game-id 67890 67890
# Копирование игры целиком (из 12345 в 67890)
# Рекомендуется сначала admin-wipe-game на целевой
encli admin-copy-game -game-id 12345 67890
# Перестановка и клонирование уровней
encli admin-swap-levels -game-id 12345 2 5
encli admin-insert-level -game-id 12345 3 0
encli admin-clone-levels -game-id 12345 2 1
# Обновление бонуса и подсказки
encli admin-update-bonus -game-id 12345 1 <bonus-id> name="Новое имя" answers="код1,код2"
encli admin-update-hint -game-id 12345 1 <hint-id> text="Новый текст" delay=0:45:00
# Удаление задания
encli admin-delete-task -game-id 12345 1 <task-id>
# Жизненный цикл игры
encli admin-deliver -game-id 12345
encli admin-not-deliver -game-id 12345
encli admin-award-points -game-id 12345
encli admin-end-ratings -game-id 12345
encli admin-calc-ik -game-id 12345
encli admin-action-monitor -game-id 12345
LLM-агент и OpenRouter
encli встраивает PicoClaw с tool-calling: агент читает состояние игры, вызывает admin-команды, ищет факты в Википедии и читает локальные файлы со сценарием. Один runtime используется в CLI (--llm), локальном Web UI (-web) и Docker (через -e).
API-ключ и модель
По умолчанию используется OpenRouter (https://openrouter.ai/api/v1) и бесплатная модель openai/gpt-oss-120b:free. Чтобы использовать свой ключ и другую модель, задайте переменные окружения (или передайте их в docker run -e ...):
| Переменная | Алиас | Назначение |
|---|---|---|
LLM_API_KEY |
OPENROUTER_API_KEY |
API-ключ OpenRouter (получить) |
LLM_MODEL |
OPENROUTER_MODEL |
Идентификатор модели в каталоге OpenRouter, например anthropic/claude-sonnet-4 или google/gemini-2.5-pro-preview |
LLM_BASE_URL |
OPENROUTER_BASE_URL |
Base URL OpenAI-совместимого API (если не OpenRouter) |
Приоритет: сначала LLM_*, затем OPENROUTER_*. Для localhost (127.0.0.1 / localhost в URL) ключ не обязателен — удобно для локального прокси.
Всё то же самое настраивается мышкой в Web UI — см. Настройка LLM в Web UI.
Подписка ChatGPT вместо API-ключа
Вместо ключа агент умеет работать на подписке ChatGPT — через тот же backend, что использует Codex CLI. Токенов по прайсу OpenRouter это не тратит: в отчёте о выполнении стоимость показывается как $0 (подписка ChatGPT).
encli codex-login # откроет браузер и дождётся редиректа
encli codex-login -device # headless-хост: код вводится на chatgpt.com
encli codex-login -no-browser # напечатает ссылку, ждёт вставки redirect URL
encli codex-status # аккаунт, срок действия токена, путь к файлу
encli codex-logout # удалить сохранённую учётку
Учётка (access- и refresh-токен) лежит в ~/.config/encli/codex/auth.json с правами 0600; путь переопределяется через ENCLI_CODEX_AUTH_FILE. Отдельный подкаталог не случаен: -web считает каждый *.json в ~/.config/encli/ сессией домена Encounter, и учётка в общем каталоге показалась бы там фиктивным доменом с кнопкой Logout, которая её удаляет. Access-токен обновляется автоматически за 5 минут до истечения, обновлённое значение сразу пишется обратно в файл — повторный codex-login нужен, только если истёк refresh-токен.
Как выбирается транспорт:
| Условие | Транспорт |
|---|---|
-llm-auth codex или LLM_AUTH=codex |
Подписка ChatGPT (ошибка, если codex-login не выполнен) |
-llm-auth gigachat или LLM_AUTH=gigachat |
GigaChat (ошибка, если не задан GIGACHAT_CREDENTIALS) |
-llm-auth apikey или LLM_AUTH=apikey |
Только API-ключ, сохранённая учётка игнорируется |
Ничего не задано, LLM_API_KEY задан |
API-ключ (явный ключ всегда в приоритете) |
Ничего не задано, задан LLM_BASE_URL / OPENROUTER_BASE_URL |
Указанный endpoint (в том числе локальный прокси без ключа) |
| Ничего не задано в окружении, есть настройки из Web UI | То, что сохранено в ~/.config/encli/llm/settings.json |
| Ничего не задано, ключа и endpoint нет, учётка ChatGPT есть | Подписка ChatGPT |
Ничего не задано, ключа и endpoint нет, задан GIGACHAT_CREDENTIALS |
GigaChat |
Автовыбор подписки или GigaChat срабатывает, только когда не задано вообще ничего:
и ключ, и base URL — это явно выраженное намерение, поэтому оставшаяся с прошлого раза
учётка не уводит локальный прокси на chatgpt.com. Чтобы использовать подписку
при заданном ключе или endpoint, укажите -llm-auth codex явно. Учётка ChatGPT имеет
приоритет над GIGACHAT_CREDENTIALS; выбрать GigaChat при обеих настройках можно
через -llm-auth gigachat.
Модель по умолчанию для подписки — gpt-5.5; LLM_MODEL её переопределяет, но только
именем, которое backend действительно обслуживает: префикс openai/ отбрасывается,
принимаются семейства gpt-*, o3*, o4*. Любое другое имя (в том числе забытое в шелле
openrouter/free или claude-sonnet-4) заменяется на дефолтную модель с предупреждением
в stderr — иначе backend подменил бы её молча, а в отчёте стояла бы запрошенная модель,
а не та, что отвечала. Флаг -llm-auth доступен в --llm, -chat и -web.
# Разовая настройка
encli codex-login
# Дальше ключ не нужен ни в одном из режимов
encli -game-id 12345 --llm "проверь ответы на уровне 3"
encli -game-id 12345 -chat
encli -web
# Явный выбор, когда в окружении есть и LLM_API_KEY
encli -llm-auth codex -game-id 12345 --llm "покажи статус игры"
GigaChat (Сбер)
Агент умеет работать на GigaChat API.
Ключ авторизации (Authorization key — base64 от Client ID:Client Secret) берётся в
личном кабинете Сбера, в проекте GigaChat API; по нему клиент сам получает access-токен,
живущий 30 минут, и обновляет его за 2 минуты до истечения.
export GIGACHAT_CREDENTIALS=<Ключ авторизации из личного кабинета>
encli -game-id 12345 --llm "покажи уровни"
# Явный выбор, когда в окружении есть и LLM_API_KEY
encli -llm-auth gigachat -game-id 12345 --llm "покажи статус игры"
| Переменная | Назначение |
|---|---|
GIGACHAT_CREDENTIALS |
Ключ авторизации (обязательна) |
GIGACHAT_SCOPE |
Версия API: GIGACHAT_API_PERS (по умолчанию), GIGACHAT_API_B2B, GIGACHAT_API_CORP |
GIGACHAT_MODEL |
Модель (по умолчанию: GigaChat-2-Max); при отсутствии берётся LLM_MODEL |
GIGACHAT_BASE_URL |
Base URL API (по умолчанию: https://api.giga.chat/v1) |
GIGACHAT_AUTH_URL |
Endpoint OAuth (по умолчанию: https://ngw.devices.sberbank.ru:9443/api/v2/oauth) |
GIGACHAT_CA_BUNDLE |
PEM-файл с «Российским доверенным корневым УЦ» — включает проверку сертификата |
GIGACHAT_INSECURE |
0 — проверять сертификат; 1 — не проверять (по умолчанию не проверяется) |
Две площадки и модели. По умолчанию используется https://api.giga.chat/v1 — там живут
GigaChat-2, GigaChat-2-Pro, GigaChat-2-Max и третье поколение: GigaChat-3-Lightning,
GigaChat-3-Pro, GigaChat-3-Ultra. Старые имена (GigaChat, GigaChat-Pro, GigaChat-Max,
*-preview) обслуживает только legacy-хост — для них задайте
GIGACHAT_BASE_URL=https://gigachat.devices.sberbank.ru/api/v1. Если модели на площадке нет,
API отвечает 404 No such model; encli дополняет эту ошибку списком моделей, которые площадка
реально отдаёт, и подсказкой про legacy-хост.
LLM_BASE_URL для GigaChat НЕ читается — это намеренно. Переменная означает «адрес
OpenAI-совместимого endpoint», и если бы она действовала здесь, забытый в шелле
LLM_BASE_URL=https://openrouter.ai/api/v1 отправил бы OAuth-токен GigaChat в заголовке
Authorization постороннему хосту. Адрес переопределяется только через GIGACHAT_BASE_URL.
Сертификат. GigaChat отдаётся под сертификатом Минцифры, которого нет ни в одном
системном хранилище по умолчанию. Проверка, которая падает всегда, — это не защита, а
поломка, поэтому по умолчанию сертификат GigaChat не проверяется (один раз за запуск
об этом печатается предупреждение в stderr). Чтобы включить проверку, скачайте
корневой сертификат Минцифры
и укажите путь в GIGACHAT_CA_BUNDLE — он добавляется к системному пулу, а не заменяет
его. Если корень уже установлен в системе, достаточно GIGACHAT_INSECURE=0.
# Просто работает, без проверки сертификата
export GIGACHAT_CREDENTIALS=<ключ>
encli -game-id 12345 --llm "проверь ответы на уровне 3"
# Третье поколение
GIGACHAT_MODEL=GigaChat-3-Ultra encli -domain demo.en.cx --llm "покажи игры на домене"
# С проверкой сертификата
export GIGACHAT_CA_BUNDLE=~/.config/encli/russian_trusted_root_ca.pem
export GIGACHAT_MODEL=GigaChat-2-Pro
encli -game-id 12345 --llm "проверь ответы на уровне 3"
Инструменты передаются GigaChat в его собственном формате (functions / function_call),
а не в OpenAI-совместимом tools / tool_calls: OpenAI-образный запрос GigaChat принимает
без ошибки, но молча теряет все инструменты, и агент остаётся без единого действия.
За один ход GigaChat вызывает не больше одной функции — цикл агента от этого просто
становится длиннее. Стоимость в отчёте о выполнении не показывается: GigaChat тарифицируется
в собственных единицах с предоплаченного баланса, а не в долларах.
Примеры:
# Разовый запуск с другой моделью
LLM_API_KEY=sk-or-v1-XXXX LLM_MODEL=anthropic/claude-sonnet-4 \
encli -game-id 12345 --llm "создай уровень с бонусом"
# Постоянная настройка в shell
export LLM_API_KEY=sk-or-v1-XXXX
export LLM_MODEL=google/gemini-2.5-flash-preview
encli -game-id 12345 --llm "проверь ответы на уровне 3"
# Старые имена переменных (обратная совместимость)
export OPENROUTER_API_KEY=sk-or-v1-XXXX
export OPENROUTER_MODEL=openai/gpt-4o
encli -game-id 12345 --llm "покажи статус"
# Локальный OpenAI-совместимый сервер (Ollama, LiteLLM, Cursor proxy и т.п.)
export LLM_BASE_URL=http://127.0.0.1:8317/v1
export LLM_MODEL=my-local-model
encli -game-id 12345 --llm "покажи уровни"
Список моделей и цены: openrouter.ai/models. В конце сессии агент печатает отчёт: модель, число запросов к LLM, токены и ориентировочная стоимость (для OpenRouter — по прайсу из /models).
Режим --llm
encli -game-id 12345 --llm "скопируй игру 82033 в 82034"
encli -debug -game-id 12345 --llm "покажи статус игры" # подробный лог в stderr
encli -readonly -game-id 12345 --llm "аудит всех уровней" # без изменений в игре
Для review-запросов (проверь ответы, найди ошибки) агент не применяет правки сразу: сначала предлагает исправления, затем CLI (или Web UI) спрашивает подтверждение по каждому пункту.
Поведение агента:
- Вызовы инструментов выводятся в читаемом виде (например,
[admin_level_content] Читаю содержимое уровня 3). - Большие ответы tool-call'ов сжимаются перед отправкой обратно в модель.
- При 429 и 502–504 — до 3 повторов с нарастающей задержкой.
- После создания или изменения уровней агент проверяет результат (коды, тайминги, подсказки, задания).
Web UI (-web)
Локальный чат с тем же агентом, историей диалогов и переключателем режима безопасности (только чтение / с подтверждением / полный доступ):
export LLM_API_KEY=sk-or-v1-...
export LLM_MODEL=anthropic/claude-sonnet-4
encli -web
# по умолчанию http://127.0.0.1:8787 — откроется в браузере
encli -web -web-addr 0.0.0.0:8787 # слушать на всех интерфейсах
# или: ENCLI_WEB_ADDR=0.0.0.0:8787 encli -web
В шапке UI отображается текущая модель. Сессии Encounter логинятся через UI; чаты сохраняются в ~/.config/encli/web/chats/.
Онбординг при первом запуске
Ничего не настроено — и первый же encli -web встречает мастером вместо пустого чата,
который не может ответить. Четыре шага: приветствие → модель → движок → вход в en.cx.
- Модель. Те же два способа, что и в панели «Настройки LLM»: вход по подписке ChatGPT или OpenAI-совместимый провайдер (Base URL, ключ, модель). Кнопка «Проверить подключение» показывает, что именно уйдёт агенту, до того как вы закроете мастер.
- Движок.
auto,legacyилиnew. Кнопка «Определить автоматически» спрашивает у API-хоста, на чём реально живёт домен, и предлагает найденное значение. Выбор сохраняется в~/.config/encli/engine/settings.json(права0600, атомарная запись), путь переопределяется черезENCLI_ENGINE_SETTINGS_FILE. - Вход в en.cx. Домен, логин, пароль. Шаг пропускаемый: без него агент уже настроен, просто ещё не авторизован, и войти можно позже в шапке.
Факт прохождения хранится в ~/.config/encli/onboarding/state.json
(переопределяется через ENCLI_ONBOARDING_FILE), так что мастер не повторяется на каждом
старте. Кнопка «Пропустить настройку» закрывает его тоже насовсем. Открыть мастер снова —
кнопка «Мастер настройки» (◈) в шапке; чтобы он снова появился при следующем запуске,
удалите файл состояния или вызовите POST /api/v1/onboarding/reset.
Файлы настроек и состояния лежат в подкаталогах, а не прямо в ~/.config/encli/, по той же
причине, что и учётка ChatGPT: -web показывает каждый *.json из корня этого каталога как
сессию домена Encounter, и кнопка «Выйти» рядом с такой «сессией» удалила бы файл.
Приоритет источников тот же, что и у настроек LLM: флаг CLI → переменная окружения → сохранённые настройки → дефолт. Если поле перекрыто переменной или флагом, мастер показывает это рядом с полем, а не делает вид, что сохранение подействовало.
Эндпоинты: GET /api/v1/onboarding, POST /api/v1/onboarding/complete,
POST /api/v1/onboarding/reset, GET/PUT/DELETE /api/v1/engine/settings,
GET /api/v1/engine/probe?domain=....
Настройка LLM в Web UI
Переменные окружения задавать не обязательно: кнопка «Настройки LLM» в шапке открывает панель, где настраивается подключение к модели.
- Подписка ChatGPT. Кнопка «Войти через ChatGPT» запускает тот же OAuth-поток, что и
encli codex-login, но целиком из браузера: encli открывает страницу авторизации, ждёт редирект наhttp://localhost:1455/auth/callbackи сохраняет учётку в~/.config/encli/codex/auth.json. Если браузер живёт на другой машине, в панели есть поле для ручной вставки redirect URL. Порт 1455 не выбирается произвольно: OpenAI сверяет redirect URI с зарегистрированным для клиента Codex, поэтому при занятом порте вход честно падает с ошибкой вместо молчаливого отказа на стороне провайдера. - OpenAI-совместимый провайдер. Поля Base URL, API-ключ и модель. Сохраняются в
~/.config/encli/llm/settings.json(права0600, атомарная запись); путь переопределяется черезENCLI_LLM_SETTINGS_FILE. Файл лежит в подкаталоге по той же причине, что и учётка ChatGPT:-webпоказывает каждый*.jsonиз~/.config/encli/как сессию домена Encounter. Через API ключ наружу не отдаётся — только маска вида••••1234.
Приоритет источников: флаг CLI → переменная окружения → сохранённые настройки → дефолт. Окружение намеренно старше файла: ключ, выставленный в шелле или в контейнере, — это голос деплоя, и его не должно перебивать значение, введённое в UI когда-то давно. Чтобы это не выглядело как «сохранил, а ничего не изменилось», панель рядом с таким полем показывает плашку с именем переменной, которая его перекрывает, и внизу — что реально уйдёт агенту.
Настройки читаются на каждом запросе, так что менять их можно не перезапуская encli -web.
Соответствие эндпоинтов: GET/PUT/DELETE /api/v1/llm/settings, POST /api/v1/llm/codex/login
(+ GET .../login/{id} для статуса, POST .../login/{id}/code для ручной вставки),
GET /api/v1/llm/codex/status, POST /api/v1/llm/codex/logout.
На Windows Web UI стартует и без флага: если encli.exe запущен без команды и без терминала (двойной клик в проводнике, ярлык), вместо мелькнувшей справки поднимается Web UI и открывается браузер. Запуск из cmd.exe или PowerShell не меняется — encli.exe без аргументов там по-прежнему печатает справку, а любая команда (encli.exe game-list) выполняется как обычно.
TUI-чат (-chat)
Консольный аналог Web UI: полноэкранный чат в терминале с тем же агентом, той же историей (~/.config/encli/web/chats/ — чаты видны и в -web), теми же сессиями Encounter и режимами безопасности per-chat.
export LLM_API_KEY=sk-or-v1-...
encli -chat -domain tech.en.cx
encli -chat -readonly # по умолчанию режим approve
Слева — список чатов, справа — переписка с инлайновым логом вызовов инструментов; подтверждения изменений запрашиваются прямо в интерфейсе (y / n / q). Слэш-команды: /help, /new, /chats, /domain, /game, /games, /mode readonly|approve|full, /login, /logout, /auth, /delete, /export [md|json], /quit. Клавиши: Enter — отправить, Tab — фокус на список чатов, Ctrl+B — показать/скрыть список, Esc — отменить текущий запрос, PgUp/PgDn — прокрутка.
Импорт HTML-сценария в чате
Для файла экспорта Encounter GameScenario агент использует inspect_scenario_file
(название и точные счётчики), затем admin_import_scenario с ID целевой игры.
Импортёр переносит уровни и содержимое напрямую из файла и сверяет результат со
свежим экспортом. admin_verify_scenario выполняет такую сверку без изменений.
Повторный импорт выравнивает содержимое существующих уровней; лишние уровни не
удаляются автоматически и попадают в отчёт о расхождениях.
Большие результаты старых инструментов сокращаются только в запросе к модели; полная история сохраняется. Пустые и незавершённые ответы модели выводятся как ошибки, а в Web UI ошибки остаются в переписке после обновления страницы.
Локальные файлы, Википедия и веб
Агент может читать сценарии с диска, открывать ссылки и сверять факты:
| Инструмент | Назначение |
|---|---|
read_local_file |
Прочитать текстовый файл |
list_local_dir |
Список файлов в каталоге |
search_local_files |
Поиск по содержимому / glob |
wikipedia_search |
Поиск статей |
wikipedia_article |
Краткое содержание статьи |
fetch_url |
Загрузить внешнюю страницу по ссылке как текст |
fetch_url принимает только http/https, конвертирует HTML в читаемый текст
(содержимое <script> и <style> отбрасывается) и отдаёт длинные страницы
частями через offset. Адреса, которые резолвятся в loopback, приватные или
link-local сети, отклоняются на этапе подключения — включая редиректы, — поэтому
ссылкой из чата нельзя дотянуться до внутренних сервисов хоста.
Корень для локальных путей — LLM_FILES_ROOT (по умолчанию текущая рабочая директория); файлы вне этого каталога недоступны.
export LLM_FILES_ROOT=~/quests/my-scenario
encli -game-id 12345 --llm "прочитай levels.md и создай уровни по сценарию"
Команды CLI
| Команда | Что делает |
|---|---|
login |
Логинится и сохраняет сессию |
logout |
Чистит сохраненную сессию |
codex-login |
Авторизует агента по подписке ChatGPT (OAuth, без API-ключа) |
codex-logout |
Удаляет сохранённую учётку ChatGPT |
codex-status |
Показывает сохранённую учётку ChatGPT (аккаунт, срок, путь) |
games |
Показывает список игр через HTML-страницу домена |
game-list |
Показывает список игр через JSON API |
status |
Показывает текущее состояние игры |
level |
Печатает текст текущего задания |
levels |
Показывает все уровни с прогрессом |
bonuses |
Показывает бонусы текущего уровня |
hints |
Показывает подсказки (обычные и штрафные) |
sectors |
Показывает секторы текущего уровня |
log |
Показывает лог пробитий кодов |
messages |
Показывает сообщения от организаторов |
enter |
Подает заявку на вход в игру |
send-code |
Отправляет код уровня/сектора через LevelAction.Answer |
send-bonus |
Отправляет бонусный код через BonusAction.Answer |
hint |
Запрашивает штрафную подсказку |
game-stats |
Показывает статистику игры (уровни, команды, результаты) |
profile |
Показывает профиль текущего пользователя (ранг, очки, домен) |
import-scenario |
Импортирует Game scenario.html в игру (уровни, задания, подсказки, ответы) |
--llm <prompt> |
Естественно-языковая команда через LLM-агента (OpenRouter или совместимый API) |
-web |
Локальный Web UI для агента (чат, история, режимы безопасности) |
-chat |
Полноэкранный TUI-чат с агентом в терминале (общая история с -web) |
-readonly |
Запретить агенту инструменты, изменяющие игру (для --llm, -web и -chat) |
-llm-auth |
Транспорт агента: apikey (по умолчанию) или codex — подписка ChatGPT |
-v |
Показывает версию |
Admin-команды (требуют прав редактора игры):
| Команда | Что делает |
|---|---|
admin-games |
Показывает список авторских игр |
admin-levels |
Показывает все уровни с их ID (админка) |
admin-create-levels |
Создаёт указанное количество новых уровней |
admin-delete-level |
Удаляет уровень по номеру |
admin-rename-level |
Переименовывает уровень |
admin-set-autopass |
Устанавливает таймер автоперехода |
admin-set-block |
Настраивает блокировку ответов |
admin-create-bonus |
Создаёт бонус на уровне |
admin-delete-bonus |
Удаляет бонус по ID |
admin-create-sector |
Создаёт сектор на уровне |
admin-delete-sector |
Удаляет сектор по ID |
admin-update-sector |
Обновляет сектор по ID |
admin-create-hint |
Создаёт подсказку на уровне |
admin-delete-hint |
Удаляет подсказку по ID |
admin-update-hint |
Обновляет подсказку по ID |
admin-create-task |
Создаёт задание на уровне |
admin-update-task |
Обновляет задание по ID |
admin-set-comment |
Устанавливает название и комментарий уровня |
admin-teams |
Показывает команды в игре |
admin-corrections |
Показывает начисления бонусного/штрафного времени |
admin-add-correction |
Добавляет начисление времени |
admin-delete-correction |
Удаляет начисление по ID |
admin-level-content |
Читает содержимое уровня (задание, секторы, бонусы, подсказки, настройки) |
admin-create-message |
Создаёт игровое сообщение |
admin-messages |
Показывает сообщения уровня |
admin-update-message |
Обновляет сообщение по ID |
admin-delete-message |
Удаляет сообщение по ID |
admin-create-game |
Создаёт новую игру (key=value: title, start, finish обязательны) |
admin-game-info |
Показывает информацию об игре (название, авторы, описание, дата) |
admin-update-game |
Обновляет настройки игры (key=value: title, authors, description, prize, start, finish, request_last_date, moderated) |
admin-deliver |
Помечает игру как состоявшуюся |
admin-award-points |
Начисляет очки участникам |
admin-end-ratings |
Завершает приём оценок |
admin-calc-ik |
Считает игровой коэффициент |
admin-wipe-game |
Полностью обнуляет игру (удаляет всё содержимое) |
admin-copy-game |
Копирует всю игру (уровни, настройки, бонусы, секторы, подсказки) в другую |
admin-delete-game |
Удаляет игру целиком, без возврата (ID повторяется вторым аргументом как подтверждение) |
admin-swap-levels |
Меняет местами два уровня по номеру |
admin-insert-level |
Перемещает уровень на новую позицию |
admin-clone-levels |
Клонирует N уровней с настроек существующего |
admin-delete-task |
Удаляет задание по ID |
admin-update-bonus |
Обновляет бонус по ID (key=value) |
admin-update-hint |
Обновляет подсказку по ID (key=value) |
admin-action-monitor |
Показывает монитор действий в игре |
admin-not-deliver |
Помечает игру как несостоявшуюся |
Импорт сценария из HTML
import-scenario загружает экспорт страницы Game scenario.html (Encounter), удаляет текущие уровни в целевой игре и создаёт новые:
Запросы нового REST API по умолчанию отправляются с интервалом не меньше 40 мс. Заголовки X-Ratelimit-Limit и X-Ratelimit-Remaining могут дополнительно замедлить запросы: клиент распределяет остаток бюджета с запасом 5% (минимум один запрос), а при малом остатке ждёт минуту. Без заголовка сброса минута — консервативное предположение клиента. Retry-After также учитывается. Бюджет общий для клиентов одного API-хоста внутри процесса; другие процессы и устройства за общим IP учитываются только косвенно через ответы сервера. Ожидание не расходует сетевой таймаут, но подчиняется контексту операции. Для изменения минимального интервала: -api-request-interval 200ms (5 запросов/с); серверный бюджет приоритетнее этого параметра. При --sync-missing -har журнал сохраняется и при ошибке импорта.
- названия уровней
- автопереходы
- задания
- подсказки с задержками
- ответы (как секторы)
Для ссылок на локальные изображения (./..._files/...) команда встраивает файлы как data: URL прямо в HTML текста заданий/подсказок.
Если Encounter включает anti-spam (NotHumanRequest.aspx) или запрос попадает в таймаут, import-scenario не падает: CLI возьмёт ссылку на Login.aspx со страницы проверки и попробует автоматически ввести -login/-password (или ENCX_*), затем JSON sign-in; если не выйдет — попросит завершить проверку в браузере и повторит шаг.
Флаг --sync-missing приводит существующие уровни в соответствие со сценарием: при расхождении задания, подсказки и секторы на уровне пересоздаются по экспорту (источник истины — HTML), без полного wipe игры. Недостающие уровни создаются целиком.
encli import-scenario -game-id 82307 "/Users/svk/Downloads/moscow.en.cx __ Game scenario.html"
# Проверка без записи в игру
encli import-scenario -game-id 82307 --dry-run "/Users/svk/Downloads/moscow.en.cx __ Game scenario.html"
# Сверка и выравнивание уровней по сценарию (без полного wipe)
encli import-scenario -game-id 82307 --sync-missing "/Users/svk/Downloads/moscow.en.cx __ Game scenario.html"
Флаги и переменные окружения
Почти все можно передавать либо через флаги, либо через env. Удобно, если гоняете команды часто.
| Флаг | Env-переменная | Описание |
|---|---|---|
-domain |
ENCX_DOMAIN |
Домен Encounter (по умолчанию: tech.en.cx) |
-login |
ENCX_LOGIN |
Логин |
-password |
ENCX_PASSWORD |
Пароль |
-game-id |
ENCX_GAME_ID |
ID игры |
-insecure |
ENCX_INSECURE |
Пропустить проверку TLS-сертификата |
-http |
— | Использовать HTTP вместо HTTPS |
-engine |
ENCX_ENGINE |
Движок Encounter: auto (по умолчанию), legacy, new |
-api-base-url |
ENCX_API_BASE_URL |
Хост нового движка (по умолчанию выводится из домена) |
-json |
— | Выводить результат в формате JSON |
-debug |
ENCX_DEBUG |
Включить отладочный вывод в stderr |
-har |
ENCX_HAR |
Записывать HTTP-трафик в HAR 1.2 |
-har-out |
ENCX_HAR_OUT |
Путь экспорта HAR (файл или каталог) |
-web-addr |
ENCLI_WEB_ADDR |
Адрес Web UI (по умолчанию: 127.0.0.1:8787) |
-readonly |
— | Блокировать у агента инструменты записи (см. также режим в Web UI) |
| — | LLM_BASE_URL |
Base URL OpenAI-совместимого API (по умолчанию: https://openrouter.ai/api/v1) |
| — | OPENROUTER_BASE_URL |
Алиас для LLM_BASE_URL |
| — | LLM_API_KEY |
API-ключ для --llm и -web (не нужен для localhost) |
| — | LLM_MODEL |
Модель для агента (по умолчанию: openai/gpt-oss-120b:free) |
-llm-auth |
LLM_AUTH |
Транспорт агента: apikey (по умолчанию), codex — подписка ChatGPT, gigachat — GigaChat API |
| — | ENCLI_CODEX_AUTH_FILE |
Путь к учётке ChatGPT (по умолчанию: ~/.config/encli/codex/auth.json) |
| — | ENCLI_LLM_SETTINGS_FILE |
Путь к настройкам LLM из Web UI (по умолчанию: ~/.config/encli/llm/settings.json) |
| — | ENCLI_ENGINE_SETTINGS_FILE |
Путь к настройкам движка из Web UI (по умолчанию: ~/.config/encli/engine/settings.json) |
| — | ENCLI_ONBOARDING_FILE |
Путь к состоянию мастера первого запуска (по умолчанию: ~/.config/encli/onboarding/state.json) |
| — | GIGACHAT_CREDENTIALS |
Ключ авторизации GigaChat (base64 от Client ID:Client Secret) |
| — | GIGACHAT_SCOPE |
Версия GigaChat API (по умолчанию: GIGACHAT_API_PERS) |
| — | GIGACHAT_MODEL |
Модель GigaChat (по умолчанию: GigaChat-2-Max) |
| — | GIGACHAT_BASE_URL |
Base URL GigaChat (по умолчанию: https://api.giga.chat/v1) |
| — | GIGACHAT_AUTH_URL |
Endpoint OAuth GigaChat (по умолчанию: https://ngw.devices.sberbank.ru:9443/api/v2/oauth) |
| — | GIGACHAT_CA_BUNDLE |
PEM с корневым сертификатом Минцифры — включает проверку TLS для GigaChat |
| — | GIGACHAT_INSECURE |
0 — проверять TLS-сертификат GigaChat (по умолчанию не проверяется) |
| — | LLM_FILES_ROOT |
Корень каталога для read_local_file / search_local_files (по умолчанию: cwd) |
| — | OPENROUTER_API_KEY |
Алиас для LLM_API_KEY |
| — | OPENROUTER_MODEL |
Алиас для LLM_MODEL |
Пример:
export ENCX_DOMAIN=tech.en.cx
export ENCX_LOGIN=my_login
export ENCX_PASSWORD=my_password
export ENCX_GAME_ID=12345
export ENCX_DEBUG=1
# LLM (OpenRouter)
export LLM_API_KEY=sk-or-v1-...
export LLM_MODEL=anthropic/claude-sonnet-4
encli login -insecure
encli status
encli -debug status
encli -game-id 12345 --llm "покажи уровни"
В -debug режиме encli пишет в stderr полный разбор аргументов, шаги LLM-агента, запуск и завершение tool-call'ов, а также HTTP-запросы encx с таймингами. Вывод не обрезается — данные показываются целиком для полноценной диагностики.
Подробнее про агента, смену модели и Web UI — в разделе LLM-агент и OpenRouter.
Сборка из исходников
go build -o encli ./cmd/encli/
API
Ниже краткая шпаргалка по основным методам, которые уже завернуты в клиент. Endpoint'ы в таблице — старого движка; чем каждый метод обслуживается на новом, перечислено в матрице паритета.
| Метод | Endpoint | Описание |
|---|---|---|
Login |
POST /login/signin |
Авторизация |
GetGameModel |
POST /gameengines/encounter/play/{id} |
Состояние игры |
SendCode |
POST /gameengines/encounter/play/{id} |
Отправка кода (LevelAction.Answer) |
GetPenaltyHint |
GET /gameengines/encounter/play/{id} |
Запрос штрафной подсказки |
GetGameList |
GET /home/?json=1 |
Список игр (JSON, с пагинацией) |
GetDomainGames |
GET m.{domain}/ |
Список игр (HTML) |
GetGameStatistics |
GET /gamestatistics/full/{id}?json=1 |
Полная статистика игры |
GetTimeoutToGame |
GET m.{domain}/gameengines/encounter/play/{id} |
Таймер до начала |
EnterGame |
GET /MakeGameFee.aspx?confirm=yes&gid={id} (fallback: POST …/makefee/Login.aspx) |
Подать заявку / вступить в игру |
GetGameDetails |
GET /GameDetails.aspx?gid={id} |
Детали игры (HTML) |
GetTeamDetails |
GET /Teams/TeamDetails.aspx?tid={id} |
Информация о команде |
AcceptTeamInvitation |
GET /Teams/TeamDetails.aspx?action=accept_invitation&tid={id} |
Принять приглашение |
Admin API (требует прав редактора):
| Метод | Endpoint | Описание |
|---|---|---|
AdminGetLevels |
GET /Administration/Games/LevelManager.aspx |
Список уровней (ID, названия) |
AdminCreateLevels |
GET /Administration/Games/LevelManager.aspx?levels=create |
Создание уровней |
AdminDeleteLevel |
GET /Administration/Games/LevelManager.aspx?levels=delete |
Удаление уровня |
AdminRenameLevels |
POST /Administration/Games/LevelManager.aspx?level_names=update |
Переименование уровней |
AdminUpdateAutopass |
POST /Administration/Games/LevelEditor.aspx |
Настройка автоперехода |
AdminUpdateAnswerBlock |
POST /Administration/Games/LevelEditor.aspx |
Настройка блокировки ответов |
AdminCreateBonus |
POST /Administration/Games/BonusEdit.aspx?action=save |
Создание бонуса |
AdminDeleteBonus |
GET /Administration/Games/BonusEdit.aspx?action=delete |
Удаление бонуса |
AdminCreateSector |
POST /Administration/Games/LevelEditor.aspx |
Создание сектора |
AdminDeleteSector |
GET /Administration/Games/LevelEditor.aspx?delsector={id} |
Удаление сектора |
AdminCreateHint |
POST /Administration/Games/PromptEdit.aspx |
Создание подсказки |
AdminDeleteHint |
GET /Administration/Games/PromptEdit.aspx?action=PromptDelete |
Удаление подсказки |
AdminCreateTask |
POST /Administration/Games/TaskEdit.aspx |
Создание задания |
AdminUpdateComment |
POST /Administration/Games/NameCommentEdit.aspx |
Обновление названия/комментария |
AdminGetTeams |
GET /Administration/Games/TaskEdit.aspx |
Список команд |
AdminGetCorrections |
GET /GameBonusPenaltyTime.aspx |
Список начислений времени |
AdminAddCorrection |
POST /GameBonusPenaltyTime.aspx?action=save |
Добавление начисления |
AdminDeleteCorrection |
GET /GameBonusPenaltyTime.aspx?action=delete |
Удаление начисления |
AdminGetLevelSettings |
GET /Administration/Games/LevelEditor.aspx |
Чтение настроек уровня (автопереход, блокировка) |
AdminGetBonusIds |
GET /Administration/Games/LevelEditor.aspx |
Список ID бонусов на уровне |
AdminGetBonus |
GET /Administration/Games/BonusEdit.aspx?action=edit |
Чтение деталей бонуса |
AdminGetHintIds |
GET /Administration/Games/LevelEditor.aspx |
Список ID подсказок на уровне |
AdminGetHint |
GET /Administration/Games/PromptEdit.aspx?action=PromptEdit |
Чтение деталей подсказки (обычной и штрафной) |
AdminGetTaskIds |
GET /Administration/Games/LevelEditor.aspx |
Список ID заданий на уровне |
AdminGetTask |
GET /Administration/Games/TaskEdit.aspx?action=TaskEdit |
Чтение деталей задания |
AdminGetComment |
GET /Administration/Games/NameCommentEdit.aspx |
Чтение названия и комментария уровня |
AdminGetSectorAnswers |
GET /ALoader/LevelInfo.aspx |
Чтение секторов и ответов уровня |
AdminGetGameInfo |
GET /Administration/Games/GameEditor.aspx |
Чтение настроек игры (название, авторы, описание, приз, дата) |
AdminUpdateGameInfo |
POST /Administration/Games/GameEditor.aspx |
Обновление настроек игры |
AdminWipeGame |
(комбинированный) | Полная очистка игры (удаление всего содержимого) |
AdminCopyGame |
(комбинированный) | Полное копирование игры (уровни, настройки, бонусы, секторы, подсказки) |
Полная неофициальная (полученная методом реверс-инжиниринга) спецификация API в формате OpenAPI 3.1: openapi.yaml.
Поддерживаемые домены: *.en.cx, *.encounter.cx, *.encounter.ru. Домен quest.ua deprecated — мигрирован в {city}questua.en.cx (напр. kharkov.quest.ua -> kharkovquestua.en.cx).
Тестовый домен
Для тестирования собственных разработок предусмотрен специализированный домен tech.en.cx. Чтобы получить на нём права создания игр (исключительно в технологических целях) — напишите в техподдержку сети.
Тесты
Интеграционные тесты ходят в tech.en.cx:
ENCX_INTEGRATION=1 go test ./encx/ -v -count=1
Если переменную ENCX_INTEGRATION не задавать, запустятся только юнит-тесты.
E2E-тесты
Пакет e2e/ содержит сквозные тесты библиотеки encx и CLI encli
против реального домена Encounter. Они исключены из обычной сборки
билд-тегом e2e:
go test -tags e2e ./e2e -v -timeout 15m
Конфигурация через переменные окружения:
| Переменная | Назначение | По умолчанию |
|---|---|---|
ENCX_E2E_DOMAIN |
Домен Encounter | svk.en.cx |
ENCX_E2E_LOGIN |
Логин аккаунта | skrashevich |
ENCX_E2E_PASSWORD |
Пароль аккаунта | — |
ENCX_E2E_GAME_ID |
ID игры-песочницы | 82448 |
Аккаунт должен быть автором игры-песочницы. Тесты создают собственные
уровни в конце игры, проверяют контент и удаляют всё созданное; существующие
уровни не изменяются. Если игра-песочница завершилась, тесты сами продлевают
дату её окончания. CLI-тесты собирают encli во временный каталог и работают
с изолированным HOME, не трогая сессии в ~/.config/encli.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agentmcp serves the Encounter engine toolset over the Model Context Protocol so a standalone PicoClaw instance (or any MCP client) can play through the same tools the in-app agent uses.
|
Package agentmcp serves the Encounter engine toolset over the Model Context Protocol so a standalone PicoClaw instance (or any MCP client) can play through the same tools the in-app agent uses. |
|
Package agenttools exposes the Encounter engine to LLM agents as a catalog of callable tools.
|
Package agenttools exposes the Encounter engine to LLM agents as a catalog of callable tools. |
|
bindings
|
|
|
php
Package php holds the PHP bindings for encx: a C-shared library built from mobile/encxmobile and the PHP classes that call it through FFI.
|
Package php holds the PHP bindings for encx: a C-shared library built from mobile/encxmobile and the PHP classes that call it through FFI. |
|
php/cmd/encxphpgen
command
Command encxphpgen generates the PHP bindings for encx from the Go source of mobile/encxmobile.
|
Command encxphpgen generates the PHP bindings for encx from the Go source of mobile/encxmobile. |
|
php/cshared
command
Command cshared is the c-shared library the PHP bindings load through FFI.
|
Command cshared is the c-shared library the PHP bindings load through FFI. |
|
php/internal/gen
Package gen emits the C side of the PHP bindings from a surface.Model: the cgo wrapper file compiled into the c-shared library, the FFI-parsable C header, and a JSON manifest describing what was bound and what was not.
|
Package gen emits the C side of the PHP bindings from a surface.Model: the cgo wrapper file compiled into the c-shared library, the FFI-parsable C header, and a JSON manifest describing what was bound and what was not. |
|
php/internal/genphp
Package genphp emits the PHP side of the encx bindings: the Client class wrapping the opaque Go handle, and the Helpers class holding the package-level functions that need no client.
|
Package genphp emits the PHP side of the encx bindings: the Client class wrapping the opaque Go handle, and the Helpers class holding the package-level functions that need no client. |
|
php/internal/rt
Package rt provides the hand-written runtime shared by the generated PHP cgo bindings: a handle registry for live *encxmobile.EncClient values and a JSON response envelope.
|
Package rt provides the hand-written runtime shared by the generated PHP cgo bindings: a handle registry for live *encxmobile.EncClient values and a JSON response envelope. |
|
php/internal/surface
Package surface models the exported surface of the encxmobile Go package as it can be projected onto a C ABI.
|
Package surface models the exported surface of the encxmobile Go package as it can be projected onto a C ABI. |
|
cmd
|
|
|
encli
command
Command encli is a CLI tool for interacting with the Encounter (en.cx) game engine.
|
Command encli is a CLI tool for interacting with the Encounter (en.cx) game engine. |
|
encx-mock
command
|
|
|
Package encx provides a Go client for the Encounter (en.cx) game engine JSON API.
|
Package encx provides a Go client for the Encounter (en.cx) game engine JSON API. |
|
enapi
Package enapi is a low-level HTTP client for the Encounter Go Backend API — the REST engine that replaces the ASP.NET one.
|
Package enapi is a low-level HTTP client for the Encounter Go Backend API — the REST engine that replaces the ASP.NET one. |
|
mobile
|
|
|
encxmobile
Package encxmobile provides gomobile-compatible bindings for the encx Encounter API client.
|
Package encxmobile provides gomobile-compatible bindings for the encx Encounter API client. |