Skip to main content
Glama

Whispera

by sapoepsilon
PermissionManager.swift3.89 kB
import Foundation import AVFoundation import ApplicationServices import AppKit import Observation @Observable class PermissionManager { // MARK: - Observable Properties var microphonePermissionGranted = false var accessibilityPermissionGranted = false var needsPermissions = false // MARK: - Private Properties private var permissionCheckTimer: Timer? init() { updatePermissionStatus() startPeriodicChecks() } deinit { permissionCheckTimer?.invalidate() } // MARK: - Public Methods /// Updates all permission statuses func updatePermissionStatus() { let newMicrophonePermission = checkMicrophonePermission() let newAccessibilityPermission = checkAccessibilityPermission() microphonePermissionGranted = newMicrophonePermission accessibilityPermissionGranted = newAccessibilityPermission needsPermissions = !newMicrophonePermission || !newAccessibilityPermission } /// Requests microphone permission func requestMicrophonePermission() async -> Bool { return await withCheckedContinuation { continuation in AVCaptureDevice.requestAccess(for: .audio) { granted in DispatchQueue.main.async { self.updatePermissionStatus() continuation.resume(returning: granted) } } } } /// Opens System Settings to the Privacy & Security section func openSystemSettings() { if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy") { NSWorkspace.shared.open(url) } } /// Opens Accessibility settings specifically func openAccessibilitySettings() { if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { NSWorkspace.shared.open(url) } } /// Opens Microphone settings specifically func openMicrophoneSettings() { if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone") { NSWorkspace.shared.open(url) } } // MARK: - Private Methods private func checkMicrophonePermission() -> Bool { return AVCaptureDevice.authorizationStatus(for: .audio) == .authorized } private func checkAccessibilityPermission() -> Bool { return AXIsProcessTrusted() } private func startPeriodicChecks() { // Check permissions every 2 seconds to detect changes permissionCheckTimer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] _ in self?.updatePermissionStatus() } } } // MARK: - Permission Status Helpers extension PermissionManager { /// Returns a user-friendly description of missing permissions var missingPermissionsDescription: String { var missing: [String] = [] if !microphonePermissionGranted { missing.append("Microphone access") } if !accessibilityPermissionGranted { missing.append("Accessibility access") } if missing.isEmpty { return "All permissions granted" } else if missing.count == 1 { return "\(missing[0]) required" } else { return "\(missing.joined(separator: " and ")) required" } } /// Returns the permission status as a color var permissionStatusColor: NSColor { return needsPermissions ? .systemOrange : .systemGreen } /// Returns an appropriate system icon for permission status var permissionStatusIcon: String { return needsPermissions ? "exclamationmark.triangle.fill" : "checkmark.circle.fill" } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sapoepsilon/Whispera'

If you have feedback or need assistance with the MCP directory API, please join our Discord server