diff --git a/Sources/HelperManager.swift b/Sources/HelperManager.swift index 93707bf..c4c6cc7 100644 --- a/Sources/HelperManager.swift +++ b/Sources/HelperManager.swift @@ -259,6 +259,7 @@ final class HelperManager: ObservableObject { // binary. For updates we must always go through the legacy path which does // the actual file copy. Only use SMAppService for first-time installs. let helperPath = "/Library/PrivilegedHelperTools/\(kHelperToolMachServiceName)" + let plistPath = "/Library/LaunchDaemons/\(kHelperToolMachServiceName).plist" if FileManager.default.fileExists(atPath: helperPath) { print("🔐 Helper exists on disk, using legacy path for update...") return installHelperLegacy() @@ -268,12 +269,22 @@ final class HelperManager: ObservableObject { let service = SMAppService.daemon(plistName: "\(kHelperToolMachServiceName).plist") try await service.register() - installationError = nil print("✅ Helper registered successfully via SMAppService") + + // SMAppService.register() can report success without actually placing the + // binary/plist on disk (observed with enterprise security tools like Zscaler). + // Verify both files exist before trusting the registration. + if !FileManager.default.fileExists(atPath: helperPath) || + !FileManager.default.fileExists(atPath: plistPath) { + print("⚠️ SMAppService reported success but helper files missing on disk, falling back to legacy install...") + return installHelperLegacy() + } + + installationError = nil return true } catch { print("⚠️ SMAppService failed: \(error.localizedDescription)") - print("🔐 Falling back to legacy SMJobBless...") + print("🔐 Falling back to legacy install...") return installHelperLegacy() } }