From 8f3226350ee49edc8284440d8bc69747e2e9e6ad Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 20 Oct 2026 08:55:37 -0700 Subject: [PATCH] Check the CloudKit schema when packaging and show broken iCloud sync --- api/openapi.yaml | 24 ++ docs/macos.md | 2 +- docs/operations.md | 8 +- internal/agents/clients.go | 3 + internal/api/api.gen.go | 601 +++++++++++++++++--------------- internal/clients/clients.go | 7 + internal/server/clients.go | 28 ++ internal/server/server.go | 2 + mac/Sources/Byre/Remote.swift | 10 + mac/Sources/Byre/Settings.swift | 22 ++ mac/Sources/Byre/Startup.swift | 40 ++- mac/Sources/Byre/Status.swift | 21 +- mac/Sources/Byre/Store.swift | 6 + mac/package.sh | 16 + 14 files changed, 502 insertions(+), 288 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index 03956d4..ed4ea44 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1015,6 +1015,20 @@ paths: description: Switched to the WebSocket protocol. "400": { $ref: "#/components/responses/Error" } + /api/clients/sync: + put: + operationId: setClientSync + description: Records how the calling app's iCloud sync last went, for the clients in the status. The Byre-Client and Byre-Host headers name the client. + requestBody: + required: true + content: + application/json: + schema: { $ref: "#/components/schemas/ClientSync" } + responses: + "204": + description: Recorded. + "400": { $ref: "#/components/responses/Error" } + /api/clients/transfers/{transferId}: parameters: - { name: transferId, in: path, required: true, schema: { type: string } } @@ -1984,6 +1998,16 @@ components: 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 WebSocket connected. } last_action_at: { type: string, format: date-time, description: When byre last received a request the human made from this client since byre started; absent when none. } + sync: { $ref: "#/components/schemas/ClientSync" } + + ClientSync: + type: object + required: [ok, at] + description: The app's latest finished iCloud sync operation; absent from a client until it reports one. + properties: + ok: { type: boolean, description: Whether that operation succeeded. } + error: { type: string, description: Why it failed; absent when ok. } + at: { type: string, format: date-time, description: When it finished. } ClientRequest: type: object diff --git a/docs/macos.md b/docs/macos.md index 22df01a..ce0d606 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -163,7 +163,7 @@ A Mac that runs the server but not the app is updated by `byre-update` (`mac/Sou ## Persistence -`Persistence.swift` holds the app's state in SwiftData, synced through CloudKit to the private database of container `iCloud.run.fc.byre.mac`, so it follows the iCloud account to every Mac: `ServerRecord` (one per saved server: its origin and `color`, an index into the task colours), `DraftRecord` (drafts, see "Drafts") and `VisitRecord` (one per task: when it was last selected, read by the task switcher). CloudKit has no unique constraints, so two Macs can each create a record for the same thing; readers take the oldest `ServerRecord` per origin, the newest `DraftRecord` and the latest `VisitRecord` per task; the next draft or visit write keeps one and deletes the rest, and Forget Server deletes every record for its origin. The store is `byre.store` in `run.fc.byre.mac` under Application Support (the app isn't sandboxed, so SwiftData's default `default.store` would be shared with every other unsandboxed app). It opens at launch and the app exits if it can't, which is what an unsigned build without the iCloud entitlement does. Forget Server (in the app menu) flushes that server's drafts, reports pane focus false, drops its store and streams, and shows the connection screen again, its field holding the forgotten origin. Quitting does the same flush and report for every server at once, waiting up to 2 s for the reports before exiting (`applicationShouldTerminate` in `ByreApp.swift`). +`Persistence.swift` holds the app's state in SwiftData, synced through CloudKit to the private database of container `iCloud.run.fc.byre.mac`, so it follows the iCloud account to every Mac: `ServerRecord` (one per saved server: its origin and `color`, an index into the task colours), `DraftRecord` (drafts, see "Drafts") and `VisitRecord` (one per task: when it was last selected, read by the task switcher). CloudKit has no unique constraints, so two Macs can each create a record for the same thing; readers take the oldest `ServerRecord` per origin, the newest `DraftRecord` and the latest `VisitRecord` per task; the next draft or visit write keeps one and deletes the rest, and Forget Server deletes every record for its origin. The store is `byre.store` in `run.fc.byre.mac` under Application Support (the app isn't sandboxed, so SwiftData's default `default.store` would be shared with every other unsandboxed app). It opens at launch and the app exits if it can't, which is what an unsigned build without the iCloud entitlement does. `Servers.sync` is the iCloud sync state, from the latest finished CloudKit setup, import and export, which Settings shows under iCloud sync and every connection reports to its server (`docs/operations.md`, "Storage"). Forget Server (in the app menu) flushes that server's drafts, reports pane focus false, drops its store and streams, and shows the connection screen again, its field holding the forgotten origin. Quitting does the same flush and report for every server at once, waiting up to 2 s for the reports before exiting (`applicationShouldTerminate` in `ByreApp.swift`). ## Gaps in SwiftUI diff --git a/docs/operations.md b/docs/operations.md index 5e73a2c..1702a4f 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -133,7 +133,7 @@ A new build reaches the service by replacing `/Applications/Byre.app`: `mac/depl A release is one notarized disk image, `byre-.dmg`, holding `Byre.app`: the macOS app, with the server (`Contents/Helpers/byre`), `byre-hands` (`Contents/Helpers/byre-hands`) and `byre-update` (`Contents/Helpers/byre-update`) inside it. Every Mac installs the same app; the server's Mac also runs `byre install` from it (see "Running as a service"). Release images are served from `https://dl.byre.run/` (see "Website"), and each release's notes are on the repo's releases page on g.fc.run, `https://g.fc.run/ai/byre/releases`. -`mac/package.sh` builds one for whatever the checkout holds, into `mac/.build/release//`, `` being the bundle's `CFBundleShortVersionString`, and prints the image's path. It builds with `mac/bundle.sh` (signed with Developer ID, see `docs/macos.md`), zips the app and notarizes it with `xcrun notarytool submit --keychain-profile byre-notary --wait`, staples the ticket, builds the disk image with `hdiutil` (the app and an `/Applications` link), signs, notarizes and staples that too, and writes its SHA-256 beside it. It then signs the image for Sparkle with `sign_update --account byre` and writes `appcast.xml`, a feed with that one release. +`mac/package.sh` builds one for whatever the checkout holds, into `mac/.build/release//`, `` being the bundle's `CFBundleShortVersionString`, and prints the image's path. It first checks CloudKit's Production schema (see "Storage" under "macOS app"): it exports it with `cktool`, lists every `var` of every `@Model` class in `mac/Sources/Byre/Persistence.swift` as `CD_.CD_`, and stops, naming them, when any is missing, since iCloud would reject those records and stall the sync of every Mac that writes one. It needs the `cktool` management token on the Mac it runs on. It then builds with `mac/bundle.sh` (signed with Developer ID, see `docs/macos.md`), zips the app and notarizes it with `xcrun notarytool submit --keychain-profile byre-notary --wait`, staples the ticket, builds the disk image with `hdiutil` (the app and an `/Applications` link), signs, notarizes and staples that too, and writes its SHA-256 beside it. It then signs the image for Sparkle with `sign_update --account byre` and writes `appcast.xml`, a feed with that one release. A deploy is that image installed rather than published: `mac/deploy.sh` runs `package.sh`, installs the image's app with `mac/install.sh ` (mounts the image, copies `Byre.app` to `/Applications/Byre.app.new`, removes the old bundle and moves the new one into place, so nothing running from the old bundle has its files written over; it prints the installed `ByreCommit`); the service sees its executable replaced and restarts itself (see "Running as a service"). Client Macs get the same image through the client tools and `install.sh` (`AGENTS.md`, "Deploying"). A deployed build's `CFBundleVersion` is at least the last release's, so the updater never offers to go back to it. @@ -167,7 +167,7 @@ Clients reach `tailscale serve` over HTTP/2; it proxies to byre over HTTP/1.1 an **Installing.** A release is installed from its disk image into `/Applications` (see "Releases") and updates itself (`docs/macos.md`, "Updates"). A deploy installs its build there the same way (see "Releases"). A build from a checkout can also run from where it was built, `open mac/.build/Byre.app` in the worktree that built it, after quitting the running app (`pkill -x Byre` quits every running copy). Every build shares the bundle identifier `run.fc.byre.mac` and the same store, so any build reconnects to the servers the last one saved, switched between with the window's server picker in its compact toolbar, which ends with the Away button (see "Away"). Closing the window leaves the app running for notifications; ⌘Q quits it. -**Storage.** The app keeps three kinds of record in SwiftData (`mac/Sources/Byre/Persistence.swift`): each saved server's origin and window colour, when each task was last selected, and composer and answer drafts as JSON, one record per server and login (`docs/macos.md`, "Persistence" and "Drafts"). The store is `~/Library/Application Support/run.fc.byre.mac/byre.store`, with its `-shm` and `-wal` files and CloudKit's working files beside it in that directory; the app isn't sandboxed, so it names its own file rather than SwiftData's default `default.store`, which every unsandboxed app shares. The store syncs through the iCloud private database of container `iCloud.run.fc.byre.mac`, in CloudKit's Production environment (`com.apple.developer.icloud-container-environment` in `mac/byre.entitlements`), whose schema is deployed from Development in the CloudKit console before a release that changes the SwiftData models; the CloudKit console (icloud.developer.apple.com, the container, Production, Private Database, zone `com.apple.coredata.cloudkit.zone`) shows the records. Deleting the local files while the app isn't running makes the next launch download the records again from iCloud; deleting the records in the console as well starts from the connection screen. Pasted images stay in memory and are dropped on Forget Server and quit. The app logs through `os.Logger`: `log show --last 10m --predicate 'subsystem == "run.fc.byre.mac"'` prints it. +**Storage.** The app keeps three kinds of record in SwiftData (`mac/Sources/Byre/Persistence.swift`): each saved server's origin and window colour, when each task was last selected, and composer and answer drafts as JSON, one record per server and login (`docs/macos.md`, "Persistence" and "Drafts"). The store is `~/Library/Application Support/run.fc.byre.mac/byre.store`, with its `-shm` and `-wal` files and CloudKit's working files beside it in that directory; the app isn't sandboxed, so it names its own file rather than SwiftData's default `default.store`, which every unsandboxed app shares. The store syncs through the iCloud private database of container `iCloud.run.fc.byre.mac`, in CloudKit's Production environment (`com.apple.developer.icloud-container-environment` in `mac/byre.entitlements`). Production never creates fields itself: a record carrying an attribute its schema lacks is rejected, and that one rejection stalls every later upload from that Mac (its `ANSCKEVENT` table shows exports failing with `CKErrorDomain` 2), until the app relaunches after the schema is fixed. The app never writes to Development either, so a change to the SwiftData models must be added there by hand before it is deployed or released: `xcrun cktool export-schema --team-id --container-id iCloud.run.fc.byre.mac --environment development --output-file ` (`` is `TeamIdentifier` in `security cms -D -i mac/byre.provisionprofile`), add each new attribute to its `CD_` record type as `CD_` (`Int` is `INT64 QUERYABLE SORTABLE`), `xcrun cktool import-schema` the file back to Development, then Deploy Schema Changes in the CloudKit console; `export-schema --environment production` shows what Production has, and `mac/package.sh` refuses to build while it lacks a model attribute. `cktool` needs a management token saved with `xcrun cktool save-token --type management --method file` (the console's Tokens & Keys page makes one). The CloudKit console (icloud.developer.apple.com, the container, Production, Private Database, zone `com.apple.coredata.cloudkit.zone`) shows the records. The app watches every finished CloudKit setup, import and export (`NSPersistentCloudKitContainer.eventChangedNotification`, in `Servers` in `mac/Sources/Byre/Startup.swift`) and keeps the latest of each kind: succeeded, or failed with the error (for a partial failure, the first record's error and how many records failed), and when. Its sync state is the newest failed one while any kind's latest failed, so a successful import doesn't hide exports that still fail, and otherwise the newest. A failure is logged with the error in the clear, which CoreData's own log lines redact. The Settings tab shows the state under iCloud sync, a failure in the danger colour, and the app reports it to every server it is connected to with `PUT /api/clients/sync` (`ClientSync`: `ok`, `error`, `at`), on each change and each time its `/api/clients/connect` WebSocket connects or reconnects, so a report lost while the server was unreachable is sent again; the server keeps it in the client registry and shows it as the client's `sync` in the status (see "Status"), so the Status tab's Clients table and `list_clients` show a Mac whose sync is failing. Deleting the local files while the app isn't running makes the next launch download the records again from iCloud; deleting the records in the console as well starts from the connection screen. Pasted images stay in memory and are dropped on Forget Server and quit. The app logs through `os.Logger`: `log show --last 10m --predicate 'subsystem == "run.fc.byre.mac"'` prints it. ## Screen and app control @@ -252,7 +252,7 @@ Antigravity runs `agy` from `PATH` with the user's own Antigravity login and con ## Status -`GET /api/admin/status` reports `running_turns` (task sessions now `running`, as session, task and vendor; lead turns aren't listed); `rate_limits`, each vendor's latest usage, see "Usage" below; `agent_processes`, `zombies`, `stuck_after_seconds` and `protocol_errors` (below); `clients`, one entry per command channel byre holds (the `/api/clients/connect` WebSockets in `internal/clients`, see "Client Macs"), oldest first, so it lists exactly the Macs `list_clients` and the `client_*` tools can reach; a Mac whose event streams are open but whose command channel is down isn't listed. `id`, `host` and `commit` come from the `Byre-Client`, `Byre-Host` and `Byre-Commit` headers, which the macOS app sends on every request: a UUID the app makes up each time it starts, the Mac's name (`Host.current().localizedName`, else `ProcessInfo.processInfo.hostName`, the name agents' client tools take) and the `ByreCommit` that `mac/bundle.sh` stamped into its `Info.plist`. To check that a rebuilt app is running, compare each client's `commit` with the commit it was built from. `last_action_at` is when byre last received a request the human made from that client `id`: any request other than a `GET`, except the two the app sends on its own (`PUT /api/me/pane-focus` and `PUT /api/tasks/{taskId}/summary-read`, listed in `automaticRequests` in `internal/server/server.go`), and every binary message on a terminal WebSocket, which includes the replies SwiftTerm sends by itself to queries in the output (cursor position and the like). The `identify` middleware and `terminalInput` record it in the client registry's memory (`Registry.Acted` in `internal/clients`), so it is absent until that client's first action after byre starts. The registry keeps each client by `id` across command-channel drops, so a Mac that sleeps and reconnects keeps its `last_action_at`, and forgets a client 24 hours after it was last connected or acted, since an app that quit never comes back under that `id`. An action at least a second after the last one from that client that broadcast, every `GET /api/events` stream opening or closing, and every command channel connecting or disconnecting broadcasts an unstored `admin.clients` event (`clientsChanged`). 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`, each vendor's latest usage, see "Usage" below; `agent_processes`, `zombies`, `stuck_after_seconds` and `protocol_errors` (below); `clients`, one entry per command channel byre holds (the `/api/clients/connect` WebSockets in `internal/clients`, see "Client Macs"), oldest first, so it lists exactly the Macs `list_clients` and the `client_*` tools can reach; a Mac whose event streams are open but whose command channel is down isn't listed. `id`, `host` and `commit` come from the `Byre-Client`, `Byre-Host` and `Byre-Commit` headers, which the macOS app sends on every request: a UUID the app makes up each time it starts, the Mac's name (`Host.current().localizedName`, else `ProcessInfo.processInfo.hostName`, the name agents' client tools take) and the `ByreCommit` that `mac/bundle.sh` stamped into its `Info.plist`. To check that a rebuilt app is running, compare each client's `commit` with the commit it was built from. `last_action_at` is when byre last received a request the human made from that client `id`: any request other than a `GET`, except the two the app sends on its own (`PUT /api/me/pane-focus` and `PUT /api/tasks/{taskId}/summary-read`, listed in `automaticRequests` in `internal/server/server.go`), and every binary message on a terminal WebSocket, which includes the replies SwiftTerm sends by itself to queries in the output (cursor position and the like). The `identify` middleware and `terminalInput` record it in the client registry's memory (`Registry.Acted` in `internal/clients`), so it is absent until that client's first action after byre starts. `sync` is the app's latest iCloud sync result, from `PUT /api/clients/sync` (`Registry.Synced`; an automatic request, so not an action, and broadcasting `admin.clients`), absent until the app reports one; the Status tab's Clients table shows it as `ok` or `failing` in the danger colour, the error and time in its tooltip. The registry keeps each client by `id` across command-channel drops, so a Mac that sleeps and reconnects keeps its `last_action_at`, and forgets a client 24 hours after it was last connected or acted, since an app that quit never comes back under that `id`. An action at least a second after the last one from that client that broadcast, every `GET /api/events` stream opening or closing, and every command channel connecting or disconnecting broadcasts an unstored `admin.clients` event (`clientsChanged`). And server counts (start time, agent processes, projects, active tasks, SSE clients, database size including WAL, last event seq). **Usage.** Each vendor's entry in `rate_limits` is a `Usage` (`api/openapi.yaml`): `updated_at`, an optional `rejected` (why the vendor is refusing work, such as Claude's `rateLimitType` when its status is `rejected` or Codex's `rateLimitReachedType`, which covers depleted credits and caps no window shows) and `windows`, each with `label`, `used`, an optional `limit`, `unit` (`usd`, `tokens`, `requests` or `percent`), and optional `resets_at` and `window_seconds`. It lives in memory only (`Manager.rateLimits`) and comes from one of two places, both in `internal/agents/usage.go`: @@ -377,7 +377,7 @@ Task agents use the Macs the human has byre's app open on directly, with no appr Implementer sessions get nine MCP tools for it (`internal/agents/clients.go`). The app sets its WebSocket's `maximumMessageSize` to 256 MiB, the same as byre's read limit, so file contents fit in one message each way. -- `list_clients`: the status's `clients` (see "Status"), with each one's host, login, `id`, connection time, app commit and `last_action_at`; the most recently active is listed first and marked. +- `list_clients`: the status's `clients` (see "Status"), with each one's host, login, `id`, connection time, app commit and `last_action_at`, and its iCloud sync error when its `sync` failed; the most recently active is listed first and marked. - `client_bash`: `op: bash`. The app runs `/bin/zsh -l -c ` in the human's home directory through `Process`, which starts it in its own process group, stdin from `/dev/null` and stdout and stderr into one temporary file, and kills the group with `SIGKILL` after `timeout_ms` (default 120000, at most 600000), replying `exit_code` -1. Otherwise `exit_code` is the exit status, or 128 plus the signal. `output` is the file's last 64 KiB. The agent's call waits the timeout plus 30s for the reply. Background children the command leaves are not waited for or killed, and a command that quits the app keeps running. - `client_upload`: `op: upload`. `path` is checked against the worktree through an `os.Root`; byre registers a transfer naming the path's directory and base name under a random id for as long as the call waits (at most 10 minutes), and the app downloads `GET /api/clients/transfers/{transferId}`, which streams `tar -czf - -C -- ` (404 once the call has ended). The app unpacks it into a new `.byre-upload-` directory beside `dest` (`~/` expanded, parent directories created), removes whatever is at `dest`, moves the archive's one entry there and removes the temporary directory. - `client_open`: a `client_upload` of `path` to `~/Library/Caches/byre/open//`, then an `op: bash` running `open` on it, so the file opens in that Mac's default app for its type. The folder is left in place for the human to keep or for macOS to clear. diff --git a/internal/agents/clients.go b/internal/agents/clients.go index bd80c2b..cab80e7 100644 --- a/internal/agents/clients.go +++ b/internal/agents/clients.go @@ -210,6 +210,9 @@ func (m *Manager) listClients(ctx context.Context) (string, error) { fmt.Fprintf(b, "last action %s ago", now.Sub(*r.LastActionAt).Round(time.Second)) } fmt.Fprintf(b, ", connected %s ago, app built from %s", now.Sub(r.ConnectedAt).Round(time.Second), r.Commit) + if r.Sync != nil && !r.Sync.Ok { + fmt.Fprintf(b, ", iCloud sync failing since %s ago: %s", now.Sub(r.Sync.At).Round(time.Second), *r.Sync.Error) + } if i == 0 && r.LastActionAt != nil { b.WriteString(" (most recently active)") } diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 6e0f1ac..d16924d 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -769,6 +769,18 @@ type ClientRequest struct { // ClientRequestOp defines model for ClientRequest.Op. type ClientRequestOp string +// ClientSync The app's latest finished iCloud sync operation; absent from a client until it reports one. +type ClientSync struct { + // At When it finished. + At time.Time `json:"at"` + + // Error Why it failed; absent when ok. + Error *string `json:"error,omitempty"` + + // Ok Whether that operation succeeded. + Ok bool `json:"ok"` +} + // CompactCreate defines model for CompactCreate. type CompactCreate struct { // Model Model for the new session, on either vendor. The old session still writes the handoff. Defaults to the old session's model. @@ -792,6 +804,9 @@ type ConnectedClient struct { // LastActionAt When byre last received a request the human made from this client since byre started; absent when none. LastActionAt *time.Time `json:"last_action_at,omitempty"` Login string `json:"login"` + + // Sync The app's latest finished iCloud sync operation; absent from a client until it reports one. + Sync *ClientSync `json:"sync,omitempty"` } // DefaultModel defines model for DefaultModel. @@ -1680,6 +1695,9 @@ type GetTaskTerminalParams struct { // UpdateUsageCommandsJSONRequestBody defines body for UpdateUsageCommands for application/json ContentType. type UpdateUsageCommandsJSONRequestBody = UsageCommands +// SetClientSyncJSONRequestBody defines body for SetClientSync for application/json ContentType. +type SetClientSyncJSONRequestBody = ClientSync + // AnswerAgainJSONRequestBody defines body for AnswerAgain for application/json ContentType. type AnswerAgainJSONRequestBody = InboxResponseCreate @@ -1764,6 +1782,9 @@ type ServerInterface interface { // (GET /api/clients/connect) ConnectClient(w http.ResponseWriter, r *http.Request) + // (PUT /api/clients/sync) + SetClientSync(w http.ResponseWriter, r *http.Request) + // (GET /api/clients/transfers/{transferId}) GetClientTransfer(w http.ResponseWriter, r *http.Request, transferId string) @@ -2079,6 +2100,20 @@ func (siw *ServerInterfaceWrapper) ConnectClient(w http.ResponseWriter, r *http. handler.ServeHTTP(w, r) } +// SetClientSync operation middleware +func (siw *ServerInterfaceWrapper) SetClientSync(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.SetClientSync(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetClientTransfer operation middleware func (siw *ServerInterfaceWrapper) GetClientTransfer(w http.ResponseWriter, r *http.Request) { @@ -4069,6 +4104,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/inbox/{itemId}", wrapper.GetInboxItem) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/inbox/{itemId}/dismiss", wrapper.DismissInbox) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/clients/connect", wrapper.ConnectClient) + m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/clients/sync", wrapper.SetClientSync) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/clients/transfers/{transferId}", wrapper.GetClientTransfer) m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/clients/transfers/{transferId}", wrapper.PutClientTransfer) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/inbox/responses", wrapper.RespondInbox) @@ -4091,287 +4127,290 @@ 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/clients/clients.go b/internal/clients/clients.go index 927031f..d67d19a 100644 --- a/internal/clients/clients.go +++ b/internal/clients/clients.go @@ -32,6 +32,7 @@ type Client struct { Commit string ConnectedAt time.Time LastActionAt *time.Time + Sync *api.ClientSync } type Transfer struct { @@ -99,6 +100,12 @@ func (r *Registry) Acted(id, login, host, commit string) bool { return true } +func (r *Registry) Synced(id, login, host, commit string, sync api.ClientSync) { + r.mu.Lock() + defer r.mu.Unlock() + r.entry(id, login, host, commit, time.Now()).Sync = &sync +} + func (r *Registry) Serve(ctx context.Context, ws *websocket.Conn, id, login, host, commit string) { ws.SetReadLimit(readLimit) c := &conn{ws: ws, done: make(chan struct{}), pending: map[string]chan api.ClientReply{}} diff --git a/internal/server/clients.go b/internal/server/clients.go index a4d2736..f2b0e37 100644 --- a/internal/server/clients.go +++ b/internal/server/clients.go @@ -2,6 +2,7 @@ package server import ( "bytes" + "encoding/json" "errors" "fmt" "io" @@ -12,6 +13,8 @@ import ( "path/filepath" "github.com/coder/websocket" + + "g.fc.run/ai/byre/internal/api" ) func (s *Server) ConnectClient(w http.ResponseWriter, r *http.Request) { @@ -32,6 +35,31 @@ func (s *Server) ConnectClient(w http.ResponseWriter, r *http.Request) { s.clients.Serve(r.Context(), ws, id, actor(r), host, r.Header.Get("Byre-Commit")) } +func (s *Server) SetClientSync(w http.ResponseWriter, r *http.Request) { + id := r.Header.Get("Byre-Client") + host := r.Header.Get("Byre-Host") + if id == "" || host == "" { + writeError(w, http.StatusBadRequest, errors.New("missing Byre-Client or Byre-Host")) + return + } + sync := api.ClientSync{} + err := json.NewDecoder(r.Body).Decode(&sync) + if err != nil { + writeError(w, http.StatusBadRequest, err) + return + } + if !sync.Ok { + if sync.Error == nil { + writeError(w, http.StatusBadRequest, errors.New("a failed sync needs an error")) + return + } + slog.Warn("client icloud sync failing", "id", id, "host", host, "error", *sync.Error) + } + s.clients.Synced(id, actor(r), host, r.Header.Get("Byre-Commit"), sync) + s.clientsChanged() + w.WriteHeader(http.StatusNoContent) +} + func (s *Server) GetClientTransfer(w http.ResponseWriter, r *http.Request, transferID string) { t, ok := s.clients.Transfer(transferID) if !ok || t.Receive { diff --git a/internal/server/server.go b/internal/server/server.go index 607cd51..f592624 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -165,6 +165,7 @@ func (s *Server) connectedClients() []api.ConnectedClient { Commit: c.Commit, ConnectedAt: c.ConnectedAt, LastActionAt: c.LastActionAt, + Sync: c.Sync, }) } return out @@ -176,6 +177,7 @@ func (s *Server) clientsChanged() { var automaticRequests = map[string]bool{ "PUT /api/me/pane-focus": true, + "PUT /api/clients/sync": true, "PUT /api/tasks/{taskId}/summary-read": true, } diff --git a/mac/Sources/Byre/Remote.swift b/mac/Sources/Byre/Remote.swift index 0d06019..ec62722 100644 --- a/mac/Sources/Byre/Remote.swift +++ b/mac/Sources/Byre/Remote.swift @@ -5,6 +5,7 @@ import OpenAPIRuntime typealias ClientRequest = Components.Schemas.ClientRequest typealias ClientReply = Components.Schemas.ClientReply +typealias ClientSync = Components.Schemas.ClientSync private let retryAfter = Duration.seconds(2) private let checkEvery = Duration.seconds(10) @@ -48,6 +49,15 @@ final class Remote { heard = Date() task.resume() receive(task) + if let sync = Servers.shared.sync { + Task { + do { + _ = try await api.client.setClientSync(body: .json(sync)).noContent + } catch { + log.error("report icloud sync: \(errorText(error), privacy: .public)") + } + } + } if checker == nil { checker = Task { [weak self] in while !Task.isCancelled { diff --git a/mac/Sources/Byre/Settings.swift b/mac/Sources/Byre/Settings.swift index 0fc23f2..2916b23 100644 --- a/mac/Sources/Byre/Settings.swift +++ b/mac/Sources/Byre/Settings.swift @@ -49,6 +49,8 @@ struct SettingsView: View { try await store.api.client.updateReviewModels(body: .json(.init(reviewModel: me.reviewModel, secondReviewModel: m))).ok.body.json } } + heading("iCloud sync") + SyncState() heading("This Mac's permissions") Text("The macOS privacy grants byre's app needs for agents to see and control this Mac. Checking asks macOS for any that are missing.") .foregroundStyle(Palette.muted) @@ -97,6 +99,26 @@ private struct ServerColorPicker: View { } } +private struct SyncState: View { + var body: some View { + TimelineView(.periodic(from: .now, by: 30)) { _ in + if let sync = Servers.shared.sync { + if sync.ok { + Text("This Mac shares its servers, window colours, drafts and task visits through iCloud. Last synced \(ago(sync.at)).") + .fixedSize(horizontal: false, vertical: true) + } else { + Text("iCloud sync is failing on this Mac, so changes made here aren't reaching your other Macs. Failed \(ago(sync.at)): \(sync.error ?? "")") + .foregroundStyle(Palette.danger) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + } + } else { + Text("No iCloud sync has finished since byre started.").foregroundStyle(Palette.muted) + } + } + } +} + private struct DeviceNotifications: View { @ViewState private var status: UNAuthorizationStatus? @ViewState private var asking = false diff --git a/mac/Sources/Byre/Startup.swift b/mac/Sources/Byre/Startup.swift index eddea7f..79512e3 100644 --- a/mac/Sources/Byre/Startup.swift +++ b/mac/Sources/Byre/Startup.swift @@ -1,3 +1,5 @@ +import ByreAPI +import CloudKit import CoreData import Foundation import Observation @@ -11,6 +13,7 @@ final class Servers { private(set) var entries: [(origin: String, record: ServerRecord)] = [] private(set) var connections: [String: Connection] = [:] + private var syncs: [NSPersistentCloudKitContainer.EventType: ClientSync] = [:] var current: String? @ObservationIgnored var openWindow: (() -> Void)? @@ -19,7 +22,11 @@ final class Servers { current = origins.first Task { for await note in NotificationCenter.default.notifications(named: NSPersistentCloudKitContainer.eventChangedNotification) { - if let event = note.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event, event.type == .import, event.endDate != nil { + guard let event = note.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event, let end = event.endDate else { + continue + } + synced(event, end) + if event.type == .import { for connection in connections.values { connection.store?.drafts?.reload() } @@ -29,6 +36,26 @@ final class Servers { } } + private func synced(_ event: NSPersistentCloudKitContainer.Event, _ end: Date) { + let error = event.succeeded ? nil : syncError(event.error) + if let error { + log.error("icloud \(String(describing: event.type), privacy: .public) failed: \(error, privacy: .public)") + } + syncs[event.type] = ClientSync(ok: event.succeeded, error: error, at: end) + guard let sync else { + return + } + for connection in connections.values { + connection.store?.reportSync(sync) + } + } + + // A failed export stays the state until an export succeeds, whatever imports do meanwhile. + var sync: ClientSync? { + let failed = syncs.values.filter { !$0.ok } + return (failed.isEmpty ? Array(syncs.values) : failed).max { $0.at < $1.at } + } + var origins: [String] { entries.map(\.origin) } @@ -103,6 +130,17 @@ final class Servers { } } +private func syncError(_ error: (any Error)?) -> String { + guard let error else { + return "failed with no error" + } + let ns = error as NSError + if let partial = ns.userInfo[CKPartialErrorsByItemIDKey] as? [AnyHashable: any Error], let first = partial.values.first { + return "\(first.localizedDescription) (\(partial.count) records)" + } + return "\(ns.localizedDescription) (\(ns.domain) \(ns.code))" +} + func serverName(_ origin: String) -> String { let host = URL(string: origin)?.host() ?? origin return host.split(separator: ".").first.map(String.init) ?? host diff --git a/mac/Sources/Byre/Status.swift b/mac/Sources/Byre/Status.swift index 3b62be6..b416b1c 100644 --- a/mac/Sources/Byre/Status.swift +++ b/mac/Sources/Byre/Status.swift @@ -382,7 +382,7 @@ private struct ClientTable: View { } else { Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 2) { GridRow { - ForEach(["Mac", "User", "Build", "Connected", "Last action"], id: \.self) { h in + ForEach(["Mac", "User", "Build", "Connected", "Last action", "iCloud"], id: \.self) { h in Text(h).font(Fonts.font(11)).foregroundStyle(Palette.muted) } } @@ -394,6 +394,7 @@ private struct ClientTable: View { Text(c.commit.isEmpty ? "—" : String(c.commit.prefix(8))).help(c.commit) Text(since(c.connectedAt)) Text(c.lastActionAt.map(since) ?? "—") + SyncCell(sync: c.sync) } .textSelection(.enabled) Divider().overlay(Palette.border) @@ -403,6 +404,24 @@ private struct ClientTable: View { } } +private struct SyncCell: View { + let sync: ClientSync? + + var body: some View { + if let sync { + if sync.ok { + Text("ok").help("Last synced \(since(sync.at))") + } else { + Text("failing") + .foregroundStyle(Palette.danger) + .help("Failed \(since(sync.at)): \(sync.error ?? "")") + } + } else { + Text("—") + } + } +} + private struct ServerRows: View { let status: ServerStatus diff --git a/mac/Sources/Byre/Store.swift b/mac/Sources/Byre/Store.swift index 405c15d..fc0444c 100644 --- a/mac/Sources/Byre/Store.swift +++ b/mac/Sources/Byre/Store.swift @@ -531,6 +531,12 @@ final class Store { } } + func reportSync(_ sync: ClientSync) { + spawn("report icloud sync") { + _ = try await self.api.client.setClientSync(body: .json(sync)).noContent + } + } + func selectTask(_ taskId: String?) { markVisited(selectedTaskId) markVisited(taskId) diff --git a/mac/package.sh b/mac/package.sh index bbf7ffb..5ab892f 100755 --- a/mac/package.sh +++ b/mac/package.sh @@ -5,6 +5,22 @@ sparkle_version=2.10.0 sparkle=$HOME/Library/Caches/byre/sparkle-$sparkle_version identity="Developer ID Application" +fields=$(mktemp -d) +team=$(security cms -D -i "$mac/byre.provisionprofile" | plutil -extract TeamIdentifier.0 raw -o - -) +xcrun cktool export-schema --team-id "$team" --container-id iCloud.run.fc.byre.mac --environment production --output-file "$fields/schema" +awk '$1 == "RECORD" && $2 == "TYPE" { type = $3; next } type != "" && $1 ~ /^CD_/ { print type "." $1 } /\);/ { type = "" }' "$fields/schema" | + sort > "$fields/production" +awk '/^@Model/ { model = 1; next } model && /^final class/ { type = $3; next } model && /^ var / { print "CD_" type ".CD_" $2 } /^}/ { model = 0 }' "$mac/Sources/Byre/Persistence.swift" | + sort > "$fields/models" +missing=$(comm -23 "$fields/models" "$fields/production") +rm -rf "$fields" +if [ -n "$missing" ]; then + echo "CloudKit's Production schema lacks these SwiftData fields, so iCloud would reject them:" >&2 + echo "$missing" >&2 + echo "Add them to Development and deploy them (docs/operations.md, \"Storage\")." >&2 + exit 1 +fi + if [ ! -x "$sparkle/bin/sign_update" ]; then mkdir -p "$sparkle" curl -fsSL "https://github.com/sparkle-project/Sparkle/releases/download/$sparkle_version/Sparkle-$sparkle_version.tar.xz" | tar -xJ -C "$sparkle"