#!/usr/bin/env bash
# ============================================================================
#  AVELINE - Installer  ·  by KomputerMechanic
#  The curl one-liner target. Verifies your membership key, shows the terms, then runs the real
#  installer in a temporary folder that is wiped afterwards. The installer is never stored on your machine.
#
#     curl -fsSL https://aveline-amber-harbor.komputermechanic.com | bash
#     curl -fsSL https://aveline-amber-harbor.komputermechanic.com | bash -s -- --update     (bring an existing install up to date)
#     curl -fsSL https://aveline-amber-harbor.komputermechanic.com | bash -s -- --undo       (put back the version from before the last update)
#
#  Running it again on a machine that has Aveline also brings it up to date: your money data, receipts
#  and keys are kept. (Built on the Venari bootstrap, which carries Accordo Pro's update block.)
# ============================================================================
set -euo pipefail
WORKER_URL="https://aveline-amber-harbor.komputermechanic.com"
TUTORIAL_SLUG="aveline"
AVE_FLAG_UPDATE=""; AVE_FLAG_UNDO=""
for _arg in "$@"; do case "$_arg" in --update) AVE_FLAG_UPDATE=1 ;; --undo) AVE_FLAG_UNDO=1 ;; esac; done
if [ -n "$AVE_FLAG_UPDATE" ] && [ -n "$AVE_FLAG_UNDO" ]; then printf '\n  --update and --undo together? Run one of them.\n\n'; exit 1; fi

if [ -t 1 ]; then B=$'\e[1m'; DIM=$'\e[2m'; G=$'\e[38;5;71m'; R=$'\e[38;5;167m'; ACC=$'\e[38;5;209m'; Z=$'\e[0m'
else B=; DIM=; G=; R=; ACC=; Z=; fi

printf '\n  %s%sKomputerMechanic  ~  AVELINE%s\n' "$B" "$ACC" "$Z"
printf '  %sYour money in one place, on your own Hermes · installer%s\n\n' "$DIM" "$Z"

command -v curl >/dev/null || { printf '  %scurl is required.%s\n' "$R" "$Z"; exit 1; }
# said BEFORE the key is asked for, so nobody types a key for a machine Aveline does not run on
case "$(uname -s 2>/dev/null)" in
  Linux|Darwin) : ;;
  *) printf '  %sThis one-liner is for Linux and macOS.%s On Windows run in PowerShell:  irm %s/install.ps1 | iex\n' "$B" "$Z" "$WORKER_URL"
     printf '  No key was asked for and nothing was changed.\n\n'; exit 1 ;;
esac

# macOS has no `timeout`: perl's alarm does the same job (the signal ends the command after N seconds)
if ! command -v timeout >/dev/null 2>&1; then
timeout() { local s="${1%s}"; shift; perl -e 'alarm shift; exec @ARGV' "$s" "$@"; }
fi
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
# a request body with the membership key: a private file curl reads (a retry reads it again), never an argument that
# other accounts on a shared server could see with ps
_req() { ( umask 077; printf '%s' "$1" > "$TMP/req.json" ); printf '%s' "$TMP/req.json"; }

# the machine's time zone (an IANA name like Europe/Berlin): money is booked on the owner's own calendar day
_tz() {
  local z="${AVE_TIMEZONE:-}"
  [ -n "$z" ] || z="$(timedatectl show -p Timezone --value 2>/dev/null || true)"
  [ -n "$z" ] || z="$(readlink /etc/localtime 2>/dev/null | sed -n 's#.*/zoneinfo/##p' || true)"
  [ -n "$z" ] || z="$(head -1 /etc/timezone 2>/dev/null || true)"
  case "$z" in ''|*[!A-Za-z0-9_+/-]*) z="" ;; esac
  printf '%s' "${z:-UTC}"
}

# ---- --undo: put back the version from before the last update ------------------------------------------
# The way back when an update left the dashboard unable to open, so Setup > Updates > Undo is out of reach. No key
# and no download: the copy and the script that restores it are on the machine (~/.aveline/update).
if [ -n "$AVE_FLAG_UNDO" ]; then
  _UPY='import json, os, re, sys, datetime
def label(v):
    m = re.match(r"^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})", v or "")
    if not m:
        return v or ""
    return datetime.date(int(m.group(1)), int(m.group(2)), int(m.group(3))).strftime("%d %b %Y").lstrip("0") + ", %s:%s" % (m.group(4), m.group(5))
