Let agents show and drive Page view tabs on the iPhone

This commit is contained in:
Ian Gulliver
2026-10-21 22:31:48 -07:00
parent 0efdc8651f
commit caf0a830a9
11 changed files with 48 additions and 23 deletions
+1
View File
@@ -118,6 +118,7 @@ final class API: Sendable {
config.httpAdditionalHeaders = [
"Byre-Client": clientID,
"Byre-Commit": Bundle.main.object(forInfoDictionaryKey: "ByreCommit") as! String,
"Byre-Device": deviceKind,
"Byre-Host": deviceName,
]
config.proxyConfigurations = [tailnetNode.proxy()]
+11
View File
@@ -278,6 +278,17 @@ final class Pages {
}
return png
}
#else
func capture(_ view: WKWebView) async throws -> Data {
guard view.window != nil, !view.isHidden else {
throw ApiError(status: 0, message: "the tab isn't on screen: the human has the page view closed or another tab showing")
}
let image = try await view.takeSnapshot(configuration: nil)
guard let png = image.pngData() else {
throw ApiError(status: 0, message: "couldn't encode the capture as PNG")
}
return png
}
#endif
func close() {
+12 -2
View File
@@ -7,12 +7,13 @@ typealias ClientRequest = Components.Schemas.ClientRequest
typealias ClientReply = Components.Schemas.ClientReply
typealias ClientSync = Components.Schemas.ClientSync
#if os(macOS)
private let retryAfter = Duration.seconds(2)
private let checkEvery = Duration.seconds(10)
private let staleAfter: TimeInterval = 25
private let maxOutput = 64 << 10
private let maxMessage = 256 << 20
#if os(macOS)
private let maxOutput = 64 << 10
private let maxRead = 64 << 20
private let copyTimeoutMs = 10 * 60 * 1000
private let hands = Hands()
@@ -27,6 +28,7 @@ private let signScript = """
esac
exec "$program" --status-fd=2 -bsau "$(git config user.signingkey || echo "$1")" >"$2"
"""
#endif
@MainActor
final class Remote {
@@ -157,6 +159,10 @@ final class Remote {
private func perform(_ req: ClientRequest) async throws -> ClientReply {
switch req.op {
#if os(iOS)
case .bash, .exec, .upload, .download, .readFile, .writeFile, .hands, .sign:
throw ApiError(status: 0, message: "an iPhone only shows and drives pages and files in byre's app (show_page, show_file, drive_page)")
#else
case .bash:
let home = FileManager.default.homeDirectoryForCurrentUser.path
let (code, output) = try await spawn(["/bin/zsh", "-l", "-c", req.command ?? ""], in: home, timeoutMs: req.timeoutMs ?? 120_000)
@@ -190,6 +196,7 @@ final class Remote {
case .sign:
let (code, status, signature) = try await sign(Data(req.data?.data ?? []), as: req.ident ?? "", timeoutMs: req.timeoutMs ?? 120_000)
return ClientReply(id: req.id, exitCode: code, output: status, data: Base64EncodedData(signature))
#endif
case .openPage:
if let address = req.url {
guard let url = URL(string: address) else {
@@ -219,6 +226,7 @@ final class Remote {
}
}
#if os(macOS)
private func sign(_ payload: Data, as ident: String, timeoutMs: Int) async throws -> (Int, String, Data) {
let fm = FileManager.default
let dir = fm.temporaryDirectory.appending(path: "byre-sign-\(UUID().uuidString)", directoryHint: .isDirectory)
@@ -293,8 +301,10 @@ final class Remote {
}
try fm.moveItem(at: entries[0], to: target)
}
#endif
}
#if os(macOS)
private func expand(_ path: String) -> URL {
URL(filePath: (path as NSString).expandingTildeInPath)
}
-6
View File
@@ -254,13 +254,7 @@ final class Connection {
store.startGlobalStream(after: status.lastEventSeq) { [weak self] in
self?.reconnected(status.startedAt)
}
#if os(macOS)
store.remote.start()
#else
if let sync = Servers.shared.sync {
store.reportSync(sync)
}
#endif
forwards.sync(store.api, Set(store.activeTasks().map(\.task.id)))
Notifier.shared.deliver()
return true
-6
View File
@@ -89,9 +89,7 @@ final class Store {
let imageCache = ImageCache()
let events = Listeners<ByreEvent>()
let paneFocus: PaneFocus
#if os(macOS)
let remote: Remote
#endif
let forwards: Forwards
let pages: Pages
@@ -115,10 +113,8 @@ final class Store {
self.pages = pages
leadFeed = Feed(shown: { true })
paneFocus = PaneFocus(api: api)
#if os(macOS)
remote = Remote(api: api)
remote.store = self
#endif
pages.popup = { [weak self] page in
self?.openPage(page)
}
@@ -144,9 +140,7 @@ final class Store {
stopped = true
globalStream?.stop()
leadStream?.stop()
#if os(macOS)
remote.stop()
#endif
drafts?.flush()
await paneFocus.leave()
}