Для AI-агентов: markdown этой страницы — /docs-content/infra/deploy/operation-status.md индекс документации — /llms.txt

Исход операции

GET /v1/infra/operations/:operationId

Возвращает исход конкретного запуска по его идентификатору. Ручка обслуживает два вида операций — выкладку POST /deploy и команду POST /exec. Нужна, когда соединение оборвалось и терминальный ответ до клиента не дошёл: по идентификатору можно узнать, чем всё кончилось, вместо того чтобы запускать вызов заново.

Для команды это единственный способ узнать исход после обрыва. Транспортный таймаут прекращает только ожидание на стороне клиента, сама команда на сервере продолжает выполняться и может завершиться уже после того, как клиент получил ошибку. Поэтому повторный запуск изменяющей команды может применить изменение второй раз.

Ручка только на чтение, побочных действий у неё нет. Каждый запуск адресуется отдельно, включая предыдущие попытки на том же сервере. Исход хранится 7 суток.

Идентификатор есть у обоих видов выкладки, но момент его выдачи различается:

  • для отдельной виртуальной машины (kind: "STANDALONE") запись заводится до конвейера. Если машина спит, во время блокирующего пробуждения строка уже видна как running в списке операций сервера, но HTTP-статус, заголовок и SSE-кадр по этому соединению не приходят до завершения wake. Заголовок и первый SSE-кадр приходят до первого шага выкладки;
  • для galaxy-приложения (kind: "GALAXY_APP") запись заводится после захвата mutex приложения, при входе в первый шаг. Идентификатор приходит только с терминальным JSON-ответом в заголовке и теле. Если соединение оборвалось раньше, найдите запуск через список операций сервера.

У команды POST /exec запись заводится после захвата блокировки сервера, до отправки агенту. В длительном режиме async=true только STANDALONE возвращает идентификатор в заголовке и теле немедленного 202 после ограниченного ожидания подтверждения запуска; команда продолжает работу независимо. Синхронный exec отдельной машины отправляет заголовок до ожидания завершения. Синхронный exec galaxy-приложения — только с терминальным ответом: при обрыве ожидания идентификатор может не прийти, тогда найдите запуск через список операций сервера с ?kind=exec.

Где взять идентификатор — три канала, все три несёт один и тот же запуск:

  • заголовок ответа X-Vibe-Operation-Id — у команды и у выкладки на уже доступной отдельной машине приходит сразу, у выкладки на спящей отдельной машине задерживается до конца блокирующего пробуждения, у синхронного exec и выкладки galaxy-приложения приходит только с терминальным ответом; длительный exec с async=true возвращает его с 202 только на STANDALONE,
  • первый кадр event: operation при ?stream=true — у синхронного exec и у выкладки на отдельной машине, но только после возможного блокирующего пробуждения. У длительного exec SSE нет. Браузерный EventSource заголовков ответа не отдаёт, поэтому для него это единственный доконвейерный канал,
  • поле operationId в теле — data.operationId при успехе, error.operationId при отказе.

Отсутствие идентификатора не означает, что выкладка не запускалась. Связь со спящей отдельной машиной могла оборваться во время блокирующего пробуждения, связь с galaxy могла оборваться до терминального ответа, либо платформа могла не суметь завести запись и продолжить без идентификатора. Не повторяйте выкладку вслепую: сначала запросите GET /v1/infra/servers/:id/operations, затем сверьте состояние сервера и логи.

Длительный exec доступен только отдельной машине. async=true принимает таймаут от 1 до 86400 секунд на STANDALONE. Другие виды сервера получают 400 DURABLE_EXEC_UNSUPPORTED_SERVER_KIND до блокировки, создания операции и отправки команды, без перехода к синхронному исполнению. Exec Galaxy остаётся синхронным с пределом 600 секунд. Работающая длительная задача отдельной машины занимает канал после 202; опрашивайте эту операцию вместо повторного запуска. Неизвестный исход требует проверки и подтверждения остановки. /unstick и удаление лока не снимают ограничение приёма длительной задачи.

Параметры

Параметр В Тип Обяз. Описание
operationId path string да Идентификатор операции из ответа POST /deploy или POST /exec

Примеры

curl — личный ключ

