macOS menu bar app for aggregating API usage quotas via plugin scripts
  • Swift 57.9%
  • Python 36.6%
  • HTML 3.4%
  • Shell 2.1%
Find a file
Mars May c095667537 test: 更新 claude 插件测试,移除已废弃的 design 用量字段
API 不再返回 seven_day_omelette 字段,删除对应测试,
替换为验证当前两字段行为和忽略未知字段的测试。
2026-06-02 00:25:07 +08:00
docs docs: 同步更新架构文档目录结构 2026-06-02 00:23:34 +08:00
Resources fix: MiniMax 插件适配新 API 返回结构 2026-06-01 19:22:59 +08:00
Screenshots docs: 更新截图,替换为 v0.1.22 新 UI 2026-05-12 09:43:44 +08:00
scripts chore: 更新发布脚本服务器目录路径 2026-05-15 16:29:25 +08:00
Sources refactor: 阶段四架构改进 2026-06-02 00:16:17 +08:00
Tests test: 更新 claude 插件测试,移除已废弃的 design 用量字段 2026-06-02 00:25:07 +08:00
.gitignore docs: 新增架构说明与开发规范文档 2026-06-01 20:11:35 +08:00
LICENSE Add MIT license 2026-04-29 22:03:18 +08:00
Package.swift Remove redundant SelfTest target and update docs 2026-04-29 21:43:25 +08:00
README.md docs: 更新 README 说明 Claude 插件 PLAN=none 选项 2026-05-28 09:32:22 +08:00
README_EN.md docs: 更新 README 说明 Claude 插件 PLAN=none 选项 2026-05-28 09:32:22 +08:00

UsageBoard

中文

UsageBoard is a native macOS menu bar app that aggregates and displays usage quotas for APIs, model services, search services, proxy services, and more. Each data source is a plugin; the app periodically executes plugins, parses their stdout JSON, and renders usage as progress bars.

Features

  • Resides in the menu bar; click the icon to open a quick preview.
  • Supports grouped and tabbed display modes.
  • Supports manual refresh, scheduled refresh, per-card refresh, and a quit button.
  • Scheduled refresh pauses during system sleep and resumes on wake.
  • Plugin-based usage queries with per-plugin configurable refresh intervals and parameters.
  • Plugin icon support: loads remote images from metadata config and caches them.
  • Subscription level badge display (black background, white rounded label).
  • Plugin settings UI auto-generated from script metadata, including segmented controls, directory pickers, and file pickers.
  • New plugins are disabled by default; required parameters are checked before enabling.
  • Plugin data cached to disk by stateID; last successful data shown on launch.
  • Bundled plugins are installed to the user plugin directory on first launch.
  • Settings page supports launch at login, plugin drag-and-drop reordering, plugin help docs, update checking, and in-app updates.
  • Usage display supports percentage or ratio, reset time, progress bar colors, and optional token usage charts.
  • Plugins can return failures as {"error": "message"}; the error is shown directly in the card body.
  • Supports Chinese and English; both app UI and plugin metadata display in the selected language.

Screenshots

Claude Tab MiniMax Tab
Claude Tab MiniMax Tab
Grouped View GLM Chart
Grouped View Grouped — GLM Chart
Codex Chart Settings
Grouped — Codex Chart Plugin Settings

Bundled Plugins

Plugin Script Purpose
Zhipu glm-usage-plugin.py Query Zhipu / ZAI Coding Plan usage and stats
Claude claude-usage-plugin.py Query Claude subscription usage and stats
Codex codex-usage-plugin.py Query OpenAI Codex CLI usage and stats
MiniMax minimax-usage-plugin.py Query MiniMax Coding Plan usage
DeepSeek deepseek-usage-plugin.py Query DeepSeek account balance
Tavily tavily-usage-plugin.py Query Tavily Search monthly usage

Bundled plugin source files are in Resources/BundledPlugins. After packaging, they reside in the app bundle at Contents/Resources/Plugins/.

Runtime Directory

UsageBoard uses:

~/Library/Application Support/UsageBoard/

Contents:

  • config.json: Main configuration file.
  • plugins/: User plugin directory. The file picker defaults to this location when adding plugins.
  • states/: Plugin data cache directory.

On launch, the app creates symlinks in plugins/ pointing to bundled plugins from Contents/Resources/Plugins/ inside the app bundle (or Resources/BundledPlugins/ during development).

Configuration

Main configuration JSON structure:

