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.
This commit is contained in:
+140
-13
@@ -1,21 +1,37 @@
|
||||
#!/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
|
||||
echo "Installing $SERVICE_NAME..."
|
||||
log "Installing $SERVICE_NAME..."
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "ERROR: $ENV_FILE not found."
|
||||
echo "Create it with at minimum:"
|
||||
echo " CLAWTAP_PASSWORD=your-password-here"
|
||||
exit 1
|
||||
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"
|
||||
@@ -43,21 +59,132 @@ EOF
|
||||
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable "$SERVICE_NAME"
|
||||
echo "Service installed and enabled."
|
||||
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 ---
|
||||
echo "Building frontend..."
|
||||
cd "$REPO_DIR"
|
||||
log "Building frontend..."
|
||||
npm run build
|
||||
log "Frontend built successfully."
|
||||
|
||||
# --- Install CLI binary ---
|
||||
echo "Installing CLI binary..."
|
||||
sudo cp "$REPO_DIR/bin/clawtap" /usr/bin/clawtap
|
||||
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 ---
|
||||
echo "Restarting $SERVICE_NAME..."
|
||||
log "Restarting $SERVICE_NAME..."
|
||||
systemctl --user restart "$SERVICE_NAME"
|
||||
|
||||
echo "Status:"
|
||||
systemctl --user status "$SERVICE_NAME" --no-pager
|
||||
log "Status:"
|
||||
systemctl --user status "$SERVICE_NAME" --no-pager || true
|
||||
|
||||
Reference in New Issue
Block a user