diff --git a/Sources/removemacai/App/Pages.swift b/Sources/removemacai/App/Pages.swift index aadf4d3..57df695 100644 --- a/Sources/removemacai/App/Pages.swift +++ b/Sources/removemacai/App/Pages.swift @@ -300,13 +300,16 @@ struct BackgroundView: View { Text(item.label).font(.caption.monospaced()).foregroundStyle(.secondary) Text(item.program).font(.caption2.monospaced()).foregroundStyle(.tertiary) .lineLimit(1).truncationMode(.middle) + if let warning = item.warning { + Label(warning, systemImage: "exclamationmark.triangle").font(.caption).foregroundStyle(.orange) + } } } .toggleStyle(.switch) .disabled(model.backgroundBusy) } } header: { - Text("Updaters, helpers and agents other apps installed. They run whether or not their app is open. Switching one off takes effect at once.") + Text("Updaters, helpers and agents other apps installed. They run whether or not their app is open. Switching one off takes effect at once. Updaters are safe to switch off; a helper can be what makes its app work, such as a VPN or Docker.") .font(.body).foregroundStyle(.secondary).textCase(nil) } footer: { Footer("Items marked All users ask for your password. Apple's own background services are protected by System Integrity Protection, so RemoveMacAI leaves them alone.") diff --git a/Sources/removemacai/BackgroundItems.swift b/Sources/removemacai/BackgroundItems.swift index ca04c1b..5ebdf0a 100644 --- a/Sources/removemacai/BackgroundItems.swift +++ b/Sources/removemacai/BackgroundItems.swift @@ -16,6 +16,18 @@ struct BackgroundItem: Identifiable, Hashable { static let interpreters: Set = ["sh", "bash", "zsh", "python3", "python", "node", "osascript", "perl", "ruby"] + /// Whether it only keeps its app up to date, judged by its label and the + /// name of what it runs. Anything else (a VPN, Docker's socket, a sync or + /// licensing helper) can be what makes its app work, so it gets a warning. + var isUpdater: Bool { + [label, (program as NSString).lastPathComponent].contains { $0.lowercased().contains("update") } + } + + /// What switching it off can break, or nil for an updater. + var warning: String? { + isUpdater ? nil : "Not an updater. Turning it off can stop \(owner) working." + } + /// The app or program it runs, as people know it. var owner: String { if let app = program.split(separator: "/").first(where: { $0.hasSuffix(".app") }) { diff --git a/Sources/removemacai/SelfTest.swift b/Sources/removemacai/SelfTest.swift index e260482..0061e53 100644 --- a/Sources/removemacai/SelfTest.swift +++ b/Sources/removemacai/SelfTest.swift @@ -217,6 +217,15 @@ func selfTest() -> Bool { program: "/Library/PrivilegedHelperTools/dev.orbstack.OrbStack.privhelper", system: true) check(script.owner == "sync.sh" && helper.owner == "OrbStack", "background items are named after what they run") + let updater = BackgroundItem(label: "com.google.GoogleUpdater.wake", plist: "", + program: "/Users/x/Library/Application Support/Google/GoogleUpdater/Current/GoogleUpdater.app/Contents/MacOS/GoogleUpdater", + system: false) + check(updater.isUpdater && updater.warning == nil && !helper.isUpdater && helper.warning != nil, + "helpers that aren't updaters carry a warning") + let vpn = BackgroundItem(label: "com.example.vpn", plist: "", program: "/Library/Example Updates/vpnd", + arguments: ["/Library/Example Updates/vpnd", "--check-updates"], system: true) + check(!vpn.isUpdater && vpn.warning != nil, "an unrelated mention of updates keeps the warning") + print(failed == 0 ? Term.green("all checks passed") : Term.red("\(failed) failed")) return failed == 0 } diff --git a/Sources/removemacai/TweakCommands.swift b/Sources/removemacai/TweakCommands.swift index 0c11e7d..dc1a87b 100644 --- a/Sources/removemacai/TweakCommands.swift +++ b/Sources/removemacai/TweakCommands.swift @@ -142,10 +142,14 @@ enum TweakCommands { } print(Term.bold("Background items from other apps")) for item in items { - let state = BackgroundItems.isDisabled(item, labels) ? Term.dim("off") : Term.green("on") - print(" " + Term.pad(item.label, 48) + Term.pad(item.owner, 22) + state + (item.system ? Term.dim(" (all users)") : "")) + let off = BackgroundItems.isDisabled(item, labels) + let state = off ? Term.dim(Term.pad("off", 5)) : Term.green(Term.pad("on", 5)) + let kind = item.isUpdater ? Term.dim("updater") : Term.yellow("helper") + print(" " + Term.pad(item.label, 48) + Term.pad(item.owner, 22) + state + kind + + (item.system ? Term.dim(" (all users)") : "")) } print() + print(Term.dim("Turning off a helper can stop its app working, for example a VPN or Docker.")) print(Term.dim("Turn one off with: removemacai background off