{
  "schemaVersion": 1,
  "language": "zh-Hans",
  "overviewDisplayMode": "tabs",
  "launchAtLogin": false,
  "plugins": [
    {
      "stateID": "stable-cache-id",
      "name": "Example",
      "enabled": false,
      "executablePath": "~/Library/Application Support/UsageBoard/plugins/example-plugin.py",
      "refreshIntervalSeconds": 300,
      "metadata": {
        "name": "Example",
        "description": "Example plugin",
        "description@zh-Hans": "示例插件",
        "description@en": "Example plugin",
        "parameters": [
          {
            "name": "API_KEY",
            "label": "API Key",
            "label@zh-Hans": "Api Key",
            "label@en": "API Key",
            "type": "secret",
            "required": true,
            "placeholder": "Service API Key"
          },
          {
            "name": "STAT_PERIOD",
            "label": "Stats Period",
            "label@zh-Hans": "统计周期",
            "label@en": "Stats Period",
            "type": "choice",
            "required": true,
            "defaultValue": "7d",
            "options": [
              {"label": "7 days", "label@zh-Hans": "7 天", "label@en": "7 days", "value": "7d"},
              {"label": "15 days", "label@zh-Hans": "15 天", "label@en": "15 days", "value": "15d"},
              {"label": "30 days", "label@zh-Hans": "30 天", "label@en": "30 days", "value": "30d"}
            ]
          }
        ]
      },
      "parameterValues": {
        "API_KEY": "",
        "STAT_PERIOD": "7d"
      }
    }
  ]
}

Notes:

  • overviewDisplayMode supports grouped and tabs.
  • language supports zh-Hans and en; takes effect after restart.
  • launchAtLogin controls launch at login.
  • plugins[].stateID is the plugin cache ID, persisted across launches.
  • plugins[].enabled — when false, the plugin is not executed.
  • plugins[].metadata is typically parsed from the script header comment block.
  • plugins[].parameterValues stores parameter values from the settings UI.

Plugin Development

Plugins are recommended to use Python scripts. UsageBoard executes .py plugins with:

/usr/bin/env python3 /path/to/plugin.py --usageboard-param KEY=value --usageboard-param USAGEBOARD_LANGUAGE=en

Plugins must output valid JSON to stdout. stderr can be used for debugging; non-zero exit codes, timeouts, or invalid JSON will show as plugin errors. Plugins can also write {"error": "message"} to stdout to report a failure, and UsageBoard shows that error in the plugin card body.

See the Plugin Authoring Guide for complete documentation.

Parameter Metadata

Place a UsageBoardPlugin comment block at the top of the script. UsageBoard reads this block and generates a settings form:

#!/usr/bin/env python3
# UsageBoardPlugin:
# {
#   "name": "Example",
#   "icon": "https://example.com/icon.png",
#   "description": "Example plugin",
#   "description@zh-Hans": "示例插件",
#   "description@en": "Example plugin",
#   "parameters": [
#     {
#       "name": "API_KEY",
#       "label": "API Key",
#       "label@zh-Hans": "Api Key",
#       "label@en": "API Key",
#       "type": "secret",
#       "required": true,
#       "placeholder": "Service API Key"
#     },
#     {
#       "name": "STAT_PERIOD",
#       "label": "Stats Period",
#       "label@zh-Hans": "统计周期",
#       "label@en": "Stats Period",
#       "type": "choice",
#       "required": true,
#       "defaultValue": "7d",
#       "options": [
#         {"label": "7 days", "label@zh-Hans": "7 天", "label@en": "7 days", "value": "7d"},
#         {"label": "15 days", "label@zh-Hans": "15 天", "label@en": "15 days", "value": "15d"},
#         {"label": "30 days", "label@zh-Hans": "30 天", "label@en": "30 days", "value": "30d"}
#       ]
#     }
#   ]
# }
# /UsageBoardPlugin

Display-related plugin metadata fields support locale-specific variants, e.g. name@zh-Hans, name@en, description@zh-Hans, description@en, label@zh-Hans, label@en, placeholder@zh-Hans, placeholder@en. If the field for the current language is missing or empty, UsageBoard falls back to the base field without a language suffix.

Supported parameter types:

  • string
  • secret
  • integer
  • boolean
  • choice
  • directory
  • file

choice parameters render as segmented controls in Settings; directory parameters render as a path field with a folder picker; file parameters render as a path field with a file picker.

Parameter reading example:

def parse_usageboard_params(argv):
    values = {}
    index = 0
    while index < len(argv):
        if argv[index] == "--usageboard-param" and index + 1 < len(argv):
            key_value = argv[index + 1]
            if "=" in key_value:
                key, value = key_value.split("=", 1)
                values[key] = value
            index += 2
        else:
            index += 1
    return values

UsageBoard also passes the current app language: --usageboard-param USAGEBOARD_LANGUAGE=zh-Hans or --usageboard-param USAGEBOARD_LANGUAGE=en. Scripts should read this reserved parameter and return display text in the corresponding language.

Response Data Format

