124 lines
4.7 KiB
Swift
124 lines
4.7 KiB
Swift
import Foundation
|
|
|
|
// MARK: - Capabilities
|
|
|
|
public enum Capability: String, Codable, CaseIterable, Sendable {
|
|
case notifications
|
|
case accessibility
|
|
case screenRecording
|
|
case microphone
|
|
case speechRecognition
|
|
}
|
|
|
|
// MARK: - Requests
|
|
|
|
public enum Request: Sendable {
|
|
case notify(title: String, body: String, sound: String?)
|
|
case ensurePermissions([Capability], interactive: Bool)
|
|
case screenshot(displayID: UInt32?, windowID: UInt32?, format: String)
|
|
case runShell(command: [String], cwd: String?, env: [String: String]?, timeoutSec: Double?, needsScreenRecording: Bool)
|
|
case status
|
|
}
|
|
|
|
// MARK: - Responses
|
|
|
|
public struct Response: Codable, Sendable {
|
|
public var ok: Bool
|
|
public var message: String?
|
|
/// Optional payload (PNG bytes, stdout text, etc.).
|
|
public var payload: Data?
|
|
|
|
public init(ok: Bool, message: String? = nil, payload: Data? = nil) {
|
|
self.ok = ok
|
|
self.message = message
|
|
self.payload = payload
|
|
}
|
|
}
|
|
|
|
// MARK: - Codable conformance for Request
|
|
|
|
extension Request: Codable {
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
case title, body, sound
|
|
case caps, interactive
|
|
case displayID, windowID, format
|
|
case command, cwd, env, timeoutSec, needsScreenRecording
|
|
}
|
|
|
|
private enum Kind: String, Codable {
|
|
case notify
|
|
case ensurePermissions
|
|
case screenshot
|
|
case runShell
|
|
case status
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
switch self {
|
|
case let .notify(title, body, sound):
|
|
try container.encode(Kind.notify, forKey: .type)
|
|
try container.encode(title, forKey: .title)
|
|
try container.encode(body, forKey: .body)
|
|
try container.encodeIfPresent(sound, forKey: .sound)
|
|
|
|
case let .ensurePermissions(caps, interactive):
|
|
try container.encode(Kind.ensurePermissions, forKey: .type)
|
|
try container.encode(caps, forKey: .caps)
|
|
try container.encode(interactive, forKey: .interactive)
|
|
|
|
case let .screenshot(displayID, windowID, format):
|
|
try container.encode(Kind.screenshot, forKey: .type)
|
|
try container.encodeIfPresent(displayID, forKey: .displayID)
|
|
try container.encodeIfPresent(windowID, forKey: .windowID)
|
|
try container.encode(format, forKey: .format)
|
|
|
|
case let .runShell(command, cwd, env, timeoutSec, needsSR):
|
|
try container.encode(Kind.runShell, forKey: .type)
|
|
try container.encode(command, forKey: .command)
|
|
try container.encodeIfPresent(cwd, forKey: .cwd)
|
|
try container.encodeIfPresent(env, forKey: .env)
|
|
try container.encodeIfPresent(timeoutSec, forKey: .timeoutSec)
|
|
try container.encode(needsSR, forKey: .needsScreenRecording)
|
|
|
|
case .status:
|
|
try container.encode(Kind.status, forKey: .type)
|
|
}
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
let kind = try container.decode(Kind.self, forKey: .type)
|
|
switch kind {
|
|
case .notify:
|
|
let title = try container.decode(String.self, forKey: .title)
|
|
let body = try container.decode(String.self, forKey: .body)
|
|
let sound = try container.decodeIfPresent(String.self, forKey: .sound)
|
|
self = .notify(title: title, body: body, sound: sound)
|
|
|
|
case .ensurePermissions:
|
|
let caps = try container.decode([Capability].self, forKey: .caps)
|
|
let interactive = try container.decode(Bool.self, forKey: .interactive)
|
|
self = .ensurePermissions(caps, interactive: interactive)
|
|
|
|
case .screenshot:
|
|
let displayID = try container.decodeIfPresent(UInt32.self, forKey: .displayID)
|
|
let windowID = try container.decodeIfPresent(UInt32.self, forKey: .windowID)
|
|
let format = try container.decode(String.self, forKey: .format)
|
|
self = .screenshot(displayID: displayID, windowID: windowID, format: format)
|
|
|
|
case .runShell:
|
|
let command = try container.decode([String].self, forKey: .command)
|
|
let cwd = try container.decodeIfPresent(String.self, forKey: .cwd)
|
|
let env = try container.decodeIfPresent([String: String].self, forKey: .env)
|
|
let timeout = try container.decodeIfPresent(Double.self, forKey: .timeoutSec)
|
|
let needsSR = try container.decode(Bool.self, forKey: .needsScreenRecording)
|
|
self = .runShell(command: command, cwd: cwd, env: env, timeoutSec: timeout, needsScreenRecording: needsSR)
|
|
|
|
case .status:
|
|
self = .status
|
|
}
|
|
}
|
|
}
|