Przejdź do głównej zawartości

Codex bez interakcji: codex exec

codex exec uruchamia Codeksa bez interaktywnego interfejsu terminalowego: postęp wypisuje na stderr, końcową odpowiedź agenta na stdout, nigdy nie zatrzymuje się, by prosić o zgodę, i kończy się niezerowym kodem, gdy przebieg zawiedzie. To podstawowy klocek skryptów, zadań cron i kroków CI, pod warunkiem że wynik sprawdza skrypt, a nie agent.

Ta strona jest dla programistów i tech leadów, którzy znają już Codeksa w trybie interaktywnym, a teraz chcą go wpiąć w pipeline. Typowa sytuacja: nocne zadanie ma przejrzeć padające testy, krok przed merge’em ma zwrócić werdykt czytelny dla maszyny, a skrypt wydania potrzebuje notatek w stałym formacie. Zapis rozmowy nic skryptowi powłoki nie da. Potrzebujesz dokładnych flag, wyjścia, któremu parser może zaufać, i bramki, która głośno zawodzi, gdy agent ogłasza sukces, którego nie osiągnął.

  • Wywołania codex exec do analizy tylko do odczytu, edycji w obszarze roboczym, JSON-a sprawdzanego schematem i przebiegów wieloetapowych, sprawdzone na Codex CLI 0.157.1.
  • Ścisły JSON Schema i przepisy jq, które zamieniają strumień zdarzeń JSONL w sygnały „przeszło / nie przeszło”.
  • Zadanie GitHub Actions, które uruchamia Codeksa na zaufanej gałęzi, trzyma klucz API z dala od powłoki agenta i weryfikuje zmianę bez czytania diffa linia po linii.
  • Listę typowych awarii z dokładną treścią błędu i sposobem naprawy każdej z nich.

Czym codex exec różni się od interaktywnego Codeksa?

Dział zatytułowany „Czym codex exec różni się od interaktywnego Codeksa?”

Pod spodem działa ten sam agent, ale domyślne zachowanie się zmienia, bo nikt nie siedzi przy klawiaturze. Poniższe zachowania odczytano z codex exec --help i ze źródeł codex-rs/exec w wersji Codex CLI 0.157.1.

ZachowanieInteraktywny codexcodex exec
WyjściePełnoekranowy interfejs terminalowyPostęp na stderr, końcowa odpowiedź na stdout; --json przełącza stdout na zdarzenia JSONL
ZgodyPyta zgodnie z approval_policyNigdy nie pyta. Polecenie wymagające zgody poza sandboxem zostaje odrzucone
SandboxZ config.toml lub /permissionsZ --sandbox, -c default_permissions=… lub config.toml. Tylko do odczytu, dopóki nie przyznasz więcej
Repozytorium GitBrak wymoguKończy się kodem 1 poza repozytorium Git, chyba że podasz --skip-git-repo-check
Pliki sesjiZapisywane, do wznowienia przez codex resumeZapisywane, do wznowienia przez codex exec resume, chyba że podasz --ephemeral
Źródło promptuWpisywany ręcznieArgument, - dla stdin albo oba naraz: stdin z potoku jest doklejany do promptu jako blok <stdin>

codex exec ma też alias codex e oraz wariant do przeglądów kodu, codex exec review, opisany dalej na tej stronie.

  1. Uwierzytelnij się. Na własnej maszynie wystarczy codex login; codex exec korzysta z zapisanego logowania. W CI ustaw zamiast tego CODEX_API_KEY z magazynu sekretów. codex exec czyta tę zmienną bezpośrednio, więc na runnerze nie powstaje plik z danymi logowania. Opcje logowania opisuje strona uwierzytelnianie w Codeksie.

  2. Zacznij od trybu tylko do odczytu. Bez flagi sandboxa Codex czyta repozytorium i uruchamia polecenia tylko do odczytu, ale nie edytuje plików.

    Okno terminala
    codex exec "List the five modules with the most complex control flow and say why each is risky."
  3. Podaj kontekst przez potok. Gdy stdin pochodzi z potoku i jednocześnie podajesz prompt, Codex dokleja stdin do promptu. Tak przekażesz diffy, logi i wyniki testów.

    Okno terminala
    git diff origin/main...HEAD | codex exec "Summarise this diff as release notes: Features, Fixes, Breaking changes."
  4. Daj prawo zapisu tylko wtedy, gdy zadanie edytuje pliki. Z --sandbox workspace-write agent może zmieniać drzewo robocze; sieć pozostaje wyłączona.

    Okno terminala
    codex exec --sandbox workspace-write "Fix the failing test in tests/billing/invoice.test.ts. Change only src/billing/."
  5. Zapisz odpowiedź do pliku. -o (--output-last-message) zapisuje końcową odpowiedź agenta do pliku, więc skrypt nie musi wyłuskiwać jej ze stdout.

    Okno terminala
    codex exec -o triage.md "Read the last CI log in ci.log and name the root cause in three sentences."