Terminal
curl -H "X-Api-Key: YOUR_API_KEY" \
  https://vibecode.bitrix24.tech/v1/infra/operations/OPERATION_ID

curl — OAuth-приложение

Terminal
curl -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  https://vibecode.bitrix24.tech/v1/infra/operations/OPERATION_ID

JavaScript — личный ключ

javascript
// Выкладка: после возможного блокирующего wake забираем id из заголовка ДО тела.
// Если связь оборвалась во время wake, ищем id через список операций сервера.
const deploy = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/servers/${serverId}/deploy`,
  {
    method: 'POST',
    headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
    body: JSON.stringify({ source: { content: archiveBase64 }, start: 'node index.js' }),
  }
)
const operationId = deploy.headers.get('X-Vibe-Operation-Id')

// Связь оборвалась — спрашиваем исход вместо повторной выкладки
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/operations/${operationId}`,
  { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)

if (res.status === 410) {
  console.log('Операция была, исход больше не хранится')
} else if (res.status === 404) {
  console.log('Такой операции нет')
} else {
  const { data } = await res.json()
  console.log(`Статус: ${data.status}, шаг: ${data.step}`)
}

JavaScript — OAuth-приложение

javascript
const res = await fetch(
  `https://vibecode.bitrix24.tech/v1/infra/operations/${operationId}`,
  {
    headers: {
      'X-Api-Key': 'YOUR_APP_KEY',
      'Authorization': 'Bearer USER_SESSION_TOKEN',
    },
  }
)

Поля ответа

Поле Тип Описание
success boolean Всегда true при успехе
data.operationId string Идентификатор операции
data.kind string Вид операции: deploy или exec
data.serverId string ID сервера, на котором шёл запуск
data.status string running, succeeded, failed или unknown — см. таблицу ниже
data.step string | null Шаг конвейера, на котором операция находится или на котором остановилась. У kind: "exec" всегда null — у команды шагов нет
data.exitCode number | null Только при kind: "exec". Код возврата команды, если агент его подтвердил, иначе null. Число приходит только вместе со статусом succeeded — см. правило про пару под таблицей статусов
data.stdout string (необяз.) Сохранённый стандартный вывод длительного exec, включая частичный вывод при unknown, если он доступен; только для ключа в области операции, принадлежащего пользователю-создателю
data.stderr string (необяз.) Сохранённый поток ошибок длительного exec; та же приватная область, что у stdout
data.duration number (необяз.) Сохранённая длительность exec в миллисекундах; та же приватная область, что у stdout
data.truncated boolean (необяз.) Достигнут ли общий предел сохранённого stdout+stderr 1 МиБ; та же приватная область, что у stdout
data.startedAt string Время открытия записи операции, ISO 8601. Для длительного exec не подтверждает запуск процесса
data.finishedAt string | null Время завершения, ISO 8601. null, пока операция идёт
data.error object | null { code, message } при status: "failed", иначе null

Значения data.status:

Значение Что означает
running Терминальный исход операции ещё не получен. Длительная команда может выполняться или ожидать подтверждения запуска, в том числе после потери ACK; статус не доказывает, что процесс запущен. Опрашивайте ту же операцию, не отправляйте команду повторно
succeeded У выкладки означает, что она завершилась успешно. У команды означает, что она отработала до конца, а её собственный вердикт лежит в data.exitCode — ненулевой код возврата приходит с этим же статусом, потому что статус отвечает на вопрос «выполнилась», а не «с каким результатом»
failed Причина — в data.error. У выкладки означает, что она провалилась. У команды означает, что она не запускалась: этот статус ставится, только когда платформа может это доказать, — например при EXEC_BUSY, когда агент был занят другой командой. Это доказывает только отказ этой попытке; при EXEC_BUSY до повтора сверьте занявшую канал операцию и прежние попытки с потерянным подтверждением
unknown Платформа не может доказать удалённый исход: транспорт оборвался либо процесс прервался между стартом и записью исхода. Запись операции существует, но она не доказывает ни запуск команды, ни её остановку. Для команды это означает, что она может выполняться на сервере прямо сейчас, поэтому перед повторным запуском изменяющей команды сверьтесь с состоянием сервера в GET /v1/infra/servers/:id и журналом

