Last active
October 7, 2025 19:28
-
-
Save plasmadice/8def4891bc3ee4148926c0337be707c4 to your computer and use it in GitHub Desktop.
Control Kanata LaunchDaemon with Raycast
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Required parameters: | |
| # @raycast.schemaVersion 1 | |
| # @raycast.title Install/Restart Karabiner & Kanata | |
| # @raycast.mode fullOutput | |
| # Optional parameters: | |
| # @raycast.icon πΉ | |
| # Documentation: | |
| # @raycast.author plasmadice | |
| # @raycast.authorURL https://github.com/plasmadice | |
| # Source: | |
| # https://gist.github.com/Jaycedam/4db80fc49c1d23c76c90c9b3e653c07f | |
| # Installs or restarts Karabiner DriverKit, Kanata, and sets up LaunchDaemons | |
| set -euo pipefail | |
| # Enhanced error handling and debugging | |
| debug() { | |
| echo "π DEBUG: $1" >&2 | |
| } | |
| error_exit() { | |
| echo "β ERROR: $1" >&2 | |
| exit 1 | |
| } | |
| success() { | |
| echo "β $1" | |
| } | |
| # Retrieve password from keychain https://scriptingosx.com/2021/04/get-password-from-keychain-in-shell-scripts/ | |
| # Added with security add-generic-password -s 'kanata' -a 'myUser' -w 'myPassword' | |
| # Retrieve password with security find-generic-password -w -s 'kanata' -a 'myUser' | |
| # Deleted with security delete-generic-password -s 'kanata' -a 'myUser' | |
| # Retrieve password from keychain | |
| debug "Starting password retrieval from keychain" | |
| pw_name="supa" # name of the password in the keychain | |
| pw_account=$(id -un) # current username e.g. "viper" | |
| debug "Looking for password with name: $pw_name, account: $pw_account" | |
| if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account" 2>&1); then | |
| error_exit "Could not get password (error $?)" | |
| echo "Please add your password to keychain with:" | |
| echo "security add-generic-password -s 'supa' -a '$(id -un)' -w 'your_password'" | |
| exit 1 | |
| fi | |
| debug "Password retrieved successfully" | |
| #### CONFIGURATION #### | |
| KANATA_CONFIG="${HOME}/.config/kanata/kanata.kbd" | |
| KANATA_PORT=10000 | |
| PLIST_DIR="/Library/LaunchDaemons" | |
| ################################### | |
| # 1. Install Kanata via Homebrew if not present | |
| debug "Checking if Kanata is installed via Homebrew" | |
| if ! brew list kanata >/dev/null 2>&1; then | |
| debug "Kanata not found, installing via Homebrew" | |
| if ! brew install kanata; then | |
| error_exit "Failed to install Kanata via Homebrew" | |
| fi | |
| success "Kanata installed successfully" | |
| else | |
| debug "Kanata already installed" | |
| fi | |
| # Find Kanata binary - try multiple locations | |
| KANATA_BIN="" | |
| if command -v kanata >/dev/null 2>&1; then | |
| KANATA_BIN=$(command -v kanata) | |
| elif [ -f "/opt/homebrew/bin/kanata" ]; then | |
| KANATA_BIN="/opt/homebrew/bin/kanata" | |
| elif [ -f "/usr/local/bin/kanata" ]; then | |
| KANATA_BIN="/usr/local/bin/kanata" | |
| else | |
| # Try to find in Homebrew Cellar directories | |
| KANATA_BIN=$(find /usr/local/Cellar/kanata -name "kanata" -type f 2>/dev/null | head -1) | |
| if [ -z "$KANATA_BIN" ]; then | |
| KANATA_BIN=$(find /opt/homebrew/Cellar/kanata -name "kanata" -type f 2>/dev/null | head -1) | |
| fi | |
| if [ -z "$KANATA_BIN" ]; then | |
| error_exit "Kanata binary not found in expected locations" | |
| fi | |
| fi | |
| debug "Kanata binary found at: $KANATA_BIN" | |
| # 2. Fetch & install latest Karabiner DriverKit pkg | |
| debug "Fetching latest Karabiner DriverKit download URL" | |
| if ! DRIVERKIT_PKG_URL=$(curl -s "https://api.github.com/repos/pqrs-org/Karabiner-DriverKit-VirtualHIDDevice/releases/latest" | jq -r '.assets[] | select(.name|endswith(".pkg")) | .browser_download_url'); then | |
| error_exit "Failed to fetch Karabiner DriverKit URL" | |
| fi | |
| debug "Download URL: $DRIVERKIT_PKG_URL" | |
| debug "Downloading Karabiner DriverKit package" | |
| if ! curl -L -o /tmp/karabiner-driverkit.pkg "$DRIVERKIT_PKG_URL"; then | |
| error_exit "Failed to download Karabiner DriverKit package" | |
| fi | |
| success "Karabiner DriverKit package downloaded" | |
| debug "Installing Karabiner DriverKit package" | |
| if ! echo "$cli_password" | sudo -S installer -pkg /tmp/karabiner-driverkit.pkg -target /; then | |
| error_exit "Failed to install Karabiner DriverKit package" | |
| fi | |
| success "Karabiner DriverKit installed successfully" | |
| rm -f /tmp/karabiner-driverkit.pkg | |
| # 3. Write plist files | |
| debug "Creating LaunchDaemon plist files" | |
| debug "Target directory: $PLIST_DIR" | |
| # Kanata plist | |
| debug "Creating Kanata plist file" | |
| if ! echo "$cli_password" | sudo -S tee "${PLIST_DIR}/com.example.kanata.plist" >/dev/null <<EOF | |
| <?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>Label</key><string>com.example.kanata</string> | |
| <key>ProgramArguments</key><array> | |
| <string>${KANATA_BIN}</string> | |
| <string>--nodelay</string> | |
| <string>-c</string><string>${KANATA_CONFIG}</string> | |
| <string>--port</string><string>${KANATA_PORT}</string> | |
| </array> | |
| <key>RunAtLoad</key><true/> | |
| <key>KeepAlive</key><true/> | |
| <key>StandardOutPath</key> | |
| <string>/Library/Logs/Kanata/kanata.out.log</string> | |
| <key>StandardErrorPath</key> | |
| <string>/Library/Logs/Kanata/kanata.err.log</string> | |
| </dict></plist> | |
| EOF | |
| then | |
| error_exit "Failed to create Kanata plist file" | |
| fi | |
| success "Kanata plist file created" | |
| debug "Setting ownership and permissions for Kanata plist" | |
| if ! echo "$cli_password" | sudo -S chown root:wheel "${PLIST_DIR}/com.example.kanata.plist"; then | |
| error_exit "Failed to set ownership for Kanata plist" | |
| fi | |
| if ! echo "$cli_password" | sudo -S chmod 644 "${PLIST_DIR}/com.example.kanata.plist"; then | |
| error_exit "Failed to set permissions for Kanata plist" | |
| fi | |
| success "Kanata plist permissions set" | |
| # Karabiner daemon plist | |
| debug "Creating Karabiner daemon plist file" | |
| if ! echo "$cli_password" | sudo -S tee "${PLIST_DIR}/com.example.karabiner-vhiddaemon.plist" >/dev/null <<EOF | |
| <?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>Label</key><string>com.example.karabiner-vhiddaemon</string> | |
| <key>ProgramArguments</key><array> | |
| <string>/Library/Application Support/org.pqrs/Karabiner-DriverKit-VirtualHIDDevice/Applications/Karabiner-VirtualHIDDevice-Daemon.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Daemon</string> | |
| </array> | |
| <key>RunAtLoad</key><true/> | |
| <key>KeepAlive</key><true/> | |
| </dict></plist> | |
| EOF | |
| then | |
| error_exit "Failed to create Karabiner daemon plist file" | |
| fi | |
| success "Karabiner daemon plist file created" | |
| debug "Setting ownership and permissions for Karabiner daemon plist" | |
| if ! echo "$cli_password" | sudo -S chown root:wheel "${PLIST_DIR}/com.example.karabiner-vhiddaemon.plist"; then | |
| error_exit "Failed to set ownership for Karabiner daemon plist" | |
| fi | |
| if ! echo "$cli_password" | sudo -S chmod 644 "${PLIST_DIR}/com.example.karabiner-vhiddaemon.plist"; then | |
| error_exit "Failed to set permissions for Karabiner daemon plist" | |
| fi | |
| success "Karabiner daemon plist permissions set" | |
| # Note: Karabiner manager is started manually in foreground for permissions | |
| # 4. Create log directory | |
| debug "Creating log directory" | |
| if ! echo "$cli_password" | sudo -S mkdir -p /Library/Logs/Kanata; then | |
| error_exit "Failed to create log directory" | |
| fi | |
| if ! echo "$cli_password" | sudo -S chown root:wheel /Library/Logs/Kanata; then | |
| error_exit "Failed to set ownership for log directory" | |
| fi | |
| success "Log directory created and configured" | |
| # 5. Stop existing services | |
| debug "Stopping existing services" | |
| echo "$cli_password" | sudo -S launchctl bootout system "${PLIST_DIR}/com.example.kanata.plist" 2>/dev/null || debug "Kanata service not running or already stopped" | |
| echo "$cli_password" | sudo -S launchctl bootout system "${PLIST_DIR}/com.example.karabiner-vhiddaemon.plist" 2>/dev/null || debug "Karabiner daemon service not running or already stopped" | |
| success "Existing services stopped" | |
| # 6. Start services | |
| debug "Starting services" | |
| # First, start Karabiner VirtualHIDDevice Manager in foreground to request permissions | |
| debug "Starting Karabiner VirtualHIDDevice Manager (foreground for permissions)" | |
| if [ -f "/Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager" ]; then | |
| # Kill any existing manager processes | |
| debug "Stopping any existing Karabiner VirtualHIDDevice Manager processes" | |
| pkill -f "Karabiner-VirtualHIDDevice-Manager" 2>/dev/null || debug "No existing manager processes found" | |
| echo "π Starting Karabiner VirtualHIDDevice Manager..." | |
| echo "β οΈ Please grant the necessary permissions when prompted by macOS" | |
| if ! /Applications/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager activate; then | |
| error_exit "Failed to start Karabiner VirtualHIDDevice Manager" | |
| fi | |
| success "Karabiner VirtualHIDDevice Manager started and permissions granted" | |
| else | |
| error_exit "Karabiner VirtualHIDDevice Manager not found at expected location" | |
| fi | |
| # Now start the other services | |
| debug "Starting Kanata service" | |
| if ! echo "$cli_password" | sudo -S launchctl bootstrap system "${PLIST_DIR}/com.example.kanata.plist"; then | |
| error_exit "Failed to bootstrap Kanata service" | |
| fi | |
| if ! echo "$cli_password" | sudo -S launchctl enable system/com.example.kanata; then | |
| error_exit "Failed to enable Kanata service" | |
| fi | |
| success "Kanata service started and enabled" | |
| debug "Starting Karabiner daemon service" | |
| if ! echo "$cli_password" | sudo -S launchctl bootstrap system "${PLIST_DIR}/com.example.karabiner-vhiddaemon.plist"; then | |
| error_exit "Failed to bootstrap Karabiner daemon service" | |
| fi | |
| if ! echo "$cli_password" | sudo -S launchctl enable system/com.example.karabiner-vhiddaemon; then | |
| error_exit "Failed to enable Karabiner daemon service" | |
| fi | |
| success "Karabiner daemon service started and enabled" | |
| success "Kanata and Karabiner services installed/restarted successfully!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Required parameters: | |
| # @raycast.schemaVersion 1 | |
| # @raycast.title Restart Kanata | |
| # @raycast.mode inline | |
| # Optional parameters: | |
| # @raycast.icon π€ | |
| # Documentation: | |
| # @raycast.author plasmadice | |
| # @raycast.authorURL https://github.com/plasmadice | |
| # Name of the password in the keychain | |
| pw_name="supa" | |
| pw_account=$(id -un) | |
| if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then | |
| echo "β Could not get password (error $?)" | |
| exit 1 | |
| fi | |
| # Stop Kanata service | |
| echo "$cli_password" | sudo -S launchctl bootout system /Library/LaunchDaemons/com.example.kanata.plist 2>/dev/null || true | |
| # Start Kanata service | |
| error_output=$(echo "$cli_password" | sudo -S launchctl bootstrap system /Library/LaunchDaemons/com.example.kanata.plist 2>&1) | |
| exit_code=$? | |
| if [ $exit_code -eq 0 ]; then | |
| echo "β Kanata restarted successfully!" | |
| elif echo "$error_output" | grep -q "Already loaded"; then | |
| echo "β Kanata restarted successfully!" | |
| else | |
| echo "β Failed to restart Kanata:" | |
| echo "$error_output" | |
| exit 1 | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Required parameters: | |
| # @raycast.schemaVersion 1 | |
| # @raycast.title Start Kanata | |
| # @raycast.mode inline | |
| # Optional parameters: | |
| # @raycast.icon π€ | |
| # Documentation: | |
| # @raycast.author plasmadice | |
| # @raycast.authorURL https://github.com/plasmadice | |
| # Name of the password in the keychain | |
| pw_name="supa" | |
| pw_account=$(id -un) | |
| if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then | |
| echo "β Could not get password (error $?)" | |
| exit 1 | |
| fi | |
| # Check if Kanata is already running | |
| if ps aux | grep -E "[k]anata" > /dev/null; then | |
| echo "β οΈ Kanata is already running!" | |
| exit 0 | |
| fi | |
| # Start Kanata service | |
| error_output=$(echo "$cli_password" | sudo -S launchctl bootstrap system /Library/LaunchDaemons/com.example.kanata.plist 2>&1) | |
| exit_code=$? | |
| if [ $exit_code -eq 0 ]; then | |
| echo "β Kanata started successfully!" | |
| elif echo "$error_output" | grep -q "Already loaded"; then | |
| echo "β Kanata started successfully!" | |
| else | |
| echo "β Failed to start Kanata:" | |
| echo "$error_output" | |
| exit 1 | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Required parameters: | |
| # @raycast.schemaVersion 1 | |
| # @raycast.title Kanata Status | |
| # @raycast.mode fullOutput | |
| # Optional parameters: | |
| # @raycast.icon π | |
| # Documentation: | |
| # @raycast.author plasmadice | |
| # @raycast.authorURL https://github.com/plasmadice | |
| echo "=== Kanata & Karabiner Status ===" | |
| echo "" | |
| # Check if processes are running | |
| echo "π Running Processes:" | |
| if ps aux | grep -E "[k]anata" | grep -v grep; then | |
| echo "" | |
| else | |
| echo " Kanata process not found" | |
| fi | |
| if ps aux | grep -E "[K]arabiner.*VirtualHIDDevice" | grep -v grep; then | |
| echo "" | |
| else | |
| echo " Karabiner VirtualHIDDevice processes not found" | |
| fi | |
| echo "" | |
| # Check service status | |
| echo "π Service Status:" | |
| launchctl list | grep -E "(kanata|karabiner)" || echo " No matching services found" | |
| echo "" | |
| # Check recent logs | |
| echo "π Recent Kanata Logs (last 10 lines):" | |
| if [ -f /Library/Logs/Kanata/kanata.out.log ]; then | |
| tail -10 /Library/Logs/Kanata/kanata.out.log | |
| else | |
| echo " No output log found" | |
| fi | |
| echo "" | |
| echo "π Recent Error Logs (last 10 lines):" | |
| if [ -f /Library/Logs/Kanata/kanata.err.log ]; then | |
| tail -10 /Library/Logs/Kanata/kanata.err.log | |
| else | |
| echo " No error log found" | |
| fi | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Required parameters: | |
| # @raycast.schemaVersion 1 | |
| # @raycast.title Stop Kanata | |
| # @raycast.mode inline | |
| # Optional parameters: | |
| # @raycast.icon π€ | |
| # Documentation: | |
| # @raycast.author plasmadice | |
| # @raycast.authorURL https://github.com/plasmadice | |
| # Retrieve password from keychain https://scriptingosx.com/2021/04/get-password-from-keychain-in-shell-scripts/ | |
| # Added with security add-generic-password -s 'kanata' -a 'myUser' -w 'myPassword' | |
| # Retrieve password with security find-generic-password -w -s 'kanata' -a 'myUser' | |
| # Deleted with security delete-generic-password -s 'kanata' -a 'myUser' | |
| # Retrieve password from keychain | |
| pw_name="supa" # name of the password in the keychain | |
| pw_account=$(id -un) # current username e.g. "viper" | |
| if ! cli_password=$(security find-generic-password -w -s "$pw_name" -a "$pw_account"); then | |
| echo "β Could not get password (error $?)" | |
| exit 1 | |
| fi | |
| # Stop Kanata service | |
| error_output=$(echo "$cli_password" | sudo -S launchctl bootout system /Library/LaunchDaemons/com.example.kanata.plist 2>&1) | |
| exit_code=$? | |
| if [ $exit_code -eq 0 ]; then | |
| echo "β Kanata stopped successfully!" | |
| elif echo "$error_output" | grep -q "Could not find service"; then | |
| echo "β οΈ Kanata is not running!" | |
| exit 0 | |
| else | |
| echo "β Failed to stop Kanata:" | |
| echo "$error_output" | |
| exit 1 | |
| fi | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment