把 Codex CLI 內建的 $imagegen 包成簡單的 FastAPI 服務。
在後台幫每個內部腳本、CI 工作、Side Project 各發一把 bearer key,
它們就能共用一份 ChatGPT 訂閱的產圖額度,不用各自燒 OpenAI Images API credits。
你已經付了 ChatGPT 訂閱費。Codex CLI 也已經裝在你 homelab 那台機器上、
codex login 過了。但每一個想產圖的內部腳本、CI workflow、
Discord/Telegram bot 要不就要 ssh 進你的機器跑
codex exec、要不就得另外花 OpenAI Images API 的錢。
這個服務提供第三條路:把 $imagegen 包成一條
Authorization: Bearer cimg_<random-token> 的小 HTTP API,
搭配一個 admin 後台讓你可以隨時發 key、停 key、刪 key。
一個 ChatGPT 帳號、多個 caller、不必多花一塊錢。
medium。
背後跑的是 gpt-image-2,也就是 Codex CLI 內建
image_gen 工具的模型。這個服務 不 對輸出做二次處理或過濾,
$imagegen 產出什麼就回傳什麼,並把這次產圖的歷史記到 SQLite。
前置:Codex CLI 已裝好、
codex login 完成。要跑 container 的話再加 Docker + Docker Compose。
A. 本機測試 — 不需要 nginx
git clone https://github.com/yazelin/codex-image-service cd codex-image-service cp .env.example .env # 編輯 .env:設好 ADMIN_PASSWORD、ADMIN_SESSION_SECRET 即可 docker compose -f docker-compose.local.yml up -d --build open http://localhost:8000/admin
Port 8000 直接 bind 到你主機,不用 reverse proxy。
API 回傳的圖片網址會是
http://localhost:8000/generated/...。
B. 正式環境 — 接到既有 nginx 後面
# .env 還要加: # PUBLIC_BASE_URL=https://YOUR-DOMAIN/codex-image # ADMIN_URL_PREFIX=/codex-image docker compose up -d --build # 然後把 deploy/nginx.codex-image-service.location.conf.example # 貼進你既有的 nginx,nginx -s reload
預設的 docker-compose.yml 會把 container 接到一個既有的
Docker network nginx_bridge_network。初次健康檢查:
curl -sf https://YOUR-DOMAIN/codex-image/health # {"status":"ok"}
C. 完全不用 Docker — 開發最快
python3 -m venv .venv && . .venv/bin/activate pip install -r requirements.txt uvicorn app.main:app --reload --port 8000
直接吃主機的 ~/.codex/auth.json,不掛 volume,改 code 自動 reload。
到 /codex-image/admin 登入,按 Create API Key,
把跳出來的 cimg_<random-token> 複製起來。
重整或離開這頁之後原始字串就消失了(伺服器只留 sha256 hash),然後就可以打 API:
# shell / GitHub Actions curl -sS --fail --max-time 650 \ -X POST https://YOUR-DOMAIN/codex-image/v1/images/generate \ -H "Authorization: Bearer $CODEX_IMAGE_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt":"...","size":"1024x1024","quality":"medium","count":1}'
# python (httpx) import httpx, os r = httpx.post( "https://YOUR-DOMAIN/codex-image/v1/images/generate", headers={"Authorization": f"Bearer {os.environ['CODEX_IMAGE_KEY']}"}, json={"prompt": "...", "size": "1024x1024", "quality": "medium", "count": 1}, timeout=httpx.Timeout(connect=10, read=600, write=30, pool=10), ) images = r.json()["images"] # [{"url": "...", "expires_at": "..."}]
回傳格式:
{
"id": "img_3f81...",
"status": "succeeded",
"images": [{"url": "https://YOUR-DOMAIN/codex-image/generated/img_3f81....png",
"expires_at": "..."}],
"created_at": "..."
}
單一 ChatGPT 訂閱的 per-account 產圖配額才是真正的 throughput 上限。
設兩個以上的 ChatGPT 帳號,服務就會逐請求輪流切 CODEX_HOME;
其中一個失敗時自動 retry 下一個帳號。
# host 端 ~/codex-homes/<label>/ 各放一個 mkdir -p ~/codex-homes/{personal,team} CODEX_HOME=~/codex-homes/personal codex login # 登第一個帳號 CODEX_HOME=~/codex-homes/team codex login # 登第二個帳號 # .env(路徑是 container 內的觀點) CODEX_HOMES=/host_codex_homes/personal:/host_codex_homes/team # 重啟 — ~/codex-homes 父目錄已經被掛進 /host_codex_homes docker compose up -d --build
子資料夾名稱隨你(personal /
team-acme / dev …)。
同一個 ChatGPT 用戶登兩次只會指到同一個 quota 池,**只有用不同 user 帳號**
才會真的多開配額池。
後台 Overview 會每個帳號出一張卡:近 30 天 request 數、成功 / 失敗、
auth token 健康度(綠 ≤6 天、黃 7–9 天、紅 ≥10 天 — access token 剛好
10 天到期),加上 ChatGPT account_id 前 8 碼
讓你辨認。access token 過期前要把 host 端 token 弄新,
auth.json 是 read-only 掛載,container 不會自己刷新:
# 每週 cron:touch 每個 home 觸發 codex token refresh for h in ~/codex-homes/*/; do CODEX_HOME="$h" codex --version >/dev/null; done
名稱進 api_keys 表,token 只存 sha256 hash,原始字串只在建立時顯示一次。
挑一把 key + 打 prompt + 選 size/quality,從後台直接丟一個 job 進去驗證 key 能用,不用開終端機。
每張圖都帶 expires_at(預設 IMAGE_RETENTION_DAYS=7),背景排程定期清 PNG 與工作目錄。
Image Requests 表每一列有 Delete,立刻清檔案、工作目錄、DB row,不必等到自動過期。
後台實際操作(19 秒、無聲):
這個專案是為了我們自己 homelab 的開發與測試而寫的。
與 OpenAI 沒有任何附屬關係,也不在他們的支援範圍內。
它只是包了官方 @openai/codex CLI、把其中的
$imagegen skill 重新導出成 HTTP API;
每一次呼叫都消耗你掛進容器的那一個 ChatGPT 帳號
(~/.codex/auth.json)的額度。
要 fork 或自行架設請先看清楚以下事項:
$imagegen skill、背後的模型(目前 gpt-image-2)、sandbox flag 或檔案配置。這個服務有時要跟著改才會繼續正常。app/services/codex_image.py — 它對 Codex 下的是 --dangerously-bypass-approvals-and-sandbox,因為 bubblewrap 在 Docker 裡跑不起來,請在指向任何重要系統前重新評估威脅模型。