Skip to main content

Запуск в CI и скриптах

Veai CLI выполняет один запрос и завершается, если запустить его с флагом -p (вывод текстом) или --mode json (вывод событиями). Терминал для этого не нужен, поэтому агента можно запускать в GitHub Actions, GitLab CI, cron и shell-скриптах: для ревью диффа, разбора падений и тестирования.

Платформы

Поддерживаются Linux и macOS. В Windows запуск стартует, но не поддерживается и не проверяется.

Как вызвать​

veai -p "проверь дифф" # режим print: итоговый ответ в stdout
veai --mode json -p "перечисли файлы" # режим JSON: события JSONL в stdout
git diff | veai -p "проверь это" # текст из pipe добавляется к запросу
  • В stdout попадает только ответ (print) или события (JSON). Все сообщения самого агента идут в stderr.
  • Запрос берите в кавычки. Каждое слово без кавычек становится отдельным запросом, и напечатан будет ответ только на последний.
  • Запрос из переменной передавайте после --: veai -p -- "$PROMPT". Без -- запрос, начинающийся с -, прочитается как флаг: --permission-mode=bypassPermissions внутри $PROMPT разрешил бы выполнять любые команды. Слово с @ в начале и после -- остаётся файлом для вложения, поэтому текст, который вы не контролируете, передавайте через stdin, а не в командной строке.
  • JSON-запуск пишите как --mode json -p "<запрос>". Запрос перед флагами требует -p: veai "проверь" -p работает, а veai "проверь" --mode json отклоняется как неизвестная команда.
  • Подкоманды (update, login, plugin, tui и другие) работают только первым аргументом. В запуске с -p в другом месте они попадут к модели как запрос: veai -p update спросит модель про «update».
  • Голый veai без терминала отказывается запускаться: это терминальный интерфейс.
  • Запуск без запроса (пустой -p, пустой pipe, незаданный $PROMPT) завершается с кодом 1 до обращения к модели.
  • Если ничего не передаётся через pipe, а stdin задачи может остаться открытым, добавьте < /dev/null: агент читает stdin из pipe до конца, прежде чем стартует. Шаг GitHub Actions закрывает stdin сам.
  • Вывод команды передавайте через pipe или файл, а не через @<(git diff): движок пропускает файл нулевого размера, а подстановка процесса сообщает нулевой размер.

Модель и ключи​

Задавайте модель явно: --model <провайдер>/<модель>. Без флага агент выбирает модель сам, обычно самую мощную и дорогую у провайдера. Список моделей выводит veai --list-models (без -p, тогда список попадёт в stdout).

Приоритет, если ключ указан в нескольких местах: --api-key, auth.json, apiKey в models.json, переменная окружения провайдера. Не передавайте ключ через --api-key: он виден всем процессам на раннере и может попасть в лог шага.

Ключ провайдера из окружения​

Для известного провайдера (Anthropic, OpenAI, Google, OpenRouter, Mistral и других) файл не нужен. Задайте переменную провайдера из секрета CI, например ANTHROPIC_API_KEY, OPENAI_API_KEY или OPENROUTER_API_KEY, и укажите --model <провайдер>/<модель>.

Подписка Veai​

Вход через /login на ранере не пройти, поэтому в CI модели подписки подключаются через шлюз Veai по персональному API-ключу из личного кабинета. Опишите шлюз в models.json как своего провайдера. Значение вида "$ИМЯ" читается из переменной окружения:

{
"providers": {
"veai-gateway": {
"baseUrl": "https://api.veai.ru/openai/api/v1",
"api": "openai-completions",
"apiKey": "$VEAI_API_KEY",
"models": [{ "id": "autopilot" }]
}
}
}

Запускайте с --model veai-gateway/autopilot и задайте VEAI_API_KEY из секрета. autopilot сам подбирает модель под задачу, см. «Модели и автоподбор». Другие id моделей перечислены по адресу https://api.veai.ru/openai/api/models.

Свой провайдер​

