Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d56e685
feat(menubar): add companion runtime and backend APIs
prakersh Mar 9, 2026
f48dfa6
feat(menubar): add dashboard controls and browser coverage
prakersh Mar 9, 2026
a203e94
chore(menubar): update build release and documentation
prakersh Mar 9, 2026
fcf10c6
fix(menubar): add UniformTypeIdentifiers framework for macOS 15+ comp…
prakersh Mar 9, 2026
364fcbc
fix(menubar): avoid macOS AppDelegate symbol collision
prakersh Mar 9, 2026
272d678
fix(menubar): pass macOS linker flags to menubar e2e builds
prakersh Mar 9, 2026
2d23393
fix(menubar): use single UI framework to resolve thread conflict
prakersh Mar 9, 2026
fe2d17e
fix(menubar): add child supervision and crash logging
prakersh Mar 9, 2026
eb78572
fix(menubar): handle --help flag properly
prakersh Mar 9, 2026
2befb76
test(menubar): verify lifecycle and crash handling
prakersh Mar 9, 2026
cd084db
Use native macOS popover for menubar
prakersh Mar 9, 2026
4cf6c98
feat: refine menubar popover and tray display
prakersh Mar 10, 2026
983b725
feat(menubar): polish native panel UX and tray status refresh
prakersh Mar 10, 2026
c7f3c5c
feat(menubar): simplify macOS distribution and polish quota UX
prakersh Mar 10, 2026
e4f76a0
refactor(menubar): simplify normalize and test assertions
prakersh Mar 10, 2026
991a564
fix(runtime): remove redundant stderr writer assignment
prakersh Mar 10, 2026
8fb22c7
feat(menubar): polish tray UX and stabilize ordering behavior
prakersh Mar 11, 2026
661f1ca
feat(logging): add rotating daemon and menubar logs
prakersh Mar 11, 2026
49c48df
feat(menubar): streamline tray menu and wire native close/dashboard a…
prakersh Mar 11, 2026
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
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ ONWATCH_ADMIN_PASS=changeme

# --- Logging ---
# Log level: debug, info, warn, error (default: info)
# In background mode (default), logs go to .onwatch.log
# In background mode (default), logs are stored in the DB directory (default: ~/.onwatch/data/)
# Main daemon log: .onwatch.log (or .onwatch-test.log in --test mode)
# Menubar companion log (macOS menubar builds): menubar.log (or menubar-test.log in --test mode)
# Each file rotates at 50MB with 3 backups (.1, .2, .3)
# In debug mode (--debug), logs go to stdout
ONWATCH_LOG_LEVEL=info
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,39 @@ jobs:

- name: Build
run: go build -o onwatch .

menubar-macos:
runs-on: macos-latest
name: Menubar macOS

steps:
- uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Compile tagged menubar packages
run: |
go test -tags menubar ./internal/menubar ./internal/web
CGO_LDFLAGS="-framework UniformTypeIdentifiers" go build -tags menubar,desktop,production -o /tmp/onwatch-menubar .

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install E2E dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r tests/e2e/requirements.txt
python -m playwright install chromium

- name: Run menubar browser tests
env:
CGO_LDFLAGS: -framework UniformTypeIdentifiers
ONWATCH_E2E_GO_BUILD_TAGS: menubar,desktop,production
run: |
cd tests/e2e
pytest tests/test_menubar.py -v
75 changes: 66 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,11 @@ jobs:
- name: Test with race detection
run: go test -race ./...

# Build binaries in parallel (after tests pass)
build:
build-standard:
needs: test
strategy:
matrix:
include:
- goos: darwin
goarch: arm64
binary: onwatch-darwin-arm64
- goos: darwin
goarch: amd64
binary: onwatch-darwin-amd64
- goos: linux
goarch: amd64
binary: onwatch-linux-amd64
Expand Down Expand Up @@ -88,9 +81,73 @@ jobs:
name: ${{ matrix.binary }}
path: ${{ matrix.binary }}

build-macos-amd64:
needs: test
runs-on: macos-13
name: Build macOS amd64

steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Read version
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"