W CI przypnij też model flagą -m, żeby zmiana domyślnego modelu nie zmieniła po cichu zachowania nocnego zadania. Aktualny model domyślny i jego poziomy wysiłku znajdziesz w przeglądzie modeli; wysiłek dla pojedynczego przebiegu ustawisz przez -c model_reasoning_effort="high", gdy zadanie tego wymaga.

Wybierz najwęższą opcję, która pozwala dokończyć zadanie. Skoro codex exec o nic nie pyta, ten wybór jest całą decyzją o bezpieczeństwie.

Opcja (Codex CLI 0.157.1)Co może agentDo czego
Bez flagi (read-only)Czytać pliki, uruchamiać polecenia tylko do odczytuTriage, analizy, przeglądy, raporty
--sandbox workspace-writeEdytować drzewo robocze i ścieżki z --add-dir; bez sieciPoprawki, refaktoryzacje, pliki generowane
-c default_permissions=":workspace"Profil uprawnień, rozwiązanie, które OpenAI preferuje dziś dla nowych integracji (beta, CLI 0.138.0 lub nowsze)Nowe pipeline’y; nie łącz z --sandbox ani --approve-for-me, bo według OpenAI oba systemy „do not compose”
--approve-for-meKieruje prośby o zgodę do automatycznego recenzenta, w sandboxie workspace-writeZadania, w których prośba o wyjście poza granicę jest spodziewana i może o niej rozstrzygnąć model
--dangerously-bypass-approvals-and-sandboxWszystko, bez sandboxaTylko w jednorazowym kontenerze z ograniczoną siecią, który sam stanowi granicę

Wszystkie przykłady na tej stronie używają starszej flagi --sandbox workspace-write, a nie profilu uprawnień, z dwóch powodów: w 0.157.1 profile są nadal w wersji beta, a według OpenAI system profili i starszy system sandboxa „do not compose”, więc jedno polecenie powinno używać tylko jednego z nich. Nowy pipeline może przejść na profile, zastępując flagę przez -c default_permissions=":workspace" w każdym poleceniu; nie zostawiaj obu naraz.

Dwie kolejne flagi sprawiają, że przebieg w CI jest powtarzalny. --ignore-user-config pomija $CODEX_HOME/config.toml, więc pozostawione na runnerze serwery MCP czy ustawienia modelu nie zmienią zadania. --strict-config przerywa przebieg, gdy config.toml zawiera klucz nieznany tej wersji Codeksa, dzięki czemu literówka nie zamienia się w cichą wartość domyślną. Klucze konfiguracji i ich warstwy wyjaśnia strona konfiguracja Codeksa.

--output-schema przyjmuje plik JSON Schema i ogranicza do niego końcową odpowiedź agenta. Codex wysyła schemat z włączoną ścisłą walidacją (strict: true w źródłach 0.157.1), więc sam schemat musi spełniać reguły trybu ścisłego: każdy obiekt wymienia wszystkie swoje właściwości w required i ustawia "additionalProperties": false, na każdym poziomie. Pole opcjonalne zapisz jako typ dopuszczający null, zamiast pomijać je w required.

Zapisz to jako .github/codex/risk-schema.json:

