Every provider settings card gets an accent color row: swatch, hex field, color well,
and a reset shown only when an override exists. Descriptor colors stay compile-time constants,
so a reset restores the shipped color.
Overrides persist as ProviderConfig.accentColor in ~/.codexbar/config.json,
which reaches the app and the codexbar serve dashboard, and mirror
into App Group defaults for the widget, which cannot read that file.
Overrides sync across Macs via ProviderIntentPayload.
Translations for the 22 non-English catalogs are machine-generated and want a native-speaker pass.
* refactor: derive widget colors from provider branding
* refactor: generate provider manifests from one registry
* refactor: colocate provider cookie import priorities
* refactor: move share plan labels into descriptors
* refactor: move debug defaults into provider descriptors
* refactor: derive debug pane provider curation
* refactor: derive provider presentation capabilities
* refactor: unify standard provider config bindings
* style: remove stale lint suppression
* fix: use grep instead of rg in manifest regeneration script
CI lint runners do not have ripgrep; grep -rlE --include is portable and
matches the same files.
---------
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
The AppIntents metadata processor in current Xcode rejects computed
caseDisplayRepresentations (must be a literal, exhaustive dictionary),
which broke the 0.47.0 release build. Titles are pinned to the
descriptor registry by WidgetProviderChoiceTests so providers cannot
drift silently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Add Notion AI provider
Tracks the two usage-allowance windows Notion shows in Settings > Notion AI >
Usage: the rolling 6-hour window and the billing-period window. Notion begins
enforcing the allowance on 2026-08-03; before then the same endpoint already
returns real numbers with "enforcement": "preview".
Reads two cookie-authenticated endpoints on app.notion.com that the Notion web
app itself calls: getSpaces for account identity and workspace plans, and
getCreditRateLimitStatus for the allowance. Only Business and Enterprise
workspaces carry one; anything else reports not_applicable as a clear error
rather than an empty gauge.
The imported cookie header is persisted through CookieHeaderCache. Chromium
cookie reads are gated to user-initiated refreshes to avoid a Keychain prompt,
so without a cached header the provider fails on every background refresh and
reports "no cookies found" while the session is valid. Cookies are also
de-duplicated by name across the Notion domains, since a stale token_v2 left on
the legacy notion.so alongside the live one would otherwise send both.
Every field of the rate-limit response is optional, so an unrelated 200 body
decodes cleanly into an all-nil status. The parser rejects a payload carrying
neither window, and a window with no usable limit is omitted rather than
reported as 0% used.
Adding the .notion debug-log case tipped UsageStore.debugLogText past the
cyclomatic-complexity cap, so .openai and .azureopenai are folded into one case;
both already call the same apiKeyDebugLine helper.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(notion): harden cookie session handling
* test: isolate widget snapshot persistence
* feat(notion): pace both allowance bars and honor CLI provider settings
The billing-period window is scored against the real calendar month ending
at its reset rather than a flat 30 days, and the rolling window gets the
session-pace treatment on both the card and in `codexbar usage`.
Notion reports only `periodEndMs` for the billing period, so the snapshot
carries the shared monthly sentinel and the descriptor declares
`.calendarMonthResetWindow`. Resolution then substitutes the true cycle
length. Three paths scored these windows without resolving first --
`UsageStore.weeklyPace` (menu-bar pace token, "runs out" text, predictive
pace warnings) and `resetWindowPaceDetail` when handed a precomputed pace
-- so a February cycle read 6% expected at 0% used and a 31-day cycle lost
its pace token for a day. Both now resolve, which also fixes the eight
other providers that carry the same sentinel.
The CLI ignored the Notion settings snapshot entirely, so Workspace ID, a
manual cookie header, and the `off` source had no effect on `codexbar`.
Also drops a rolling window length that parses to exactly the monthly
sentinel (`30d`, `720h`, `43200m`), which would otherwise be resolved as a
calendar cycle ending hours from now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
Add a separate xai provider for xAI developer-platform billing from the
documented Management API (management key + team ID): prepaid credit
balance from /prepaid/balance (inverted ledger in string USD cents; a
200 without a parseable total is an error, never $0.00) and a 30-day
daily USD spend dashboard from /usage via the shared cost-history seam.
History is best-effort enrichment - its failure keeps the balance while
auth failures and cancellation escalate - and limitReached marks the
window "(partial)" with estimated confidence instead of exact. No
coupling to the Grok subscription provider. Covers descriptor, fetcher,
settings reader/store, registries, config projection, widget and CLI
switches, template icon with a Grok-distinctness test, docs, and
fixture-based tests on stubbed transports. Moves ProviderIdentitySnapshot
to its own file to hold the UsageFetcher length limit. (#1712)
Add a fully-wired "Qwen Cloud" provider that tracks the individual token
plan subscription on https://home.qwencloud.com/billing/subscription/token-plan-individual
(the plan that grants hosted Claude access), alongside all existing settings.
Qwen Cloud shares the Aliyun one-console auth + BssOpenAPI gateway used by
the Alibaba token plan, so it reuses that parser/cookie plumbing:
- New provider `.qwencloud` (CLI name `qwen-cloud`, aliases qwencloud/qwen/
qwen-token-plan) with descriptor, web fetch strategy, monochrome icon, and
a curated confetti palette.
- QwenCloudUsageFetcher posts form-encoded GetSubscriptionSummary
(BssOpenAPI-V3, productCode sfm_tokenplansolo_public_intl) after resolving
sec_token from the dashboard HTML, the sec_token cookie, or /tool/user/info.json.
- URL-scoped cookie headers for qwencloud.com plus a browser cookie importer
over the qwencloud/alibabacloud/aliyun passport domains.
- Settings UI (cookie source picker + manual Cookie header + "Open Token Plan"
link), CLI (`codexbar usage --provider qwen-cloud`), diagnostics, widget,
and docs (docs/qwen-cloud.md, README, providers.md, configuration.md).
- Generalized AlibabaCodingPlanCookieImporter.importSession to accept a
provider-specific domain list + session validator so Qwen Cloud can reuse it;
the existing Alibaba behavior is unchanged (delegates with its own defaults).
- Extended the Alibaba token-plan parser to read nested EquityList quota fields
(CycleTotalValue/CycleSurplusValue/EndTime) without changing existing results.
Tests: parser/snapshot/cookie-header/stub-fetch/import-validation suites plus
an opt-in live smoke test (disabled by default; gated on QWEN_CLOUD_LIVE_TEST=1
and QWEN_CLOUD_COOKIE) so CI never touches the network or Keychain.
Sum 30-day organization spend from ai&'s documented request-log API with
an org-scoped API key (AIAND_API_KEY or ~/.codexbar/config.json), parsing
per-request cost strings as exact decimals in the org's billing currency
and following dual-cursor pagination capped at 10 pages (capped totals
are labeled partial, never silently truncated). ai& is prepaid with no
quota windows, so the snapshot carries only the cost figure and no rate
windows. Covers descriptor, fetcher, settings reader/store, registries,
widget and CLI switches, provider icon, and fixture-based tests.
* Add Cursor widget provider options
* Label legacy Cursor widget quota as Requests
Match the menu bar label for request-based Cursor plans so widgets do not show Total for that quota.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: ignore stale widget providers
* fix: narrow Cursor widget support
---------
Co-authored-by: Zihaoqi <Zihao-Qi@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: give widget token rows their own age instead of inheriting snapshot freshness
Token-cost rows refresh on a slower cadence than quota rows, so a fresh
widget snapshot could present hour-old token data as current
(steipete/CodexBar#1964). Persist a token-specific updatedAt on
WidgetSnapshot.TokenUsageSummary, suffix widget row titles with the token
snapshot's own age once it lags the entry's freshness signal past ten
minutes, and clear the token-fetch TTL on fast failures so the next
scheduled pass retries instead of serving stale rows for the rest of the
TTL. Legacy snapshots without the new field decode as fresh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: cover stale token age across widgets
* fix: keep cached codex scan time as token snapshot updatedAt
Hydrating from the persisted codex scan cache rebuilt the token snapshot
with the hydration time as updatedAt while returning the real cache scan
time separately, so a stale cached token row could inherit app-start
freshness and skip the widget's stale-age suffix for the rest of the
fetch TTL. Cached snapshots now carry the cache's lastScan time; paths
without a known scan time keep the previous behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: keep the oldest cache scan time when cached pi sessions merge
Merged cached codex snapshots dropped the known scan times entirely, so
a hydrated snapshot that included cached pi sessions fell back to the
hydration time as updatedAt. Expose the pi cache's lastScan time and use
the oldest constituent scan as the merged snapshot's updatedAt, while
keeping TTL bookkeeping native-only so merged loads never suppress a
pending rescan.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor: clarify widget freshness summary
* test: strengthen widget freshness coverage
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Third conflict resolution (clawrouter + earlier crossmodel/qoder landings) across the
shared registration surfaces; docs regenerated in enum order, CodexParserHash regenerated.
Address the ClawSweeper P1 patch-quality finding: LongCat fetches now run on an isolated,
ephemeral, cookie-free ProviderHTTPClient (httpCookieStorage = nil, httpShouldSetCookies =
false) so the console's Set-Cookie responses never enter the shared provider cookie jar; auth
is carried solely by the explicit request Cookie header. Mirrors the Sakana provider.
Add compact cost scale labels to detailed history charts, inline usage bars, and widgets. Keep labels truthful to the plotted domain, reserve layout space to avoid peak-bar overlap, and use consistent currency formatting across surfaces.
Co-authored-by: FND <fndevve@proton.me>