Skip to main content
Самостоятельно размещённые среды находятся в публичной бета-версии на планах Team и Enterprise; раздел Доступность и ограничения охватывает путь включения. Эта страница — рецепт для CI-тестирования; см. краткое руководство для настройки и Развёртывание в production для рецептов флота.
В самостоятельно размещённой среде облачные сеансы Claude Code работают на образе runner, который вы создаёте и поддерживаете. Перед развёртыванием нового образа в вашу production-среду запустите полный сеанс против тестовой среды из скрипта: создайте сеанс, прочитайте ответ Claude, отправьте дополнительный вопрос и прочитайте этот ответ тоже. Это форма CI smoke-теста, который проверяет ваш образ runner, доступ к git и любые пользовательские инструменты перед тем, как вы продвинете изменение. Этот рецепт предполагает, что вы уже настроили среду и runner, и что ваша CI-задача запускает процесс runner на том же хосте, что и тестовый скрипт — это естественная настройка для тестирования нового образа runner. Hook Stop, который вы устанавливаете на runner, записывает финальный ответ каждого хода в локальный файл, и скрипт читает его оттуда, поэтому единственные вызовы к API Anthropic — это две сами отправки. Если ваши тестовые runner находятся на отдельной инфраструктуре, см. Удалённые тестовые runner.

Установите hook захвата на ваш тестовый runner

Обратное чтение работает через Claude Code hook Stop: когда Claude завершает ход, hook получает финальное сообщение ассистента как last_assistant_message в JSON stdin и добавляет его в $E2E_REPLY_DIR/<session_id>.txt. Установите его так же, как hook Stop commit-nudge, в ~/.claude/ на хосте runner, который runner вносит в каждый сеанс.

Сохраните файлы hook

Сохраните два файла ниже на хосте runner:
  • Блок настроек: объедините в ~/.claude/settings.json на хосте runner
  • Скрипт: сохраните как ~/.claude/hooks/e2e-stop-hook-capture.sh на хосте runner и сделайте его исполняемым

Перед запуском runner

Две вещи, от которых зависит hook:
  • Установите его перед запуском runner. Runner создаёт снимок ~/.claude/ один раз при запуске, поэтому hook, добавленный к работающему runner, вступает в силу только после перезагрузки.
  • Экспортируйте E2E_REPLY_DIR в процесс runner. Hook не выполняет никаких действий, когда переменная не установлена или каталог не существует, поэтому установите её везде, где вы запускаете runner, например в модуле systemd, спецификации pod или шаге CI. Тестовый скрипт ниже также требует её.
Установите этот hook только на runner, обслуживающих ваше тестовое окружение. Он записывает финальный ответ каждого сеанса на диск всякий раз, когда существует E2E_REPLY_DIR, что безвредно на одноразовом CI runner, но не то, что нужно переносить в образ runner production-окружения, где переменная может быть установлена случайно.

Запустите тестовый цикл

Флаги dispatch --environment и --ref требуют Claude Code v2.1.224 или позже на машине, которая запускает скрипт, то же минимальное требование, что и для самого runner. С установленным hook и запущенным runner на этом хосте тестовый скрипт:
  1. Создаёт сеанс в тестовом окружении с помощью claude -p "<prompt>" --environment <environment-id> --output-format json, запущенной из git-репозитория, чтобы CLI мог автоматически обнаружить репозиторий из удалённого origin. Опциональный флаг --ref <branch> основывает checkout сеанса на именованном ref вместо локального HEAD. Команда создаёт сеанс, выводит одну строку JSON, содержащую session_id, и выходит без ожидания ответа Claude.
  2. Ждёт, пока ответ появится в $E2E_REPLY_DIR/<session_id>.txt, записанный hook Stop на runner после завершения хода.
  3. Отправляет дополнительный вопрос с помощью claude -p "<message>" --cloud <session_id> --output-format json (см. Отправка дополнительного сообщения в работающий сеанс), который отправляет событие пользователя в существующий сеанс и выходит.
  4. Ждёт ответа на дополнительный вопрос так же, как на шаге 2.

Поведение dispatch --environment

Claude Code создаёт сеанс, выводит ID сеанса и ссылку на него, и выходит. Флаг имеет приоритет над параметром remote.defaultEnvironmentId. Он не поддерживает --output-format stream-json и не может быть объединён с флагами, которые возобновляют, присоединяются к или предварительно настраивают сеанс, такими как --resume, --continue, --teleport, --session-id или --init-only. --cloud отклоняется с ID сеанса или URL, и в неинтерактивных запусках, когда он содержит описание. Голый --cloud рассматривается как отсутствующий. Из терминала вы можете передать задачу как описание --cloud вместо позиционного prompt.

Пример скрипта

