Коротко (TL;DR)
codex exec— это тот же агент Codex, только без интерфейса: задача приходит аргументом или через stdin, прогресс уходит в stderr, а в stdout остаётся финальный ответ. Официальное описание режима — на странице неинтерактивного режима Codex.- Конвейеру нужны две вещи:
--jsonи код возврата. По замеру на версии 0.147.0 (13 августа 2026) провал хода даёт код 1 и событиеturn.failedв потоке, а ошибка в флагах — код 2 ещё до обращения к сети. - Первая ловушка не в правах, а в stdin. Если поток ввода открыт,
codex exec "задача"пишет «Reading additional input from stdin…» и ждёт бесконечно. Лечится добавлением< /dev/null. - В CI ключ передают на одну команду, а не на весь job — переменной
CODEX_API_KEY. Для GitHub есть официальный экшен, который прячет ключ за прокси и снимаетsudoс раннера. - Один прогон стоит центы, и это считается точно. Сверка тарифной сетки кредитов с ценами API даёт курс 1 кредит = $0,04, а типовой ночной прогон на GPT-5.6 Terra выходит около $0,18.
- Данные актуальны на 13 августа 2026 и проверены на Codex CLI 0.147.0 (релиз 7 августа 2026). Версии здесь стареют быстро: три официальных рецепта уже разошлись с бинарником — разбор в конце.
Что делает codex exec и чем отличается от Codex в терминале
codex exec — это запуск Codex без текстового интерфейса: команда получает задачу, выполняет её до конца и завершается, возвращая процессу код выхода. В справке версии 0.147.0 она описана буквально как «Run Codex non-interactively» и имеет короткий алиас codex e.
- Коротко (TL;DR)
- Что делает codex exec и чем отличается от Codex в терминале
- Три пути запустить Codex без человека: exec, SDK и GitHub Action
- Флаги codex exec, от которых зависит судьба прогона в CI
- Машиночитаемый вывод: поток JSONL, схема ответа и коды возврата
- Почему codex exec зависает в раннере и лечится одним /dev/null
- Аутентификация автоматического прогона: ключ, а не аккаунт
- Codex GitHub Action: прокси для ключа и снятие sudo с раннера
- Codex в GitLab CI и любом другом раннере: рецепт на голом CLI
- Права и сеть автономного прогона: песочница и профили доступа
- Сколько стоит один прогон: курс кредита и расчёт по токенам
- Отказы, ретраи и таймауты: что Codex делает сам, а что должен CI
- Устаревшие рецепты: —full-auto и имена входов Codex Action
- Три рецепта: автофикс тестов, changelog и правка по репозиторию
- Codex SDK на TypeScript и Python: когда код лучше, чем exec
- Риски автономного прогона: утечка ключа и чужой текст в задаче
- FAQ
Разница с интерактивным циклом в терминале не в силе агента, а в том, кто принимает решения. В интерактиве вы видите план, подтверждаете команды и правите курс. В exec подтверждать некому: агент отработает ровно с теми правами и по тем правилам, которые вы задали до запуска.
Задачу можно передать тремя способами, и они не взаимозаменяемы:
# 1. Промпт аргументом — обычный случай для CI
codex exec "проверь миграции на обратную совместимость"
# 2. Промпт из stdin: сентинел «-» говорит «читай задачу целиком отсюда»
cat prompt.txt | codex exec -
# 3. Промпт аргументом + данные через трубу: stdin приедет как блок <stdin>
npm test 2>&1 | codex exec "суммируй упавшие тесты и предложи минимальную правку"
Третий способ — самый недооценённый. Он превращает агента в обычный элемент конвейера: слева команда, которая производит данные, справа — инструмент, который их разбирает. Именно так пишут сводки по логам, разборы упавших сборок и черновики комментариев к pull request.
Потоки разведены намеренно: прогресс печатается в stderr, а в stdout попадает только финальное сообщение. Поэтому codex exec "..." > result.md даёт чистый файл без служебного шума, а не перемешанный лог.
Три пути запустить Codex без человека: exec, SDK и GitHub Action
Поверхностей три, и выбирать между ними нужно осознанно. Неочевидное здесь то, что все три ведут в один и тот же бинарник: TypeScript SDK, по его собственному README, запускает процесс codex и обменивается с ним событиями JSONL через stdin и stdout, а официальный экшен просто ставит CLI и вызывает codex exec. Меняется обвязка, а не движок.Поверхность Что это Когда брать Чем платит codex execкоманда CLI job уже живёт в shell-скрипте; нужен минимум зависимостей сами разбираете JSONL и коды возврата Codex SDK библиотека для TypeScript или Python Codex — движок внутри вашего продукта или сложного оркестратора нужен рантайм Node 18+ или Python 3.10+ openai/codex-actionготовый экшен GitHub конвейер живёт в GitHub Actions меньше контроля, зато ключ защищён из коробки codex cloud execотправка задачи в облако работа долгая, раннер держать не хочется помечена экспериментальной, нужен ID окружения
Четвёртая строка часто выпадает из обзоров. Команда codex cloud exec --env <ENV_ID> "задача" отправляет работу в облачные задачи Codex прямо из скрипта, поддерживает --branch и --attempts (несколько попыток с выбором лучшей). По справке 0.147.0 вся ветка codex cloud помечена как экспериментальная — в критичный конвейер её ставить рано, но для фоновых задач она снимает вопрос «где держать раннер».
Флаги codex exec, от которых зависит судьба прогона в CI
Справка codex exec --help на версии 0.147.0 длинная, но для автоматизации значение имеет короткий список. Ниже — то, что реально меняет поведение прогона.Флаг Что делает Почему важно в CI --jsonпереводит stdout в поток JSON Lines единственный способ читать ход работы программно -o, --output-last-message <файл>пишет финальный ответ в файл файл остаётся артефактом job; в stdout ответ тоже придёт --output-schema <файл>заставляет финальный ответ соответствовать JSON Schema следующий шаг конвейера получает стабильные поля, а не прозу --sandbox <режим>read-only, workspace-write, danger-full-accessпо умолчанию агент не может писать на диск --ephemeralне сохранять файлы сессии на диск на общем раннере не оставляет следов работы --ignore-user-configне читать config.toml из CODEX_HOMEпрогон перестаёт зависеть от настроек чужой машины --ignore-rulesпропустить файлы .rules пользователя и проектато же самое для правил запуска команд --skip-git-repo-checkразрешить запуск вне git-репозитория нужен редко и осознанно, см. ниже -C, --cd <каталог>рабочий корень агента в монорепозитории ограничивает область работы --add-dir <каталог>дополнительный каталог на запись когда правки нужны вне основного рабочего каталога
Отдельно стоит -c (или --config): он переопределяет любое значение конфигурации по точечному пути, например -c model="gpt-5.6-terra". Именно через него официальный cookbook выдаёт агенту право на запись, не трогая --sandbox.
Машиночитаемый вывод: поток JSONL, схема ответа и коды возврата
Без --json конвейер видит только финальный текст. С ним stdout превращается в поток событий, где каждая строка — самостоятельный объект JSON. Типы событий: thread.started, turn.started, turn.completed, turn.failed, семейство item.* и error. Внутри item приезжают сообщения агента, рассуждения, запуски команд, правки файлов, вызовы инструментов и обновления плана.
Практическая ценность в том, что расход токенов приходит прямо в потоке: событие turn.completed несёт поле usage со счётчиками input_tokens, cached_input_tokens, output_tokens и reasoning_output_tokens. Это готовый вход для учёта стоимости, о котором ниже.
Что происходит при провале, обзоры обычно не показывают — а для CI это важнее успешного случая. Вот реальный хвост потока из прогона без действительных учётных данных (13 августа 2026, версия 0.147.0):
{"type":"thread.started","thread_id":"019ffaa2-1de8-76d3-896a-5352ae4e9447"}
{"type":"turn.started"}
{"type":"error","message":"Reconnecting... 2/5 (unexpected status 401 Unauthorized...)"}
{"type":"item.completed","item":{"id":"item_0","type":"error","message":"Falling back from WebSockets to HTTPS transport..."}}
{"type":"turn.failed","error":{"message":"unexpected status 401 Unauthorized: Missing bearer or basic authentication in header..."}}
Отсюда два вывода. Первый: события error идут в stdout вперемешку с полезными, поэтому «взять последнюю строку» — рабочая стратегия только если вы действительно ждёте turn.failed или turn.completed. Второй: причина отказа лежит в потоке текстом, и её не нужно выковыривать из логов.
Коды возврата в документации не описаны, поэтому я измерил их на версии 0.147.0:Код Что случилось Что делать конвейеру 0 ход завершён читать финальное сообщение или файл -o1 агент дошёл до модели, но ход провалился; в потоке turn.failedпоказать причину из потока, решить — повторять или падать 2 Codex до работы не дошёл: неизвестный флаг, ошибка командной строки чинить сам вызов, повтор бесполезен
Двойка проверена контрольным опытом: заведомо несуществующий флаг --no-such-flag-xyz даёт ровно тот же код и ту же форму сообщения, что и устаревший флаг. То есть 2 означает «неверная команда», а не «конкретно эта опция устарела».
Если следующему шагу нужны поля, а не текст, задайте схему. Файл schema.json описывает форму ответа, а прогон выглядит так:
codex exec "собери метаданные проекта" \
--output-schema ./schema.json \
-o ./project-metadata.json < /dev/null
Почему codex exec зависает в раннере и лечится одним /dev/null
Это самая дорогая ловушка неинтерактивного запуска, и в материалах топа её нет. Симптом: job висит до таймаута, в логе одна строка — «Reading additional input from stdin…». Задача при этом передана аргументом, и логически читать больше нечего.
Механика простая. Codex поддерживает передачу данных через трубу: если stdin запайплен, его содержимое добавляется к промпту как блок <stdin>. Отличить «труба пустая» от «данные ещё не пришли» невозможно, пока труба не закрыта. В интерактивном терминале это незаметно, а в раннере, cron-задаче или при запуске из родительского процесса поток ввода часто остаётся открытым — и агент ждёт данные, которых не будет.
Замер 13 августа 2026 на версии 0.147.0, два прогона с одинаковой задачей:Условие запуска Поведение Итог stdin открыт (держим трубу) печатает «Reading additional input from stdin…» висит бесконечно, снят сторожем через 30 с stdin закрыт: < /dev/nullработает штатно завершился за 22 с с кодом 1 (нет учётных данных)
Лечение — одна конструкция в конце команды:
codex exec --json "прогони тесты и почини падение" < /dev/null
Официальный cookbook по GitLab обходит ту же проблему иначе — заворачивает вызов в script -q -c, то есть выдаёт процессу псевдотерминал. Способ рабочий, но < /dev/null проще и не тянет за собой лишнюю утилиту.
Рядом живёт вторая причина мгновенной остановки: Codex отказывается работать вне git-репозитория. Сообщение при этом говорит не про git, а про доверие — «Not inside a trusted directory and —skip-git-repo-check was not specified», код возврата 1. По тексту ошибки в документации искать бесполезно, поэтому запомните формулировку: она означает «положи задачу в репозиторий или осознанно добавь --skip-git-repo-check».
Аутентификация автоматического прогона: ключ, а не аккаунт
Развилка «аккаунт ChatGPT или API-ключ» в автоматическом прогоне решается иначе, чем на своей машине. Рекомендованный путь — ключ: его проще выдать, отозвать и ротировать.
Переменная называется CODEX_API_KEY, и у неё есть особенность, которую легко пропустить: она поддерживается только в codex exec. Ставить её положено на одну команду, а не на весь job:
CODEX_API_KEY=$MY_KEY codex exec --json "разбери упавшие тесты" < /dev/null
Требование не косметическое. Документация прямо запрещает объявлять OPENAI_API_KEY или CODEX_API_KEY переменной уровня job в конвейере, который выкачивает или запускает код из репозитория: скрипты сборки, тесты, хуки установки зависимостей и скомпрометированный сторонний экшен в том же job прочитают эту переменную без всякого взлома.
Гонять CI под аккаунтом ChatGPT тоже можно, но путь ровно один и он неудобен по устройству, а не по недосмотру. Нужно создать auth.json командой codex login на доверенной машине, положить файл на раннер и позволить Codex обновлять его самому: клиент освежает пачку токенов, если последнее обновление старше примерно 8 суток, и записывает новые значения обратно в тот же файл. Отсюда три правила, которые ломают наивную схему:
- Не перезаписывайте файл исходным секретом на каждом рейсе — затрёте токены, которые Codex только что обновил.
- Один
auth.jsonна один раннер или на строго последовательный поток задач; два параллельных job рассинхронизируют сессию. - Для публичных и open-source репозиториев этот путь запрещён прямо в документации: файл содержит действующие токены доступа.
Из остальных переменных в автоматическом прогоне пригодятся CODEX_HOME (корень состояния, каталог обязан существовать заранее), RUST_LOG для подробных логов при разборе инцидента и CODEX_NON_INTERACTIVE=1 для тихой установки через официальный скрипт.
Codex GitHub Action: прокси для ключа и снятие sudo с раннера
Для GitHub Actions есть официальный экшен openai/codex-action@v1. Он ставит CLI, поднимает прокси Responses API, если вы передали ключ, и запускает codex exec с заданными правами. Финальное сообщение агента отдаётся выходом final-message — его можно передать в следующий job.
- name: Run Codex
id: run_codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: .github/codex/prompts/review.md
output-file: codex-output.md
Самое интересное в экшене — не входы, а то, как он обращается с ключом. Разбор его action.yml показывает три приёма подряд: ключ передаётся прокси через stdin и вычищается из окружения процесса, файл со сведениями о прокси получает права только на чтение и владельца root, а затем с пользователя раннера необратимо снимается sudo — и экшен тут же проверяет, что sudo действительно перестал работать, иначе падает.
Зачем такие сложности, объясняют сами авторы в разборе угроз экшена: на раннерах GitHub по умолчанию доступен sudo без пароля, а значит через procfs можно прочитать память чужого процесса и достать оттуда ключ. Комбинация «read-only песочница плюс отключённая сеть», которая многим кажется достаточной, ключ не защищает.
Отсюда практический вывод: safety-strategy по умолчанию (drop-sudo) менять не нужно, а значение unsafe на общем раннере ставить нельзя. Исключение одно — Windows, где других вариантов пока нет и экшен сам это проверяет первым шагом.
Ещё одна деталь для тех, кто читает только страницу документации: в action.yml объявлены входы, которых там нет. Это permission-profile (современная замена входу sandbox; в описании входа они прямо названы взаимоисключающими), output-schema (текст схемы прямо в workflow) и output-schema-file (путь к файлу схемы в репозитории), responses-api-endpoint (свой эндпоинт, например в Azure) и allow-bot-users (поимённый список доверенных ботов).
По умолчанию запустить такой workflow может только тот, у кого есть право записи в репозиторий, — это отдельный шаг проверки внутри экшена. Расширять список входами allow-users и allow-bots стоит с осторожностью: если открыть запуск всем, чужие люди начнут расходовать квоту вашего ключа.
Codex в GitLab CI и любом другом раннере: рецепт на голом CLI
Экшен удобен, но он не обязателен: codex exec — обычная консольная команда, и она одинаково работает в GitLab CI, Jenkins, Buildkite или в cron на своей машине. У OpenAI есть официальный cookbook именно для GitLab, а сам GitLab описывает связку в своём блоге, так что тема раннеров давно не сводится к GitHub.
Минимальная job для GitLab CI выглядит так:
codex_review:
stage: codex
image: node:24
rules:
- if: '$CI_MERGE_REQUEST_ID && $OPENAI_API_KEY'
script:
- npm -g i @openai/codex@0.147.0
- codex --version
- |
CODEX_API_KEY="$OPENAI_API_KEY" codex exec --json \
--sandbox read-only \
-o codex-report.md \
"Разбери изменения ветки против $CI_MERGE_REQUEST_TARGET_BRANCH_NAME и опиши риски" \
< /dev/null > codex-events.jsonl
echo "codex rc=$?"
artifacts:
when: always
paths: [codex-report.md, codex-events.jsonl]
Здесь собраны все правила, разобранные выше: версия CLI закреплена, ключ живёт в переменной CI/CD и подставляется на одну команду, поток ввода закрыт, права минимальны, а артефакты сохраняются when: always — иначе при падении job вы не увидите ни отчёта, ни потока событий. Правило rules пропускает job, если ключа нет: на merge request из форка секреты не отдаются, и без такой проверки job будет падать без пользы.
Официальный cookbook делает ещё один шаг, полезный в GitLab: складывает вывод агента в формат CodeClimate, и тогда замечания видны прямо в виджете merge request, рядом с линтером и тестами.
Права и сеть автономного прогона: песочница и профили доступа
По умолчанию агент работает в режиме только для чтения: он видит репозиторий, но записать ничего не может. Это правильный дефолт для CI, и именно поэтому рецепт автофикса вынужден явно выдавать право на запись.
Три режима песочницы и их смысл в автоматическом прогоне:
read-only— анализ, ревью, сводки, генерация отчётов. Годится для большинства задач в конвейере.workspace-write— правки файлов в рабочем каталоге: автофикс, миграции, обновление документации.danger-full-access— снимает ограничения; документация допускает его только в изолированной среде вроде одноразового контейнера.
Флаг --ask-for-approval в автоматическом прогоне почти всегда бессмысленен: спрашивать некого. Значение never описано как «execution failures are immediately returned to the model» — то есть агент не остановится, а попробует решить проблему сам. Границу ему ставит песочница, а не политика одобрений; подробный разбор самих режимов одобрения и песочницы — в отдельном уроке.
Два флага изоляции стоит ставить в CI по умолчанию. --ignore-user-config отвязывает прогон от config.toml в домашнем каталоге (аутентификация при этом всё равно берётся из CODEX_HOME), а --ignore-rules — от пользовательских и проектных файлов .rules. Вместе они дают воспроизводимость: результат перестаёт зависеть от того, что лежало на конкретной машине.
Сколько стоит один прогон: курс кредита и расчёт по токенам
Стоимость автоматизации — то место, где обзоры обрываются на фразе «зависит от модели». Считать можно точнее, и для этого достаточно двух официальных таблиц.
Расход по подписке ChatGPT измеряется кредитами, расход по ключу — долларами. Величины связаны жёстко: если сопоставить обе таблицы на одну дату, множитель оказывается одинаковым для всех моделей и всех трёх классов токенов.Модель Кредиты за 1 млн (вход / кэш / выход) Доллары за 1 млн (вход / кэш / выход) GPT-5.6 Sol 125 / 12,5 / 750 $5 / $0,50 / $30 GPT-5.6 Terra 50 / 5 / 300 $2 / $0,20 / $12 GPT-5.6 Luna 5 / 0,5 / 30 $0,20 / $0,02 / $1,20 GPT-5.4 62,5 / 6,25 / 375 $2,50 / $0,25 / $15
Разделите любую пару — получится $0,04 за кредит, то есть 25 кредитов на доллар. Совпадение держится на 14 сверках из 15 (пять моделей на три класса токенов); единственное отклонение — выход GPT-5.4 mini, где в кредитной таблице стоит округлённое 113 вместо точного 112,5. Величина нигде не объявлена вендором как «курс» — это вывод из сопоставления официального прайса API с тарифной сеткой кредитов на 13 августа 2026.
Теперь прогон. Возьмём типовую ночную задачу: агент читает часть репозитория, гоняет тесты, правит файл и пишет отчёт. Допущения (это модель, а не счёт из личного кабинета): 150 тысяч входных токенов суммарно по ходам, из них 120 тысяч кэшированных, и 8 тысяч токенов на выходе.Модель Стоимость прогона 30 прогонов в месяц GPT-5.6 Luna ~$0,018 ~$0,54 GPT-5.6 Terra ~$0,18 ~$5,40 GPT-5.6 Sol ~$0,45 ~$13,50
В кредитах тот же прогон на Terra стоит 4,5 кредита — и $0,18 при курсе выше. Цифры скромные, но масштаб решает: ревью на каждый pull request при сотне PR в месяц даёт уже $18 на Terra и $45 на Sol.
Два рычага влияют на счёт сильнее выбора модели. Первый — кэш: повторный контекст стоит в десять раз дешевле свежего, поэтому длинная цепочка ходов по одному репозиторию обходится не так дорого, как кажется. Второй — объём выхода: он самый дорогой класс токенов, и просьба «покажи только диff и три строки объяснения» экономит больше, чем смена модели. Если прогоны идут по подписке, полезно понимать и устройство лимитов плана — кредиты начинают тратиться после того, как включённый объём исчерпан.
Отказы, ретраи и таймауты: что Codex делает сам, а что должен CI
Codex обороняется от сетевых сбоев сам, и знать глубину этой обороны полезно: она напрямую влияет на длину job. Измеренное поведение при недоступной модели (13 августа 2026, версия 0.147.0): пять попыток подключения по WebSocket, честное сообщение «Falling back from WebSockets to HTTPS transport», ещё пять попыток по HTTPS и выход с кодом 1. На пустом отказе весь цикл занял 21–22 секунды.
Замер совпадает с документированными значениями по умолчанию: stream_max_retries равен 5, request_max_retries — 4. То есть на реальной сетевой икоте прогон не упадёт с первой попытки, и добавлять свой цикл повторов поверх обычно не нужно.
А вот чего у команды нет — так это собственного потолка времени. Флага общего таймаута в справке 0.147.0 не существует; параметр stream_idle_timeout_ms со значением 300 000 мс — это окно тишины потока, а не предел длительности прогона. Вывод практический: ограничение обязан ставить конвейер.
# потолок в 15 минут средствами системы
timeout 900 codex exec --json "задача" < /dev/null
echo "rc=$?" # 124 — сработал таймаут
В GitHub Actions ту же роль играет timeout-minutes у шага или job, в GitLab CI — свойство timeout. Ставить его нужно всегда: даже без ловушки stdin агент может уйти в долгую задачу, а раннер стоит денег.Симптом Вероятная причина Что проверить Job висит, в логе «Reading additional input from stdin…» открытый stdin добавить < /dev/nullМгновенный выход с кодом 2 неизвестный флаг или опечатка в команде codex exec --help на своей версииКод 1 и «Not inside a trusted directory» запуск вне git-репозитория положить работу в репозиторий или --skip-git-repo-checkКод 1, в потоке turn.failed с 401ключ не доехал до команды ключ ставится на сам вызов, а не на job Прогон падает, хотя раньше работал обновился CLI, флаг исчез закрепить версию, сверить справку Пустой stdout при непустом stderr смотрите не тот поток финальный ответ в stdout, прогресс в stderr
Устаревшие рецепты: —full-auto и имена входов Codex Action
Эта тема стареет быстрее, чем пишутся статьи о ней, — включая официальные. Три расхождения, найденные 13 августа 2026 на версии 0.147.0.
Флага --full-auto больше нет. Проверено тремя путями: codex exec --full-auto отвечает «error: unexpected argument ‘—full-auto’ found» и кодом 2; codex --full-auto даёт то же самое; поиск строк по самому бинарнику находит ноль вхождений. Метод при этом проверен на контрольной группе — тем же поиском найдены девять других имён флагов и переменных (CODEX_HOME — 60 вхождений, ephemeral — 36, skip-git-repo-check — 2 и так далее). То есть ноль означает отсутствие, а не сломанный поиск.
При этом страница неинтерактивного режима на ту же дату утверждает, что флаг сохранён как устаревший ради совместимости и печатает предупреждение, а официальный cookbook по GitLab ставит codex exec --full-auto в рабочий пример — рядом с командой npm -g i @openai/codex@latest, то есть рецепт устанавливает свежую версию и тут же вызывает то, чего в ней нет.
Имена входов экшена расходятся между двумя официальными страницами. Cookbook по автофиксу использует openai_api_key и codex_args через подчёркивание, тогда как action.yml объявляет openai-api-key и codex-args через дефис. GitHub Actions сопоставляет входы по точному имени, поэтому скопированный рецепт даёт ровно тот симптом, который описан в разделе устранения неполадок официальной документации: прокси не стартует, потому что ключ до него не доехал.Что написано в источнике Что в артефакте на 13.08.2026 Как писать codex exec --full-auto (cookbook GitLab)флага нет, код возврата 2 --sandbox workspace-write« --full-auto сохранён как deprecated» (документация)в бинарнике 0 вхождений считать флаг удалённым openai_api_key, codex_args (cookbook автофикса)openai-api-key, codex-args в action.ymlчерез дефис openai/codex-action@main (cookbook)тег @v1 в документациизакреплять версию
Отсюда правило, которое дороже любого из этих трёх фактов: закрепляйте версию CLI и тег экшена. npm -g i @openai/codex@latest в job означает, что однажды утром конвейер поедет на новой версии, о которой вы не знали. У экшена для этого есть вход codex-version.
Три рецепта: автофикс тестов, changelog и правка по репозиторию
Автофикс упавшего CI. Официальный шаблон устроен вокруг разделения прав, и это главное в нём. Job с Codex получает только contents: read и persist-credentials: false, агент чинит тесты, а результат сохраняется патчем-артефактом. Второй job с правами на запись применяет патч и открывает pull request — ключа OpenAI он при этом не видит вовсе. Человек остаётся в контуре: он смотрит PR, а не доверяет автоматике коммит в основную ветку.
Заметьте, что второй официальный источник — cookbook по автофиксу — делает иначе: коммитит и открывает PR в том же job. Схема проще, но права шире, и на публичном репозитории я бы выбрал вариант с двумя job.
Черновик changelog. Здесь агент вообще не должен ничего менять, поэтому режим только для чтения и труба:
git log --oneline v1.4.0..HEAD \
| codex exec --sandbox read-only \
"Сгруппируй коммиты по смыслу и напиши changelog для пользователей, без внутренних деталей" \
> CHANGELOG.draft.md
Массовая правка по репозиторию. Задача вроде «переименуй устаревший вызов во всех сервисах» решается не одним прогоном, а циклом с проверкой. Здесь пригодятся --output-schema (чтобы получить машиночитаемый отчёт о том, что изменено) и двухшаговая работа через codex exec resume --last "теперь прогони тесты и почини, что сломал" — вторая команда продолжает ту же сессию, а не начинает с нуля. Файлы инструкций проекта агент читает и в неинтерактивном режиме, поэтому правила стиля лучше держать в AGENTS.md, а не переписывать в промпт каждый раз.
Codex SDK на TypeScript и Python: когда код лучше, чем exec
Если Codex становится частью вашего продукта, а не шагом конвейера, разбирать JSONL руками незачем — есть официальные SDK.
import { Codex } from "@openai/codex-sdk";
const codex = new Codex();
const thread = codex.startThread();
const turn = await thread.run("Найди причину падения тестов и предложи правку");
console.log(turn.finalResponse);
TypeScript-версия требует Node.js 18 и старше, умеет продолжать тред повторным run(), восстанавливать его по идентификатору через resumeThread() и отдавать события по мере поступления через runStreamed(). Схему ответа можно задать на каждый ход, в том числе сгенерировав её из Zod.
Python-версия ставится как pip install openai-codex, требует Python 3.10 и старше и устроена иначе: она управляет локальным app-server по JSON-RPC, а не гоняет CLI. Пресеты песочницы там заданы явно — Sandbox.read_only, Sandbox.workspace_write, Sandbox.full_access — и режим можно менять между ходами одного треда.
Ключевое отличие SDK от прямого вызова команды — не возможности, а форма. Под капотом TypeScript SDK, по его README, запускает тот же CLI и читает тот же поток JSONL, а CODEX_API_KEY подкладывает в окружение процесса сам. Поэтому выбор простой: если задача помещается в shell-скрипт — берите exec и не тащите рантайм в раннер; если вокруг агента нужна логика (очередь задач, свой интерфейс, разбор промежуточных событий) — берите SDK и получите типы вместо ручного парсинга строк.
Риски автономного прогона: утечка ключа и чужой текст в задаче
Автономность здесь означает, что ошибок никто не заметит в момент их совершения. Отсюда честный список того, где эта схема ломается.
- Ключ утекает через права, а не через сеть. Пока у процесса есть
sudoбез пароля, содержимое памяти соседних процессов доступно на чтение — и режим «только чтение с диска» ключ не спасает. Поэтому в GitHub Actions работаетdrop-sudo, а в своём раннере ключ стоит держать в отдельном шаге и не пускать в job недоверенный код. - Недоверенный текст приезжает вместе с задачей. Тело pull request может содержать инструкции в HTML-комментарии, невидимом для человека; туда же относятся сообщения отдельных коммитов, скриншоты и файлы инструкций репозитория, если работа идёт над содержимым чужого PR. Промпт, собранный из таких данных, нужно чистить — и не давать агенту прав на запись в этом сценарии.
- Состояние раннера после агента не гарантировано. Авторы экшена рекомендуют ставить его последним шагом job: агент мог оставить работающие процессы, переписать файлы других экшенов на хосте или дописать что-то в
.git/hooksв расчёте на привилегированные шаги, которые пойдут следом. - Тихий провал дороже громкого. Прогон без потолка времени и без проверки кода возврата выглядит как успешный: job зелёный, артефакт пустой. Проверяйте
$?и сохраняйте артефактыwhen: always. - Рецепты стареют. Три расхождения официальных источников с бинарником, разобранные выше, — не единичный случай, а свойство быстро меняющегося продукта. Незакреплённая версия превращает это свойство в поломку конвейера.
Что в этой схеме работает в вашу пользу: права по умолчанию узкие (только чтение), запуск вне репозитория запрещён, провал машиночитаем, а официальный шаблон с патчем-артефактом позволяет оставить человека в контуре, не замедляя конвейер.
Что устареет первым. Состав флагов и наличие отдельных опций — проверяйте командой codex exec --help на своей версии. Дальше — тарифная сетка и цены API (курс кредита придётся пересчитать), затем набор входов экшена: сверяйте с action.yml того тега, который закрепили. Статус экспериментальных команд вроде codex cloud exec тоже меняется.
FAQ
Какой код возврата даёт codex exec, если агент не справился?
По замеру на версии 0.147.0 — 1. Тот же код приходит при запуске вне git-репозитория. Код 2 означает другое: Codex до работы не приступал, потому что не разобрал командную строку (неизвестный флаг, опечатка). Проверять $? обязательно: без этого упавший прогон выглядит в конвейере успешным, а артефакт остаётся пустым.
Почему codex exec завис и ничего не выводит?
Скорее всего, открыт поток ввода. Codex поддерживает передачу данных через трубу и добавляет содержимое stdin к промпту, поэтому ждёт, пока труба закроется, даже если задача уже передана аргументом. В логе при этом одна строка — «Reading additional input from stdin…». Лечится добавлением < /dev/null к команде.
Можно ли запускать codex exec по расписанию через cron?
Да, это обычная консольная команда. Три вещи обязательны: закрыть stdin, поставить внешний потолок времени через timeout и передавать ключ на сам вызов, а не экспортировать в окружение. Полезно добавить --ephemeral, чтобы прогон не оставлял файлов сессии, и писать поток --json в файл — иначе разбирать ночной инцидент будет нечем.
Нужен ли для CI ключ API или хватит подписки ChatGPT?
Официально рекомендован ключ: его проще выдавать, отзывать и ротировать. Работа под аккаунтом ChatGPT поддержана, но требует возить между рейсами файл auth.json и соблюдать три правила: не перезаписывать его исходным секретом, не делить между параллельными задачами и не применять эту схему в публичных репозиториях.
Работает ли codex exec вне git-репозитория?
По умолчанию нет: прогон завершается с кодом 1 и сообщением «Not inside a trusted directory and —skip-git-repo-check was not specified». Ограничение защищает от разрушительных правок там, где нельзя откатиться. Обойти можно флагом --skip-git-repo-check, но делать это стоит осознанно — например, в одноразовом контейнере, где терять нечего.
Чем Codex SDK отличается от вызова codex exec из своего кода? Возможности те же: TypeScript-версия запускает тот же CLI и читает тот же поток JSONL. Отличается форма работы — вы получаете треды, типизированные события, структурированный вывод и управление окружением вместо ручного разбора строк. Если задача помещается в shell-скрипт, SDK не нужен; если вокруг агента растёт своя логика, он экономит много кода.
Курс «OpenAI Codex: агентный кодинг» · модуль «PRO: автономность и качество». Полная программа и два маршрута обучения — на странице курса.
Предыдущий урок: Безопасность и песочница: сеть, секреты




