#!/bin/sh
# Remote Control Support — agent installer for macOS and Linux.
#
# Served from the hub, which substitutes HUB and TOKEN below before sending it,
# so the published one-liner carries the enrolment token in its URL:
#
# curl -fsSL https://support.freakma.com/install.sh?token=TOKEN | sh
#
# Fetched without a token it still works, taking one as an argument instead:
#
# curl -fsSL https://support.freakma.com/install.sh | sh -s -- TOKEN
#
# Everything lands under ~/.rcs-agent. Nothing here needs root.
set -eu
HUB="__HUB__"
TOKEN="__TOKEN__"
# Written long-hand rather than `[ $# -gt 0 ] && TOKEN="$1"`: under `set -e` the
# short-circuit form leaves the script's exit status at 1 when no argument was
# given, which is the normal case for the piped one-liner.
if [ $# -gt 0 ]; then TOKEN="$1"; fi
INSTALL_DIR="$HOME/.rcs-agent"
AGENT="$INSTALL_DIR/agent.js"
CONFIG="$INSTALL_DIR/config.json"
SERVICE_NAME="rcs-agent"
say() { printf '%s\n' "$*"; }
warn() { printf '\033[33m!\033[0m %s\n' "$*" >&2; }
die() { printf '\033[31mx\033[0m %s\n' "$*" >&2; exit 1; }
step() { printf '\033[36m>\033[0m %s\n' "$*"; }
[ -n "$TOKEN" ] || die "no enrolment token. Open the invite link on this machine, or pass one:
curl -fsSL $HUB/install.sh | sh -s -- YOUR_TOKEN"
case "$(uname -s)" in
Darwin) PLATFORM=macos ;;
Linux) PLATFORM=linux ;;
*) die "$(uname -s) is not supported by this script — see $HUB/docs" ;;
esac
# ------------------------------------------------------------------ node
# The agent uses the global WebSocket, which is only on by default from Node 22.
command -v node >/dev/null 2>&1 || die "Node.js 22 or newer is required and was not found.
macOS: brew install node (or https://nodejs.org)
Debian: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt install -y nodejs
Fedora: sudo dnf install nodejs"
NODE_MAJOR=$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo 0)
[ "$NODE_MAJOR" -ge 22 ] 2>/dev/null || die "Node.js 22 or newer is required — this machine has $(node -v).
Upgrade it, or run the agent on another machine on this LAN and point it here:
node agent.js enroll --vnc-host $(hostname)"
# ------------------------------------------------------------------- vnc
# Advisory only: the VNC server can be started after enrolment, and on a fresh
# macOS the Screen Sharing toggle is usually the last thing someone does.
vnc_listening() {
if command -v nc >/dev/null 2>&1; then nc -z -w 2 127.0.0.1 5900 >/dev/null 2>&1; return $?; fi
return 1
}
if ! vnc_listening; then
case "$PLATFORM" in
macos) warn "nothing is listening on 127.0.0.1:5900 — turn on System Settings > General > Sharing > Screen Sharing" ;;
linux) warn "nothing is listening on 127.0.0.1:5900 — start one, e.g. x11vnc -localhost -rfbport 5900" ;;
esac
fi
# --------------------------------------------------------------- install
step "installing to $INSTALL_DIR"
mkdir -p "$INSTALL_DIR"
curl -fsSL "$HUB/download/agent.js" -o "$AGENT" || die "could not download the agent from $HUB"
step "enrolling with $HUB"
node "$AGENT" enroll "$HUB/enroll/$TOKEN" --config "$CONFIG"
# --------------------------------------------------------------- service
NODE_BIN=$(command -v node)
install_launchd() {
PLIST="$HOME/Library/LaunchAgents/com.freakma.rcs-agent.plist"
mkdir -p "$HOME/Library/LaunchAgents"
cat > "$PLIST" <Labelcom.freakma.rcs-agentProgramArguments$NODE_BIN$AGENTrun--config$CONFIGRunAtLoadKeepAliveStandardOutPath$INSTALL_DIR/agent.logStandardErrorPath$INSTALL_DIR/agent.log
PLIST_EOF
launchctl unload "$PLIST" >/dev/null 2>&1 || true
launchctl load "$PLIST"
say ""
say "Running as a launchd agent. Useful commands:"
say " launchctl unload $PLIST # stop"
say " launchctl load $PLIST # start"
say " tail -f $INSTALL_DIR/agent.log"
}
install_systemd_user() {
UNIT_DIR="$HOME/.config/systemd/user"
mkdir -p "$UNIT_DIR"
cat > "$UNIT_DIR/$SERVICE_NAME.service" </dev/null 2>&1 \
|| warn "run 'sudo loginctl enable-linger $(id -un)' so the agent survives logout"
}
install_manual() {
say ""
say "No supported service manager found. Start the agent by hand:"
say " node $AGENT run --config $CONFIG"
}
step "setting it to start automatically"
case "$PLATFORM" in
macos) install_launchd ;;
linux)
if command -v systemctl >/dev/null 2>&1 && systemctl --user show-environment >/dev/null 2>&1; then
install_systemd_user
else
install_manual
fi
;;
esac
say ""
say "Done. This machine is registered and should now appear in the console."
say "Config: $CONFIG"