root = os.path.join(sys.argv[1], ".aveline", "update", "backups")
for n in sorted(os.listdir(root) if os.path.isdir(root) else [], reverse=True):
    try:
        m = json.load(open(os.path.join(root, n, "backup.json")))
    except Exception:
        continue
    if m.get("status") == "applied":
        print("%s|%s|%s|%s" % (m.get("version") or "", m.get("to") or "", label(m.get("version")), label(m.get("to"))))
        break'
  _U_WHERE=(); _U_NAME=()   # "host||<home>" or "ctr|<container>|<user>|<home>"
  [ -z "${AVE_CONTAINER_NAME:-}" ] && [ -f "$HOME/.aveline/update/aveline_update.py" ] && { _U_WHERE+=("host|||$HOME"); _U_NAME+=("on this machine"); }
  if [ "$(uname -s 2>/dev/null)" = "Linux" ] && command -v docker >/dev/null 2>&1; then
    while IFS= read -r _n; do
      [ -n "$_n" ] || continue
      [ -n "${AVE_CONTAINER_NAME:-}" ] && [ "$_n" != "$AVE_CONTAINER_NAME" ] && continue
      _hh="$(timeout 10 docker exec "$_n" sh -c 'printf %s "${HERMES_HOME:-/opt/data}"' 2>/dev/null || true)"
      _cu="$(timeout 10 docker exec "$_n" sh -c "ls -ld '${_hh:-/opt/data}' | awk '{print \$3}'" 2>/dev/null || true)"
      _ch="$(timeout 10 docker exec "$_n" sh -c "getent passwd '${_cu:-hermes}' | cut -d: -f6" 2>/dev/null || true)"
      if [ -n "$_ch" ] && timeout 10 docker exec "$_n" test -f "$_ch/.aveline/update/aveline_update.py" >/dev/null 2>&1; then
        _U_WHERE+=("ctr|$_n|${_cu:-hermes}|$_ch"); _U_NAME+=("in the Docker container $_n")
      fi
    done < <(docker ps --format '{{.Names}}' 2>/dev/null || true)
  fi
  if [ "${#_U_WHERE[@]}" -eq 0 ]; then
    printf '  %sThere is no Aveline here with an update to undo%s (none on this machine or in its containers).\n' "$R" "$Z"
    printf '  Installed in a container? Set AVE_CONTAINER_NAME to its name.\n\n'; exit 1
  fi
  _pick=0
  if [ "${#_U_WHERE[@]}" -gt 1 ]; then
    if [ -n "${AVE_YES:-}" ] || ! { : </dev/tty; } 2>/dev/null; then
      printf '  %sAveline is installed in more than one place:%s\n' "$B" "$Z"
      for _i in "${!_U_NAME[@]}"; do printf '     %s\n' "${_U_NAME[$_i]}"; done
      printf '  Run this in a terminal to choose, or set AVE_CONTAINER_NAME to the container.\n\n'; exit 1
    fi
    printf '  %sAveline is installed in more than one place. Which one should go back?%s\n' "$B" "$Z"
    for _i in "${!_U_NAME[@]}"; do printf '     %s%d)%s %s\n' "$B" $((_i + 1)) "$Z" "${_U_NAME[$_i]}"; done
    read -r -p "  Enter a number: " _n </dev/tty || _n=""
    case "$_n" in ''|*[!0-9]*) printf '  %sNothing was changed.%s\n\n' "$DIM" "$Z"; exit 1 ;; esac
    [ "$_n" -ge 1 ] && [ "$_n" -le "${#_U_WHERE[@]}" ] || { printf '  %sNothing was changed.%s\n\n' "$DIM" "$Z"; exit 1; }
    _pick=$((_n - 1))
  fi
  IFS='|' read -r _kind _ctr _cuser _uh <<< "${_U_WHERE[$_pick]}"
  _urun() { if [ "$_kind" = ctr ]; then docker exec -u "$_cuser" "$_ctr" python3 "$@"; else python3 "$@"; fi; }
  IFS='|' read -r _ufrom _uto _ufroml _utol <<< "$(_urun -c "$_UPY" "$_uh" 2>/dev/null | tr -d '\r' | head -1 || true)"
  if [ -z "$_ufrom$_uto" ]; then
    printf '  %sNothing to undo %s:%s there is no earlier version to go back to (no update since it was installed, or since the last undo).\n' "$B" "${_U_NAME[$_pick]}" "$Z"
    printf '  %sNothing was changed.%s\n\n' "$DIM" "$Z"; exit 0
  fi
  printf '  %sUndo the last update%s %s:\n' "$B" "$Z" "${_U_NAME[$_pick]}"
  printf '     back to %s%s%s, from %s.\n' "$B" "${_ufroml:-the earlier version}" "$Z" "${_utol:-the one you have now}"
  printf '  %sYour money data, receipts and keys are not touched. Only Aveline'"'"'s own files go back.%s\n' "$DIM" "$Z"
  if [ -z "${AVE_YES:-}" ] && { : </dev/tty; } 2>/dev/null; then
    read -r -p "  Put the previous version back? [Y/n]: " _ok </dev/tty || _ok="n"
    case "$_ok" in [Nn]*) printf '  %sNothing was changed.%s\n\n' "$DIM" "$Z"; exit 0 ;; esac
  fi
  echo
  set +e; _urun "$_uh/.aveline/update/aveline_update.py" undo "$_uh/aveline" "$_uh/.aveline"; _urc=$?; set -e
  if [ "$_urc" = 0 ] || [ "$_urc" = 2 ]; then
    _ukey="$(_urun -c 'import sys
for l in open(sys.argv[1]):
    if l.startswith("membership_key="): print(l.split("=", 1)[1].strip())' "$_uh/.aveline/license" 2>/dev/null | tr -cd 'A-Za-z0-9_-' | head -c 128 || true)"
    case "$(uname -s 2>/dev/null)" in Darwin) _uos=macos ;; *) _uos=linux ;; esac
    [ "$_kind" = ctr ] && _uos=container
    _ust="$(printf 'rolled back by the owner (--undo)' | base64 | tr -d '\n\r')"
    [ -n "$_ukey" ] && [ "$_ukey" != local-test ] && curl -sS -m 20 -X POST "$WORKER_URL" -H 'Content-Type: application/json' \
      --data-binary @"$(_req "{\"license_key\":\"$_ukey\",\"tutorial_slug\":\"$TUTORIAL_SLUG\",\"action\":\"record_update\",\"via\":\"oneliner\",\"result\":\"undone\",\"from\":\"$_uto\",\"to\":\"$_ufrom\",\"os\":\"$_uos\",\"stage_b64\":\"$_ust\"}")" \
      -o /dev/null 2>/dev/null || true
  fi
  echo
  case "$_urc" in
    0) printf '  %s✓ Done: Aveline is back on %s, and it answers.%s\n' "$G" "${_ufroml:-the previous version}" "$Z"
       printf '  Open it as before. If there was an earlier update too, running this again steps back once more.\n'
       printf '  To get the newest version again:  %scurl -fsSL %s | bash -s -- --update%s\n\n' "$B" "$WORKER_URL" "$Z"; exit 0 ;;
    2) printf '  %sThe previous version is back, but Aveline did not answer after its restart.%s\n' "$R" "$Z"
       printf '  Give it a minute and open it again. Still nothing? Email info@komputermechanic.com\n\n'; exit 2 ;;
    *) printf '  %sThe undo did not complete (see above). Your install was not changed by it.%s\n' "$R" "$Z"
       printf '  Questions: info@komputermechanic.com\n\n'; exit 1 ;;
  esac
fi

