#!/usr/bin/env bash set -euo pipefail log() { echo "[$(date '+%H:%M:%S')] $*"; } die() { echo "[$(date '+%H:%M:%S')] ERROR: $*" >&2; exit 1; } SKIP_FIREWALL=0 for arg in "$@"; do case "$arg" in --skip-firewall) SKIP_FIREWALL=1 ;; *) die "Unknown argument: $arg" ;; esac done REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SERVICE_NAME="clawtap.service" UNIT_DIR="$HOME/.config/systemd/user" ENV_FILE="$HOME/.clawtap/env" UNIT_FILE="$UNIT_DIR/$SERVICE_NAME" log "Repo: $REPO_DIR" log "Service unit: $UNIT_FILE" # --- Install systemd unit if missing --- if [ ! -f "$UNIT_FILE" ]; then log "Installing $SERVICE_NAME..." if [ ! -f "$ENV_FILE" ]; then log "$ENV_FILE not found — creating it now." mkdir -p "$HOME/.clawtap" read -rsp "Enter CLAWTAP_PASSWORD: " _pw echo echo "CLAWTAP_PASSWORD=$_pw" > "$ENV_FILE" log "Created $ENV_FILE" fi mkdir -p "$UNIT_DIR" cat > "$UNIT_FILE" </dev/null; then log "tmux found: $(tmux -V)" else log "tmux not found — installing..." if command -v pacman &>/dev/null; then sudo pacman -S --noconfirm tmux elif command -v apt-get &>/dev/null; then sudo apt-get install -y tmux elif command -v brew &>/dev/null; then brew install tmux else die "Cannot install tmux: no supported package manager found (pacman/apt/brew). Install it manually and re-run." fi log "tmux installed: $(tmux -V)" fi # --- Open firewall port --- if [ "$SKIP_FIREWALL" = "1" ]; then log "Skipping firewall rule (--skip-firewall)." elif command -v ufw &>/dev/null; then _port="$(grep -E '^PORT=' "$ENV_FILE" 2>/dev/null | cut -d= -f2 || true)" _port="${_port:-3456}" log "Opening port $_port for local network..." sudo ufw allow from 192.168.0.0/16 to any port "$_port" comment 'ClawTap' else log "ufw not found, skipping firewall rule." fi # --- Install dependencies (skip if package files haven't changed) --- cd "$REPO_DIR" _pkg_hash="$(cat package.json package-lock.json 2>/dev/null | sha256sum | awk '{print $1}')" _hash_file="$REPO_DIR/node_modules/.clawtap-install-hash" if [ -d node_modules ] && [ -f "$_hash_file" ] && [ "$(cat "$_hash_file")" = "$_pkg_hash" ]; then log "package.json/package-lock.json unchanged, skipping npm install." else log "Running npm install..." npm install # Interactively approve/deny any packages with pending install scripts log "Checking for pending install scripts..." _pending=$(npm approve-scripts --allow-scripts-pending 2>&1 | grep -E '^\s+\S+@' | awk '{print $1}' || true) if [ -n "$_pending" ]; then log "The following packages have pending install scripts:" echo "$_pending" echo for _pkg in $_pending; do read -rp " Approve install scripts for $_pkg? [y/N] " _ans if [[ "$_ans" =~ ^[Yy]$ ]]; then npm approve-scripts "$_pkg" log "Approved $_pkg" else npm deny-scripts "$_pkg" log "Denied $_pkg" fi done log "Re-running npm install after approvals..." npm install else log "No pending install scripts." fi echo "$_pkg_hash" > "$_hash_file" fi # --- Build frontend --- log "Building frontend..." npm run build log "Frontend built successfully." # --- Install CLI binary --- log "Installing CLI binary to ~/.local/bin/clawtap..." mkdir -p "$HOME/.local/bin" cp "$REPO_DIR/bin/clawtap" "$HOME/.local/bin/clawtap" chmod +x "$HOME/.local/bin/clawtap" log "Binary installed." # Ensure ~/.local/bin is in PATH for future shells _fish_config="$HOME/.config/fish/config.fish" if [ -f "$_fish_config" ] && ! grep -q '\.local/bin' "$_fish_config"; then echo 'fish_add_path $HOME/.local/bin' >> "$_fish_config" log "Added ~/.local/bin to PATH in $_fish_config" elif ! grep -q '\.local/bin' "$HOME/.bashrc" 2>/dev/null && ! grep -q '\.local/bin' "$HOME/.profile" 2>/dev/null; then echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.profile" log "Added ~/.local/bin to PATH in ~/.profile" else log "~/.local/bin already in PATH config, skipping." fi # --- HTTPS certificate --- _cert="$HOME/.clawtap/cert.pem" _key="$HOME/.clawtap/key.pem" if [ -f "$_cert" ] && [ -f "$_key" ]; then log "HTTPS certificate already exists, skipping generation." else read -rp "[$(date '+%H:%M:%S')] Generate self-signed HTTPS certificate? (recommended for PWA/push) [Y/n] " _ans if [[ ! "$_ans" =~ ^[Nn]$ ]]; then _lan_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || echo '0.0.0.0')" log "Generating certificate for IP $_lan_ip..." openssl req -x509 -newkey rsa:2048 -nodes \ -keyout "$_key" \ -out "$_cert" \ -days 365 \ -subj "/CN=ClawTap" \ -addext "subjectAltName=IP:$_lan_ip" \ 2>/dev/null chmod 600 "$_key" log "Certificate generated at ~/.clawtap/{cert,key}.pem" echo "" echo " To trust on Android: send cert.pem to your phone → Settings → Security → Install certificate → CA certificate" echo " To trust on iOS: send cert.pem to your phone → open it → Install Profile → Settings → General → About → Certificate Trust Settings → enable ClawTap" echo "" else log "Skipping certificate generation." fi fi # --- Restart --- log "Restarting $SERVICE_NAME..." systemctl --user restart "$SERVICE_NAME" log "Status:" systemctl --user status "$SERVICE_NAME" --no-pager || true