fix: accept the Win-CodexBar CLI's JSON, and stop one failed provider discarding the whole report
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:
| Change | Why |
|---|---|
reset_at,omitzero | The 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_COLOR | The 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.WaitDelay | Killing the process on a deadline does not unblock Wait while any child still holds the output pipe — that could wedge the poller behind pollMu. |
parseConfiguredCommand | strings.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 message | A 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 aTEMPpath containing spaces. go vetclean; cross-compiles for all five platforms inmanifest.yaml;node --test16/16;go.mod/go.sumuntouched.- Each fix was mutation-tested: removing
stripSGRfails the stderr tests, removingWaitDelaymakes 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.zipas 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. pinnedVersionstays 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_atis omitted when zero, so "resets now" no longer shows forever; this also fixes macOS when upstream omitsresetsAt.- stderr selection skips colored log lines and strips SGR escapes, so failures report the real cause, with upstream's swift-log output provably unchanged.
WaitDelayboundsWaitafter 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.