# Aveline is here already: this run is an update. It does not use up an install, and the saved key is offered.
# (AVE_CONTAINER_NAME names the container this run is for: Aveline on the server itself does not make that an update)
IS_UPDATE=""; _CTR_INSTALLED=""; [ -f "${AVE_DIR:-$HOME/aveline}/VERSION" ] && [ -z "${AVE_CONTAINER_NAME:-}" ] && IS_UPDATE=1
# (|| true: this script runs with set -e and pipefail, and on a fresh machine there is no saved key file)
SAVED_KEY="$(sed -n 's/^membership_key=//p' "$HOME/.aveline/license" 2>/dev/null | head -1 | tr -d '[:space:]' || true)"
# Docker that only works through sudo (a sudo user on a server where our installer set Hermes up in Docker): every docker
# call goes through sudo from here on, asked once in the open, or no update or undo could find the container (audit)
if [ "$(uname -s 2>/dev/null)" = Linux ] && command -v docker >/dev/null 2>&1 && ! docker ps >/dev/null 2>&1 && [ "$(id -u)" != 0 ] && command -v sudo >/dev/null 2>&1; then
  # a small "docker" in front of the real one, not a shell function: `timeout 10 docker ...` runs programs only
  _sudo_docker() { mkdir -p "$TMP/bin"; printf '#!/bin/sh\nexec sudo -n %s "$@"\n' "$(command -v docker)" > "$TMP/bin/docker"; chmod +x "$TMP/bin/docker"; PATH="$TMP/bin:$PATH"; export PATH; }
  if sudo -n docker ps >/dev/null 2>&1; then _sudo_docker
  # asked only of an account that may use sudo at all (one without it was told "your password is asked once" and then
  # never asked: Linux audit 2)
  elif { : </dev/tty; } 2>/dev/null && [ -z "${AVE_YES:-}" ] && id -nG 2>/dev/null | tr ' ' '\n' | grep -qxE 'sudo|wheel|admin'; then
    printf '  %sDocker on this server needs sudo: your password is asked once.%s\n' "$DIM" "$Z"
    sudo -v </dev/tty 2>/dev/null && sudo -n docker ps >/dev/null 2>&1 && _sudo_docker
  fi
fi
# Aveline inside a Docker container already (Hermes as a container, Hostinger style): that is an update too. Known
# BEFORE the key and the terms, so the key is checked as an update and the terms are not shown again
if [ -z "$IS_UPDATE" ] && command -v docker >/dev/null 2>&1 && docker ps >/dev/null 2>&1; then
  while IFS= read -r _n; do
    [ -n "$_n" ] || continue
    [ -n "${AVE_CONTAINER_NAME:-}" ] && [ "$_n" != "$AVE_CONTAINER_NAME" ] && continue
    if timeout 10 docker exec "$_n" sh -c 'test -f "${HERMES_HOME:-/opt/data}/aveline/VERSION"' >/dev/null 2>&1; then
      IS_UPDATE=1; _CTR_INSTALLED="$_n"
      [ -n "$SAVED_KEY" ] || SAVED_KEY="$(timeout 10 docker exec "$_n" sh -c 'sed -n "s/^membership_key=//p" "${HERMES_HOME:-/opt/data}/.aveline/license" 2>/dev/null' 2>/dev/null | head -1 | tr -d '[:space:]' || true)"
      break
    fi
  done < <(docker ps --format '{{.Names}}' 2>/dev/null || true)
fi
[ "$SAVED_KEY" = local-test ] && SAVED_KEY=""
[ -n "$IS_UPDATE" ] && [ -z "$_CTR_INSTALLED" ] && printf '  %sAveline is installed here already (version %s): this updates it. Your money data, receipts and keys are kept.%s\n' "$B" "$(tr -d '\n' < "${AVE_DIR:-$HOME/aveline}/VERSION")" "$Z"
if [ -n "$AVE_FLAG_UPDATE" ] && [ -z "$IS_UPDATE$_CTR_INSTALLED" ]; then
  printf '  %s--update brings an existing Aveline up to date, and there is none here.%s\n' "$R" "$Z"
  printf '  To install Aveline, run the same command without --update. No key was asked for and nothing was changed.\n\n'; exit 1
fi
# The dashboard's port taken by another program: said now, before the key, the terms and the model check (it came
# after a 43-second model test: Linux audit, 2026-11-06). Only on a first install without Docker (in a container the
# port inside it is what counts, and install.sh checks that one).
_P="${AVE_PORT:-8796}"
# (bash's own /dev/tcp: on a fresh Mac `python3` is a stub that opens Apple's developer tools installer)
_host_hermes_now() { command -v hermes >/dev/null 2>&1 || [ -x "$HOME/.local/bin/hermes" ] || [ -x /usr/local/bin/hermes ]; }
# (also on a Docker server with no container yet: the installer then makes Hermes' container and publishes the port here;
# it was found only after the 5-minute Hermes setup: final Linux check, 2026-11-06)
if [ -z "$IS_UPDATE$_CTR_INSTALLED" ] && { ! command -v docker >/dev/null 2>&1 || _host_hermes_now || [ -z "$(docker ps -q 2>/dev/null)" ]; } && ( exec 3<>"/dev/tcp/127.0.0.1/$_P" ) 2>/dev/null; then
  printf '\n  %sPort %s is already in use by another program on this machine.%s Free it, or pick another port:\n' "$R" "$_P" "$Z"
  printf '     %scurl -fsSL <the same link> | AVE_PORT=8797 bash%s\n  Nothing was changed and no install was used.\n\n' "$B" "$Z"; exit 1
fi
# TESTS ONLY: AVE_LOCAL_TARBALL=<file> uses a package already on this machine (no activation recorded).
if [ -n "${AVE_LOCAL_TARBALL:-}" ]; then
  cp "$AVE_LOCAL_TARBALL" "$TMP/body" || { printf '  %sNo package at %s%s\n' "$R" "$AVE_LOCAL_TARBALL" "$Z"; exit 1; }
  : > "$TMP/head"; KEY="local-test"
  printf '  %sTest run: using the local package %s%s\n' "$DIM" "$AVE_LOCAL_TARBALL" "$Z"
