fix: sync native agent permission states
This commit is contained in:
@@ -14,9 +14,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
private var webView: WKWebView?
|
||||
private var inputMonitoringTap: CFMachPort?
|
||||
private var localNetworkBrowser: NWBrowser?
|
||||
private var activeTab = "overview"
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(handleApplicationDidBecomeActive),
|
||||
name: NSApplication.didBecomeActiveNotification,
|
||||
object: nil
|
||||
)
|
||||
|
||||
let webConfiguration = WKWebViewConfiguration()
|
||||
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
|
||||
@@ -38,18 +45,29 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
self.window = window
|
||||
|
||||
loadAgentPanel()
|
||||
loadAgentPanel(tab: activeTab)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
private func loadAgentPanel() {
|
||||
guard let url = URL(string: "http://127.0.0.1:4317/boss-agent") else {
|
||||
private func loadAgentPanel(tab: String? = nil) {
|
||||
activeTab = normalizedTab(tab ?? activeTab)
|
||||
var components = URLComponents()
|
||||
components.scheme = "http"
|
||||
components.host = "127.0.0.1"
|
||||
components.port = 4317
|
||||
components.path = "/boss-agent"
|
||||
components.queryItems = [URLQueryItem(name: "tab", value: activeTab)] + nativePermissionQueryItems()
|
||||
guard let url = components.url else {
|
||||
loadFallback()
|
||||
return
|
||||
}
|
||||
webView?.load(URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData))
|
||||
}
|
||||
|
||||
@objc private func handleApplicationDidBecomeActive() {
|
||||
loadAgentPanel(tab: activeTab)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
|
||||
loadFallback()
|
||||
}
|
||||
@@ -74,6 +92,16 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
if isAgentPanelUrl(url) && !hasNativePermissionQuery(url) {
|
||||
decisionHandler(.cancel)
|
||||
loadAgentPanel(tab: queryValue("tab", in: url))
|
||||
return
|
||||
}
|
||||
|
||||
if isAgentPanelUrl(url), let tab = queryValue("tab", in: url) {
|
||||
activeTab = normalizedTab(tab)
|
||||
}
|
||||
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
|
||||
@@ -81,12 +109,38 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
url.path == "/api/v1/boss-agent/permissions/open"
|
||||
}
|
||||
|
||||
private func isAgentPanelUrl(_ url: URL) -> Bool {
|
||||
let host = url.host ?? ""
|
||||
return (host == "127.0.0.1" || host == "localhost") && url.port == 4317 && (url.path == "/boss-agent" || url.path == "/")
|
||||
}
|
||||
|
||||
private func hasNativePermissionQuery(_ url: URL) -> Bool {
|
||||
URLComponents(url: url, resolvingAgainstBaseURL: false)?
|
||||
.queryItems?
|
||||
.contains(where: { $0.name.hasPrefix("native") }) == true
|
||||
}
|
||||
|
||||
private func queryValue(_ name: String, in url: URL) -> String? {
|
||||
URLComponents(url: url, resolvingAgainstBaseURL: false)?
|
||||
.queryItems?
|
||||
.first(where: { $0.name == name })?
|
||||
.value
|
||||
}
|
||||
|
||||
private func normalizedTab(_ value: String?) -> String {
|
||||
switch value {
|
||||
case "permissions", "skills", "license", "logs", "overview":
|
||||
return value ?? "overview"
|
||||
default:
|
||||
return "overview"
|
||||
}
|
||||
}
|
||||
|
||||
private func handlePermissionSetupNavigation(_ url: URL) {
|
||||
let target =
|
||||
URLComponents(url: url, resolvingAgainstBaseURL: false)?
|
||||
.queryItems?
|
||||
.first(where: { $0.name == "target" })?
|
||||
.value ?? "all"
|
||||
queryValue("target", in: url) ?? "all"
|
||||
let returnTab = normalizedTab(queryValue("returnTab", in: url) ?? activeTab)
|
||||
activeTab = returnTab
|
||||
|
||||
requestNativePermission(for: target)
|
||||
if let settingsUrl = systemSettingsUrl(for: target) {
|
||||
@@ -94,7 +148,49 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { [weak self] in
|
||||
self?.loadAgentPanel()
|
||||
self?.loadAgentPanel(tab: returnTab)
|
||||
}
|
||||
}
|
||||
|
||||
private func nativePermissionQueryItems() -> [URLQueryItem] {
|
||||
[
|
||||
URLQueryItem(name: "nativeAccessibility", value: AXIsProcessTrusted() ? "granted" : "missing"),
|
||||
URLQueryItem(name: "nativeScreenRecording", value: screenRecordingStatus()),
|
||||
URLQueryItem(name: "nativeMicrophone", value: microphonePermissionStatus()),
|
||||
URLQueryItem(name: "nativeCamera", value: cameraPermissionStatus()),
|
||||
]
|
||||
}
|
||||
|
||||
private func screenRecordingStatus() -> String {
|
||||
if #available(macOS 10.15, *) {
|
||||
return CGPreflightScreenCaptureAccess() ? "granted" : "missing"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
private func microphonePermissionStatus() -> String {
|
||||
switch AVCaptureDevice.authorizationStatus(for: .audio) {
|
||||
case .authorized:
|
||||
return "granted"
|
||||
case .denied, .restricted:
|
||||
return "missing"
|
||||
case .notDetermined:
|
||||
return "unknown"
|
||||
@unknown default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
private func cameraPermissionStatus() -> String {
|
||||
switch AVCaptureDevice.authorizationStatus(for: .video) {
|
||||
case .authorized:
|
||||
return "granted"
|
||||
case .denied, .restricted:
|
||||
return "missing"
|
||||
case .notDetermined:
|
||||
return "unknown"
|
||||
@unknown default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +229,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
case "notifications":
|
||||
requestNotificationPermission()
|
||||
case "microphone":
|
||||
AVCaptureDevice.requestAccess(for: .audio) { _ in }
|
||||
requestMicrophonePermission()
|
||||
case "camera":
|
||||
AVCaptureDevice.requestAccess(for: .video) { _ in }
|
||||
requestCameraPermission()
|
||||
case "localNetwork":
|
||||
requestLocalNetworkPermission()
|
||||
default:
|
||||
@@ -149,6 +245,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate {
|
||||
_ = AXIsProcessTrustedWithOptions(options)
|
||||
}
|
||||
|
||||
private func requestMicrophonePermission() {
|
||||
if AVCaptureDevice.authorizationStatus(for: .audio) == .notDetermined {
|
||||
AVCaptureDevice.requestAccess(for: .audio) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
private func requestCameraPermission() {
|
||||
if AVCaptureDevice.authorizationStatus(for: .video) == .notDetermined {
|
||||
AVCaptureDevice.requestAccess(for: .video) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
private func requestScreenRecordingPermission() {
|
||||
if #available(macOS 10.15, *) {
|
||||
_ = CGRequestScreenCaptureAccess()
|
||||
|
||||
Reference in New Issue
Block a user