From 24b04966dc15b1a9ba427cd87efbdd0f11ba1a41 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sat, 17 Oct 2026 15:32:35 -0700 Subject: [PATCH] Report each macOS app's build commit in /api/admin/status --- api/openapi.yaml | 15 +- docs/macos.md | 2 +- docs/operations.md | 4 +- internal/api/api.gen.go | 477 +++++++++++++++++++------------------ internal/server/hub.go | 33 ++- internal/server/server.go | 1 + internal/server/sse.go | 7 +- mac/Sources/Byre/API.swift | 7 +- mac/bundle.sh | 1 + 9 files changed, 306 insertions(+), 241 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index 8a95f0f..a93cee2 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1680,7 +1680,7 @@ components: Status: type: object - required: [started_at, db_bytes, last_event_seq, projects, active_tasks, sse_clients, live_agents, running_turns, rate_limits, agent_processes, zombies, stuck_after_seconds, protocol_errors] + required: [started_at, db_bytes, last_event_seq, projects, active_tasks, sse_clients, clients, live_agents, running_turns, rate_limits, agent_processes, zombies, stuck_after_seconds, protocol_errors] properties: live_agents: { type: integer, description: Agent processes currently running. } agent_processes: @@ -1707,6 +1707,19 @@ components: projects: { type: integer } active_tasks: { type: integer } sse_clients: { type: integer } + clients: + type: array + description: Each distinct user, Mac and app build with an open event stream, oldest connection first. + items: { $ref: "#/components/schemas/ConnectedClient" } + + ConnectedClient: + type: object + required: [login, host, commit, connected_at] + properties: + login: { type: string } + host: { type: string, description: The Mac's name from its Byre-Host header; empty when the client sent none. } + commit: { type: string, description: The git commit the app was built from, from its Byre-Commit header; empty when the client sent none. } + connected_at: { type: string, format: date-time, description: When the client's oldest open stream connected. } TurnRef: type: object diff --git a/docs/macos.md b/docs/macos.md index efce3f1..7fb433c 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -16,7 +16,7 @@ The app connects to byre's `tailscale serve` HTTPS URL, so its requests get the - `UserNotifications` for notifications. - Third-party code: Apple's `swift-openapi-generator` (with `swift-openapi-runtime` and `swift-openapi-urlsession`) for the API types and client, and SwiftTerm for the terminal. -The project is a Swift package under `mac/`, built with `swift build`, not an Xcode project. It builds with Xcode's toolchain: `xcode-select --print-path` must print `/Applications/Xcode.app/Contents/Developer`. With the Command Line Tools selected while Xcode is installed, Swift Build finds two toolchains for the Command Line Tools path and picks one at random on each run; the pick is part of the build request, so every change of pick rebuilds every target. `mac/bundle.sh` builds the release configuration in one directory shared by every worktree, `~/Library/Caches/byre/build`: SwiftPM keys a build on the package's absolute path, so a build in a fresh worktree, or one sharing a `--scratch-path`, recompiles every dependency. Under a `lockf` lock on `~/Library/Caches/byre/build.lock`, so builds from different worktrees take turns, it copies the worktree's `mac/` and `api/` there with `rsync --checksum`, which rewrites only files whose contents differ and leaves the rest's modification times alone, builds, and copies the executable out to `mac/.build/Byre`. It passes `--no-color-diagnostics` because `COLOR_DIAGNOSTICS` otherwise follows whether output is a terminal and is part of the build request, so agents whose shells differ would each rebuild every target in turn; `build-request.json` under `.build/out/Intermediates.noindex/XCBuildData/` shows what a build was keyed on. It then assembles `mac/.build/byre.app` (the executable, `mac/Info.plist` with the fixed bundle identifier `run.fc.byre.mac`, the `mac/fonts/` files and an `.icns` rendered from `mac/icon.svg`, cached as `mac/.build/byre.icns` until the SVG changes) and signs it; notification permission needs the bundle identifier. +The project is a Swift package under `mac/`, built with `swift build`, not an Xcode project. It builds with Xcode's toolchain: `xcode-select --print-path` must print `/Applications/Xcode.app/Contents/Developer`. With the Command Line Tools selected while Xcode is installed, Swift Build finds two toolchains for the Command Line Tools path and picks one at random on each run; the pick is part of the build request, so every change of pick rebuilds every target. `mac/bundle.sh` builds the release configuration in one directory shared by every worktree, `~/Library/Caches/byre/build`: SwiftPM keys a build on the package's absolute path, so a build in a fresh worktree, or one sharing a `--scratch-path`, recompiles every dependency. Under a `lockf` lock on `~/Library/Caches/byre/build.lock`, so builds from different worktrees take turns, it copies the worktree's `mac/` and `api/` there with `rsync --checksum`, which rewrites only files whose contents differ and leaves the rest's modification times alone, builds, and copies the executable out to `mac/.build/Byre`. It passes `--no-color-diagnostics` because `COLOR_DIAGNOSTICS` otherwise follows whether output is a terminal and is part of the build request, so agents whose shells differ would each rebuild every target in turn; `build-request.json` under `.build/out/Intermediates.noindex/XCBuildData/` shows what a build was keyed on. It then assembles `mac/.build/byre.app` (the executable, `mac/Info.plist` with the fixed bundle identifier `run.fc.byre.mac` and a `ByreCommit` key added holding the worktree's `git rev-parse HEAD`, which `API` sends as the `Byre-Commit` header on every request beside `Byre-Host`, the Mac's name, the `mac/fonts/` files and an `.icns` rendered from `mac/icon.svg`, cached as `mac/.build/byre.icns` until the SVG changes) and signs it; notification permission needs the bundle identifier. The app is signed with the team's Apple Development certificate (`codesign --sign "Apple Development"`), with the entitlements in `mac/byre.entitlements` (the team-prefixed application identifier, the iCloud container with CloudKit, and development push for sync), and `mac/byre.provisionprofile` embedded as `Contents/embedded.provisionprofile`. The profile is a development profile Xcode manages: it allows the team's Apple Development certificates and the Macs registered to the team, so it runs only on those Macs, and its CloudKit environment is Development. `security cms -D -i mac/byre.provisionprofile` prints it, `ProvisionedDevices` and `DeveloperCertificates` included, and `security find-identity -v -p codesigning` lists the certificates on this Mac. Building on a Mac not yet in the profile, or one without an Apple Development certificate, means regenerating it with Xcode's automatic signing: an Xcode app project with bundle identifier `run.fc.byre.mac`, the team, and an entitlements file naming the iCloud container with CloudKit, built with `xcodebuild -allowProvisioningUpdates -allowProvisioningDeviceRegistration` while the team's Apple ID is signed in to Xcode, creates the certificate, registers the Mac and writes the new profile to `~/Library/Developer/Xcode/UserData/Provisioning Profiles/`; copy it over `mac/byre.provisionprofile` and commit it. diff --git a/docs/operations.md b/docs/operations.md index 543ca90..8f02ae5 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -112,7 +112,7 @@ Clients reach `tailscale serve` over HTTP/2; it proxies to byre over HTTP/1.1 an ## macOS app -**Building.** `mac/bundle.sh`, from any directory, builds the release configuration with `swift build` in `~/Library/Caches/byre/build`, one build directory every worktree shares and takes turns in (`docs/macos.md`, "Stack", says why), and assembles `mac/.build/byre.app`: the `Byre` executable, `mac/Info.plist`, the `mac/fonts/` woff2 files, an icon rendered from `mac/icon.svg` with `rsvg-convert` and `iconutil` (cached as `mac/.build/byre.icns` until the SVG changes) and `mac/byre.provisionprofile`, then signs the bundle with the Mac's Apple Development certificate and `mac/byre.entitlements`. It prints the bundle's path. It needs Xcode selected (`xcode-select --print-path` prints `/Applications/Xcode.app/Contents/Developer`), `rsvg-convert` (Homebrew's `librsvg`), an Apple Development certificate for the team (`security find-identity -v -p codesigning` lists them) and a Mac registered in the profile (`security cms -D -i mac/byre.provisionprofile` prints `ProvisionedDevices`); `docs/macos.md`, "Stack", says how to regenerate the profile for a new Mac. Debug builds are refused by an `#error` in `ByreApp.swift`. The first build on a Mac fetches the Swift packages pinned in `mac/Package.resolved` and compiles them and the generated API client; later builds, from any worktree, recompile only the targets whose sources differ from the last build's, usually just `Byre`. The API client is generated from `api/openapi.yaml` at build time. The palette is `mac/Sources/Byre/Palette.swift`, one light and one dark value per colour. +**Building.** `mac/bundle.sh`, from any directory, builds the release configuration with `swift build` in `~/Library/Caches/byre/build`, one build directory every worktree shares and takes turns in (`docs/macos.md`, "Stack", says why), and assembles `mac/.build/byre.app`: the `Byre` executable, `mac/Info.plist` with a `ByreCommit` key added holding the worktree's `git rev-parse HEAD`, the `mac/fonts/` woff2 files, an icon rendered from `mac/icon.svg` with `rsvg-convert` and `iconutil` (cached as `mac/.build/byre.icns` until the SVG changes) and `mac/byre.provisionprofile`, then signs the bundle with the Mac's Apple Development certificate and `mac/byre.entitlements`. It prints the bundle's path. It needs Xcode selected (`xcode-select --print-path` prints `/Applications/Xcode.app/Contents/Developer`), `rsvg-convert` (Homebrew's `librsvg`), an Apple Development certificate for the team (`security find-identity -v -p codesigning` lists them) and a Mac registered in the profile (`security cms -D -i mac/byre.provisionprofile` prints `ProvisionedDevices`); `docs/macos.md`, "Stack", says how to regenerate the profile for a new Mac. Debug builds are refused by an `#error` in `ByreApp.swift`. The first build on a Mac fetches the Swift packages pinned in `mac/Package.resolved` and compiles them and the generated API client; later builds, from any worktree, recompile only the targets whose sources differ from the last build's, usually just `Byre`. The API client is generated from `api/openapi.yaml` at build time. The palette is `mac/Sources/Byre/Palette.swift`, one light and one dark value per colour. **Installing.** The app runs from where it was built: `open mac/.build/byre.app` in the worktree that built it. Nothing copies it to `/Applications`. A new build replaces the bundle, so quit the running app first (`pkill -x Byre` quits every running copy, whichever worktree built it) and open the new one; step 4 of "Deploying" in `AGENTS.md` does this after a deploy. Every build shares the bundle identifier `run.fc.byre.mac` and the same store, so any build reconnects to the server the last one used. Closing the window leaves the app running for notifications; ⌘Q quits it. @@ -174,7 +174,7 @@ Codex runs with its own home, `codex` under the data directory (`CODEX_HOME`, se ## Status -`GET /api/admin/status` reports `running_turns` (task sessions now `running`, as session, task and vendor; lead turns aren't listed); `rate_limits`, the latest status each vendor sent since byre started (Claude's `rate_limit_info`, with `windowMins` added to each `unifiedWindows` entry by `addClaudeWindowMins` in `internal/agents/manager.go` from the window's name, since Claude doesn't send a length; Codex's `account/rateLimits/updated` reshaped by `codexRateLimit` in `internal/agents/codex.go` into the same `status`, `rateLimitType` and `unifiedWindows` fields, `windowMins` from its `windowDurationMins`). Claude only reports during its turns; Codex is also asked once at startup (`readCodexRateLimit`, a short-lived `codex app-server` that calls `account/rateLimits/read` without opening a thread), so its entry is there before any Codex turn runs, and every Codex update broadcasts `admin.health`; `agent_processes`, `zombies`, `stuck_after_seconds` and `protocol_errors` (below); and server counts (start time, agent processes, projects, active tasks, SSE clients, database size including WAL, last event seq). +`GET /api/admin/status` reports `running_turns` (task sessions now `running`, as session, task and vendor; lead turns aren't listed); `rate_limits`, the latest status each vendor sent since byre started (Claude's `rate_limit_info`, with `windowMins` added to each `unifiedWindows` entry by `addClaudeWindowMins` in `internal/agents/manager.go` from the window's name, since Claude doesn't send a length; Codex's `account/rateLimits/updated` reshaped by `codexRateLimit` in `internal/agents/codex.go` into the same `status`, `rateLimitType` and `unifiedWindows` fields, `windowMins` from its `windowDurationMins`). Claude only reports during its turns; Codex is also asked once at startup (`readCodexRateLimit`, a short-lived `codex app-server` that calls `account/rateLimits/read` without opening a thread), so its entry is there before any Codex turn runs, and every Codex update broadcasts `admin.health`; `agent_processes`, `zombies`, `stuck_after_seconds` and `protocol_errors` (below); `clients`, one entry per distinct login, `host` and `commit` among the open `GET /api/events` streams, with the oldest stream's `connected_at` (the app opens two streams, so it shows once). `host` and `commit` come from the `Byre-Host` and `Byre-Commit` headers, which the macOS app sends on every request: the Mac's name (`Host.current().localizedName`, else `ProcessInfo.processInfo.hostName`, the same name `ClientRun.swift` reports) and the `ByreCommit` that `mac/bundle.sh` stamped into its `Info.plist`; they are empty for a client that sends neither. To check that a rebuilt app is running, compare each client's `commit` with the commit it was built from. And server counts (start time, agent processes, projects, active tasks, SSE clients, database size including WAL, last event seq). Agent health is `internal/agents/health.go`: diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index e53a743..3df44e0 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -637,6 +637,19 @@ type Blob struct { Id string `json:"id"` } +// ConnectedClient defines model for ConnectedClient. +type ConnectedClient struct { + // Commit The git commit the app was built from + Commit string `json:"commit"` + + // ConnectedAt When the client's oldest open stream connected. + ConnectedAt time.Time `json:"connected_at"` + + // Host The Mac's name from its Byre-Host header; empty when the client sent none. + Host string `json:"host"` + Login string `json:"login"` +} + // DirEntry defines model for DirEntry. type DirEntry struct { // Ignored git ignores the entry: .gitignore and the other exclude files match it or a directory above it. @@ -1063,8 +1076,11 @@ type Status struct { // AgentProcesses Every agent process byre is tracking, idle sessions included. AgentProcesses []AgentProcess `json:"agent_processes"` - DbBytes int64 `json:"db_bytes"` - LastEventSeq int64 `json:"last_event_seq"` + + // Clients Each distinct user, Mac and app build with an open event stream, oldest connection first. + Clients []ConnectedClient `json:"clients"` + DbBytes int64 `json:"db_bytes"` + LastEventSeq int64 `json:"last_event_seq"` // LiveAgents Agent processes currently running. LiveAgents int `json:"live_agents"` @@ -3572,234 +3588,235 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/server/hub.go b/internal/server/hub.go index 3e016e8..cad3552 100644 --- a/internal/server/hub.go +++ b/internal/server/hub.go @@ -1,6 +1,7 @@ package server import ( + "sort" "sync" "g.fc.run/misc/byre/internal/api" @@ -8,25 +9,26 @@ import ( type hub struct { mu sync.Mutex - subs map[chan api.Event]bool + subs map[chan api.Event]api.ConnectedClient } func newHub() *hub { - return &hub{subs: map[chan api.Event]bool{}} + return &hub{subs: map[chan api.Event]api.ConnectedClient{}} } -func (h *hub) subscribe() chan api.Event { +func (h *hub) subscribe(client api.ConnectedClient) chan api.Event { h.mu.Lock() defer h.mu.Unlock() ch := make(chan api.Event, 256) - h.subs[ch] = true + h.subs[ch] = client return ch } func (h *hub) unsubscribe(ch chan api.Event) { h.mu.Lock() defer h.mu.Unlock() - if !h.subs[ch] { + _, ok := h.subs[ch] + if !ok { return } delete(h.subs, ch) @@ -51,3 +53,24 @@ func (h *hub) count() int { defer h.mu.Unlock() return len(h.subs) } + +func (h *hub) clients() []api.ConnectedClient { + h.mu.Lock() + defer h.mu.Unlock() + type key struct{ login, host, commit string } + oldest := map[key]api.ConnectedClient{} + for _, c := range h.subs { + k := key{c.Login, c.Host, c.Commit} + prev, ok := oldest[k] + if ok && !c.ConnectedAt.Before(prev.ConnectedAt) { + continue + } + oldest[k] = c + } + out := []api.ConnectedClient{} + for _, c := range oldest { + out = append(out, c) + } + sort.Slice(out, func(i, j int) bool { return out[i].ConnectedAt.Before(out[j].ConnectedAt) }) + return out +} diff --git a/internal/server/server.go b/internal/server/server.go index ae8069f..9e4f4cd 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -599,6 +599,7 @@ func (s *Server) status(ctx context.Context) (api.Status, error) { Projects: c.Projects, ActiveTasks: c.ActiveTasks, SseClients: s.hub.count(), + Clients: s.hub.clients(), LiveAgents: s.agents.LiveCount(), RunningTurns: usage.Running, RateLimits: usage.RateLimits, diff --git a/internal/server/sse.go b/internal/server/sse.go index e32fb3a..220332e 100644 --- a/internal/server/sse.go +++ b/internal/server/sse.go @@ -55,7 +55,12 @@ func (s *Server) StreamEvents(w http.ResponseWriter, r *http.Request, params api w.Header().Set("Cache-Control", "no-cache") w.WriteHeader(http.StatusOK) - live := s.hub.subscribe() + live := s.hub.subscribe(api.ConnectedClient{ + Login: viewer, + Host: r.Header.Get("Byre-Host"), + Commit: r.Header.Get("Byre-Commit"), + ConnectedAt: time.Now(), + }) defer s.hub.unsubscribe(live) for { diff --git a/mac/Sources/Byre/API.swift b/mac/Sources/Byre/API.swift index 421cdeb..d61edfb 100644 --- a/mac/Sources/Byre/API.swift +++ b/mac/Sources/Byre/API.swift @@ -102,7 +102,12 @@ final class API: Sendable { init(origin: URL) { self.origin = origin - session = URLSession(configuration: .default) + let config = URLSessionConfiguration.default + config.httpAdditionalHeaders = [ + "Byre-Commit": Bundle.main.object(forInfoDictionaryKey: "ByreCommit") as! String, + "Byre-Host": Host.current().localizedName ?? ProcessInfo.processInfo.hostName, + ] + session = URLSession(configuration: config) client = Client( serverURL: origin, configuration: Configuration(dateTranscoder: ByreDates()), diff --git a/mac/bundle.sh b/mac/bundle.sh index c11721c..093d329 100755 --- a/mac/bundle.sh +++ b/mac/bundle.sh @@ -27,6 +27,7 @@ rm -rf "$app" mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources/fonts" cp "$build/Byre" "$app/Contents/MacOS/Byre" cp "$mac/Info.plist" "$app/Contents/Info.plist" +plutil -insert ByreCommit -string "$(git -C "$root" rev-parse HEAD)" "$app/Contents/Info.plist" cp "$mac"/fonts/*.woff2 "$app/Contents/Resources/fonts/" cp "$icns" "$app/Contents/Resources/byre.icns" cp "$mac/byre.provisionprofile" "$app/Contents/embedded.provisionprofile"