else
_server_reason() {
  python3 -c 'import json, sys
try:
    print((json.load(open(sys.argv[1])).get("error") or "").strip())
except Exception:
    pass' "$1" 2>/dev/null && return 0
  { grep -o '"error":"[^"]*"' "$1" 2>/dev/null || true; } | head -1 | cut -d'"' -f4
}
LAST_ERR=""; LAST_CODE=""; NOKEY=""
# curl 7.71+ retries on every error with this; older curl (Ubuntu 20.04) refuses the whole command with it
_CRETRY=""; [ "$(curl --help all 2>/dev/null | grep -c -- '--retry-all-errors' || true)" -gt 0 ] && _CRETRY=--retry-all-errors
ATTEMPTS=2; [ -n "${AVE_LICENSE:-}" ] && ATTEMPTS=1
VERIFIED=0
for try in $(seq 1 "$ATTEMPTS"); do
  KEY="${AVE_LICENSE:-}"
  if [ -z "$KEY" ]; then
    [ "$try" -gt 1 ] && printf "  %sLet's try that once more.%s\n" "$DIM" "$Z"
    if [ -n "$SAVED_KEY" ] && [ "$try" = 1 ]; then
      read -r -p "  Enter your membership key (Enter uses the saved one, ${SAVED_KEY:0:4}...${SAVED_KEY: -4}): " KEY </dev/tty || true
      [ -n "$(printf '%s' "$KEY" | tr -d '[:space:]')" ] || KEY="$SAVED_KEY"
    else
      read -r -p "  Enter your membership key: " KEY </dev/tty || true
    fi
  fi
  KEY="$(printf '%s' "$KEY" | tr -d "[:space:]\"'")"
  if [ -z "$KEY" ]; then
    if ! { : </dev/tty; } 2>/dev/null; then NOKEY=1; break; fi
    printf '  %sNo key entered.%s\n' "$R" "$Z"; continue
  fi
  printf '  %sVerifying membership...%s\n' "$DIM" "$Z"
  CODE="$(curl -sS --retry 3 --retry-delay 2 $_CRETRY -X POST "$WORKER_URL" \
          -H 'Content-Type: application/json' \
          --data-binary @"$(_req "{\"license_key\":\"$KEY\",\"tutorial_slug\":\"$TUTORIAL_SLUG\"${IS_UPDATE:+,\"action\":\"update\"}}")" \
          -D "$TMP/head" -o "$TMP/body" -w '%{http_code}' 2>/dev/null || true)"
  CODE="${CODE:0:3}"; [ -n "$CODE" ] || CODE=000
  if [ "$CODE" = "200" ]; then VERIFIED=1; break; fi
  # the server says WHY (install limit, renewal, payment...): its words, as they are
  ERR="$(_server_reason "$TMP/body")"
  LAST_CODE="$CODE"; LAST_ERR="$ERR"
  if [ -z "$ERR" ]; then
    case "$CODE" in
      000|5??) ERR="Could not reach the membership server. Check this machine's internet connection and try again." ;;
      *)       ERR="The membership server refused this key (HTTP $CODE) without saying why. Contact info@komputermechanic.com" ;;
    esac
  fi
  printf '  %s✗  %s%s\n' "$R" "$ERR" "$Z"
  case "$CODE:$LAST_ERR" in 403:*[Ii]nvalid*|403:) : ;; 403:*) break ;; esac
done
if [ "$VERIFIED" != "1" ]; then
  if [ -n "$NOKEY" ]; then
    printf '\n  %sNo membership key here, and no terminal to ask for one.%s Run the one-liner in a terminal, or set AVE_LICENSE=<your key>.\n\n' "$R$B" "$Z"; exit 1
  fi
  if [ -n "$LAST_ERR" ]; then printf '\n  %sNothing was installed. Questions: info@komputermechanic.com%s\n\n' "$DIM" "$Z"
  else
    case "${LAST_CODE:-000}" in
      000|5??) printf '\n  %s%sCould not reach the membership server.%s Check this machine'"'"'s internet connection and try again.\n\n' "$R" "$B" "$Z" ;;
      *)       printf '\n  %s%sYour key was not accepted.%s Contact info@komputermechanic.com\n\n' "$R" "$B" "$Z" ;;
    esac
  fi
  exit 1
fi
printf '  %s✓ Membership verified.%s\n' "$G" "$Z"
fi

tar -xzf "$TMP/body" -C "$TMP" 2>/dev/null || { printf '  %sInstaller package was invalid.%s\n' "$R" "$Z"; exit 1; }
DIR="$TMP/aveline-installer"
[ -f "$DIR/install.sh" ] || { printf '  %sInstaller not found in package.%s\n' "$R" "$Z"; exit 1; }

# ---- the terms: accepted BEFORE anything on this machine is touched -----------------------------------
if [ -n "$IS_UPDATE" ]; then
  printf '  %s✓ Updating the Aveline you installed under these terms (accepted at install).%s\n' "$G" "$Z"
  export AVE_TERMS_ACCEPTED=1
elif [ -f "$DIR/payload/TERMS.txt" ]; then
  _tw=70; _te=""; _ti=0; while [ "$_ti" -lt $((_tw + 2)) ]; do _te="${_te}╌"; _ti=$((_ti + 1)); done
  printf '\n  %s┌%s┐%s\n' "$ACC" "$_te" "$Z"
  printf '  %s┊%s %sPLEASE READ  -  LICENSE, DISCLAIMER & TERMS%s\n  %s┊%s\n' "$ACC" "$Z" "$B" "$Z" "$ACC" "$Z"
  awk -v RS= '{gsub(/\n/," "); print; print ""}' "$DIR/payload/TERMS.txt" | fold -s -w "$_tw" | sed "s/^/  ${ACC}┊${Z} /"
  printf '  %s└%s┘%s\n' "$ACC" "$_te" "$Z"
  if [ -n "${AVE_YES:-}" ]; then
    printf '  %s(AVE_YES set: the terms are accepted for this unattended run)%s\n' "$DIM" "$Z"
  elif { : </dev/tty; } 2>/dev/null; then
    _acc=""; read -r -p "  Type Y to accept and continue, anything else to stop: " _acc </dev/tty || _acc=""
    case "$_acc" in [Yy]|[Yy][Ee][Ss]) printf '  %s✓ Terms accepted.%s\n' "$G" "$Z" ;;
      *) printf '\n  %sThe terms were not accepted. Nothing was installed and no install was used.%s\n\n' "$R" "$Z"; exit 1 ;; esac
  else
    printf '\n  %sThere is no terminal here to accept the terms.%s Run the installer in a terminal.\n\n' "$R" "$Z"; exit 1
  fi
  export AVE_TERMS_ACCEPTED=1
fi