Скрипт ниже запускает полный цикл против $CLAUDE_TEST_ENVIRONMENT_ID, ID ccpool_... вашего тестового окружения, показанный в диалоговом окне деталей окружения на странице администратора или возвращённый вызовом create-environment, и проверяет наличие фразы-маркера в каждом ответе. Запустите его из git-репозитория, который вы хотите, чтобы сеанс работал, после запуска runner на этом хосте с установленным hook захвата и экспортированным E2E_REPLY_DIR.
Замените prompts TURN1/TURN2 и маркеры EXPECT1/EXPECT2 на всё, что проверяет вашу настройку, например попросите Claude запустить один из ваших пользовательских инструментов MCP и проверьте его вывод.

Удалённые тестовые runner

Если ваши тестовые runner находятся на отдельной инфраструктуре, например на постоянном флоте Kubernetes, с которым ваша CI-задача не может поделиться файловой системой, замените запись файла в hook Stop на POST к конечной точке, которую слушает ваш драйвер:
На стороне драйвера запустите что-нибудь, что принимает POST и удерживает ответ до тех пор, пока тест его не запросит, например небольшой HTTP-слушатель внутри CI-задачи или приёмник webhook, который вы уже запускаете. Hook работает на вашей инфраструктуре, поэтому конечная точка должна быть доступна только из ваших runner.

Аутентификация из CI

Как claude -p ... --environment, так и claude -p ... --cloud аутентифицируются с помощью токена OAuth claude.ai; ключи API, такие как sk-ant-xxxxx, не принимаются ни для одного из вызовов. Два подхода делают токен доступным в CI.

Долгоживущий хост CI

Запустите claude auth login один раз интерактивно на машине, которая выполняет скрипт, используя выделенную учётную запись пользователя для автоматизации. Claude Code хранит токен в OS keychain на macOS или в ~/.claude/.credentials.json на Linux и Windows. На хосте macOS, чей Keychain не может быть записан, как это типично в SSH-сеансе, где Keychain входа остаётся заблокированным, Claude Code также хранит токен в ~/.claude/.credentials.json. См. Управление учётными данными. CLI автоматически обновляет краткосрочный токен доступа при каждом вызове, но базовый грант refresh-token ограничен 30 днями с момента первоначального входа, поэтому повторно запустите claude auth login интерактивно на этом хосте каждые 30 дней.

Эфемерные CI runner

На сегодняшний день нет долгоживущего CI-токена для этого. Область, которая предоставляет управление удалённым сеансом, user:sessions:claude_code, ограничена на сервере 30 днями, поэтому claude setup-token, который выпускает токен только для вывода на один год, не охватывает это. Секрет окружения также не принимается, так как он только авторизует runner на регистрацию в окружении, а не на создание сеансов. Чтобы предоставить сохранённый вход на эфемерный runner, установите CLAUDE_CODE_OAUTH_REFRESH_TOKEN и CLAUDE_CODE_OAUTH_SCOPES, чтобы claude auth login обменял токен без браузера; то же ограничение 30 дней применяется к гранту refresh. Свяжитесь с вашей командой учётных записей Anthropic, если вам нужен путь идентификации машины, который не привязан к учётной записи человека.

Создайте выделенное тестовое окружение

Создавайте и удаляйте окружения программно, чтобы каждый запуск CI получал чистое; runner, который запускает ваша CI-задача, регистрируется в свежем окружении. Вызовы create и delete ниже — это те же конечные точки, которые использует страница администратора Cloud environments на claude.ai, и они требуют заголовка anthropic-beta: ccr-byoc-2025-07-29.

Выпустите токен администратора

$ADMIN_TOKEN — это токен доступа OAuth claude.ai для учётной записи, которая имеет роль Owner, выпущенный так же, как Аутентификация из CI:
  • Выпустите его: запустите claude auth login с учётной записью, которая имеет роль Owner, затем прочитайте текущий токен доступа из того места, где Долгоживущий хост CI говорит, что Claude Code его сохранил.
  • Прочитайте его свежим при каждом запуске: CLI ротирует токен доступа, и то же ограничение 30 дней для гранта refresh применяется, поэтому не сохраняйте копию.
  • Передайте его через stdin: как делает пример, чтобы токен никогда не попал в список аргументов curl или ваш журнал сборки.

Создайте окружение

Захватите ответ без его вывода: pool_secret — это долгоживущее учётное данные, которое может регистрировать runner в окружении, поэтому сохраните его как замаскированный секрет CI и выводите только ID окружения. Форма -H @-, которая держит токен вне списка процессов, требует curl 7.55 или позже; более старый curl рассматривает @- как буквальный заголовок и отправляет запрос без авторизации.
Пока Owner не включит Allow self-hosted environments для организации, вызов завершится с ошибкой 403 permission_error, читающей self-hosted runners are disabled by your organization's policy. Запустите runner на этом хосте с SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET=$ENVIRONMENT_SECRET, плюс hook захвата и E2E_REPLY_DIR согласно Установите hook захвата, затем запустите тестовый скрипт.

Удалите окружение

Удалите окружение после завершения запуска, чтобы каждый запуск CI начинался чистым: