This commit is contained in:
Max Goedjen 2025-09-06 18:32:19 -07:00
parent be28d59873
commit e966203312
No known key found for this signature in database
4 changed files with 66 additions and 73 deletions

View File

@ -21,13 +21,16 @@ let package = Package(
targets: ["SmartCardSecretKit"]),
.library(
name: "SecretAgentKit",
targets: ["SecretAgentKit"]),
targets: ["SecretAgentKit", "XPCWrappers"]),
.library(
name: "SecretAgentKitHeaders",
targets: ["SecretAgentKitHeaders"]),
.library(
name: "Brief",
targets: ["Brief"]),
.library(
name: "XPCWrappers",
targets: ["XPCWrappers"]),
],
dependencies: [
],
@ -70,7 +73,7 @@ let package = Package(
),
.target(
name: "Brief",
dependencies: [],
dependencies: ["XPCWrappers"],
resources: [localization],
swiftSettings: swiftSettings,
),
@ -78,6 +81,10 @@ let package = Package(
name: "BriefTests",
dependencies: ["Brief"],
),
.target(
name: "XPCWrappers",
swiftSettings: swiftSettings,
),
]
)

View File

@ -1,5 +1,6 @@
import Foundation
import Observation
import XPCWrappers
/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
@Observable public final class Updater: UpdaterProtocol, Sendable {
@ -46,14 +47,9 @@ import Observation
/// Manually trigger an update check.
public func checkForUpdates() async throws {
let session: XPCSession
if #available(macOS 26.0, *) {
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.ReleasesDownloader", requirement: .isFromSameTeam())
} else {
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.ReleasesDownloader")
}
let session = try XPCTypedSession<[Release], Never>(serviceName: "com.maxgoedjen.Secretive.ReleasesDownloader")
await evaluate(releases: try await session.send())
session.cancel(reason: "Done")
session.complete()
}
@ -103,33 +99,3 @@ extension Updater {
}
}
private extension XPCSession {
func send<Response: Decodable & Sendable>(_ message: some Encodable = XPCSession.emptyMessage) async throws -> Response {
try await withCheckedThrowingContinuation { continuation in
do {
try send(message) { result in
switch result {
case .success(let message):
do {
let decoded = try message.decode(as: Response.self)
continuation.resume(returning: decoded)
} catch {
continuation.resume(throwing: error)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
} catch {
continuation.resume(throwing: error)
}
}
}
static var emptyMessage: some Encodable {
Data()
}
}

View File

@ -0,0 +1,49 @@
import Foundation
public struct XPCTypedSession<ResponseType: Codable & Sendable, ErrorType: Error & Codable>: Sendable {
private let session: XPCSession
public init(serviceName: String, warmup: Bool = false) throws {
if #available(macOS 26.0, *) {
session = try XPCSession(xpcService: serviceName, requirement: .isFromSameTeam())
} else {
session = try XPCSession(xpcService: serviceName)
}
if warmup {
Task { [self] in
_ = try? await send()
}
}
}
public func send(_ message: some Encodable = Data()) async throws -> ResponseType {
try await withCheckedThrowingContinuation { continuation in
do {
try session.send(message) { result in
switch result {
case .success(let message):
if let result = try? message.decode(as: ResponseType.self) {
continuation.resume(returning: result)
} else if let error = try? message.decode(as: ErrorType.self) {
continuation.resume(throwing: error)
} else {
continuation.resume(throwing: UnknownMessageError())
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
} catch {
continuation.resume(throwing: error)
}
}
}
public func complete() {
session.cancel(reason: "Done")
}
}
public struct UnknownMessageError: Error, Codable {}

View File

@ -1,52 +1,23 @@
import Foundation
import SecretAgentKit
import Brief
import XPCWrappers
/// Delegates all agent input parsing to an XPC service which wraps OpenSSH
public final class XPCAgentInputParser: SSHAgentInputParserProtocol {
private let session: XPCSession
private let queue = DispatchQueue(label: "com.maxgoedjen.Secretive.AgentRequestParser", qos: .userInteractive)
private let session: XPCTypedSession<SSHAgent.Request, SSHAgentInputParser.AgentParsingError>
public init() throws {
if #available(macOS 26.0, *) {
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue, requirement: .isFromSameTeam())
} else {
session = try XPCSession(xpcService: "com.maxgoedjen.Secretive.AgentRequestParser", targetQueue: queue)
}
Task {
// Warm up the XPC endpoint.
_ = try? await parse(data: Data())
}
session = try XPCTypedSession(serviceName: "com.maxgoedjen.Secretive.AgentRequestParser", warmup: true)
}
public func parse(data: Data) async throws -> SSHAgent.Request {
try await withCheckedThrowingContinuation { continuation in
do {
try session.send(data) { result in
switch result {
case .success(let result):
if let result = try? result.decode(as: SSHAgent.Request.self) {
continuation.resume(returning: result)
} else if let error = try? result.decode(as: SSHAgentInputParser.AgentParsingError.self) {
continuation.resume(throwing: error)
} else {
continuation.resume(throwing: UnknownMessageError())
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
} catch {
continuation.resume(throwing: error)
}
}
try await session.send(data)
}
deinit {
session.cancel(reason: "Done")
session.complete()
}
struct UnknownMessageError: Error {}
}