Addresses two review focus areas:
- Stale Parked Rows: profiles with no on-disk credentials cached their parked
row for the full quota TTL, so a fresh login stayed dimmed for up to 10 min.
Parked rows (quotaStatus 'unsupported') now use a 30s TTL so a new login is
picked up within seconds.
- Codex Fallback: a named codex profile whose token authenticated but whose
response lacked core windows was downgraded to a parked/needsAuth row, hiding
a real subscription. It now emits an active (quota-less) row; only the bare
default still falls back to global local session data.
Also fixes a latent coalescing bug the short TTL exposed: a synchronous return
inside the pending IIFE cleared state.pending during its own assignment, leaving
a stale resolved promise that the next call reused. The IIFE now yields once so
the assignment lands before the finally clears it.
The collector cached each row before the default profile was resolved, so the
cache-fallback path (getCachedNativeAccountRows) served the default account with
is_default:false -- the UI then lost default ordering/tagging when /summary
served from cache. Write the resolved default flag back onto the cached copy so
the fallback stays consistent with a fresh collection.
Addresses two review focus areas:
- Reauth polling: a 401/expired profile now caches its dimmed reauth row and
opens a cooldown, so it is shown parked and re-checked at most every 10 min
instead of being re-polled (and re-401'd) on every /summary refresh.
- Wrong fallback: the global ~/.codex session-log fallback now applies ONLY to
the bare default account. A named codex profile with no on-disk auth parks
instead of borrowing the default's local usage (no misattribution).
Replace single-account native quota collection with per-profile enumeration:
read ccs auth (Claude) and ccsx auth (Codex) profile registries plus the bare
~/.codex login, fetch each profile's quota under the existing TTL cache,
per-profile circuit breaker and 2.5s summary deadline. Emit surface, profile
and is_subscription wire fields; account_id becomes "<surface>:<profile>".
Active profiles (valid token) are live-polled and shown undimmed regardless of
default status; profiles without resolvable on-disk credentials are parked
(cache-only, dimmed). Claude per-profile credentials are read from disk only --
no macOS Keychain access -- so a profile without a credentials file renders as
needs-reauth instead of triggering a keychain prompt.
Codex quota in the menu bar came only from frozen local session logs, so it
showed stale data ("older session") that no refresh could update. Fetch it live
from the same source the dashboard uses, under the existing Claude-style safety
controls (10-min TTL, in-flight coalescing, 429 backoff, circuit breaker,
serve-stale), falling back to local logs when offline or rate-limited. A success
with no usable 5h/weekly window keeps the local reading rather than caching a
contentless row. The footer refresh and a new inline button on the Codex card
force a live pull past the open debounce, and a forced /summary re-pulls native
rows while serving the last-known cached rows if the live pull overruns its
budget, so the Claude/Codex cards never blank mid-refresh.
The spend strip gains a Today / 7d / 30d selector (default 7d), a taller chart,
and bottom time-axis labels (local hours for today, weekday for 7d, dates for
30d). The analytics endpoint now exposes a 24-bucket hourly series for today,
converted from the pipeline's UTC hour keys to the user's local clock so the
intra-day chart matches the dashboard.
Redact StageOptions error payloads and summarize debug launch args.
Propagate request IDs through Cursor daemon and dashboard completion logs.
Mark remaining P2/P3 maintainability targets as partial instead of overclaiming.
Epic P2. Closes the three real traceability gaps so every diagnostic log can
carry a requestId from origin (ccs.ts main) through HTTP edges and spawned
daemons.
- log-context: add REQUEST_ID_HEADER/ENV/PATTERN, resolveRequestIdFromEnv,
forwardRequestIdEnv. runWithRequestId now reuses a forwarded CCS_REQUEST_ID
when well-formed (child re-anchor), else mints fresh.
- request-logging-middleware: wrap the Express dashboard handler chain in
withRequestContext so downstream route-handler logs carry requestId (mirrors
src/proxy/server/proxy-server.ts, which is untouched prior art).
- forward CCS_REQUEST_ID at the 4 child-daemon spawn sites: delegation
headless-executor, cursor-daemon, cursor-profile-executor, copilot-executor.
- cursor-daemon-entry: re-anchor via runWithRequestId so daemon startup logs
correlate with the spawning CLI invocation (ALS does not cross spawn).
- logger adoption toe-hold: delegation/session-manager (load/save failures) and
docker/supervisord-lifecycle (restart failure) now use createLogger.
api/channels/shared have no touchable diagnostic console.error (CLI-UX only or
pure data); dispatcher's only diagnostic is in pre-dispatch.ts, owned by plan
#1165. Those defer to P3's full per-site sweep.
Tests: request-context-middleware (handler log requestId === header),
request-id-forwarding (env resolve/reject, re-anchor, forwardRequestIdEnv).
validate + validate:ci-parity green.
Hardens the macOS bar launch.json descriptor against untrusted/foreign-owned files and constrains the decoded descriptor; blocks dashboard file writes to the launch descriptor.
Gate /api/bar/* behind the localhost-when-auth-disabled guard (single DRY
choke point) so native quota/tier/cost can't leak on a non-loopback bind with
auth disabled; add a guard test. Delete the dishonest maxRedirections test that
asserted the opposite of the production redirect hardening. Key per-account
today-cost on the local day (matching analytics) instead of UTC. Stop the
inner 429 retry in the Claude usage fetch so the outer cache + circuit breaker
honor Retry-After. Narrow the usage-transformer map type and fix stale
doc-comments; clarify the one-alert-per-reset-window quota rule; gitignore the
local demo scaffolding.
Expose per-window detail (5h / weekly / Opus / Sonnet) on native subscription
rows instead of collapsing to a single percentage, so the bar can show the
binding window, resets, and a burn-rate pace line. Fix the Codex collector to
scan recent session rollouts newest-first for the latest non-null rate_limits
(today's exec-mode session is null) and mark the result stale with its source
time, so Codex quota appears instead of silently vanishing.
Surface the logged-in Claude Code and Codex subscription quota as first-class
summary rows so the existing gauge and alert engine render them with no new UI.
Claude Code: read the native token (~/.claude/.credentials.json, macOS Keychain
fallback) and reuse the existing Anthropic usage fetch+normalize via a new
token-fed entry point. The /oauth/usage endpoint is hostile to polling, so the
fetch is server-side only behind a 10-minute on-demand cache, in-flight
coalescing, Retry-After + exponential backoff with jitter, a 3-strike circuit
breaker with a 15-minute cooldown, and serve-stale-on-failure; logged-out or
unsupported subscriptions never spend a token call. Codex: zero-network read of
the latest rate_limits from local session rollouts, omitted when absent.
Native rows side-load bounded so a slow or failed fetch degrades to CLIProxy
rows, never an error.
monthToDate (calendar 1st-of-month to now, local) on BarAnalytics in both
compute paths, kept distinct from the rolling last30d so a fresh month resets
toward zero. Feeds the monthly-spend alert and the month-spend glance without
a rolling-window false breach.
Derive a tri-state quotaStatus (ok|unsupported|error) per account so a
provider with no quota API renders as 'no quota' with a healthy dot instead
of an alarming bare dash, and treat unsupported as healthy in deriveHealth.
Switch the analytics endpoint off the CLIProxy snapshot, which freezes
whenever the proxy restarts (usage is in-memory only), and onto the merged
daily/hourly usage the dashboard uses. Recent activity from Claude Code,
Codex, and Droid now shows, and a per-surface breakdown answers where usage
goes. Add lastActivityAt, daysSinceLastActivity, hasRecentData and a 30-day
series; per-account today_cost reads null (unknown) rather than a misleading 0.
Summary aggregator could block indefinitely: it ran the full system health
audit (a synchronous execSync) on every request and had no per-account or
request-level timeout. Now:
- per-account fetch is bounded; whole response is raced against a deadline
- health is derived per-account from each quota result (no blocking audit)
- stale-while-revalidate keeps the last value when a refresh is slow/fails
Adds GET /api/bar/analytics plus a pure, tested aggregator rolling up
today/7d/30d/all-time spend, a 7-day sparkline, and top models.
Only managed-quota providers (agy/claude/codex/gemini/ghcp) enforce tier_lock;
validate the provider against that set so locking a non-managed provider returns
400 instead of persisting a silently-unenforced config entry.
Drop the detail.source fallback and dead numeric-key lookup in the usage
transformer so unmapped auth_index rows go to the 'unknown' bucket instead of
mis-keying cost; null out today_cost when an email cost-key is shared by more
than one account (duplicate-email providers) instead of double-displaying the
combined spend.
Make tier_lock a per-provider map and honor it in findHealthyAccount and
preflightCheck for the selected provider only, so locking one provider never
disables failover for others. Add POST /api/accounts/tier-lock with tier
validation against known tiers, and serialize quota_management on config write
so the lock persists.
Single endpoint merging per-account quota, tier, paused, health and today cost
for the menu bar. Cached by default; ?refresh=true invalidates the quota cache
and pulls live server-side, debounced ~15s. Per-account errors degrade one row
without failing the payload; force-fresh skips paused accounts and caps fetch
concurrency.
Carry the CLIProxy auth_index through the usage transformer and aggregator,
build an auth_index->account map from the auth files, and wire it into the
usage syncer so persisted snapshots stamp accountId. Adds getTodayCostByAccount
and a snapshot detail reader. Backward-compatible: accountId is optional and
profile-based aggregation is unchanged.
- Add CCS_SQLITE_BIN env-var override; validated with fs.realpathSync so
symlinks are fully resolved before prefix check
- Reject any override whose realpath does not start under a trusted system
prefix (prevents PATH-hijack reintroduction from #1347)
- Add TRUSTED_PREFIX_UNIX covering /nix/store/, /opt/local/ (MacPorts),
/snap/, /run/current-system/ in addition to existing /usr/* and
/opt/homebrew/ entries
- Add TRUSTED_PREFIX_WINDOWS covering Program Files, System32, and the
Chocolatey managed bin dir (no canonical winget/Scoop path exists)
- Keep TRUSTED_SQLITE_PATHS_WINDOWS empty — Windows users set CCS_SQLITE_BIN
- Pass env as optional third param to querySqliteJson (backward compatible)
- Add 16-test suite covering env-var acceptance, /tmp rejection, symlink
traversal, NixOS paths, MacPorts, Windows fallback, and prefix safety