A native macOS menubar app (SwiftUI MenuBarExtra) that surfaces Edgee stats
and launches or relays installed desktop apps. It shells out to the edgee CLI
and reads Edgee's local files, so the Rust CLI stays the single source of truth.
The CLI itself lives in a separate repo (edgee-ai/edgee);
this app does not link it — it drives it as a subprocess and consumes its
--json output. make bundle embeds whichever edgee binary EDGEE_BIN
resolves to (the one on your PATH by default).
- macOS 14+
- Xcode / Swift toolchain (
swift,xcodebuild) - The
edgeeCLI installed (found onPATH, in~/.local/bin, or setEDGEE_BIN)
make run # build, bundle into Edgee.app, and launch it
make build # just `swift build -c release`
make test # run the unit tests (swift test)
make bundle # produce Edgee.app without launching
make dist # package the signed bundle into dist/Edgee-<version>.zip (+ sha256)
make cleanmake run produces Edgee.app (marked LSUIElement, so it lives only in the
menubar — no Dock icon) and opens it. Click the menubar icon for the dropdown.
make bundle stamps the bundle version from APP_VERSION, defaulting to the
embedded CLI's version, and always records that CLI's version in the plist's
EdgeeCLIVersion.
The menubar shows the destination model (for example, “→ gpt-5.6-terra”) when your latest recorded request was rerouted. Hover for the full route and request date. Overview shows the same details below Total spend, including fallback and unknown states. Data refreshes every 5 seconds and when the panel opens; request logs can arrive after a delay.
This requires a CLI build exposing last_request and last_request_checked in
edgee stats --json. Older CLIs show “Request routing unavailable”. The lookup
covers your current API keys in the dashboard's usage window (currently one hour),
across devices. No requests in that window clears the menubar indicator.
The menubar shows desktop apps only, and only when their app bundle is installed
in /Applications or ~/Applications. Terminal agents stay available through
the edgee CLI but do not appear in the menubar. If a relay-capable desktop app
is open without a menubar-managed proxy, its chip and the desktop-app card show
an orange warning telling you to quit and relaunch it through Edgee.
Once a release is published, end users install via the tap:
brew install --cask edgee-ai/tap/edgee-menubar
xattr -dr com.apple.quarantine "/Applications/Edgee.app" # ad-hoc signed → clear Gatekeeper onceThis installs Edgee.app and puts the bundled edgee CLI on your PATH
(symlinked into the Homebrew prefix's bin), so the app and the command line
tool always stay in lockstep. It therefore conflicts with the standalone edgee
formula — install one or the other (brew unlink edgee first if needed).
The two carry separate version numbers: the cask version is the app's, while
the CLI it installs is whichever release that build embedded — edgee --version,
or the app bundle's EdgeeCLIVersion key. Upgrading the cask only moves the CLI
when the app release bumped it, which each release states in its notes.
The app is ad-hoc signed, not yet notarized, so Gatekeeper blocks a
downloaded build on first open — hence the one-time xattr above (or right-click
→ Open in Finder). This step goes away once we ship a notarized, Developer-ID
build.
Releases are automated via .github/workflows/, mirroring the CLI's setup:
The app version is its own. The edgee CLI the bundle embeds is versioned
separately by EDGEE_CLI in release.yml, so an app-only fix ships without
touching the CLI.
- To move the embedded CLI, bump
EDGEE_CLIto a published edgee release and commit it. Leave it alone to keep the CLI you're already shipping. - Tag the app and push:
git tag vX.Y.Z && git push origin vX.Y.Z.release.yml(macOS runner) builds an arm64Edgee.app, packagesEdgee-X.Y.Z.zip(+ sha256), and opens a draft GitHub Release. - Review and publish the draft. That fires
homebrew-cask.yml, which pinsedgee-ai/homebrew-tap/Casks/edgee-menubar.rbto the new version + checksum.
Required secret: HOMEBREW_TAP_TOKEN (write on edgee-ai/homebrew-tap; the CLI
binary is fetched from edgee's public releases, so no read token is needed). The
edgee release must be published (not a draft) or the build can't download the CLI
— its arm64 darwin binary is the asset the app embeds. Running release.yml by
hand instead takes both versions as fields, so you can embed a different CLI for
one build without a commit. Manual fallback: make dist APP_VERSION=X.Y.Z EDGEE_BIN=<edgee>,
upload the zip to a release, paste version/sha256 into the cask, copy to the tap.
Currently an arm64-only build (Apple Silicon). Universal (arm64 + x86_64 via
lipo) is a follow-up.
When configured with the repo secrets below, release.yml produces Developer ID
signed (hardened runtime), notarized, and stapled releases, so Gatekeeper accepts
them with no quarantine-clearing step.
APPLE_SIGN_IDENTITY—Developer ID Application: Edgee Cloud (VC4Z4MZLM6)APPLE_CERT_P12(base64 of the exported.p12) +APPLE_CERT_PASSWORDAPPLE_API_KEY_ID,APPLE_API_ISSUER,APPLE_API_KEY_P8(base64 App Store Connect API key, fornotarytool)
All five go together: the signing and notarizing steps both key off
APPLE_SIGN_IDENTITY, so setting it without the API key secrets fails the
release. Leave it unset and builds fall back to ad-hoc signing.
The .p12 holds the signing private key — Apple cannot reissue it, only revoke
and replace the certificate, so keep a backup outside CI. Locally:
make dist CODESIGN_IDENTITY="Developer ID Application: Edgee Cloud (VC4Z4MZLM6)".
Sources/EdgeeMenuBar/EdgeeMenuBarApp.swift—@mainapp +MenuBarExtrasceneSources/EdgeeMenuBar/MenuContentView.swift— the dropdown UISources/EdgeeMenuBar/EdgeeCLI.swift— subprocess wrapper aroundedgeeSources/EdgeeMenuBar/AuthStatus.swift— decodesedgee auth status --jsonInfo.plist— bundle metadata (LSUIElement)Makefile— build/bundle/run