#!/usr/bin/env bash
# ============================================================================
#  ACCORDO PRO - Installer  ·  by KomputerMechanic
#  The curl one-liner target. Verifies your membership key, then runs the real
#  installer in a temporary folder that is wiped afterwards. The installer is
#  never stored on your machine.
#
#     curl -fsSL https://accordo-silver-canyon.komputermechanic.com | bash
#     curl -fsSL https://accordo-silver-canyon.komputermechanic.com | bash -s -- --update     (bring an existing install up to date)
#     curl -fsSL https://accordo-silver-canyon.komputermechanic.com | bash -s -- --undo       (put back the version from before the last update)
# ============================================================================
set -euo pipefail
WORKER_URL="https://accordo-silver-canyon.komputermechanic.com"
TUTORIAL_SLUG="accordo-pro"
ACC_UPDATE="${ACC_UPDATE:-}"
ACC_UNDO="${ACC_UNDO:-}"
for _arg in "$@"; do case "$_arg" in --update) ACC_UPDATE=1 ;; --undo) ACC_UNDO=1 ;; esac; done
if [ -n "$ACC_UPDATE" ] && [ -n "$ACC_UNDO" ]; then printf '\n  --update and --undo together? Run one of them.\n\n'; exit 1; fi
# an update is fetched with its own action: the worker serves it to a key that has used every
# install (the machine was counted when it was installed), and no activation is recorded after it
ACTION=""; [ -n "$ACC_UPDATE" ] && { ACTION=',"action":"update"'; export ACC_UPDATE; }

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

printf '\n  %s%sKomputerMechanic  ~  ACCORDO PRO%s\n' "$B" "$ACC" "$Z"
printf '  %sYour AI team in one dashboard · installer%s\n\n' "$DIM" "$Z"

command -v curl >/dev/null || { printf '  %scurl is required.%s\n' "$R" "$Z"; exit 1; }

TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
[ -n "$ACC_UPDATE" ] && { export ACC_UPDATE_RESULT="$TMP/update-result"; printf '  %sUpdating your Accordo Pro: your team, data and settings are kept.%s\n\n' "$DIM" "$Z"; }

# ---- Intel Macs: not supported, said BEFORE the key is asked for ---------------------------
# A deliberate scope decision (2026-08-14): the test fleet is Apple Silicon + Linux, and a
# commercial product does not sell onto silicon it has never run on. Said here, before the
# licence prompt, so nobody types a key for a machine we will not support. ACC_INTEL_OK=1
# proceeds anyway for tinkerers - unsupported, clearly labelled.
if [ "$(uname -s 2>/dev/null)" = "Darwin" ] && [ "$(uname -m 2>/dev/null)" = "x86_64" ] && [ -z "${ACC_INTEL_OK:-}" ]; then
  printf '\n  %s%sThis is an Intel-based Mac.%s\n\n' "$R" "$B" "$Z"
  printf '  Accordo Pro supports %sApple Silicon Macs (M1 or newer) and Linux%s.\n' "$B" "$Z"
  printf '  Intel Macs are not supported - not because it cannot work, but because we\n'
  printf '  do not test on them, and we only sell what we test.\n\n'
  printf '  %sNo key was asked for and nothing was changed on this machine.%s\n' "$DIM" "$Z"
  printf '  Determined to try anyway, unsupported? Re-run with ACC_INTEL_OK=1\n'
  printf '  Questions or refunds: %sinfo@komputermechanic.com%s\n\n' "$DIM" "$Z"
  exit 1
fi

# ---- --undo: put back the version from before the last update ------------------------------------------
# The way back when an update left the dashboard unable to open, so Settings, Updates, Undo is out of reach.
# No key and no download: the backups and the engine that restores them are already in the install
# (.accordo/backups, .accordo/accordo_update.py). Each run steps back one update (the last 5 are kept).
if [ -n "$ACC_UNDO" ]; then
  # the newest update still applied: "<from id>|<to id>|<from label>|<to label>", empty when there is none
  _UPY='import json, os, re, sys, datetime
def label(v, at=False):   # "13 Oct 2026", with the time when two releases share a day
    m = re.match(r"^(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})", v or "")
    if not m:
        return v or ""
    d = datetime.date(int(m.group(1)), int(m.group(2)), int(m.group(3))).strftime("%d %b %Y").lstrip("0")
    return d + (", %s:%s" % (m.group(4), m.group(5)) if at else "")
