Самостоятельно размещённые среды находятся в публичной бета-версии на планах Team и Enterprise; раздел Доступность и ограничения охватывает путь включения. Эта страница — рецепт для CI-тестирования; см. краткое руководство для настройки и Развёртывание в production для рецептов флота.
Установите 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. Тестовый скрипт ниже также требует её.
E2E_REPLY_DIR, что безвредно на одноразовом CI runner, но не то, что нужно переносить в образ runner production-окружения, где переменная может быть установлена случайно.
Запустите тестовый цикл
Флаги dispatch--environment и --ref требуют Claude Code v2.1.224 или позже на машине, которая запускает скрипт, то же минимальное требование, что и для самого runner. С установленным hook и запущенным runner на этом хосте тестовый скрипт:
- Создаёт сеанс в тестовом окружении с помощью
claude -p "<prompt>" --environment <environment-id> --output-format json, запущенной из git-репозитория, чтобы CLI мог автоматически обнаружить репозиторий из удалённогоorigin. Опциональный флаг--ref <branch>основывает checkout сеанса на именованном ref вместо локального HEAD. Команда создаёт сеанс, выводит одну строку JSON, содержащуюsession_id, и выходит без ожидания ответа Claude. - Ждёт, пока ответ появится в
$E2E_REPLY_DIR/<session_id>.txt, записанный hook Stop на runner после завершения хода. - Отправляет дополнительный вопрос с помощью
claude -p "<message>" --cloud <session_id> --output-format json(см. Отправка дополнительного сообщения в работающий сеанс), который отправляет событие пользователя в существующий сеанс и выходит. - Ждёт ответа на дополнительный вопрос так же, как на шаге 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.
TURN1/TURN2 и маркеры EXPECT1/EXPECT2 на всё, что проверяет вашу настройку, например попросите Claude запустить один из ваших пользовательских инструментов MCP и проверьте его вывод.
Удалённые тестовые runner
Если ваши тестовые runner находятся на отдельной инфраструктуре, например на постоянном флоте Kubernetes, с которым ваша CI-задача не может поделиться файловой системой, замените запись файла в hook Stop на POST к конечной точке, которую слушает ваш драйвер:Аутентификация из 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 рассматривает @- как буквальный заголовок и отправляет запрос без авторизации.
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 захвата, затем запустите тестовый скрипт.