refactor(codex_account): 改用 Node.js 實作帳號查詢
CI / Release Tag Version (pull_request) Successful in 4s
CI / Codex (pull_request) Successful in 13s

This commit is contained in:
2026-06-29 09:53:07 +00:00
parent 2e36931f83
commit 68c78e6736
5 changed files with 137 additions and 104 deletions
+122
View File
@@ -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");
});
-70
View File
@@ -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"