Files
clawtap/update-service.sh
T
izackp 2ded310472 feat: plan-approval overlay, auth-expiry handling, cert download, login reset
- Add a dedicated plan-approval bottom sheet (interactive-prompt) driven by
  ExitPlanMode's now-4-option layout (Claude Code v2.1.177), with
  approve/YOLO/reject/feedback wired through respondPlan(requestId).
- PermissionManager: stop auto-expiring AskUserQuestion/plan prompts and stop
  releasing pending prompts on client disconnect — they reflect real CLI
  state and must survive reconnects.
- Detect server-rejected tokens (401/403 or failed WS handshake) and bounce
  back to the login screen via a new AUTH_EXPIRED_EVENT.
- Add `/cert` endpoint + Settings entry so phones can download/trust the
  self-signed HTTPS cert directly; fix cert IP detection on non-macOS hosts.
- Add `clawtap reset-login` command and `/api/auth/reset-attempts` endpoint
  to clear login rate-limit lockouts.
- Codex adapter: only forward recognized keystrokes/option indices to tmux,
  never the synthetic "deny" dismissal signal.
- PWA: actively poll for service-worker updates (iOS standalone apps don't
  reliably check on navigation).
- update-service.sh: install tmux if missing, prompt to create the env file
  instead of failing, add --skip-firewall, and improve logging.
2026-06-15 16:30:24 -04:00

191 lines
6.0 KiB
Bash
Executable File

#!/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" <<EOF
[Unit]
Description=ClawTap — mobile UI for AI coding sessions
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=$REPO_DIR
Environment=HOME=$HOME
Environment=PATH=/usr/local/bin:/usr/bin:/bin
EnvironmentFile=$ENV_FILE
ExecStart=$REPO_DIR/node_modules/.bin/tsx $REPO_DIR/server/index.ts
Restart=on-failure
RestartSec=5
StandardOutput=append:$HOME/.clawtap/server.log
StandardError=append:$HOME/.clawtap/server.log
[Install]
WantedBy=default.target
EOF
systemctl --user daemon-reload
systemctl --user enable "$SERVICE_NAME"
log "Service installed and enabled."
else
log "Service unit already exists, skipping install."
fi
# --- Check dependencies ---
log "Checking for tmux..."
if command -v tmux &>/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