broot = os.path.join(sys.argv[1], ".accordo", "backups")
for n in sorted(os.listdir(broot) if os.path.isdir(broot) else [], reverse=True):
    try:
        m = json.load(open(os.path.join(broot, n, "backup.json")))
    except Exception:
        continue
    if m.get("status") == "applied":
        at = label(m.get("from")) == label(m.get("to"))
        print("%s|%s|%s|%s" % (m.get("from") or "", m.get("to") or "", label(m.get("from"), at), label(m.get("to"), at)))
        break'
  _U_WHERE=(); _U_NAME=()   # "host|||<project>" or "ctr|<container>|<user>|<project>"
  _hp="${ACC_DIR:-$HOME/accordo-pro}"
  [ -z "${ACC_CONTAINER_NAME:-}" ] && [ -f "$_hp/.accordo/accordo_update.py" ] && { _U_WHERE+=("host|||$_hp"); _U_NAME+=("this machine ($_hp)"); }
  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 "${ACC_CONTAINER_NAME:-}" ] && [ "$_n" != "$ACC_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 "stat -c %U '${_hh:-/opt/data}'" 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/accordo-pro/.accordo/accordo_update.py" >/dev/null 2>&1; then
        _U_WHERE+=("ctr|$_n|${_cu:-hermes}|$_ch/accordo-pro"); _U_NAME+=("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 Accordo Pro here with an update to undo%s (%s).\n' "$R" "$Z" \
      "$([ -n "${ACC_CONTAINER_NAME:-}" ] && printf 'none in container %s' "$ACC_CONTAINER_NAME" || printf 'none on this machine or in its containers')"
    printf '  Installed somewhere else? Set ACC_DIR to that folder, or ACC_CONTAINER_NAME to the container.\n\n'; exit 1
  fi
  _pick=0
  if [ "${#_U_WHERE[@]}" -gt 1 ]; then
    if [ -n "${ACC_YES:-}" ] || ! { : </dev/tty; } 2>/dev/null; then
      printf '  %sAccordo Pro 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 ACC_CONTAINER_NAME to the container.\n\n'; exit 1
    fi
    printf '  %sAccordo Pro 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 _proj <<< "${_U_WHERE[$_pick]}"
  _urun() {   # python3 <args...>, on this machine or in the container, as the install's owner
    if [ "$_kind" = ctr ]; then docker exec -u "$_cuser" "$_ctr" python3 "$@"; else python3 "$@"; fi
  }
  IFS='|' read -r _ufrom _uto _ufroml _utol <<< "$(_urun -c "$_UPY" "$_proj" 2>/dev/null | tr -d '\r' | head -1)"
  if [ -z "$_ufrom$_uto" ]; then
    printf '  %sNothing to undo in %s:%s no update has been applied there 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 in %s:\n' "$B" "$Z" "${_U_NAME[$_pick]}"
  printf '     back to the %s%s%s release, from %s.\n' "$B" "${_ufroml:-earlier}" "$Z" "${_utol:-the one you have now}"
  printf '  %sYour team, chats, tasks, mail and settings are not touched. Only the dashboard'"'"'s own files go back.%s\n' "$DIM" "$Z"
  if [ -z "${ACC_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 "$_proj/.accordo/accordo_update.py" undo --project "$_proj"; _urc=$?; set -e
  # recorded beside the updates, so a release people roll back stands out (best effort, the key kept at install)
  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())' "$_proj/.accordo/license" 2>/dev/null | tr -cd 'A-Za-z0-9_-' | head -c 128)"
    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" ] && curl -sS -m 20 -X POST "$WORKER_URL" -H 'Content-Type: application/json' \
      -d "{\"license_key\":\"$_ukey\",\"tutorial_slug\":\"$TUTORIAL_SLUG\",\"action\":\"record_update\",\"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: Accordo Pro is back on the %s release, and the dashboard answers.%s\n' "$G" "${_ufroml:-previous}" "$Z"
       printf '  Open it as before. Run this again to step back one more update.\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 the dashboard 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

# TESTS ONLY: ACC_LOCAL_TARBALL=<file> uses a package already on this machine instead of the worker. It
# skips only the DOWNLOAD: the installer's own licence gate still runs (ACC_LICENSE_REQUIRED decides), and
# no activation is recorded.
if [ -n "${ACC_LOCAL_TARBALL:-}" ]; then
  cp "$ACC_LOCAL_TARBALL" "$TMP/body" || { printf '  %sNo package at %s%s\n' "$R" "$ACC_LOCAL_TARBALL" "$Z"; exit 1; }
  : > "$TMP/head"; KEY="local-test"
  printf '  %sTest run: using the local package %s%s\n' "$DIM" "$ACC_LOCAL_TARBALL" "$Z"
else
# the "error" from the worker's JSON answer, read properly (a message with a quote in it defeated grep)
_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
}
# membership key — verify, with one extra try in case of a typo
LAST_ERR=""
ATTEMPTS=2; [ -n "${ACC_LICENSE:-}" ] && ATTEMPTS=1
VERIFIED=0
for try in $(seq 1 "$ATTEMPTS"); do
  KEY="${ACC_LICENSE:-}"
  if [ -z "$KEY" ]; then
    [ "$try" -gt 1 ] && printf "  %sLet's try that once more.%s\n" "$DIM" "$Z"
    read -r -p "  Enter your membership key: " KEY </dev/tty || true
  fi
  KEY="$(printf '%s' "$KEY" | tr -d '[:space:]')"
  if [ -z "$KEY" ]; then printf '  %sNo key entered.%s\n' "$R" "$Z"; continue; fi
  printf '  %sVerifying membership...%s\n' "$DIM" "$Z"
  # --retry alone does not cover a dropped connection; --retry-all-errors does. Validation is
  # read-only server-side, so retrying it never costs an activation.
  CODE="$(curl -sS --retry 3 --retry-delay 2 --retry-all-errors -X POST "$WORKER_URL" \
          -H 'Content-Type: application/json' \
          -d "{\"license_key\":\"$KEY\",\"tutorial_slug\":\"$TUTORIAL_SLUG\"$ACTION}" \
          -D "$TMP/head" -o "$TMP/body" -w '%{http_code}' 2>/dev/null || true)"
  CODE="${CODE:0:3}"; [ -n "$CODE" ] || CODE=000      # curl prints 000 itself when nothing answered
  if [ "$CODE" = "200" ]; then VERIFIED=1; break; fi
  # The server says WHY (install limit, renewal date, payment, wrong installer...): show its words as they
  # are. A buyer who hit his install limit was told "check your key and your connection" twelve times
  # (2026-10-12) because the reason was not read. Only a reply that never came is a connection problem.
  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"
  # a typo is worth a second try; an install limit, a payment or an expired key is not
  case "$CODE:$LAST_ERR" in 403:*[Ii]nvalid*|403:) : ;; 403:*) break ;; esac