# ---- Hermes in a Docker container on this server (Hostinger's VPS image and the like) ----------------
# There `hermes` is not on the server itself: it lives in a container, data at /opt/data, as its own user.
# Every running container is ASKED whether it holds Hermes (never guessed from its name). With a choice to
# make the buyer picks; unattended runs keep the server itself, or need AVE_CONTAINER_NAME=<name>.
_hermes_on_host() { command -v hermes >/dev/null 2>&1 || [ -x "$HOME/.local/bin/hermes" ] || [ -x /usr/local/bin/hermes ]; }
_ctr_is_hermes() {
  timeout 10 docker exec "$1" sh -c 'command -v hermes >/dev/null 2>&1 || [ -n "${HERMES_HOME:-}" ] \
    || ls /opt/data/config.yaml /home/*/.hermes/config.yaml /root/.hermes/config.yaml >/dev/null 2>&1' >/dev/null 2>&1
}
_tty() { { : </dev/tty; } 2>/dev/null; }
CTR=""; NAMES=()
if command -v docker >/dev/null 2>&1 && docker ps >/dev/null 2>&1; then
  while IFS= read -r _n; do [ -n "$_n" ] && _ctr_is_hermes "$_n" && NAMES+=("$_n"); done < <(docker ps --format '{{.Names}}' 2>/dev/null || true)
  if [ -n "${AVE_CONTAINER_NAME:-}" ]; then
    [ "$(docker ps --format '{{.Names}}' | grep -cx "$AVE_CONTAINER_NAME" || true)" -gt 0 ] \
      || { printf '\n  %sNo running container called %s.%s Running ones:\n' "$R" "$AVE_CONTAINER_NAME" "$Z"; docker ps --format '     {{.Names}}  {{.Image}}'; echo; exit 1; }
    CTR="$AVE_CONTAINER_NAME"
  elif [ -n "$_CTR_INSTALLED" ]; then
    CTR="$_CTR_INSTALLED"
    _CV="$(docker exec "$CTR" sh -c 'cat "${HERMES_HOME:-/opt/data}/aveline/VERSION" 2>/dev/null' 2>/dev/null | tr -d '\r\n' || true)"
    printf '\n  %sAveline is installed in the Docker container %s already (version %s): this updates it. Your money data, receipts and keys are kept.%s\n' "$B" "$CTR" "$_CV" "$Z"
    [ "${#NAMES[@]}" -gt 1 ] && printf '  %sTo install Aveline into another container too, run the one-liner with AVE_CONTAINER_NAME=<that container> (a new install).%s\n' "$DIM" "$Z"
    if _tty && [ -z "${AVE_YES:-}" ]; then
      read -r -p "  Update Aveline in '$CTR'? [Y/n]: " _ok </dev/tty || _ok=""
      case "$_ok" in [Nn]*) printf '\n  Nothing was changed.\n\n'; exit 0 ;; esac
    fi
  elif [ -n "$IS_UPDATE" ]; then
    # Aveline on this server itself: the update goes there, never into a container (that would be a new install)
    CTR=""
    [ "${#NAMES[@]}" -gt 0 ] && printf '  %sTo install Aveline into a Docker container too, run the one-liner with AVE_CONTAINER_NAME=<that container> (a new install).%s\n' "$DIM" "$Z"
  elif [ "${#NAMES[@]}" -gt 0 ]; then
    _HOST=""; _hermes_on_host && _HOST=1
    if [ -z "$_HOST" ] && [ "${#NAMES[@]}" -eq 1 ]; then
      CTR="${NAMES[0]}"
      # Aveline in there already: this is an update, and the question says so
      _CV="$(docker exec "$CTR" sh -c 'cat "${HERMES_HOME:-/opt/data}/aveline/VERSION" 2>/dev/null' 2>/dev/null | tr -d '\r\n' || true)"
      if [ -n "$_CV" ]; then
        printf '\n  %sAveline is installed in the Docker container %s already (version %s): this updates it. Your money data, receipts and keys are kept.%s\n' "$B" "$CTR" "$_CV" "$Z"
        _Q="  Update Aveline in '$CTR'? [Y/n]: "
      else
        printf '\n  %sYour Hermes runs in the Docker container %s%s: Aveline goes in there with it.\n' "$B" "$CTR" "$Z"
        _Q="  Install Aveline into '$CTR'? [Y/n]: "
      fi
      if _tty && [ -z "${AVE_YES:-}" ]; then
        read -r -p "$_Q" _ok </dev/tty || _ok=""
        case "$_ok" in [Nn]*) printf '\n  Nothing was changed.\n\n'; exit 0 ;; esac
      fi
    elif ! _tty || [ -n "${AVE_YES:-}" ]; then
      if [ -z "$_HOST" ]; then
        printf '\n  %sMore than one Hermes container and nobody to ask.%s Name one and re-run:  AVE_CONTAINER_NAME=<name>\n\n' "$R" "$Z"; exit 1
      fi
      printf '\n  %sHermes containers here too (%s), but no one to ask: Aveline goes on this server itself.%s\n' "$DIM" "${NAMES[*]}" "$Z"
    else
      printf '\n  %sWhere should Aveline go?%s\n' "$B" "$Z"
      [ -n "$_HOST" ] && printf '     %s0)%s this server %s(the Hermes installed on it)%s\n' "$B" "$Z" "$DIM" "$Z"
      _i=0; while [ "$_i" -lt "${#NAMES[@]}" ]; do printf '     %s%d)%s the Docker container %s\n' "$B" $((_i+1)) "$Z" "${NAMES[$_i]}"; _i=$((_i+1)); done
      _pick=""; _tries=0
      while [ "$_tries" -lt 3 ]; do
        read -r -p "  Your choice: " _pick </dev/tty || _pick=""
        if [ "$_pick" = 0 ] && [ -n "$_HOST" ]; then CTR=""; break
        elif printf '%s' "$_pick" | grep -qE '^[0-9]+$' && [ "$_pick" -ge 1 ] && [ "$_pick" -le "${#NAMES[@]}" ]; then CTR="${NAMES[$((_pick-1))]}"; break; fi
        printf '  %sPick a number from the list.%s\n' "$R" "$Z"; _tries=$((_tries+1)); _pick=""
      done
      [ -z "$_pick" ] && { printf '\n  Nothing installed: no choice made.\n\n'; exit 1; }
    fi
  fi
fi

# ---- Hermes this installer set up last time, whose model step did not finish ----------------------------
# (the marker is still in Hermes' data): the setup continues at the model instead of installing on a model that
# never answered
_MADE=""
if [ -n "$CTR" ] && docker exec "$CTR" sh -c 'test -f "${HERMES_HOME:-/opt/data}/.aveline-hermes-setup"' >/dev/null 2>&1; then
  printf '\n  %sHermes is in container %s, but its setup did not finish last time.%s Continuing with your AI model.\n' "$B" "$CTR" "$Z"
  _HD_OUT="$(AVE_HD_RESUME="$CTR" bash "$DIR/payload/hermes-docker.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Aveline was installed and no install was used.%s\n\n' "$R" "$Z"; exit 1; }
  _MADE=1
