Skip to content

Commit d1a1d03

Browse files
committed
feat: detect disabled Login Item and show helpful message instead of re-prompting
Check SMAppService.daemon().status before falling through to reinstall. If the user disabled VPN Bypass in System Settings → Login Items, show a specific error message instead of triggering the admin password prompt. Closes #25
1 parent 1905522 commit d1a1d03

4 files changed

Lines changed: 22 additions & 0 deletions

File tree

Resources/en.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"Error: %@" = "Error: %@";
9090
"Installation failed" = "Installation failed";
9191
"Cannot connect to helper after reinstall" = "Cannot connect to helper after reinstall";
92+
"Please enable VPN Bypass in System Settings → General → Login Items" = "Please enable VPN Bypass in System Settings → General → Login Items";
9293
"Helper update failed: %@" = "Helper update failed: %@";
9394
"Helper update did not take effect (got %@, expected %@)" = "Helper update did not take effect (got %@, expected %@)";
9495
"unknown" = "unknown";

Resources/es.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"Error: %@" = "Error: %@";
9090
"Installation failed" = "La instalación falló";
9191
"Cannot connect to helper after reinstall" = "No se puede conectar al asistente tras reinstalar";
92+
"Please enable VPN Bypass in System Settings → General → Login Items" = "Activa VPN Bypass en Ajustes del Sistema → General → Ítems de inicio";
9293
"Helper update failed: %@" = "La actualización del asistente falló: %@";
9394
"Helper update did not take effect (got %@, expected %@)" = "La actualización del asistente no surtió efecto (obtenido %@, esperado %@)";
9495
"unknown" = "desconocido";

Resources/fr.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"Error: %@" = "Erreur : %@";
9090
"Installation failed" = "L'installation a échoué";
9191
"Cannot connect to helper after reinstall" = "Impossible de se connecter à l'assistant après réinstallation";
92+
"Please enable VPN Bypass in System Settings → General → Login Items" = "Activez VPN Bypass dans Réglages du système → Général → Ouverture";
9293
"Helper update failed: %@" = "La mise à jour de l'assistant a échoué : %@";
9394
"Helper update did not take effect (got %@, expected %@)" = "La mise à jour de l'assistant n'a pas pris effet (obtenu %@, attendu %@)";
9495
"unknown" = "inconnu";

Sources/HelperManager.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ final class HelperManager: ObservableObject {
4848
/// XPC timeout for all helper RPCs (seconds)
4949
private let xpcTimeout: TimeInterval = 10
5050

51+
/// Check if the helper daemon is disabled in System Settings → Login Items.
52+
/// Returns true if the daemon is blocked by the user, meaning we should NOT
53+
/// attempt reinstall (it would just prompt again next boot).
54+
@available(macOS 13.0, *)
55+
private func isDaemonDisabledByUser() -> Bool {
56+
let service = SMAppService.daemon(plistName: "\(kHelperToolMachServiceName).plist")
57+
let status = service.status
58+
// .notRegistered means the user toggled it off in Login Items, or it was never registered
59+
// .requiresApproval means macOS is blocking it pending user approval
60+
return status == .notRegistered || status == .requiresApproval
61+
}
62+
5163
private init() {
5264
// Only set initial state — do NOT start route application here.
5365
// The app must call ensureHelperReady() before using helper RPCs.
@@ -97,6 +109,13 @@ final class HelperManager: ObservableObject {
97109
let version = await getVersionWithTimeout()
98110

99111
guard let version = version else {
112+
// Check if the user disabled the background item in System Settings
113+
if #available(macOS 13.0, *), isDaemonDisabledByUser() {
114+
print("🔐 Helper daemon disabled by user in System Settings")
115+
helperState = .failed(String(localized: "Please enable VPN Bypass in System Settings → General → Login Items"))
116+
return false
117+
}
118+
100119
// XPC connection failed — helper may be corrupted or wrong arch
101120
print("🔐 Helper XPC connection failed, attempting reinstall...")
102121
helperState = .installing

0 commit comments

Comments
 (0)