OpenAICodexProvider replaces the openai stub. It reads the rate-limit snapshot
the Codex CLI already records locally (no second auth): each turn Codex writes a
token_count event into $CODEX_HOME/sessions/**/rollout-*.jsonl whose
payload.rate_limits mirrors Claude's model — primary (5h) + secondary (weekly),
each with used_percent + resets_at + plan_type. The provider surfaces the
freshest such snapshot; a window whose reset time has passed is reported as a
fresh 0% (local read, so current in-session and self-heals between sessions).
primary -> s/sr, secondary -> w/wr, plan_type -> status ("Plus"/…),
rate_limit_reached_type -> "limited". No per-token cost (subscription), so the
two rate-limit bars are the metric, per the locked v3 decision.
Verified against the real ~/.codex: full daemon payload with openai active =
{pv:openai, pnm:"OpenAI Codex", ac:0x10a37f, st:"Plus", ok:true, weekly reset}.
registry test updated (openai now real, zai still stub); +7 provider tests.
113 passed, 2 Linux-only failures (baseline). spec: hiddenimport added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The watch recolors to the active provider's brand, live, and it's cheap to add
more providers later.
Firmware:
- theme.h: expose THEME_ACCENT_HEX; ui.cpp: COL_ACCENT becomes a runtime
lv_color_t backed by a shared style (s_accent_style). Static accent widgets
(launcher tile icons, usage status line, session tokens) attach the shared
style, so ui_set_theme() recolors them instantly via lv_obj_report_style_change
— no widget tracking, no recreation.
- ui_set_theme(pv, accent_rgb): swaps the accent + a small logo table keyed by
provider id (fallback to the Claude mark until a provider ships a logo). Change-
guarded, so the ~3s payload cadence doesn't churn.
- main.cpp: parse "pv" (id) + "ac" (0xRRGGBB) from the payload and apply.
- Adding a provider needs zero firmware color edits (accent is data-driven) and
at most one logo-table row.
Daemon: push "ac" = active provider's brand accent alongside "pv".
Test fix: test_providers used asyncio.run(), which closed the suite's shared
event loop and broke every later get_event_loop() test (Py3.13) — mirror the
repo's _run() helper instead.
Verified on the 2.06: forcing an OpenAI-green theme recolored the live launcher
icons (report_style_change path). 216 builds clean; daemon suite 94 passed.
Introduce the multi-provider seam on the host, behaviour-identical for Anthropic.
- config v2: `providers` (per-provider enable + creds), `active_provider`,
`display_order`; v1 configs migrate transparently (anthropic enabled+active).
New accessors: active_provider_id / provider_conf / enabled_providers.
- daemon/providers/: Provider ABC + normalized ProviderStatus (maps to the same
s/sr/w/wr/st/tk/to/tc/tn BLE fields), AnthropicProvider (wraps the existing
OAuth+poll+local-usage logic), StubProvider for openai/zai until M2/M3, and a
get_provider() registry.
- poll loop: polls the config-selected active provider each cycle and tags the
payload with `pv` (the brand-theme id the watch will wear). Watch->PC
`{"cmd":"prov","id":".."}` persists the active provider and repolls at once.
- clawdmeter.spec: bundle daemon.providers for the frozen exe.
- test: 7 new provider/config tests; fix a stale start_notify count assertion
(the M2 command channel means both REQ and CMD subscriptions are attempted).
Verified live: config loads as v2, active provider resolves to AnthropicProvider,
a real poll returns ok=True (s=53%, w=5%); full suite 87 passed (+7 new).