fi

# ---- no Hermes anywhere: set it up first ---------------------------------------------------------------
# A Linux server gets Hermes Agent (Nous Research, MIT) in Docker, the way NEXORA and Accordo Pro do it; a Mac
# gets it natively (no Docker there). Aveline then installs on it like on any Hermes.
# A Linux account that cannot use Docker (not root, not in the docker group, no sudo it can use) gets Hermes natively
# for itself, the way Hermes' own docs install it: it was asked for a sudo password it did not have and then told
# "Docker is not running" (Linux audit, 2026-11-06).
_docker_usable() {
  [ "$(id -u)" = 0 ] && return 0
  command -v docker >/dev/null 2>&1 && docker ps >/dev/null 2>&1 && return 0
  command -v sudo >/dev/null 2>&1 || return 1
  sudo -n true 2>/dev/null && return 0
  id -nG 2>/dev/null | tr ' ' '\n' | grep -qxE 'sudo|wheel|admin'
}
_NATIVE=""; [ "$(uname -s)" != Linux ] && _NATIVE=1
[ "$(uname -s)" = Linux ] && ! _docker_usable && _NATIVE=1
_HN_RESUME=""
[ -z "$CTR" ] && _hermes_on_host && [ -n "$_NATIVE" ] && [ -f "${HERMES_HOME:-$HOME/.hermes}/.aveline-hermes-setup" ] && _HN_RESUME=1
if [ -z "$CTR" ] && { ! _hermes_on_host || [ -n "$_HN_RESUME" ]; }; then
  if [ -z "$_HN_RESUME" ]; then
    printf '\n  %sThere is no Hermes on this machine yet.%s Aveline'"'"'s assistant runs on Hermes Agent (free, open source, by Nous Research).\n' "$B" "$Z"
    if [ -z "$_NATIVE" ]; then printf '  The installer sets it up for you first, in Docker.\n'
    elif [ "$(uname -s)" = Linux ]; then printf '  The installer sets it up for you first, for this account only (it may not use Docker here).\n'
    else printf '  The installer sets it up for you first.\n'; fi
    _go="y"
    if [ -z "${AVE_YES:-}" ] && _tty; then read -r -p "  Install Hermes Agent now? [Y/n]: " _go </dev/tty || _go=""; fi
    case "${_go:-y}" in [Nn]*) printf '\n  Nothing installed. To set Hermes up yourself:  %scurl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash%s\n  then run this installer again.\n\n' "$B" "$Z"; exit 0 ;; esac
  fi
  if [ -z "$_NATIVE" ]; then
    _HD_OUT="$(bash "$DIR/payload/hermes-docker.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Aveline was installed and no install was used.%s\n\n' "$R" "$Z"; exit 1; }
    CTR="$(printf '%s\n' "$_HD_OUT" | sed -n 's/^CONTAINER=//p' | tail -1)"
    [ -n "$CTR" ] || { printf '  %sHermes was set up but did not report its container.%s\n' "$R" "$Z"; exit 1; }
    docker ps >/dev/null 2>&1 || docker() { sudo docker "$@"; }      # a fresh Docker may still need sudo here
  else
    _HN_OUT="$(AVE_HN_RESUME="$_HN_RESUME" bash "$DIR/payload/hermes-native.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Aveline was installed and no install was used.%s\n\n' "$R" "$Z"; exit 1; }
    _HN_BIN="$(printf '%s\n' "$_HN_OUT" | sed -n 's/^HERMES=//p' | tail -1)"
    [ -n "$_HN_BIN" ] || { printf '  %sHermes was set up but did not report where it is.%s\n' "$R" "$Z"; exit 1; }
    export PATH="$(dirname "$_HN_BIN"):$PATH"
  fi
  _MADE=1
fi

# ---- a Hermes that is already in use: a dedicated machine is recommended ------------------------------------
# Aveline adds one agent of its own (the "aveline" profile) and never changes the owner's agents. But it holds the
# owner's money data, and anyone who can use this Hermes can talk to that agent. So when this Hermes already has
# agents or a history of its own, the buyer is told, and decides. Unattended: AVE_FORCE=1.
_lived() {   # prints "<extra agents> <conversations>" for the Hermes Aveline is going into
  local _py='import os, sqlite3, sys
h = sys.argv[1]
pd = os.path.join(h, "profiles")
# real agents only: Hermes keeps hidden folders there too (.deleted since 0.23)
p = [d for d in os.listdir(pd) if d != "aveline" and not d.startswith(".") and os.path.isdir(os.path.join(pd, d))] if os.path.isdir(pd) else []
n = 0
try:
    n = sqlite3.connect("file:" + os.path.join(h, "state.db") + "?mode=ro", uri=True).execute("select count(*) from sessions where coalesce(source, '') not in ('oneshot', 'tool')").fetchone()[0]
except Exception:
    pass
print(len(p), n)'
  if [ -n "$CTR" ]; then docker exec "$CTR" sh -c 'h="${HERMES_HOME:-/opt/data}"; [ -f "$h/aveline/VERSION" ] && exit 3; for p in /opt/hermes/.venv/bin/python3 python3; do command -v "$p" >/dev/null 2>&1 && exec "$p" -c "$0" "$h"; done' "$_py" 2>/dev/null
  else [ -f "${AVE_DIR:-$HOME/aveline}/VERSION" ] && return 3; python3 -c "$_py" "${HERMES_HOME:-$HOME/.hermes}" 2>/dev/null; fi
}
if [ -z "$_MADE" ]; then
  _lrc=0; _L="$(_lived)" || _lrc=$?      # 3 = Aveline is here already (an update): never stop the script (set -e)
  [ "$_lrc" = 3 ] && IS_UPDATE=1
  _NA="${_L%% *}"; _NS="${_L##* }"
  if [ "$_lrc" != 3 ] && { [ "${_NA:-0}" -gt 0 ] || [ "${_NS:-0}" -gt 20 ]; } 2>/dev/null; then
    printf '\n  %sThis Hermes is already in use%s (%s agent profile(s), %s conversations).\n' "$B" "$Z" "${_NA:-0}" "${_NS:-0}"
    printf '  Aveline adds one agent of its own and never changes yours. But it holds your money data,\n'
    printf '  and anyone who can use this Hermes can talk to the Aveline agent.\n'
    printf '  %sRecommended: a machine that only you use.%s\n' "$B" "$Z"
    # Yes is the default (Larry, 2026-11-06, after Venari: members who already run NEXORA or Accordo Pro pressed Enter
    # at a [y/N] here and nothing was installed). The warning stays; No still stops before anything changes.
    if [ -n "${AVE_FORCE:-}" ]; then printf '  %s(AVE_FORCE set: installing here)%s\n' "$DIM" "$Z"
    elif _tty && [ -z "${AVE_YES:-}" ]; then
      read -r -p "  Install Aveline on this Hermes? [Y/n]: " _f </dev/tty || _f=""
      case "$_f" in [Nn]*) printf '\n  Nothing installed and no install was used. Run this one-liner on a machine only you use.\n\n'; exit 0 ;; esac
    else
      printf '  %s(nobody to ask: installing next to your agents)%s\n' "$DIM" "$Z"
    fi
  fi