{
"type": "object",
"properties": {
"verdict": { "type": "string", "enum": ["pass", "fail"] },
"findings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": { "type": "string" },
"line": { "type": ["integer", "null"] },
"severity": { "type": "string", "enum": ["high", "medium", "low"] },
"evidence": { "type": "string" }
},
"required": ["file", "line", "severity", "evidence"],
"additionalProperties": false
}
}
},
"required": ["verdict", "findings"],
"additionalProperties": false
}

Następnie uruchom zadanie i pozwól, by jq -e zamienił odpowiedź w kod wyjścia:

Okno terminala
codex exec --output-schema .github/codex/risk-schema.json -o risk.json \
"$(cat .github/codex/risk-prompt.md)"
jq -e '.verdict == "pass" and ([.findings[] | select(.severity == "high")] | length == 0)' risk.json

Schemat gwarantuje kształt, nie prawdziwość. "verdict": "pass" znaczy, że agent w to uwierzył; to, czy uwierzysz ty, rozstrzygają testy i kontrole z sekcji o weryfikacji poniżej.

--json zastępuje czytelne dla człowieka stdout jednym obiektem JSON na linię. W Codex CLI 0.157.1 wartości type najwyższego poziomu to thread.started, turn.started, turn.completed, turn.failed, item.started, item.updated, item.completed i error. Elementy mają własny type: agent_message, reasoning, command_execution, file_change, mcp_tool_call, collab_tool_call, web_search, todo_list i error.

Skrócony przebieg wygląda tak (wartości są przykładowe):