done
if [ "$VERIFIED" != "1" ]; then
  # Don't contradict the server. When it told us WHY (e.g. the install limit is reached),
  # repeating "check your key" sends a customer whose key is perfectly valid down the
  # wrong path. Echo guidance that matches the actual reason.
  if [ -n "$LAST_ERR" ]; then
    # the server's own words were printed above, as they are; just close the run
    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

# unpack into the temp folder, stamp the key, run, then wipe
tar -xzf "$TMP/body" -C "$TMP" 2>/dev/null || { printf '  %sInstaller package was invalid.%s\n' "$R" "$Z"; exit 1; }
DIR="$TMP/accordo-pro-installer"
[ -f "$DIR/install.sh" ] || { printf '  %sInstaller not found in package.%s\n' "$R" "$Z"; exit 1; }
CUSTOMER="$( { grep -i '^x-km-customer:' "$TMP/head" || true; } | tr -d '\r' | awk '{print $2}')"
printf 'membership_key=%s\ncustomer=%s\ninstalled=%s\n' \
  "$KEY" "$CUSTOMER" "$(date -u +%FT%TZ 2>/dev/null || echo now)" > "$DIR/.license" 2>/dev/null || true

# ---- the terms: accepted BEFORE anything on this machine is touched -----------------------------------
# Right after the membership key and before Docker, Hermes or anything else goes in: the old order showed
# them only once Hermes was already installed (owner, 2026-10-12: "it makes no sense to touch the
# person's machine if they haven't accepted the terms"). The text is payload/TERMS.txt, the same file the
# installer shows when it is run on its own; once accepted here it does not ask again. An update was
# accepted at install. No Y: nothing installed, no install used.
if [ -z "$ACC_UPDATE" ] && [ -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 "${ACC_YES:-}" ]; then
    printf '  %s(ACC_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\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 ACC_TERMS_ACCEPTED=1
fi

printf '  %sStarting the installer...%s\n' "$DIM" "$Z"
cd "$DIR"
# The real installer runs its own licence step; it already verified here, so skip the double-check.
[ -n "${ACC_LOCAL_TARBALL:-}" ] || export ACC_SKIP_LICENSE=1   # a local test package keeps the installer's own gate
set +e