fi

if [ -n "$AVE_FLAG_UPDATE" ] && [ ! -f "${AVE_DIR:-$HOME/aveline}/VERSION" ] && [ "${_lrc:-0}" != 3 ]; then
  printf '\n  %s--update brings an existing Aveline up to date, and there is none here.%s\n' "$R" "$Z"
  printf '  To install Aveline, run the same command without --update. Nothing was changed and no install was used.\n\n'; exit 1
fi
set +e
if [ -n "$CTR" ]; then
  # the user Hermes runs as in there: whoever owns its data folder (uid 10000 on the official image)
  _CHH="$(docker exec "$CTR" sh -c 'printf %s "${HERMES_HOME:-/opt/data}"' 2>/dev/null || echo /opt/data)"
  _CUSER="$(docker exec "$CTR" sh -c "ls -ld '$_CHH' | awk '{print \$3}'" 2>/dev/null || echo hermes)"
  printf '  %sCopying the installer into %s (it runs there as %s)...%s\n' "$DIM" "$CTR" "$_CUSER" "$Z"
  docker cp "$TMP/body" "$CTR:/tmp/aveline-installer.tar.gz" >/dev/null 2>&1 \
    || { printf '  %sCould not copy the installer into the container.%s\n' "$R" "$Z"; exit 1; }
  docker exec "$CTR" chown "$_CUSER" /tmp/aveline-installer.tar.gz >/dev/null 2>&1 || true
  # the tunnel the buyer opens goes through THIS server: name it, since inside the container it is unknowable
  [ -n "${AVE_VPS_HOST:-}" ] || AVE_VPS_HOST="$(id -un)@$( { hostname -I 2>/dev/null || true; } | awk '{print $1}')"
  case "$AVE_VPS_HOST" in *@) AVE_VPS_HOST="" ;; esac
  _LK="$KEY"; [ -n "${AVE_LOCAL_TARBALL:-}" ] && _LK=""
  # the key goes in through a private env file, not "-e KEY=..." on the docker command line (visible with ps)
  ( umask 077; printf 'AVE_LICENSE_KEY=%s\n' "$_LK" > "$TMP/ctr.env" )
  _ENVARGS=(--env-file "$TMP/ctr.env" -e AVE_CONTAINER=1 -e AVE_TERMS_ACCEPTED=1 -e AVE_UPDATE_RESULT=/tmp/aveline-update-result
            -e "AVE_WORKER_URL=$WORKER_URL")
  # the server's own zone only for a new install: an update keeps the zone the owner has (it was reset to UTC, audit)
  [ -z "$IS_UPDATE" ] && _ENVARGS+=(-e "AVE_TIMEZONE=$(_tz)")
  # a container this installer made publishes the dashboard on this server's 127.0.0.1: the link goes through that,
  # and the dashboard listens on the container side of that mapping ("8796/tcp -> 127.0.0.1:8797")
  _PM="$(docker port "$CTR" 2>/dev/null | grep -E '127\.0\.0\.1:[0-9]+' | head -1)"
  _PUB="$(printf '%s' "$_PM" | grep -oE '127\.0\.0\.1:[0-9]+' | cut -d: -f2)"
  _PUBC="$(printf '%s' "$_PM" | cut -d/ -f1 | tr -cd '0-9')"
  [ -n "$_PUB" ] && _ENVARGS+=(-e "AVE_PUBLISHED=$_PUB")
  [ -n "$_PUBC" ] && [ -z "${AVE_PORT:-}" ] && _ENVARGS+=(-e "AVE_PORT=$_PUBC")
  [ -n "$AVE_VPS_HOST" ] && _ENVARGS+=(-e "AVE_VPS_HOST=$AVE_VPS_HOST")
  [ -n "$AVE_FLAG_UPDATE" ] && _ENVARGS+=(-e "AVE_ONLY_NEWER=1")
  while IFS= read -r _kv; do _ENVARGS+=(-e "$_kv"); done < <(env | grep -E '^AVE_[A-Z_]+=' | grep -vE '^AVE_(LICENSE|LICENSE_KEY|CONTAINER|CONTAINER_NAME|TERMS_ACCEPTED|VPS_HOST|LOCAL_TARBALL|TIMEZONE|WORKER_URL)=' || true)
  _CSCRIPT='rm -rf /tmp/aveline-installer && tar -xzf /tmp/aveline-installer.tar.gz -C /tmp \
    && cd /tmp/aveline-installer && bash install.sh; rc=$?
    rm -rf /tmp/aveline-installer /tmp/aveline-installer.tar.gz; exit $rc'
  # the installer's questions read the terminal: hand it ours. Never this script's own stdin (curl's pipe).
  if _tty; then docker exec -it -u "$_CUSER" "${_ENVARGS[@]}" "$CTR" bash -c "$_CSCRIPT" </dev/tty
  else docker exec -i -u "$_CUSER" -e AVE_YES=1 "${_ENVARGS[@]}" "$CTR" bash -c "$_CSCRIPT" </dev/null; fi
  INSTALL_EXIT=$?
  docker exec "$CTR" cat /tmp/aveline-update-result > "$TMP/update-result" 2>/dev/null || true
  docker exec "$CTR" rm -f /tmp/aveline-installer.tar.gz /tmp/aveline-update-result >/dev/null 2>&1 || true
  # the private link for every device: Tailscale on this server, to the published dashboard port
  if [ "$INSTALL_EXIT" = 0 ] && [ -n "$_PUB" ] && [ "${AVE_TAILNET:-1}" != 0 ] && command -v tailscale >/dev/null 2>&1 && tailscale status >/dev/null 2>&1; then
    _TP="${AVE_TS_PORT:-8478}"; _SU=""; [ "$(id -u)" = 0 ] || _SU=sudo
    _TSN="$(tailscale status --json 2>/dev/null | python3 -c 'import json,sys;print(json.load(sys.stdin)["Self"]["DNSName"].rstrip("."))' 2>/dev/null)"
    _TOK="$(docker exec "$CTR" sh -c 'head -1 "${HERMES_HOME:-/opt/data}/.aveline/.aveline-token" 2>/dev/null' 2>/dev/null | tr -cd 'A-Za-z0-9')"
    if [ "$(tailscale serve status 2>/dev/null | grep -c ":$_TP")" -gt 0 ]; then
      if [ "$(tailscale serve status 2>/dev/null | grep -c "127.0.0.1:$_PUB")" -gt 0 ]; then
        [ -n "$_TSN" ] && printf '  %s✓ Private link for all your devices (your tailnet only):%s %shttps://%s:%s/%s%s\n\n' "$G" "$Z" "$B" "$_TSN" "$_TP" "${_TOK:+?k=$_TOK}" "$Z"
      else printf '  %sTailnet port %s is already used for something else: left as it is.%s\n' "$DIM" "$_TP" "$Z"; fi
    elif tailscale serve --bg --https="$_TP" "http://127.0.0.1:$_PUB" >/dev/null 2>&1 || $_SU tailscale serve --bg --https="$_TP" "http://127.0.0.1:$_PUB" >/dev/null 2>&1; then
      [ -n "$_TSN" ] && printf '  %s✓ Private link for all your devices (your tailnet only):%s %shttps://%s:%s/%s%s\n\n' "$G" "$Z" "$B" "$_TSN" "$_TP" "${_TOK:+?k=$_TOK}" "$Z"
      [ -n "$_TSN" ] && docker exec -u "$_CUSER" "$CTR" sh -c 'printf "%s\n" "$1" > "${HERMES_HOME:-/opt/data}/aveline/LINK.txt"' _ "https://$_TSN:$_TP" >/dev/null 2>&1 || true
    fi
  fi
