# Zakuro for macOS (menu bar + widget)
`Zakuro.app` shows your Zakuro box at a glance, and lets you set this Mac's price, the account
default price, the number of workers, and pause or resume sharing. It talks **only** to
`zc agent` on this Mac (`http://127.0.0.1:4720/v1`, see `docs/agent-api.md`). zc talks to the hub.
- `Zakuro/`: menu bar popover (SwiftUI `MenuBarExtra`). Not sandboxed, hardened runtime, no Dock icon.
- `ZakuroWidget/`: WidgetKit extension, small and medium. Sandboxed, with `network.client` and the App Group.
- `Packages/ZakuroAgentKit/`: `Summary` v1 models, `AgentClient`, formatting, banners (`swift test`).
- `Scripts/stub-agent.py`: a stub of the agent API for development.
## Requirements
macOS 14 or later (interactive widgets), Xcode 26, `brew install xcodegen`, an
**Apple Development** signing identity, and zc with `zc agent`.
## One-time setup: your Team ID
```bash
security find-certificate -c "Apple Development" -p \
| openssl x509 -noout -subject -nameopt multiline \
| awk -F' = ' '/organizationalUnitName/ {print $2}'
export ZAKURO_DEVELOPMENT_TEAM=<that value> # export it in your shell profile, or pass it per command; never commit it
```
The code in parentheses in `security find-identity` output is not always the Team ID. Use the
certificate's organizational unit, as above.
## Build, install, test
```bash
task macos:install # build (Release, signed), copy to /Applications, quit and relaunch
task macos:test # swift test (ZakuroAgentKit) + xcodebuild test (PopoverModel)
```
`Taskfile.yml`'s `MACOS_SIGN_ARGS` holds the signing form that works on our Macs. The default is
manual signing with the Apple Development identity. The fallback is
`-allowProvisioningUpdates CODE_SIGN_STYLE=Automatic`, with Xcode signed in to the team's Apple ID.
Manual signing works with no provisioning profile: both targets get signed with your team, and the
sandboxed widget reads the App Group the app writes. The Automatic fallback is untested.
Add the widget: right-click the desktop → **Edit Widgets…** → search "Zakuro".
## Develop against the stub agent
```bash
task macos:stub-agent # terminal 2; add -- --port 4721 if a real agent runs
defaults write ai.zakuro.Zakuro ZakuroHome ~/.zakuro-stub
task macos:install
```
Stub switches: `--fail-price` (the price write is rejected), `--problem CODE` (repeatable, injects
problem banners), `--summary-version 2` ("Update zc"). Go back to the real agent with
`defaults delete ai.zakuro.Zakuro ZakuroHome`.
## Use the real agent
```bash
zc login # device key, no 12-hour expiry
zc agent install # launchd agent on 127.0.0.1:4720
```
Or click **Start agent** in the popover. It runs `zc agent install`, finding zc through the
`ZcPath` default (`defaults write ai.zakuro.Zakuro ZcPath /path/to/zc`), then `/usr/local/bin/zc`,
`/opt/homebrew/bin/zc`, `~/.cargo/bin/zc`, `~/.local/bin/zc`.
## Troubleshooting
| Widget missing from the gallery | `pluginkit -m -p com.apple.widgetkit-extension -i ai.zakuro.Zakuro.Widget`; relaunch the app from /Applications |
| Widget stuck on old numbers | It refreshes every 15 min; any change made from the popover reloads it at once. Remove and re-add it to force a reload. |
| Widget says "Open Zakuro" | The agent is down (`zc agent status`), or the app hasn't handed the endpoint over yet: open the popover once |
| Widget says "Open Zakuro to reconnect" | The agent was reinstalled (new token): open the popover |
| "Update zc" | The agent and the app speak different summary versions: `zc update`, then `task macos:install` |
| Tests "missing" | xcodegen skips files outside `sources:`. Check by test-case name: `task macos:test \| grep "Test Case"` |
| "zc at … doesn't support `zc agent`" | The zc found first is too old. Run `zc update`, or point the app at a newer zc with `defaults write ai.zakuro.Zakuro ZcPath /path/to/zc` |
CI (`.github/workflows/macos.yml`) is informational. It runs only when the repository variable
`MACOS_CI=true` is set and a `[self-hosted, macOS, ARM64]` runner is enrolled.