OpenUsage Documentation

repository·main·Indexed 25 days ago

https://github.com/robinebers/openusage

A native macOS application (Swift/SwiftUI) for tracking AI coding subscription usage, including session/weekly limits, credits, and spend, directly from the menu bar. It supports providers like Claude, Copilot, Cursor, and OpenRouter, and features a local HTTP API and a command-line interface (CLI) for automation. Compatible with macOS 15 (Sequoia) or later on both Apple Silicon and Intel Macs.

Tokens
26.1K
Snippets
26
Records
150
Agent score
87%

What's inside OpenUsage

  1. Overview of OpenUsage features and documentation

    main

    OpenUsage is an application that tracks usage and spend across various AI providers. The documentation is organized into several key areas to help users and developers:

    • App Behavior: Details on the Dashboard, Menu bar, Settings, Refreshing/Caching, iCloud Sync, Model pricing, Updates, and Privacy.
    • Integrations: Instructions for using the Command-line interface (CLI), the Local HTTP API, and Proxy settings.
    • Providers: Specific details for each supported provider (e.g., Claude, Copilot, Cursor, OpenRouter) regarding tracking, credentials, and error handling.
    • Developer Guides: Information on Architecture, adding new providers, debugging, and logging.
  2. Understand the OpenUsage project structure

    main

    OpenUsage is a SwiftPM package consisting of a shared module and two thin executables. The code is organized into the following functional groups:

    • App/: Startup logic and the AppKit bridge (status item, panel, app entry point).
    • Models/: Small value types used throughout the app (e.g., MetricLine, WidgetData).
    • Providers/: Individual folders for each supported provider (e.g., Claude, Codex, Cursor).
    • Stores/: Mutable state that the SwiftUI interface observes.
    • Services/: Shared infrastructure such as HTTP, the local API, and process management.
    • Support/: Shared helpers for formatting, parsing, and animations.
    • Views/: SwiftUI screens including the dashboard, settings, and menu-bar strip.
  3. Understand shared anonymous usage data

    main

    When Share Anonymous Usage is enabled, OpenUsage sends daily summaries to help improve the app. This data is anonymous and uses a random ID not tied to your identity or accounts.

    Shared Data includes:

    • App use: App activity status, app version, macOS version, enabled providers/metrics, and UI configuration (pinned vs. tucked metrics).
    • Provider refreshes: Success/failure counts per provider, coarse error categories (e.g., "network", "not logged in"), and manual refresh counts.
    • Crash reports: Technical stack traces (OpenUsage's own code only), app version, and macOS version.
  4. Understand the Model Hover Panel architecture

    main

    The Model Hover Panel is a feature designed to show a per-model spend and usage breakdown when hovering over spend rows (e.g., Today, Yesterday, Last 30 Days) in the dashboard.

    Note: This document is a historical feasibility report from 2026-07-04. The implementation has since shipped. For the current implementation, refer to:

    • SpendTileMapper.swift for mapping spend tiles.
    • HoverPopoverState.swift for popover state management.
    • ModelUsageDetail.swift for usage detail views.
    • ModelUsageSeries for per-model usage data.
    • Cursor provider documentation for current CSV parsing behavior.
  5. Understand the Account-First Multi-Account Model

    main

    OpenUsage uses an account-first model for managing multiple Claude or Codex accounts.

    • Account: An opaque identity key with a stable record ID (e.g., claude@ab12cd34).
    • Source: A location where an account is signed in (e.g., default home, config directory, cswap vault slot, Desktop/Cowork, or Codex home). Sources are attached to an account record.
    • Default Badge: A source can hold a holdsDefaultBadge status. This badge is used for bare-id aliases (like claude or codex), CLI resolution, and attribution. It is not a permanent key or a sort order.
    • Card Rendering: A card renders only if at least one of its sources is found on the local computer. If all sources for an account are removed, the card stops rendering, but its record, layout, and history are retained so it can reattach if the login reappears.
  6. Review data privacy guarantees

    main

    OpenUsage is designed to ensure sensitive information never leaves your device. The following data is never shared:

    • Account details, names, emails, or credentials.
    • Actual usage values (spend amounts, token counts, or limits).
    • Specific error messages or local file paths (only coarse error categories are sent).
    • Raw JSONL logs or conversation text.
    • Anything while the Share Anonymous Usage toggle is off.
  7. Understand Grok metrics and spend tiles

    main

    OpenUsage provides several metrics for Grok usage:

    • Weekly: Shows the shared weekly pool's usage percentage (the limit enforced by Grok's unified billing) and the countdown to the weekly reset.
    • Extra Usage: Displays the status of your pay-as-you-go cap (e.g., 2500 cap or Disabled).
    • Today / Yesterday / Last 30 Days: Displays local cost and token estimates (e.g., $4.08 · 1.2M tokens).

    Note on Spend Tiles: Cost estimates are calculated locally by reading ~/.grok/logs/unified.jsonl (or $GROK_HOME/logs/unified.jsonl). The dollar amounts are estimated from token counts using public API rates. No log data is sent externally.

  8. Understand caching behavior and data freshness

    main

    OpenUsage uses disk-based caching to show last-known values instantly at launch.

    Key Caching Rules:

    • Session Freshness: A cached value is only considered "fresh" (skipping a refresh) if it was fetched during the current running session. Values from previous sessions are displayed instantly but will be re-fetched on the first pass after launch.
    • Account Security: For Claude and Codex, cached entries are tied to the specific account. If you switch accounts, the previous account's cached values are discarded to prevent showing incorrect limits or plans.
    • Log Scan Cache: Claude, Codex, and pi spend history uses a local-log parse cache located at ~/Library/Application Support/OpenUsage/log-scan-cache/. This cache is reused only if the file path, size, modification time, and parser version match.
  9. Implement a new AI Provider

    main

    To add a new AI provider to OpenUsage, create a Swift module under Sources/OpenUsage/Providers/<Name>/ that conforms to the ProviderRuntime protocol.

    A provider consists of three components:

    1. Auth Store: Reads existing credentials from the user's machine (config files, keychain).
    2. Usage Client: Handles API calls to the provider.
    3. Mapper: Converts API responses into the OpenUsage metric vocabulary.

    Implementation Requirements

    • hasLocalCredentials(): Implement this for a local-only check (files, keychain) to see if credentials exist. This is used by FirstRunSeeder and NewProviderSeeder to auto-enable providers. This must be a cheap, non-network operation and should use loadOffMainActor for blocking loads.
    • refresh(): Returns a ProviderSnapshot. Use ProviderSnapshot.error(provider:error:) with a typed error on failure to ensure telemetry can group errors correctly (e.g., "not logged in").
    • Credential Parity: Ensure hasLocalCredentials() uses the exact same credential-reading logic and filters as refresh() to avoid duplicate logic.
  10. Handle Cursor Enterprise usage data

    main

    When working with Cursor Enterprise accounts, the GetCurrentPeriodUsage method may return no usable planUsage. In these cases, OpenUsage implements a fallback mechanism to ensure usage data is still displayed.

    To correctly display usage for Enterprise accounts, the system must:

    1. Fetch both usage-summary and request-based usage data.
    2. Use valid request allowances as the default Total Usage meter.
    3. Prefer user-scoped individualUsage.onDemand over the team aggregate teamUsage.onDemand.
    4. Map structured Auto/API percentages from individualUsage.plan.
    5. Fall back to pooled/overall usage-summary variants if request counts are unavailable.
    6. Append usage-history rows after the fallback mapping is complete.
  11. Implement smooth content-driven auto-resize for Menu-Bar Panel

    main

    To achieve smooth auto-resizing of the custom Menu-Bar Panel without the 'diagonal' effect (where sliding and resizing happen on different clocks), use a single-clock follower approach. This involves driving both the horizontal slide and the vertical height change within the same SwiftUI animation block.

    Set the height target to the destination screen's ideal height inside the same withAnimation block that drives slideProgress. This causes the panel to morph (grow/shrink) as it slides.

    withAnimation(Motion.spring) {
        slideProgress = 1
        animatedTarget = idealHeight[destinationScreen] ?? animatedTarget
    }

    Option B: Sequenced Resize (Fallback)

    If the morph feels too busy, slide at a constant height first, then resize using the macOS 14+ completion API.

    withAnimation(.spring, completionCriteria: .removed) {
        slideProgress = 1
    } completion: {
        withAnimation { animatedTarget = idealHeight[destinationScreen] ?? animatedTarget }
    }
  12. Use the Total Spend ring and metrics

    main

    If enabled providers track daily spend (Claude, Codex, Cursor, Grok, or OpenCode), a Total Spend card appears above the provider sections.

    Metric Selection: You can switch the displayed metric using the pull-down menu. The choice persists across restarts:

    • Cost: Shows each provider's share of combined dollars (default).
    • Cost/MTok: Shows the dollars-per-million-tokens rate. The center displays the blended rate across providers.
    • Tokens: Shows each provider's share of combined tokens.

    Time Periods: Use the capsule switcher to toggle between Today, Yesterday, and 30 Days.

    Interactions:

    • Hover the center ring: View the exact one-line figure.
    • Share: Click the share icon in the header or right-click the card to copy a branded PNG of the ring to your clipboard.
    • Disable: Turn off the card entirely in Settings via the Show Total Spend option.
    • Information: Click the ⓘ icon to see which providers contribute to the total.