Skip to content

feat(cli): add Session pace and JSON pace output - #9

Closed
kmatsunami wants to merge 1279 commits into
mainfrom
feat/cli-session-and-json-pace
Closed

feat(cli): add Session pace and JSON pace output#9
kmatsunami wants to merge 1279 commits into
mainfrom
feat/cli-session-and-json-pace

Conversation

@kmatsunami

Copy link
Copy Markdown
Owner

What

  • codexbar usage (text): adds a Pace line under Session, mirroring the GUI's session pace calculation (5-hour window; codex/claude/ollama). Session uses the GUI wording "Projected empty in …"; Weekly keeps "Runs out in …".
  • --format json: adds a derived pace object to each provider payload.

JSON shape

pace.primary = session window, pace.secondary = weekly window. A key is emitted only when the corresponding text Pace line would render; nil sub-objects/fields are omitted (consistent with the existing payload style).

"pace": {
  "primary":   { "stage": "...", "deltaPercent": 30, "expectedUsedPercent": 20,
                 "actualUsedPercent": 50, "willLastToReset": false,
                 "etaSeconds": 3600, "summary": "30% in deficit | Expected 20% used | Projected empty in 1h" },
  "secondary": { "...": "...", "summary": "4% in deficit | Expected 4% used | Runs out in 3d" }
}
  • deltaPercent = actualUsedPercent − expectedUsedPercent (positive = ahead of pace / deficit; negative = behind / reserve).
  • etaSeconds / runOutProbability omitted when nil; summary is the same content as the text Pace line (minus the Pace: label).

Implementation

  • Shared computePace + paceSummary helpers in CLIRenderer so text and JSON output stay in sync.
  • PaceKind (session = 300 min / weekly = 10080 min) with provider guards matching the GUI's UsagePaceText.sessionPace.
  • Additive only — no existing JSON keys changed.

Test

  • swift test --filter CLISnapshotTests32 passing, including 4 new JSON pace tests and the session-pace text tests; related CLI suites green.
  • swiftformat --lint clean.

🤖 Generated with Claude Code

Quicksaver and others added 30 commits June 12, 2026 08:10
* Add Copilot budget extras

- Import optional GitHub web budget windows for Copilot
- Add settings for budget extras, cookies, and icon selection
- Cover the new resolver, fetcher, and settings persistence

* Fix Copilot budget cookie imports

- Default Copilot budget cookie import to Chrome only

- Surface budget fetch and decode failures consistently

- Cover cookie defaults and invalid budget JSON

* Fix Copilot budget cookie routing

- Ignore stale manual cookies when budget cookies are auto
- Cover Copilot budget cookie source routing

* Fix Copilot budget review issues

- Default budget extras off unless explicitly enabled
- Harden budget web fetch and manual cookie refresh
- Document budget cookie and reset heuristics

* Fix Copilot budget settings preview

- Forward the budget extras setting into provider previews

- Cover Copilot preview budget visibility

* Add helper for Copilot budget snapshot copies

- Centralize UsageSnapshot extra-window copying
- Clear Copilot reset baselines consistently
- Remove dead Copilot budget overloads
- Log when budget pagination hits the page cap

* Prevent Copilot budget cookies from crossing accounts

- Thread selected Copilot account identity into settings

- Reject budget cookie sessions for a different GitHub account

- Cover the matching and mismatched account flows in tests

* Bind Copilot budgets to token identity

- Resolve GitHub identity from the active token before cookie checks
- Cover stale selected account identifiers in budget tests

* Harden Copilot budget identity parsing

- Treat missing budget identity metadata as invalid response

- Reuse compiled meta parsing regexes and tighten test stubs

* Fix Copilot budget cookie fallback

- Clear mismatched cached budget cookies before browser fallback
- Treat missing GitHub identity as an unknown account mismatch
- Cover cached mismatch and missing-identity fallback behavior

* Move Copilot menu card test

* Fix settings defaults lint after rebase

* fix: preserve subscription metadata in snapshot copies

* fix: keep manual Copilot budget cookies fail closed

* refactor: keep menu card model within lint limit

* fix: preserve snapshot data with budget windows

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
…equires-approval