{
  "updatedAt": "2026-04-29T00:00:00Z",
  "items": [
    {
      "id": "requests",
      "name": "Requests",
      "used": 1200,
      "limit": 1500,
      "displayStyle": "ratio",
      "resetAt": "2026-04-29T05:00:00Z",
      "status": "normal",
      "color": "blue"
    }
  ],
  "badge": "PRO",
  "chart": {
    "kind": "line",
    "period": "30d",
    "bucketUnit": "day",
    "buckets": [
      {
        "id": "2026-05-01",
        "label": "05-01",
        "segments": [
          {"model": "glm-4.5", "tokens": 1200},
          {"model": "glm-4.6", "tokens": 800}
        ]
      }
    ],
    "message": null
  }
}

Failures can also return:

{
  "error": "Invalid API Key. Check your settings."
}

Field descriptions:

  • updatedAt: Plugin data update time, ISO 8601 format.
  • items[].id: Stable item ID.
  • items[].name: Display name.
  • items[].used / items[].limit: Used amount and total quota.
  • items[].displayStyle: percent shows percentage, ratio shows numeric ratio.
  • items[].resetAt: Optional reset time, ISO 8601 format.
  • items[].status: normal, warning, critical, or unknown.
  • items[].color: Optional progress bar color. Supports blue, yellow, orange, red, green; defaults to blue.
  • badge: Optional string displayed in a black rounded badge next to the plugin card title (white uppercase bold text).
  • chart: Optional token usage chart. Currently supports kind: "line".
  • chart.period: Stats period identifier, e.g. 7d, 15d, 30d.
  • chart.bucketUnit: Time bucket unit, supports hour or day.
  • chart.buckets[].segments[]: Per-bucket model segments with model and tokens.
  • chart.message: Optional message shown when stats data is empty or unavailable.
  • error: Optional top-level error message. When present and non-empty, the run is treated as failed and the text is shown in the card body.

The bundled Zhipu, Claude, and Codex plugins provide a STAT_PERIOD parameter supporting 7d, 15d, and 30d. The Zhipu plugin uses the domestic API endpoint and is compatible with both Zhipu and ZAI Coding Plan keys. The Claude plugin fetches subscription usage via OAuth API; its PLAN parameter supports a none option that skips the API call and returns only local JSONL stats. It also supports a CLAUDE_ONLY toggle to filter third-party models and can use DATA_DIR to point at the ~/.claude data directory. The Codex plugin uses the DATA_DIR parameter to specify the data directory (default ~/.codex), reads auth.json for authentication, and parses session files to generate token stats. Both Claude and Codex plugins use an incremental caching strategy stored in the data directory and re-scan today's data on every run. The DeepSeek plugin provides a LIMIT parameter for the displayed balance limit and colors the progress bar by the balance-to-limit ratio.

Installation

Install via Homebrew:

brew tap marsmay/usageboard
brew install --cask usageboard

On first launch, macOS may show a "cannot verify developer" warning. Open System Settings → Privacy & Security and click Open Anyway, or run:

xattr -cr /Applications/UsageBoard.app

System Requirements

Runtime:

  • macOS 13.0 or later
  • System python3 available for executing Python plugins

Development:

  • Xcode
  • Swift 6.3 toolchain

Build & Test

Debug build:

swift build

Run tests:

swift test

Release build:

swift build -c release

Build, sign, and launch dist/UsageBoard.app locally:

bash scripts/build.sh

scripts/build.sh stops any running UsageBoard instance, builds a release, copies the binary and bundled plugins into dist/UsageBoard.app, injects the update check URL into Info.plist via PlistBuddy, performs ad-hoc signing, and launches the app. The UB_UPDATE_CHECK_URL environment variable can be used to customize the update check URL.

Release

Generate and upload a new version:

bash scripts/release.sh

Specify a version:

bash scripts/release.sh 0.1.20

The release script:

  1. Reads the current version from dist/UsageBoard.app/Contents/Info.plist.
  2. Generates a new version number.
  3. Auto-generates release notes from commits since the last release tag (or accepts manual notes as the second argument).
  4. Builds a release.
  5. Copies the binary and bundled plugins.
  6. Injects the update check URL into Info.plist via PlistBuddy.
  7. Re-signs and verifies the app.
  8. Generates UsageBoard-<version>.zip.
  9. Generates version.json.
  10. Uploads to the configured server path.
  11. Cleans up old remote zips.

Current release artifacts:

  • dist/UsageBoard-0.1.20.zip
  • dist/version.json

Project Structure

Sources/
  UsageBoardCore/       Config, models, plugin execution, cache, updates, and core logic
  UsageBoardApp/        SwiftUI + AppKit macOS app
Tests/
  UsageBoardTests/      XCTest unit tests
Resources/
  BundledPlugins/       Bundled Python plugins
  PluginAuthoringGuide.html
  UsageBoard.icns
scripts/
  build.sh              Local build, sign, and launch
  release.sh            Release script
dist/
  UsageBoard.app        Local test app bundle

License

MIT