From e2ad0ed9f7368135103a43f2f2b4c2924fd3ffcc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 12 Dec 2025 19:20:47 +0000 Subject: [PATCH 1/3] fix(mac): disable restricted time-sensitive entitlement --- scripts/codesign-mac-app.sh | 29 ++++------------------------- scripts/restart-mac.sh | 11 ++++++++++- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/scripts/codesign-mac-app.sh b/scripts/codesign-mac-app.sh index 3a124d9f8..2276aa514 100755 --- a/scripts/codesign-mac-app.sh +++ b/scripts/codesign-mac-app.sh @@ -51,14 +51,8 @@ cat > "$ENT_TMP_BASE" <<'PLIST' - com.apple.security.hardened-runtime - - com.apple.security.cs.allow-jit - com.apple.security.automation.apple-events - com.apple.security.device.audio-input - PLIST @@ -68,14 +62,8 @@ cat > "$ENT_TMP_APP_BASE" <<'PLIST' - com.apple.security.hardened-runtime - - com.apple.security.cs.allow-jit - com.apple.security.automation.apple-events - com.apple.security.device.audio-input - PLIST @@ -87,29 +75,20 @@ cat > "$ENT_TMP_APP" <<'PLIST' com.apple.developer.usernotifications.time-sensitive - com.apple.security.hardened-runtime - - com.apple.security.cs.allow-jit - com.apple.security.automation.apple-events - com.apple.security.device.audio-input - PLIST -# The time-sensitive entitlement is restricted and needs to be present in a -# matching provisioning profile when using Apple Development signing. -# Avoid breaking local debug builds by only enabling it when forced, or when -# using distribution-style identities. +# The time-sensitive entitlement is restricted and requires explicit enablement +# (and typically a matching provisioning profile). It is *not* safe to enable +# unconditionally for local debug packaging since AMFI will refuse to launch. APP_ENTITLEMENTS="$ENT_TMP_APP_BASE" if [[ "${ENABLE_TIME_SENSITIVE_NOTIFICATIONS:-}" == "1" ]]; then APP_ENTITLEMENTS="$ENT_TMP_APP" -elif [[ "$IDENTITY" == *"Developer ID Application"* ]] || [[ "$IDENTITY" == *"Apple Distribution"* ]]; then - APP_ENTITLEMENTS="$ENT_TMP_APP" else - echo "Note: Time Sensitive Notifications entitlement disabled for this signing identity." + echo "Note: Time Sensitive Notifications entitlement disabled." echo " To force it: ENABLE_TIME_SENSITIVE_NOTIFICATIONS=1 scripts/codesign-mac-app.sh " fi diff --git a/scripts/restart-mac.sh b/scripts/restart-mac.sh index ed8a4285c..6cfd3c404 100755 --- a/scripts/restart-mac.sh +++ b/scripts/restart-mac.sh @@ -86,7 +86,16 @@ choose_app_bundle() { choose_app_bundle # 4) Launch the installed app in the foreground so the menu bar extra appears. -run_step "launch app" open "${APP_BUNDLE}" +# LaunchServices can inherit a huge environment from this shell (secrets, prompt vars, etc.). +# That can cause launchd spawn failures and is undesirable for a GUI app anyway. +run_step "launch app" env -i \ + HOME="${HOME}" \ + USER="${USER:-$(id -un)}" \ + LOGNAME="${LOGNAME:-$(id -un)}" \ + TMPDIR="${TMPDIR:-/tmp}" \ + PATH="/usr/bin:/bin:/usr/sbin:/sbin" \ + LANG="${LANG:-en_US.UTF-8}" \ + /usr/bin/open "${APP_BUNDLE}" # 5) Verify the app is alive. sleep 1.5 From 3f7fcad9acfaedc7aeca751ad493482ef1641d06 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 12 Dec 2025 19:20:47 +0000 Subject: [PATCH 2/3] fix(mac): ignore cancelled webchat navigations --- apps/macos/Sources/Clawdis/WebChatWindow.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/macos/Sources/Clawdis/WebChatWindow.swift b/apps/macos/Sources/Clawdis/WebChatWindow.swift index 79a90ffe3..5394fcc17 100644 --- a/apps/macos/Sources/Clawdis/WebChatWindow.swift +++ b/apps/macos/Sources/Clawdis/WebChatWindow.swift @@ -388,11 +388,19 @@ final class WebChatWindowController: NSWindowController, WKNavigationDelegate, N } func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + if Self.shouldIgnoreNavigationError(error) { + webChatLogger.debug("webchat navigation cancelled (provisional)") + return + } webChatLogger.error("webchat navigation failed (provisional): \(error.localizedDescription, privacy: .public)") self.showError(error.localizedDescription) } func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + if Self.shouldIgnoreNavigationError(error) { + webChatLogger.debug("webchat navigation cancelled") + return + } webChatLogger.error("webchat navigation failed: \(error.localizedDescription, privacy: .public)") self.showError(error.localizedDescription) } @@ -483,6 +491,11 @@ final class WebChatWindowController: NSWindowController, WKNavigationDelegate, N if let url = Bundle.module.url(forResource: "WebChat", withExtension: nil) { return url } throw NSError(domain: "WebChat", code: 10, userInfo: [NSLocalizedDescriptionKey: "WebChat assets missing"]) } + + private static func shouldIgnoreNavigationError(_ error: Error) -> Bool { + let ns = error as NSError + return ns.domain == NSURLErrorDomain && ns.code == NSURLErrorCancelled + } } extension WebChatWindowController { From bf41197b97fc2f03dac3074dad97835a6754e6c4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 12 Dec 2025 19:25:21 +0000 Subject: [PATCH 3/3] fix(mac): open settings for microphone permission --- .../Sources/Clawdis/PermissionManager.swift | 42 ++++++++++++++++--- scripts/codesign-mac-app.sh | 6 +++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/apps/macos/Sources/Clawdis/PermissionManager.swift b/apps/macos/Sources/Clawdis/PermissionManager.swift index 7017a10ca..517825aeb 100644 --- a/apps/macos/Sources/Clawdis/PermissionManager.swift +++ b/apps/macos/Sources/Clawdis/PermissionManager.swift @@ -67,12 +67,27 @@ enum PermissionManager { results[cap] = ScreenRecordingProbe.isAuthorized() case .microphone: - let granted = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized - if interactive, !granted { - let ok = await AVCaptureDevice.requestAccess(for: .audio) - results[cap] = ok - } else { - results[cap] = granted + let status = AVCaptureDevice.authorizationStatus(for: .audio) + switch status { + case .authorized: + results[cap] = true + + case .notDetermined: + if interactive { + let ok = await AVCaptureDevice.requestAccess(for: .audio) + results[cap] = ok + } else { + results[cap] = false + } + + case .denied, .restricted: + results[cap] = false + if interactive { + MicrophonePermissionHelper.openSettings() + } + + @unknown default: + results[cap] = false } case .speechRecognition: @@ -150,6 +165,21 @@ enum NotificationPermissionHelper { } } +enum MicrophonePermissionHelper { + static func openSettings() { + let candidates = [ + "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone", + "x-apple.systempreferences:com.apple.preference.security", + ] + + for candidate in candidates { + if let url = URL(string: candidate), NSWorkspace.shared.open(url) { + return + } + } + } +} + enum AppleScriptPermission { private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "AppleScriptPermission") diff --git a/scripts/codesign-mac-app.sh b/scripts/codesign-mac-app.sh index 2276aa514..ca9ca0e93 100755 --- a/scripts/codesign-mac-app.sh +++ b/scripts/codesign-mac-app.sh @@ -53,6 +53,8 @@ cat > "$ENT_TMP_BASE" <<'PLIST' com.apple.security.automation.apple-events + com.apple.security.device.audio-input + PLIST @@ -64,6 +66,8 @@ cat > "$ENT_TMP_APP_BASE" <<'PLIST' com.apple.security.automation.apple-events + com.apple.security.device.audio-input + PLIST @@ -77,6 +81,8 @@ cat > "$ENT_TMP_APP" <<'PLIST' com.apple.security.automation.apple-events + com.apple.security.device.audio-input + PLIST