Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

This project follows [Semantic Versioning](https://semver.org/).

## 0.8.7 - 2026-08-22

### Changed

- Menu-bar data refreshes automatically every 30 minutes, while manual refresh and opening the
dashboard trigger a fresh bounded query instead of only repainting cached values.
- Codex quota collection now follows the official app-server rate-limit query path, with bounded
fallback and explicit stale-data handling when the upstream service cannot be reached.
- GPT, Codex, and Cursor capacity displays now preserve live changes and avoid duplicate balance
rows or misleading low estimates caused by mixed-age snapshots.

### Security

- Dependency checks now pin the build environment to `pip 26.2` and retain the existing release
artifact audit boundary.

### Fixed

- Cross-platform tests no longer invoke POSIX-only process-group behavior or hard-code `/bin/sh`
on Windows.
- Long Provider refreshes no longer cross the old three-minute desktop deadline and get reported
as failed shortly before the background refresh succeeds.
- The 30-minute menu-bar schedule now waits one interval before its first background refresh and
defers provider credential initialization until refresh is due, so app launch does not compete
with an immediate user or Capacity-page refresh.
- Refresh failures keep the last known-good quota visible with a clear freshness state instead of
silently presenting it as current data.

## 0.8.6 - 2026-08-16

### Added
Expand Down
20 changes: 10 additions & 10 deletions README.en.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<!-- openusage-release-version: 0.8.6 -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.6 build=28 channel=rc stage=candidate publication=not_published published=v0.7.1 -->
<!-- openusage-release-version: 0.8.7 -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.7 build=29 channel=rc stage=candidate publication=not_published published=v0.7.1 -->
<div align="center">

# UsageHub

### The All-in-One AI Usage & Provider Manager — Menu-bar Summary · Capacity · API Spend

[![Version](https://img.shields.io/github/v/release/tttboy123/openusage-bar?include_prereleases&color=0A84FF&label=version)](https://github.com/tttboy123/openusage-bar/releases)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Windows%20%7C%20Linux-lightgrey.svg)](https://github.com/tttboy123/openusage-bar/releases)
[![Version](https://img.shields.io/github/v/release/tttboy123/usagehub?include_prereleases&color=0A84FF&label=version)](https://github.com/tttboy123/usagehub/releases)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Windows%20%7C%20Linux-lightgrey.svg)](https://github.com/tttboy123/usagehub/releases)
[![Built with](https://img.shields.io/badge/built%20with-Electron%20%2B%20SwiftUI-blue.svg)](https://www.electronjs.org/)
[![Downloads](https://img.shields.io/github/downloads/tttboy123/openusage-bar/total)](https://github.com/tttboy123/openusage-bar/releases/latest)
[![Downloads](https://img.shields.io/github/downloads/tttboy123/usagehub/total)](https://github.com/tttboy123/usagehub/releases/latest)
[![License](https://img.shields.io/badge/License-Apache--2.0-111111?style=flat-square)](LICENSE)

[中文](README.md) | English | [Changelog](CHANGELOG.md) | [Install guide](docs/release-quick-start.md) | [Provider support](docs/provider-support.md) | [Local API](docs/api/local-api-v1.md)
Expand Down Expand Up @@ -140,8 +140,8 @@ corrupt existing configs.
<summary><strong>Why is the menu-bar icon missing?</strong></summary>

The tray icon only shows while the app is running — open UsageHub (or let it auto-start at login).
Versions before 0.8.6 had a tray-icon path bug that made it invisible; 0.8.6 ships a bundled brand
template icon.
Versions before 0.8.6 had a tray-icon path bug that made it invisible; 0.8.6 fixed it with a bundled
brand template icon.

</details>

Expand Down Expand Up @@ -190,13 +190,13 @@ Remove quarantine for this app only; do not disable Gatekeeper system-wide.

### macOS

Grab the latest `UsageHub-0.8.6-mac-arm64.dmg` from [Releases](https://github.com/tttboy123/openusage-bar/releases),
Grab the latest `UsageHub-0.8.7-mac-arm64.dmg` from [Releases](https://github.com/tttboy123/usagehub/releases),
open it, and drag **UsageHub** into **Applications**. First launch registers the login item and
bundled collector; the menu-bar icon immediately shows today's usage summary.

Native SwiftUI build: [OpenUsage-Bar-v0.8.6-macos-arm64.dmg](https://github.com/tttboy123/openusage-bar/releases/download/v0.8.6/OpenUsage-Bar-v0.8.6-macos-arm64.dmg) (available after candidate publication).
Native SwiftUI build: [OpenUsage-Bar-v0.8.7-macos-arm64.dmg](https://github.com/tttboy123/usagehub/releases/download/v0.8.7/OpenUsage-Bar-v0.8.7-macos-arm64.dmg) (available after candidate publication).

> 0.8.6 is a candidate pre-release without Developer ID notarization; Windows/Linux installers
> 0.8.7 is a candidate pre-release without Developer ID notarization; Windows/Linux installers
> ship with the cross-platform release.

## Development
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- openusage-release-version: 0.8.6 -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.6 build=28 channel=rc stage=candidate publication=not_published published=v0.7.1 -->
<!-- openusage-release-version: 0.8.7 -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.7 build=29 channel=rc stage=candidate publication=not_published published=v0.7.1 -->
<div align="center">

# UsageHub
Expand Down Expand Up @@ -139,7 +139,7 @@ flowchart LR
<summary><strong>为什么菜单栏看不到图标?</strong></summary>

菜单栏图标只在 App 运行时显示。请确认 App 已打开(或已加入登录项自动启动)。0.8.6 之前
存在托盘图标路径硬编码导致的不可见问题,已在此版本修复:图标现在使用随包携带的品牌模板图标。
存在托盘图标路径硬编码导致的不可见问题,已在 0.8.6 修复:图标现在使用随包携带的品牌模板图标。

</details>

Expand Down Expand Up @@ -188,12 +188,12 @@ xattr -dr com.apple.quarantine "/Applications/UsageHub.app"
### macOS

从 [Releases](https://github.com/tttboy123/usagehub/releases) 下载最新
`UsageHub-0.8.6-mac-arm64.dmg`,双击打开后把 **UsageHub** 拖入 **Applications**。
`UsageHub-0.8.7-mac-arm64.dmg`,双击打开后把 **UsageHub** 拖入 **Applications**。
首次打开自动注册登录项与后台采集器,菜单栏图标随即显示今日用量简况。

原生 SwiftUI 版本:[OpenUsage-Bar-v0.8.6-macos-arm64.dmg](https://github.com/tttboy123/usagehub/releases/download/v0.8.6/OpenUsage-Bar-v0.8.6-macos-arm64.dmg)(候选发布后可用)。
原生 SwiftUI 版本:[OpenUsage-Bar-v0.8.7-macos-arm64.dmg](https://github.com/tttboy123/usagehub/releases/download/v0.8.7/OpenUsage-Bar-v0.8.7-macos-arm64.dmg)(候选发布后可用)。

> 0.8.6 为候选预发布,未做 Developer ID 公证;Windows/Linux 安装包随跨平台发布提供。
> 0.8.7 为候选预发布,未做 Developer ID 公证;Windows/Linux 安装包随跨平台发布提供。

## Development

Expand Down
8 changes: 4 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# OpenUsage Bar Roadmap

<!-- openusage-release-state: version=0.8.6 channel=rc api=1.0 canary=0/5 clock=not_started -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.6 build=28 channel=rc stage=candidate publication=not_published published=v0.7.1 -->
<!-- openusage-release-state: version=0.8.7 channel=rc api=1.0 canary=0/5 clock=not_started -->
<!-- openusage-build-identity: product=UsageHub candidate=0.8.7 build=29 channel=rc stage=candidate publication=not_published published=v0.7.1 -->

OpenUsage Bar is a local-first AI usage product with two compatible layers:

Expand All @@ -22,10 +22,10 @@ contract, packaging, and canary gates below pass. The existing external Canary
remains **0 / 5 qualified Apple Silicon Macs** with its 30-day clock **not
started**.

The repository's single candidate truth is now `0.8.6` build `28`, channel
The repository's single candidate truth is now `0.8.7` build `29`, channel
`rc`, across machine state, bundle metadata, Python, Web, Desktop, release
guides, and verification markers. That is candidate preparation, not evidence
that `v0.8.6` has been published or that its Canary clock has started.
that `v0.8.7` has been published or that its Canary clock has started.

## Compatibility invariants

Expand Down
6 changes: 3 additions & 3 deletions desktop/collector_runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const childProcess = require("child_process");
const path = require("path");

const MAX_RUNTIME_PATH_LENGTH = 4096;
const MAX_COLLECTOR_COMMAND_TIMEOUT_MS = 180_000;
const MAX_COLLECTOR_COMMAND_TIMEOUT_MS = 300_000;
const COLLECTOR_INTERVAL_SECONDS = "1800";
const DELETE_STATE_CONFIRMATION = "DELETE-LOCAL-USAGEHUB-STATE";
const VALID_LIFECYCLE_PLAN = Symbol("validCollectorLifecyclePlan");
Expand Down Expand Up @@ -300,15 +300,15 @@ function runCollectorCommand(
async function waitForActiveRefresh({
readStatus,
wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
attempts = 90,
attempts = 150,
intervalMs = 2_000,
} = {}) {
if (
typeof readStatus !== "function" ||
typeof wait !== "function" ||
!Number.isInteger(attempts) ||
attempts < 1 ||
attempts > 90 ||
attempts > 150 ||
!Number.isInteger(intervalMs) ||
intervalMs < 100 ||
intervalMs > 2_000
Expand Down
2 changes: 1 addition & 1 deletion desktop/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ async function refreshUsageData() {
const ok = await runCollectorCommand(
command,
["__refresh-current", "--ledger", ledger],
{ timeoutMs: 180_000 },
{ timeoutMs: 300_000 },
);
if (ok) await refreshTraySnapshot().catch(() => {});
return {
Expand Down
4 changes: 2 additions & 2 deletions desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "usagehub-desktop",
"version": "0.8.6",
"version": "0.8.7",
"description": "UsageHub cross-platform desktop client (Electron) wrapping the local web dashboard",
"main": "main.js",
"scripts": {
Expand All @@ -16,11 +16,11 @@
"build": {
"appId": "com.lune.openusagebar",
"productName": "UsageHub",
"buildVersion": "28",
"buildNumber": "28",
"buildVersion": "29",
"buildNumber": "29",
"extraMetadata": {
"buildVersion": "28",
"buildNumber": "28"
"buildVersion": "29",
"buildNumber": "29"
},
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"files": [
Expand Down Expand Up @@ -115,7 +115,7 @@
"category": "Utility",
"desktop": {
"entry": {
"X-AppImage-Version": "28"
"X-AppImage-Version": "29"
}
},
"extraResources": [
Expand Down
4 changes: 2 additions & 2 deletions desktop/product_version_truth.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
const productVersionTruth = Object.freeze({
displayName: "UsageHub",
legacyDisplayName: "OpenUsage Bar",
candidateVersion: "0.8.6",
candidateBuild: "28",
candidateVersion: "0.8.7",
candidateBuild: "29",
channel: "rc",
releaseStage: "candidate",
publicationStatus: "not_published",
Expand Down
4 changes: 2 additions & 2 deletions desktop/tests/build_identity_ui.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const require = createRequire(import.meta.url);
const BASE_IDENTITY = Object.freeze({
displayName: "UsageHub",
legacyDisplayName: "OpenUsage Bar",
candidateVersion: "0.8.6",
candidateBuild: "28",
candidateVersion: "0.8.7",
candidateBuild: "29",
channel: "rc",
releaseStage: "candidate",
publicationStatus: "not_published",
Expand Down
26 changes: 26 additions & 0 deletions desktop/tests/collector_lifecycle.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ test("desktop reuses an active scheduled refresh instead of launching a competit
assert.equal(waits, 2);
});

test("desktop can wait through a scheduled refresh beyond the old three-minute ceiling", async () => {
const { waitForActiveRefresh } = require("../collector_runtime.js");
let reads = 0;
let waits = 0;

const result = await waitForActiveRefresh({
readStatus: async () => {
reads += 1;
return reads <= 101
? { phase: "running", state: "attention", succeeded: null }
: { phase: "idle", state: "ok", succeeded: true };
},
wait: async () => {
waits += 1;
},
attempts: 105,
intervalMs: 2_000,
});

assert.deepEqual(result, {
waited: true,
status: { phase: "idle", state: "ok", succeeded: true },
});
assert.equal(waits, 101);
});

test("Linux invokes the packaged managed-service command and waits for readiness", {
skip: process.platform === "win32",
}, async (context) => {
Expand Down
6 changes: 3 additions & 3 deletions desktop/tests/product_version_truth.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const { formatCandidateVersion, productVersionTruth } = productVersionModule;
const expectedIdentity = {
displayName: "UsageHub",
legacyDisplayName: "OpenUsage Bar",
candidateVersion: "0.8.6",
candidateBuild: "28",
candidateVersion: "0.8.7",
candidateBuild: "29",
channel: "rc",
releaseStage: "candidate",
publicationStatus: "not_published",
Expand All @@ -36,7 +36,7 @@ test("freezes the projection and module exports", () => {
});

test("formats version and channel without embedding final UI copy", () => {
assert.equal(formatCandidateVersion(productVersionTruth), "0.8.6 RC");
assert.equal(formatCandidateVersion(productVersionTruth), "0.8.7 RC");
assert.equal(
formatCandidateVersion({ candidateVersion: "1.2.3", channel: "beta" }),
"1.2.3 BETA",
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-development-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ clean source tree on an idle reference machine.
`docs/schemas/product-version-truth-v1.schema.json`; every role runs
`scripts/verify_product_version_truth.py` after changing a bound surface.
- Candidate identity and published identity are independent facts. The current
candidate is UsageHub 0.8.6 build 28 RC; the published stable baseline remains
candidate is UsageHub 0.8.7 build 29 RC; the published stable baseline remains
v0.7.1. A candidate may never use the `stable` channel.
- Product versions are not API versions. Local API `1.0`, Gateway
`gateway.openusage/v1`, and runtime capability
Expand Down
4 changes: 2 additions & 2 deletions docs/artifact-build-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ For an extracted or mounted package, `release_artifact_audit.py` performs a
three-way binding among the packaged bytes, the canonical source, and product
truth. It also validates native metadata:

- macOS: `UsageHub`, version `0.8.6`, build `28` in `Info.plist`;
- Windows: `UsageHub`, product version `0.8.6.28`, and a file version whose
- macOS: `UsageHub`, version `0.8.7`, build `29` in `Info.plist`;
- Windows: `UsageHub`, product version `0.8.7.29`, and a file version whose
normalized build is `28` in PE `VersionInfo`;
- Linux: version/build fields in `app.asar`; a final AppImage additionally has
exactly one desktop entry with `Name=UsageHub` and
Expand Down
16 changes: 8 additions & 8 deletions docs/canary.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ maintainer explicitly activates the timed cohort, the clock state is
`not_started`.

The repository is preparing the
[v0.8.6 RC candidate](https://github.com/tttboy123/openusage-bar/releases/tag/v0.8.6).
[v0.8.7 RC candidate](https://github.com/tttboy123/openusage-bar/releases/tag/v0.8.7).
That link becomes an intake surface only after the immutable candidate is
published; preparing metadata does not activate or qualify the cohort. The
last published baseline remains v0.7.1.
Expand Down Expand Up @@ -111,18 +111,18 @@ Record pass/fail and UTC date for each event:
files extracted from it. Use the single candidate version below only after
the candidate assets have been published and checksummed.

The current candidate version is `0.8.6`:
The current candidate version is `0.8.7`:
```bash
gh attestation verify OpenUsage-Bar-v0.8.6-macos-arm64.zip \
gh attestation verify OpenUsage-Bar-v0.8.7-macos-arm64.zip \
--repo tttboy123/openusage-bar \
--signer-workflow \
tttboy123/openusage-bar/.github/workflows/release.yml \
--source-ref refs/tags/v0.8.6 \
--source-ref refs/tags/v0.8.7 \
--deny-self-hosted-runners
shasum -a 256 -c OpenUsage-Bar-v0.8.6-macos-arm64.zip.sha256
unzip OpenUsage-Bar-v0.8.6-macos-arm64.zip
cd OpenUsage-Bar-v0.8.6-macos-arm64
scripts/verify_canary_candidate.py --assets-dir .. --version 0.8.6
shasum -a 256 -c OpenUsage-Bar-v0.8.7-macos-arm64.zip.sha256
unzip OpenUsage-Bar-v0.8.7-macos-arm64.zip
cd OpenUsage-Bar-v0.8.7-macos-arm64
scripts/verify_canary_candidate.py --assets-dir .. --version 0.8.7
```

The packaged verifier requires the expected version and exactly one release
Expand Down
4 changes: 2 additions & 2 deletions docs/distribution-trust-posture.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,13 @@ Inspection and immediate binding verification use:
python scripts/distribution_trust_posture.py inspect \
--platform linux \
--package-root dist-desktop/linux-unpacked \
--artifact dist-desktop/UsageHub-0.8.6-linux-x86_64.AppImage \
--artifact dist-desktop/UsageHub-0.8.7-linux-x86_64.AppImage \
--output dist-evidence/usagehub-distribution-trust-linux-x64.json

python scripts/distribution_trust_posture.py verify \
--report dist-evidence/usagehub-distribution-trust-linux-x64.json \
--platform linux \
--artifact dist-desktop/UsageHub-0.8.6-linux-x86_64.AppImage
--artifact dist-desktop/UsageHub-0.8.7-linux-x86_64.AppImage
```

The CLI intentionally has no option for supplying signing status,
Expand Down
2 changes: 1 addition & 1 deletion docs/product-version-truth.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ work. It does not bump a version or make a release claim.
The current repository has two deliberately separate truths:

- published stable baseline: `v0.7.1`;
- development candidate: `UsageHub 0.8.6 RC (build 28)`, not published and
- development candidate: `UsageHub 0.8.7 RC (build 29)`, not published and
not release eligible.

`releaseStage=candidate` describes the candidate's lifecycle. The independent
Expand Down
2 changes: 1 addition & 1 deletion docs/release-handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ assembly is not reported as success.
python scripts/release_handoff.py assemble \
--bundle-dir dist-handoff/mac-arm64 \
--collector dist-collector/openusage-collector \
--artifact dist-desktop/UsageHub-0.8.6-mac-arm64.dmg \
--artifact dist-desktop/UsageHub-0.8.7-mac-arm64.dmg \
--evidence dist-evidence/usagehub-native-evidence-mac-arm64.json \
--trust-posture-report \
dist-evidence/usagehub-distribution-trust-mac-arm64.json
Expand Down
Loading