Fix Launch at Login disable for pending approval
ProviderSettingsPickerRowView evaluates each picker's trailingText
closure inside body, and the cookie-source pickers resolve their
"Cached: …" label through CookieHeaderCache.load, which pays a
synchronous SecItemCopyMatching round-trip through securityd plus a
CSSM decrypt per call. SwiftUI re-runs those bodies repeatedly within
a single AppKit layout pass, so opening Settings on the Providers pane
froze the main thread for seconds (a 17s sample showed 10s of one
layout pass bottoming out in keychain reads under
ProviderSettingsPickerRowView.body).

Display paths now use CookieHeaderCache.loadForDisplay, which serves a
memoized snapshot and revalidates a stale one off the calling path.
In-process store/clear calls update the snapshot synchronously; only
the first lookup per key pays the keychain read. Strict load semantics
are unchanged for probes and fetchers.
Display-cache keychain reads run outside the lock, so an in-flight
stale revalidation (or first population) could commit after a newer
in-process store or clear and temporarily resurrect superseded cookie
metadata in Settings. Every mutation now bumps a per-key generation,
reads capture the generation they started from and only commit when it
is unchanged, and clearAll bumps all known keys so wiped snapshots
cannot reappear. Losing reads return whichever newer snapshot won the
race. Adds deterministic race coverage for store, clear, and clearAll
against a stale commit.
…e-trailing-text-memo

Memoize cookie cache lookups behind settings picker labels
package_app.sh resolved built binaries by checking the legacy native
build system layout (.build/<arch>-apple-macosx/<conf>/) first and only
falling back to .build/<conf>/. Newer toolchains default to the
swiftbuild build system, which emits products to
.build/out/Products/<Conf> (with .build/<conf> as a symlink). When a
stale .build/<arch>-apple-macosx/ directory is left behind from an
earlier toolchain, the script silently packages those old binaries
instead of the ones `swift build` just produced - the app looks freshly
built but runs days-old code.

Fix: ask the toolchain where the products actually are via
`swift build --show-bin-path -c <conf> --arch <arch>` (cached per arch)
and prefer that location. The legacy layouts remain as fallbacks for
toolchains where --show-bin-path is unavailable or reports a path the
product is missing from, so the native build system and universal
(ARCHES="arm64 x86_64") flows are unchanged: there --show-bin-path
reports the per-arch directories the script already used. The
install_binary error message now lists every location searched.

Verified on macOS with bash 3.2 (no associative arrays used): with a
stale .build/arm64-apple-macosx/release/CodexBar planted next to a
fresh swiftbuild product, the script now packages the fresh binary;
with --show-bin-path unavailable the legacy resolution still wins.
…le-build-products

Fix package_app.sh preferring stale legacy build products over fresh swiftbuild output
The provider-switcher shortcut monitor peeked the event queue with
NSApp.nextEvent(matching:until:inMode:dequeue:) from a run-loop
observer in .eventTracking mode. That call re-enters the event loop in
the given mode, dispatching the menu session's own timers and sources
mid-observer. When the reentry landed during menu setup (the opening
click advances the HID counters, so the gate guaranteed a peek right
then) or amid rapid claimed Cmd-number presses, it killed the tracking
session and left a zombie menu on screen that no longer dequeued
events: clicks sat at the queue head for tens of seconds while the
cursor beach-balled, with Settings... the usual victim. A captured
trace showed one click re-peeked 13 times over 21 seconds and a menu
that stayed deaf for 92 seconds with the main thread idle in the
normal run loop - no tracking session left at all.

No passive delivery path exists for these events: local monitors,
Carbon dispatcher handlers, RegisterEventHotKey (tracking pushes a
hotkey-disable mode), and menuHasKeyEquivalent all go dark during
NSMenu tracking, so the peek stays - but it is now isolated. Peeks run
in a barren private run-loop mode with no sources or timers registered
so the reentry cannot dispatch menu-session work, they start only once
a block queued in the tracking mode proves the session is pumping, and
mouse events are no longer monitored at all - ProviderSwitcherView
already handles switcher clicks via its mouseDown/mouseUp overrides.