else
  _LK="$KEY"; [ -n "${AVE_LOCAL_TARBALL:-}" ] && _LK=""     # a test run with a local package keeps the saved key
  AVE_LICENSE_KEY="$_LK" AVE_UPDATE_RESULT="$TMP/update-result" AVE_WORKER_URL="$WORKER_URL" AVE_ONLY_NEWER="$AVE_FLAG_UPDATE" bash "$DIR/install.sh"
  INSTALL_EXIT=$?
fi
set -e

if [ -n "${AVE_LOCAL_TARBALL:-}" ]; then
  _canc="$(grep -c '^result=cancelled' "$TMP/update-result" 2>/dev/null || true)"
  [ "${_canc:-0}" -gt 0 ] && { printf '  %sUpdate cancelled. Nothing was changed.%s\n\n' "$DIM" "$Z"; exit 0; }
  [ "$INSTALL_EXIT" = "0" ] && printf '  %s✓ Installed (test run: no activation recorded).%s\n\n' "$G" "$Z"; exit "$INSTALL_EXIT"
elif [ -n "$IS_UPDATE" ]; then
  # an update is recorded as one, whatever its outcome, and never counts as an install
  _rv() { sed -n "s/^$1=//p" "$TMP/update-result" 2>/dev/null | head -1 || true; }
  _RES="$(_rv result)"; [ -n "$_RES" ] || { [ "$INSTALL_EXIT" = 0 ] && _RES=updated || _RES=failed; }
  if [ "$_RES" = cancelled ]; then printf '  %sUpdate cancelled. Nothing was changed.%s\n\n' "$DIM" "$Z"; exit 0; fi
  _OS=linux; [ "$(uname -s)" = Darwin ] && _OS=macos; [ -n "$CTR" ] && _OS=container
  _b64() { printf '%s' "$1" | base64 | tr -d '\n'; }
  curl -sS -m 20 -X POST "$WORKER_URL" -H 'Content-Type: application/json' \
    --data-binary @"$(_req "{\"license_key\":\"$KEY\",\"tutorial_slug\":\"$TUTORIAL_SLUG\",\"action\":\"record_update\",\"via\":\"oneliner\",\"result\":\"$_RES\",\"from\":\"$(_rv from)\",\"to\":\"$(_rv to)\",\"os\":\"$_OS\",\"stage_b64\":\"$(_b64 "$(_rv stage)")\",\"error_b64\":\"$(_b64 "$(_rv error)")\"}")" \
    -o /dev/null 2>/dev/null || true
  [ "$INSTALL_EXIT" = 0 ] && [ "$_RES" != up_to_date ] && printf '  %s✓ Update done. No install was used.%s\n\n' "$G" "$Z"
  exit "$INSTALL_EXIT"
elif [ "$INSTALL_EXIT" = "0" ]; then
  printf '  %sRecording activation...%s\n' "$DIM" "$Z"
  ACT_CODE="$(curl -sS -X POST "$WORKER_URL" -H 'Content-Type: application/json' \
    --data-binary @"$(_req "{\"license_key\":\"$KEY\",\"tutorial_slug\":\"$TUTORIAL_SLUG\",\"action\":\"activate\"}")" \
    -o "$TMP/act" -w '%{http_code}' 2>/dev/null || echo 000)"
  if [ "$ACT_CODE" = "200" ]; then
    USED="$(grep -o '"used":[0-9]*' "$TMP/act" 2>/dev/null | cut -d: -f2 || true)"
    LIMIT="$(grep -o '"limit":[0-9]*' "$TMP/act" 2>/dev/null | cut -d: -f2 || true)"
    printf '  %s✓ Activation recorded (%s/%s installs used).%s\n\n' "$G" "${USED:-?}" "${LIMIT:-?}" "$Z"
  else
    printf '  %s⚠ Could not record activation: the install succeeded, but the count may not have updated.%s\n\n' "$R" "$Z"
  fi
else
  printf '\n  %s✗ Installation failed: no activation was recorded. Your install count was not affected.%s\n\n' "$R" "$Z"
  exit "$INSTALL_EXIT"
fi
