第 4 篇 高手 · 第 10 章
非互動自動化:codex exec
到目前為止,你跟 Codex 都是「面對面」聊天:你打字、它回話、跑到一半要不要動手還會停下來問你。這一章要教你另一種完全不同的用法——不開對話視窗、把任務寫成一張紙條塞給它、它自己跑完吐結果。
這正是把 Codex 變成「自動化工人」的關鍵一招。
10.0 一句話 + 一個比喻
codex exec 是 Codex CLI 的「非互動模式」——你給它一句指令,它不開聊天視窗、不等你按鍵,自己跑完、把結果印到畫面上就退出。
打個比方:
- 平常的
codex(互動模式)像是把同事叫到你旁邊,你一句他一句,過程中他還會回頭問你「這個檔可以改嗎?」。 codex exec像是寫一張工作紙條,塞進一台會自己動的機器,按下開始,它就照紙條一路做到底,做完把成果丟出來給你,中間不打擾你。
為什麼需要這種模式?因為很多場景根本沒有「你」坐在電腦前面:
- 半夜兩點,伺服器自動跑你排程的整理任務。
- GitHub 上有人開了 Pull Request(程式碼修改提案),系統自動叫 Codex 幫忙審查。
- 你寫一個腳本,把網路抓來的資料丟給 Codex 整理成表格,再存檔。
這些都沒有人在旁邊按 [Y/n]。這就是 codex exec 的舞台。
小技巧
「互動 vs 非互動」是這整章的核心分界。記一句口訣就好:有人看著螢幕 → codex;沒人看著、要塞進腳本或 CI → codex exec。
本章只談終端裡的 codex exec。GitHub Actions、雲端委派那些「接到生產線」的玩法,屬於第 11 章,我們這章專心把「一台機器自己跑」這件事學透。
10.1 codex exec 概念與退出碼
它到底做了什麼
官方對 exec 的定義是這樣寫的(逐字):
"Automate workflows or wire Codex into your existing scripts with the
execsubcommand. This runs Codex non-interactively, piping the final plan and results back tostdout."
翻成白話:用 exec 子命令把 Codex 接進你現有的腳本,它會非互動地跑,並把最終計畫與結果送回 stdout(標準輸出,也就是終端機畫面)。
把這句話拆成三個你必須記住的特性:
| 特性 | 白話解釋 |
|---|---|
| 非互動 | 不開 TUI 聊天介面、不等鍵盤輸入,跑完就退出 |
| 結果走 stdout | 最終結果印到畫面,所以可以被 > 存檔、用 |(管道)接給下一個指令 |
| 退出碼可判斷成敗 | 跑完留下一個「退出碼」,腳本可以用它接 && / || 判斷成功還失敗 |
第一次跑跑看
最簡單的形式就是 codex exec 後面接一句你的需求:
🍎 Mac / 🐧 Linux:
codex exec "summarize the repository structure and list top 5 risky areas"
🪟 Windows(PowerShell):
codex exec "summarize the repository structure and list top 5 risky areas"
它會分析你目前所在的這個專案,把「結構摘要 + 最該注意的 5 個風險區」直接印出來,然後退出。全程不會問你任何問題。
小技巧
codex exec 有一個更短的別名 codex e,功能完全一樣,打字懶得多按幾個字時很方便。官方 CLI 參考頁逐字寫:「Use codex exec (or the short form codex e) for scripted or CI-style runs」。
退出碼:腳本怎麼知道它成功還失敗
每個終端指令跑完,都會留下一個叫退出碼(exit code)的數字。0 代表成功,非 0 代表出事了。這是所有 shell 腳本判斷上一步成敗的依據。
codex exec 在這方面有兩條官方明講的行為:
- CLI 功能頁逐字:
exec「the command exits non-zero if submission fails so you can wire it into scripts or CI.」——提交失敗就以非零退出,讓你可以接進腳本或 CI。 - 非互動頁逐字:如果某個「必要的」MCP server(外掛工具,詳見第 9 章)啟動失敗,
codex exec「exits with an error instead of continuing.」——直接以錯誤退出,不會硬著頭皮繼續跑。
你可以這樣親眼看退出碼:
🍎 Mac / 🐧 Linux:
codex exec "do something"
echo $?
🪟 Windows(PowerShell):
codex exec "do something"
echo $LASTEXITCODE
$?(Mac/Linux)或 $LASTEXITCODE(PowerShell)會印出剛才那個指令的退出碼。
重要提醒
官方目前沒有提供一張完整的「退出碼 0/1/2…各代表什麼」對照表。我們只能確定「提交失敗會非零」「必要 MCP 失敗會以錯誤退出」這兩條,其餘各碼的精確意義請以實機 codex exec ...; echo $? 實測為準,別寫死成斬釘截鐵的對照。
別只信退出碼:雙重判斷
正因為退出碼不是萬無一失的單一真相,CI 實務上的建議是「退出碼 + 產出檔案」雙重判斷:不只看它有沒有非零退出,還要看「它該產出的東西(例如一份報告、一個修補檔)到底有沒有真的生出來、是不是空的」。
這個「看 artifact(產出物)在不在」的思路,在第 11 章的自動修復範例會再用到——官方那個範例就是用「修補檔是不是非空」來判斷成功,而不是只看退出碼。
小技巧
這跟你平常用 Codex 的鐵則一樣:「跑完了」不等於「做對了」。多看一眼它真正吐出來的東西,比盲信一個數字可靠。
10.2 結構化輸出(--json / --output-schema / -o)
預設情況下,codex exec 吐給你的是人看的自由文字。但如果你要用程式去接它的輸出(例如自動把結果存進資料庫、貼到 PR 留言、產生報表),自由文字就很難穩定解析。
這時候你需要「結構化輸出」。Codex 提供三個旗標,目的各不相同,先看一張對照表:
| 旗標 | 簡寫 | 它輸出什麼 | 適合 |
|---|---|---|---|
--json | — | 整段過程的 JSONL 事件流(逐行 JSON,含每一步進度) | 想監看「它做了哪些事」、抓 token 用量 |
--output-schema | — | 讓最終訊息符合你給的 JSON Schema(固定欄位) | 要穩定欄位的報表 / metadata |
--output-last-message | -o | 把最終訊息額外寫到一個檔案(同時也印到 stdout) | 存檔、給 CI 後續步驟讀 |
下面一個一個拆。
--json:看它每一步在做什麼
--json 會讓 stdout 變成逐行的 JSON 事件(這種「一行一個 JSON」的格式叫 JSONL,JSON Lines)。每完成一個動作,就吐一行事件出來。
官方列出的事件型別(逐字)有:
- Thread / Turn 事件:
thread.started、turn.started、turn.completed、turn.failed - Item 事件:
item.*(泛指,含item.started、item.completed) - 錯誤事件:
error
item.* 涵蓋的內容包括:agent 訊息、推理、指令執行、檔案修改、MCP 工具呼叫、網路搜尋、計畫更新等。
官方給的一個事件範例長這樣(逐字):
{"type":"turn.completed","usage":{"input_tokens":24763,"cached_input_tokens":24448,"output_tokens":122,"reasoning_output_tokens":0}}
你可以看到它連 token 用量都報給你了。配上 jq(一個處理 JSON 的小工具)就能漂亮地解析:
🍎 Mac / 🐧 Linux:
codex exec --json "summarize repo structure" | jq
🪟 Windows(PowerShell):
codex exec --json "summarize repo structure" | jq
重要提醒
官方目前只公開了「事件型別」和上面那個 turn.completed 範例,各種 item 型別的完整欄位並未逐一公開。所以別把某個欄位名寫死進你的腳本,先實跑 codex exec --json "..." | jq 採樣看看真實長相,以實機輸出為準。
--output-schema:強迫最終結果照你的格式來
--output-schema <檔案路徑> 指向一個 JSON Schema 檔(描述「我要的結果該有哪些欄位、各是什麼型別」)。Codex 會讓最終訊息乖乖符合這個格式。
這在「我要穩定欄位的報表」場景超好用——例如每次都要產出 {專案名稱, 用到的語言} 這種固定結構,比解析一坨自由文字可靠太多。
官方的 schema 範例(逐字):
{
"type": "object",
"properties": {
"project_name": { "type": "string" },
"programming_languages": { "type": "array", "items": { "type": "string" } }
},
"required": ["project_name", "programming_languages"]
}
「嚴格 schema」是必答題,不是選答題
上面那個範例其實漏了一個容易被忽略的必要欄位。--output-schema 吃的是嚴格模式(strict mode)的 JSON Schema,有兩條硬性要求:必須加一行 "additionalProperties": false,而且"required" 陣列要把 properties 裡列出的每一個欄位都寫進去,一個都不能少。少了任何一條,Codex 不會幫你把結果硬湊成自由格式將就過去,而是直接回報 schema 驗證錯誤——這是你的 request 本身在送出那一刻就被拒絕,不是模型「答錯」的問題。很多從網路上複製貼上的「範例 schema」剛好就是漏了這兩條的寬鬆版,照抄下去大機率會炸。把上面的範例補齊成真正嚴格、能穩定跑的樣子:
{
"type": "object",
"properties": {
"project_name": { "type": "string" },
"programming_languages": { "type": "array", "items": { "type": "string" } }
},
"required": ["project_name", "programming_languages"],
"additionalProperties": false
}
養成一個小習慣就能躲掉這個雷:每加一個欄位到 properties,就順手把它的名字加進 required,收尾前再檢查一次最外層有沒有 "additionalProperties": false。巢狀物件(properties 裡面又是一個 object)同一條規矩要逐層都套用,不是只顧最外面那一層。
-o:把結果順手存成一個檔
-o(全名 --output-last-message)把 assistant 的最終訊息寫到指定檔案——而且它同時還是會印到 stdout,不是二選一。
三招最常一起用:讓結果符合 schema、又存成檔給後續步驟讀。
🍎 Mac / 🐧 Linux:
codex exec "Extract metadata" --output-schema ./schema.json -o ./output.json
🪟 Windows(PowerShell):
codex exec "Extract metadata" --output-schema .\schema.json -o .\output.json
小技巧
--json 和 --output-schema 目的不同、可以並用:--json 管「整段過程」的事件流(它做了哪些事),--output-schema 只管「最終訊息」的形狀(結果長怎樣)。一個看過程、一個看結果。
10.3 stdin、pipe、codex exec resume 串接
這一節教你把 codex exec 接進「資料流水線」,以及怎麼讓兩次執行接續同一段記憶。
stdin 只讀一份完整 prompt:先把資料組好
codex exec 沒有「引號放指示、管道再自動附上資料」的雙輸入模式。先記住兩條互斥規則:
- 如果引號裡已有位置引數(prompt),stdin 內容會被忽略;這時不要把日誌或其他資料接在管道前面。
- 如果要讓外部文字成為 prompt 的一部分,就用單獨的
-,並先把「指示 + 已處理資料」組成同一份輸入。
含日誌範例:先遮罩,再把指示和資料一起送
日誌常藏著 token、密碼、cookie、Email、內網網址或使用者資料。先複製成 app.redacted.log,把這些值以固定文字取代後再重新打開檢查;不確定能不能提供時,改用測試日誌。不要把原始日誌、密鑰或個資直接送進 prompt,也不要把遮罩後的檔案提交到公開儲存庫。
🍎 Mac / 🐧 Linux:
# app.redacted.log 必須已先遮罩;這次只做唯讀分析
{
printf '%s\n\n' '以下是已遮罩的應用程式日誌。只分析可能原因與下一步;不要執行指令、修改檔案或連線。'
tail -n 200 app.redacted.log
} | codex exec --sandbox read-only - > log-triage.md
🪟 Windows(PowerShell):
# app.redacted.log 必須已先遮罩;這次只做唯讀分析
$prompt = @'
以下是已遮罩的應用程式日誌。只分析可能原因與下一步;不要執行指令、修改檔案或連線。
'@
$prompt += "`n`n" + ((Get-Content .\app.redacted.log -Tail 200) -join "`n")
$prompt | codex exec --sandbox read-only - > log-triage.md
--sandbox read-only 限制的是 Codex 的可寫範圍;最後的 > log-triage.md 是終端機把回覆存成報告。請先確認該檔名不存在,避免覆寫你原本的分析結果。
這行看似合理,實際會忽略資料
只要引號裡已有 prompt,stdin 就不會被讀。所以下面這行的 context.txt 內容根本不會出現在這次對話裡:
# context.txt 會被忽略:不要用這種寫法傳資料
cat context.txt | codex exec "do X"
需要資料時,請像前面的日誌範例一樣用 -,並且不要再額外給位置引數;兩者只能二選一。
從檔案讀取一份完整 prompt
當 prompt 很長、想存在一個檔案裡(例如 prompt.txt),用 - 告訴 Codex:「整句指令請從 stdin 讀」。官方逐字:「Use codex exec - when you want to force that behavior explicitly.」檔案一樣只能放可安全提供的內容。
🍎 Mac / 🐧 Linux:
codex exec - < prompt.txt
cat prompt.txt | codex exec -
generate_prompt.sh | codex exec --json - > result.jsonl
🪟 Windows(PowerShell):
Get-Content prompt.txt | codex exec -
小技巧
把常用的長 prompt 存成 .txt,用 codex exec - < prompt.txt 一鍵餵進去,團隊就能共用同一份「標準工單」,不必每次手打。檔案只放可安全分享的指示;若含日誌、客戶資料或密鑰名稱以外的憑證,請先遮罩並確認不會被 Git 提交。
stdin 沒關好,CI 裡會卡到天荒地老
社群回報過一個在排程/CI 環境特別容易中招的情況(issue #20919):codex exec "你的 prompt" 明明已經用位置引數給了 prompt、根本用不到 stdin,但只要它是被某個「不會主動關閉 stdin」的父行程呼叫(例如某些 process supervisor、沒設定 stdin 的背景工作),它還是會傻傻等 stdin 送出結束訊號(EOF),從此卡住不動、不吐任何錯誤。這種卡死在 CI 裡特別難查,因為畫面上什麼異常都看不到,就是「跑到一半沒反應了」。
保險做法:只要這次沒有要用管道餵東西,就明講把 stdin 接到空裝置,讓它一開始就沒有懸念。以下是 Mac/Linux 寫法:
codex exec "do the task" < /dev/null
cron、launchd、被其他工具當子行程呼叫的場景尤其該養成這個習慣——多打六個字元,換一個排程任務不會半夜卡死。
夾帶圖片:-i 跟 prompt 的先後順序,跟你在互動模式學的相反
第 4 章教過互動模式怎麼用 -i / --image 附圖,codex exec 一樣吃這個旗標——但有一個容易讓人跌一跤的地方:旗標跟 prompt 的先後順序,跟你剛練熟的互動模式手感是反的。
互動模式你已經習慣寫成「旗標在前、prompt 在後」:
# 互動模式:-i 在前;圖片必須已做資料分級與遮罩
codex -i sanitized-bug.png "這張截圖哪裡有問題?只提出排查建議。"
但 codex exec 的位置引數解析邏輯,吃的是「prompt 先、-i 後」:
# exec 模式:prompt 先、-i 後;圖片必須已做資料分級與遮罩
codex exec "這張截圖哪裡有問題?只提出排查建議。" -i sanitized-bug.png
順序寫反會發生什麼事
如果照互動模式的手感寫成 codex exec -i sanitized-bug.png "這張截圖哪裡有問題?",社群回報過會被誤判成「這次沒有位置引數、改讀 stdin」,結果就是卡住不動,或行為跟你預期的完全不一樣——症狀跟上一個「stdin 沒關好」的雷很像,但根因不一樣:一個是旗標順序寫反,一個是父行程沒關 stdin。兩種都認得,遇到「exec 莫名卡住」時才好判斷該往哪個方向查。
多張圖片一樣支援逗號分隔或重複旗標(跟互動模式語法相同)。哪些終端機貼不了原始影像、貼圖失敗時該怎麼排除,複習第 4 章即可,這裡不重複。
--ephemeral:不持久化 session rollout
平常 codex exec 跑完會把這段對話存成 session rollout 檔(歷程紀錄,放在 ~/.codex/sessions/,詳見第 7 章)。如果這次是「一次性、不需要後續 resume」的 CI 任務,加 --ephemeral 讓它不持久化 session rollout:
codex exec --ephemeral "triage this repository and suggest next steps"
重要提醒
--ephemeral 不持久化可續接的 rollout,所以不能事後 resume。如果你打算做「先分析、再實作」的兩段式串接(下面就講),千萬別加 --ephemeral,否則第二步找不到第一步的 session。它也不等於「任何其他 log/telemetry 都不存在」。
codex exec resume:讓第二次接著第一次
codex exec resume 讓你沿用前一輪的 transcript(對話歷程)、計畫、核准設定繼續跑下一句指令。最經典的用法是「兩段式自動化」:第一段先讓它分析,第二段再叫它動手實作,而且它記得第一段的脈絡。
官方確認的兩種形式(逐字):
codex exec resume --last "now implement the fix you proposed"
codex exec resume <SESSION_ID> "continue with the next step"
--last:接續最近一次的對話。<SESSION_ID>:指定某一個 session 的 ID 來接續。
重要提醒
0.139.0(2026-06-09)修了一個 bug:codex exec resume --last "..." 現在會正確地把尾隨的字串當成新的初始 prompt,而不是誤判成 session ID。所以請確認你的版本 ≥ 0.139.0,舊版這招可能不靈。(版本以實機 codex --version 為準。)
小技巧
還有一個 codex exec resume --all,官方 CLI 參考頁逐字解釋:「Include sessions outside the current working directory when selecting the most recent session.」——意思是挑「最近一次 session」時,連其他目錄的 session 也一起納入考慮(不限於你現在這個資料夾)。當你的排程任務跨多個專案目錄、想接續任意目錄最近那次時用得到。
多階段 resume,別中途換模型
resume 接續的不只是對話內容,還有 prompt 的快取狀態——前面輪次已經送過的內容,同一個模型再問一次可以吃「快取折扣」,省錢又省時間。如果在同一條 resume 鏈裡中途切換 --model,非官方實測觀察到快取命中率會明顯下滑(一份社群測試從八千多 token 的快取命中掉到剩三千多)。兩段式、三段式的自動化流程,盡量從頭到尾固定用同一個模型;真的要換模型,就當成開一段全新的對話,不要用 resume 接下去。
跑很長的自動化鏈,別把安全底線全押在 AGENTS.md 上
第 5 章、第 13 章教你把長期規則寫進 AGENTS.md,平常這樣做完全正確。但社群回報過一種情況:同一個 session 拉得很長之後,AGENTS.md 裡的規則有時在對話後段套用得不如一開始穩定。對「一次性、當下講完就結束」的用法影響不大,但 codex exec resume 串起來的多階段自動化正好就是「拉得很長的 session」——如果某條規則是「絕對不能做 X」這種安全紅線,更保險的做法是把它直接寫進每一次 exec 的 prompt 本身,不要完全依賴 AGENTS.md 在長對話裡全程被穩定遵守。常駐規則放 AGENTS.md、當次安全紅線在 prompt 裡再講一次,雙重保險。
10.4 環境變數認證與 non-TTY 防呆
最後一節處理兩個自動化必踩的雷:怎麼在沒有人登入的環境給它鑰匙,以及怎麼讓它別卡在「等你按 Y」。
CODEX_API_KEY:exec 專屬的鑰匙
在 CI 或排程環境裡,沒有人能坐下來跑 codex login 點按鈕。這時由 CI 的 secret 機制把 CODEX_API_KEY 注入單一、受信任的步驟;命令列本身不寫 API key,也不顯示它的值。注入完成後,該步驟只需要執行一般指令:
# CODEX_API_KEY 由 CI secret 在這一個步驟注入;不要在命令列填入值
codex exec --json "triage open bug reports"
在自己的電腦第一次使用時,請先走第 2 章的登入流程;不要為了省一步,把 API key 貼到終端機、腳本或聊天訊息裡。
這裡有一個非常重要、容易記錯的限制——官方逐字:
"CODEX_API_KEY is only supported in codex exec"
也就是說:CODEX_API_KEY 這個環境變數只有 codex exec 認得,你在互動模式 codex 裡設它是沒用的。
重要提醒
API key 等同密碼。永遠不要把它寫進命令列、腳本、.env、截圖、工單或 Git commit。要用 CI 的 secret 機制(例如 GitHub Actions 的 secrets)在需要的單一步驟注入,並避免把值印進 log。憑證安全的完整規矩請複習第 3 章。
比「別寫死」更進一步:金鑰別跟不受信任的程式碼共用同一個 job
光用 CI 的 secret 機制注入還不夠細。官方明確警告:如果把 CODEX_API_KEY 設成整個 job 層級的環境變數,同一個 job 裡若還跑了「repo 受控的程式碼」(build script、測試、第三方 action),這些程式碼理論上都讀得到這把鑰匙。別人只要在你的 repo 開一個夾帶惡意程式碼的 PR,讓你的 CI 自動去跑 build/test,就有機會把金鑰讀走外流——這條攻擊路徑不需要打穿任何防護,金鑰本來就攤在同一個行程的環境變數裡,誰都讀得到。更穩妥的做法:金鑰所在的那個 step,盡量別跟未信任的程式碼共用同一個 job,或改用官方 openai/codex-action 內建的 proxy 機制(金鑰留在 action 內部,不直接暴露成環境變數)。完整的 GitHub Actions 佈署方式第 11 章會講。
小技巧
認證憑證放在 ~/.codex/auth.json,設定目錄由 CODEX_HOME 控制(預設 ~/.codex)。把 CI 機器上的登入「搬過去」,沿用 auth.json 而不是設 CODEX_API_KEY,屬於第 11 章團隊協作的範圍——但如果你正考慮這條路,先知道兩個限制:auth.json 裡的 token 大約每 8 天左右會自動刷新一次,跑完記得把刷新後的檔案存回去,不然下次又要重新登入;而且它只適合單機、序列化執行,不能讓好幾個 codex exec 平行跑同一份 auth.json(並行寫入互搶,狀態會亂)。多數 CI 場景 CODEX_API_KEY 還是比較省心的預設選擇,auth.json 這條路留給有特殊理由(例如只想用 ChatGPT 訂閱額度、不想另外申請 API key)的情境用。auth.json 本身等同密碼,別 commit、別貼進工單,完整警語複習第 3 章。
non-TTY 防呆:別讓它卡在 [Y/n]
這是自動化最常見的地雷,務必看懂。
互動模式下,Codex 動手前可能會跳出「要不要執行這個指令?[Y/n]」讓你確認。但在 CI / 排程 / agent 這類「non-TTY」環境(non-TTY = 沒有真正的互動終端,沒有人能回應 [Y/n])裡,這個 prompt 沒有人會去按——結果就是任務卡死,或者更糟,某些工具會在 stdin 結束時直接採預設值(等於沒確認就硬跑)。
防呆不是只有一條路。你要先選擇:這個 job 是越界就失敗(fail-closed),還是允許 Auto-review 審查符合資格的越界。兩者都不依賴人工 [Y/n],但安全語意完全不同。
codex -a never exec --sandbox workspace-write "run the cleanup task"
-a never(--ask-for-approval never):從頭到尾不產生 approval request;沙箱內照跑,仍需核准的越界直接失敗,也不會啟動 Auto-review。適合封閉、可預測的 fail-closed CI。--sandbox:同時界定它「能動到哪」(三值read-only/workspace-write/danger-full-access,雙軸權限心法詳見第 6 章)。
若 job 確實需要讓獨立 reviewer 判斷 eligible escalation,則保留 on-request,再設定 approvals_reviewer="auto_review"。本站把這條路封裝成安全 launcher;完整 interactive/exec 配方與權限邊界見第 7 章 7.6。
兩個旗標的可選值速查:
| 旗標 | 簡寫 | 可選值 |
|---|---|---|
--ask-for-approval | -a | untrusted | on-request | never |
--sandbox | -s | read-only / workspace-write / danger-full-access |
寫錯位置,指令直接被拒:-a 要放在 exec 前面
上面那行指令的旗標順序不是隨便排的。-a / --ask-for-approval 是「全域旗標」,跟 codex 與 codex exec 共用同一層,寫法上要放在 exec 這個子指令前面——也就是 codex -a never exec ...,不是 codex exec -a never ...。順序寫反,輕則被當成不認得的旗標直接報錯,重則悄悄沒生效,你以為關掉了核准暫停,其實根本沒關,腳本照樣可能卡在等你按鍵。
相對地,--sandbox、--json、--output-schema、-o、--ephemeral 這些管「這次 exec 具體怎麼跑」的旗標,習慣上放在 exec 後面。一個簡單的經驗法則:偏「這次要不要問我」這類全域行為的旗標放前面,偏「exec 這次輸出長怎樣、可以動多少東西」的旗標放後面。⚠️ 全域旗標與子指令旗標的分界,實際仍以你版本的 codex --help / codex exec --help 為準,部分旗標官方兩邊都收,寫哪邊都通。
不要再說「non-TTY 的 on-request 一定等於 never」
沒有 Auto-review 時,non-TTY 確實無法顯示新的人工核准,越界通常只能失敗;但設定 approvals_reviewer="auto_review" 後,eligible approval request 可以交給 reviewer agent。差別正在「有沒有產生 request、誰來 review」,所以 on-request + auto_review 與 never 不能視為同一個模式。
重要提醒
過去有一個 --full-auto 旗標,現已標為 deprecated(棄用)。官方非互動頁逐字:「Codex keeps codex exec --full-auto as a deprecated compatibility flag and prints a warning. Prefer the explicit --sandbox workspace-write flag in new scripts.」——它只保留為相容路徑、會印警告,新腳本請改用明確的 --sandbox workspace-write。CI 環境用上面的 --sandbox + --ask-for-approval 組合明確控制,別再投資 --full-auto。
小技巧
0.140.0(2026-06-15)起,non-TTY 背景命令可以用 Ctrl-C 中斷,並保留它已有的輸出與退出狀態。換句話說,即使在腳本裡被打斷,它也不會把已經做出來的成果整個丟掉。
看到這裡,你可能會想:「乾脆用 --yolo 把核准跟沙箱都關掉,不是更省事?」——先踩煞車。--dangerously-bypass-approvals-and-sandbox(別名 --yolo)拆的不只是核准暫停,連檔案圍欄和網路圍欄一起拆,官方原話是只在外部已加固的環境內使用。codex exec 自動化真正要解決的是「不要卡在等你按鍵」,不是「乾脆什麼都別擋」——上面 -a never + --sandbox workspace-write 的組合已經解決了卡住的問題,同時還留著沙箱圍欄保護你。--yolo 這條紅線的完整風險,第 6 章已經講透,這裡只提醒一句:能用 -a never + --sandbox 解決的自動化,就不要跳去 --yolo。
非 git 目錄要加 --skip-git-repo-check
codex exec 預設會檢查你是不是在一個 Git 儲存庫裡(這是安全機制,提醒你「改檔前最好有版本控制當安全網」)。如果你故意要在一個非 git 的資料夾跑(例如純粹整理一批檔案),這個檢查會擋你,這時加上:
codex exec --skip-git-repo-check "organize these notes into folders"
小技巧
還有幾個 exec 專屬旗標在進階自動化會用到:--ignore-user-config(跳過 $CODEX_HOME/config.toml,讓這次跑乾淨、不受個人設定干擾)、--ignore-rules(跳過 execpolicy 的 .rules 規則檔)。日常用不到,知道它們存在即可,細節以 codex exec --help 為準。
10.5 🎓 高手進階
前面四節你已經會把 codex exec 接進腳本。這一節是寫給要把它放進正式 CI、跨夜排程、團隊流水線的人——新手可以先跳過,等你真的要「無人值守、出事還能查」時再回來。
小技巧
下面所有逐字旗標都來自官方 CLI 參考頁(reference),它才是旗標全集;非互動頁(noninteractive)是敘事介紹,沒列全。所以有些旗標你在非互動頁找不到,去 reference 頁查就有。
進階入口一:幾個你之前沒見過、但 CI 很需要的旗標
把前面零散提到的補齊成一張速查,全是官方逐字佐證的:
| 旗標 / 別名 | 值 | 官方逐字描述 | 用途 |
|---|---|---|---|
codex e | — | codex exec 短別名 | 少打四個字 |
--json / --experimental-json | bool | "Print newline-delimited JSON events instead of formatted text." | 事件流(--experimental-json 是同義別名) |
--ephemeral | bool | "Run without persisting session rollout files to disk." | 一次性任務不留歷程 |
--full-auto | bool | deprecated 相容路徑、會印 warning | 別用,改 --sandbox workspace-write |
exec resume --last | bool | "Resume the most recent conversation from the current working directory." | 接續本目錄最近一次 |
exec resume --all | bool | "Include sessions outside the current working directory when selecting the most recent session." | 跨目錄挑最近一次 |
--color | always|never|auto | "Control ANSI color in stdout." | CI log 關掉色碼跳脫字元 |
CODEX_API_KEY | key | 環境變數認證,只 codex exec 認得 | 由 CI secret 在單一步驟注入;命令列不寫值 |
小技巧
CI 的 log 檔常被 ANSI 色碼污染成一堆 \033[…m 亂碼。加 --color never 讓 stdout 不帶顏色,log 就乾淨可讀。
進階入口二:--json 事件流的 schema 與 jq 解析鐵律
10.2 節教過 --json 會吐逐行 JSON 事件(JSONL)。要在 CI 裡穩穩解析,先分清楚兩層:
頂層事件型別(官方逐字列舉,可信):thread.started、turn.started、turn.completed、turn.failed、item.*、error。
每個 item.* 裡面包一個 item 物件,有三個相位:item.started →(item.updated)→ item.completed,每個 item 都帶 item.id 和 item.type。官方逐字列出的 item.type 種類有:agent 訊息、推理、指令執行、檔案修改、MCP 工具呼叫、網路搜尋、計畫更新等。
⚠️ 關鍵分界:型別名是官方的(可信),但 item 物件「裡面有哪些欄位」官方只公開了型別名加兩個範例,完整欄位細節目前最齊的來源是第三方 cheatsheet。 下表的欄位名請當「參考、待實機驗」,別寫死:
item.type | 欄位(⚠️ 欄位名待實機驗) |
|---|---|
agent_message | text |
command_execution | command、aggregated_output、exit_code、status |
file_change | changes[]:每筆 {path, kind},kind 為 add/delete/update |
mcp_tool_call | server、tool、arguments、result、error、status |
幾個立刻能用的 jq 配方(jq 是處理 JSON 的小工具,第 9 章提過):
🍎 Mac / 🐧 Linux:
# 1) 不靠 -o,直接從事件流撈「最終訊息」
codex exec --json "summarize repo risks" \
| jq -r 'select(.type=="item.completed" and .item.type=="agent_message") | .item.text'
# 2) 撈 token 用量(成本監控)
codex exec --json "do task" \
| jq -c 'select(.type=="turn.completed") | .usage'
# 3) 撈它實際跑過的每一條命令(稽核)
codex exec --json "..." \
| jq -c 'select(.type=="item.completed" and .item.type=="command_execution")
| {cmd:.item.command, exit:.item.exit_code, status:.item.status}'
🪟 Windows(PowerShell):
codex exec --json "summarize repo risks" | jq -r 'select(.type==\"item.completed\" and .item.type==\"agent_message\") | .item.text'
解析鐵律(三條,務必照做)
- 型別名信官方,欄位名先採樣——寫 filter 前先跑一次
codex exec --json "noop" | jq -c '.type'在你的版本上看真實長相。 - 未知型別一律略過、不報錯——Codex 數天就出一版,schema 沒凍結,parser 要容錯,碰到沒見過的
type跳過就好。 --json事件流 ≠ 磁碟上的 session 檔——~/.codex/sessions/裡的 rollout 檔是另一套形狀(用role/tool_call那類欄位),別把同一個jqfilter 套兩邊。
小技巧
turn.completed 的 usage 分 input_tokens / cached_input_tokens / output_tokens / reasoning_output_tokens 四欄。CI 監控成本時記得把 cached_input_tokens(走快取折扣)和一般 input_tokens 分開計,不然成本會算不準。(實際計價以 OpenAI 官方定價頁為準,本書不報價。)
進階入口三:CI 別只信退出碼——三重判斷
10.1 節說過「退出碼是粗閘,別只信它」。高手在 CI 裡用三重判斷才算數,任一條不過就當失敗:
- 退出碼:
$?非零 → 失敗(官方確認:提交失敗會非零、必要 MCP 啟動失敗會以錯誤退出、git apply衝突也會非零)。 - 失敗事件:
--json串流裡出現turn.failed或error事件 → 失敗(比退出碼更細,有時退出碼是 0 但中途其實出過事)。 - 產出物非空:它「該生出來的東西」(修補檔 / schema 輸出檔)真的存在、而且非空,才算成功。
第二條的 jq 寫法:
🍎 Mac / 🐧 Linux:
out=$(codex exec --json "...")
echo "$out" | jq -e 'select(.type=="turn.failed" or .type=="error")' >/dev/null \
&& { echo "Codex reported a failure event"; exit 1; }
小技巧
第三條的精神,就是官方 CI 自動修復範例的做法:它不單靠退出碼,而是用 if [ -s codex.patch ](檔案存在且非空)判斷有沒有真的產出修補檔。完整的 GitHub Actions 自動修復、cron / launchd 排程那一整套生產線流程,我們下放到第 14 章專章講,這裡先把 exec 本身的判斷心法立好。
進階入口四:--output-schema + MCP 同開的「靜默降級」陷阱 ⚠️
這是高手級 CI 才會踩到、但一踩就很難 debug 的雷,務必記住。
- 觸發條件:你同時
--json搭--output-schema(要求最終訊息照固定 schema),而且這次跑有掛 MCP server / tools。 - 症狀:
--output-schema那層嚴格格式被靜默丟棄,最終訊息退回成沒結構的自由文字——可能缺最外層{}、屬性間漏逗號、key 沒加引號,甚至整段被 markdown code block 包起來。你的下游解析會莫名其妙爆掉,卻找不到錯在哪。
重要提醒
這個行為來自社群回報(GitHub issue #15451,狀態已 Closed),確認版本是 codex-cli 0.116.0;是否在新版已修尚待確認,請以實機測試為準。但「拿到 schema 輸出一定要機械驗證」這條防呆,不管修沒修都該做。
三條規避鐵律:
- 拆兩段:需要嚴格 schema 輸出的步驟,盡量別同時掛 MCP / tools。把「需要 MCP 的分析」和「結構化輸出」拆開——前段用 MCP 分析、後段
resume接續但只跑「無工具的彙整」來產 schema 輸出。 -
jq empty驗合法性:拿到-o或 schema 輸出檔後,一定機械驗一次:🍎 Mac / 🐧 Linux:
jq empty ./output.json && echo "OK: valid JSON" || { echo "FAIL: not valid JSON"; exit 1; }jq empty會把檔讀一遍但不輸出,只要 JSON 不合法就非零退出。FAIL 就 retry 或報錯,別盲信。 - 鎖版本:Codex 數天一版、事件 schema 沒凍結。CI 裡把 Codex 版本釘死(例如本機 cron 用
npm i -g @openai/codex@<版本>釘版),避免哪天 schema 漂移就把你的 parser 整個打爛。
小技巧
「鎖版本 + 採樣 + 容錯 parser」是吃這口飯的三件套。任何把 --json 事件接進長期自動化的人,版本一漂、欄位一改,沒鎖版的腳本就會在某個半夜默默壞掉,而且因為是 non-TTY 沒人看著,你隔天才發現。
本章小結
這一章你學會了把 Codex 從「對話夥伴」變成「自動化工人」:
codex exec非互動跑、結果走 stdout、用退出碼判斷成敗(但別只信退出碼,搭配「產出物在不在」雙重判斷)。- 結構化輸出三招:
--json(看過程的事件流)、--output-schema(固定最終欄位)、-o(順手存檔)。 - 串接:先把指示與已遮罩資料組成一份完整 prompt,再用管道和
-從 stdin 讀入;也能用codex exec resume接續記憶(但--ephemeral不能 resume)。 - 自動化防呆:
CODEX_API_KEY僅由 CI secret 在受信任的單一步驟注入(僅 exec 認得,命令列不寫值);封閉 CI 用--ask-for-approval never讓越界 fail-closed,需要自動審查 eligible escalation 則用on-request + auto_review;非 git 目錄才考慮--skip-git-repo-check。 - 高手進階:CI 別只信退出碼,疊上「失敗事件 + 產出物非空」三重判斷;
--json事件「型別名信官方、欄位先採樣、未知略過、鎖版本」;--output-schema+ MCP 同開有靜默降級陷阱,拿到輸出一定jq empty驗。
下一章我們會繼續把 exec 接到更大的場景;而 GitHub Actions 自動審 PR、cron / launchd 跨夜排程那一整套生產線流程,留到第 14 章專章講透。
動手試試
- 跑
codex exec "summarize the repository structure",看它印什麼;再跑一次echo $?(PowerShell 用echo $LASTEXITCODE)看退出碼。 - 跑
codex exec --json "list the top 3 files in this repo" | jq,觀察 JSONL 事件流,找找看有沒有turn.completed那一行的 token 用量。 - 把一句不含密鑰、個資或原始日誌的長指令存進
prompt.txt,用codex exec - < prompt.txt餵進去,體會「整段 prompt 從檔案來」的感覺。 - (進階)跑
codex exec --json "list files" | jq -c 'select(.type=="turn.failed" or .type=="error")',正常情況應該沒有任何輸出——這就是 CI 用「失敗事件」當第二道閘門的偵測寫法。