# ---- Hermes in a Docker container on this VPS (Hostinger's image and the like) ---------------
# On such a box there is no `hermes` on the host at all: it lives inside a container, with its
# data at /opt/data, running as its own user. The installer used to die here at "hermes CLI not
# found". Now: if there is no hermes on the host and a running container looks like Hermes, offer
# to install INTO it - the package is copied in and run as the container's Hermes user, and the
# activation is recorded from the result exactly as for a host install.
#
# WHICH container: the first version only looked at containers whose NAME or IMAGE contained
# "hermes", then quietly took the first of them. A buyer with two Hermes containers - one he
# wanted to keep and a new one for Accordo Pro - got the old one every time, because the new one
# was named after his project, not after Hermes (feedback, 2026-09-10). Now every running
# container is PROBED for Hermes (a hermes binary, HERMES_HOME, or a Hermes config in a home
# dir), the ones that already carry a Accordo Pro install are flagged, and with more than one the
# BUYER picks from a numbered list - no default, the installer never chooses a container for
# him (it is his setup). Headless with more than one: stop and ask for ACC_CONTAINER_NAME=<name>.
#
# HOST **AND** CONTAINER: a buyer running Mission Control on the host as root spun up a fresh
# Hermes container to try Accordo Pro beside it, and the script never even looked - the whole Docker
# path was gated on there being NO Hermes on the host, so the host install won by default and
# his container was invisible (feedback, 2026-09-11). Having both is a normal thing to want:
# the old crew keeps working while the new one is tried next to it. So containers are now
# looked for ALWAYS, and when both exist the buyer is asked which one. Unattended runs keep
# the host, exactly as before, and say which containers they saw.
_hermes_on_host() { command -v hermes >/dev/null 2>&1 || [ -x "$HOME/.local/bin/hermes" ] || [ -x /usr/local/bin/hermes ]; }
# does this running container hold a Hermes? asks the container itself, never its name
_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
}
# the Hermes user's home in this container (where a Accordo Pro install lands), empty if unknown
_ctr_accordo_installed() {
  local hh u home
  hh="$(timeout 10 docker exec "$1" sh -c 'printf %s "${HERMES_HOME:-/opt/data}"' 2>/dev/null || true)"
  u="$(timeout 10 docker exec "$1" sh -c "stat -c %U '${hh:-/opt/data}'" 2>/dev/null || true)"
  home="$(timeout 10 docker exec "$1" sh -c "getent passwd '${u:-hermes}' | cut -d: -f6" 2>/dev/null || true)"
  [ -n "$home" ] && timeout 10 docker exec "$1" sh -c "[ -f '$home/accordo-pro/server/server.py' ]" >/dev/null 2>&1
}
CTR=""
_HOST_HERMES=""; _hermes_on_host && _HOST_HERMES=1
if [ "$(uname -s 2>/dev/null)" = "Linux" ] && command -v docker >/dev/null 2>&1; then
  _NAMES=(); _IMAGES=(); _FLAGS=()
  while IFS=$'\t' read -r _n _img; do
    [ -n "$_n" ] || continue
    if _ctr_is_hermes "$_n" || printf '%s %s' "$_n" "$_img" | grep -qi hermes; then
      _NAMES+=("$_n"); _IMAGES+=("$_img")
      if _ctr_accordo_installed "$_n"; then _FLAGS+=("Accordo Pro already installed")
      else _FLAGS+=(""); fi
    fi
  done < <(docker ps --format '{{.Names}}\t{{.Image}}' 2>/dev/null || true)
  # Hermes containers that exist but are NOT running are named too, so "the script does not
  # see my new container" has an answer on screen: start it, then re-run.
  _STOPPED="$(docker ps -a --filter status=exited --filter status=created --format '{{.Names}} {{.Image}}' 2>/dev/null | grep -i hermes || true)"
  # An update goes where Accordo Pro already IS: the one container that carries it, or this machine
  # when no container does. Only when both (or several) carry it is the owner asked, as for an install.
  _UPD_HOST=""
  if [ -n "$ACC_UPDATE" ] && [ -z "${ACC_CONTAINER_NAME:-}" ]; then
    _with=(); _i=0
    while [ "$_i" -lt "${#_NAMES[@]}" ]; do [ -n "${_FLAGS[$_i]}" ] && _with+=("${_NAMES[$_i]}"); _i=$((_i+1)); done
    _host_has=""; [ -f "${ACC_DIR:-$HOME/accordo-pro}/server/server.py" ] && _host_has=1
    if [ "${#_with[@]}" -eq 1 ] && [ -z "$_host_has" ]; then
      ACC_CONTAINER_NAME="${_with[0]}"
    elif [ "${#_with[@]}" -eq 0 ]; then
      _UPD_HOST=1
      [ -n "$_host_has" ] || [ -n "$_HOST_HERMES" ] || { printf '\n  %sThere is no Accordo Pro on this machine or in its containers to update.%s\n  To install it, run the same command without --update.\n\n' "$R" "$Z"; exit 1; }
    fi
  fi
  if [ -n "$_UPD_HOST" ]; then
    :   # this machine's own install
  elif [ -n "${ACC_CONTAINER_NAME:-}" ]; then
    if docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$ACC_CONTAINER_NAME"; then
      CTR="$ACC_CONTAINER_NAME"; ACC_CONTAINER_OK=1
      printf '\n  %sHermes is running in a Docker container; ACC_CONTAINER_NAME picked %s%s\n' "$B" "$CTR" "$Z"
    elif docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "$ACC_CONTAINER_NAME"; then
      printf '\n  %sContainer %s exists but is not running.%s Start it, then re-run:\n     docker start %s\n\n' "$R" "$ACC_CONTAINER_NAME" "$Z" "$ACC_CONTAINER_NAME"; exit 1
    else
      printf '\n  %sNo container called %s on this machine%s (ACC_CONTAINER_NAME). Running containers:\n' "$R" "$ACC_CONTAINER_NAME" "$Z"
      docker ps --format '     {{.Names}}  {{.Image}}' 2>/dev/null; echo; exit 1
    fi
  elif [ "${#_NAMES[@]}" -gt 0 ] && [ -n "$_HOST_HERMES" ]; then
    # Both. Neither is the obvious answer, so neither is assumed.
    printf '\n  %sHermes is installed on this machine AND running in Docker.%s Where should Accordo Pro go?\n' "$B" "$Z"
    printf '     %s0)%s this machine %s(the Hermes already installed here)%s\n' "$B" "$Z" "$DIM" "$Z"
    _i=0; while [ "$_i" -lt "${#_NAMES[@]}" ]; do
      printf '     %s%d)%s %-28s %s%s%s\n' "$B" $((_i+1)) "$Z" "${_NAMES[$_i]}" "$DIM" "${_IMAGES[$_i]}" "$Z"
      [ -n "${_FLAGS[$_i]}" ] && printf '        %s%s%s\n' "$ACC" "${_FLAGS[$_i]}" "$Z"
      _i=$((_i+1))
    done
    [ -n "$_STOPPED" ] && { printf '     %snot running (docker start <name> first):%s\n' "$DIM" "$Z"; printf '%s\n' "$_STOPPED" | sed "s/^/        /"; }
    printf '     %sInstalling into a container leaves whatever is on this machine untouched.%s\n' "$DIM" "$Z"
    if ! { : </dev/tty; } 2>/dev/null; then
      # Unattended keeps the old behaviour to the letter: the host. Changing the
      # default here would silently move somebody else's scripted install.
      printf '\n  %sNo terminal to ask, so Accordo Pro goes on this machine%s (as it always has).\n' "$ACC" "$Z"
      printf '  To install into a container instead, re-run with:  ACC_CONTAINER_NAME=<name>\n\n'
    else
      _try=0; _done=""
      while [ -z "$_done" ] && [ "$_try" -lt 3 ]; do
        read -r -p "  Which one? [0-${#_NAMES[@]}]: " _pick </dev/tty || _pick=""
        if [ "$_pick" = "0" ]; then _done=1; CTR=""          # this machine
        elif printf '%s' "$_pick" | grep -qE '^[0-9]+$' && [ "$_pick" -ge 1 ] && [ "$_pick" -le "${#_NAMES[@]}" ]; then
          CTR="${_NAMES[$((_pick-1))]}"; _done=1
        elif docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$_pick"; then
          CTR="$_pick"; _done=1
        else
          [ -n "$_pick" ] && printf '  %s"%s" is not one of the choices.%s\n' "$R" "$_pick" "$Z"
          _try=$((_try+1))
        fi
      done
      [ -z "$_done" ] && { printf '\n  Nothing installed: no target chosen. Re-run and pick one.\n\n'; exit 1; }
    fi
  elif [ "${#_NAMES[@]}" -gt 0 ]; then
    printf '\n  %sNo Hermes on this machine itself, but Hermes is running in a Docker container:%s\n' "$B" "$Z"
    _i=0; while [ "$_i" -lt "${#_NAMES[@]}" ]; do
      printf '     %s%d)%s %-28s %s%s%s\n' "$B" $((_i+1)) "$Z" "${_NAMES[$_i]}" "$DIM" "${_IMAGES[$_i]}" "$Z"
      [ -n "${_FLAGS[$_i]}" ] && printf '        %s%s%s\n' "$ACC" "${_FLAGS[$_i]}" "$Z"
      _i=$((_i+1))
    done
    [ -n "$_STOPPED" ] && { printf '     %snot running (docker start <name> first):%s\n' "$DIM" "$Z"; printf '%s\n' "$_STOPPED" | sed "s/^/        /"; }
    printf '     %sNot the one you want? Start the Hermes container you mean, or set ACC_CONTAINER_NAME=<name>.%s\n' "$DIM" "$Z"
    if [ "${#_NAMES[@]}" -eq 1 ]; then
      CTR="${_NAMES[0]}"     # the only one: confirmed below, never assumed
    elif ! { : </dev/tty; } 2>/dev/null; then
      printf '\n  %sMore than one Hermes container and no terminal to ask you.%s Name the one you mean and re-run:\n     ACC_CONTAINER_NAME=<name> curl ... | bash\n\n' "$R" "$Z"; exit 1
    else
      _try=0
      while [ -z "$CTR" ] && [ "$_try" -lt 3 ]; do
        read -r -p "  Which container? [1-${#_NAMES[@]}]: " _pick </dev/tty || _pick=""
        if printf '%s' "$_pick" | grep -qE '^[0-9]+$' && [ "$_pick" -ge 1 ] && [ "$_pick" -le "${#_NAMES[@]}" ]; then
          CTR="${_NAMES[$((_pick-1))]}"; break
        elif docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$_pick"; then
          CTR="$_pick"; break     # any RUNNING container by name, even one the probe did not flag
        fi
        [ -n "$_pick" ] && printf '  %sNo running container "%s" - a number from the list or an exact name.%s\n' "$R" "$_pick" "$Z"
        _try=$((_try+1))
      done
      [ -z "$CTR" ] && { printf '\n  Nothing installed: no container chosen. Re-run and pick one, or set ACC_CONTAINER_NAME=<name>.\n\n'; exit 1; }
    fi
    if [ -z "${ACC_CONTAINER_OK:-}" ] && { : </dev/tty; } 2>/dev/null; then
      read -r -p "  Install Accordo Pro into container '$CTR'? [Y/n]: " _ok </dev/tty || _ok=""
      case "$_ok" in [Nn]*)
        printf '\n  Nothing installed. Start or name the container you want (ACC_CONTAINER_NAME=<name>) and re-run.\n\n'; exit 0 ;;
      esac
    fi
  elif [ -n "$_STOPPED" ] && [ -n "$_HOST_HERMES" ]; then
    # There is a perfectly good Hermes here; a stopped container is worth naming, not fatal.
    printf '\n  %sHermes container(s) here are not running, so Accordo Pro goes on this machine:%s\n' "$DIM" "$Z"
    printf '%s\n' "$_STOPPED" | sed 's/^/     /'
    printf '     %sWanted one of those instead? docker start <name>, then re-run.%s\n' "$DIM" "$Z"
  elif [ -n "$_STOPPED" ]; then
    printf '\n  %sNo Hermes on this machine, and the Hermes container(s) here are not running:%s\n' "$R" "$Z"
    printf '%s\n' "$_STOPPED" | sed 's/^/     /'
    printf '  Start the one you want (docker start <name>), then re-run.\n\n'; exit 1
  fi
