Codex SDK w praktyce: wątki, ustrukturyzowany wynik i weryfikacja
Codex SDK (@openai/codex-sdk) to biblioteka TypeScript od OpenAI do uruchamiania agenta Codex z własnego kodu. Uruchamia CLI codex, zakłada lub wznawia wątki i zwraca dla każdej tury końcową odpowiedź, zmiany w plikach, wykonane polecenia oraz zużycie tokenów. Sprawdza się tam, gdzie między turami potrzebna jest logika programu; do jednorazowego skryptu wystarczy codex exec.
Twój zespół chce przycisku „Napraw to” przy czerwonych buildach: serwis pobiera gałąź, prosi Codexa o przyczynę i minimalną poprawkę, a pull request otwiera tylko wtedy, gdy testy przechodzą. Pętla w bashu wokół codex exec doprowadzi cię do połowy drogi. Potem potrzebujesz typowanych wyników, ponowienia w tej samej rozmowie i bramki, która odrzuci łatkę dotykającą plików, których nie powinna. Ta strona jest dla dewelopera, który buduje taki serwis, i dla tech leada, który musi ufać temu, co serwis wypuszcza.
Co zyskujesz, budując na Codex SDK
Dział zatytułowany „Co zyskujesz, budując na Codex SDK”- Działającą integrację w TypeScript z jawnie ustawionym sandboxem, polityką zatwierdzeń i dostępem do sieci, zamiast tego, co akurat jest w
config.tomlna danej maszynie. - Schemat ustrukturyzowanego wyniku, który zamienia odpowiedź agenta w dane możliwe do sprawdzenia w kodzie.
- Bramkę weryfikacyjną, która sama ponownie uruchamia testy i odrzuca edycje poza zakresem, więc nikt nie musi czytać każdego diffa.
- Wzorce streamingu postępu, wznawiania wątku po ID i przerywania tury, która wymknęła się spod kontroli.
- Tabelę awarii z dokładnymi komunikatami błędów rzucanymi przez SDK i sposobem wyjścia z każdej sytuacji.
Kiedy użyć SDK zamiast codex exec?
Dział zatytułowany „Kiedy użyć SDK zamiast codex exec?”SDK to nakładka, a nie osobny agent. README pakietu mówi, że SDK „spawns the CLI and exchanges JSONL events over stdin/stdout”: każde wywołanie run() uruchamia codex exec --experimental-json z flagami zbudowanymi z twoich opcji. Czego nie potrafi CLI, tego nie potrafi też SDK. SDK dokłada typowane zdarzenia, obiekty wątków i obsługę błędów w twoim języku.
| Potrzebujesz | Użyj | Dlaczego |
|---|---|---|
| Jednego promptu ze skryptu powłoki lub kroku CI | codex exec | --json, --output-schema i codex exec resume już to pokrywają, bez serwisu w Node do utrzymania |
| Codexa przy każdym pull requeście w GitHub Actions | openai/codex-action@v1 | Gotowe uprawnienia, zero kodu |
| Cyklicznej pracy według harmonogramu | Automatyzacje Codexa | Harmonogram, historia i widok przeglądu są wbudowane |
| Długich zadań poza twoją maszyną, w kontenerach OpenAI | Codex cloud | Środowiskiem i kontenerem zarządza OpenAI |
| Logiki programu między turami: rozgałęzienia po wyniku, ponowienia, bramki, kolejki, dashboardu | Codex SDK | Typowane elementy, opcje per wątek, ustrukturyzowany wynik, AbortSignal |
| Rozbudowanego klienta z interaktywnymi zatwierdzeniami | codex app-server (eksperymentalny w 0.157.1) | Protokół JSON-RPC; SDK nie ma callbacku do zatwierdzeń |
Codex nie może już działać jako serwer MCP (podpolecenie mcp-server usunięto w 0.154.0), więc z kodu sterujesz nim przez SDK, codex exec albo codex app-server. Jeśli wybierasz między dostawcami, strona sterowanie agentami z kodu porównuje Codex SDK z Claude Agent SDK i Cursor SDK na tym samym zadaniu.
Wszystko poniżej sprawdzono 2026-09-26 na @openai/codex-sdk 0.157.1 (README i definicje typów) oraz na codex exec --help z Codex CLI 0.157.1.
Skonfiguruj Codex SDK
Dział zatytułowany „Skonfiguruj Codex SDK”-
Zainstaluj pakiet w projekcie na Node.js 18+. Zależy on od
@openai/codexw tej samej wersji (dziś oba to 0.157.1), więc przypnij wersję dokładnie i aktualizuj oba razem:Okno terminala npm install --save-exact @openai/codex-sdkW Pythonie oficjalny pakiet to
openai-codexna PyPI (0.157.1, wersjonowany razem z CLI). Pakiet o podobnej nazwieopenai-codex-sdknie pochodzi od OpenAI; nie instaluj go. -
Uwierzytelnij maszynę, na której działa SDK. Na stacji roboczej
codex loginzapisuje dane logowania, które odczytuje uruchomione CLI. Na serwerze pobierz klucz API z magazynu sekretów i przekaż go jakoapiKey; SDK wstrzykuje go do procesu CLI jakoCODEX_API_KEY. Opcje kont opisuje strona uwierzytelnianie w Codexie. -
Daj każdemu zadaniu własny checkout Gita. CLI odmawia pracy poza repozytorium Gita, chyba że przekażesz
skipGitRepoCheck: true, a dwa wątki edytujące ten sam katalog nadpisują sobie nawzajem zmiany. Osobny worktree Gita na zadanie rozwiązuje oba problemy. -
Ustaw opcje bezpieczeństwa wątku jawnie, jak w następnej sekcji. Pominięte opcje biorą wartości z
~/.codex/config.tomlna danej maszynie, a ten plik na laptopie i na runnerze CI wygląda inaczej.networkAccessEnabled: falseobejmuje tylko polecenia powłoki agenta: ustaw teżwebSearchMode: "disabled"i daj zadaniuCODEX_HOME, któregoconfig.tomlnie deklaruje żadnych serwerów MCP, bo wyszukiwanie w sieci i narzędzia MCP to osobne drogi do sieci.
Uruchom pierwszy wątek z jawnymi ustawieniami bezpieczeństwa
Dział zatytułowany „Uruchom pierwszy wątek z jawnymi ustawieniami bezpieczeństwa”import { execFileSync } from "node:child_process";import { Codex } from "@openai/codex-sdk";
// Zapisz commit bazowy przed pierwszą turą: bramka weryfikacji liczy diff względem niego.const base = execFileSync("git", ["rev-parse", "HEAD"], { cwd: "/srv/jobs/4812/repo" }).toString().trim();
const codex = new Codex({ apiKey: process.env.CODEX_API_KEY });
const thread = codex.startThread({ workingDirectory: "/srv/jobs/4812/repo", // worktree tego zadania sandboxMode: "workspace-write", // edycje tylko wewnątrz checkoutu approvalPolicy: "never", // nikt nie zatwierdza; zablokowane akcje kończą się błędem networkAccessEnabled: false, // bez sieci dla poleceń powłoki agenta webSearchMode: "disabled", // wyszukiwanie w sieci to osobny przełącznik});
const turn = await thread.run( "Run `npm test`. For each failing test, find the root cause and describe it. Do not edit files yet.");
console.log(turn.finalResponse); // ostatnia wiadomość agentaconsole.log(turn.usage); // tokeny: wejściowe, z cache, zapisane do cache, wyjściowe, na rozumowanieconsole.log(thread.id); // zapisz, by później wznowić wątekrun() czeka na koniec całej tury i zwraca { items, finalResponse, usage }. items zawiera wszystko, co zrobił agent, jako typowane obiekty: command_execution (z command, aggregated_output i exit_code), file_change (ze ścieżkami zmienionych plików), mcp_tool_call, web_search, todo_list, reasoning, error i agent_message. To właśnie te elementy sprawdza twój kod weryfikujący.
Nie ustawiaj model, a wątek użyje domyślnego modelu Codexa (GPT-6 Astra w 0.157.1). Zanim zmienisz model, dostrój modelReasoningEffort; opcje i ceny znajdziesz w przeglądzie modeli. Jeśli chodzi o sandbox, SDK udostępnia starszą flagę sandboxMode (read-only, workspace-write, danger-full-access). OpenAI zaleca teraz profile uprawnień (beta), które przekażesz przez opcję config konstruktora Codex, na przykład new Codex({ config: { default_permissions: ":workspace" } }). Opcja config istnieje tylko w konstruktorze, więc profil obowiązuje we wszystkich wątkach danej instancji Codex. Na jedną instancję Codex wybierz jeden system: według OpenAI profile i starszy sandbox „do not compose”, więc jeśli używasz profilu, usuń sandboxMode i networkAccessEnabled z opcji wątku. Dostęp do sieci zależy wtedy od tego, na co pozwala wybrany profil, więc sprawdź jego definicję (profil nazwany znajduje się w sekcji [permissions] w config.toml). Przykłady na tej stronie zostają przy starszej parze sandboxMode i networkAccessEnabled, bo daje ona każdemu wątkowi jawny przełącznik wyłączający sieć. Nigdy nie używaj danger-full-access poza jednorazowym kontenerem z ograniczoną siecią.
Wywołaj run() ponownie na tym samym obiekcie Thread, aby kontynuować rozmowę. Agent pamięta kontekst wcześniejszych tur, więc drugi prompt może mówić „poprawka, którą opisałeś” bez powtarzania jej treści.
Ustrukturyzowany wynik, który sprawdzi twój kod
Dział zatytułowany „Ustrukturyzowany wynik, który sprawdzi twój kod”Swobodny tekst wystarczy człowiekowi, ale bramce nic nie daje. Przekaż schemat JSON jako outputSchema, a końcowa wiadomość agenta stanie się JSON-em w tym kształcie. Rzutowanie typu w TypeScripcie nie sprawdza tego w czasie działania, więc zweryfikuj sparsowany obiekt, zanim odczyta go jakakolwiek bramka: gdy tura skończy się nietypowo, brakujące risk byłoby undefined i przeszłoby jako „nie wysokie”. SDK zapisuje schemat do pliku tymczasowego i przekazuje go do CLI jako --output-schema. Schemat musi być zwykłym obiektem, inaczej run() rzuca outputSchema must be a plain JSON object.
const fixReport = { type: "object", properties: { root_cause: { type: "string" }, files_changed: { type: "array", items: { type: "string" } }, tests_passed: { type: "boolean" }, risk: { type: "string", enum: ["low", "medium", "high"] }, }, required: ["root_cause", "files_changed", "tests_passed", "risk"], additionalProperties: false,} as const;
const fix = await thread.run( "Implement the smallest fix for the root cause you found. Run `npm test` after the change. " + "Report the files you changed and whether the full suite passed.", { outputSchema: fixReport, signal: AbortSignal.timeout(20 * 60 * 1000) });
type FixReport = { root_cause: string; files_changed: string[]; tests_passed: boolean; risk: "low" | "medium" | "high";};
// Rzutowanie niczego nie sprawdza w runtime: zweryfikuj kształt, zanim bramka się na nim oprze.function parseReport(text: string): FixReport { const r = JSON.parse(text) as Partial<FixReport> | null; // rzuca, gdy tekst nie jest JSON-em if ( typeof r?.root_cause !== "string" || !Array.isArray(r.files_changed) || !r.files_changed.every((f) => typeof f === "string") || typeof r.tests_passed !== "boolean" || !["low", "medium", "high"].includes(String(r.risk)) ) { throw new Error("Rejected: final response does not match fixReport"); } return r as FixReport;}
const report = parseReport(fix.finalResponse);Opcja signal przerywa turę, która przekroczy twój budżet czasu. Dwadzieścia minut to przykład; ustal limit na podstawie historii własnych zadań.
Zweryfikuj pracę agenta bez czytania każdej linii
Dział zatytułowany „Zweryfikuj pracę agenta bez czytania każdej linii”Raport to deklaracja agenta. Bramka konfrontuje ją z dowodami, których agent nie podrobi: z diffem Gita w worktree, z typowanymi elementami tury i z przebiegiem testów, który uruchamia twój własny kod. Uruchom ją, zanim cokolwiek opuści worktree zadania.
import { execFileSync } from "node:child_process";import path from "node:path";import type { FileChangeItem, CommandExecutionItem } from "@openai/codex-sdk";
const repo = "/srv/jobs/4812/repo";const ALLOWED = ["src/", "test/"];const git = (...args: string[]) => execFileSync("git", args, { cwd: repo }).toString();const lines = (s: string) => s.split("\n").filter(Boolean);const rel = (p: string) => path.relative(repo, path.resolve(repo, p));
// `base` to commit zapisany przed pierwszą turą (zob. przykład pierwszego wątku): jeśli agent// albo hook zrobi commit w trakcie przebiegu, HEAD się przesunie, a diff względem niego ukryje te zmiany.
// Źródłem prawdy jest Git, nie elementy tury: zmiany zrobione poleceniami powłoki// (sed -i, npm install przepisujący lockfile, skrypt generujący kod) nigdy nie są file_change.git("add", "-A");const touched = lines(git("diff", "--cached", "--name-only", base));const outOfScope = touched.filter((f) => !ALLOWED.some((dir) => f.startsWith(dir)));const unreported = touched.filter((f) => !report.files_changed.map(rel).includes(f));
if (outOfScope.length || unreported.length) { throw new Error(`Rejected: out of scope ${outOfScope}, unreported ${unreported}`);}
// Ścieżki z diffu, których nie zgłosił żaden element file_change, zmieniło polecenie: zapisz je.const viaFileChange = new Set( fix.items .filter((i): i is FileChangeItem => i.type === "file_change" && i.status === "completed") .flatMap((i) => i.changes.map((c) => rel(c.path))));const viaShell = touched.filter((f) => !viaFileChange.has(f));const failedCommands = fix.items.filter( (i): i is CommandExecutionItem => i.type === "command_execution" && i.status === "failed");
// Istniejący test zmieniony, usunięty lub przemianowany przez agenta może ukryć błąd: do człowieka.const editedTests = lines(git("diff", "--cached", "--name-status", base)) .map((l) => l.split("\t")) .filter(([status, file]) => /^[MDR]/.test(status) && file.startsWith("test/")) .map(([, file]) => file);
// Nie ufaj tests_passed: uruchom testy sam, stałym poleceniem.// Testy wykonują kod napisany przez agenta, więc usuń klucz API z ich środowiska.const { CODEX_API_KEY: _key, ...testEnv } = process.env;execFileSync("npm", ["test"], { cwd: repo, stdio: "inherit", env: testEnv }); // rzuca przy niezerowym kodzie wyjścia
// Limit rozmiaru: „minimalna poprawka” na 400 linii trafia do człowieka.const stat = git("diff", "--cached", "--shortstat", base);const MAX_LINES = 150;const linesChanged = (s: string) => [...s.matchAll(/(\d+) (?:insertion|deletion)/g)].reduce((n, m) => n + Number(m[1]), 0);const needsHuman = linesChanged(stat) > MAX_LINES || report.risk === "high" || editedTests.length > 0; // te kieruj do recenzentaCo udowadnia ta bramka i kto zatwierdza wynik:
- Zakres. Każda ścieżka z diffu Gita w indeksie leży w dozwolonych katalogach i figuruje w raporcie. Źródłem jest diff, a nie elementy
file_change, bo plik zmieniony poleceniem powłoki (sed -i,npm installprzepisującypackage-lock.json, skrypt generujący kod) nigdy nie pojawia się jakofile_change. Diff liczysz względembase, czyli commita zapisanego przed pierwszą turą, a nie względemHEAD: agent albo hook Gita, który zrobi commit w trakcie przebiegu, przesuwaHEAD, a diff względem niego ukryłby te zmiany. Niezgłoszona edycja oznacza twarde odrzucenie, bo raport przestaje być wiernym podsumowaniem. ZapisujviaShell, czyli ścieżki bez pokrycia wfile_change, żeby recenzent widział, które zmiany pochodzą z poleceń. - Zachowanie. Twój kod uruchamia stałe polecenie testowe, nigdy polecenie zasugerowane przez agenta, i robi to bez
CODEX_API_KEYw środowisku, bo testy wykonują kod, który agent właśnie napisał. Jeśli zadanie ma jakikolwiek inny sekret, uruchom testy w tym samym kontenerze z ograniczoną siecią co agenta. Zielonetests_passedprzy czerwonym przebiegu na hoście to odrzucenie i sygnał, by przyjrzeć się promptowi. Zielony przebieg na hoście nic nie dowodzi, jeśli agent osłabił asercję, która nie przechodziła, dlatego każdy istniejący plik wtest/, który diff pokazuje jako zmieniony, usunięty lub przemianowany, ustawianeedsHuman; nowo dodane pliki testów tego nie robią. Dla ostrzejszej kontroli uruchom też pierwotnie nieprzechodzące testy w wersji plików testowych z commita bazowego. - Rozmiar.
git add -Anajpierw dodaje nowe pliki do indeksu, bogit diffpomija pliki nieśledzone, a łatka, która tylko dodaje pliki, pokazałaby zero linii. Zapisz wynik--shortstati kieruj do recenzenta zadania zneedsHuman(powyżejMAX_LINES, zrisk: "high"albo ze zmienionym istniejącym testem); 150 linii to przykładowy próg. Zapisuj teżfailedCommands: przebieg, który pięć razy ponawiał nieudaną migrację, zasługuje na uwagę, nawet jeśli skończył się na zielono. - Zatwierdzenie. Łatki o niskim ryzyku, w zakresie i z zielonymi testami otwierają pull request, który nadal przechodzi zwykły code review i CI. Bramka decyduje, co trafia do recenzenta; nie zastępuje zgody na merge. Dołącz do pull requesta raport, log elementów i wynik testów z hosta jako pakiet dowodów.
Śledź postęp na bieżąco i zapisuj zużycie tokenów
Dział zatytułowany „Śledź postęp na bieżąco i zapisuj zużycie tokenów”run() wraca dopiero po zakończeniu tury. Na potrzeby dashboardu lub pipeline’u logów runStreamed() zwraca { events }, asynchroniczny generator tych samych zdarzeń JSONL, które wypisuje codex exec --json:
const { events } = await thread.runStreamed("Update the changelog for the fix you made.");
for await (const event of events) { if (event.type === "thread.started") log({ threadId: event.thread_id }); if (event.type === "item.completed") log({ item: event.item.type, id: event.item.id }); if (event.type === "turn.completed") log({ usage: event.usage }); // tokeny na turę if (event.type === "turn.failed") log({ error: event.error.message });}Wysyłaj usage z turn.completed tam, gdzie trafiają inne metryki zadań. Tokeny wejściowe, wejściowe z cache, zapisane do cache, wyjściowe i wyjściowe na rozumowanie w podziale na zadania to dokładnie to, czego potrzebuje zarządzanie kosztami Codexa, by przypisać wydatki do pipeline’u, a nie do osoby.
Wznów wątek w kolejnym etapie
Dział zatytułowany „Wznów wątek w kolejnym etapie”thread.id jest uzupełniane, gdy startuje pierwsza tura. Zapisz je razem z zadaniem, a kolejny etap (na przykład po tym, jak człowiek zatwierdzi diagnozę) może kontynuować tę samą rozmowę:
const resumed = codex.resumeThread(savedThreadId, { workingDirectory: "/srv/jobs/4812/repo", sandboxMode: "workspace-write", approvalPolicy: "never", networkAccessEnabled: false, webSearchMode: "disabled",});await resumed.run("The reviewer approved your diagnosis. Implement the fix.");Przekaż opcje wątku jeszcze raz. To flagi CLI podawane przy każdym uruchomieniu, a nie ustawienia zapisane w wątku, więc wznowienie bez nich działa na domyślnej konfiguracji hosta. Stan wątków leży w CODEX_HOME (domyślnie ~/.codex: katalog sessions/ i bazy danych stanu) na maszynie, która je uruchomiła; wznowienie w innym kontenerze się nie powiedzie, chyba że cały CODEX_HOME leży na trwałym wolumenie.
Jak bezpiecznie udostępnić Codexa narzędziom wewnętrznym
Dział zatytułowany „Jak bezpiecznie udostępnić Codexa narzędziom wewnętrznym”Endpoint HTTP, który przekazuje req.body.prompt do thread.run(), pozwala każdemu, kto do niego dotrze, uruchamiać polecenia na twoim serwerze. Jeśli budujesz wewnętrzny przycisk „Napraw to”, zostaw kontrolę w swoim kodzie:
- Wywołujący wybiera typ zadania i ID zgłoszenia, nigdy dowolny prompt ani ścieżkę. Twój kod mapuje typ zadania na stały szablon promptu i repozytorium z listy dozwolonych.
- Traktuj treść zgłoszeń, opisy issues i logi CI jako niezaufane dane. Cytuj je w prompcie jako materiał do analizy i trzymaj
approvalPolicy: "never". PrzynetworkAccessEnabled: false,webSearchMode: "disabled"i braku serwerów MCP w konfiguracjiCODEX_HOMEzadania wstrzyknięta instrukcja nie ma żadnej drogi do sieci, którą mogłaby coś pobrać albo wyprowadzić. - Zadania czysto analityczne uruchamiaj z
sandboxMode: "read-only", aworkspace-writedawaj tylko etapowi poprawki. - Kolejkuj zadania i dawaj każdemu osobny worktree; nie uruchamiaj dwóch wątków w jednym katalogu.
- Uwierzytelniaj endpoint, loguj
thread.idrazem z użytkownikiem, który zlecił zadanie, i pozwól bramce opisanej wyżej decydować, co staje się pull requestem.
Pełny przepływ od zgłoszenia do pull requesta we wszystkich narzędziach opisuje pipeline od issue do PR.
Co psuje się, gdy Codex SDK działa na produkcji?
Dział zatytułowany „Co psuje się, gdy Codex SDK działa na produkcji?”| Objaw | Przyczyna | Jak wyjść |
|---|---|---|
run() rzuca Codex Exec exited with code 1: … | Zawiodło samo CLI: brak danych logowania, katalog poza repozytorium Gita albo klucz konfiguracji odrzucony przez CLI. Komunikat kończy się stderr z CLI | Przeczytaj koniec stderr. Uruchom codex login status na hoście, sprawdź, czy workingDirectory to checkout Gita, i przetestuj te same opcje przez codex exec w terminalu |
run() rzuca błąd z komunikatem modelu | Tura wyemitowała turn.failed; SDK zamienia to na Error | Zaloguj thread.id, ponów raz w tym samym wątku albo wznów go później. Powtarzające się błędy na jednym prompcie to zwykle problem z limitem lub kontekstem: zobacz zarządzanie kosztami |
Po ustawieniu env CLI nie widzi danych logowania ani narzędzi | Opcja env całkowicie zastępuje dziedziczone środowisko (SDK tylko dopisuje na początek PATH własne katalogi z dołączonymi plikami binarnymi) | Przekaż PATH i HOME (albo CODEX_HOME) razem z własnymi zmiennymi; SDK nadal dodaje CODEX_API_KEY, gdy ustawisz apiKey |
| Tura trwa godzinę | Brak limitu czasu i pętla nieudanych poleceń | Przekaż signal: AbortSignal.timeout(…); znajdź pętlę w elementach command_execution ze status: "failed" |
parseReport() rzuca wyjątek: JSON.parse się wywraca albo raport nie pasuje do fixReport | Tura zakończyła się komunikatem o błędzie albo w tej turze nie przekazano outputSchema | Złap wyjątek, wyślij jedno doprecyzowanie w tym samym wątku („Return only the JSON object for the schema”) i odrzuć zadanie, jeśli znowu się nie uda |
resumeThread() zawodzi albo zaczyna od zera | Pliki sesji są na innej maszynie albo zadanie działało z ulotnym CODEX_HOME | Utrwalaj CODEX_HOME per zadanie albo zacznij nowy wątek od podsumowania poprzedniego etapu |
| Agent edytuje nie te pliki albo dwa zadania psują sobie nawzajem pracę | Wspólny katalog roboczy albo nieustawione workingDirectory (SDK używa wtedy bieżącego katalogu procesu) | Jeden worktree na zadanie, zawsze przekazuj workingDirectory i zostaw sprawdzanie zakresu w bramce |
| Instrukcja wstrzyknięta w treść zgłoszenia i tak sięga do sieci | networkAccessEnabled: false blokuje tylko polecenia powłoki agenta; wyszukiwanie w sieci (webSearchMode) i serwery MCP z config.toml hosta to osobne drogi | Ustaw webSearchMode: "disabled" w każdym wątku i przy każdym wznowieniu, a CODEX_HOME skieruj na konfigurację zadania bez serwerów MCP |
Zachowanie zmienia się po npm update | Wersje SDK i CLI zmieniają się razem, a nowe wydania CLI zmieniają wartości domyślne | Przypnij SDK dokładnie i aktualizuj go w osobnym pull requeście, z uruchomionymi testami bramki |
Dokąd dalej z Codex SDK
Dział zatytułowany „Dokąd dalej z Codex SDK”- Tryb headless i nieinteraktywny opisuje flagi
codex exec, na których opiera się SDK; zacznij tam, jeśli wystarczy ci skrypt. - Codex w CI/CD pokazuje drogę przez GitHub Action, gdy nie potrzebujesz własnego serwisu.
- Worktrees w Codexie wyjaśniają izolację, w której powinno działać każde zadanie SDK.
- Sterowanie agentami z kodu porównuje Codex SDK z Claude Agent SDK i Cursor SDK na tym samym zadaniu.
- Pakiety dowodów określają, co powinien zawierać pull request otwarty przez pipeline, by recenzent miał na czym się oprzeć.