feat(codex_account): 改用 shell 取得登入帳號 #5
+23
@@ -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"]
|
||||||
+16
-1
@@ -62,6 +62,21 @@ runs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
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 指令
|
# 全域安裝 Codex CLI 套件,安裝後可直接使用 codex 指令
|
||||||
npm install -g @openai/codex
|
npm install -g @openai/codex
|
||||||
|
|
||||||
@@ -97,7 +112,7 @@ runs:
|
|||||||
# 當 PROMPT 為空時,改走「取得登入帳號 email」分支
|
# 當 PROMPT 為空時,改走「取得登入帳號 email」分支
|
||||||
if [ -z "$PROMPT" ]; then
|
if [ -z "$PROMPT" ]; then
|
||||||
# 呼叫 codex_account.py 取得目前登入帳號的 email($GITHUB_ACTION_PATH 為 action 根目錄)
|
# 呼叫 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 為步驟輸出檔)
|
# 將 email 寫入 text 輸出($GITHUB_OUTPUT 為步驟輸出檔)
|
||||||
echo "text=$email" >> "$GITHUB_OUTPUT"
|
echo "text=$email" >> "$GITHUB_OUTPUT"
|
||||||
# 以 0 正常退出,不再執行 codex exec
|
# 以 0 正常退出,不再執行 codex exec
|
||||||
|
|||||||
Regular → Executable
+55
-58
@@ -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,
|
set -uo pipefail
|
||||||
而是由 codex 自身回報登入帳號。將 email 印到 stdout(取不到時印空字串)。
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
timeout_seconds="${1:-25}"
|
||||||
import subprocess
|
if ! [[ "$timeout_seconds" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||||
import time
|
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:
|
email=""
|
||||||
"""透過 codex app-server 的 JSON-RPC account/read 取得目前登入帳號的 email。
|
|
||||||
|
|
||||||
啟動 ``codex app-server`` 子行程,依序送出 initialize / initialized /
|
cleanup() {
|
||||||
account/read 三筆 JSON-RPC 訊息,並讀取其回報的登入帳號 email。
|
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:
|
coproc CODEX_SERVER { codex app-server 2>/dev/null; }
|
||||||
timeout_seconds: 等待 app-server 回應的秒數上限,預設 25.0。
|
exec {codex_server_stdin}>&"${CODEX_SERVER[1]}"
|
||||||
|
exec {codex_server_stdout}<&"${CODEX_SERVER[0]}"
|
||||||
|
|
||||||
Returns:
|
send_json() {
|
||||||
登入帳號的 email;取不到或逾時時回傳空字串 ""。
|
printf '%s\n' "$1" >&"$codex_server_stdin"
|
||||||
|
}
|
||||||
|
|
||||||
使用情境:
|
send_json '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"ci","version":"1.0"}}}'
|
||||||
在 CI 中驗證登入身分時呼叫。前置條件為 codex CLI 已安裝,
|
send_json '{"jsonrpc":"2.0","method":"initialized","params":{}}'
|
||||||
且 $HOME/.codex/auth.json 已寫入有效的 OAuth token。
|
send_json '{"jsonrpc":"2.0","id":2,"method":"account/read","params":{}}'
|
||||||
"""
|
|
||||||
process = subprocess.Popen(
|
|
||||||
["codex", "app-server"],
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.DEVNULL,
|
|
||||||
text=True,
|
|
||||||
bufsize=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
def send(obj):
|
deadline="$(
|
||||||
"""將 dict 物件序列化成 JSON 後寫入 app-server 的 stdin 並 flush。"""
|
awk -v now="$(date +%s)" -v timeout="$timeout_seconds" 'BEGIN { printf "%.3f", now + timeout }'
|
||||||
process.stdin.write(json.dumps(obj) + "\n")
|
)"
|
||||||
process.stdin.flush()
|
|
||||||
|
|
||||||
send({
|
while awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN { exit !(now < deadline) }'; do
|
||||||
"jsonrpc": "2.0",
|
remaining="$(
|
||||||
"id": 1,
|
awk -v now="$(date +%s)" -v deadline="$deadline" 'BEGIN {
|
||||||
"method": "initialize",
|
remaining = deadline - now
|
||||||
"params": {"clientInfo": {"name": "ci", "version": "1.0"}},
|
if (remaining < 1) {
|
||||||
})
|
remaining = 1
|
||||||
send({"jsonrpc": "2.0", "method": "initialized", "params": {}})
|
}
|
||||||
send({"jsonrpc": "2.0", "id": 2, "method": "account/read", "params": {}})
|
printf "%.0f", remaining
|
||||||
|
}'
|
||||||
|
)"
|
||||||
|
|
||||||
email = ""
|
if ! IFS= read -r -t "$remaining" line <&"$codex_server_stdout"; then
|
||||||
deadline = time.time() + timeout_seconds
|
|
||||||
try:
|
|
||||||
while time.time() < deadline:
|
|
||||||
line = process.stdout.readline()
|
|
||||||
if not line:
|
|
||||||
break
|
break
|
||||||
try:
|
fi
|
||||||
message = json.loads(line)
|
|
||||||
except ValueError:
|
message_id="$(jq -r 'try .id catch empty' <<<"$line" 2>/dev/null || true)"
|
||||||
|
if [[ "$message_id" != "2" ]]; then
|
||||||
continue
|
continue
|
||||||
if message.get("id") == 2:
|
fi
|
||||||
account = (message.get("result") or {}).get("account") or {}
|
|
||||||
email = account.get("email") or ""
|
email="$(jq -r 'try (.result.account.email // "") catch ""' <<<"$line" 2>/dev/null || true)"
|
||||||
break
|
break
|
||||||
finally:
|
done
|
||||||
process.terminate()
|
|
||||||
|
|
||||||
return email
|
printf '%s\n' "$email"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print(read_account_email())
|
|
||||||
|
|||||||
Reference in New Issue
Block a user