Судите по паре data.exitCode + data.status, а не по одному полю. Число приходит только у succeeded и означает, что команда отработала до конца. Само по себе null не означает «исход не доказан»: у succeeded оно означает, что команда отработала, а кода агент не назвал, — повторять её не нужно; у failed — что эта попытка не запускалась; при EXEC_BUSY до повтора всё равно нужно сверить занявшую канал операцию; у running — что попытка ожидает завершения или подтверждения запуска, поэтому опрашивайте ту же операцию; у unknown — что исход не доказан.

Что хранится, а что нет. Запись синхронной команды несёт исход попытки — статус, код возврата и причину отказа — без stdout/stderr. Для длительной команды с зашифрованным сохранённым выводом используйте async=true. Эта ручка отдаёт вывод только когда текущий ключ в области операции принадлежит пользователю-создателю. Ротация ключа того же владельца сохраняет доступ; передача другому владельцу переносит метаданные операции, но не доступ к её приватному выводу. Записи без сохранённой личности создателя отдают только метаданные. Чтение по серверу никогда не отдаёт вывод команды.

Коды отказа длительного exec

Для kind: "exec" и status: "failed" доказано, что команда не запускалась. data.error.code может обозначать отказ приёма, уже возвращённый POST, или отказ до запуска, обнаруженный после HTTP 202. HTTP-статус этого GET остаётся 200; коды описывают записанную попытку команды, а не сбой чтения статуса. Подтвердите статус операции до нового исправленного запроса. При EXEC_BUSY прочитайте список операций сервера с kind=exec и сверьте занявшую канал попытку, включая потерянные подтверждения. Известную выполняющуюся операцию дождитесь; неизвестный исход требует проверки состояния и журналов либо обращения в поддержку вместо нового exec.

Код Условие Действие клиента
EXEC_JOB_STORAGE_UNAVAILABLE Не удалось сохранить операцию или её адрес назначения Проверьте операцию, если получен идентификатор; дождитесь доступности хранения до повтора
EXEC_BUSY Приём исполнения занят Дождитесь известной работающей задачи; при неизвестном исходе обратитесь в поддержку
EXEC_JOB_CAPACITY Хост уже хранит 64 записи задач Дождитесь допустимой очистки по сроку хранения или обратитесь в поддержку; автоматического повтора нет
EXEC_JOB_UNAVAILABLE Недоступны приём, хранение или запуск длительных задач Устраните недоступность с поддержкой; не заменяйте запрос синхронным исполнением
EXEC_JOB_INVALID Агент отклонил данные или окно запуска Проверьте записанный отказ и запрос; при отказе корректному запросу обратитесь в поддержку
EXEC_JOB_REJECTED Приём отклонён без отдельного кода Обратитесь в поддержку
UNKNOWN_TYPE Исполнение не поддерживает длительные задачи Обратитесь в поддержку для обновления поддержки исполнения; синхронного перехода нет
EXEC_JOB_START_EXPIRED Окно запуска истекло до старта пользовательской команды Новый запуск безопасен после подтверждения этого отказа; при повторном отказе обратитесь в поддержку
EXEC_WORKDIR_DENIED Рабочая директория нарушает правила пути Выберите разрешённую директорию; правила описаны в exec
EXEC_WORKDIR_NOT_FOUND Рабочая директория отсутствует или недоступна для проверки Укажите существующую доступную директорию
EXEC_WORKDIR_NOT_DIR Рабочий путь не является директорией Укажите директорию
EXEC_FAILED Не удалось запустить интерпретатор команды отдельной машины Проверьте окружение и обратитесь в поддержку до нового запуска

Истёкший таймаут или потеря процесса отдельной машины могут дать status: "unknown", частичный сохранённый вывод и data.error: null. Это не отказ до запуска и не разрешение на новый exec. Сверьте состояние и логи, подтвердите остановку исполнения и при необходимости обратитесь в поддержку. TTL, разрыв туннеля, /unstick, force и удаление лока не снимают ограничение приёма длительной задачи.

Пример ответа

Выкладка идёт:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "running",
    "step": "install",
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": null,
    "error": null
  }
}

Выкладка завершилась успешно:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "succeeded",
    "step": null,
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": "2026-08-11T09:18:04.902Z",
    "error": null
  }
}

