#!/usr/bin/env bash
# Start the per-box VNC stack: Xvnc on :1 (loopback inside container) +
# websockify on 0.0.0.0:6080 serving noVNC's HTML5 client and proxying RFB.
# Launched by the host via `docker exec -d --user vscode` after the container
# is up. Idempotent — re-running while the daemons are alive is a no-op, so
# `agentbox start` can blindly call us again.

set -euo pipefail

PASS="${AGENTBOX_VNC_PASSWORD:-}"
if [[ -z "$PASS" ]]; then
  echo "agentbox-vnc-start: AGENTBOX_VNC_PASSWORD is not set" >&2
  exit 64
fi

if pgrep -u "$(id -u)" -x Xvnc >/dev/null \
   && pgrep -u "$(id -u)" -f "websockify.*6080" >/dev/null; then
  exit 0
fi

mkdir -p "$HOME/.vnc"
# vncpasswd's -f mode reads plaintext on stdin, writes the DES blob to stdout.
# VncAuth truncates >8 chars at compare time, which is fine — the host writes
# an 8-char password. Write to a temp file + rename so a failure (e.g.,
# vncpasswd missing) doesn't leave an empty file that Xvnc would then reject.
#
# Debian 12 (E2B base) doesn't package vncpasswd at all — tigervnc-tools is
# Ubuntu-only. When vncpasswd is missing, fall back to `-SecurityTypes None`
# and rely on the cloud provider's signed preview URL as the access boundary
# (same effective model: holding the URL = holding the credential). Other
# providers (docker, hetzner, daytona, vercel) keep VncAuth.
VNC_SECURITY_ARGS=(-SecurityTypes VncAuth -PasswordFile "$HOME/.vnc/passwd")
if command -v vncpasswd >/dev/null 2>&1; then
  TMP_PASSWD="$(mktemp "$HOME/.vnc/passwd.XXXXXX")"
  printf '%s\n' "$PASS" | vncpasswd -f > "$TMP_PASSWD"
  chmod 600 "$TMP_PASSWD"
  mv "$TMP_PASSWD" "$HOME/.vnc/passwd"
else
  echo "agentbox-vnc-start: vncpasswd not installed; starting Xvnc with -SecurityTypes None (preview URL is the access boundary)" >&2
  VNC_SECURITY_ARGS=(-SecurityTypes None)
fi

mkdir -p /var/log/agentbox 2>/dev/null || true

# Xvnc on display :1, loopback-only (websockify is the only public ingress).
# 1280x800x24 is a sensible laptop-browser viewport.
# The clipboard params are on-by-default in TigerVNC 1.13 but pinned here so a
# base-image bump can't silently break host->box paste, and to document intent:
# accept cut-text from noVNC, set both the X CLIPBOARD and PRIMARY selections.
Xvnc :1 \
  -localhost \
  "${VNC_SECURITY_ARGS[@]}" \
  -geometry 1280x800 \
  -depth 24 \
  -AlwaysShared \
  -AcceptCutText=1 \
  -SendCutText=1 \
  -SetPrimary=1 \
  -SendPrimary=1 \
  >/var/log/agentbox/xvnc.log 2>&1 &

# Wait for Xvnc's RFB socket (5901). bash's /dev/tcp pseudo-device makes the
# probe a one-liner without needing netcat in the image.
for _ in $(seq 1 50); do
  if (echo > /dev/tcp/127.0.0.1/5901) 2>/dev/null; then break; fi
  sleep 0.1
done

# With no window manager, nothing owns the X selections, so Xvnc's RFB cut-text
# isn't reliably handed to Chromium on Ctrl+V. autocutsel (one daemon per
# selection) keeps CLIPBOARD and PRIMARY populated and synced. Best-effort: a
# clipboard failure must never abort VNC, and the pgrep guard keeps a stray
# re-entry from spawning duplicates (Xvnc is up now, so DISPLAY=:1 resolves).
if ! pgrep -u "$(id -u)" -x autocutsel >/dev/null; then
  DISPLAY=:1 autocutsel -selection CLIPBOARD -fork >/dev/null 2>&1 || true
  DISPLAY=:1 autocutsel -selection PRIMARY -fork >/dev/null 2>&1 || true
fi

# noVNC's static assets live at different paths per base image: Debian/Ubuntu
# (docker, hetzner) ship them at /usr/share/novnc via apt; the AL2023 bake
# (vercel) git-clones them to /usr/local/share/novnc. websockify runs
# os.chdir(--web) at startup, so a wrong path makes it FileNotFoundError and
# never bind 6080 — pick the first dir that exists.
NOVNC_WEB=""
for _d in /usr/share/novnc /usr/local/share/novnc; do
  if [[ -d "$_d" ]]; then NOVNC_WEB="$_d"; break; fi
done
if [[ -z "$NOVNC_WEB" ]]; then
  echo "agentbox-vnc-start: noVNC assets not found (looked in /usr/share/novnc, /usr/local/share/novnc)" >&2
  exit 65
fi

# websockify serves noVNC at /vnc.html (--web) and tunnels WS frames to Xvnc's
# RFB. Bind 0.0.0.0:6080 so both Docker `-p` mappings and OrbStack's
# <name>.orb.local routing reach it.
websockify \
  --web="$NOVNC_WEB" \
  0.0.0.0:6080 \
  127.0.0.1:5901 \
  >/var/log/agentbox/websockify.log 2>&1 &

disown -a
