diff --git a/apps/boss-agent-mac/Sources/BossAgentApp.swift b/apps/boss-agent-mac/Sources/BossAgentApp.swift index 752fa97..fc71f6f 100644 --- a/apps/boss-agent-mac/Sources/BossAgentApp.swift +++ b/apps/boss-agent-mac/Sources/BossAgentApp.swift @@ -1,22 +1,10 @@ import Cocoa import WebKit import ApplicationServices -import AVFoundation -import IOKit.hid -import Network -import UserNotifications - -private let bossInputMonitoringTapCallback: CGEventTapCallBack = { _, _, event, _ in - Unmanaged.passUnretained(event) -} final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { private var window: NSWindow? private var webView: WKWebView? - private var inputMonitoringTap: CFMachPort? - private var globalKeyMonitor: Any? - private var inputMonitoringManager: IOHIDManager? - private var localNetworkBrowser: NWBrowser? private var activeTab = "overview" func applicationDidFinishLaunching(_ notification: Notification) { @@ -166,7 +154,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { private func handleBossAgentDeepLink(_ url: URL) { if url.host == "permissions" && url.path == "/open" { handlePermissionTarget( - queryValue("target", in: url) ?? "all", + queryValue("target", in: url) ?? "core", returnTab: queryValue("returnTab", in: url) ?? "permissions" ) return @@ -206,22 +194,23 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { private func handlePermissionSetupNavigation(_ url: URL) { handlePermissionTarget( - queryValue("target", in: url) ?? "all", + queryValue("target", in: url) ?? "core", returnTab: queryValue("returnTab", in: url) ?? activeTab ) } private func handlePermissionTarget(_ target: String, returnTab rawReturnTab: String) { + let permissionTarget = normalizedPermissionTarget(target) let returnTab = normalizedTab(rawReturnTab) activeTab = returnTab - UserDefaults.standard.set(target, forKey: "lastPermissionRequestTarget") + UserDefaults.standard.set(permissionTarget, forKey: "lastPermissionRequestTarget") UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastPermissionRequestAt") - NSLog("boss-agent permission request target=%@ returnTab=%@", target, returnTab) + NSLog("boss-agent permission request target=%@ returnTab=%@", permissionTarget, returnTab) NSApp.activate(ignoringOtherApps: true) - requestNativePermission(for: target) + requestNativePermission(for: permissionTarget) DispatchQueue.main.asyncAfter(deadline: .now() + 0.45) { [weak self] in - if let settingsUrl = self?.systemSettingsUrl(for: target) { + if let settingsUrl = self?.systemSettingsUrl(for: permissionTarget) { NSWorkspace.shared.open(settingsUrl) } } @@ -235,9 +224,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { [ URLQueryItem(name: "nativeAccessibility", value: AXIsProcessTrusted() ? "granted" : "missing"), URLQueryItem(name: "nativeScreenRecording", value: screenRecordingStatus()), - URLQueryItem(name: "nativeInputMonitoring", value: inputMonitoringStatus()), - URLQueryItem(name: "nativeMicrophone", value: microphonePermissionStatus()), - URLQueryItem(name: "nativeCamera", value: cameraPermissionStatus()), ] } @@ -248,63 +234,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { 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" - } - } - - private func inputMonitoringStatus() -> String { - if #available(macOS 10.15, *) { - return CGPreflightListenEventAccess() ? "granted" : "missing" - } - - switch IOHIDCheckAccess(kIOHIDRequestTypeListenEvent) { - case kIOHIDAccessTypeGranted: - return "granted" - case kIOHIDAccessTypeDenied: - return "missing" - default: - return "unknown" - } - } - private func requestNativePermission(for target: String) { let targets: [String] if target == "core" { targets = ["accessibility", "screenRecording"] - } else if target == "all" { - targets = [ - "accessibility", - "screenRecording", - "automation", - "fullDiskAccess", - "inputMonitoring", - "notifications", - "microphone", - "camera", - "localNetwork", - ] } else { targets = [target] } @@ -320,20 +253,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { requestAccessibilityPermission() case "screenRecording": requestScreenRecordingPermission() - case "automation": - requestAutomationPermission() - case "fullDiskAccess": - preflightFullDiskAccess() - case "inputMonitoring": - requestInputMonitoringPermission() - case "notifications": - requestNotificationPermission() - case "microphone": - requestMicrophonePermission() - case "camera": - requestCameraPermission() - case "localNetwork": - requestLocalNetworkPermission() default: break } @@ -345,18 +264,6 @@ 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() @@ -365,105 +272,22 @@ final class AppDelegate: NSObject, NSApplicationDelegate, WKNavigationDelegate { } } - private func requestAutomationPermission() { - let script = NSAppleScript(source: "tell application \"Finder\" to get name") - var error: NSDictionary? - _ = script?.executeAndReturnError(&error) - } - - private func preflightFullDiskAccess() { - let candidatePaths = [ - "\(NSHomeDirectory())/Library/Safari/Bookmarks.plist", - "\(NSHomeDirectory())/Library/Mail", - "/Library/Application Support/com.apple.TCC/TCC.db", - ] - for path in candidatePaths { - if FileManager.default.fileExists(atPath: path) { - _ = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) - } - } - } - - private func requestInputMonitoringPermission() { - var listenRequestResult = false - if #available(macOS 10.15, *) { - listenRequestResult = CGRequestListenEventAccess() - } else { - listenRequestResult = IOHIDRequestAccess(kIOHIDRequestTypeListenEvent) - } - NSLog("boss-agent input monitoring listen request result=%@", listenRequestResult ? "true" : "false") - - if globalKeyMonitor == nil { - globalKeyMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.keyDown]) { _ in } - } - preflightKeyboardStateRead() - primeInputMonitoringHidPath() - - let eventMask = CGEventMask(1 << CGEventType.keyDown.rawValue) - inputMonitoringTap = CGEvent.tapCreate( - tap: .cgSessionEventTap, - place: .headInsertEventTap, - options: .listenOnly, - eventsOfInterest: eventMask, - callback: bossInputMonitoringTapCallback, - userInfo: nil - ) - if let tap = inputMonitoringTap { - CFMachPortInvalidate(tap) - inputMonitoringTap = nil - } - } - - private func preflightKeyboardStateRead() { - for keyCode in [CGKeyCode(0), CGKeyCode(1), CGKeyCode(49), CGKeyCode(53)] { - _ = CGEventSource.keyState(.combinedSessionState, key: keyCode) - } - } - - private func primeInputMonitoringHidPath() { - let manager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone)) - let keyboardMatching = [ - kIOHIDDeviceUsagePageKey as String: kHIDPage_GenericDesktop, - kIOHIDDeviceUsageKey as String: kHIDUsage_GD_Keyboard, - ] as CFDictionary - IOHIDManagerSetDeviceMatching(manager, keyboardMatching) - let openResult = IOHIDManagerOpen(manager, IOOptionBits(kIOHIDOptionsTypeNone)) - NSLog("boss-agent input monitoring hid open result=%d", openResult) - inputMonitoringManager = manager - } - - private func requestNotificationPermission() { - UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, _ in } - } - - private func requestLocalNetworkPermission() { - let parameters = NWParameters.tcp - parameters.includePeerToPeer = true - let browser = NWBrowser(for: .bonjour(type: "_http._tcp", domain: nil), using: parameters) - browser.stateUpdateHandler = { _ in } - localNetworkBrowser = browser - browser.start(queue: .main) - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { [weak self] in - self?.localNetworkBrowser?.cancel() - self?.localNetworkBrowser = nil - } - } - private func systemSettingsUrl(for target: String) -> URL? { let mapping = [ - "all": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Security", "core": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", "accessibility": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", "screenRecording": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ScreenCapture", - "automation": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Automation", - "fullDiskAccess": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles", - "inputMonitoring": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ListenEvent", - "notifications": "x-apple.systempreferences:com.apple.Notifications-Settings.extension", - "microphone": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Microphone", - "camera": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Camera", - "localNetwork": "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?privacy-localnetwork", ] - return URL(string: mapping[target] ?? mapping["all"]!) + return URL(string: mapping[normalizedPermissionTarget(target)] ?? mapping["core"]!) + } + + private func normalizedPermissionTarget(_ target: String) -> String { + switch target { + case "accessibility", "screenRecording", "core": + return target + default: + return "core" + } } private func loadFallback() { diff --git a/local-agent/boss-agent-status.mjs b/local-agent/boss-agent-status.mjs index 29748ef..dfb6717 100644 --- a/local-agent/boss-agent-status.mjs +++ b/local-agent/boss-agent-status.mjs @@ -18,73 +18,17 @@ const PERMISSION_DEFS = [ }, ]; -const EXTENDED_PERMISSION_DEFS = [ - { - key: "automation", - label: "自动化控制", - description: "用于 AppleScript 控制 Finder、浏览器和企业软件;基础桌面控制不强制依赖", - tier: "extended", - }, - { - key: "fullDiskAccess", - label: "全磁盘访问", - description: "用于读取和写入企业授权目录、日志与开发资产", - tier: "extended", - }, - { - key: "inputMonitoring", - label: "输入监控", - description: "用于低层热键、复杂输入和部分不可访问控件兜底", - tier: "extended", - }, - { - key: "notifications", - label: "通知权限", - description: "用于后台任务、接管结果和风险告警提醒", - tier: "extended", - }, - { - key: "microphone", - label: "麦克风", - description: "用于语音指令、会议和音频协作场景", - tier: "extended", - }, - { - key: "camera", - label: "摄像头", - description: "用于视觉协作、会议和现场画面确认", - tier: "extended", - }, - { - key: "localNetwork", - label: "本地网络", - description: "用于发现和连接局域网设备、开发板与企业内网服务", - tier: "extended", - }, -]; - const MACOS_PERMISSION_SETTINGS = { - all: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Security", core: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", accessibility: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Accessibility", screenRecording: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ScreenCapture", - automation: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Automation", - fullDiskAccess: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_AllFiles", - inputMonitoring: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ListenEvent", - notifications: "x-apple.systempreferences:com.apple.Notifications-Settings.extension", - microphone: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Microphone", - camera: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_Camera", - localNetwork: "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?privacy-localnetwork", }; -const AUTO_PREFLIGHT_PERMISSION_KEYS = new Set(["accessibility", "screenRecording", "automation"]); +const AUTO_PREFLIGHT_PERMISSION_KEYS = new Set(["accessibility", "screenRecording"]); const NATIVE_PERMISSION_QUERY_PARAMS = { accessibility: "nativeAccessibility", screenRecording: "nativeScreenRecording", - inputMonitoring: "nativeInputMonitoring", - microphone: "nativeMicrophone", - camera: "nativeCamera", }; function nonEmpty(value) { @@ -204,11 +148,7 @@ function resolvePermissionReadiness(coreItems, extendedItems) { const extendedGrantedCount = extendedItems.filter((item) => item.status === "granted").length; const coreReady = coreGrantedCount === coreItems.length; const fullControlReady = coreReady && extendedGrantedCount === extendedItems.length; - const summary = fullControlReady - ? "基础桌面控制和扩展能力权限已具备" - : coreReady - ? "基础桌面控制已可用,扩展权限按场景启用" - : "基础桌面控制待授权,桌面接管不可用"; + const summary = coreReady ? "基础桌面控制已可用" : "基础桌面控制待授权,桌面接管不可用"; return { coreReady, @@ -219,12 +159,12 @@ function resolvePermissionReadiness(coreItems, extendedItems) { extendedTotal: extendedItems.length, summary, detail: - "参考 Codex Computer Use 的最小权限模型,基础桌面控制只要求辅助功能和屏幕录制;自动化控制、全磁盘访问、输入监控、通知、麦克风、摄像头和本地网络都按具体任务场景再单独启用。", + "参考 Codex Computer Use 的最小权限模型,boss-agent 只要求辅助功能和屏幕录制:辅助功能负责点击输入,屏幕录制负责画面识别。", }; } -function buildPermissionSetupPlan(coreItems, extendedItems, readiness) { - const actions = [...coreItems, ...extendedItems].map((item) => ({ +function buildPermissionSetupPlan(coreItems, readiness) { + const actions = coreItems.map((item) => ({ key: item.key, label: item.label, description: item.description, @@ -232,21 +172,18 @@ function buildPermissionSetupPlan(coreItems, extendedItems, readiness) { status: item.status, requiredForSilentControl: item.tier === "core", canPreflight: AUTO_PREFLIGHT_PERMISSION_KEYS.has(item.key), - settingsUrl: MACOS_PERMISSION_SETTINGS[item.key] ?? MACOS_PERMISSION_SETTINGS.all, + settingsUrl: MACOS_PERMISSION_SETTINGS[item.key] ?? MACOS_PERMISSION_SETTINGS.core, openUrl: `/api/v1/boss-agent/permissions/open?target=${encodeURIComponent(item.key)}&returnTab=permissions`, owner: "boss-agent.app", })); const missingRequiredActions = actions.filter( (action) => action.requiredForSilentControl && action.status !== "granted", ); - const optionalMissingActions = actions.filter( - (action) => !action.requiredForSilentControl && action.status !== "granted", - ); return { mode: "minimal_computer_use", title: "基础桌面控制授权", - goal: "按 Codex Computer Use 的思路,先拿辅助功能和屏幕录制两项最小权限;其他能力等任务需要时再申请。", + goal: "按 Codex Computer Use 的思路,只申请辅助功能和屏幕录制两项最小权限。", silentUseReady: missingRequiredActions.length === 0, primaryAction: { label: "打开基础授权", @@ -256,9 +193,9 @@ function buildPermissionSetupPlan(coreItems, extendedItems, readiness) { actions, missingKeys: missingRequiredActions.map((action) => action.key), missingRequiredKeys: missingRequiredActions.map((action) => action.key), - optionalMissingKeys: optionalMissingActions.map((action) => action.key), + optionalMissingKeys: [], summary: readiness.coreReady - ? "基础桌面控制已可用;扩展权限不会阻塞接管,只在对应任务需要时提示。" + ? "基础桌面控制已可用;后续控制只校验这两项权限。" : "仍缺少基础桌面控制权限,请先授权辅助功能和屏幕录制。", persistenceNote: "macOS 会把授权持久写入系统隐私数据库;稳定签名后,后续更新不会因为二进制哈希变化反复丢失授权。", @@ -270,7 +207,12 @@ export function mergeBossAgentNativePermissionOverrides(permissions = {}, queryP if (typeof queryParams.get === "function") return queryParams.get(name); return queryParams[name]; }; - const merged = { ...permissions }; + const merged = {}; + for (const permissionKey of Object.keys(NATIVE_PERMISSION_QUERY_PARAMS)) { + if (isPermissionStatus(permissions[permissionKey])) { + merged[permissionKey] = permissions[permissionKey]; + } + } for (const [permissionKey, queryKey] of Object.entries(NATIVE_PERMISSION_QUERY_PARAMS)) { const value = getQueryValue(queryKey); if (isPermissionStatus(value)) { @@ -312,9 +254,9 @@ export function buildBossAgentStatus(config = {}, runtime = {}, options = {}) { const serverOk = runtime.lastHeartbeatOk === true; const qrPayload = bound ? "" : buildBindingPayload(config); const corePermissionItems = permissionItems(PERMISSION_DEFS, permissions); - const extendedPermissionItems = permissionItems(EXTENDED_PERMISSION_DEFS, permissions); + const extendedPermissionItems = []; const permissionReadiness = resolvePermissionReadiness(corePermissionItems, extendedPermissionItems); - const permissionSetup = buildPermissionSetupPlan(corePermissionItems, extendedPermissionItems, permissionReadiness); + const permissionSetup = buildPermissionSetupPlan(corePermissionItems, permissionReadiness); return { appName: "boss-agent", @@ -883,22 +825,27 @@ function runCommand(command, args, timeoutMs = 2500) { }); } -export function resolveBossAgentPermissionSettingsUrl(target = "all") { - return MACOS_PERMISSION_SETTINGS[target] ?? MACOS_PERMISSION_SETTINGS.all; +function normalizePermissionTarget(target = "core") { + return Object.hasOwn(MACOS_PERMISSION_SETTINGS, target) ? target : "core"; } -export async function openBossAgentPermissionSettings(target = "all", platform = process.platform) { - const settingsUrl = resolveBossAgentPermissionSettingsUrl(target); +export function resolveBossAgentPermissionSettingsUrl(target = "core") { + return MACOS_PERMISSION_SETTINGS[normalizePermissionTarget(target)]; +} + +export async function openBossAgentPermissionSettings(target = "core", platform = process.platform) { + const normalizedTarget = normalizePermissionTarget(target); + const settingsUrl = resolveBossAgentPermissionSettingsUrl(normalizedTarget); if (platform !== "darwin") { return { ok: false, - target, + target: normalizedTarget, settingsUrl, message: "当前平台暂不支持自动打开系统隐私设置,请在系统设置中手动完成授权。", }; } - const nativeUrl = `boss-agent://permissions/open?target=${encodeURIComponent(target)}&returnTab=permissions`; + const nativeUrl = `boss-agent://permissions/open?target=${encodeURIComponent(normalizedTarget)}&returnTab=permissions`; const nativeLaunch = await runCommand( "open", [ @@ -906,7 +853,7 @@ export async function openBossAgentPermissionSettings(target = "all", platform = "/Applications/boss-agent.app", "--args", "--request-permission", - target, + normalizedTarget, "--return-tab", "permissions", ], @@ -915,7 +862,7 @@ export async function openBossAgentPermissionSettings(target = "all", platform = if (nativeLaunch.ok) { return { ok: true, - target, + target: normalizedTarget, settingsUrl, message: "已通过 boss-agent 发起系统权限申请。", nativeRequest: true, @@ -927,7 +874,7 @@ export async function openBossAgentPermissionSettings(target = "all", platform = if (nativeDeepLink.ok) { return { ok: true, - target, + target: normalizedTarget, settingsUrl, message: "已通过 boss-agent 发起系统权限申请。", nativeRequest: true, @@ -938,7 +885,7 @@ export async function openBossAgentPermissionSettings(target = "all", platform = const result = await runCommand("open", [settingsUrl], 2500); return { ok: result.ok, - target, + target: normalizedTarget, settingsUrl, message: result.ok ? "已打开系统权限设置。" @@ -959,7 +906,6 @@ export async function detectLocalComputerPermissions(platform = process.platform return { accessibility: "unknown", screenRecording: "unknown", - automation: "unknown", }; } @@ -967,7 +913,6 @@ export async function detectLocalComputerPermissions(platform = process.platform "-e", 'tell application "System Events" to get UI elements enabled', ]); - const automation = await runCommand("osascript", ["-e", 'tell application "Finder" to get name']); const screenshotPath = path.join(os.tmpdir(), `boss-agent-permission-${Date.now()}.png`); const screen = await runCommand("screencapture", ["-x", "-t", "png", screenshotPath], 3500); let screenRecording = "missing"; @@ -985,6 +930,5 @@ export async function detectLocalComputerPermissions(platform = process.platform return { accessibility: accessibility.ok && /true/i.test(accessibility.stdout) ? "granted" : "missing", screenRecording, - automation: automation.ok ? "granted" : "missing", }; } diff --git a/local-agent/server.mjs b/local-agent/server.mjs index f1e42b9..4fcf6e8 100755 --- a/local-agent/server.mjs +++ b/local-agent/server.mjs @@ -1037,7 +1037,7 @@ const server = createServer(async (request, response) => { } if (requestUrl.pathname === "/api/v1/boss-agent/permissions/open") { - const target = requestUrl.searchParams.get("target") || "all"; + const target = requestUrl.searchParams.get("target") || "core"; const returnTab = normalizeBossAgentTab(requestUrl.searchParams.get("returnTab") ?? "permissions"); const result = await openBossAgentPermissionSettings(target); const wantsJson = String(request.headers.accept || "").includes("application/json"); diff --git a/scripts/build-boss-agent-mac-app.sh b/scripts/build-boss-agent-mac-app.sh index 7490260..5d81571 100644 --- a/scripts/build-boss-agent-mac-app.sh +++ b/scripts/build-boss-agent-mac-app.sh @@ -43,11 +43,7 @@ swiftc "$SOURCE_FILE" \ -o "$BINARY_PATH" \ -framework Cocoa \ -framework WebKit \ - -framework ApplicationServices \ - -framework AVFoundation \ - -framework IOKit \ - -framework Network \ - -framework UserNotifications + -framework ApplicationServices chmod +x "$BINARY_PATH" @@ -169,23 +165,8 @@ cat > "$CONTENTS_DIR/Info.plist" <<'PLIST' 13.0 NSHighResolutionCapable - NSAppleEventsUsageDescription - boss-agent 需要通过自动化控制 Finder、浏览器和授权的企业应用,以完成远程桌面级任务。 NSScreenCaptureUsageDescription boss-agent 需要读取屏幕画面,用于识别桌面状态、系统弹窗和远程控制结果。 - NSInputMonitoringUsageDescription - boss-agent 需要输入监控权限,用于低层热键、复杂输入和部分不可访问控件兜底。 - NSMicrophoneUsageDescription - boss-agent 需要麦克风权限,用于语音协作和远程指令输入。 - NSCameraUsageDescription - boss-agent 需要摄像头权限,用于视觉协作和现场画面确认。 - NSLocalNetworkUsageDescription - boss-agent 需要访问本地网络,用于发现和连接局域网设备、开发板和企业内网服务。 - NSBonjourServices - - _http._tcp - _boss-agent._tcp - PLIST diff --git a/tests/boss-agent-status.test.mjs b/tests/boss-agent-status.test.mjs index 6f93eaa..0fe1e4c 100644 --- a/tests/boss-agent-status.test.mjs +++ b/tests/boss-agent-status.test.mjs @@ -58,19 +58,14 @@ test("boss-agent status exposes unbound QR binding and local permission states", assert.match(status.permissionReadiness.summary, /基础桌面控制/); assert.equal(status.permissionSetup.mode, "minimal_computer_use"); assert.equal(status.permissionSetup.silentUseReady, false); - assert.equal(status.permissionSetup.actions.some((action) => action.key === "fullDiskAccess"), true); + assert.deepEqual( + status.permissionSetup.actions.map((action) => action.key), + ["accessibility", "screenRecording"], + ); assert.equal( status.permissionSetup.actions.every((action) => action.settingsUrl.startsWith("x-apple.systempreferences:")), true, ); - assert.equal( - status.permissionSetup.actions.find((action) => action.key === "localNetwork")?.settingsUrl, - "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?privacy-localnetwork", - ); - assert.equal( - status.permissionSetup.actions.find((action) => action.key === "inputMonitoring")?.settingsUrl, - "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ListenEvent", - ); assert.equal( status.permissionSetup.actions.find((action) => action.key === "screenRecording")?.settingsUrl, "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?Privacy_ScreenCapture", @@ -82,12 +77,7 @@ test("boss-agent status exposes unbound QR binding and local permission states", ["screenRecording", "missing"], ], ); - assert.deepEqual( - status.permissions.extendedItems.slice(0, 1).map((item) => [item.key, item.status]), - [ - ["automation", "unknown"], - ], - ); + assert.deepEqual(status.permissions.extendedItems, []); }); test("boss-agent status treats token-backed devices as bound and renders enterprise UI", () => { @@ -186,10 +176,9 @@ test("boss-agent permission and skill menu entries render as separate tab pages" assert.match(permissionsHtml, /class="active" href="\/boss-agent\?tab=permissions"/); assert.match(permissionsHtml, /

基础桌面控制授权<\/h2>/); assert.match(permissionsHtml, /基础桌面控制已可用/); - assert.match(permissionsHtml, /扩展权限不会阻塞接管/); assert.match(permissionsHtml, /后续静默使用/); assert.match(permissionsHtml, /api\/v1\/boss-agent\/permissions\/open\?target=core&returnTab=permissions/); - assert.match(permissionsHtml, /api\/v1\/boss-agent\/permissions\/open\?target=fullDiskAccess&returnTab=permissions/); + assert.doesNotMatch(permissionsHtml, /fullDiskAccess|inputMonitoring|automation|microphone|camera|localNetwork|notifications/); assert.doesNotMatch(permissionsHtml, /

Skill 部署情况<\/h2>/); const skillsHtml = renderBossAgentHtml(status, { activeTab: "skills" }); @@ -199,7 +188,7 @@ test("boss-agent permission and skill menu entries render as separate tab pages" assert.doesNotMatch(skillsHtml, /

基础桌面控制授权<\/h2>/); }); -test("boss-agent treats accessibility and screen recording as the minimal computer-use permission set", () => { +test("boss-agent only exposes accessibility and screen recording permission requests", () => { const status = buildBossAgentStatus( { deviceId: "macbook-air", @@ -228,16 +217,20 @@ test("boss-agent treats accessibility and screen recording as the minimal comput assert.equal(status.permissionSetup.silentUseReady, true); assert.equal(status.permissionSetup.primaryAction.href, "/api/v1/boss-agent/permissions/open?target=core&returnTab=permissions"); assert.match(status.permissionReadiness.summary, /基础桌面控制已可用/); - assert.equal(status.permissionSetup.actions.find((action) => action.key === "automation")?.requiredForSilentControl, false); - assert.equal(status.permissionSetup.actions.find((action) => action.key === "fullDiskAccess")?.requiredForSilentControl, false); assert.deepEqual(status.permissionSetup.missingRequiredKeys, []); assert.deepEqual( status.permissions.items.map((item) => item.key), ["accessibility", "screenRecording"], ); + assert.deepEqual(status.permissions.extendedItems, []); + assert.deepEqual( + status.permissionSetup.actions.map((action) => action.key), + ["accessibility", "screenRecording"], + ); + assert.deepEqual(status.permissionSetup.optionalMissingKeys, []); }); -test("boss-agent native permission overrides update app-owned camera and microphone state", () => { +test("boss-agent native permission overrides only update core desktop-control permissions", () => { const merged = mergeBossAgentNativePermissionOverrides( { accessibility: "missing", @@ -258,10 +251,6 @@ test("boss-agent native permission overrides update app-owned camera and microph assert.deepEqual(merged, { accessibility: "granted", screenRecording: "granted", - automation: "granted", - camera: "granted", - microphone: "missing", - inputMonitoring: "granted", }); }); @@ -303,41 +292,18 @@ test("boss-agent mac app intercepts permission links and triggers native app per assert.match(swiftSource, /lastPermissionRequestTarget/); assert.match(swiftSource, /AXIsProcessTrustedWithOptions/); assert.match(swiftSource, /CGRequestScreenCaptureAccess/); - assert.match(swiftSource, /AVCaptureDevice\.requestAccess\(for: \.audio/); - assert.match(swiftSource, /AVCaptureDevice\.requestAccess\(for: \.video/); - assert.match(swiftSource, /UNUserNotificationCenter\.current\(\)\.requestAuthorization/); - assert.match(swiftSource, /import IOKit\.hid/); - assert.match(swiftSource, /CGRequestListenEventAccess\(\)/); - assert.match(swiftSource, /CGPreflightListenEventAccess\(\)/); - assert.match(swiftSource, /NSEvent\.addGlobalMonitorForEvents\(matching: \[\.keyDown\]\)/); - assert.match(swiftSource, /CGEventSource\.keyState\(\.combinedSessionState/); - assert.match(swiftSource, /IOHIDManagerOpen\(manager/); - assert.match(swiftSource, /kHIDUsage_GD_Keyboard/); - assert.match(swiftSource, /IOHIDRequestAccess\(kIOHIDRequestTypeListenEvent\)/); - assert.match(swiftSource, /IOHIDCheckAccess\(kIOHIDRequestTypeListenEvent\)/); - assert.match(swiftSource, /CGEvent\.tapCreate/); + assert.doesNotMatch(swiftSource, /AVCaptureDevice\.requestAccess|UNUserNotificationCenter|import IOKit\.hid|CGRequestListenEventAccess|IOHIDRequestAccess|CGEvent\.tapCreate|NWBrowser/); assert.match(swiftSource, /NSWorkspace\.shared\.open/); assert.match(swiftSource, /deadline: \.now\(\) \+ 0\.45/); assert.match(swiftSource, /loadAgentPanel\(tab:/); assert.match(swiftSource, /returnTab/); assert.match(swiftSource, /nativePermissionQueryItems/); - assert.match(swiftSource, /nativeCamera/); - assert.match(swiftSource, /nativeMicrophone/); - assert.match(swiftSource, /nativeInputMonitoring/); - assert.match(swiftSource, /privacy-localnetwork/); - assert.match(swiftSource, /com\.apple\.settings\.PrivacySecurity\.extension\?Privacy_ListenEvent/); + assert.doesNotMatch(swiftSource, /nativeCamera|nativeMicrophone|nativeInputMonitoring|privacy-localnetwork|Privacy_ListenEvent/); assert.doesNotMatch(swiftSource, /com\.apple\.preference\.security\?Privacy_ListenEvent/); 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/); - assert.match(buildScript, /NSMicrophoneUsageDescription/); - assert.match(buildScript, /NSCameraUsageDescription/); - assert.match(buildScript, /NSAppleEventsUsageDescription/); - assert.match(buildScript, /NSLocalNetworkUsageDescription/); - assert.match(buildScript, /NSInputMonitoringUsageDescription/); - assert.match(buildScript, /framework IOKit/); + assert.doesNotMatch(buildScript, /NSMicrophoneUsageDescription|NSCameraUsageDescription|NSAppleEventsUsageDescription|NSLocalNetworkUsageDescription|NSInputMonitoringUsageDescription|framework IOKit|framework Network|framework UserNotifications|framework AVFoundation/); assert.match(buildScript, /CFBundleURLTypes/); assert.match(buildScript, /boss-agent/); assert.match(buildScript, /CFBundleIconFile/);