- name: Build macOS artifact
run: |
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build \
-tags menubar,desktop,production \
-ldflags="-s -w -X main.version=${{ steps.version.outputs.version }}" \
-o onwatch-darwin-amd64 .

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: onwatch-darwin-amd64
path: onwatch-darwin-amd64

build-macos-arm64:
needs: test
runs-on: macos-14
name: Build macOS arm64

steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Read version
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"

- name: Build macOS artifact
run: |
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build \
-tags menubar,desktop,production \
-ldflags="-s -w -X main.version=${{ steps.version.outputs.version }}" \
-o onwatch-darwin-arm64 .

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: onwatch-darwin-arm64
path: onwatch-darwin-arm64

# Create GitHub release with all binaries
release:
needs: build
needs: [build-standard, build-macos-amd64, build-macos-arm64]
runs-on: ubuntu-latest
name: Release

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Thumbs.db
*.gif
*.bmp
!docs/screenshots/*.png
!internal/menubar/icon_template.png
!internal/menubar/icon_template@2x.png
tmp/
temp/
test-screenshots/
Expand Down
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ curl -fsSL https://raw.githubusercontent.com/onllm-dev/onwatch/main/install.sh |

This downloads the binary to `~/.onwatch/`, creates a `.env` config, sets up a systemd service (Linux) or self-daemonizes (macOS), and adds `onwatch` to your PATH.

On macOS, the installer downloads the standard binary with menubar support.

### Homebrew (macOS & Linux)

```bash
Expand Down Expand Up @@ -127,7 +129,7 @@ Provider setup guides:
### Run

```bash
onwatch # start in background (daemonizes, logs to ~/.onwatch/.onwatch.log)
onwatch # start in background (daemonizes, logs to ~/.onwatch/data/.onwatch.log)
onwatch --debug # foreground mode, logs to stdout
onwatch stop # stop the running instance
onwatch status # check if running
Expand Down Expand Up @@ -177,6 +179,15 @@ Each quota card shows: usage vs. limit with progress bar, live countdown to rese

**Settings** -- Dedicated settings page (`/settings`) with tabs for general preferences, provider controls, notification thresholds, and SMTP email configuration.

**Menubar (macOS, Beta)** -- The macOS build includes a menubar companion with two preset views:

- **Standard** -- Provider cards with circular quota meters and reset metadata
- **Detailed** -- Expanded provider cards with sparkline trends and full quota breakdowns

Configure it in **Settings > Menubar**. You can enable or disable the companion, pick the default view, change refresh and threshold settings, and drag providers into the order you want.

Menubar is currently in beta. Feedback is highly appreciated at [github.com/onllm-dev/onwatch/issues](https://github.com/onllm-dev/onwatch/issues).

**Email notifications (Beta)** -- Configure SMTP to receive alerts when quotas cross warning or critical thresholds, or when quotas reset. Per-quota threshold overrides for fine-grained control. SMTP passwords are encrypted at rest with AES-GCM.

**Push notifications (Beta)** -- Receive browser push notifications when quotas cross thresholds. onWatch is a PWA (Progressive Web App) - install it from your browser for a native app experience. Uses Web Push protocol (VAPID) with zero external dependencies. Configure delivery channels (email, push, or both) per your preference.
Expand Down Expand Up @@ -319,10 +330,13 @@ All endpoints require authentication (session cookie or Basic Auth). Append `?pr
| `/api/cycles?type=subscription` | GET | Reset cycle history |
| `/api/cycle-overview` | GET | Cross-quota correlation at peak usage |
| `/api/summary` | GET | Usage summaries |
| `/api/capabilities` | GET | Build/runtime capabilities (platform, menubar) |
| `/api/menubar/summary` | GET | Normalized menubar snapshot payload |
| `/api/menubar/test` | GET | Browser-testable menubar page in test mode |
| `/api/sessions` | GET | Session history |
| `/api/insights` | GET | Usage insights |
| `/api/providers` | GET | Available providers |
| `/api/settings` | GET/PUT | User settings (notifications, SMTP, providers) |
| `/api/settings` | GET/PUT | User settings (notifications, SMTP, providers, menubar) |
| `/api/settings/smtp/test` | POST | Send test email via configured SMTP |
| `/api/password` | PUT | Change password |
| `/api/push/vapid` | GET | Get VAPID public key for push subscription |
Expand Down Expand Up @@ -366,11 +380,15 @@ onwatch stop && onwatch
```shell
~/.onwatch/
├── onwatch.pid # PID file
├── .onwatch.log # Log file (background mode)
└── data/
└── onwatch.db # SQLite database (WAL mode)
├── onwatch.db # SQLite database (WAL mode)
├── .onwatch.log # Main daemon log file (background mode)
└── menubar.log # Menubar companion log file (macOS menubar builds)
```

Log files are stored next to the database (default `~/.onwatch/data/`).
Each log rotates at 50 MB with 3 backups (`.1`, `.2`, `.3`) for both main and menubar logs.

On first run, if a database exists at `./onwatch.db`, onWatch auto-migrates it to `~/.onwatch/data/`.

---
Expand Down Expand Up @@ -494,7 +512,7 @@ The `docker-compose.yml` includes memory limits (64M limit, 32M reservation), lo
See [DEVELOPMENT.md](docs/DEVELOPMENT.md) for build instructions, cross-compilation, and testing.

```bash
./app.sh --build # Production binary (or: make build)
./app.sh --build # Production binary (macOS includes menubar) (or: make build)
./app.sh --test # Tests with race detection (or: make test)
./app.sh --build --run # Build + run debug mode (or: make run)
./app.sh --release # Cross-compile all platforms (or: make release-local)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.11.18
2.11.19
61 changes: 51 additions & 10 deletions app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
VERSION=$(cat "$SCRIPT_DIR/VERSION")
BINARY="onwatch"
LDFLAGS="-ldflags=-s -w -X main.version=$VERSION"
DARWIN_FULL_TAGS="menubar,desktop,production"
DARWIN_CGO_LDFLAGS="-framework UniformTypeIdentifiers"

# --- Colors ---
RED='\033[0;31m'
Expand All @@ -28,11 +29,11 @@ ${CYAN}USAGE:${NC}
./app.sh [FLAGS...]

${CYAN}FLAGS:${NC}
--build, -b Build production binary with version ldflags
--build, -b Build production binary (macOS includes menubar support)
--test, -t Run all tests with race detection and coverage
--smoke, -s Quick validation: vet + build check + short tests
--run, -r Build and run in debug mode (foreground)
--release Run tests, then cross-compile for 5 platforms
--release Run tests, then build release binaries
--clean, -c Remove binary, coverage files, dist/, test cache
--stop Stop a running instance (native or Docker)
--docker Docker mode: --build/--run/--clean/--stop use Docker
Expand All @@ -43,7 +44,7 @@ ${CYAN}FLAGS:${NC}

${CYAN}EXAMPLES:${NC}
./app.sh --build # Build production binary
./app.sh --test # Run full test suite
./app.sh --test # Run full test suite
./app.sh --smoke # Quick pre-commit check
./app.sh --clean --build --run # Clean, rebuild, and run
./app.sh --deps --build --test # Install deps, build, test
Expand Down Expand Up @@ -176,11 +177,47 @@ do_clean() {

do_build() {
info "Building onWatch v${VERSION}..."
cd "$SCRIPT_DIR"
go build -ldflags="-s -w -X main.version=$VERSION" -o "$BINARY" .
build_native_binary "$SCRIPT_DIR/$BINARY"
success "Built ./$BINARY ($(du -h "$BINARY" | cut -f1 | xargs))"
}

build_native_binary() {
local output="$1"
cd "$SCRIPT_DIR"

if [[ "$(uname)" == "Darwin" ]]; then
CGO_ENABLED=1 CGO_LDFLAGS="$DARWIN_CGO_LDFLAGS" go build \
-tags "$DARWIN_FULL_TAGS" \
-ldflags="-s -w -X main.version=$VERSION" \
-o "$output" .
return
fi

go build \
-ldflags="-s -w -X main.version=$VERSION" \
-o "$output" .
}

build_darwin() {
cd "$SCRIPT_DIR"
if [[ "$(uname)" != "Darwin" ]]; then
error "macOS menubar builds require a macOS host"
return 1
fi

mkdir -p "$SCRIPT_DIR/dist"
for arch in arm64 amd64; do
local output="dist/onwatch-darwin-${arch}"
info "Building ${output}..."
CGO_ENABLED=1 CGO_LDFLAGS="$DARWIN_CGO_LDFLAGS" GOOS=darwin GOARCH="$arch" go build \
-tags "$DARWIN_FULL_TAGS" \
-ldflags="-s -w -X main.version=$VERSION" \
-o "$SCRIPT_DIR/$output" .
done

success "Built macOS binaries in dist/"
}

do_test() {
info "Running tests with race detection and coverage..."
cd "$SCRIPT_DIR"
Expand All @@ -196,7 +233,7 @@ do_smoke() {
go vet ./...

info " Build check..."
go build -ldflags="-s -w -X main.version=$VERSION" -o /dev/null .
build_native_binary /dev/null

info " Short tests..."
go test -short -count=1 ./...
Expand All @@ -210,12 +247,16 @@ do_release() {
go test -race -cover -count=1 ./...
success "Tests passed."

info "Cross-compiling onWatch v${VERSION} for 5 platforms..."
info "Building release artifacts for onWatch v${VERSION}..."
mkdir -p "$SCRIPT_DIR/dist"

if [[ "$(uname)" == "Darwin" ]]; then
build_darwin
else
warn "Skipping macOS binaries on non-macOS host. Use macOS CI or a local macOS build host for menubar artifacts."
fi

local targets=(
"darwin:arm64:"
"darwin:amd64:"
"linux:amd64:"
"linux:arm64:"
"windows:amd64:.exe"
Expand Down
2 changes: 1 addition & 1 deletion docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ go build -ldflags="-s -w" -o onwatch.exe .
`app.sh` is the primary entry point. `make` targets are thin wrappers.

```bash
./app.sh --build # Build production binary (or: make build)
./app.sh --build # Build production binary (macOS includes menubar support) (or: make build)
./app.sh --test # Tests with race detection and coverage (or: make test)
./app.sh --build --run # Build + run in debug mode (or: make run)
./app.sh --clean # Remove binary, coverage, dist/ (or: make clean)
Expand Down
11 changes: 11 additions & 0 deletions docs/screenshots/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ Dashboard screenshots captured from a live onWatch v2.11.0 instance with real An
| `all-light.png` | All Providers view in light mode. Anthropic, Synthetic, Z.ai, Codex, GitHub Copilot (Beta), and Antigravity quotas side-by-side with combined insights. |
| `all-dark.png` | All Providers view in dark mode. |

## Menubar Companion (macOS, Beta)

| File | Description |
|------|-------------|
| `menubar-minimal-light.png` | Menubar companion minimal mode in light theme. Compact single-row quota display for quick glance checks. |
| `menubar-minimal-dark.png` | Menubar companion minimal mode in dark theme. |
| `menubar-standard-light.png` | Menubar companion standard mode in light theme. Provider cards with circular quota meters and reset metadata. |
| `menubar-standard-dark.png` | Menubar companion standard mode in dark theme. |
| `menubar-detailed-light.png` | Menubar companion detailed mode in light theme. Expanded quota details with bar rows and richer metadata. |
| `menubar-detailed-dark.png` | Menubar companion detailed mode in dark theme. |

## Legacy (pre-v2.1.0)

| File | Description |
Expand Down
Binary file added docs/screenshots/menubar-detailed-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/menubar-detailed-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/menubar-minimal-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/menubar-minimal-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/menubar-standard-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/menubar-standard-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ module github.com/onllm-dev/onwatch/v2
go 1.25.7

require (
fyne.io/systray v1.12.0
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
golang.org/x/crypto v0.47.0
modernc.org/sqlite v1.44.3
)

require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/tools v0.40.0 // indirect
modernc.org/libc v1.67.6 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
Expand Down
Loading
Loading