#!/bin/sh # Builds padmon and (re)loads the launchd agent that runs `padmon daemon` at login. # The agent runs ~/Applications/padmon.app (an ad-hoc-signed bundle, so macOS can grant # it Bluetooth permission). The Claude Code hooks and the statusline use target/release/padmon. set -eu repo=$(cd "$(dirname "$0")/.." && pwd) bin="$repo/target/release/padmon" label=dev.padmon.daemon plist="$HOME/Library/LaunchAgents/$label.plist" log="$HOME/Library/Logs/padmon.log" domain="gui/$(id -u)" cd "$repo" devenv shell -q --no-tui -- cargo build --release for lib in $(otool -L "$bin" | awk '/\/nix\/store\/.*libiconv/ {print $1}'); do install_name_tool -change "$lib" /usr/lib/libiconv.2.dylib "$bin" 2>/dev/null done codesign -s - --force "$bin" 2>/dev/null mkdir -p "$HOME/Library/LaunchAgents" "$HOME/Library/Logs" app="$HOME/Applications/padmon.app" exe="$app/Contents/MacOS/padmon" info="$app/Contents/Info.plist" stamp="$repo/target/padmon.app.stamp" new_info=$(cat <<'EOF' CFBundleIdentifierdev.padmon.daemon CFBundleNamepadmon CFBundleExecutablepadmon CFBundlePackageTypeAPPL CFBundleVersion1 LSUIElement NSBluetoothAlwaysUsageDescriptionpadmon writes session colors to the JP-Mini pad controller over Bluetooth. EOF ) launchctl bootout "$domain/$label" 2>/dev/null || true # Replace and re-sign only when the build or Info.plist changed: a new signature makes macOS ask for Bluetooth permission again. want=$({ shasum -a 256 < "$bin"; printf '%s\n' "$new_info" | shasum -a 256; echo system-libiconv; }) if [ ! -x "$exe" ] || [ "$(cat "$stamp" 2>/dev/null)" != "$want" ]; then mkdir -p "$app/Contents/MacOS" cp "$bin" "$exe.new" # The Nix toolchain links libiconv from /nix/store. At login, launchd can start the daemon # before the Nix volume is mounted, so use the system copy (in the dyld shared cache). for lib in $(otool -L "$exe.new" | awk '/\/nix\/store\/.*libiconv/ {print $1}'); do install_name_tool -change "$lib" /usr/lib/libiconv.2.dylib "$exe.new" 2>/dev/null done if otool -L "$exe.new" | grep -q /nix/store; then echo "warning: padmon still uses a library from /nix/store; the daemon may fail at login:" >&2 otool -L "$exe.new" | grep /nix/store >&2 fi mv -f "$exe.new" "$exe" # new inode: never modify a mapped signed binary printf '%s\n' "$new_info" > "$info" codesign -s - --force "$app" printf '%s\n' "$want" > "$stamp" echo "padmon.app updated. macOS asks once for Bluetooth permission when the controller is next used over Bluetooth." fi cat > "$plist" < Label$label ProgramArguments $exedaemon RunAtLoad KeepAlive ThrottleInterval5 ProcessTypeInteractive StandardErrorPath$log EOF launchctl bootout "$domain/$label" 2>/dev/null || true sleep 1 # bootstrap right after bootout often fails with "5: Input/output error" launchctl bootstrap "$domain" "$plist" || { sleep 2; launchctl bootstrap "$domain" "$plist"; } echo "padmon daemon loaded ($label). Log: $log"