From f8f67524ae2cedb855bc89ee47386e0255c4000e Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 09:48:24 +0000 Subject: [PATCH 1/6] =?UTF-8?q?feat(codex=5Faccount):=20=E6=94=B9=E7=94=A8?= =?UTF-8?q?=20shell=20=E5=8F=96=E5=BE=97=E7=99=BB=E5=85=A5=E5=B8=B3?= =?UTF-8?q?=E8=99=9F=E4=B8=A6=E8=A3=9C=E4=B8=8A=E5=AE=B9=E5=99=A8=E7=92=B0?= =?UTF-8?q?=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 23 +++++++++ action.yml | 17 ++++++- app/codex_account.py | 115 +++++++++++++++++++++---------------------- 3 files changed, 95 insertions(+), 60 deletions(-) create mode 100644 Dockerfile mode change 100644 => 100755 app/codex_account.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..68ac4c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:latest + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash \ + ca-certificates \ + coreutils \ + gawk \ + jq \ + nodejs \ + npm \ + && npm install -g @openai/codex \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /workspace + +COPY app/codex_account.py /usr/local/bin/codex-account +RUN chmod +x /usr/local/bin/codex-account + +CMD ["codex-account"] diff --git a/action.yml b/action.yml index 28e24b3..7843853 100644 --- a/action.yml +++ b/action.yml @@ -62,6 +62,21 @@ runs: exit 1 fi + # 安裝 jq,供 app/codex_account.py 以 shell 解析 JSON-RPC 回應。 + if ! command -v jq >/dev/null 2>&1; then + if command -v apt-get >/dev/null 2>&1; then + apt_get='apt-get' + if command -v sudo >/dev/null 2>&1; then + apt_get='sudo apt-get' + fi + $apt_get update + $apt_get install -y jq + else + echo 'jq is required but apt-get is not available.' >&2 + exit 1 + fi + fi + # 全域安裝 Codex CLI 套件,安裝後可直接使用 codex 指令 npm install -g @openai/codex @@ -97,7 +112,7 @@ runs: # 當 PROMPT 為空時,改走「取得登入帳號 email」分支 if [ -z "$PROMPT" ]; then # 呼叫 codex_account.py 取得目前登入帳號的 email($GITHUB_ACTION_PATH 為 action 根目錄) - email="$(python3 "$GITHUB_ACTION_PATH/app/codex_account.py")" + email="$(bash "$GITHUB_ACTION_PATH/app/codex_account.py")" # 將 email 寫入 text 輸出($GITHUB_OUTPUT 為步驟輸出檔) echo "text=$email" >> "$GITHUB_OUTPUT" # 以 0 正常退出,不再執行 codex exec diff --git a/app/codex_account.py b/app/codex_account.py old mode 100644 new mode 100755 index e73ae55..6087e9f --- a/app/codex_account.py +++ b/app/codex_account.py @@ -1,73 +1,70 @@ -"""透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。 +#!/usr/bin/env bash +# 透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。 +# +# 這是 TUI `/status` Account 欄位的程式化來源,不解析本地 OAuth token, +# 而是由 codex 自身回報登入帳號。將 email 印到 stdout(取不到時印空字串)。 -這是 TUI `/status` Account 欄位的程式化來源,不解析本地 OAuth token, -而是由 codex 自身回報登入帳號。將 email 印到 stdout(取不到時印空字串)。 -""" +set -uo pipefail -import json -import subprocess -import time +timeout_seconds="${1:-25}" +if ! [[ "$timeout_seconds" =~ ^[0-9]+([.][0-9]+)?$ ]]; then + echo "timeout_seconds must be a number." >&2 + exit 2 +fi +if ! command -v jq >/dev/null 2>&1; then + echo "jq is required." >&2 + exit 2 +fi -def read_account_email(timeout_seconds: float = 25.0) -> str: - """透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。 +email="" - 啟動 ``codex app-server`` 子行程,依序送出 initialize / initialized / - account/read 三筆 JSON-RPC 訊息,並讀取其回報的登入帳號 email。 +cleanup() { + if [[ -n "${CODEX_SERVER_PID:-}" ]]; then + kill "$CODEX_SERVER_PID" >/dev/null 2>&1 || true + wait "$CODEX_SERVER_PID" >/dev/null 2>&1 || true + fi +} +trap cleanup EXIT - Args: - timeout_seconds: 等待 app-server 回應的秒數上限,預設 25.0。 +coproc CODEX_SERVER { codex app-server 2>/dev/null; } +exec {codex_server_stdin}>&"${CODEX_SERVER[1]}" +exec {codex_server_stdout}<&"${CODEX_SERVER[0]}" - Returns: - 登入帳號的 email;取不到或逾時時回傳空字串 ""。 +send_json() { + printf '%s\n' "$1" >&"$codex_server_stdin" +} - 使用情境: - 在 CI 中驗證登入身分時呼叫。前置條件為 codex CLI 已安裝, - 且 $HOME/.codex/auth.json 已寫入有效的 OAuth token。 - """ - process = subprocess.Popen( - ["codex", "app-server"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - text=True, - bufsize=1, - ) +send_json '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"ci","version":"1.0"}}}' +send_json '{"jsonrpc":"2.0","method":"initialized","params":{}}' +send_json '{"jsonrpc":"2.0","id":2,"method":"account/read","params":{}}' - def send(obj): - """將 dict 物件序列化成 JSON 後寫入 app-server 的 stdin 並 flush。""" - process.stdin.write(json.dumps(obj) + "\n") - process.stdin.flush() +deadline="$( + awk -v now="$(date +%s)" -v timeout="$timeout_seconds" 'BEGIN { printf "%.3f", now + timeout }' +)" - send({ - "jsonrpc": "2.0", - "id": 1, - "method": "initialize", - "params": {"clientInfo": {"name": "ci", "version": "1.0"}}, - }) - send({"jsonrpc": "2.0", "method": "initialized", "params": {}}) - send({"jsonrpc": "2.0", "id": 2, "method": "account/read", "params": {}}) +while awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN { exit !(now < deadline) }'; do + remaining="$( + awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN { + remaining = deadline - now + if (remaining < 1) { + remaining = 1 + } + printf "%.0f", remaining + }' + )" - email = "" - deadline = time.time() + timeout_seconds - try: - while time.time() < deadline: - line = process.stdout.readline() - if not line: - break - try: - message = json.loads(line) - except ValueError: - continue - if message.get("id") == 2: - account = (message.get("result") or {}).get("account") or {} - email = account.get("email") or "" - break - finally: - process.terminate() + if ! IFS= read -r -t "$remaining" line <&"$codex_server_stdout"; then + break + fi - return email + message_id="$(jq -r 'try .id catch empty' <<<"$line" 2>/dev/null || true)" + if [[ "$message_id" != "2" ]]; then + continue + fi + email="$(jq -r 'try (.result.account.email // "") catch ""' <<<"$line" 2>/dev/null || true)" + break +done -if __name__ == "__main__": - print(read_account_email()) +printf '%s\n' "$email" -- 2.53.0 From 2e36931f83a728946a111bcde48e6ae333b247cd Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 09:48:29 +0000 Subject: [PATCH 2/6] =?UTF-8?q?docs(README):=20=E6=9B=B4=E6=96=B0=20shell?= =?UTF-8?q?=20helper=20=E8=88=87=20Docker=20=E4=BD=BF=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 43b6816..55a8ef9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Codex CLI Composite Action -提供一個 Gitea/GitHub Actions composite action,用於安裝 Codex CLI、寫入 OAuth token、執行指定提示詞,並將 CLI 輸出寫入 action output;另提供透過 `codex app-server` 取得目前登入帳號 email 的工具函式。 +提供一個 Gitea/GitHub Actions composite action,用於安裝 Codex CLI、寫入 OAuth token、執行指定提示詞,並將 CLI 輸出寫入 action output;另提供透過 `codex app-server` 取得目前登入帳號 email 的 shell helper。 > 更新時間:2026/06/29 14:18:26 @@ -10,7 +10,7 @@ | 專案名稱 | 專案描述 | | --- | --- | -| [codex](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/) | 提供 Codex CLI composite action(安裝 `@openai/codex`、寫入 OAuth token、執行提示詞並輸出文字),並提供透過 `codex app-server` JSON-RPC `account/read` 取得目前登入帳號 email 的工具函式。 | +| [codex](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/) | 提供 Codex CLI composite action(安裝 `@openai/codex`、寫入 OAuth token、執行提示詞並輸出文字),並提供透過 `codex app-server` JSON-RPC `account/read` 取得目前登入帳號 email 的 shell helper。 | ### 參考專案 @@ -30,46 +30,47 @@ | 功能名稱 | 功能描述 | | --- | --- | -| [codex_account.read_account_email](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/app/codex_account.py#L12) | [透過 codex app-server 的 JSON-RPC `account/read` 取得目前登入帳號的 email,取不到或逾時回傳空字串。](#codex_accountread_account_email) | +| [app/codex_account.py](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/app/codex_account.py#L1) | [透過 codex app-server 的 JSON-RPC `account/read` 取得目前登入帳號的 email,取不到或逾時輸出空字串。](#appcodex_accountpy) | ## 使用範例 - -### codex_account.read_account_email + +### app/codex_account.py -透過 `codex app-server` 的 JSON-RPC `account/read` 取得目前登入帳號的 email。函式會啟動 `codex app-server` 子行程,依序送出 `initialize` / `initialized` / `account/read` 三筆 JSON-RPC 訊息,讀取回應中 `result.account.email` 作為回傳值;讀到 EOF、JSON 解析失敗或超過 `timeout_seconds` 逾時都會停止,取不到時回傳空字串 `""`,且不論成功或失敗都會在 `finally` 終止子行程。 - -簽名: - -```python -def read_account_email(timeout_seconds: float = 25.0) -> str -``` +透過 `codex app-server` 的 JSON-RPC `account/read` 取得目前登入帳號的 email。腳本會啟動 `codex app-server` 子行程,依序送出 `initialize` / `initialized` / `account/read` 三筆 JSON-RPC 訊息,使用 `jq` 讀取回應中 `result.account.email` 並輸出到 stdout;讀到 EOF、JSON 解析失敗或超過逾時都會停止,取不到時輸出空字串。 前置條件: - `codex` CLI 已安裝(例如本 action 的安裝步驟已執行)。 +- `jq` 已安裝。 - `$HOME/.codex/auth.json` 已寫入有效的 OAuth token。 典型呼叫方式: -```python -from app.codex_account import read_account_email +```bash +# 取得目前登入帳號 email;預設等待 25 秒 +bash app/codex_account.py -# 取得目前登入帳號 email;可自訂等待 app-server 回應的逾時秒數 -email = read_account_email(timeout_seconds=25.0) -if email: - print(f"目前登入帳號:{email}") -else: - print("取不到登入帳號(未登入或逾時)") +# 自訂等待 app-server 回應的逾時秒數 +bash app/codex_account.py 10 ``` 預期結果: -- 成功時回傳登入帳號的 email 字串。 -- 未登入、回應缺漏或逾時時回傳空字串 `""`。 +- 成功時輸出登入帳號的 email 字串。 +- 未登入、回應缺漏或逾時時輸出空字串。 此模組亦可直接以指令執行,會將 email 印到 stdout(取不到時印空字串): ```bash -python3 app/codex_account.py +bash app/codex_account.py +``` + +## Docker + +此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,會安裝 `bash`、`jq`、Node.js/npm 與 `@openai/codex`: + +```bash +docker build -t codex-account . +docker run --rm -v "$HOME/.codex:/root/.codex:ro" codex-account ``` -- 2.53.0 From 68c78e673669daf0291d55cfbececcdf2a58fac1 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 09:53:07 +0000 Subject: [PATCH 3/6] =?UTF-8?q?refactor(codex=5Faccount):=20=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20Node.js=20=E5=AF=A6=E4=BD=9C=E5=B8=B3=E8=99=9F?= =?UTF-8?q?=E6=9F=A5=E8=A9=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 +-- README.md | 22 ++++---- action.yml | 21 ++------ app/codex_account.js | 122 +++++++++++++++++++++++++++++++++++++++++++ app/codex_account.py | 70 ------------------------- 5 files changed, 137 insertions(+), 104 deletions(-) create mode 100755 app/codex_account.js delete mode 100755 app/codex_account.py diff --git a/Dockerfile b/Dockerfile index 68ac4c2..f056a8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,11 +4,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ && apt-get install -y --no-install-recommends \ - bash \ ca-certificates \ - coreutils \ - gawk \ - jq \ nodejs \ npm \ && npm install -g @openai/codex \ @@ -17,7 +13,7 @@ RUN apt-get update \ WORKDIR /workspace -COPY app/codex_account.py /usr/local/bin/codex-account +COPY app/codex_account.js /usr/local/bin/codex-account RUN chmod +x /usr/local/bin/codex-account CMD ["codex-account"] diff --git a/README.md b/README.md index 55a8ef9..11ef4f2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Codex CLI Composite Action -提供一個 Gitea/GitHub Actions composite action,用於安裝 Codex CLI、寫入 OAuth token、執行指定提示詞,並將 CLI 輸出寫入 action output;另提供透過 `codex app-server` 取得目前登入帳號 email 的 shell helper。 +提供一個 Gitea/GitHub Actions composite action,用於安裝 Codex CLI、寫入 OAuth token、執行指定提示詞,並將 CLI 輸出寫入 action output;另提供透過 `codex app-server` 取得目前登入帳號 email 的 Node.js helper。 > 更新時間:2026/06/29 14:18:26 @@ -10,7 +10,7 @@ | 專案名稱 | 專案描述 | | --- | --- | -| [codex](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/) | 提供 Codex CLI composite action(安裝 `@openai/codex`、寫入 OAuth token、執行提示詞並輸出文字),並提供透過 `codex app-server` JSON-RPC `account/read` 取得目前登入帳號 email 的 shell helper。 | +| [codex](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/) | 提供 Codex CLI composite action(安裝 `@openai/codex`、寫入 OAuth token、執行提示詞並輸出文字),並提供透過 `codex app-server` JSON-RPC `account/read` 取得目前登入帳號 email 的 Node.js helper。 | ### 參考專案 @@ -30,29 +30,29 @@ | 功能名稱 | 功能描述 | | --- | --- | -| [app/codex_account.py](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/app/codex_account.py#L1) | [透過 codex app-server 的 JSON-RPC `account/read` 取得目前登入帳號的 email,取不到或逾時輸出空字串。](#appcodex_accountpy) | +| [app/codex_account.js](https://gitea.jsc.idv.tw/composite-actions/codex/src/branch/develop/app/codex_account.js#L1) | [透過 codex app-server 的 JSON-RPC `account/read` 取得目前登入帳號的 email,取不到或逾時輸出空字串。](#appcodex_accountjs) | ## 使用範例 - -### app/codex_account.py + +### app/codex_account.js -透過 `codex app-server` 的 JSON-RPC `account/read` 取得目前登入帳號的 email。腳本會啟動 `codex app-server` 子行程,依序送出 `initialize` / `initialized` / `account/read` 三筆 JSON-RPC 訊息,使用 `jq` 讀取回應中 `result.account.email` 並輸出到 stdout;讀到 EOF、JSON 解析失敗或超過逾時都會停止,取不到時輸出空字串。 +透過 `codex app-server` 的 JSON-RPC `account/read` 取得目前登入帳號的 email。腳本會啟動 `codex app-server` 子行程,依序送出 `initialize` / `initialized` / `account/read` 三筆 JSON-RPC 訊息,使用 Node.js 標準函式庫讀取回應中 `result.account.email` 並輸出到 stdout;讀到 EOF、JSON 解析失敗或超過逾時都會停止,取不到時輸出空字串。 前置條件: - `codex` CLI 已安裝(例如本 action 的安裝步驟已執行)。 -- `jq` 已安裝。 +- Node.js 已安裝。 - `$HOME/.codex/auth.json` 已寫入有效的 OAuth token。 典型呼叫方式: ```bash # 取得目前登入帳號 email;預設等待 25 秒 -bash app/codex_account.py +node app/codex_account.js # 自訂等待 app-server 回應的逾時秒數 -bash app/codex_account.py 10 +node app/codex_account.js 10 ``` 預期結果: @@ -63,12 +63,12 @@ bash app/codex_account.py 10 此模組亦可直接以指令執行,會將 email 印到 stdout(取不到時印空字串): ```bash -bash app/codex_account.py +node app/codex_account.js ``` ## Docker -此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,會安裝 `bash`、`jq`、Node.js/npm 與 `@openai/codex`: +此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,會安裝 Node.js/npm 與 `@openai/codex`: ```bash docker build -t codex-account . diff --git a/action.yml b/action.yml index 7843853..bc78299 100644 --- a/action.yml +++ b/action.yml @@ -7,7 +7,7 @@ # 讓 codex CLI 取得登入憑證。 # 3. 執行 inputs.prompt 指定的提示詞,並把 Codex 的輸出寫入 # steps.codex.outputs.text 供後續 step 使用。 -# - 若未提供 prompt(PROMPT 為空),則改呼叫 app/codex_account.py 取得目前 +# - 若未提供 prompt(PROMPT 為空),則改呼叫 app/codex_account.js 取得目前 # 登入帳號的 email,寫入 text 輸出後結束(用於驗證身分)。 # 更新日期:2026/06/29 14:18:26 # ============================================================================= @@ -62,21 +62,6 @@ runs: exit 1 fi - # 安裝 jq,供 app/codex_account.py 以 shell 解析 JSON-RPC 回應。 - if ! command -v jq >/dev/null 2>&1; then - if command -v apt-get >/dev/null 2>&1; then - apt_get='apt-get' - if command -v sudo >/dev/null 2>&1; then - apt_get='sudo apt-get' - fi - $apt_get update - $apt_get install -y jq - else - echo 'jq is required but apt-get is not available.' >&2 - exit 1 - fi - fi - # 全域安裝 Codex CLI 套件,安裝後可直接使用 codex 指令 npm install -g @openai/codex @@ -111,8 +96,8 @@ runs: fi # 當 PROMPT 為空時,改走「取得登入帳號 email」分支 if [ -z "$PROMPT" ]; then - # 呼叫 codex_account.py 取得目前登入帳號的 email($GITHUB_ACTION_PATH 為 action 根目錄) - email="$(bash "$GITHUB_ACTION_PATH/app/codex_account.py")" + # 呼叫 codex_account.js 取得目前登入帳號的 email($GITHUB_ACTION_PATH 為 action 根目錄) + email="$(node "$GITHUB_ACTION_PATH/app/codex_account.js")" # 將 email 寫入 text 輸出($GITHUB_OUTPUT 為步驟輸出檔) echo "text=$email" >> "$GITHUB_OUTPUT" # 以 0 正常退出,不再執行 codex exec diff --git a/app/codex_account.js b/app/codex_account.js new file mode 100755 index 0000000..e97b717 --- /dev/null +++ b/app/codex_account.js @@ -0,0 +1,122 @@ +#!/usr/bin/env node +// 透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。 +// +// 這是 TUI `/status` Account 欄位的程式化來源,不解析本地 OAuth token, +// 而是由 codex 自身回報登入帳號。將 email 印到 stdout(取不到時印空字串)。 + +const { spawn } = require("node:child_process"); +const readline = require("node:readline"); + +const timeoutSeconds = Number(process.argv[2] ?? "25"); +if (!Number.isFinite(timeoutSeconds) || timeoutSeconds < 0) { + console.error("timeout_seconds must be a non-negative number."); + process.exit(2); +} + +function sendJson(processHandle, message) { + try { + processHandle.stdin.write(`${JSON.stringify(message)}\n`); + } catch { + return false; + } + return true; +} + +async function readAccountEmail() { + const processHandle = spawn("codex", ["app-server"], { + stdio: ["pipe", "pipe", "ignore"], + }); + + let settled = false; + let timer; + + const cleanup = () => { + if (!processHandle.killed) { + processHandle.kill(); + } + }; + + process.once("exit", cleanup); + process.once("SIGINT", () => { + cleanup(); + process.exit(130); + }); + process.once("SIGTERM", () => { + cleanup(); + process.exit(143); + }); + + return await new Promise((resolve) => { + const done = (email = "") => { + if (settled) { + return; + } + settled = true; + clearTimeout(timer); + cleanup(); + resolve(email); + }; + + timer = setTimeout(() => done(""), timeoutSeconds * 1000); + + processHandle.once("error", () => done("")); + processHandle.once("exit", () => done("")); + processHandle.stdin.once("error", () => done("")); + + const lines = readline.createInterface({ + input: processHandle.stdout, + crlfDelay: Infinity, + }); + + lines.on("line", (line) => { + let message; + try { + message = JSON.parse(line); + } catch { + return; + } + + if (message.id !== 2) { + return; + } + + const email = message?.result?.account?.email; + done(typeof email === "string" ? email : ""); + }); + + const messages = [ + { + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { clientInfo: { name: "ci", version: "1.0" } }, + }, + { + jsonrpc: "2.0", + method: "initialized", + params: {}, + }, + { + jsonrpc: "2.0", + id: 2, + method: "account/read", + params: {}, + }, + ]; + + for (const message of messages) { + if (!sendJson(processHandle, message)) { + done(""); + break; + } + } + }); +} + +readAccountEmail() + .then((email) => { + process.stdout.write(`${email}\n`); + }) + .catch(() => { + process.stdout.write("\n"); + }); diff --git a/app/codex_account.py b/app/codex_account.py deleted file mode 100755 index 6087e9f..0000000 --- a/app/codex_account.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -# 透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。 -# -# 這是 TUI `/status` Account 欄位的程式化來源,不解析本地 OAuth token, -# 而是由 codex 自身回報登入帳號。將 email 印到 stdout(取不到時印空字串)。 - -set -uo pipefail - -timeout_seconds="${1:-25}" -if ! [[ "$timeout_seconds" =~ ^[0-9]+([.][0-9]+)?$ ]]; then - echo "timeout_seconds must be a number." >&2 - exit 2 -fi - -if ! command -v jq >/dev/null 2>&1; then - echo "jq is required." >&2 - exit 2 -fi - -email="" - -cleanup() { - if [[ -n "${CODEX_SERVER_PID:-}" ]]; then - kill "$CODEX_SERVER_PID" >/dev/null 2>&1 || true - wait "$CODEX_SERVER_PID" >/dev/null 2>&1 || true - fi -} -trap cleanup EXIT - -coproc CODEX_SERVER { codex app-server 2>/dev/null; } -exec {codex_server_stdin}>&"${CODEX_SERVER[1]}" -exec {codex_server_stdout}<&"${CODEX_SERVER[0]}" - -send_json() { - printf '%s\n' "$1" >&"$codex_server_stdin" -} - -send_json '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"ci","version":"1.0"}}}' -send_json '{"jsonrpc":"2.0","method":"initialized","params":{}}' -send_json '{"jsonrpc":"2.0","id":2,"method":"account/read","params":{}}' - -deadline="$( - awk -v now="$(date +%s)" -v timeout="$timeout_seconds" 'BEGIN { printf "%.3f", now + timeout }' -)" - -while awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN { exit !(now < deadline) }'; do - remaining="$( - awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN { - remaining = deadline - now - if (remaining < 1) { - remaining = 1 - } - printf "%.0f", remaining - }' - )" - - if ! IFS= read -r -t "$remaining" line <&"$codex_server_stdout"; then - break - fi - - message_id="$(jq -r 'try .id catch empty' <<<"$line" 2>/dev/null || true)" - if [[ "$message_id" != "2" ]]; then - continue - fi - - email="$(jq -r 'try (.result.account.email // "") catch ""' <<<"$line" 2>/dev/null || true)" - break -done - -printf '%s\n' "$email" -- 2.53.0 From 77b6bc3b80506ab2aa1c1fafe66a856964b5ae46 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 10:10:29 +0000 Subject: [PATCH 4/6] =?UTF-8?q?chore(Dockerfile):=20=E9=A9=97=E8=AD=89=20C?= =?UTF-8?q?odex=20=E5=B8=B3=E8=99=9F=E9=9B=BB=E5=AD=90=E9=83=B5=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 +++- README.md | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f056a8f..7794cde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,6 @@ WORKDIR /workspace COPY app/codex_account.js /usr/local/bin/codex-account RUN chmod +x /usr/local/bin/codex-account -CMD ["codex-account"] +ENV EXPECTED_CODEX_EMAIL=temp@gmail.com + +CMD ["sh", "-c", "email=\"$(codex-account)\"; if [ \"$email\" = \"$EXPECTED_CODEX_EMAIL\" ]; then printf 'Codex account email verified: %s\\n' \"$email\"; else printf 'Codex account email mismatch: expected %s, got %s\\n' \"$EXPECTED_CODEX_EMAIL\" \"${email:-}\" >&2; exit 1; fi"] diff --git a/README.md b/README.md index 11ef4f2..7a95dae 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,12 @@ node app/codex_account.js docker build -t codex-account . docker run --rm -v "$HOME/.codex:/root/.codex:ro" codex-account ``` + +容器啟動時會執行 `codex-account`,並確認取得的 email 是否等於 `EXPECTED_CODEX_EMAIL`。預設值為 `temp@gmail.com`,可用環境變數覆寫: + +```bash +docker run --rm \ + -e EXPECTED_CODEX_EMAIL='temp@gmail.com' \ + -v "$HOME/.codex:/root/.codex:ro" \ + codex-account +``` -- 2.53.0 From 15dc62d9294d70da45d63e84d565f2ce236ce7fd Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 10:13:58 +0000 Subject: [PATCH 5/6] =?UTF-8?q?chore(Dockerfile):=20=E5=BB=BA=E7=BD=AE?= =?UTF-8?q?=E6=99=82=E9=A9=97=E8=AD=89=20Codex=20=E5=B8=B3=E8=99=9F?= =?UTF-8?q?=E9=9B=BB=E5=AD=90=E9=83=B5=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 16 +++++++++++++--- README.md | 10 +++++++--- app/verify_codex_email.sh | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 app/verify_codex_email.sh diff --git a/Dockerfile b/Dockerfile index 7794cde..ef752b0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,8 @@ +# syntax=docker/dockerfile:1.4 FROM ubuntu:latest ENV DEBIAN_FRONTEND=noninteractive +ARG EXPECTED_CODEX_EMAIL=temp@gmail.com RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -14,8 +16,16 @@ RUN apt-get update \ WORKDIR /workspace COPY app/codex_account.js /usr/local/bin/codex-account -RUN chmod +x /usr/local/bin/codex-account +COPY app/verify_codex_email.sh /usr/local/bin/verify-codex-email +RUN chmod +x /usr/local/bin/codex-account /usr/local/bin/verify-codex-email -ENV EXPECTED_CODEX_EMAIL=temp@gmail.com +ENV EXPECTED_CODEX_EMAIL=${EXPECTED_CODEX_EMAIL} -CMD ["sh", "-c", "email=\"$(codex-account)\"; if [ \"$email\" = \"$EXPECTED_CODEX_EMAIL\" ]; then printf 'Codex account email verified: %s\\n' \"$email\"; else printf 'Codex account email mismatch: expected %s, got %s\\n' \"$EXPECTED_CODEX_EMAIL\" \"${email:-}\" >&2; exit 1; fi"] +RUN --mount=type=secret,id=codex_auth,required=true \ + install -d -m 700 /root/.codex \ + && cp /run/secrets/codex_auth /root/.codex/auth.json \ + && chmod 600 /root/.codex/auth.json \ + && EXPECTED_CODEX_EMAIL="$EXPECTED_CODEX_EMAIL" verify-codex-email \ + && rm -f /root/.codex/auth.json + +CMD ["codex-account"] diff --git a/README.md b/README.md index 7a95dae..e1d650b 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,19 @@ node app/codex_account.js 此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,會安裝 Node.js/npm 與 `@openai/codex`: ```bash -docker build -t codex-account . +DOCKER_BUILDKIT=1 docker build \ + --secret id=codex_auth,src=/home/coder/jiantw83/Docker/gitea/codex/housefun.dev.01@gmail.com \ + --build-arg EXPECTED_CODEX_EMAIL='temp@gmail.com' \ + -t codex-account . docker run --rm -v "$HOME/.codex:/root/.codex:ro" codex-account ``` -容器啟動時會執行 `codex-account`,並確認取得的 email 是否等於 `EXPECTED_CODEX_EMAIL`。預設值為 `temp@gmail.com`,可用環境變數覆寫: +建置階段會透過 BuildKit secret 暫時掛載 OAuth token,執行 `verify-codex-email` 檢查 `codex-account` 取得的 email 是否等於 `EXPECTED_CODEX_EMAIL`。預設值為 `temp@gmail.com`;不相符時 `docker build` 會失敗,且 token 不會保留在 image layer。 + +容器啟動後預設執行 `codex-account`: ```bash docker run --rm \ - -e EXPECTED_CODEX_EMAIL='temp@gmail.com' \ -v "$HOME/.codex:/root/.codex:ro" \ codex-account ``` diff --git a/app/verify_codex_email.sh b/app/verify_codex_email.sh new file mode 100644 index 0000000..e3f5fb9 --- /dev/null +++ b/app/verify_codex_email.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# 驗證 codex-account 取得的登入 email 是否符合 EXPECTED_CODEX_EMAIL。 + +set -eu + +expected_email="${EXPECTED_CODEX_EMAIL:-temp@gmail.com}" +actual_email="$(codex-account)" + +if [ "$actual_email" = "$expected_email" ]; then + printf 'Codex account email verified: %s\n' "$actual_email" + exit 0 +fi + +printf 'Codex account email mismatch: expected %s, got %s\n' \ + "$expected_email" "${actual_email:-}" >&2 +exit 1 -- 2.53.0 From ba47c2fd9f428057e729e0db22b923d25737c0cd Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 29 Jun 2026 10:18:56 +0000 Subject: [PATCH 6/6] =?UTF-8?q?chore(Dockerfile):=20=E6=94=B9=E7=82=BA?= =?UTF-8?q?=E7=B4=94=E5=AE=89=E8=A3=9D=20Codex=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 17 +---------------- README.md | 15 +++++---------- app/verify_codex_email.sh | 16 ---------------- 3 files changed, 6 insertions(+), 42 deletions(-) delete mode 100644 app/verify_codex_email.sh diff --git a/Dockerfile b/Dockerfile index ef752b0..037435e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,6 @@ -# syntax=docker/dockerfile:1.4 FROM ubuntu:latest ENV DEBIAN_FRONTEND=noninteractive -ARG EXPECTED_CODEX_EMAIL=temp@gmail.com RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -15,17 +13,4 @@ RUN apt-get update \ WORKDIR /workspace -COPY app/codex_account.js /usr/local/bin/codex-account -COPY app/verify_codex_email.sh /usr/local/bin/verify-codex-email -RUN chmod +x /usr/local/bin/codex-account /usr/local/bin/verify-codex-email - -ENV EXPECTED_CODEX_EMAIL=${EXPECTED_CODEX_EMAIL} - -RUN --mount=type=secret,id=codex_auth,required=true \ - install -d -m 700 /root/.codex \ - && cp /run/secrets/codex_auth /root/.codex/auth.json \ - && chmod 600 /root/.codex/auth.json \ - && EXPECTED_CODEX_EMAIL="$EXPECTED_CODEX_EMAIL" verify-codex-email \ - && rm -f /root/.codex/auth.json - -CMD ["codex-account"] +CMD ["codex", "--version"] diff --git a/README.md b/README.md index e1d650b..d6fae97 100644 --- a/README.md +++ b/README.md @@ -68,22 +68,17 @@ node app/codex_account.js ## Docker -此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,會安裝 Node.js/npm 與 `@openai/codex`: +此專案提供以 `ubuntu:latest` 為基底的 Dockerfile,僅安裝 Node.js/npm 與 `@openai/codex`: ```bash -DOCKER_BUILDKIT=1 docker build \ - --secret id=codex_auth,src=/home/coder/jiantw83/Docker/gitea/codex/housefun.dev.01@gmail.com \ - --build-arg EXPECTED_CODEX_EMAIL='temp@gmail.com' \ - -t codex-account . -docker run --rm -v "$HOME/.codex:/root/.codex:ro" codex-account +docker build -t codex-cli . +docker run --rm codex-cli ``` -建置階段會透過 BuildKit secret 暫時掛載 OAuth token,執行 `verify-codex-email` 檢查 `codex-account` 取得的 email 是否等於 `EXPECTED_CODEX_EMAIL`。預設值為 `temp@gmail.com`;不相符時 `docker build` 會失敗,且 token 不會保留在 image layer。 - -容器啟動後預設執行 `codex-account`: +若要在容器內執行 Codex 並使用本機登入憑證,可掛載 `$HOME/.codex`: ```bash docker run --rm \ -v "$HOME/.codex:/root/.codex:ro" \ - codex-account + codex-cli codex --version ``` diff --git a/app/verify_codex_email.sh b/app/verify_codex_email.sh deleted file mode 100644 index e3f5fb9..0000000 --- a/app/verify_codex_email.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# 驗證 codex-account 取得的登入 email 是否符合 EXPECTED_CODEX_EMAIL。 - -set -eu - -expected_email="${EXPECTED_CODEX_EMAIL:-temp@gmail.com}" -actual_email="$(codex-account)" - -if [ "$actual_email" = "$expected_email" ]; then - printf 'Codex account email verified: %s\n' "$actual_email" - exit 0 -fi - -printf 'Codex account email mismatch: expected %s, got %s\n' \ - "$expected_email" "${actual_email:-}" >&2 -exit 1 -- 2.53.0