Любой сервер с поддерживаемым API (корпоративный прокси, vLLM, LM Studio, облачный шлюз) описывается в models.json отдельным провайдером. apiKey и значения headers могут называть переменные окружения:

{
"providers": {
"corp": {
"baseUrl": "https://llm.example.com/v1",
"api": "openai-completions",
"apiKey": "$CORP_LLM_KEY",
"headers": { "X-Team": "$CORP_TEAM" },
"models": [{ "id": "qwen2.5-coder-32b" }]
}
}
}

Запускайте с --model corp/qwen2.5-coder-32b. Поле api задаёт протокол сервера: openai-completions (большинство OpenAI-совместимых серверов), openai-responses, azure-openai-responses, anthropic-messages, google-generative-ai или mistral-conversations. Как подключить свой провайдер в терминале, описано на странице «Свои провайдеры и локальные модели».

Где лежат models.json и auth.json​

Оба файла лежат в каталоге агента:

  • установка через установщик: <корень установки>/agent, по умолчанию ~/.local/share/veai-ai-agent/agent;
  • установка через npm на время задачи (см. «Закрепить версию»): $XDG_DATA_HOME/veai-ai-agent/agent, а если XDG_DATA_HOME не задана, ~/.local/share/veai-ai-agent/agent.

Переменная PI_CODING_AGENT_DIR указывает любой из вариантов на каталог по вашему выбору, например на тот, куда задача сама записывает models.json.

Разрешения​

Запуск стартует в режиме разрешений терминальной сессии: default, если permissions.defaultMode не задаёт другой (см. «Настройки»). В default вызов, который потребовал бы подтверждения, блокируется, потому что ответить некому, и модель узнаёт причину. Чтобы выполнялись все вызовы:

veai --permission-mode bypassPermissions -p "почини упавший тест"

Продолженный чат (--continue) стартует в режиме, сохранённом вместе с ним, а сохранённый afk стартует как bypassPermissions. Подробнее о режимах — на странице «Режимы разрешений».

Недоверенное содержимое

Задача, которая работает с содержимым pull request, выполняет любые инструкции из него. Пока человек, которому вы доверяете, не прочитал это содержимое, не давайте задаче секреты ни в одном режиме разрешений.

  • default блокирует только вызовы, которые спросили бы подтверждение. Остальные команды выполняются без вопросов, включая сетевые (curl) и команды, печатающие окружение.
  • Запуск загружает ресурсы проекта так же, как терминал: .veai/settings.json, правила (.veai/rules/, AGENTS.md), скиллы, вспомогательных агентов и профили моделей. Он читает и файлы правил и скиллов других агентов (CLAUDE.md, .claude/ и другие), а также запускает MCP-серверы из MCP-файлов проекта. Команды этих серверов берутся из файлов проекта и выполняются до того, как модель что-либо увидит. Подтверждения запуска проектных MCP-серверов пока нет.
  • Движок не загружает .pi-настройки, системные промпты, пакеты и расширения проекта.

Текст из pull request, issue или комментария не передавайте в командной строке. Пусть аргумент -p будет вашим фиксированным текстом, а недоверенный текст передавайте через stdin, где агент читает его как обычный текст: запишите в файл и перенаправьте < "$RUNNER_TEMP/input.txt".

В GitHub Actions никогда не вставляйте в run: выражения ${{ github.event.* }} и любые другие, значение которых контролирует кто-то другой: раннер подставляет значение в скрипт до запуска оболочки, поэтому оно может выполнить команды. Передавайте значение через env: и обращайтесь к переменной.

on: pull_request не даёт pull request из форка секретов, поэтому задача ревью там остаётся без ключа и падает. Не переключайте её на pull_request_target или на workflow_run с checkout головы pull request: оба варианта отдают секреты содержимому форка. Pull request из вашего репозитория секреты получает, поэтому в примере ниже ключ лежит в окружении GitHub с обязательными проверяющими: задача ждёт, пока проверяющий прочитает pull request и одобрит запуск, и только потом видит ключ. Создайте окружение (agent-review в примере) в Settings → Environments, добавьте себя или команду в Required reviewers, включите «Prevent self-review» и храните ключ как секрет окружения, а не репозитория.

