#Выполнить команду
POST /v1/infra/servers/:id/exec
Выполняет shell-команду на BLACKHOLE-сервере через агент туннеля. Полный стандартный вывод (stdout), поток ошибок (stderr) и код возврата передаются клиенту. По умолчанию ответ — JSON после завершения команды (удобно для AI-агентов и скриптов). Если нужен построчный прогресс — передайте ?stream=true и читайте SSE-события stdout/stderr/exit.
Для AI-агентов и MCP-клиентов обязательно используйте JSON-режим (без ?stream=true) — SSE они разбирать не умеют.
#Параметры
| Параметр | В | Тип | Обяз. | По умолч. | Описание |
|---|---|---|---|---|---|
id |
path | string (UUID) | да | — | ID BLACKHOLE-сервера, status: running, blackholeStatus: CONNECTED |
stream |
query | string | нет | false |
true — SSE-режим; иначе — JSON-ответ |
#Поля запроса (body)
| Поле | Тип | Обяз. | Описание |
|---|---|---|---|
command |
string | да | Shell-команда. 1–10 000 символов |
timeout |
number | нет | Таймаут выполнения в секундах: 1–600. По умолчанию 300 |
workdir |
string | нет | Рабочая директория. До 500 символов |
env |
object | нет | Переменные окружения: { "KEY": "value" }. Только строковые значения |
#Примеры
#curl — личный ключ
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/exec?stream=false" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command": "ls -la /opt/app", "timeout": 30}'
#curl — OAuth-приложение
curl -X POST "https://vibecode.bitrix24.tech/v1/infra/servers/SERVER_ID/exec?stream=false" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{"command": "npm ci --production", "workdir": "/opt/app", "timeout": 180}'
#JavaScript — личный ключ
const res = await fetch(
`https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/exec?stream=false`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
command: 'node -v',
timeout: 10,
}),
}
)
const { data } = await res.json()
console.log(`exit ${data.exitCode} за ${data.duration}s:\n${data.stdout}`)
#JavaScript — OAuth-приложение
// Команда с переменными окружения и рабочей директорией
const res = await fetch(
`https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/exec?stream=false`,
{
method: 'POST',
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
command: 'npx tsx scripts/seed.ts',
workdir: '/opt/app',
env: { DATABASE_URL: 'postgresql://localhost/mydb' },
timeout: 60,
}),
}
)
#Поля ответа
| Поле | Тип | Описание |
|---|---|---|
success |
boolean | true если команда выполнилась (включая ненулевой exitCode) |
data.exitCode |
number | Код возврата процесса |
data.stdout |
string | Стандартный вывод команды |
data.stderr |
string | Поток ошибок команды |
data.duration |
number | Время выполнения в секундах |
data.truncated |
boolean | true если stdout/stderr обрезаны по размеру (5 МБ на поток) |
#Пример ответа
{
"success": true,
"data": {
"exitCode": 0,
"stdout": "Linux epd65hdv07p8g89c0c06 6.8.0-107-generic #107-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 13 19:51:50 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux\n",
"stderr": "",
"duration": 5,
"truncated": false
}
}
#Пример ответа при ошибке
409 — на сервере уже выполняется другая операция:
{
"success": false,
"error": {
"code": "EXEC_BUSY",
"message": "Another operation is running on this server"
}
}
#Ошибки
| HTTP | Код | Описание |
|---|---|---|
| 400 | VALIDATION_ERROR |
Нарушена схема запроса (пустая команда, неверный таймаут, слишком длинные поля) |
| 400 | NOT_BLACKHOLE |
Сервер в режиме OPEN — Deploy API недоступен |
| 401 | MISSING_API_KEY |
Не передан заголовок X-Api-Key |
| 401 | INVALID_API_KEY |
Неверный или просроченный API-ключ |
| 404 | NOT_FOUND |
Сервер не существует, удалён или принадлежит другому API-ключу |
| 409 | EXEC_BUSY |
На сервере уже выполняется другая /exec или /deploy. Подождите или снимите лок через `/lock` |
| 409 | AGENT_NOT_CONNECTED |
Агент туннеля не в статусе CONNECTED — попробуйте `/repair` |
| 429 | RATE_LIMIT_EXCEEDED |
Превышен лимит 10 операций в минуту на сервер |
| 500 | EXEC_FAILED |
Ошибка выполнения на агенте |
| 502 | GATEWAY_ERROR |
Gateway вернул ошибку при связи с агентом |
| 504 | EXEC_TIMEOUT |
Превышен timeout (или стандартные 300 секунд). Агент убивает процесс |
Полный список общих ошибок API — Ошибки.
#Известные особенности
- Блокировка на уровне сервера. Пока идёт
/execили/deploy, второй такой вызов вернёт 409EXEC_BUSY. Если клиент оборвал соединение и не дождался таймаута, снимите зависший лок через `DELETE /lock`. - Максимальный размер
stdout/stderr— 5 МБ на поток. Если вывод команды больше, приходитtruncated: trueи хвост обрезается. Команда при этом отработала,exitCodeкорректный. Для большого вывода перенаправляйте в файл:command: "my-cmd > /opt/app/output.log 2>&1"и потом читайте через/exec cat /opt/app/output.log. .envиз/deployне подхватывается автоматически. Systemd подгрузит.envпри старте сервиса, но не для разовых команд через/exec. Если нужныDATABASE_URL/ API-ключи — передавайте в полеenv:{ env: { DATABASE_URL: "..." } }.- Два таймаута в сумме дают
timeout + 30секунд. Агент убивает процесс ровно поtimeout; Gateway ждёт ещё 30 секунд на финальные события и только потом отдаётEXEC_TIMEOUT. - Поддержание соединения в JSON-режиме. Если команда выполняется дольше 15 секунд, сервер периодически шлёт пробелы в тело ответа — это предотвращает таймауты nginx и промежуточных прокси. JSON-парсер игнорирует начальные и конечные пробелы, так что клиент просто прочитает корректный JSON по окончании.
#Смотрите также
- Полный деплой —
POST /v1/infra/servers/:id/deploy— цепочка нескольких/execс проверкой работоспособности. - Снять зависший лок —
DELETE /v1/infra/servers/:id/lock— еслиEXEC_BUSYпосле обрыва. - Логи сервиса —
GET /v1/infra/servers/:id/logs— читать системный журнал черезjournalctl. - Загрузить файл —
POST /v1/infra/servers/:id/upload— записать файл на сервер. - Восстановить туннель — если
AGENT_NOT_CONNECTED.