From 2e99b58d095b847d0f8862b634262ae79a5666d5 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Tue, 9 Jun 2026 21:11:01 -0400 Subject: [PATCH] fix(bar): read codex rollout tail via fs so quota surfaces under node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit defaultTailLines used Bun.spawn(['tail',...]), but the ccs server (and 'ccs bar launch') runs under node where Bun is undefined — it threw, the per-path catch swallowed it, and Codex quota silently never appeared in real deployments (it only worked under the bun-run dev harness). Replace with a pure fs read: runtime- and OS-agnostic, no subprocess. Verified under node: surfaces a live rollout's rate_limits (primary/secondary). --- .../usage/codex-local-quota-collector.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/web-server/usage/codex-local-quota-collector.ts b/src/web-server/usage/codex-local-quota-collector.ts index ffea21b5..82e077a8 100644 --- a/src/web-server/usage/codex-local-quota-collector.ts +++ b/src/web-server/usage/codex-local-quota-collector.ts @@ -167,17 +167,19 @@ function collectRolloutFiles( } /** - * Default tail using Bun.spawn (macOS-safe: no tac, no GNU timeout). - * Reads the last `lines` lines so we never load a huge session into memory. + * Default tail via a pure fs read. Must work under BOTH node and bun: the `ccs` + * CLI (and `ccs bar launch`) runs under node, where `Bun.spawn` is undefined — + * the previous Bun-only implementation threw there, so Codex quota silently + * never surfaced in real deployments (only under the bun-run dev harness). + * Reading the JSONL and slicing the tail is cheap (session rollouts are small) + * and carries no runtime/OS/subprocess dependency. */ async function defaultTailLines(file: string, lines: number): Promise { - const proc = Bun.spawn(['tail', `-${lines}`, file], { - stdout: 'pipe', - stderr: 'ignore', - }); - const text = await new Response(proc.stdout).text(); - await proc.exited; - return text.split('\n').filter((l) => l.trim().length > 0); + const text = await fs.promises.readFile(file, 'utf8'); + return text + .split('\n') + .filter((l) => l.trim().length > 0) + .slice(-lines); } function computeQuotaPercentage(rate: CodexRateLimits): number {