fi

# ---- Hermes this installer set up last time, whose model or Telegram step did not finish ---------------
# The marker in Hermes' data says so. Without this, a re-run found Hermes, skipped straight to the dashboard and
# installed it on a model that never answered. Now the setup continues at the model, in that same container.
_MADE_HERMES=""
if [ -z "$ACC_UPDATE" ] && [ -n "$CTR" ] && docker exec "$CTR" sh -c 'test -f "${HERMES_HOME:-/opt/data}/.accordo-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="$(ACC_HD_RESUME="$CTR" bash "$DIR/payload/hermes-docker.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Accordo was installed and no install was used.%s\n\n' "$R" "$Z"; exit 1; }
  _p="$(printf '%s\n' "$_HD_OUT" | sed -n 's/^PORT=//p' | tail -1)"; [ -n "$_p" ] && ACC_PORT="$_p"
  export ACC_PORT ACC_PUBLISHED=1 ACC_HERMES_MADE=1
  ACC_CONTAINER_OK=1; _MADE_HERMES=1
fi

# ---- no Hermes anywhere: install it in Docker first (Linux servers) ---------------------------------
# Not on this machine and not in any container: Hermes Agent (Nous Research, MIT) is set up in Docker by
# payload/hermes-docker.sh, which asks the buyer for their AI model (Hermes' own wizard), their Telegram
# bot token and their Telegram user ID. Accordo Pro then installs into that container like any other.
if [ -n "$ACC_UPDATE" ] && [ -z "$CTR" ] && [ -z "$_HOST_HERMES" ]; then
  printf '\n  %sThere is no Accordo Pro here to update%s (no Hermes on this machine or in a container).\n  To install it, run the same command without --update.\n\n' "$R" "$Z"; exit 1
