fix(mac): treat timeSensitive as best-effort
This commit is contained in:
@@ -1,9 +1,17 @@
|
|||||||
import ClawdisIPC
|
import ClawdisIPC
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Security
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct NotificationManager {
|
struct NotificationManager {
|
||||||
|
private static let hasTimeSensitiveEntitlement: Bool = {
|
||||||
|
guard let task = SecTaskCreateFromSelf(nil) else { return false }
|
||||||
|
let key = "com.apple.developer.usernotifications.time-sensitive" as CFString
|
||||||
|
guard let val = SecTaskCopyValueForEntitlement(task, key, nil) else { return false }
|
||||||
|
return (val as? Bool) == true
|
||||||
|
}()
|
||||||
|
|
||||||
func send(title: String, body: String, sound: String?, priority: NotificationPriority? = nil) async -> Bool {
|
func send(title: String, body: String, sound: String?, priority: NotificationPriority? = nil) async -> Bool {
|
||||||
let center = UNUserNotificationCenter.current()
|
let center = UNUserNotificationCenter.current()
|
||||||
let status = await center.notificationSettings()
|
let status = await center.notificationSettings()
|
||||||
@@ -29,7 +37,7 @@ struct NotificationManager {
|
|||||||
case .active:
|
case .active:
|
||||||
content.interruptionLevel = .active
|
content.interruptionLevel = .active
|
||||||
case .timeSensitive:
|
case .timeSensitive:
|
||||||
content.interruptionLevel = .timeSensitive
|
content.interruptionLevel = Self.hasTimeSensitiveEntitlement ? .timeSensitive : .active
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ struct Response { ok: Bool; message?: String; payload?: Data }
|
|||||||
- `run -- cmd args... [--cwd] [--env KEY=VAL] [--timeout 30] [--needs-screen-recording]`
|
- `run -- cmd args... [--cwd] [--env KEY=VAL] [--timeout 30] [--needs-screen-recording]`
|
||||||
- `status`
|
- `status`
|
||||||
- Sounds: supply any macOS alert name with `--sound` per notification; omit the flag to use the system default. There is no longer a persisted “default sound” in the app UI.
|
- Sounds: supply any macOS alert name with `--sound` per notification; omit the flag to use the system default. There is no longer a persisted “default sound” in the app UI.
|
||||||
|
- Priority: `timeSensitive` is best-effort and falls back to `active` unless the app is signed with the Time Sensitive Notifications entitlement.
|
||||||
- Internals: builds Request, connects via AsyncXPCConnection, prints Response as JSON to stdout.
|
- Internals: builds Request, connects via AsyncXPCConnection, prints Response as JSON to stdout.
|
||||||
|
|
||||||
## Integration with clawdis/Clawdis (Node/TS)
|
## Integration with clawdis/Clawdis (Node/TS)
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ set -euo pipefail
|
|||||||
|
|
||||||
APP_BUNDLE="${1:-dist/Clawdis.app}"
|
APP_BUNDLE="${1:-dist/Clawdis.app}"
|
||||||
IDENTITY="${SIGN_IDENTITY:-}"
|
IDENTITY="${SIGN_IDENTITY:-}"
|
||||||
ENT_TMP=$(mktemp -t clawdis-entitlements)
|
ENT_TMP_BASE=$(mktemp -t clawdis-entitlements-base)
|
||||||
|
ENT_TMP_APP=$(mktemp -t clawdis-entitlements-app)
|
||||||
|
ENT_TMP_APP_BASE=$(mktemp -t clawdis-entitlements-app-base)
|
||||||
|
|
||||||
if [ ! -d "$APP_BUNDLE" ]; then
|
if [ ! -d "$APP_BUNDLE" ]; then
|
||||||
echo "App bundle not found: $APP_BUNDLE" >&2
|
echo "App bundle not found: $APP_BUNDLE" >&2
|
||||||
@@ -44,7 +46,41 @@ fi
|
|||||||
|
|
||||||
echo "Using signing identity: $IDENTITY"
|
echo "Using signing identity: $IDENTITY"
|
||||||
|
|
||||||
cat > "$ENT_TMP" <<'PLIST'
|
cat > "$ENT_TMP_BASE" <<'PLIST'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.hardened-runtime</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
PLIST
|
||||||
|
|
||||||
|
cat > "$ENT_TMP_APP_BASE" <<'PLIST'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.hardened-runtime</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.automation.apple-events</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
PLIST
|
||||||
|
|
||||||
|
cat > "$ENT_TMP_APP" <<'PLIST'
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
@@ -63,12 +99,27 @@ cat > "$ENT_TMP" <<'PLIST'
|
|||||||
</plist>
|
</plist>
|
||||||
PLIST
|
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.
|
||||||
|
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 " To force it: ENABLE_TIME_SENSITIVE_NOTIFICATIONS=1 scripts/codesign-mac-app.sh <app>"
|
||||||
|
fi
|
||||||
|
|
||||||
# clear extended attributes to avoid stale signatures
|
# clear extended attributes to avoid stale signatures
|
||||||
xattr -cr "$APP_BUNDLE" 2>/dev/null || true
|
xattr -cr "$APP_BUNDLE" 2>/dev/null || true
|
||||||
|
|
||||||
sign_item() {
|
sign_item() {
|
||||||
local target="$1"
|
local target="$1"
|
||||||
codesign --force --options runtime --timestamp=none --entitlements "$ENT_TMP" --sign "$IDENTITY" "$target"
|
local entitlements="$2"
|
||||||
|
codesign --force --options runtime --timestamp=none --entitlements "$entitlements" --sign "$IDENTITY" "$target"
|
||||||
}
|
}
|
||||||
|
|
||||||
sign_plain_item() {
|
sign_plain_item() {
|
||||||
@@ -78,16 +129,16 @@ sign_plain_item() {
|
|||||||
|
|
||||||
# Sign main binary and CLI helper if present
|
# Sign main binary and CLI helper if present
|
||||||
if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdis" ]; then
|
if [ -f "$APP_BUNDLE/Contents/MacOS/Clawdis" ]; then
|
||||||
echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdis"
|
echo "Signing main binary"; sign_item "$APP_BUNDLE/Contents/MacOS/Clawdis" "$APP_ENTITLEMENTS"
|
||||||
fi
|
fi
|
||||||
if [ -f "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" ]; then
|
if [ -f "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" ]; then
|
||||||
echo "Signing CLI helper"; sign_item "$APP_BUNDLE/Contents/MacOS/ClawdisCLI"
|
echo "Signing CLI helper"; sign_item "$APP_BUNDLE/Contents/MacOS/ClawdisCLI" "$ENT_TMP_BASE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Sign bundled gateway payload (native addons, libvips dylibs)
|
# Sign bundled gateway payload (native addons, libvips dylibs)
|
||||||
if [ -d "$APP_BUNDLE/Contents/Resources/Relay" ]; then
|
if [ -d "$APP_BUNDLE/Contents/Resources/Relay" ]; then
|
||||||
find "$APP_BUNDLE/Contents/Resources/Relay" -type f \( -name "*.node" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
|
find "$APP_BUNDLE/Contents/Resources/Relay" -type f \( -name "*.node" -o -name "*.dylib" \) -print0 | while IFS= read -r -d '' f; do
|
||||||
echo "Signing gateway payload: $f"; sign_item "$f"
|
echo "Signing gateway payload: $f"; sign_item "$f" "$ENT_TMP_BASE"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -115,7 +166,7 @@ if [ -d "$APP_BUNDLE/Contents/Frameworks" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Finally sign the bundle
|
# Finally sign the bundle
|
||||||
sign_item "$APP_BUNDLE"
|
sign_item "$APP_BUNDLE" "$APP_ENTITLEMENTS"
|
||||||
|
|
||||||
rm -f "$ENT_TMP"
|
rm -f "$ENT_TMP_BASE" "$ENT_TMP_APP_BASE" "$ENT_TMP_APP"
|
||||||
echo "Codesign complete for $APP_BUNDLE"
|
echo "Codesign complete for $APP_BUNDLE"
|
||||||
|
|||||||
Reference in New Issue
Block a user