diff --git a/apps/boss-agent-mac/Sources/BossAgentApp.swift b/apps/boss-agent-mac/Sources/BossAgentApp.swift index 35a0896..90c7cb4 100644 --- a/apps/boss-agent-mac/Sources/BossAgentApp.swift +++ b/apps/boss-agent-mac/Sources/BossAgentApp.swift @@ -68,6 +68,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { loadAgentPanel(tab: activeTab) } + func application(_ application: NSApplication, open urls: [URL]) { + for url in urls where isBossAgentDeepLink(url) { + handleBossAgentDeepLink(url) + } + } + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { loadFallback() } @@ -109,6 +115,24 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { url.path == "/api/v1/boss-agent/permissions/open" } + private func isBossAgentDeepLink(_ url: URL) -> Bool { + url.scheme == "boss-agent" + } + + private func handleBossAgentDeepLink(_ url: URL) { + if url.host == "permissions" && url.path == "/open" { + handlePermissionTarget( + queryValue("target", in: url) ?? "all", + returnTab: queryValue("returnTab", in: url) ?? "permissions" + ) + return + } + + if url.host == "tab" { + loadAgentPanel(tab: String(url.path.dropFirst())) + } + } + 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 == "/") @@ -137,9 +161,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { } private func handlePermissionSetupNavigation(_ url: URL) { - let target = - queryValue("target", in: url) ?? "all" - let returnTab = normalizedTab(queryValue("returnTab", in: url) ?? activeTab) + handlePermissionTarget( + queryValue("target", in: url) ?? "all", + returnTab: queryValue("returnTab", in: url) ?? activeTab + ) + } + + private func handlePermissionTarget(_ target: String, returnTab rawReturnTab: String) { + let returnTab = normalizedTab(rawReturnTab) activeTab = returnTab requestNativePermission(for: target) diff --git a/scripts/build-boss-agent-mac-app.sh b/scripts/build-boss-agent-mac-app.sh index e4d049e..fd9bb4e 100644 --- a/scripts/build-boss-agent-mac-app.sh +++ b/scripts/build-boss-agent-mac-app.sh @@ -132,6 +132,17 @@ cat > "$CONTENTS_DIR/Info.plist" <<'PLIST' boss-agent CFBundleIconFile BossAgent.icns + CFBundleURLTypes + + + CFBundleURLName + com.hyzq.boss.agent + CFBundleURLSchemes + + boss-agent + + + CFBundlePackageType APPL CFBundleShortVersionString @@ -162,4 +173,5 @@ cat > "$CONTENTS_DIR/Info.plist" <<'PLIST' PLIST plutil -lint "$CONTENTS_DIR/Info.plist" >/dev/null +codesign --force --deep --sign - "$APP_DIR" >/dev/null echo "$APP_DIR" diff --git a/tests/boss-agent-status.test.mjs b/tests/boss-agent-status.test.mjs index 6879746..fd61837 100644 --- a/tests/boss-agent-status.test.mjs +++ b/tests/boss-agent-status.test.mjs @@ -248,6 +248,8 @@ test("boss-agent mac app intercepts permission links and triggers native app per assert.match(swiftSource, /nativePermissionQueryItems/); assert.match(swiftSource, /nativeCamera/); assert.match(swiftSource, /nativeMicrophone/); + assert.match(swiftSource, /func application\(_ application: NSApplication, open urls: \[URL\]\)/); + assert.match(swiftSource, /isBossAgentDeepLink/); assert.match(swiftSource, /AVCaptureDevice\.authorizationStatus\(for: \.video/); assert.match(swiftSource, /AVCaptureDevice\.authorizationStatus\(for: \.audio/); assert.match(swiftSource, /NSApplication\.didBecomeActiveNotification/); @@ -255,7 +257,10 @@ test("boss-agent mac app intercepts permission links and triggers native app per assert.match(buildScript, /NSCameraUsageDescription/); assert.match(buildScript, /NSAppleEventsUsageDescription/); assert.match(buildScript, /NSLocalNetworkUsageDescription/); + assert.match(buildScript, /CFBundleURLTypes/); + assert.match(buildScript, /boss-agent/); assert.match(buildScript, /CFBundleIconFile/); assert.match(buildScript, /BossAgent\.icns/); assert.match(buildScript, /iconutil -c icns/); + assert.match(buildScript, /codesign --force --deep --sign - "\$APP_DIR"/); });