fi
if [ -z "$CTR" ] && [ -z "$_HOST_HERMES" ] && [ "$(uname -s 2>/dev/null)" = "Linux" ]; then
  printf '\n  %sThere is no Hermes on this server yet.%s Accordo Pro runs on Hermes Agent (free, open source,\n' "$B" "$Z"
  printf '  by Nous Research). The installer can set it up for you in Docker first.\n'
  _go="y"
  if [ -z "${ACC_YES:-}" ] && { : </dev/tty; } 2>/dev/null; then
    read -r -p "  Install Hermes Agent in Docker now? [Y/n]: " _go </dev/tty || _go=""
  fi
  case "${_go:-y}" in [Nn]*) printf '\n  Nothing installed. Install Hermes yourself, then run this installer again.\n\n'; exit 0 ;; esac
  _HD_OUT="$(bash "$DIR/payload/hermes-docker.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Accordo 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)"
  ACC_PORT="$(printf '%s\n' "$_HD_OUT" | sed -n 's/^PORT=//p' | tail -1)"
  [ -n "$CTR" ] && [ -n "$ACC_PORT" ] || { printf '  %sHermes was set up but did not report its container.%s\n' "$R" "$Z"; exit 1; }
  export ACC_PORT ACC_PUBLISHED=1 ACC_HERMES_MADE=1
  ACC_CONTAINER_OK=1; _MADE_HERMES=1
  # a fresh Docker may still need sudo for this account
  docker ps >/dev/null 2>&1 || docker() { sudo docker "$@"; }
fi

# ---- no Hermes on a Mac: install it natively first ------------------------------------------------
# A Mac has no Docker, so Hermes goes in the way its own docs install it (payload/hermes-native.sh asks
# the same three things: AI model, Telegram bot token, Telegram user ID). Accordo Pro then installs on
# this Mac like on any Mac that already had Hermes.
# a Mac where this installer put Hermes in last time but the model or Telegram step did not finish: continue there
_HN_RESUME=""
[ -z "$ACC_UPDATE" ] && [ -z "$CTR" ] && [ -n "$_HOST_HERMES" ] && [ "$(uname -s 2>/dev/null)" = "Darwin" ] \
  && [ -f "${HERMES_HOME:-$HOME/.hermes}/.accordo-hermes-setup" ] && _HN_RESUME=1