Установка на чистый раннер​

Установщик ставит самую свежую версию выбранного канала:

curl --proto '=https' --tlsv1.2 -fsSL https://veai.ru/cli/install.sh | sh -s -- --channel stable

Без терминала установщик не добавляет ~/.local/bin в PATH, а только сообщает нужную строку. Следующий шаг вызывает ~/.local/bin/veai по полному пути или сначала добавляет каталог:

echo "$HOME/.local/bin" >> "$GITHUB_PATH"

Подробнее об установке — на странице «Установка и обновление».

Закрепить версию​

Установщик не принимает версию. Чтобы закрепить её, установите пакет во временный каталог задачи и запускайте оттуда. Для этого в PATH должен быть Node.js 22.19 или новее, например из actions/setup-node:

npm install --prefix "$RUNNER_TEMP/agent" @veai-ru/ai-agent@<версия>
exec "$RUNNER_TEMP/agent/node_modules/.bin/veai" -p -- "$PROMPT"

Такая установка ничего не добавляет в PATH и не применяет закреплённые в релизе транзитивные зависимости, поэтому они могут отличаться от управляемой установки. npx в CI не рекомендуется: npm остаётся верхним процессом шага и передаёт не все сигналы отмены.

Отмена и ограничение времени​

Делайте агента последней командой шага через exec:

exec veai -p -- "$PROMPT"

При отмене раннер посылает сигнал только оболочке шага. С exec оболочкой становится сам агент, поэтому он останавливается сразу. Без exec оболочка завершится, а агент продолжит работать и тратить токены, пока раннер не снесёт задачу. Pipe в агента (git diff | exec veai …) тоже оставляет оболочку наверху, поэтому передавайте вывод через файл:

git diff > "$RUNNER_TEMP/diff.txt"
exec veai -p "проверь" < "$RUNNER_TEMP/diff.txt"

Задайте шагу лимит времени (timeout-minutes в GitHub Actions): у агента нет собственного лимита по ходам или времени. Если агента убили жёстко (kill -9, OOM-killer), его движок замечает это примерно за секунду и останавливается сам.

Коды выхода​

ЗапускКод выхода
print: ответ получен0
print: ответ завершился ошибкой или прерыванием1
JSON: любой исход модели0, исход записан в событиях
нет запроса1
не найден ключ или движок не стартовалне 0
отмена сигналом128 + номер сигнала: 130 для SIGINT, 143 для SIGTERM

Отмена шага GitHub Actions с exec veai -p ... даёт 130: раннер сначала посылает SIGINT.

Полный пример для GitHub Actions​

name: review
on: pull_request
permissions:
contents: read
jobs:
review:
runs-on: ubuntu-latest
environment: agent-review # обязательные проверяющие; ключ лежит секретом окружения
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Установить агента
run: curl --proto '=https' --tlsv1.2 -fsSL https://veai.ru/cli/install.sh | sh -s -- --channel stable
- name: Проверить дифф
env:
VEAI_API_KEY: ${{ secrets.VEAI_API_KEY }}
BASE: ${{ github.base_ref }}
run: |
mkdir -p ~/.local/share/veai-ai-agent/agent
cat > ~/.local/share/veai-ai-agent/agent/models.json <<'EOF'
{"providers":{"veai-gateway":{"baseUrl":"https://api.veai.ru/openai/api/v1","api":"openai-completions","apiKey":"$VEAI_API_KEY","models":[{"id":"autopilot"}]}}}
EOF
git diff "origin/$BASE...HEAD" > "$RUNNER_TEMP/diff.txt"
exec ~/.local/bin/veai --model veai-gateway/autopilot -p "Проверь этот дифф" < "$RUNNER_TEMP/diff.txt"

Что дальше​

  • Workflows — многоэтапные сценарии субагентов, которые тоже запускаются без терминала: veai --workflow <имя>.
  • Режимы разрешений — как работают default, paranoid, bypassPermissions и afk.