mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-03-10 19:47:24 +01:00
XPC scaffolding
This commit is contained in:
@@ -15,6 +15,8 @@ struct Secretive: App {
|
||||
return list
|
||||
}()
|
||||
private let agentStatusChecker = AgentStatusChecker()
|
||||
private let agentLaunchController = AgentLaunchController()
|
||||
private let agentCommunicationController = AgentCommunicationController()
|
||||
private let justUpdatedChecker = JustUpdatedChecker()
|
||||
|
||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||
@@ -27,6 +29,7 @@ struct Secretive: App {
|
||||
.environmentObject(storeList)
|
||||
.environmentObject(Updater(checkOnLaunch: hasRunSetup))
|
||||
.environmentObject(agentStatusChecker)
|
||||
.environmentObject(agentCommunicationController)
|
||||
.onAppear {
|
||||
if !hasRunSetup {
|
||||
showingSetup = true
|
||||
@@ -35,11 +38,17 @@ struct Secretive: App {
|
||||
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
||||
guard hasRunSetup else { return }
|
||||
agentStatusChecker.check()
|
||||
if agentStatusChecker.running && justUpdatedChecker.justUpdated {
|
||||
// Relaunch the agent, since it'll be running from earlier update still
|
||||
reinstallAgent()
|
||||
} else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild {
|
||||
forceLaunchAgent()
|
||||
if justUpdatedChecker.justUpdated || !agentStatusChecker.running {
|
||||
// Two conditions in which we reinstall/attempt a force launch:
|
||||
// 1: The app was just updated, and an old version of the agent is alive. Reinstall will deactivate this and activate a new one.
|
||||
// 2: The agent is not running for some reason. We'll attempt to reinstall it, or relaunch directly if that fails.
|
||||
reinstallAgent {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
agentCommunicationController.configure()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
agentCommunicationController.configure()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +69,13 @@ struct Secretive: App {
|
||||
showingSetup = true
|
||||
}
|
||||
}
|
||||
CommandGroup(after: .help) {
|
||||
Button("TEST") {
|
||||
Task {
|
||||
try await agentCommunicationController.agent.updatedStore(withID: storeList.modifiableStore?.id as? UUID ?? UUID())
|
||||
}
|
||||
}
|
||||
}
|
||||
SidebarCommands()
|
||||
}
|
||||
}
|
||||
@@ -68,27 +84,24 @@ struct Secretive: App {
|
||||
|
||||
extension Secretive {
|
||||
|
||||
private func reinstallAgent() {
|
||||
private func reinstallAgent(completion: @escaping () -> Void) {
|
||||
justUpdatedChecker.check()
|
||||
LaunchAgentController().install {
|
||||
agentLaunchController.install {
|
||||
// Wait a second for launchd to kick in (next runloop isn't enough).
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
agentStatusChecker.check()
|
||||
if !agentStatusChecker.running {
|
||||
forceLaunchAgent()
|
||||
agentLaunchController.forceLaunch { _ in
|
||||
agentStatusChecker.check()
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func forceLaunchAgent() {
|
||||
// We've run setup, we didn't just update, launchd is just not doing it's thing.
|
||||
// Force a launch directly.
|
||||
LaunchAgentController().forceLaunch { _ in
|
||||
agentStatusChecker.check()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import AppKit
|
||||
import SecretKit
|
||||
import SecretAgentKitProtocol
|
||||
|
||||
protocol AgentCommunicationControllerProtocol: ObservableObject {
|
||||
var agent: AgentProtocol { get }
|
||||
}
|
||||
|
||||
class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol {
|
||||
|
||||
let agent: AgentProtocol
|
||||
private let connection: NSXPCConnection
|
||||
private var running = false
|
||||
|
||||
init() {
|
||||
connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID)
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self)
|
||||
connection.invalidationHandler = {
|
||||
print("INVALID")
|
||||
}
|
||||
agent = connection.remoteObjectProxyWithErrorHandler({ x in
|
||||
print(x)
|
||||
}) as! AgentProtocol
|
||||
}
|
||||
|
||||
func configure() {
|
||||
guard !running else { return }
|
||||
running = true
|
||||
connection.resume()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import AppKit
|
||||
import OSLog
|
||||
import SecretKit
|
||||
|
||||
struct LaunchAgentController {
|
||||
struct AgentLaunchController {
|
||||
|
||||
func install(completion: (() -> Void)? = nil) {
|
||||
Logger().debug("Installing agent")
|
||||
@@ -20,7 +20,7 @@ struct LaunchAgentController {
|
||||
}
|
||||
|
||||
func forceLaunch(completion: ((Bool) -> Void)?) {
|
||||
Logger().debug("Agent is not running, attempting to force launch")
|
||||
Logger().debug("Agent is still not running, attempting to force launch")
|
||||
let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
|
||||
let config = NSWorkspace.OpenConfiguration()
|
||||
config.activates = false
|
||||
@@ -9,6 +9,7 @@ protocol JustUpdatedCheckerProtocol: ObservableObject {
|
||||
class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
|
||||
|
||||
@Published var justUpdated: Bool = false
|
||||
var alreadyRelaunchedForDebug = false
|
||||
|
||||
init() {
|
||||
check()
|
||||
@@ -18,7 +19,12 @@ class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
|
||||
let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None"
|
||||
let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
|
||||
UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey)
|
||||
justUpdated = lastBuild != currentBuild
|
||||
if currentBuild != Constants.debugVersionKey {
|
||||
justUpdated = lastBuild != currentBuild
|
||||
} else {
|
||||
justUpdated = !alreadyRelaunchedForDebug
|
||||
alreadyRelaunchedForDebug = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +35,7 @@ extension JustUpdatedChecker {
|
||||
|
||||
enum Constants {
|
||||
static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild"
|
||||
static let debugVersionKey = "GITHUB_CI_VERSION"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ import Foundation
|
||||
|
||||
|
||||
extension Bundle {
|
||||
public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!}
|
||||
public var agentBundleID: String { "Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent" }
|
||||
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ extension Preview {
|
||||
}
|
||||
|
||||
class StoreModifiable: Store, SecretStoreModifiable {
|
||||
|
||||
override var name: String { "Modifiable Preview Store" }
|
||||
|
||||
func create(name: String, requiresAuthentication: Bool) throws {
|
||||
@@ -55,6 +56,10 @@ extension Preview {
|
||||
|
||||
func update(secret: Preview.Secret, name: String) throws {
|
||||
}
|
||||
|
||||
func reload() throws {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string>
|
||||
</array>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
|
||||
@@ -87,7 +87,7 @@ extension ContentView {
|
||||
})
|
||||
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||
if let modifiable = storeList.modifiableStore {
|
||||
CreateSecretView(store: modifiable, showing: $showingCreation)
|
||||
CreateSecretView<AnySecretStoreModifiable, AgentCommunicationController>(store: modifiable, showing: $showingCreation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
|
||||
struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationControllerType: AgentCommunicationControllerProtocol>: View {
|
||||
|
||||
@ObservedObject var store: StoreType
|
||||
@EnvironmentObject private var agentCommunicationController: AgentCommunicationControllerType
|
||||
@Binding var showing: Bool
|
||||
|
||||
@State private var name = ""
|
||||
@@ -52,6 +53,9 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
|
||||
func save() {
|
||||
try! store.create(name: name, requiresAuthentication: requiresAuthentication)
|
||||
Task {
|
||||
try! await agentCommunicationController.agent.updatedStore(withID: store.id)
|
||||
}
|
||||
showing = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ struct SecretAgentSetupView: View {
|
||||
}
|
||||
|
||||
func install() {
|
||||
LaunchAgentController().install()
|
||||
AgentLaunchController().install()
|
||||
buttonAction()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user