Fixes the menu freezes reported in steipete#1387, steipete#1427, and steipete#1329.
…ombie-events

Keep switcher shortcut peeks from killing menu tracking sessions
* Add Kimi Code API usage source

* Fix Kimi API fallback review comments

* Fix Kimi API source routing review gaps

* fix: secure Kimi API endpoint overrides

* fix: normalize Kimi coding base paths

* Prefer Kimi Code API key env var

* fix: isolate Kimi Code API credentials

* fix: harden Kimi Code API fallback

* fix: clarify Kimi Code API authentication

---------

Co-authored-by: kiranmagic7 <209323973+kiranmagic7@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Zihao-Qi and others added 28 commits June 20, 2026 03:32
* Fix memory pressure handler isolation crash

* docs: note memory pressure crash fix

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: remove dividers and extra spacing around Cost row

* fix: compact Storage, Cost, and Subscription Utilization into native rows

* test: update native cost menu assertions

* fix: preserve storage menu accessibility title

* fix: deduplicate native menu separators

* fix: separate native rows from menu actions

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: kiranmagic7 <209323973+kiranmagic7@users.noreply.github.com>
* Align provider card spacing

* docs: note provider card spacing

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Remove redundant cost tooltip

* docs: note cost tooltip cleanup

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Fix MiniMax token plan display

* fix: preserve MiniMax quota semantics

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix: keep menu bar open on refresh

* updated tests

* fix: add accessibility activation to persistent refresh row

* fix: clear recycled menu accessibility role

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: kiranmagic7 <262980978+kiranmagic7@users.noreply.github.com>
* diagnostics: support redacted diagnose output files

* fix: decode legacy diagnostic exports

* docs: note diagnostic file output

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Handle Claude CLI usage rate limits

* docs: note Claude CLI rate-limit cooldown

---------

Co-authored-by: kiranmagic7 <262980978+kiranmagic7@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* serve: report build version on /health for stale-binary detection

Expose the running build version in the `codexbar serve` /health
response so polling clients (e.g. showy-quota, glean) can compare it
against `codexbar --version` and recycle a serve process still running
an older binary after an app update. Omitted when unresolvable.

Add CLIServeRouterTests coverage and document the field in docs/cli.md.

* serve: capture /health version at startup, not lazily

Resolve the running build version once when `serve` starts (via a
ServeRuntime context) instead of a lazily-initialized static let. A
static let resolves on the first /health request, which can land after
an in-place update replaced the on-disk binary — making a stale serve
report the newly installed version and defeating the client
stale-process detection this field exists for.

Addresses Codex review feedback on PR steipete#1703.

* chore: polish health version reporting

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* ci: skip macOS shards for docs site changes

* ci: align macOS skip summary wording

* test: cover nested website assets in CI gate

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* tests: add provider quota fixture contract coverage

* test: simplify provider fixture contracts

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Secure Codex OAuth credential writes

* fix: publish Codex auth with private permissions

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
* Harden provider endpoint overrides

* Keep invalid MiMo overrides non-fallbackable

* Preserve z.ai quota override precedence

* Document endpoint override policy

* fix: surface invalid provider endpoint overrides

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
…ipete#1687)

* Harden Azure OpenAI endpoint overrides

* fix: keep invalid Azure endpoints actionable

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Render a Pace line under Session in `codexbar usage` (text), mirroring the
GUI's session pace calculation (5h window; codex/claude/ollama). Session uses
the GUI wording "Projected empty in …"; weekly keeps "Runs out in …".

Add a derived `pace` object to `--format json` (pace.primary = session,
pace.secondary = weekly) carrying the raw UsagePace fields (stage, deltaPercent,
expected/actualUsedPercent, willLastToReset, etaSeconds) plus a human-readable
summary. Emitted only when the corresponding text Pace line would render.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kmatsunami

Copy link
Copy Markdown
Owner Author

Recreating: this PR's diff was computed against a stale base (fork main was 1278 commits behind upstream). main is now fast-forwarded to upstream tip; reopening so the diff shows only the 4 feature files.

@kmatsunami kmatsunami closed this Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.