#!/bin/sh # Registers the Kestrove native-messaging host with the browsers of the current user (Linux, macOS). # Windows: install.ps1. # # install.sh [--nmh /path/to/kestrove-nmh] [--chrome-id ID] [--firefox-id ID] # [--browsers chrome,chromium,edge,brave,firefox] [--user-data-dir DIR] [--uninstall] # # With neither --browsers nor --user-data-dir it installs for every browser whose profile directory exists. # --user-data-dir adds a Chromium-style profile dir started with --user-data-dir (Chromium then # looks for NativeMessagingHosts/ inside it), e.g. for tests. # allowed_origins / allowed_extensions pin the host to our extension ids (docs/04 A8). set -eu HOST=app.kestrove.nmh HERE=$(cd "$(dirname "$0")" && pwd) NMH="" # Development ids: the unpacked Chrome build is pinned by the manifest key in kestrove-browser/scripts/build.mjs. CHROME_ID=kogapfibnnpeapfcjkkpgjddbhnjfein FIREFOX_ID=browser@kestrove.app BROWSERS="" USER_DATA_DIR="" UNINSTALL=0 die() { echo "install.sh: $*" >&2; exit 1; } while [ $# -gt 0 ]; do case "$1" in --nmh) NMH=$2; shift 2 ;; --chrome-id) CHROME_ID=$2; shift 2 ;; --firefox-id) FIREFOX_ID=$2; shift 2 ;; --browsers) BROWSERS=$(echo "$2" | tr ',' ' '); shift 2 ;; --user-data-dir) USER_DATA_DIR=$2; shift 2 ;; --uninstall) UNINSTALL=1; shift ;; -h|--help) sed -n '2,13p' "$0"; exit 0 ;; *) die "unknown option $1" ;; esac done echo "$CHROME_ID" | grep -Eq '^[a-p]{32}$' || die "--chrome-id must be 32 letters a-p" echo "$FIREFOX_ID" | grep -Eq '^[A-Za-z0-9._@{}-]+$' || die "bad --firefox-id" if [ "$UNINSTALL" = 0 ]; then if [ -z "$NMH" ]; then # Next to this script (installed layout), else the cargo build output (development). for c in "$HERE/kestrove-nmh" "$HERE/../src-tauri/target/release/kestrove-nmh" "$HERE/../src-tauri/target/debug/kestrove-nmh"; do [ -x "$c" ] && NMH=$c && break done fi [ -n "$NMH" ] || die "kestrove-nmh not found; pass --nmh" [ -x "$NMH" ] || die "$NMH is not executable" NMH=$(cd "$(dirname "$NMH")" && pwd)/$(basename "$NMH") case "$NMH" in *[\"\\\|\&]*) die "the host path may not contain quotes, backslashes, | or &" ;; esac fi case "$(uname -s)" in Darwin) # The Kestrove Mac app registers its own host on launch; this points the browsers at a desktop dev build. [ "$UNINSTALL" = 1 ] || echo "note: on macOS the Kestrove app registers itself; re-open it to switch back" >&2 BASE="$HOME/Library/Application Support" dir_chrome="$BASE/Google/Chrome" dir_chromium="$BASE/Chromium" dir_edge="$BASE/Microsoft Edge" dir_brave="$BASE/BraveSoftware/Brave-Browser" dir_firefox="$BASE/Mozilla" ff_sub=NativeMessagingHosts ;; Linux) CFG=${XDG_CONFIG_HOME:-$HOME/.config} dir_chrome="$CFG/google-chrome" dir_chromium="$CFG/chromium" dir_edge="$CFG/microsoft-edge" dir_brave="$CFG/BraveSoftware/Brave-Browser" dir_firefox="$HOME/.mozilla" ff_sub=native-messaging-hosts ;; *) die "unsupported OS $(uname -s); on Windows use install.ps1" ;; esac # Auto-detect only when nothing was named: a test run with --user-data-dir must not touch real profiles. [ -n "$BROWSERS$USER_DATA_DIR" ] || for b in chrome chromium edge brave firefox; do eval "d=\$dir_$b" [ -d "$d" ] && BROWSERS="$BROWSERS $b" done render() { # template → stdout sed -e "s|__NMH_PATH__|$NMH|" -e "s|__CHROME_EXTENSION_ID__|$CHROME_ID|" -e "s|__FIREFOX_EXTENSION_ID__|$FIREFOX_ID|" "$HERE/$HOST.$1.json" } place() { # flavor dir if [ "$UNINSTALL" = 1 ]; then rm -f "$2/$HOST.json" && echo "removed $2/$HOST.json" else mkdir -p "$2" render "$1" > "$2/$HOST.json.tmp" && chmod 644 "$2/$HOST.json.tmp" && mv "$2/$HOST.json.tmp" "$2/$HOST.json" echo "installed $2/$HOST.json" fi } for b in $BROWSERS; do case "$b" in chrome|chromium|edge|brave) eval "d=\$dir_$b"; place chrome "$d/NativeMessagingHosts" ;; firefox) place firefox "$dir_firefox/$ff_sub" ;; *) die "unknown browser $b" ;; esac done [ -z "$USER_DATA_DIR" ] || place chrome "$USER_DATA_DIR/NativeMessagingHosts" [ -n "$BROWSERS$USER_DATA_DIR" ] || echo "no supported browser profile found (use --browsers)"