macOS: split AppMain into focused modules
This commit is contained in:
72
apps/macos/Sources/Clawdis/XPCService.swift
Normal file
72
apps/macos/Sources/Clawdis/XPCService.swift
Normal file
@@ -0,0 +1,72 @@
|
||||
import Foundation
|
||||
import OSLog
|
||||
import ClawdisIPC
|
||||
|
||||
@objc protocol ClawdisXPCProtocol {
|
||||
func handle(_ data: Data, withReply reply: @escaping @Sendable (Data?, Error?) -> Void)
|
||||
}
|
||||
|
||||
final class ClawdisXPCService: NSObject, ClawdisXPCProtocol {
|
||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "xpc")
|
||||
|
||||
func handle(_ data: Data, withReply reply: @escaping @Sendable (Data?, Error?) -> Void) {
|
||||
let logger = logger
|
||||
Task.detached(priority: nil) { @Sendable in
|
||||
do {
|
||||
let request = try JSONDecoder().decode(Request.self, from: data)
|
||||
let response = try await Self.process(request: request, notifier: NotificationManager(), logger: logger)
|
||||
let encoded = try JSONEncoder().encode(response)
|
||||
reply(encoded, nil)
|
||||
} catch {
|
||||
logger.error("Failed to handle XPC request: \(error.localizedDescription, privacy: .public)")
|
||||
let resp = Response(ok: false, message: "decode/handle error: \(error.localizedDescription)")
|
||||
reply(try? JSONEncoder().encode(resp), error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func process(
|
||||
request: Request,
|
||||
notifier: NotificationManager,
|
||||
logger: Logger) async throws -> Response
|
||||
{
|
||||
let paused = await MainActor.run { AppStateStore.isPausedFlag }
|
||||
if paused {
|
||||
return Response(ok: false, message: "clawdis paused")
|
||||
}
|
||||
|
||||
switch request {
|
||||
case let .notify(title, body, sound):
|
||||
let chosenSound: String = if let sound { sound } else { await MainActor.run { AppStateStore.defaultSound } }
|
||||
let ok = await notifier.send(title: title, body: body, sound: chosenSound)
|
||||
return ok ? Response(ok: true) : Response(ok: false, message: "notification not authorized")
|
||||
|
||||
case let .ensurePermissions(caps, interactive):
|
||||
let statuses = await PermissionManager.ensure(caps, interactive: interactive)
|
||||
let missing = statuses.filter { !$0.value }.map(\.key.rawValue)
|
||||
let ok = missing.isEmpty
|
||||
let msg = ok ? "all granted" : "missing: \(missing.joined(separator: ","))"
|
||||
return Response(ok: ok, message: msg)
|
||||
|
||||
case .status:
|
||||
return Response(ok: true, message: "ready")
|
||||
|
||||
case let .screenshot(displayID, windowID, _):
|
||||
let authorized = await PermissionManager
|
||||
.ensure([.screenRecording], interactive: false)[.screenRecording] ?? false
|
||||
guard authorized else { return Response(ok: false, message: "screen recording permission missing") }
|
||||
if let data = await Screenshotter.capture(displayID: displayID, windowID: windowID) {
|
||||
return Response(ok: true, payload: data)
|
||||
}
|
||||
return Response(ok: false, message: "screenshot failed")
|
||||
|
||||
case let .runShell(command, cwd, env, timeoutSec, needsSR):
|
||||
if needsSR {
|
||||
let authorized = await PermissionManager
|
||||
.ensure([.screenRecording], interactive: false)[.screenRecording] ?? false
|
||||
guard authorized else { return Response(ok: false, message: "screen recording permission missing") }
|
||||
}
|
||||
return await ShellRunner.run(command: command, cwd: cwd, env: env, timeout: timeoutSec)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user