#!/bin/sh
# continuum-scan installer. Served verbatim at https://continuumcode.ai/scan.sh
# (the landing page is /scan/, so the script cannot also be /scan - the binaries
# live under /scan/bin/ and would collide with a file of that name).
#
#   curl -fsSL https://continuumcode.ai/scan.sh | sh
#
# Downloads the right prebuilt binary for this host, verifies it against the
# published SHA256SUMS, and runs it. The scanner reads only local AI session
# history and prints a report; it asks before sending anything.
#
# TWO THINGS ARE EASY TO GET WRONG IN A CURL-PIPED INSTALLER, and both are
# handled below:
#
#  1. stdin is the SCRIPT, not the terminal. Anything the scanner wants to read
#     from the user (the email prompt) must come from /dev/tty. The binary
#     opens /dev/tty itself, but we also re-attach stdin here so it behaves the
#     same when run under `sh -c` or a shell that closes fd 0.
#  2. A truncated download is a VALID shell script prefix. Everything happens in
#     a temp dir and is checksum-verified before execution, so a half-finished
#     transfer fails loudly instead of running a partial binary.
set -eu

DEFAULT_BASE_URL="https://continuumcode.ai/scan/bin"
BASE_URL="${CONTINUUM_SCAN_BASE_URL:-$DEFAULT_BASE_URL}"
INSTALL_DIR="${CONTINUUM_SCAN_INSTALL_DIR:-}"
KEEP=0

usage() {
  cat <<'EOF'
Usage: install-scan.sh [--keep] [--install-dir DIR] [-- <scanner args>]

  --keep             keep the binary after the scan instead of removing it
  --install-dir DIR  install to DIR (implies --keep)
  --                 pass every following argument to continuum-scan

Environment:
  CONTINUUM_SCAN_BASE_URL   override the download origin
EOF
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --keep) KEEP=1; shift ;;
    --install-dir)
      [ "$#" -ge 2 ] || { echo "error: --install-dir needs a value" >&2; exit 2; }
      INSTALL_DIR=$2; KEEP=1; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    --) shift; break ;;
    *) echo "error: unknown argument: $1" >&2; usage >&2; exit 2 ;;
  esac
done

command -v curl >/dev/null 2>&1 || { echo "error: curl is required" >&2; exit 1; }

# ---- host detection --------------------------------------------------------

os=$(uname -s 2>/dev/null || echo unknown)
arch=$(uname -m 2>/dev/null || echo unknown)

case "$os" in
  Darwin) os=darwin ;;
  Linux)  os=linux ;;
  MINGW*|MSYS*|CYGWIN*)
    echo "error: run the Windows installer instead:" >&2
    echo "  irm https://continuumcode.ai/scan.ps1 | iex" >&2
    exit 1 ;;
  *) echo "error: unsupported operating system: $os" >&2; exit 1 ;;
esac

case "$arch" in
  arm64|aarch64) arch=arm64 ;;
  x86_64|amd64)  arch=amd64 ;;
  *) echo "error: unsupported architecture: $arch" >&2; exit 1 ;;
esac

BINARY="continuum-scan-${os}-${arch}"

# ---- download + verify -----------------------------------------------------

TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/continuum-scan.XXXXXX")
cleanup() { [ "$KEEP" -eq 1 ] || rm -rf "$TMP_DIR"; }
trap cleanup EXIT HUP INT TERM

# Pin the transport to HTTPS for the PUBLIC origin, where a downgrade would be
# an attack. An operator who deliberately overrode the base URL (self-hosting,
# an internal mirror, a local test) has already chosen their own transport, and
# refusing to honour it would just push them to edit the script.
CURL_OPTS="-fsSL --retry 3"
if [ "$BASE_URL" = "$DEFAULT_BASE_URL" ]; then
  CURL_OPTS="$CURL_OPTS --proto =https --tlsv1.2"
fi

printf 'Downloading continuum-scan (%s/%s) ...\n' "$os" "$arch" >&2
# shellcheck disable=SC2086 # CURL_OPTS is intentionally word-split
curl $CURL_OPTS "$BASE_URL/$BINARY" -o "$TMP_DIR/$BINARY"
# shellcheck disable=SC2086
curl $CURL_OPTS "$BASE_URL/SHA256SUMS" -o "$TMP_DIR/SHA256SUMS"

# Verify against the published manifest. Checked BEFORE chmod +x so a corrupt or
# tampered download is never made executable, let alone run.
expected=$(grep " \{1,2\}\*\{0,1\}${BINARY}\$" "$TMP_DIR/SHA256SUMS" | awk '{print $1}' | head -n 1)
if [ -z "$expected" ]; then
  echo "error: $BINARY is not listed in SHA256SUMS" >&2
  exit 1
fi

if command -v sha256sum >/dev/null 2>&1; then
  actual=$(sha256sum "$TMP_DIR/$BINARY" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
  actual=$(shasum -a 256 "$TMP_DIR/$BINARY" | awk '{print $1}')
else
  echo "error: neither sha256sum nor shasum is available; cannot verify the download" >&2
  exit 1
fi

if [ "$actual" != "$expected" ]; then
  echo "error: checksum mismatch for $BINARY" >&2
  echo "  expected $expected" >&2
  echo "  actual   $actual" >&2
  exit 1
fi

chmod +x "$TMP_DIR/$BINARY"

TARGET="$TMP_DIR/$BINARY"
if [ -n "$INSTALL_DIR" ]; then
  mkdir -p "$INSTALL_DIR"
  # Same-filesystem rename where possible; fall back to a copy across devices.
  if mv "$TMP_DIR/$BINARY" "$INSTALL_DIR/continuum-scan" 2>/dev/null; then :; else
    cp "$TMP_DIR/$BINARY" "$INSTALL_DIR/continuum-scan"
  fi
  TARGET="$INSTALL_DIR/continuum-scan"
  printf 'Installed to %s\n' "$TARGET" >&2
fi

# ---- run -------------------------------------------------------------------

# Re-attach stdin to the terminal. Under `curl ... | sh` fd 0 is the script
# itself, so without this the scanner's email prompt would read shell source or
# hit EOF and silently skip itself.
#
# The test OPENS /dev/tty rather than asking `[ -r /dev/tty ]`. In a container,
# a CI runner, or any session with no controlling terminal the node exists and
# reports readable, but the open fails with ENXIO - so the permission test
# passes and the redirect below is what actually errors out.
if { : < /dev/tty; } 2>/dev/null; then
  "$TARGET" "$@" < /dev/tty
else
  # No controlling terminal (CI, a container, a piped shell). The scanner prints
  # its report and skips the prompt rather than blocking forever.
  "$TARGET" "$@"
fi