if [ -z "$CTR" ] && { [ -z "$_HOST_HERMES" ] || [ -n "$_HN_RESUME" ]; } && [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then
  if [ -n "$_HN_RESUME" ]; then
    printf '\n  %sHermes is on this Mac, but its setup did not finish last time.%s Continuing with your AI model.\n' "$B" "$Z"
  else
    printf '\n  %sThere is no Hermes on this Mac yet.%s Accordo Pro runs on Hermes Agent (free, open source,\n' "$B" "$Z"
    printf '  by Nous Research). The installer can set it up for you first.\n'
    _go="y"
    if [ -z "${ACC_YES:-}" ] && { : </dev/tty; } 2>/dev/null; then
      read -r -p "  Install Hermes Agent now? [Y/n]: " _go </dev/tty || _go=""
    fi
    case "${_go:-y}" in [Nn]*) printf '\n  Nothing installed. Install Hermes yourself, then run this installer again.\n\n'; exit 0 ;; esac
  fi
  _HN_OUT="$(ACC_HN_RESUME="$_HN_RESUME" bash "$DIR/payload/hermes-native.sh")" || { printf '\n  %sHermes could not be set up (see above). Nothing of Accordo 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" ACC_HERMES_MADE=1
  _HOST_HERMES=1; _MADE_HERMES=1
fi

# a container that publishes the dashboard port on this server's 127.0.0.1 (one this installer made, on a
# re-run): the tunnel and the tailnet go through that port, not the container's own address
if [ -n "$CTR" ] && [ -z "${ACC_PUBLISHED:-}" ]; then
  _pub="$(docker port "$CTR" 2>/dev/null | grep -oE '127\.0\.0\.1:[0-9]+' | head -1 | cut -d: -f2)"
  [ -n "$_pub" ] && { ACC_PORT="${ACC_PORT:-$_pub}"; export ACC_PORT ACC_PUBLISHED=1; }
fi

if [ -n "$CTR" ]; then
  # the user Hermes runs as inside the container: whoever owns its data dir (uid 10000 on the
  # official image). Root via a plain docker exec would leave files the gateway cannot read.
  _CHH="$(docker exec "$CTR" sh -c 'printf %s "${HERMES_HOME:-/opt/data}"' 2>/dev/null || echo /opt/data)"
  _CUSER="$(docker exec "$CTR" sh -c "stat -c %U '$_CHH'" 2>/dev/null || echo hermes)"
  printf '  %sCopying the installer into %s (as %s, data at %s)...%s\n' "$DIM" "$CTR" "$_CUSER" "$_CHH" "$Z"
  docker cp "$TMP/body" "$CTR:/tmp/accordo-pro-installer.tar.gz" >/dev/null 2>&1 \
    || { printf '  %sCould not copy the installer into the container.%s\n' "$R" "$Z"; exit 1; }
  docker cp "$DIR/.license" "$CTR:/tmp/accordo-pro-installer.license" >/dev/null 2>&1 || true
  # docker cp keeps OUR uid on the files; in the container's sticky /tmp the Hermes user could
  # then neither move the licence stamp nor remove the package afterwards (found by the lane)
  docker exec "$CTR" chown "$_CUSER" /tmp/accordo-pro-installer.tar.gz /tmp/accordo-pro-installer.license >/dev/null 2>&1 || true
  # The installer's prompts read the terminal, so hand it ours (-it, stdin from /dev/tty).
  # Without a terminal (a fully piped run) stdin is /dev/null and ACC_YES answers: NEVER the
  # bootstrap's own stdin, which is this very script arriving from curl - docker exec -i would
  # swallow the rest of it.
  _CSCRIPT='rm -rf /tmp/accordo-pro-installer && tar -xzf /tmp/accordo-pro-installer.tar.gz -C /tmp \
      && mv -f /tmp/accordo-pro-installer.license /tmp/accordo-pro-installer/.license 2>/dev/null
    cd /tmp/accordo-pro-installer && ACC_CONTAINER=1 bash install.sh; rc=$?
    rm -rf /tmp/accordo-pro-installer /tmp/accordo-pro-installer.tar.gz; exit $rc'
  # `-e /dev/tty` is true on every Linux box, even with no terminal attached (the device file
  # exists); only OPENING it tells the truth.
  # every ACC_* knob set on the VPS shell travels into the container (ACC_VPS_HOST, ACC_PORT,
  # ACC_NO_VOICE, ACC_FORCE_REINSTALL, ACC_TG_LINK...), or a re-run could never be steered
  # the tunnel the buyer is told to open goes through THIS server: name it for them, since from
  # inside the container its address is unknowable (ACC_VPS_HOST set by hand wins)
  [ -n "${ACC_VPS_HOST:-}" ] || ACC_VPS_HOST="$(id -un)@$( { hostname -I 2>/dev/null || true; } | awk '{print $1}')"
  case "$ACC_VPS_HOST" in *@) ACC_VPS_HOST="" ;; *) export ACC_VPS_HOST ;; esac
  _ENVARGS=()
  while IFS= read -r _kv; do _ENVARGS+=(-e "$_kv"); done < <(env | grep -E '^ACC_[A-Z_]+=' | grep -vE '^ACC_(LICENSE|SKIP_LICENSE|CONTAINER|TG_TOKEN|TG_USER|OPENROUTER_KEY|MINIMAX_KEY|UPDATE_RESULT)=' || true)
  # where install.sh leaves the update's outcome, inside the container; copied out afterwards
  [ -n "$ACC_UPDATE" ] && _ENVARGS+=(-e "ACC_UPDATE_RESULT=/tmp/accordo-update-result")
  # the bootstrap validated the key above, so the installer inside skips its own check (not for a local test package)
  [ -n "${ACC_LOCAL_TARBALL:-}" ] || _ENVARGS+=(-e "ACC_SKIP_LICENSE=1")
  if { : </dev/tty; } 2>/dev/null; then
    docker exec -it -u "$_CUSER" "${_ENVARGS[@]}" "$CTR" bash -c "$_CSCRIPT" </dev/tty
  else
    docker exec -i -u "$_CUSER" -e ACC_YES=1 "${_ENVARGS[@]}" "$CTR" bash -c "$_CSCRIPT" </dev/null
  fi
  INSTALL_EXIT=$?
  if [ -n "$ACC_UPDATE" ]; then
    docker cp "$CTR:/tmp/accordo-update-result" "$TMP/update-result" >/dev/null 2>&1 || true
    docker exec "$CTR" rm -f /tmp/accordo-update-result >/dev/null 2>&1 || true
  fi
  docker exec "$CTR" rm -f /tmp/accordo-pro-installer.tar.gz /tmp/accordo-pro-installer.license >/dev/null 2>&1 || true
else
# ---- pick the installer for this operating system ---------------------------
# macOS ships bash 3.2 (2007, frozen because bash 4 is GPLv3) and lacks GNU tools like
# `timeout` and `grep -P`, and has launchd instead of systemd. It gets its own installer.
# The Linux install.sh is left exactly as tested.
#
# `uname -s` decides — Darwin on every Mac, Linux on every Linux — but we SHOW the answer
# and let the buyer correct it, so a wrong guess can never silently run the wrong one.
OS_NAME="$(uname -s 2>/dev/null || echo unknown)"
if [ "$OS_NAME" = "Darwin" ] && [ -f ./install.sh ]; then
  PICKED="macos"; PICKED_LABEL="macOS"
else
  PICKED="linux"; PICKED_LABEL="Linux"
fi

# Friendly name only — "Darwin" is macOS's internal kernel name and means nothing to a buyer.
printf '\n  %sDetected system:%s %s%s%s\n' "$DIM" "$Z" "$B" "$PICKED_LABEL" "$Z"

# Only ask when there is a terminal AND a choice to make. Piped/unattended runs keep the
# detected answer. ACC_OS=macos|linux overrides without prompting.
if [ -n "${ACC_OS:-}" ]; then
  case "$ACC_OS" in
    macos|Darwin|mac) PICKED="macos"; PICKED_LABEL="macOS" ;;
    linux|Linux)      PICKED="linux"; PICKED_LABEL="Linux" ;;
  esac
  printf '  %sACC_OS set — using %s.%s\n' "$DIM" "$PICKED_LABEL" "$Z"
elif [ -z "${ACC_YES:-}" ] && [ -z "$ACC_UPDATE" ] && { : </dev/tty; } 2>/dev/null; then    # a terminal we can really open
  read -r -p "  Is that correct? [Y/n]: " OS_OK </dev/tty || OS_OK=""
  case "$OS_OK" in
    [Nn]*)
      printf '  Which are you on?  %s1%s) Linux   %s2%s) macOS\n' "$B" "$Z" "$B" "$Z"
      read -r -p "  Enter 1 or 2: " OS_PICK </dev/tty || OS_PICK=""
      case "$OS_PICK" in
        2) PICKED="macos"; PICKED_LABEL="macOS" ;;
        *) PICKED="linux"; PICKED_LABEL="Linux" ;;
      esac
      ;;
  esac