Выкладка провалилась:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5p",
    "kind": "deploy",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "failed",
    "step": "healthcheck",
    "startedAt": "2026-08-11T09:14:22.117Z",
    "finishedAt": "2026-08-11T09:17:41.338Z",
    "error": {
      "code": "DEPLOY_STEP_FAILED",
      "message": "connect ECONNREFUSED 127.0.0.1:3000"
    }
  }
}

Длительная команда отработала до конца; сохранённые поля показаны создателю через ключ в области операции:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5q",
    "kind": "exec",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "succeeded",
    "step": null,
    "exitCode": 0,
    "stdout": "maintenance complete\n",
    "stderr": "",
    "duration": 275000,
    "truncated": false,
    "startedAt": "2026-08-11T09:20:02.004Z",
    "finishedAt": "2026-08-11T09:24:37.815Z",
    "error": null
  }
}

Ожидание команды оборвалось, исход неизвестен:

JSON
{
  "success": true,
  "data": {
    "operationId": "clz9k2m4x0001qw8h3f7d2n5r",
    "kind": "exec",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "status": "unknown",
    "step": null,
    "exitCode": null,
    "startedAt": "2026-08-11T09:31:10.220Z",
    "finishedAt": "2026-08-11T09:36:41.905Z",
    "error": null
  }
}

Пример ответа при ошибке

410 — операция была, но исход уже не хранится:

JSON
{
  "success": false,
  "error": {
    "code": "OPERATION_OUTCOME_EXPIRED",
    "message": "The outcome of this deploy is no longer stored (kept for 7 days).",
    "serverId": "8f14e45f-ceea-467a-9c8d-2f1c5a6b7e30",
    "startedAt": "2026-07-30T11:02:19.400Z"
  }
}

Ошибки

HTTP Код Описание
400 INVALID_OPERATION_ID Идентификатор не той формы, которую выдаёт платформа
401 MISSING_API_KEY Не передан заголовок X-Api-Key
401 INVALID_API_KEY Ключ не опознан: такой строки на платформе нет
404 OPERATION_NOT_FOUND Операции нет, она принадлежит другому ключу либо запись уже удалена
410 OPERATION_OUTCOME_EXPIRED Операция ваша, но её исход больше не хранится
429 RATE_LIMITED Превышен лимит опроса

Полный список общих ошибок API — Ошибки.

Известные особенности

  • Чужая операция отвечает так же, как несуществующая — это сделано намеренно. Все три случая (идентификатора нет, идентификатор чужой, запись удалена) дают один и тот же 404. Отдельный ответ на чужую операцию подтверждал бы, что такой идентификатор существует, а идентификатор сам по себе является ключом доступа к записи: подобрав его, можно было бы прочитать чужую выкладку.
  • 410 отличается от 404 только для владельца. «Исход больше не хранится» означает, что запись операции принадлежала вашему ключу, а окно исхода истекло через семь суток после терминального исхода. Это не доказывает запуск команды или выкладки: запись операции бывает и у отклонённой попытки. На чужой или выдуманный идентификатор такой ответ не приходит никогда.
  • Запись живёт дольше, чем хранится исход. Через 7 суток исход перестаёт отдаваться, но сам факт операции сохраняется ещё некоторое время — за счёт этого и возможен различимый ответ 410. Потом запись удаляется, и тот же идентификатор начинает отвечать 404.
  • unknown — это ответ, а не ошибка. Запись операции существует, но из-за обрыва транспорта или процесса платформа не может доказать запуск команды или её исход. Сначала сверьте текущее состояние сервера и логи; повторяйте выкладку только после такой сверки.
  • Лимит опроса — 60 запросов в минуту на ключ. Точное значение — в заголовке x-ratelimit-limit (потолок делится на реплики). Исход не меняется чаще, чем идёт сама выкладка, поэтому опрашивать раз в несколько секунд достаточно.
  • Читайте через ключ, которому сейчас принадлежит операция. Ротация или передача ключа переносит метаданные операции на ключ-замену. Сохранённый вывод команды доступен только если этот ключ принадлежит пользователю-создателю: ротация того же владельца сохраняет доступ к выводу, а передача другому владельцу отдаёт только метаданные. Владелец сервера под ключом вне области операции и участник команды разработки с правом настроек читают метаданные через GET /v1/infra/servers/:id/operations/:operationId.

Смотрите также