fix: accept the Win-CodexBar CLI's JSON, and stop one failed provider discarding the whole report

Merged
JN
2mo ago
+588-10

Why

The codexbar CLI has no Windows build. Win-CodexBar is the only compatible CLI there, and it spells the usage payload differently from upstream: snake_case keys, no identity block, and a failing provider's error as a bare string rather than an object.

That last difference is not cosmetic. A string there failed the decode of the entire JSON array, so one unauthenticated provider discarded every healthy one alongside it. With codexbar_providers = all (a single CLI call) that means no data at all, and in either case the operator saw parsing codexbar output instead of the reason.

On a stock Windows machine 5 of the 8 default providers report exactly that way — codex, gemini, grok, opencodego, amp — all with exit code 0, so the plugin never even saw a run failure.

What

Both spellings decode as co-equal. Upstream may keep camelCase and the port may re-converge on it, so neither is treated as legacy: the camelCase pass stays the strict contract, and a best-effort snake_case pass fills in whatever it left zero. Best-effort matters — a wrongly-typed key in the fallback spelling must not discard the document it exists to rescue (TestParseCodexbar_BadSnakeValueIsNotFatal).

Everything else here is platform-neutral, and was found while testing against the port:

ChangeWhy
reset_at,omitzeroThe zero time serialized as 0001-01-01T00:00:00Z; fmtReset reads any non-empty value as a real date and renders "resets now" forever, never reaching the reset_description fallback. This fixes the same bug on macOS whenever upstream omits resetsAt.
firstUsefulLine + NO_COLORThe port writes structured log lines to stderr, so a failure was reported as a cookie-decryption warning. Its logging layer colors stderr even through a pipe and even with --no-color, so the escapes had to go too. Upstream's swift-log writes a +0000 offset, which time.Parse(RFC3339) rejects — macOS stderr selection is provably unchanged.
cmd.WaitDelayKilling the process on a deadline does not unblock Wait while any child still holds the output pipe — that could wedge the poller behind pollMu.
parseConfiguredCommandstrings.Fields shattered a configured path containing spaces, and the setting is documented as a path. Surrounding quotes are dropped first: that is what Explorer's "Copy as path" produces.
per-provider timeout messageA killed process reports its exit status, not the deadline.

Testing

End to end against the real codexbar-cli.exe 0.55.0 through the production runner:

PROBE: installed=true version="0.55.0"
claude    plano="Claude Max 5x"    janelas=3
            5-hour         2.0%  reset=31 Aug 13:30
            weekly        12.0%  reset=04 Sep 09:00
            Fable only    15.0%  reset=04 Sep 09:00
copilot   AI credits     0.0%  reset=(omitted)  desc="0 AI credits used"
codex     unavailable: Provider not installed: Codex auth.json not found...

Before this change the same command produced zero windows and a parse error.

  • Full suite green on Windows in both PowerShell and Git Bash, with -shuffle=on, and with a TEMP path containing spaces.
  • go vet clean; cross-compiles for all five platforms in manifest.yaml; node --test 16/16; go.mod/go.sum untouched.
  • Each fix was mutation-tested: removing stripSGR fails the stderr tests, removing WaitDelay makes its test hang and fail. The stderr fixtures are verbatim captured bytes, escape sequences included — a hand-written transcription passed while production behaviour was broken.

Windows baseline

Three tests failed on Windows before this change: TestExtractTarGz and TestDownloaderEnsure_DownloadsVerifiesCaches assert an exec bit Go never reports there (mode 0666), and TestWithStderr shelled out to sh. The first two are now GOOS-gated (the download path is unix-only) and the third uses a portable helper-process. The suite is green on Windows for the first time.

Not in this PR

  • Windows resolution and auto-download. Win-CodexBar has just merged publishing CodexBarCLI-v<version>-windows-x64.zip as a release asset, so Windows will eventually get the same zero-install path as macOS/Linux. That needs zip extraction and a pinned checksum from a published release — neither exists yet.
  • pinnedVersion stays at 0.45.2: unix-only surface, unverifiable from a Windows host.

Summary by cubic

Accepts the Win-CodexBar CLI's JSON output — snake_case keys, no identity block, bare-string errors — so usage polling works on Windows. A bare-string error previously failed the whole array decode, so one failed provider discarded every healthy one alongside it and the operator saw "parsing codexbar output".

  • Both spellings decode as co-equal: camelCase stays the strict contract, snake_case is a best-effort fallback that ignores bad values rather than failing the document.
  • reset_at is omitted when zero, so "resets now" no longer shows forever; this also fixes macOS when upstream omits resetsAt.
  • stderr selection skips colored log lines and strips SGR escapes, so failures report the real cause, with upstream's swift-log output provably unchanged.
  • WaitDelay bounds Wait after a deadline kill so a child holding the output pipe can't wedge the poller.
  • A configured command that's a path on disk is taken whole (quotes dropped first), so Explorer-copied paths with spaces work.
  • Per-provider timeouts are reported as timeouts, and on-demand session failures now reach the session card instead of returning an empty report.
  • The Windows suite is green: exec-bit assertions are non-Windows-only, and stderr tests use a portable helper process instead of sh.