fi

if [ "$PICKED" = "macos" ] && [ -f ./install.sh ]; then
  INSTALLER="./install.sh"
else
  [ "$PICKED" = "macos" ] && printf '  %sNo macOS installer in this package — falling back to the standard one.%s\n' "$R" "$Z"
  INSTALLER="./install.sh"
fi
printf '  %sRunning the %s installer...%s\n' "$DIM" "$PICKED_LABEL" "$Z"

bash "$INSTALLER"
INSTALL_EXIT=$?
fi   # (host install)
# the container this installer made publishes the dashboard on this server's 127.0.0.1: put it on the
# owner's tailnet from here (inside the container there is no Tailscale)
if [ -z "$ACC_UPDATE" ] && [ -n "${ACC_PUBLISHED:-}" ] && [ -n "${CTR:-}" ] && [ "${INSTALL_EXIT:-1}" = "0" ] && [ "${ACC_TAILNET:-y}" != "0" ] && command -v tailscale >/dev/null 2>&1 && tailscale status >/dev/null 2>&1; then
  _TP="${ACC_TS_PORT:-8456}"
  if tailscale serve --bg --https="$_TP" "http://127.0.0.1:$ACC_PORT" >/dev/null 2>&1 || sudo tailscale serve --bg --https="$_TP" "http://127.0.0.1:$ACC_PORT" >/dev/null 2>&1; then
    _TSN="$(tailscale status --json 2>/dev/null | python3 -c 'import json,sys;print(json.load(sys.stdin)["Self"]["DNSName"].rstrip("."))' 2>/dev/null)"
    _KEY="$(docker exec -u hermes "$CTR" sh -c "sed -n 's/^ACCORDO_TOKEN=//p' /opt/data/accordo-pro/.accordo-secrets" 2>/dev/null)"
    printf '\n  %sOn your tailnet (your devices only), your one-tap link:%s\n  %shttps://%s:%s/?k=%s%s\n' "$B" "$Z" "$G" "$_TSN" "$_TP" "$_KEY" "$Z"
  fi
fi
set -e

if [ -n "$ACC_UPDATE" ]; then
  # An update never records an ACTIVATION (the machine was counted when it was installed), but it is
  # recorded as an update: how it ended and between which versions. Best effort: a failed report
  # never changes what the update did or what it printed.
  _uv()  { sed -n "s/^$1=//p" "$TMP/update-result" 2>/dev/null | head -1 | tr -cd 'A-Za-z0-9._-' | cut -c1-64; }
  _uvb() { sed -n "s/^$1=//p" "$TMP/update-result" 2>/dev/null | head -1 | tr -cd 'A-Za-z0-9+/=' | cut -c1-8000; }
  _URES="$(_uv result)"; [ -n "$_URES" ] || { [ "$INSTALL_EXIT" = 0 ] && _URES=updated || _URES=failed; }
  # record-update accepts only linux, macos, windows and container: anything else is refused and lost
  case "$(uname -s 2>/dev/null)" in Darwin) _UOS=macos ;; MINGW*|MSYS*|CYGWIN*) _UOS=windows ;; *) _UOS=linux ;; esac
  [ -n "${CTR:-}" ] && _UOS="container"
  _UTO="$(_uv to)"; [ -n "$_UTO" ] || _UTO="$(tr -cd 'A-Za-z0-9._-' < "$DIR/payload/update/VERSION" 2>/dev/null | cut -c1-64)"
  if [ -z "${ACC_LOCAL_TARBALL:-}" ]; then
    curl -sS -m 20 -X POST "$WORKER_URL" -H 'Content-Type: application/json' \
      -d "{\"license_key\":\"$KEY\",\"tutorial_slug\":\"$TUTORIAL_SLUG\",\"action\":\"record_update\",\"result\":\"$_URES\",\"from\":\"$(_uv from)\",\"to\":\"$_UTO\",\"changed\":\"$(_uv changed)\",\"kept\":\"$(_uv kept)\",\"os\":\"$_UOS\",\"stage_b64\":\"$(_uvb stage)\",\"error_b64\":\"$(_uvb error)\",\"tail_b64\":\"$(_uvb tail)\"}" \
      -o /dev/null 2>/dev/null || true
  fi
  if [ "$INSTALL_EXIT" = "0" ]; then exit 0
  else printf '\n  %s✗ The update did not complete (see above). No install was used.%s\n' "$R" "$Z"; exit "$INSTALL_EXIT"; fi
elif [ -n "${ACC_LOCAL_TARBALL:-}" ]; then
  [ "$INSTALL_EXIT" = "0" ] && printf '\n  %s✓ Installed (test run: no activation recorded).%s\n' "$G" "$Z"; exit "$INSTALL_EXIT"
elif [ "$INSTALL_EXIT" = "0" ]; then
  printf '\n  %sRecording activation...%s\n' "$DIM" "$Z"
  ACT_CODE="$(curl -sS -X POST "$WORKER_URL" \
    -H 'Content-Type: application/json' \
    -d "{\"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' "$G" "${USED:-?}" "${LIMIT:-?}" "$Z"
  else
    printf '  %s⚠ Could not record activation — the install succeeded, but the count may not have updated.%s\n' "$R" "$Z"
  fi
else
  printf '\n  %s✗ Installation failed — no activation was recorded. Your install count was not affected.%s\n' "$R" "$Z"
  exit "$INSTALL_EXIT"
fi