{"type":"thread.started","thread_id":"0199a213-81c0-7800-8aa1-bbab2a035a53"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_1","type":"command_execution","command":"npm test","aggregated_output":"...","exit_code":1,"status":"failed"}}
{"type":"item.completed","item":{"id":"item_4","type":"agent_message","text":"The invoice test fails because rounding happens before tax."}}
{"type":"turn.completed","usage":{"input_tokens":24763,"cached_input_tokens":19200,"cache_write_input_tokens":0,"output_tokens":122,"reasoning_output_tokens":64}}

Zapisz strumień raz, a potem wyciągaj z niego to, czego potrzebuje pipeline:

Okno terminala
codex exec --json "Diagnose the failing tests in ci.log" > events.jsonl
# Identyfikator sesji do późniejszego wznowienia
jq -r 'select(.type == "thread.started") | .thread_id' events.jsonl
# Końcowa odpowiedź
jq -r 'select(.type == "item.completed" and .item.type == "agent_message") | .item.text' events.jsonl | tail -n 1
# Każde polecenie agenta zakończone niezerowym kodem
# (odrzucone polecenia mają exit_code null, więc test na null je pomija)
jq -r 'select(.type == "item.completed" and .item.type == "command_execution" and .item.exit_code != null and .item.exit_code != 0) | .item.command' events.jsonl
# Zmienione pliki
jq -r 'select(.type == "item.completed" and .item.type == "file_change") | .item.changes[].path' events.jsonl
# Tokeny wyjściowe całego przebiegu, do śledzenia kosztów
jq -s '[.[] | select(.type == "turn.completed") | .usage.output_tokens] | add' events.jsonl

Zachowaj events.jsonl jako artefakt builda. To ślad audytowy tego, co agent faktycznie uruchomił, a to znaczy więcej niż to, co twierdzi w końcowej odpowiedzi.

Dziel długą pracę na etapy tak, by każdy miał jedno zadanie, a skrypt mógł go sprawdzić przed startem następnego. codex exec resume kontynuuje zapisaną sesję; codex exec fork kopiuje ją do nowej sesji, więc z tej samej analizy możesz wypróbować dwa podejścia.

Okno terminala
# Etap 1: analiza tylko do odczytu, z zapisem identyfikatora sesji
codex exec --json "$(cat plan-prompt.md)" > stage1.jsonl
THREAD_ID=$(jq -r 'select(.type == "thread.started") | .thread_id' stage1.jsonl)
# Etap 2: implementacja w tej samej sesji, już z prawem zapisu
codex exec --sandbox workspace-write resume "$THREAD_ID" \
"Implement step 1 of your plan only. Run the tests you listed. Stop after that."
# Alternatywa: odgałęzienie analizy do osobnej sesji, też z prawem zapisu
codex exec --sandbox workspace-write fork "$THREAD_ID" \
"Implement the plan with the smallest possible diff instead."

Zwróć uwagę, gdzie stoi --sandbox. Ani codex exec resume, ani codex exec fork nie ma w 0.157.1 własnej opcji --sandbox (unexpected argument '--sandbox' found), więc flaga musi stać przed podpoleceniem. Fork bez niej dziedziczy domyślny tryb tylko do odczytu i niczego nie zedytuje. Wybieraj jawny identyfikator sesji zamiast resume --last: --last bierze najnowszą sesję zapisaną dla bieżącego katalogu, a to niewłaściwa sesja, gdy tylko dwa zadania dzielą runner. Przebieg uruchomiony z --ephemeral nie zapisuje plików sesji, więc nie da się go wznowić.

Do zadań czysto przeglądowych służy codex exec review, który uruchamia wbudowany przegląd Codeksa. Podajesz mu albo gotowy cel (niezatwierdzone zmiany przez --uncommitted, gałąź bazową przez --base lub jeden commit przez --commit), albo własne instrukcje przeglądu jako prompt, nigdy oba naraz. W Codex CLI 0.157.1 flag --base, --uncommitted i --commit nie da się połączyć z własnymi instrukcjami: polecenie pada na parsowaniu argumentów z komunikatem cannot be used with '[PROMPT]'.

Gotowy cel z wbudowanymi instrukcjami przeglądu:

Okno terminala
codex exec review --base main --json -o review.md > review-events.jsonl

Własne instrukcje, w których zakres diffa jest nazwany w treści, a nie flagą:

Okno terminala
codex exec review -o review.md "$(cat .github/codex/review-prompt.md)"

Obie formy przyjmują --json i -o. Plik z -o to raport dla człowieka: ogólne wyjaśnienie, a po nim uszeregowane według priorytetu uwagi. W 0.157.1 wbudowany przegląd działa według własnej rubryki i stałego formatu wyjścia, więc twoje instrukcje zmieniają to, czego szuka, a nie kształt tego, co zwraca. Nie szukaj w tym raporcie grep-em linii z werdyktem; nic nie gwarantuje, że się pojawi. --output-schema przechodzi parsowanie, ale w źródłach 0.157.1 żądanie przeglądu nie przenosi schematu. Bramka dla maszyny należy do zwykłego codex exec z --output-schema i tym samym promptem przeglądowym, jak w sekcji o JSON-ie; codex exec review zostaw do raportu, który czyta człowiek.

Jak przegląd wpisuje się w zespołowy przepływ pull requestów, łącznie z przeglądem po stronie GitHuba, opisuje strona code review z Codeksem.

Końcowa odpowiedź agenta to deklaracja. Zadaniem pipeline’u jest sprawdzić ją sygnałami, których agent nie podrobi, i podać człowiekowi krótkie podsumowanie dowodów zamiast surowego diffa. Uruchom te kontrole w skrypcie po powrocie codex exec, jako osobne kroki:

  1. Kod wyjścia. codex exec kończy się kodem 1, gdy przebieg zawiedzie (zły plik schematu, kontrola Git, nieudana tura). Traktuj to jako twardą porażkę. Kod 0 znaczy tylko, że przebieg się skończył, a nie że zadanie się udało.
  2. Uruchom testy sam. Nie ufaj frazie "tests pass" w odpowiedzi. Uruchom zestaw testów w nowym kroku, własnym poleceniem zadania. Jeśli agent zmienił test, żeby przechodził, złapie to następna kontrola.
  3. Chronione ścieżki. Zapisuj events.jsonl i plik z -o poza katalogiem repozytorium (na przykład w $RUNNER_TEMP), żeby nie trafiły do indeksu razem ze zmianą agenta i nie liczyły się do budżetu diffa. Dodaj wszystko do indeksu przez git add -A (żeby liczyły się też nowe pliki), a potem przerwij zadanie, jeśli zmiana dotyka plików, których agent nie może edytować: git diff --cached --name-only | grep -qE '^(tests/|\.github/|migrations/|package(-lock)?\.json$)' && exit 1. package.json jest na liście, bo inaczej agent mógłby przedefiniować skrypt test, który uruchamia następne sprawdzenie.
  4. Budżet diffa. Ogranicz rozmiar zmiany, żeby „poprawka w jednej linii” nie stała się po cichu przepisaniem modułu: git diff --cached --numstat | awk '{s+=$1+$2} END {exit (s > 200)}'.
  5. Werdykt ze schematu. jq -e na pliku z -o, jak pokazano wyżej.
  6. Dowody dla człowieka. Dołącz do pull requesta events.jsonl, plik z -o i wynik testów. Recenzent sprawdza, czy dowody zgadzają się z intencją; strona o pakiecie dowodów definiuje, co taki pull request musi wykazać.

Zatwierdzanie wygląda tak samo jak przy zmianach pisanych przez ludzi: właściciel kodu dotkniętych ścieżek zatwierdza pull request i nic, co wyprodukuje zadanie, nie trafia do gałęzi głównej bez jego zgody.

W GitHub Actions pierwszym wyborem jest oficjalna akcja openai/codex-action@v1: trzyma klucz za proxy i potrafi odebrać sudo, zanim wystartuje Codex. Jej parametry opisuje strona CI/CD z Codex GitHub Action. Wywołuj codex exec bezpośrednio, gdy potrzebujesz flagi, której akcja nie udostępnia, albo gdy pracujesz w innym systemie CI.

Bezpośrednie wywołanie jest bezpieczne przy dwóch zasadach. Po pierwsze, uruchamiaj je tylko na zaufanym kodzie: harmonogram albo ręczny trigger na gałęzi domyślnej, nigdy ref kontrybutora, gdy w środowisku jest sekret. Po drugie, trzymaj klucz z dala od powłoki agenta. W Codex CLI 0.157.1 polecenia uruchamiane przez Codex domyślnie dziedziczą całe środowisko (shell_environment_policy.ignore_default_excludes ma domyślnie wartość true). Ustawienie false odfiltrowuje każdą zmienną, której nazwa zawiera KEY, SECRET lub TOKEN.

.github/workflows/codex-nightly.yml
name: codex-nightly-test-fix
on:
schedule:
- cron: '17 3 * * 1-5'
workflow_dispatch:
permissions:
contents: read
jobs:
fix:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- run: npm ci # instaluj przed Codeksem: workspace-write nie ma sieci
- run: npm install -g @openai/codex@0.157.1
- name: Run Codex
env:
CODEX_API_KEY: ${{ secrets.OPENAI_API_KEY }}
CODEX_MODEL: gpt-6-astra
run: |
# Model przypięty celowo: domyślny model zmienia się między wydaniami CLI
codex exec -m "$CODEX_MODEL" --sandbox workspace-write --ephemeral --ignore-user-config \
-c shell_environment_policy.ignore_default_excludes=false \
--json --output-schema .github/codex/fix-schema.json -o "$RUNNER_TEMP/fix.json" \
"$(cat .github/codex/fix-prompt.md)" > "$RUNNER_TEMP/events.jsonl"
- name: Verify independently
run: |
STATUS=$(jq -r '.status' "$RUNNER_TEMP/fix.json")
git add -A
if [ "$STATUS" = "nothing_to_fix" ]; then
# To deklaracja, nie zaliczenie: drzewo musi być nietknięte, a testy zielone.
git diff --cached --quiet || { echo "nothing_to_fix, yet files changed"; exit 1; }
npm test
exit 0
fi
if [ "$STATUS" != "fixed" ]; then echo "Codex status: $STATUS"; exit 1; fi
if git diff --cached --name-only | grep -qE '^(tests/|\.github/|migrations/|package(-lock)?\.json$)'; then
echo "Codex changed a protected path"; exit 1
fi
git diff --cached --numstat | awk '{s+=$1+$2} END {exit (s > 200)}'
npm test
git diff --cached > "$RUNNER_TEMP/codex.patch"
- uses: actions/upload-artifact@v7
if: always()
with:
name: codex-run
path: |
${{ runner.temp }}/events.jsonl
${{ runner.temp }}/fix.json
${{ runner.temp }}/codex.patch

Krok weryfikacji traktuje nothing_to_fix też jako deklarację: pada, gdy drzewo się zmieniło, i mimo to uruchamia testy. Zadanie ma uprawnienia tylko do odczytu repozytorium i produkuje patch jako artefakt; otwarcie pull requesta to osobny, recenzowany krok, opisany na stronie CI/CD z Codex GitHub Action. .github/codex/fix-schema.json spełnia opisane wyżej reguły trybu ścisłego i zawiera pola status ("fixed", "nothing_to_fix", "not_fixed" lub "needs_human"), summary, files_changed i tests_run, wszystkie wymagane.

Not inside a trusted directory and --skip-git-repo-check was not specified. Katalog roboczy nie jest repozytorium Git. Uruchom polecenie z korzenia checkoutu albo wskaż go przez -C <dir>. --skip-git-repo-check dodawaj tylko dla katalogu tymczasowego, który sam utworzyłeś, bo bez Gita tracisz diff, na którym opierają się kroki weryfikacji.

Output schema file … is not valid JSON albo API odrzuca schemat. Pierwszy komunikat oznacza błąd składni w pliku; sprawdź go przez jq . schema.json. Drugi to prawie zawsze tryb ścisły: właściwość, której brakuje w required, albo obiekt bez "additionalProperties": false. Oznacz wszystkie właściwości jako wymagane, a dla wartości opcjonalnych użyj ["string", "null"].

error: unexpected argument '--full-auto' found albo unexpected argument '--sandbox' found. --full-auto już nie istnieje (patrz ostrzeżenie wyżej). --sandbox po resume lub fork nie jest akceptowane; przenieś flagę przed podpolecenie: codex exec --sandbox workspace-write resume <SESSION_ID> "…".

error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'. W 0.157.1 codex exec review przyjmuje gotowy cel (--base, --uncommitted, --commit) albo własne instrukcje przeglądu, nigdy oba; ten sam błąd pojawia się dla --uncommitted i --commit. Usuń flagę i nazwij zakres diffa w instrukcjach albo usuń instrukcje i użyj wbudowanego przeglądu.

Agent zgłasza, że nie mógł zapisać pliku albo zainstalować pakietu. Przebieg jest domyślnie tylko do odczytu, a workspace-write nadal nie ma sieci. Do edycji przyznaj --sandbox workspace-write (w przebiegu wieloetapowym przed resume lub fork), a zależności instaluj w kroku przed codex exec. Nie sięgaj po --dangerously-bypass-approvals-and-sandbox, żeby obejść to na współdzielonym runnerze.

resume --last kontynuuje niewłaściwą sesję. --last oznacza najnowszą sesję dla tego katalogu, a równoległe zadania na jednym runnerze ścigają się o nią. Zapisz thread_id ze zdarzenia thread.started i wznawiaj po identyfikatorze. Jeśli pierwszy etap użył --ephemeral, nie ma czego wznawiać; usuń tę flagę z etapów, które zamierzasz kontynuować.

Kod wyjścia 0, ale zadanie nie jest wykonane. Kod wyjścia raportuje błędy przebiegu, a nie sukces zadania. Opieraj bramkę na werdykcie ze schematu, niezależnym uruchomieniu testów i kontrolach diffa z sekcji o weryfikacji, nigdy na samym kodzie wyjścia.

Na runnerze zadanie zachowuje się inaczej niż na twoim laptopie. config.toml w CODEX_HOME runnera dodaje serwery MCP, inny model albo reguły. Dodaj --ignore-user-config, przypnij model przez -m i użyj --ignore-rules, jeśli pliki execpolicy .rules użytkownika lub projektu nie powinny obowiązywać w tym zadaniu.

Hooki, które powinny się uruchomić, nie działają. Hooki Codeksa wymagają utrwalonego zaufania. --dangerously-bypass-hook-trust uruchamia je bez niego, co jest dopuszczalne tylko wtedy, gdy samo zadanie weryfikuje pochodzenie hooków, i nigdy dla hooków z gałęzi kontrybutora.