#!/usr/bin/env bash
set -euo pipefail

CODEX_EXPORT_VERSION="2.1.16"
DEFAULT_BASE_URL="https://codex.naskel.systems"
LEGACY_BASE_URL="https://codex.naskel.petrsu.ru"
DEFAULT_INSTALL_URL="${DEFAULT_BASE_URL}/codex-install"
LEGACY_INSTALL_URL="${LEGACY_BASE_URL}/codex-install"
DEFAULT_SCRIPT_URL="${DEFAULT_BASE_URL}/codexport.sh"

usage() {
  cat <<'USAGE'
Usage:
  codexport.sh build   [OUT_DIR] [NODE_TARBALL_URL]
  codexport.sh pack    [OUT_DIR] [ARCHIVE]
  codexport.sh unpack  [ARCHIVE] [OUT_DIR]

Defaults:
  OUT_DIR: codex-portable
  ARCHIVE: codex-portable.tgz
  NODE_VERSION: 20.19.6
  NODE_TARBALL_URL: auto (official if glibc >= 2.28, otherwise unofficial glibc-217)
  Legacy upgrade: if OUT_DIR already exists, installer can preserve .codex chats/history
  Archive password: optional; if set, archive is encrypted with openssl (AES-256-CBC)
  Progress: if pv is installed, pack/unpack shows real-time progress
USAGE
}

prompt() {
  local label="$1"
  local def="${2:-}"
  local value=""
  if [[ -n "$def" ]]; then
    if [[ -t 0 ]]; then
      read -r -p "$label [$def]: " value
    else
      read -r -p "$label [$def]: " value < /dev/tty
    fi
    echo "${value:-$def}"
  else
    if [[ -t 0 ]]; then
      read -r -p "$label: " value
    else
      read -r -p "$label: " value < /dev/tty
    fi
    echo "$value"
  fi
}

version_gt() {
  local left="$1"
  local right="$2"
  [[ "$(printf '%s\n' "$left" "$right" | sort -V | tail -n1)" == "$left" && "$left" != "$right" ]]
}

fetch_url() {
  local url="$1"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL --max-time 8 "$url"
    return
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO- --timeout=8 "$url"
    return
  fi
  return 1
}

post_json() {
  local url="$1"
  local payload="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -sS --connect-timeout 8 --max-time 30 -X POST -H 'Content-Type: application/json' -d "$payload" "$url"
    return
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO- --timeout=30 --header='Content-Type: application/json' --post-data="$payload" "$url"
    return
  fi
  echo "Error: need curl or wget." >&2
  return 1
}

choose_action() {
  if [[ -t 1 ]]; then
    echo "Select action:"
    echo "1) build"
    echo "2) pack"
    echo "3) unpack"
  else
    printf "%s\n" "Select action:" "1) build" "2) pack" "3) unpack" > /dev/tty
  fi
  local choice
  choice="$(prompt "Choice" "1")"
  case "$choice" in
    1|build) echo "build" ;;
    2|pack) echo "pack" ;;
    3|unpack) echo "unpack" ;;
    *) echo "" ;;
  esac
}

maybe_self_update() {
  local script_path="${BASH_SOURCE[0]:-}"
  [[ -n "$script_path" ]] || return 0
  [[ -f "$script_path" ]] || return 0
  [[ -w "$script_path" ]] || return 0
  [[ -t 0 ]] || return 0

  local update_url="${CODEX_EXPORT_UPDATE_URL:-$DEFAULT_SCRIPT_URL}"
  local remote
  if ! remote="$(fetch_url "$update_url" 2>/dev/null)"; then
    return 0
  fi

  local remote_version
  remote_version="$(printf '%s\n' "$remote" | sed -n 's/^CODEX_EXPORT_VERSION="\([^"]*\)"$/\1/p' | head -n1)"
  [[ -n "$remote_version" ]] || return 0

  if ! version_gt "$remote_version" "$CODEX_EXPORT_VERSION"; then
    return 0
  fi

  local answer
  answer="$(prompt "New codexport.sh version ${remote_version} is available. Update now? (Y/n)" "Y")"
  if [[ ! "$answer" =~ ^([Yy]|)$ ]]; then
    return 0
  fi

  local tmp
  tmp="$(mktemp /tmp/codexport-update.XXXXXX.sh)"
  printf '%s\n' "$remote" > "$tmp"
  chmod +x "$tmp"
  mv "$tmp" "$script_path"

  echo "Updated codexport.sh to version ${remote_version}. Restarting..."
  CODEX_SKIP_SELF_UPDATE=1 exec "$script_path" "$@"
}

request_install() {
  local payload="$1"
  local install_url="${AUTH_INSTALL_URL:-}"
  local urls=()

  if [[ -n "$install_url" ]]; then
    urls=("$install_url")
  else
    urls=("$DEFAULT_INSTALL_URL" "$LEGACY_INSTALL_URL")
  fi

  local url
  for url in "${urls[@]}"; do
    if install_response="$(post_json "$url" "$payload" 2>/dev/null)"; then
      echo "$install_response"
      return 0
    fi
  done

  return 1
}

maybe_self_update "$@"

cmd="${1:-}"
if [[ -z "$cmd" ]]; then
  cmd="$(choose_action)"
  if [[ -z "$cmd" ]]; then
    usage
    exit 1
  fi
fi

case "$cmd" in
  build)
    OUT_DIR="${2:-}"
    NODE_TARBALL_URL="${3:-}"
    NODE_VERSION="${NODE_VERSION:-20.19.6}"

    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi

    if [[ -z "$NODE_TARBALL_URL" ]]; then
      mode="$(prompt "Node source (auto/official/unofficial/url)" "auto")"
      case "$mode" in
        official)
          NODE_TARBALL_URL="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
          ;;
        unofficial)
          NODE_TARBALL_URL="https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-glibc-217.tar.xz"
          ;;
        url)
          NODE_TARBALL_URL="$(prompt "NODE_TARBALL_URL")"
          ;;
        *)
          NODE_TARBALL_URL=""
          ;;
      esac
    fi

    AUTH_USERNAME="$(prompt "Account username (leave empty for legacy password mode)" "")"
    PASSWORD=""
    if [[ -n "$AUTH_USERNAME" ]]; then
      if [[ -t 0 ]]; then
        read -r -s -p "Account password: " PASSWORD
        echo
      else
        read -r -s -p "Account password: " PASSWORD < /dev/tty
        echo > /dev/tty
      fi
    else
      if [[ -t 0 ]]; then
        read -r -s -p "Legacy install password: " PASSWORD
        echo
      else
        read -r -s -p "Legacy install password: " PASSWORD < /dev/tty
        echo > /dev/tty
      fi
    fi

    if ! command -v python3 >/dev/null 2>&1; then
      echo "Error: python3 is required to build install request." >&2
      exit 1
    fi

    payload="$(python3 - "$AUTH_USERNAME" "$PASSWORD" <<'PY'
import json,sys
username = sys.argv[1] if len(sys.argv) > 1 else ""
password = sys.argv[2] if len(sys.argv) > 2 else ""
out = {"password": password}
if username:
    out["username"] = username
print(json.dumps(out))
PY
)"

    echo "Requesting install token..."
    if ! install_response="$(request_install "$payload")"; then
      echo "Error: failed to contact auth install endpoint (${DEFAULT_INSTALL_URL} or ${LEGACY_INSTALL_URL})." >&2
      echo "Check internet/DNS connectivity and try again." >&2
      exit 1
    fi

    INSTALL_TOKEN="$(python3 - "$install_response" <<'PY'
import json,sys
try:
    data=json.loads(sys.argv[1])
    print(data.get("install_token", ""))
except Exception:
    print("")
PY
)"
    INSTALL_ERROR="$(python3 - "$install_response" <<'PY'
import json,sys
try:
    data=json.loads(sys.argv[1])
    if isinstance(data, dict):
        print(data.get("error", ""))
    else:
        print("")
except Exception:
    print("")
PY
)"
    if [[ -z "$INSTALL_TOKEN" ]]; then
      case "$INSTALL_ERROR" in
        invalid_password)
          echo "Error: invalid legacy install password." >&2
          ;;
        invalid_credentials)
          echo "Error: invalid account username or password." >&2
          ;;
        account_inactive)
          echo "Error: account is inactive." >&2
          ;;
        no_active_sessions)
          echo "Error: no active sessions are assigned to this account." >&2
          ;;
        password_required)
          echo "Error: password is required." >&2
          ;;
      esac
      echo "Error: auth server response missing install_token." >&2
      echo "Response: $install_response" >&2
      exit 1
    fi

    TMP_DIR="${TMP_DIR:-/tmp/codex-portable-build}"
    LEGACY_CODEX_HOME="$OUT_DIR/.codex"
    LEGACY_BACKUP_HOME=""
    PRESERVE_LEGACY=0
    if [[ -d "$LEGACY_CODEX_HOME" ]]; then
      preserve_answer="$(prompt "Legacy install detected at $LEGACY_CODEX_HOME. Preserve chats/history during upgrade? (Y/n)" "Y")"
      if [[ "$preserve_answer" =~ ^([Yy]|)$ ]]; then
        PRESERVE_LEGACY=1
      fi
    fi

    rm -rf "$TMP_DIR"
    mkdir -p "$TMP_DIR"
    if [[ "$PRESERVE_LEGACY" -eq 1 ]]; then
      LEGACY_BACKUP_HOME="$TMP_DIR/legacy-codex-home"
      mkdir -p "$LEGACY_BACKUP_HOME"
      if ! cp -a "$LEGACY_CODEX_HOME/." "$LEGACY_BACKUP_HOME/"; then
        echo "Error: failed to backup legacy .codex from $LEGACY_CODEX_HOME." >&2
        exit 1
      fi
      echo "Legacy .codex backup saved (chats/history will be restored)."
    fi

    rm -rf "$OUT_DIR"
    mkdir -p "$OUT_DIR"

    if [[ -z "$NODE_TARBALL_URL" ]]; then
      glibc_ver=""
      if command -v getconf >/dev/null 2>&1; then
        glibc_ver="$(getconf GNU_LIBC_VERSION 2>/dev/null | awk '{print $2}')"
      elif command -v ldd >/dev/null 2>&1; then
        glibc_ver="$(ldd --version 2>/dev/null | head -n1 | awk '{print $NF}')"
      fi

      if [[ -n "$glibc_ver" ]] && [[ "$(printf '%s\n' "2.28" "$glibc_ver" | sort -V | head -n1)" == "2.28" ]]; then
        remote_url="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz"
      else
        remote_url="https://unofficial-builds.nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64-glibc-217.tar.xz"
      fi

      mirror_base="${NODE_MIRROR_BASE:-${DEFAULT_BASE_URL}/node}"
      mirror_url="${mirror_base}/${remote_url##*/}"
      if command -v curl >/dev/null 2>&1; then
        if curl -fsI "$mirror_url" >/dev/null 2>&1; then
          NODE_TARBALL_URL="$mirror_url"
        else
          NODE_TARBALL_URL="$remote_url"
        fi
      elif command -v wget >/dev/null 2>&1; then
        if wget -q --spider "$mirror_url"; then
          NODE_TARBALL_URL="$mirror_url"
        else
          NODE_TARBALL_URL="$remote_url"
        fi
      else
        NODE_TARBALL_URL="$remote_url"
      fi
    fi

    echo "Downloading Node from: $NODE_TARBALL_URL"
    if command -v curl >/dev/null 2>&1; then
      curl -fL "$NODE_TARBALL_URL" -o "$TMP_DIR/node.tar.xz"
    elif command -v wget >/dev/null 2>&1; then
      wget -O "$TMP_DIR/node.tar.xz" "$NODE_TARBALL_URL"
    else
      echo "Error: need curl or wget." >&2
      exit 1
    fi

    tar -xJf "$TMP_DIR/node.tar.xz" -C "$TMP_DIR"
    node_dir="$(find "$TMP_DIR" -maxdepth 1 -type d -name 'node-v*' | head -n1)"
    if [[ -z "$node_dir" ]]; then
      echo "Error: failed to unpack Node tarball." >&2
      exit 1
    fi

    mv "$node_dir" "$OUT_DIR/node"

    echo "Installing Codex..."
    export PATH="$OUT_DIR/node/bin:$PATH"
    NPM_CONFIG_OPTIONAL=true NPM_CONFIG_INCLUDE=optional "$OUT_DIR/node/bin/npm" install -g --include=optional --prefix "$OUT_DIR/codex" @openai/codex

    cat > "$OUT_DIR/run.sh" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail

RUN_SCRIPT_VERSION="2.1.16"
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AUTH_SERVER_URL="${AUTH_SERVER_URL:-https://codex.naskel.systems/codex-auth}"
LEGACY_AUTH_SERVER_URL="${LEGACY_AUTH_SERVER_URL:-https://codex.naskel.petrsu.ru/codex-auth}"
AUTH_VERSION_URL="${AUTH_VERSION_URL:-https://codex.naskel.systems/codex-auth/version}"
LEGACY_AUTH_VERSION_URL="${LEGACY_AUTH_VERSION_URL:-https://codex.naskel.petrsu.ru/codex-auth/version}"
INSTALL_SERVER_URL="${INSTALL_SERVER_URL:-https://codex.naskel.systems/codex-install}"
LEGACY_INSTALL_SERVER_URL="${LEGACY_INSTALL_SERVER_URL:-https://codex.naskel.petrsu.ru/codex-install}"
ORIGINAL_ARGS=("$@")

if [[ ! -d /dev/shm ]]; then
  echo "Error: /dev/shm not available. Refusing to store credentials on disk." >&2
  exit 1
fi

umask 077
CODEX_HOME="$ROOT_DIR/.codex"
TOKEN_PATH="$CODEX_HOME/install.token"
MENU_STYLE_PATH="$CODEX_HOME/menu_style"
AUTO_YOLO_PATH="$CODEX_HOME/auto_yolo"
RAM_DIR="$(mktemp -d /dev/shm/codex-auth.XXXXXX)"
mkdir -p "$CODEX_HOME/sessions"
CODEX_JS_PATH=""
MENU_LAYOUT_KEYS=("legacy" "fortress" "split" "telemetry" "bbs" "dos" "stack" "gridbar" "pulse")
MENU_LAYOUT_NAMES=("Legacy Console" "Fortress Grid" "Split Panels" "Telemetry Stream" "Retro BBS" "DOS Console" "Stacked Bars" "Grid Bars" "Pulse Meters")
MENU_LAYOUT_CODES=("LEGACY" "FORTRESS" "SPLIT" "STREAM" "BBS" "DOS" "STACK" "GRIDBAR" "PULSE")
MENU_FORMAT_KEYS=("thin" "bold" "double" "ascii" "dashed")
MENU_FORMAT_NAMES=("Thin Lines" "Bold Lines" "Double Lines" "ASCII" "Dashed")
MENU_FORMAT_CODES=("THIN" "BOLD" "DOUBLE" "ASCII" "DASH")
MENU_COLOR_KEYS=("cyan" "green" "yellow" "magenta" "blue" "red")
MENU_COLOR_NAMES=("Cyan Pulse" "Matrix Green" "Amber Signal" "Magenta Core" "Electric Blue" "Crimson")
MENU_COLOR_CODES=("CYAN" "GREEN" "AMBER" "MAGENTA" "BLUE" "RED")
MENU_LAYOUT_DEFAULT="legacy"
MENU_FORMAT_DEFAULT="thin"
MENU_COLOR_DEFAULT="cyan"
MENU_LAYOUT="$MENU_LAYOUT_DEFAULT"
MENU_FORMAT="$MENU_FORMAT_DEFAULT"
MENU_COLOR="$MENU_COLOR_DEFAULT"

cleanup() {
  rm -rf "$RAM_DIR"
  rm -f "$CODEX_HOME/auth.json"
}
trap cleanup EXIT

prompt() {
  local label="$1"
  local def="${2:-}"
  local value=""
  if [[ -n "$def" ]]; then
    if [[ -t 0 ]]; then
      read -r -p "$label [$def]: " value
    else
      read -r -p "$label [$def]: " value < /dev/tty
    fi
    echo "${value:-$def}"
  else
    if [[ -t 0 ]]; then
      read -r -p "$label: " value
    else
      read -r -p "$label: " value < /dev/tty
    fi
    echo "$value"
  fi
}

prompt_secret() {
  local label="$1"
  local value=""
  if [[ -t 0 ]]; then
    read -r -s -p "$label: " value
    echo
  else
    read -r -s -p "$label: " value < /dev/tty
    echo > /dev/tty
  fi
  printf '%s' "$value"
}

version_gt() {
  local left="$1"
  local right="$2"
  [[ "$(printf '%s\n' "$left" "$right" | sort -V | tail -n1)" == "$left" && "$left" != "$right" ]]
}

http_post() {
  local url="$1"
  local payload="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -sS --connect-timeout 8 --max-time 30 -X POST -H 'Content-Type: application/json' -d "$payload" "$url"
    return
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO- --timeout=30 --header='Content-Type: application/json' --post-data="$payload" "$url"
    return
  fi
  echo "Error: need curl or wget to contact auth server." >&2
  return 1
}

http_get() {
  local url="$1"
  if command -v curl >/dev/null 2>&1; then
    curl -fsS --connect-timeout 8 --max-time 12 "$url"
    return
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO- --timeout=12 "$url"
    return
  fi
  return 1
}

json_field() {
  local payload="$1"
  local field="$2"
  RESPONSE_JSON="$payload" FIELD_NAME="$field" "$ROOT_DIR/node/bin/node" -e '
try {
  const data = JSON.parse(process.env.RESPONSE_JSON || "{}");
  const key = String(process.env.FIELD_NAME || "");
  const value = (data && typeof data === "object") ? data[key] : "";
  if (value === null || value === undefined) {
    process.stdout.write("");
  } else {
    process.stdout.write(String(value));
  }
} catch {
  process.stdout.write("");
}
'
}

is_auth_response_json() {
  local payload="$1"
  RESPONSE_JSON="$payload" "$ROOT_DIR/node/bin/node" -e '
try {
  const data = JSON.parse(process.env.RESPONSE_JSON || "{}");
  const ok = data && typeof data === "object" && ("auth_json" in data || "error" in data);
  process.exit(ok ? 0 : 1);
} catch {
  process.exit(1);
}
'
}

parse_auth_error() {
  sed -n 's/^AUTH_ERROR://p' "$RAM_DIR/parse.err" | head -n1
}

load_token() {
  TOKEN=""
  if [[ -f "$TOKEN_PATH" ]]; then
    TOKEN="$(tr -d '\r\n' < "$TOKEN_PATH" 2>/dev/null || true)"
  fi
}

load_selected_session() {
  SELECTED_SESSION=""
  if [[ -f "$CODEX_HOME/selected_session" ]]; then
    SELECTED_SESSION="$(tr -d '\r\n' < "$CODEX_HOME/selected_session" 2>/dev/null || true)"
  fi
}

load_auto_yolo() {
  AUTO_YOLO=0
  if [[ ! -f "$AUTO_YOLO_PATH" ]]; then
    return 0
  fi
  local raw
  raw="$(tr -d '\r\n[:space:]' < "$AUTO_YOLO_PATH" 2>/dev/null || true)"
  case "${raw,,}" in
    1|y|yes|true|on)
      AUTO_YOLO=1
      ;;
    *)
      AUTO_YOLO=0
      ;;
  esac
}

save_auto_yolo() {
  local enabled="${1:-0}"
  if [[ "$enabled" == "1" ]]; then
    printf '1\n' > "$AUTO_YOLO_PATH"
    chmod 600 "$AUTO_YOLO_PATH" 2>/dev/null || true
    AUTO_YOLO=1
    return 0
  fi
  rm -f "$AUTO_YOLO_PATH"
  AUTO_YOLO=0
}

toggle_auto_yolo() {
  load_auto_yolo
  if [[ "${AUTO_YOLO:-0}" == "1" ]]; then
    save_auto_yolo 0
    echo "Auto YOLO mode disabled."
  else
    save_auto_yolo 1
    echo "Auto YOLO mode enabled. Codex will start with --yolo."
  fi
}

auto_yolo_label() {
  if [[ "${AUTO_YOLO:-0}" == "1" ]]; then
    printf '%s' "enabled"
  else
    printf '%s' "disabled"
  fi
}

menu_layout_index() {
  local key="$1"
  local i
  for i in "${!MENU_LAYOUT_KEYS[@]}"; do
    if [[ "${MENU_LAYOUT_KEYS[$i]}" == "$key" ]]; then
      printf '%s\n' "$i"
      return 0
    fi
  done
  printf '%s\n' "-1"
}

menu_format_index() {
  local key="$1"
  local i
  for i in "${!MENU_FORMAT_KEYS[@]}"; do
    if [[ "${MENU_FORMAT_KEYS[$i]}" == "$key" ]]; then
      printf '%s\n' "$i"
      return 0
    fi
  done
  printf '%s\n' "-1"
}

menu_color_index() {
  local key="$1"
  local i
  for i in "${!MENU_COLOR_KEYS[@]}"; do
    if [[ "${MENU_COLOR_KEYS[$i]}" == "$key" ]]; then
      printf '%s\n' "$i"
      return 0
    fi
  done
  printf '%s\n' "-1"
}

menu_layout_name() {
  local key="${1:-$MENU_LAYOUT_DEFAULT}"
  local idx
  idx="$(menu_layout_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_LAYOUT_NAMES[$idx]}"
  else
    printf '%s' "$key"
  fi
}

menu_format_name() {
  local key="${1:-$MENU_FORMAT_DEFAULT}"
  local idx
  idx="$(menu_format_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_FORMAT_NAMES[$idx]}"
  else
    printf '%s' "$key"
  fi
}

menu_color_name() {
  local key="${1:-$MENU_COLOR_DEFAULT}"
  local idx
  idx="$(menu_color_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_COLOR_NAMES[$idx]}"
  else
    printf '%s' "$key"
  fi
}

menu_layout_code() {
  local key="${1:-$MENU_LAYOUT_DEFAULT}"
  local idx
  idx="$(menu_layout_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_LAYOUT_CODES[$idx]}"
  else
    printf '%s' "LEGACY"
  fi
}

menu_format_code() {
  local key="${1:-$MENU_FORMAT_DEFAULT}"
  local idx
  idx="$(menu_format_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_FORMAT_CODES[$idx]}"
  else
    printf '%s' "LEGACY"
  fi
}

menu_color_code() {
  local key="${1:-$MENU_COLOR_DEFAULT}"
  local idx
  idx="$(menu_color_index "$key")"
  if [[ "$idx" -ge 0 ]]; then
    printf '%s' "${MENU_COLOR_CODES[$idx]}"
  else
    printf '%s' "CYAN"
  fi
}

menu_style_codename() {
  local layout_key="${1:-$MENU_LAYOUT_DEFAULT}"
  local format_key="${2:-$MENU_FORMAT_DEFAULT}"
  local color_key="${3:-$MENU_COLOR_DEFAULT}"
  printf '%s-%s-%s' \
    "$(menu_layout_code "$layout_key")" \
    "$(menu_format_code "$format_key")" \
    "$(menu_color_code "$color_key")"
}

menu_style_label() {
  local layout_key="${1:-$MENU_LAYOUT_DEFAULT}"
  local format_key="${2:-$MENU_FORMAT_DEFAULT}"
  local color_key="${3:-$MENU_COLOR_DEFAULT}"
  printf '%s + %s + %s (%s)' \
    "$(menu_layout_name "$layout_key")" \
    "$(menu_format_name "$format_key")" \
    "$(menu_color_name "$color_key")" \
    "$(menu_style_codename "$layout_key" "$format_key" "$color_key")"
}

map_legacy_numeric_style() {
  local n="$1"
  case "$n" in
    0) MENU_LAYOUT="legacy"; MENU_FORMAT="thin"; MENU_COLOR="cyan" ;;
    1) MENU_LAYOUT="fortress"; MENU_FORMAT="bold"; MENU_COLOR="cyan" ;;
    2) MENU_LAYOUT="fortress"; MENU_FORMAT="bold"; MENU_COLOR="green" ;;
    3) MENU_LAYOUT="split"; MENU_FORMAT="thin"; MENU_COLOR="magenta" ;;
    4) MENU_LAYOUT="split"; MENU_FORMAT="thin"; MENU_COLOR="blue" ;;
    5) MENU_LAYOUT="telemetry"; MENU_FORMAT="ascii"; MENU_COLOR="yellow" ;;
    6) MENU_LAYOUT="telemetry"; MENU_FORMAT="ascii"; MENU_COLOR="green" ;;
    7) MENU_LAYOUT="split"; MENU_FORMAT="dashed"; MENU_COLOR="red" ;;
    8) MENU_LAYOUT="fortress"; MENU_FORMAT="double"; MENU_COLOR="blue" ;;
    9) MENU_LAYOUT="telemetry"; MENU_FORMAT="bold"; MENU_COLOR="magenta" ;;
    10) MENU_LAYOUT="telemetry"; MENU_FORMAT="double"; MENU_COLOR="magenta" ;;
    11) MENU_LAYOUT="fortress"; MENU_FORMAT="thin"; MENU_COLOR="green" ;;
    12) MENU_LAYOUT="telemetry"; MENU_FORMAT="dashed"; MENU_COLOR="yellow" ;;
    13) MENU_LAYOUT="bbs"; MENU_FORMAT="double"; MENU_COLOR="green" ;;
    14) MENU_LAYOUT="split"; MENU_FORMAT="bold"; MENU_COLOR="cyan" ;;
    15) MENU_LAYOUT="bbs"; MENU_FORMAT="thin"; MENU_COLOR="magenta" ;;
    16) MENU_LAYOUT="dos"; MENU_FORMAT="ascii"; MENU_COLOR="yellow" ;;
    *) MENU_LAYOUT="$MENU_LAYOUT_DEFAULT"; MENU_FORMAT="$MENU_FORMAT_DEFAULT"; MENU_COLOR="$MENU_COLOR_DEFAULT" ;;
  esac
}

load_menu_style() {
  MENU_LAYOUT="$MENU_LAYOUT_DEFAULT"
  MENU_FORMAT="$MENU_FORMAT_DEFAULT"
  MENU_COLOR="$MENU_COLOR_DEFAULT"
  if [[ ! -f "$MENU_STYLE_PATH" ]]; then
    return 0
  fi

  local saved part_a part_b part_c
  saved="$(tr -d '\r\n[:space:]' < "$MENU_STYLE_PATH" 2>/dev/null || true)"
  [[ -n "$saved" ]] || return 0

  if [[ "$saved" =~ ^[0-9]+$ ]]; then
    map_legacy_numeric_style "$saved"
    return 0
  fi

  if [[ "$saved" == *"="* ]]; then
    IFS='=' read -r part_a part_b part_c <<< "$saved"
  elif [[ "$saved" == *":"* ]]; then
    IFS=':' read -r part_a part_b part_c <<< "$saved"
  else
    part_a="$saved"
    part_b=""
    part_c=""
  fi

  if [[ -n "$part_c" ]]; then
    if [[ "$(menu_layout_index "$part_a")" -ge 0 ]]; then
      MENU_LAYOUT="$part_a"
    fi
    if [[ "$(menu_format_index "$part_b")" -ge 0 ]]; then
      MENU_FORMAT="$part_b"
    fi
    if [[ "$(menu_color_index "$part_c")" -ge 0 ]]; then
      MENU_COLOR="$part_c"
    fi
    return 0
  fi

  if [[ -n "$part_a" && -n "$part_b" ]]; then
    if [[ "$(menu_layout_index "$part_a")" -ge 0 && "$(menu_color_index "$part_b")" -ge 0 ]]; then
      MENU_LAYOUT="$part_a"
      MENU_COLOR="$part_b"
      return 0
    fi
    if [[ "$(menu_layout_index "$part_a")" -ge 0 && "$(menu_format_index "$part_b")" -ge 0 ]]; then
      MENU_LAYOUT="$part_a"
      MENU_FORMAT="$part_b"
      return 0
    fi
    if [[ "$(menu_format_index "$part_a")" -ge 0 && "$(menu_color_index "$part_b")" -ge 0 ]]; then
      MENU_FORMAT="$part_a"
      MENU_COLOR="$part_b"
      return 0
    fi
  fi

  if [[ "$(menu_layout_index "$part_a")" -ge 0 ]]; then
    MENU_LAYOUT="$part_a"
  elif [[ "$(menu_format_index "$part_a")" -ge 0 ]]; then
    MENU_FORMAT="$part_a"
  elif [[ "$(menu_color_index "$part_a")" -ge 0 ]]; then
    MENU_COLOR="$part_a"
  fi
}

save_menu_style() {
  local layout_key="${1:-$MENU_LAYOUT_DEFAULT}"
  local format_key="${2:-$MENU_FORMAT_DEFAULT}"
  local color_key="${3:-$MENU_COLOR_DEFAULT}"
  if [[ "$(menu_layout_index "$layout_key")" -lt 0 ]]; then
    layout_key="$MENU_LAYOUT_DEFAULT"
  fi
  if [[ "$(menu_format_index "$format_key")" -lt 0 ]]; then
    format_key="$MENU_FORMAT_DEFAULT"
  fi
  if [[ "$(menu_color_index "$color_key")" -lt 0 ]]; then
    color_key="$MENU_COLOR_DEFAULT"
  fi
  printf '%s=%s=%s\n' "$layout_key" "$format_key" "$color_key" > "$MENU_STYLE_PATH"
  chmod 600 "$MENU_STYLE_PATH" 2>/dev/null || true
  MENU_LAYOUT="$layout_key"
  MENU_FORMAT="$format_key"
  MENU_COLOR="$color_key"
}

ui_should_clear_screen() {
  [[ "${CODEX_NO_MENU_CLEAR:-0}" != "1" ]] || return 1
  [[ -t 1 || -t 2 || -t 0 || -r /dev/tty ]] || return 1
  return 0
}

ui_clear_screen() {
  ui_should_clear_screen || return 0
  if [[ -r /dev/tty ]]; then
    printf '\033[H\033[2J\033[3J' > /dev/tty 2>/dev/null || true
    return 0
  fi
  printf '\033[H\033[2J\033[3J' || true
}

build_request_payload() {
  local token="$1"
  local session_id="$2"
  local host_name system_name machine_name kernel_name os_pretty
  host_name="$(hostname 2>/dev/null || true)"
  system_name="$(uname -s 2>/dev/null || true)"
  machine_name="$(uname -m 2>/dev/null || true)"
  kernel_name="$(uname -r 2>/dev/null || true)"
  os_pretty="$(. /etc/os-release 2>/dev/null && printf '%s' "${PRETTY_NAME:-}")"

  CLIENT_HOST="$host_name" CLIENT_SYSTEM="$system_name" CLIENT_MACHINE="$machine_name" CLIENT_KERNEL="$kernel_name" CLIENT_OS_PRETTY="$os_pretty" \
  "$ROOT_DIR/node/bin/node" - "$token" "$session_id" "$RUN_SCRIPT_VERSION" <<'NODE'
const token = process.argv[2] || "";
const sessionId = process.argv[3] || "";
const scriptVersion = process.argv[4] || "";
const system = process.env.CLIENT_SYSTEM || "";
const osPretty = process.env.CLIENT_OS_PRETTY || "";
const kernel = process.env.CLIENT_KERNEL || "";
const machine = process.env.CLIENT_MACHINE || "";
const payload = {
  token,
  client: {
    hostname: process.env.CLIENT_HOST || "",
    system: [system, osPretty].filter(Boolean).join(" | "),
    machine: [machine, kernel].filter(Boolean).join(" | "),
    script_version: scriptVersion,
  },
};
if (sessionId) payload.session_id = sessionId;
process.stdout.write(JSON.stringify(payload));
NODE
}

build_install_payload() {
  local username="$1"
  local password="$2"
  "$ROOT_DIR/node/bin/node" - "$username" "$password" <<'NODE'
const username = process.argv[2] || "";
const password = process.argv[3] || "";
const payload = { password };
if (username) payload.username = username;
process.stdout.write(JSON.stringify(payload));
NODE
}

request_install() {
  local payload="$1"
  local urls=("$INSTALL_SERVER_URL")
  if [[ "$INSTALL_SERVER_URL" == "https://codex.naskel.systems/codex-install" ]]; then
    urls+=("$LEGACY_INSTALL_SERVER_URL")
  fi

  local url response=""
  for url in "${urls[@]}"; do
    if response="$(http_post "$url" "$payload" 2>/dev/null)"; then
      printf '%s' "$response"
      return 0
    fi
  done
  return 1
}

request_install_token_interactive() {
  local username password payload response install_token install_error

  if [[ ! -t 0 && ! -r /dev/tty ]]; then
    echo "Error: interactive login required, but no TTY is available." >&2
    return 1
  fi

  username="$(prompt "Account username (leave empty for legacy password mode)" "")"
  if [[ -n "$username" ]]; then
    password="$(prompt_secret "Account password")"
  else
    password="$(prompt_secret "Legacy install password")"
  fi

  payload="$(build_install_payload "$username" "$password")"
  echo "Requesting install token..."
  if ! response="$(request_install "$payload")"; then
    echo "Error: failed to contact install endpoint (${INSTALL_SERVER_URL} or ${LEGACY_INSTALL_SERVER_URL})." >&2
    return 1
  fi

  install_token="$(json_field "$response" "install_token")"
  install_error="$(json_field "$response" "error")"
  if [[ -z "$install_token" ]]; then
    case "$install_error" in
      invalid_password)
        echo "Error: invalid legacy install password." >&2
        ;;
      invalid_credentials)
        echo "Error: invalid account username or password." >&2
        ;;
      account_inactive)
        echo "Error: account is inactive." >&2
        ;;
      no_active_sessions)
        echo "Error: no active sessions are assigned to this account." >&2
        ;;
      password_required)
        echo "Error: password is required." >&2
        ;;
      *)
        echo "Error: install token was not returned." >&2
        ;;
    esac
    echo "Response: $response" >&2
    return 1
  fi

  printf '%s\n' "$install_token" > "$TOKEN_PATH"
  chmod 600 "$TOKEN_PATH"
  printf '%s\n' "$response" > "$CODEX_HOME/install-meta.json"
  chmod 600 "$CODEX_HOME/install-meta.json"
  TOKEN="$install_token"
  echo "Login successful."
  return 0
}

request_auth() {
  local token="$1"
  local session_id="${2:-}"
  local payload
  payload="$(build_request_payload "$token" "$session_id")"

  local urls=("$AUTH_SERVER_URL")
  if [[ "$AUTH_SERVER_URL" == "https://codex.naskel.systems/codex-auth" ]]; then
    urls+=("$LEGACY_AUTH_SERVER_URL")
  fi

  local attempt url response=""
  for attempt in 1 2 3; do
    for url in "${urls[@]}"; do
      if response="$(http_post "$url" "$payload" 2>/dev/null)"; then
        if is_auth_response_json "$response"; then
          printf '%s' "$response"
          return 0
        fi
      fi
    done
    sleep "$attempt"
  done

  # One final best-effort pass to surface backend error payloads quickly if available.
  for url in "${urls[@]}"; do
    if response="$(http_post "$url" "$payload" 2>/dev/null)"; then
      if is_auth_response_json "$response"; then
        printf '%s' "$response"
        return 0
      fi
    fi
  done

  return 1
}

update_run_script_from_installer() {
  local installer_url="$1"
  local tmp_installer tmp_run tmp_renderer
  tmp_installer="$(mktemp /tmp/codexport-runupd.XXXXXX.sh)"
  tmp_run="$(mktemp /tmp/run-upd.XXXXXX.sh)"
  tmp_renderer="$(mktemp /tmp/menu-style-render-upd.XXXXXX.sh)"

  if command -v curl >/dev/null 2>&1; then
    if ! curl -fsSL "$installer_url" -o "$tmp_installer"; then
      rm -f "$tmp_installer" "$tmp_run" "$tmp_renderer"
      echo "Warning: failed to download installer from $installer_url" >&2
      return 1
    fi
  elif command -v wget >/dev/null 2>&1; then
    if ! wget -qO "$tmp_installer" "$installer_url"; then
      rm -f "$tmp_installer" "$tmp_run" "$tmp_renderer"
      echo "Warning: failed to download installer from $installer_url" >&2
      return 1
    fi
  else
    rm -f "$tmp_installer" "$tmp_run" "$tmp_renderer"
    echo "Warning: need curl or wget to update run.sh" >&2
    return 1
  fi

  awk 'BEGIN{p=0} /cat > "\$OUT_DIR\/run\.sh" <<'\''RUNNER'\''/{p=1;next} /^RUNNER$/{p=0} p{print}' "$tmp_installer" > "$tmp_run"
  if ! grep -q '^RUN_SCRIPT_VERSION=' "$tmp_run"; then
    rm -f "$tmp_installer" "$tmp_run" "$tmp_renderer"
    echo "Warning: downloaded installer does not contain run.sh template" >&2
    return 1
  fi
  if ! bash -n "$tmp_run"; then
    rm -f "$tmp_installer" "$tmp_run" "$tmp_renderer"
    echo "Warning: downloaded run.sh template has syntax errors" >&2
    return 1
  fi

  awk 'BEGIN{p=0} /cat > "\$OUT_DIR\/menu-style-render\.sh" <<'\''MENU_STYLE_RENDERER'\''/{p=1;next} /^MENU_STYLE_RENDERER$/{p=0} p{print}' "$tmp_installer" > "$tmp_renderer" || true
  if [[ -s "$tmp_renderer" ]]; then
    if bash -n "$tmp_renderer"; then
      chmod +x "$tmp_renderer"
      mv "$tmp_renderer" "$ROOT_DIR/menu-style-render.sh"
      chmod +x "$ROOT_DIR/menu-style-render.sh"
    else
      echo "Warning: downloaded menu-style-render.sh template has syntax errors" >&2
      rm -f "$tmp_renderer"
    fi
  else
    rm -f "$tmp_renderer"
  fi

  chmod +x "$tmp_run"
  mv "$tmp_run" "$ROOT_DIR/run.sh"
  chmod +x "$ROOT_DIR/run.sh"
  rm -f "$tmp_installer"
  return 0
}

check_server_updates() {
  [[ "${CODEX_SKIP_RUN_UPDATE:-0}" == "1" ]] && return 0

  local urls=("$AUTH_VERSION_URL")
  if [[ "$AUTH_VERSION_URL" == "https://codex.naskel.systems/codex-auth/version" ]]; then
    urls+=("$LEGACY_AUTH_VERSION_URL")
  fi

  local info=""
  local url
  for url in "${urls[@]}"; do
    if info="$(http_get "$url" 2>/dev/null)"; then
      break
    fi
  done
  [[ -n "$info" ]] || return 0

  local remote_version latest_url
  remote_version="$(INFO_JSON="$info" "$ROOT_DIR/node/bin/node" -e '
try {
  const data = JSON.parse(process.env.INFO_JSON || "{}");
  process.stdout.write(String(data.script_version || ""));
} catch {
  process.stdout.write("");
}
')"
  latest_url="$(INFO_JSON="$info" "$ROOT_DIR/node/bin/node" -e '
try {
  const data = JSON.parse(process.env.INFO_JSON || "{}");
  process.stdout.write(String(data.latest_codexport_url || ""));
} catch {
  process.stdout.write("");
}
')"

  [[ -n "$remote_version" ]] || return 0
  if ! version_gt "$remote_version" "$RUN_SCRIPT_VERSION"; then
    return 0
  fi

  echo "Update available: run.sh $RUN_SCRIPT_VERSION -> server recommends $remote_version"
  if [[ -n "$latest_url" ]]; then
    echo "Run script source URL: $latest_url"
  fi

  if [[ "${CODEX_AUTO_RUN_UPDATE:-1}" != "0" && -n "$latest_url" ]]; then
    if update_run_script_from_installer "$latest_url"; then
      echo "Updated: $ROOT_DIR/run.sh"
      echo "Restarting run.sh to apply the new version..."
      CODEX_SKIP_RUN_UPDATE=1 exec "$ROOT_DIR/run.sh" "${ORIGINAL_ARGS[@]}"
    fi
    return 0
  fi

  if [[ -t 0 && -n "$latest_url" ]]; then
    local answer
    answer="$(prompt "Update $ROOT_DIR/run.sh now? (Y/n)" "Y")"
    if [[ "$answer" =~ ^([Yy]|)$ ]]; then
      if update_run_script_from_installer "$latest_url"; then
        echo "Updated: $ROOT_DIR/run.sh"
        echo "Restarting run.sh to apply the new version..."
        CODEX_SKIP_RUN_UPDATE=1 exec "$ROOT_DIR/run.sh" "${ORIGINAL_ARGS[@]}"
      fi
    fi
  fi
}

parse_auth_response() {
  local response="$1"

  AUTH_RESPONSE="$response" HOME_DIR="$RAM_DIR" "$ROOT_DIR/node/bin/node" -e '
const fs = require("fs");
const path = require("path");
let data = {};
try {
  data = JSON.parse(process.env.AUTH_RESPONSE || "{}");
} catch (e) {
  console.error("Invalid auth response JSON.");
  process.exit(2);
}
if (data.error) {
  console.error(`AUTH_ERROR:${data.error}`);
  process.exit(3);
}
if (!data.auth_json) {
  console.error("Auth response missing auth_json.");
  process.exit(4);
}
const homeDir = process.env.HOME_DIR;
fs.mkdirSync(homeDir, { recursive: true });
const authPath = path.join(homeDir, "auth.json");
const proxyPath = path.join(homeDir, "proxy.env");
const sessionsPath = path.join(homeDir, "sessions.json");
const selectedPath = path.join(homeDir, "selected_session.txt");
const accountPath = path.join(homeDir, "account.json");
const versionPath = path.join(homeDir, "version.json");

const authPayload = typeof data.auth_json === "string"
  ? data.auth_json
  : JSON.stringify(data.auth_json, null, 2);
fs.writeFileSync(authPath, authPayload + (authPayload.endsWith("\n") ? "" : "\n"));

const lines = [];
if (data.proxy_url) lines.push(`PROXY_URL="${data.proxy_url}"`);
if (data.no_proxy) lines.push(`NO_PROXY="${data.no_proxy}"`);
fs.writeFileSync(proxyPath, lines.join("\n") + (lines.length ? "\n" : ""));

const sessions = Array.isArray(data.available_sessions) ? data.available_sessions : [];
fs.writeFileSync(sessionsPath, JSON.stringify(sessions, null, 2) + "\n");
fs.writeFileSync(selectedPath, String(data.session_id || "") + "\n");

const account = (data.account && typeof data.account === "object")
  ? {
      id: Number.isFinite(Number(data.account.id)) ? Number(data.account.id) : null,
      username: typeof data.account.username === "string" ? data.account.username : "",
    }
  : null;
fs.writeFileSync(accountPath, JSON.stringify(account, null, 2) + "\n");

const versionInfo = {
  script_version: typeof data.script_version === "string" ? data.script_version : "",
  server_version: typeof data.server_version === "string" ? data.server_version : "",
  latest_codexport_url: typeof data.latest_codexport_url === "string" ? data.latest_codexport_url : "",
};
fs.writeFileSync(versionPath, JSON.stringify(versionInfo, null, 2) + "\n");
' 2> "$RAM_DIR/parse.err"
}

refresh_auth_snapshot() {
  load_token
  [[ -n "$TOKEN" ]] || return 1
  load_selected_session

  local auth_response="" auth_error=""
  if [[ -n "$SELECTED_SESSION" ]]; then
    auth_response="$(request_auth "$TOKEN" "$SELECTED_SESSION" || true)"
    auth_error="$(json_field "$auth_response" "error")"
    if [[ "$auth_error" == "invalid_session" ]]; then
      auth_response=""
      SELECTED_SESSION=""
    fi
  fi

  if [[ -z "$auth_response" ]]; then
    auth_response="$(request_auth "$TOKEN" "" || true)"
  fi

  [[ -n "$auth_response" ]] || return 1
  parse_auth_response "$auth_response" || return 1

  SELECTED_SESSION="$(tr -d '\r\n' < "$RAM_DIR/selected_session.txt" 2>/dev/null || true)"
  if [[ -n "$SELECTED_SESSION" ]]; then
    printf '%s\n' "$SELECTED_SESSION" > "$CODEX_HOME/selected_session"
  fi
  return 0
}

print_account_and_sessions_overview() {
  local sessions_file="$RAM_DIR/sessions.json"
  local selected_session="${SELECTED_SESSION:-}"
  local account_file="$RAM_DIR/account.json"
  local version_file="$RAM_DIR/version.json"
  local install_meta_file="$CODEX_HOME/install-meta.json"
  local menu_layout="${1:-$MENU_LAYOUT}"
  local menu_format="${2:-$MENU_FORMAT}"
  local menu_color="${3:-$MENU_COLOR}"
  local renderer="$ROOT_DIR/menu-style-render.sh"
  local renderer_node="$ROOT_DIR/node/bin/node"

  if [[ ! -x "$renderer" ]]; then
    echo "  Script version: $RUN_SCRIPT_VERSION"
    echo "  Account: unavailable"
    echo "  Selected session: ${selected_session:-none}"
    echo "  Sessions: renderer unavailable"
    return 0
  fi

  if [[ ! -x "$renderer_node" ]]; then
    renderer_node="${NODE_BIN:-node}"
  fi

  if ! NODE_BIN="$renderer_node" "$renderer" \
    --no-clear \
    --no-menu \
    --layout "$menu_layout" \
    --format "$menu_format" \
    --color "$menu_color" \
    --sessions-file "$sessions_file" \
    --selected-session "$selected_session" \
    --account-file "$account_file" \
    --version-file "$version_file" \
    --install-meta-file "$install_meta_file" \
    --local-run-version "$RUN_SCRIPT_VERSION"; then
    echo "  Script version: $RUN_SCRIPT_VERSION"
    echo "  Account: unavailable"
    echo "  Selected session: ${selected_session:-none}"
    echo "  Sessions: renderer error"
  fi
}

choose_menu_style() {
  local preview_layout preview_format preview_color
  local current_layout current_format current_color
  local layout_idx fmt_idx col_idx pick idx_val action
  load_menu_style
  current_layout="$MENU_LAYOUT"
  current_format="$MENU_FORMAT"
  current_color="$MENU_COLOR"
  preview_layout="$current_layout"
  preview_format="$current_format"
  preview_color="$current_color"

  if [[ ! -f "$RAM_DIR/sessions.json" ]]; then
    echo "No session snapshot available for preview. Login first." >&2
    return 1
  fi

  while true; do
    ui_clear_screen
    echo "Codex style picker (layout + format + color)"
    echo "  Saved:   $(menu_style_label "$current_layout" "$current_format" "$current_color")"
    echo "  Preview: $(menu_style_label "$preview_layout" "$preview_format" "$preview_color")"
    echo
    print_account_and_sessions_overview "$preview_layout" "$preview_format" "$preview_color"
    echo
    echo "Layout keys: ${MENU_LAYOUT_KEYS[*]}"
    echo "Format keys: ${MENU_FORMAT_KEYS[*]}"
    echo "Color keys:  ${MENU_COLOR_KEYS[*]}"
    echo "n) Next layout   p) Prev layout   f) Next format  r) Prev format"
    echo "k) Next color    j) Prev color    l) Pick layout  m) Pick format"
    echo "c) Pick color    s) Save          q) Cancel"
    action="$(prompt "Action (or layout/format/color key)" "n")"
    case "$action" in
      n|N|next|NEXT)
        layout_idx="$(menu_layout_index "$preview_layout")"
        [[ "$layout_idx" -lt 0 ]] && layout_idx=0
        layout_idx=$((layout_idx + 1))
        if (( layout_idx >= ${#MENU_LAYOUT_KEYS[@]} )); then
          layout_idx=0
        fi
        preview_layout="${MENU_LAYOUT_KEYS[$layout_idx]}"
        ;;
      p|P|prev|PREV|previous|PREVIOUS)
        layout_idx="$(menu_layout_index "$preview_layout")"
        [[ "$layout_idx" -lt 0 ]] && layout_idx=0
        layout_idx=$((layout_idx - 1))
        if (( layout_idx < 0 )); then
          layout_idx=$((${#MENU_LAYOUT_KEYS[@]} - 1))
        fi
        preview_layout="${MENU_LAYOUT_KEYS[$layout_idx]}"
        ;;
      f|F|next-format|NEXT-FORMAT)
        fmt_idx="$(menu_format_index "$preview_format")"
        [[ "$fmt_idx" -lt 0 ]] && fmt_idx=0
        fmt_idx=$((fmt_idx + 1))
        if (( fmt_idx >= ${#MENU_FORMAT_KEYS[@]} )); then
          fmt_idx=0
        fi
        preview_format="${MENU_FORMAT_KEYS[$fmt_idx]}"
        ;;
      r|R|prev-format|PREV-FORMAT)
        fmt_idx="$(menu_format_index "$preview_format")"
        [[ "$fmt_idx" -lt 0 ]] && fmt_idx=0
        fmt_idx=$((fmt_idx - 1))
        if (( fmt_idx < 0 )); then
          fmt_idx=$((${#MENU_FORMAT_KEYS[@]} - 1))
        fi
        preview_format="${MENU_FORMAT_KEYS[$fmt_idx]}"
        ;;
      k|K|next-color|NEXT-COLOR)
        col_idx="$(menu_color_index "$preview_color")"
        [[ "$col_idx" -lt 0 ]] && col_idx=0
        col_idx=$((col_idx + 1))
        if (( col_idx >= ${#MENU_COLOR_KEYS[@]} )); then
          col_idx=0
        fi
        preview_color="${MENU_COLOR_KEYS[$col_idx]}"
        ;;
      j|J|prev-color|PREV-COLOR)
        col_idx="$(menu_color_index "$preview_color")"
        [[ "$col_idx" -lt 0 ]] && col_idx=0
        col_idx=$((col_idx - 1))
        if (( col_idx < 0 )); then
          col_idx=$((${#MENU_COLOR_KEYS[@]} - 1))
        fi
        preview_color="${MENU_COLOR_KEYS[$col_idx]}"
        ;;
      l|L|layout|LAYOUT)
        echo
        echo "Layouts:"
        for idx_val in "${!MENU_LAYOUT_KEYS[@]}"; do
          printf "  %d) %s [%s] code=%s\n" \
            "$idx_val" \
            "${MENU_LAYOUT_NAMES[$idx_val]}" \
            "${MENU_LAYOUT_KEYS[$idx_val]}" \
            "${MENU_LAYOUT_CODES[$idx_val]}"
        done
        pick="$(prompt "Layout index" "")"
        if [[ "$pick" =~ ^[0-9]+$ ]] && (( pick >= 0 && pick < ${#MENU_LAYOUT_KEYS[@]} )); then
          preview_layout="${MENU_LAYOUT_KEYS[$pick]}"
        else
          echo "Invalid layout index: $pick"
          sleep 1
        fi
        ;;
      m|M|format|FORMAT)
        echo
        echo "Formats:"
        for idx_val in "${!MENU_FORMAT_KEYS[@]}"; do
          printf "  %d) %s [%s] code=%s\n" \
            "$idx_val" \
            "${MENU_FORMAT_NAMES[$idx_val]}" \
            "${MENU_FORMAT_KEYS[$idx_val]}" \
            "${MENU_FORMAT_CODES[$idx_val]}"
        done
        pick="$(prompt "Format index" "")"
        if [[ "$pick" =~ ^[0-9]+$ ]] && (( pick >= 0 && pick < ${#MENU_FORMAT_KEYS[@]} )); then
          preview_format="${MENU_FORMAT_KEYS[$pick]}"
        else
          echo "Invalid format index: $pick"
          sleep 1
        fi
        ;;
      c|C|color|COLOR)
        echo
        echo "Colors:"
        for idx_val in "${!MENU_COLOR_KEYS[@]}"; do
          printf "  %d) %s [%s] code=%s\n" \
            "$idx_val" \
            "${MENU_COLOR_NAMES[$idx_val]}" \
            "${MENU_COLOR_KEYS[$idx_val]}" \
            "${MENU_COLOR_CODES[$idx_val]}"
        done
        pick="$(prompt "Color index" "")"
        if [[ "$pick" =~ ^[0-9]+$ ]] && (( pick >= 0 && pick < ${#MENU_COLOR_KEYS[@]} )); then
          preview_color="${MENU_COLOR_KEYS[$pick]}"
        else
          echo "Invalid color index: $pick"
          sleep 1
        fi
        ;;
      s|S|select|SELECT)
        save_menu_style "$preview_layout" "$preview_format" "$preview_color"
        echo "Selected style: $(menu_style_label "$MENU_LAYOUT" "$MENU_FORMAT" "$MENU_COLOR")"
        return 0
        ;;
      q|Q|quit|QUIT|exit|EXIT|cancel|CANCEL)
        echo "Style selection cancelled."
        return 0
        ;;
      *)
        if [[ "$(menu_layout_index "$action")" -ge 0 ]]; then
          preview_layout="$action"
        elif [[ "$(menu_format_index "$action")" -ge 0 ]]; then
          preview_format="$action"
        elif [[ "$(menu_color_index "$action")" -ge 0 ]]; then
          preview_color="$action"
        else
          echo "Unknown action: $action"
          sleep 1
        fi
        ;;
    esac
  done
}

choose_session_if_needed() {
  local force_prompt="${1:-0}"
  local sessions_file="$RAM_DIR/sessions.json"
  [[ -f "$sessions_file" ]] || return 0

  local count
  count="$($ROOT_DIR/node/bin/node - "$sessions_file" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
try {
  const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
  process.stdout.write(String(Array.isArray(sessions) ? sessions.length : 0));
} catch {
  process.stdout.write("0");
}
NODE
)"

  if [[ "$count" -le 0 ]]; then
    echo "Error: auth server returned zero available sessions." >&2
    return 1
  fi

  local selected_session chosen_id default_index choice
  selected_session="$(tr -d '\r\n' < "$RAM_DIR/selected_session.txt" 2>/dev/null || true)"

  if [[ "$count" -eq 1 ]]; then
    chosen_id="$($ROOT_DIR/node/bin/node - "$sessions_file" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
try {
  const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
  if (Array.isArray(sessions) && sessions[0] && sessions[0].id) {
    process.stdout.write(String(sessions[0].id));
  }
} catch {}
NODE
)"
    if [[ -n "$chosen_id" ]]; then
      SELECTED_SESSION="$chosen_id"
      printf '%s\n' "$chosen_id" > "$CODEX_HOME/selected_session"
    fi
    return 0
  fi

  if [[ "$force_prompt" -ne 1 && -n "$selected_session" ]]; then
    SELECTED_SESSION="$selected_session"
    printf '%s\n' "$selected_session" > "$CODEX_HOME/selected_session"
    return 0
  fi

  if [[ ! -t 0 ]]; then
    chosen_id="$($ROOT_DIR/node/bin/node - "$sessions_file" "$selected_session" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
const selected = process.argv[3] || "";
try {
  const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
  if (!Array.isArray(sessions) || sessions.length === 0) process.exit(0);
  let chosen = sessions.find((s) => s.id === selected);
  if (!chosen) chosen = sessions.find((s) => s.is_default) || sessions[0];
  process.stdout.write(String(chosen.id || ""));
} catch {}
NODE
)"
    if [[ -n "$chosen_id" ]]; then
      SELECTED_SESSION="$chosen_id"
      printf '%s\n' "$chosen_id" > "$CODEX_HOME/selected_session"
    fi
    return 0
  fi

  ui_clear_screen
  echo "Available sessions:"
  "$ROOT_DIR/node/bin/node" - "$sessions_file" "$selected_session" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
const selected = process.argv[3] || "";
const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
const useColor = Boolean(process.stdout && process.stdout.isTTY && !process.env.NO_COLOR);
const C = useColor
  ? {
      reset: "\x1b[0m",
      dim: "\x1b[2m",
      cyan: "\x1b[36m",
      magenta: "\x1b[35m",
      green: "\x1b[32m",
      yellow: "\x1b[33m",
      red: "\x1b[31m",
    }
  : { reset: "", dim: "", cyan: "", magenta: "", green: "", yellow: "", red: "" };

function paint(text, color) {
  const value = String(text ?? "");
  if (!useColor || !color) return value;
  return `${color}${value}${C.reset}`;
}

function shortHealthMessage(message) {
  const msg = String(message || "");
  if (!msg || msg === "ok") return "";
  if (msg.startsWith("usage_request_failed")) return "usage unavailable";
  if (msg.startsWith("http_401") || msg.startsWith("http_403")) return "access denied";
  if (msg.startsWith("access_token_expired")) return "token expired";
  if (msg === "api_key_mode_no_wham_limits") return "no limits for api_key";
  return msg.length > 36 ? `${msg.slice(0, 36)}...` : msg;
}

function fmtPercent(value) {
  const num = Number(value);
  if (!Number.isFinite(num)) return "";
  const clamped = Math.max(0, Math.min(100, num));
  const rounded = Math.round(clamped * 10) / 10;
  return Number.isInteger(rounded) ? `${rounded}%` : `${rounded.toFixed(1)}%`;
}

function fmtReset(resetAt) {
  const ts = Number(resetAt);
  if (!Number.isFinite(ts) || ts <= 0) return "-";
  const d = new Date(ts * 1000);
  return `${d.toLocaleDateString()} ${d.toLocaleTimeString()}`;
}

function clipText(value, width) {
  const text = String(value || "");
  const max = Number(width) || 0;
  if (max <= 0 || text.length <= max) return text;
  if (max <= 1) return text.slice(0, max);
  return `${text.slice(0, max - 1)}…`;
}

function padCell(value, width) {
  return clipText(value, width).padEnd(width, " ");
}

function toneColor(tone) {
  if (tone === "good") return C.green;
  if (tone === "warn") return C.yellow;
  if (tone === "bad") return C.red;
  if (tone === "fallback") return C.magenta;
  return "";
}

function cell(value, width, color = "") {
  const padded = padCell(value, width);
  return color ? paint(padded, color) : padded;
}

function fmtLimit(limit, fallback) {
  if (!limit || typeof limit !== "object") return { text: fallback || "n/a", tone: fallback ? "fallback" : "neutral" };
  const remainingRaw = Number(limit.remaining_percent);
  const remainingText = fmtPercent(remainingRaw);
  if (!remainingText) return { text: fallback || "n/a", tone: fallback ? "fallback" : "neutral" };
  let tone = "good";
  if (Number.isFinite(remainingRaw)) {
    if (remainingRaw <= 25) {
      tone = "bad";
    } else if (remainingRaw <= 60) {
      tone = "warn";
    }
  }
  return { text: `${remainingText} reset ${fmtReset(limit.reset_at)}`, tone };
}

const SESSION_WIDTH = 34;
const LIMIT_WIDTH = 30;
const STATE_WIDTH = 8;

const headerLine = `${padCell("Session", SESSION_WIDTH)} | ${padCell("5h limit", LIMIT_WIDTH)} | ${padCell("7d limit", LIMIT_WIDTH)} | ${padCell("State", STATE_WIDTH)}`;
console.log(`    ${paint(headerLine, C.dim)}`);

sessions.forEach((s, i) => {
  const id = String(s.id || "");
  const health = s && typeof s.health === "object" ? s.health : {};
  const fallback = shortHealthMessage(health.message);
  const limit5h = fmtLimit(health.limit_5h, fallback);
  const limit7d = fmtLimit(health.limit_weekly, fallback);
  const stateRaw = health.is_valid === true ? "valid" : (health.is_valid === false ? "invalid" : "unknown");
  const stateColor = stateRaw === "valid" ? C.green : (stateRaw === "invalid" ? C.red : C.yellow);
  const roleMark = id === selected ? " current" : (s.is_default ? " default" : "");
  const sessionText = `${s.name || id} [${id}]${roleMark}`;
  const sessionCell = id === selected ? cell(sessionText, SESSION_WIDTH, C.cyan) : cell(sessionText, SESSION_WIDTH);
  const limit5Cell = cell(limit5h.text, LIMIT_WIDTH, toneColor(limit5h.tone));
  const limit7Cell = cell(limit7d.text, LIMIT_WIDTH, toneColor(limit7d.tone));
  const stateCell = cell(stateRaw, STATE_WIDTH, stateColor);
  const idxText = String(i + 1).padStart(2, " ");
  console.log(`${idxText}) ${sessionCell} | ${limit5Cell} | ${limit7Cell} | ${stateCell}`);
});
NODE

  default_index="$($ROOT_DIR/node/bin/node - "$sessions_file" "$selected_session" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
const selected = process.argv[3] || "";
try {
  const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
  let idx = sessions.findIndex((s) => s.id === selected);
  if (idx < 0) idx = sessions.findIndex((s) => s.is_default);
  if (idx < 0) idx = 0;
  process.stdout.write(String(idx + 1));
} catch {
  process.stdout.write("1");
}
NODE
)"

  choice="$(prompt "Session number" "$default_index")"
  chosen_id="$($ROOT_DIR/node/bin/node - "$sessions_file" "$choice" <<'NODE'
const fs = require("fs");
const file = process.argv[2];
const choiceRaw = process.argv[3] || "1";
const sessions = JSON.parse(fs.readFileSync(file, "utf8"));
const idx = Number(choiceRaw);
if (Number.isInteger(idx) && idx >= 1 && idx <= sessions.length) {
  process.stdout.write(String(sessions[idx - 1].id || ""));
}
NODE
)"

  if [[ -z "$chosen_id" ]]; then
    echo "Error: invalid session choice." >&2
    return 1
  fi

  if [[ "$chosen_id" != "$selected_session" ]]; then
    AUTH_RESPONSE="$(request_auth "$TOKEN" "$chosen_id" || true)"
    if [[ -z "$AUTH_RESPONSE" ]]; then
      echo "Error: failed to request selected session '$chosen_id'." >&2
      return 1
    fi
    if ! parse_auth_response "$AUTH_RESPONSE"; then
      echo "Error while parsing selected session response:" >&2
      cat "$RAM_DIR/parse.err" >&2 || true
      return 1
    fi
  fi

  SELECTED_SESSION="$chosen_id"
  printf '%s\n' "$chosen_id" > "$CODEX_HOME/selected_session"
}

clear_login_state() {
  rm -f "$TOKEN_PATH" "$CODEX_HOME/install-meta.json" "$CODEX_HOME/selected_session" "$CODEX_HOME/auth.json"
}

npm_install_codex() {
  local npm_bin="$ROOT_DIR/node/bin/npm"
  if [[ ! -x "$npm_bin" ]]; then
    echo "Error: npm not found at $npm_bin" >&2
    return 1
  fi
  NPM_CONFIG_OPTIONAL=true NPM_CONFIG_INCLUDE=optional "$npm_bin" install -g --include=optional --prefix "$ROOT_DIR/codex" "$@"
}

find_codex_js() {
  local candidates=(
    "$ROOT_DIR/codex/node_modules/@openai/codex/bin/codex.js"
    "$ROOT_DIR/codex/node_modules/@openai/codex/dist/cli.js"
    "$ROOT_DIR/codex/lib/node_modules/@openai/codex/bin/codex.js"
    "$ROOT_DIR/codex/lib/node_modules/@openai/codex/dist/cli.js"
  )
  local candidate=""
  for candidate in "${candidates[@]}"; do
    if [[ -f "$candidate" ]]; then
      printf '%s\n' "$candidate"
      return 0
    fi
  done
  candidate="$(find "$ROOT_DIR/codex" -type f \( -name 'codex.js' -o -name 'cli.js' \) 2>/dev/null | grep '/@openai/codex/' | head -n1 || true)"
  if [[ -n "$candidate" ]]; then
    printf '%s\n' "$candidate"
    return 0
  fi
  return 1
}

extract_missing_optional_dep() {
  local err_file="$1"
  sed -n 's/.*Missing optional dependency \(@openai\/[A-Za-z0-9._-]*\).*/\1/p' "$err_file" | head -n1
}

ensure_codex_runtime_ready() {
  local node_bin="$ROOT_DIR/node/bin/node"
  if [[ ! -x "$node_bin" ]]; then
    echo "Error: node not found at $node_bin" >&2
    return 1
  fi

  CODEX_JS_PATH="$(find_codex_js || true)"
  if [[ -z "$CODEX_JS_PATH" ]]; then
    echo "Error: codex.js not found under $ROOT_DIR/codex" >&2
    return 1
  fi

  local err_file="$RAM_DIR/codex-runtime-check.err"
  if "$node_bin" "$CODEX_JS_PATH" --version >/dev/null 2>"$err_file"; then
    return 0
  fi

  local missing_pkg
  missing_pkg="$(extract_missing_optional_dep "$err_file")"
  if [[ -n "$missing_pkg" ]]; then
    echo "Detected missing Codex dependency: $missing_pkg"
    echo "Repairing Codex installation..."
    if npm_install_codex @openai/codex@latest "$missing_pkg"; then
      CODEX_JS_PATH="$(find_codex_js || true)"
      if [[ -n "$CODEX_JS_PATH" ]] && "$node_bin" "$CODEX_JS_PATH" --version >/dev/null 2>"$err_file"; then
        echo "Codex runtime repaired."
        return 0
      fi
    fi
    echo "Error: failed to repair missing dependency: $missing_pkg" >&2
  else
    echo "Error: Codex runtime preflight failed." >&2
  fi
  cat "$err_file" >&2 || true
  return 1
}

update_codex() {
  echo "Updating installed Codex CLI..."
  if ! npm_install_codex @openai/codex@latest; then
    echo "Error: failed to update Codex package." >&2
    return 1
  fi
  if ! ensure_codex_runtime_ready; then
    return 1
  fi
  echo "Codex update complete."
}

show_help() {
  cat <<'HELP'
Usage:
  run.sh                   # interactive menu
  run.sh menu
  run.sh login [codex args...]
  run.sh logout
  run.sh style
  run.sh update-codex
  run.sh [codex args...]

Commands:
  menu          Open interactive menu
  login         Force re-login and refresh install token, then start Codex
  logout        Remove local token/session selection
  style         Open menu style picker (layout + format + color)
  (Menu only)   Toggle auto YOLO mode (adds --yolo to Start Codex)
  update-codex  Update installed @openai/codex package
HELP
}

ensure_auth_ready() {
  load_token
  if [[ -z "$TOKEN" ]]; then
    echo "No saved login token found. Please login."
    request_install_token_interactive || return 1
  fi

  load_selected_session

  while true; do
    AUTH_RESPONSE=""
    if [[ -n "$SELECTED_SESSION" ]]; then
      AUTH_RESPONSE="$(request_auth "$TOKEN" "$SELECTED_SESSION" || true)"
      AUTH_ERROR="$(json_field "$AUTH_RESPONSE" "error")"
      if [[ "$AUTH_ERROR" == "invalid_session" ]]; then
        AUTH_RESPONSE=""
        SELECTED_SESSION=""
        rm -f "$CODEX_HOME/selected_session"
      fi
    fi

    if [[ -z "$AUTH_RESPONSE" ]]; then
      AUTH_RESPONSE="$(request_auth "$TOKEN" "" || true)"
    fi

    if [[ -z "$AUTH_RESPONSE" ]]; then
      echo "Error: failed to contact auth server." >&2
      return 1
    fi

    if parse_auth_response "$AUTH_RESPONSE"; then
      SELECTED_SESSION="$(tr -d '\r\n' < "$RAM_DIR/selected_session.txt" 2>/dev/null || true)"
      if [[ -n "$SELECTED_SESSION" ]]; then
        printf '%s\n' "$SELECTED_SESSION" > "$CODEX_HOME/selected_session"
      fi
      return 0
    fi

    AUTH_ERROR="$(parse_auth_error)"
    if [[ -z "$AUTH_ERROR" ]]; then
      echo "Error while parsing auth response:" >&2
      cat "$RAM_DIR/parse.err" >&2 || true
      return 1
    fi

    case "$AUTH_ERROR" in
      invalid_token|account_inactive)
        echo "Saved token is invalid or expired (${AUTH_ERROR})."
        clear_login_state
        TOKEN=""
        SELECTED_SESSION=""
        request_install_token_interactive || return 1
        ;;
      no_sessions_assigned|no_sessions_configured)
        echo "Access denied: no sessions are assigned to this account." >&2
        return 1
        ;;
      *)
        echo "Auth error: $AUTH_ERROR" >&2
        return 1
        ;;
    esac
  done
}

prepare_runtime_env() {
  ln -sf "$RAM_DIR/auth.json" "$CODEX_HOME/auth.json"
  export HOME="$ROOT_DIR"
  export CODEX_HOME="$CODEX_HOME"

  if [[ -f "$RAM_DIR/proxy.env" ]]; then
    . "$RAM_DIR/proxy.env"
  fi
  if [[ -n "${PROXY_URL:-}" ]]; then
    # Keep HTTPS/ALL proxy exactly as server provided.
    # For plain HTTP requests, provide a normalized http:// variant for compatibility.
    local codex_http_proxy="$PROXY_URL"
    if [[ "$codex_http_proxy" == https://* ]]; then
      codex_http_proxy="http://${codex_http_proxy#https://}"
    fi
    export HTTP_PROXY="$codex_http_proxy"
    export http_proxy="$codex_http_proxy"
    export HTTPS_PROXY="$PROXY_URL"
    export https_proxy="$PROXY_URL"
    # Some MCP clients fail on ALL_PROXY with https:// scheme.
    # Keep it unset so they rely on HTTP(S)_PROXY.
    unset ALL_PROXY
    unset all_proxy
  fi
  local openai_no_proxy combined_no_proxy
  openai_no_proxy="chatgpt.com,chat.openai.com,api.openai.com,auth.openai.com,openai.com"
  combined_no_proxy="${NO_PROXY:-}"
  # Default is strict proxy mode (no direct OpenAI bypass).
  if [[ "${CODEX_BYPASS_PROXY_FOR_OPENAI:-0}" == "1" ]]; then
    if [[ -n "$combined_no_proxy" ]]; then
      combined_no_proxy="${combined_no_proxy},${openai_no_proxy}"
    else
      combined_no_proxy="${openai_no_proxy}"
    fi
  fi
  if [[ -n "$combined_no_proxy" ]]; then
    export NO_PROXY="$combined_no_proxy"
    export no_proxy="$combined_no_proxy"
  fi

  export PATH="$ROOT_DIR/node/bin:$ROOT_DIR/codex/bin:$PATH"
}

start_codex() {
  ensure_auth_ready || return 1
  choose_session_if_needed 0 || return 1
  load_auto_yolo
  prepare_runtime_env
  ensure_codex_runtime_ready || return 1

  local -a codex_args
  codex_args=("$@")
  if [[ "${AUTO_YOLO:-0}" == "1" ]]; then
    local arg has_yolo
    has_yolo=0
    for arg in "${codex_args[@]}"; do
      if [[ "$arg" == "--yolo" ]]; then
        has_yolo=1
        break
      fi
    done
    if [[ "$has_yolo" -eq 0 ]]; then
      codex_args=(--yolo "${codex_args[@]}")
    fi
  fi

  exec "$ROOT_DIR/node/bin/node" \
    "$CODEX_JS_PATH" \
    "${codex_args[@]}"
}

open_interactive_menu() {
  while true; do
    load_token
    load_selected_session
    load_auto_yolo
    load_menu_style
    local snapshot_ok=0
    if [[ -n "$TOKEN" ]]; then
      if refresh_auth_snapshot; then
        snapshot_ok=1
        load_selected_session
      fi
    fi

    ui_clear_screen
    echo "Codex menu:"
    if [[ -n "$TOKEN" && "$snapshot_ok" -eq 1 ]]; then
      echo "  Login state: logged in"
    elif [[ -n "$TOKEN" ]]; then
      echo "  Login state: token saved (re-login may be required)"
    else
      echo "  Login state: not logged in"
    fi
    if [[ "$snapshot_ok" -eq 1 ]]; then
      print_account_and_sessions_overview "$MENU_LAYOUT" "$MENU_FORMAT" "$MENU_COLOR"
    else
      echo "  Script version: $RUN_SCRIPT_VERSION"
      echo "  Selected session: ${SELECTED_SESSION:-none}"
      echo "  Menu style: $(menu_style_label "$MENU_LAYOUT" "$MENU_FORMAT" "$MENU_COLOR")"
    fi
    echo "  YOLO auto-start: $(auto_yolo_label)"
    echo "1) Start Codex"
    echo "2) Choose session"
    echo "3) Login / Re-login"
    echo "4) Logout"
    echo "5) Update Codex"
    echo "6) Choose menu style"
    echo "7) Toggle YOLO auto-start"
    echo "8) Exit"

    choice="$(prompt "Choice" "1")"
    case "$choice" in
      1)
        ui_clear_screen
        start_codex
        return $?
        ;;
      2)
        ensure_auth_ready || continue
        choose_session_if_needed 1 || true
        ;;
      3)
        request_install_token_interactive || true
        ;;
      4)
        clear_login_state
        echo "Logged out. Local token and selected session removed."
        ;;
      5)
        update_codex || true
        ;;
      6)
        if [[ "$snapshot_ok" -eq 1 ]]; then
          choose_menu_style || true
        else
          echo "Style preview requires an active login snapshot."
        fi
        ;;
      7)
        toggle_auto_yolo
        ;;
      8|q|quit|exit)
        exit 0
        ;;
      *)
        echo "Unknown choice: $choice"
        ;;
    esac
  done
}

case "${1:-}" in
  --help|-h|help)
    show_help
    exit 0
    ;;
  menu)
    shift
    check_server_updates || true
    open_interactive_menu
    exit $?
    ;;
  style)
    shift
    check_server_updates || true
    ensure_auth_ready || exit 1
    refresh_auth_snapshot || true
    load_menu_style
    choose_menu_style
    exit $?
    ;;
  logout)
    clear_login_state
    echo "Logged out. Local token and selected session removed."
    exit 0
    ;;
  update-codex)
    update_codex
    exit $?
    ;;
  login)
    shift
    check_server_updates || true
    request_install_token_interactive || exit 1
    start_codex "$@"
    ;;
esac

if [[ $# -eq 0 && -t 0 ]]; then
  check_server_updates || true
  open_interactive_menu
  exit $?
fi

check_server_updates || true
start_codex "$@"
RUNNER

    cat > "$OUT_DIR/menu-style-render.sh" <<'MENU_STYLE_RENDERER'
#!/usr/bin/env bash
set -euo pipefail

NO_PAUSE=0
NO_CLEAR=0
NO_MENU=0
ONLY_STYLE=""
LAYOUT_KEY=""
FORMAT_KEY=""
COLOR_KEY=""
SESSIONS_FILE=""
SELECTED_SESSION_ARG=""
ACCOUNT_FILE=""
VERSION_FILE=""
INSTALL_META_FILE=""
LOCAL_RUN_VERSION_ARG=""

while [[ $# -gt 0 ]]; do
  case "$1" in
    --no-pause)
      NO_PAUSE=1
      ;;
    --no-clear)
      NO_CLEAR=1
      ;;
    --style)
      if [[ -n "${2:-}" ]]; then
        ONLY_STYLE="$2"
        shift
      fi
      ;;
    --layout)
      if [[ -n "${2:-}" ]]; then
        LAYOUT_KEY="$2"
        shift
      fi
      ;;
    --format)
      if [[ -n "${2:-}" ]]; then
        FORMAT_KEY="$2"
        shift
      fi
      ;;
    --color)
      if [[ -n "${2:-}" ]]; then
        COLOR_KEY="$2"
        shift
      fi
      ;;
    --no-menu)
      NO_MENU=1
      ;;
    --sessions-file)
      if [[ -n "${2:-}" ]]; then
        SESSIONS_FILE="$2"
        shift
      fi
      ;;
    --selected-session)
      if [[ -n "${2:-}" ]]; then
        SELECTED_SESSION_ARG="$2"
        shift
      fi
      ;;
    --account-file)
      if [[ -n "${2:-}" ]]; then
        ACCOUNT_FILE="$2"
        shift
      fi
      ;;
    --version-file)
      if [[ -n "${2:-}" ]]; then
        VERSION_FILE="$2"
        shift
      fi
      ;;
    --install-meta-file)
      if [[ -n "${2:-}" ]]; then
        INSTALL_META_FILE="$2"
        shift
      fi
      ;;
    --local-run-version)
      if [[ -n "${2:-}" ]]; then
        LOCAL_RUN_VERSION_ARG="$2"
        shift
      fi
      ;;
  esac
  shift
done

if [[ -t 1 && -z "${NO_COLOR:-}" ]]; then
  C_RESET='\033[0m'
  C_BOLD='\033[1m'
  C_DIM='\033[2m'
  C_GREEN='\033[32m'
  C_YELLOW='\033[33m'
  C_RED='\033[31m'
  C_CYAN='\033[36m'
  C_MAGENTA='\033[35m'
  C_BLUE='\033[34m'
else
  C_RESET=''
  C_BOLD=''
  C_DIM=''
  C_GREEN=''
  C_YELLOW=''
  C_RED=''
  C_CYAN=''
  C_MAGENTA=''
  C_BLUE=''
fi

LAYOUT_KEYS=("legacy" "fortress" "split" "telemetry" "bbs" "dos" "stack" "gridbar" "pulse")
LAYOUT_NAMES=("Legacy Console" "Fortress Grid" "Split Panels" "Telemetry Stream" "Retro BBS" "DOS Console" "Stacked Bars" "Grid Bars" "Pulse Meters")
LAYOUT_CODES=("LEGACY" "FORTRESS" "SPLIT" "STREAM" "BBS" "DOS" "STACK" "GRIDBAR" "PULSE")
FORMAT_KEYS=("thin" "bold" "double" "ascii" "dashed")
FORMAT_NAMES=("Thin Lines" "Bold Lines" "Double Lines" "ASCII" "Dashed")
FORMAT_CODES=("THIN" "BOLD" "DOUBLE" "ASCII" "DASH")
COLOR_KEYS=("cyan" "green" "yellow" "magenta" "blue" "red")
COLOR_NAMES=("Cyan Pulse" "Matrix Green" "Amber Signal" "Magenta Core" "Electric Blue" "Crimson")
COLOR_CODES=("CYAN" "GREEN" "AMBER" "MAGENTA" "BLUE" "RED")

DEFAULT_LAYOUT="legacy"
DEFAULT_FORMAT="thin"
DEFAULT_COLOR="cyan"
CURRENT_LAYOUT="$DEFAULT_LAYOUT"
CURRENT_FORMAT="$DEFAULT_FORMAT"
CURRENT_COLOR="$DEFAULT_COLOR"

TITLE_W=116
SESSION_W=38
LIMIT_W=33
STATE_W=8

BOX_TL="╭"
BOX_TR="╮"
BOX_BL="╰"
BOX_BR="╯"
BOX_H="─"
BOX_V="│"
BOX_TM="┬"
BOX_MM="┼"
BOX_ML="├"
BOX_MR="┤"
BOX_BM="┴"
PB_FILLED="#"
PB_EMPTY="."
THREAD_MID="|-"
THREAD_END="\\-"

ACCOUNT_NAME="legacy"
SELECTED_SESSION_ID="none"
LOGIN_STATE_TEXT="logged in"
LOCAL_RUN_VERSION="${LOCAL_RUN_VERSION_ARG:-2.1.16}"
SERVER_RUN_VERSION=""
AUTH_SERVER_VERSION=""

SESSION_NAMES=(
  "faspi [faspi]"
  "gpt01 [gpt01]"
  "gpt02 [gpt02]"
  "gpt03 [gpt03]"
  "naskelross [naskelross]"
)
SESSION_IDS=("faspi" "gpt01" "gpt02" "gpt03" "naskelross")
SESSION_DEFAULT=(1 0 0 0 0)
SESSION_SELECTED=(0 1 0 0 0)

LIMIT_5H=("41%" "100%" "100%" "34%" "77%")
RESET_5H=(
  "18.04.2026 23:36:15"
  "19.04.2026 02:11:47"
  "19.04.2026 02:11:48"
  "19.04.2026 00:52:34"
  "18.04.2026 21:31:34"
)
LIMIT_7D=("49%" "0%" "8%" "5%" "81%")
RESET_7D=(
  "23.04.2026 11:48:09"
  "21.04.2026 12:35:57"
  "22.04.2026 11:51:49"
  "23.04.2026 11:22:35"
  "24.04.2026 14:16:22"
)
STATE=("valid" "valid" "valid" "valid" "valid")

key_index() {
  local key="$1"
  shift
  local values=("$@")
  local i
  for i in "${!values[@]}"; do
    if [[ "${values[$i]}" == "$key" ]]; then
      printf '%s' "$i"
      return 0
    fi
  done
  printf '%s' "-1"
}

layout_index() { key_index "$1" "${LAYOUT_KEYS[@]}"; }
format_index() { key_index "$1" "${FORMAT_KEYS[@]}"; }
color_index() { key_index "$1" "${COLOR_KEYS[@]}"; }

layout_name() {
  local idx
  idx="$(layout_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${LAYOUT_NAMES[$idx]}"; else printf '%s' "$1"; fi
}
format_name() {
  local idx
  idx="$(format_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${FORMAT_NAMES[$idx]}"; else printf '%s' "$1"; fi
}
color_name() {
  local idx
  idx="$(color_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${COLOR_NAMES[$idx]}"; else printf '%s' "$1"; fi
}

layout_code() {
  local idx
  idx="$(layout_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${LAYOUT_CODES[$idx]}"; else printf '%s' "LEGACY"; fi
}
format_code() {
  local idx
  idx="$(format_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${FORMAT_CODES[$idx]}"; else printf '%s' "THIN"; fi
}
color_code() {
  local idx
  idx="$(color_index "$1")"
  if [[ "$idx" -ge 0 ]]; then printf '%s' "${COLOR_CODES[$idx]}"; else printf '%s' "CYAN"; fi
}

style_codename() {
  local l="$1" f="$2" c="$3"
  printf '%s-%s-%s' "$(layout_code "$l")" "$(format_code "$f")" "$(color_code "$c")"
}

style_label() {
  local l="$1" f="$2" c="$3"
  printf '%s + %s + %s (%s)' "$(layout_name "$l")" "$(format_name "$f")" "$(color_name "$c")" "$(style_codename "$l" "$f" "$c")"
}

map_legacy_numeric_style() {
  local n="$1"
  case "$n" in
    0) CURRENT_LAYOUT="legacy"; CURRENT_FORMAT="thin"; CURRENT_COLOR="cyan" ;;
    1) CURRENT_LAYOUT="fortress"; CURRENT_FORMAT="bold"; CURRENT_COLOR="cyan" ;;
    2) CURRENT_LAYOUT="fortress"; CURRENT_FORMAT="bold"; CURRENT_COLOR="green" ;;
    3) CURRENT_LAYOUT="split"; CURRENT_FORMAT="thin"; CURRENT_COLOR="magenta" ;;
    4) CURRENT_LAYOUT="split"; CURRENT_FORMAT="thin"; CURRENT_COLOR="blue" ;;
    5) CURRENT_LAYOUT="telemetry"; CURRENT_FORMAT="ascii"; CURRENT_COLOR="yellow" ;;
    6) CURRENT_LAYOUT="telemetry"; CURRENT_FORMAT="ascii"; CURRENT_COLOR="green" ;;
    7) CURRENT_LAYOUT="split"; CURRENT_FORMAT="dashed"; CURRENT_COLOR="red" ;;
    8) CURRENT_LAYOUT="fortress"; CURRENT_FORMAT="double"; CURRENT_COLOR="blue" ;;
    9) CURRENT_LAYOUT="telemetry"; CURRENT_FORMAT="bold"; CURRENT_COLOR="magenta" ;;
    10) CURRENT_LAYOUT="telemetry"; CURRENT_FORMAT="double"; CURRENT_COLOR="magenta" ;;
    11) CURRENT_LAYOUT="fortress"; CURRENT_FORMAT="thin"; CURRENT_COLOR="green" ;;
    12) CURRENT_LAYOUT="telemetry"; CURRENT_FORMAT="dashed"; CURRENT_COLOR="yellow" ;;
    13) CURRENT_LAYOUT="bbs"; CURRENT_FORMAT="double"; CURRENT_COLOR="green" ;;
    14) CURRENT_LAYOUT="split"; CURRENT_FORMAT="bold"; CURRENT_COLOR="cyan" ;;
    15) CURRENT_LAYOUT="bbs"; CURRENT_FORMAT="thin"; CURRENT_COLOR="magenta" ;;
    16) CURRENT_LAYOUT="dos"; CURRENT_FORMAT="ascii"; CURRENT_COLOR="yellow" ;;
    *) CURRENT_LAYOUT="$DEFAULT_LAYOUT"; CURRENT_FORMAT="$DEFAULT_FORMAT"; CURRENT_COLOR="$DEFAULT_COLOR" ;;
  esac
}

normalize_style_selection() {
  CURRENT_LAYOUT="$DEFAULT_LAYOUT"
  CURRENT_FORMAT="$DEFAULT_FORMAT"
  CURRENT_COLOR="$DEFAULT_COLOR"

  if [[ -n "$ONLY_STYLE" && "$ONLY_STYLE" =~ ^[0-9]+$ ]]; then
    map_legacy_numeric_style "$ONLY_STYLE"
    return 0
  fi

  if [[ -n "$LAYOUT_KEY" && "$(layout_index "$LAYOUT_KEY")" -ge 0 ]]; then
    CURRENT_LAYOUT="$LAYOUT_KEY"
  fi

  if [[ -n "$FORMAT_KEY" ]]; then
    if [[ "$(format_index "$FORMAT_KEY")" -ge 0 ]]; then
      CURRENT_FORMAT="$FORMAT_KEY"
    elif [[ "$(layout_index "$FORMAT_KEY")" -ge 0 && -z "$LAYOUT_KEY" ]]; then
      CURRENT_LAYOUT="$FORMAT_KEY"
    fi
  fi

  if [[ -n "$COLOR_KEY" && "$(color_index "$COLOR_KEY")" -ge 0 ]]; then
    CURRENT_COLOR="$COLOR_KEY"
  fi
}

paint() {
  local color="$1"
  shift
  printf "%b%s%b" "$color" "$*" "$C_RESET"
}

repeat_char() {
  local ch="$1"
  local count="$2"
  local out=""
  local i
  for ((i = 0; i < count; i++)); do
    out+="$ch"
  done
  printf "%s" "$out"
}

clip_text() {
  local text="$1"
  local width="$2"
  local len=${#text}
  if (( width <= 0 || len <= width )); then
    printf "%s" "$text"
    return
  fi
  if (( width == 1 )); then
    printf "%s" "${text:0:1}"
    return
  fi
  printf "%s" "${text:0:$((width - 1))}…"
}

pad_cell() {
  local text="$1"
  local width="$2"
  local clipped
  clipped="$(clip_text "$text" "$width")"
  printf "%-*s" "$width" "$clipped"
}

field() {
  local text="$1"
  local width="$2"
  local color="${3:-}"
  local padded
  padded="$(pad_cell "$text" "$width")"
  if [[ -n "$color" ]]; then paint "$color" "$padded"; else printf "%s" "$padded"; fi
}

percent_number() {
  local raw="${1%%%}"
  if [[ "$raw" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
    printf "%s" "${raw%%.*}"
  else
    printf "-1"
  fi
}

tone_for_percent() {
  local p
  p="$(percent_number "$1")"
  if (( p < 0 )); then printf "fallback"
  elif (( p <= 25 )); then printf "bad"
  elif (( p <= 60 )); then printf "warn"
  else printf "good"; fi
}

tone_color() {
  case "$1" in
    good) printf "%s" "$C_GREEN" ;;
    warn) printf "%s" "$C_YELLOW" ;;
    bad) printf "%s" "$C_RED" ;;
    fallback) printf "%s" "$C_MAGENTA" ;;
    *) printf "" ;;
  esac
}

state_color() {
  case "$1" in
    valid) printf "%s" "$C_GREEN" ;;
    invalid) printf "%s" "$C_RED" ;;
    *) printf "%s" "$C_YELLOW" ;;
  esac
}

accent_color() {
  case "$CURRENT_COLOR" in
    cyan) printf '%s' "$C_CYAN" ;;
    green) printf '%s' "$C_GREEN" ;;
    yellow) printf '%s' "$C_YELLOW" ;;
    magenta) printf '%s' "$C_MAGENTA" ;;
    blue) printf '%s' "$C_BLUE" ;;
    red) printf '%s' "$C_RED" ;;
    *) printf '%s' "$C_CYAN" ;;
  esac
}

apply_format_symbols() {
  case "$CURRENT_FORMAT" in
    thin)
      BOX_TL="╭"; BOX_TR="╮"; BOX_BL="╰"; BOX_BR="╯"
      BOX_H="─"; BOX_V="│"; BOX_TM="┬"; BOX_MM="┼"; BOX_ML="├"; BOX_MR="┤"; BOX_BM="┴"
      PB_FILLED="█"; PB_EMPTY="░"
      THREAD_MID="├─"; THREAD_END="└─"
      ;;
    bold)
      BOX_TL="┏"; BOX_TR="┓"; BOX_BL="┗"; BOX_BR="┛"
      BOX_H="━"; BOX_V="┃"; BOX_TM="┳"; BOX_MM="╋"; BOX_ML="┣"; BOX_MR="┫"; BOX_BM="┻"
      PB_FILLED="■"; PB_EMPTY="·"
      THREAD_MID="┣━"; THREAD_END="┗━"
      ;;
    double)
      BOX_TL="╔"; BOX_TR="╗"; BOX_BL="╚"; BOX_BR="╝"
      BOX_H="═"; BOX_V="║"; BOX_TM="╦"; BOX_MM="╬"; BOX_ML="╠"; BOX_MR="╣"; BOX_BM="╩"
      PB_FILLED="▓"; PB_EMPTY="░"
      THREAD_MID="╠═"; THREAD_END="╚═"
      ;;
    ascii)
      BOX_TL="+"; BOX_TR="+"; BOX_BL="+"; BOX_BR="+"
      BOX_H="-"; BOX_V="|"; BOX_TM="+"; BOX_MM="+"; BOX_ML="+"; BOX_MR="+"; BOX_BM="+"
      PB_FILLED="#"; PB_EMPTY="."
      THREAD_MID="|-"; THREAD_END="\\-"
      ;;
    dashed)
      BOX_TL="┌"; BOX_TR="┐"; BOX_BL="└"; BOX_BR="┘"
      BOX_H="┄"; BOX_V="┆"; BOX_TM="┬"; BOX_MM="┼"; BOX_ML="├"; BOX_MR="┤"; BOX_BM="┴"
      PB_FILLED="="; PB_EMPTY="-"
      THREAD_MID="┆-"; THREAD_END="┆_"
      ;;
    *)
      BOX_TL="╭"; BOX_TR="╮"; BOX_BL="╰"; BOX_BR="╯"
      BOX_H="─"; BOX_V="│"; BOX_TM="┬"; BOX_MM="┼"; BOX_ML="├"; BOX_MR="┤"; BOX_BM="┴"
      PB_FILLED="█"; PB_EMPTY="░"
      THREAD_MID="├─"; THREAD_END="└─"
      ;;
  esac
}

session_label() {
  local i="$1"
  local mark=" "
  local suffix=""
  if (( SESSION_SELECTED[i] == 1 )); then mark="*"; fi
  if (( SESSION_DEFAULT[i] == 1 )); then suffix=" default"; fi
  printf "%s %s%s" "$mark" "${SESSION_NAMES[i]}" "$suffix"
}

selected_name_color() {
  local i="$1"
  if (( SESSION_SELECTED[i] == 1 )); then accent_color; else printf ""; fi
}

metric_text() {
  local pct="$1"
  local reset="$2"
  printf "%s | reset %s" "$pct" "$reset"
}

load_real_data_if_available() {
  [[ -n "$SESSIONS_FILE" && -f "$SESSIONS_FILE" ]] || return 0

  local node_bin="${NODE_BIN:-node}"
  command -v "$node_bin" >/dev/null 2>&1 || return 0

  local selected_arg="$SELECTED_SESSION_ARG"
  local local_run="$LOCAL_RUN_VERSION"
  local kind
  local row_name row_id row_default row_selected row_5h row_5h_reset row_7d row_7d_reset row_state

  SESSION_NAMES=()
  SESSION_IDS=()
  SESSION_DEFAULT=()
  SESSION_SELECTED=()
  LIMIT_5H=()
  RESET_5H=()
  LIMIT_7D=()
  RESET_7D=()
  STATE=()

  while IFS=$'\t' read -r kind row_name row_id row_default row_selected row_5h row_5h_reset row_7d row_7d_reset row_state; do
    if [[ "$kind" == "META" ]]; then
      ACCOUNT_NAME="$row_name"
      SELECTED_SESSION_ID="$row_id"
      SERVER_RUN_VERSION="$row_default"
      AUTH_SERVER_VERSION="$row_selected"
      continue
    fi
    [[ "$kind" == "ROW" ]] || continue
    SESSION_NAMES+=("$row_name")
    SESSION_IDS+=("$row_id")
    SESSION_DEFAULT+=("${row_default:-0}")
    SESSION_SELECTED+=("${row_selected:-0}")
    LIMIT_5H+=("${row_5h:-n/a}")
    RESET_5H+=("${row_5h_reset:--}")
    LIMIT_7D+=("${row_7d:-n/a}")
    RESET_7D+=("${row_7d_reset:--}")
    STATE+=("${row_state:-unknown}")
  done < <(
    "$node_bin" - "$SESSIONS_FILE" "$selected_arg" "$ACCOUNT_FILE" "$VERSION_FILE" "$INSTALL_META_FILE" "$local_run" <<'NODE'
const fs = require("fs");

const sessionsFile = process.argv[2] || "";
const selectedArg = process.argv[3] || "";
const accountFile = process.argv[4] || "";
const versionFile = process.argv[5] || "";
const installMetaFile = process.argv[6] || "";
const localRunVersion = process.argv[7] || "";

function readJson(file) {
  try {
    if (!file) return null;
    return JSON.parse(fs.readFileSync(file, "utf8"));
  } catch {
    return null;
  }
}

function fmtPercent(value) {
  const num = Number(value);
  if (!Number.isFinite(num)) return "";
  const clamped = Math.max(0, Math.min(100, num));
  const rounded = Math.round(clamped * 10) / 10;
  return Number.isInteger(rounded) ? `${rounded}%` : `${rounded.toFixed(1)}%`;
}

function fmtReset(value) {
  const ts = Number(value);
  if (!Number.isFinite(ts) || ts <= 0) return "-";
  const d = new Date(ts * 1000);
  const dd = String(d.getDate()).padStart(2, "0");
  const mm = String(d.getMonth() + 1).padStart(2, "0");
  const yyyy = String(d.getFullYear());
  const hh = String(d.getHours()).padStart(2, "0");
  const mi = String(d.getMinutes()).padStart(2, "0");
  const ss = String(d.getSeconds()).padStart(2, "0");
  return `${dd}.${mm}.${yyyy} ${hh}:${mi}:${ss}`;
}

function shortHealthMessage(message) {
  const msg = String(message || "");
  if (!msg || msg === "ok") return "";
  if (msg.startsWith("usage_request_failed")) return "usage unavailable";
  if (msg.startsWith("http_401") || msg.startsWith("http_403")) return "access denied";
  if (msg.startsWith("access_token_expired")) return "token expired";
  if (msg === "api_key_mode_no_wham_limits") return "no limits for api_key";
  return msg.length > 36 ? `${msg.slice(0, 36)}...` : msg;
}

function plain(value) {
  return String(value ?? "").replace(/\t/g, " ").replace(/\r?\n/g, " ");
}

const sessionsRaw = readJson(sessionsFile);
const sessions = Array.isArray(sessionsRaw) ? sessionsRaw : [];
const accountRaw = readJson(accountFile);
const versionRaw = readJson(versionFile);
const installMeta = readJson(installMetaFile);

let accountName = "";
if (accountRaw && typeof accountRaw === "object" && typeof accountRaw.username === "string" && accountRaw.username) {
  accountName = accountRaw.username;
}
if (!accountName && installMeta && typeof installMeta === "object" && installMeta.account && typeof installMeta.account === "object" && typeof installMeta.account.username === "string" && installMeta.account.username) {
  accountName = installMeta.account.username;
}
if (!accountName) accountName = "legacy";

const selectedSession = selectedArg || "";
const serverRunVersion = versionRaw && typeof versionRaw === "object" && typeof versionRaw.script_version === "string" ? versionRaw.script_version : "";
const authVersion = versionRaw && typeof versionRaw === "object" && typeof versionRaw.server_version === "string" ? versionRaw.server_version : "";

process.stdout.write(`META\t${plain(accountName)}\t${plain(selectedSession || "none")}\t${plain(serverRunVersion)}\t${plain(authVersion)}\n`);

for (const session of sessions) {
  const id = String(session?.id || "");
  if (!id) continue;
  const name = String(session?.name || id);
  const health = session && typeof session.health === "object" ? session.health : {};
  const fallback = shortHealthMessage(health.message);
  const p5 = fmtPercent(health?.limit_5h?.remaining_percent);
  const p7 = fmtPercent(health?.limit_weekly?.remaining_percent);
  const reset5 = p5 ? fmtReset(health?.limit_5h?.reset_at) : "-";
  const reset7 = p7 ? fmtReset(health?.limit_weekly?.reset_at) : "-";
  const s5 = p5 || fallback || "n/a";
  const s7 = p7 || fallback || "n/a";
  const isDefault = session?.is_default === true ? "1" : "0";
  const isSelected = selectedSession && selectedSession === id ? "1" : "0";
  const state = health?.is_valid === true ? "valid" : (health?.is_valid === false ? "invalid" : "unknown");
  process.stdout.write(`ROW\t${plain(`${name} [${id}]`)}\t${plain(id)}\t${isDefault}\t${isSelected}\t${plain(s5)}\t${plain(reset5)}\t${plain(s7)}\t${plain(reset7)}\t${plain(state)}\n`);
}
NODE
  )

  if [[ -n "$SERVER_RUN_VERSION" ]]; then
    LOCAL_RUN_VERSION="${LOCAL_RUN_VERSION:-$local_run}"
  fi

  if [[ -z "$SELECTED_SESSION_ID" || "$SELECTED_SESSION_ID" == "none" ]]; then
    SELECTED_SESSION_ID="$selected_arg"
  fi
}

title_box() {
  local title="$1"
  local color="$2"
  local top bottom
  top="${BOX_TL}$(repeat_char "$BOX_H" "$TITLE_W")${BOX_TR}"
  bottom="${BOX_BL}$(repeat_char "$BOX_H" "$TITLE_W")${BOX_BR}"

  echo "  $(paint "$color" "$top")"
  printf "  %s%s%s\n" "$(paint "$color" "$BOX_V")" "$(paint "$C_BOLD$color" "$(pad_cell "$title" "$TITLE_W")")" "$(paint "$color" "$BOX_V")"
  echo "  $(paint "$color" "$bottom")"
}

status_block() {
  local accent
  accent="$(accent_color)"
  printf "  %-18s %s\n" "Login state:" "$(paint "$C_GREEN" "${LOGIN_STATE_TEXT}")"
  if [[ -n "$SERVER_RUN_VERSION" ]]; then
    printf "  %-18s %s\n" "Script version:" "$(paint "$accent" "$LOCAL_RUN_VERSION") (server $(paint "$accent" "$SERVER_RUN_VERSION"))"
  else
    printf "  %-18s %s\n" "Script version:" "$(paint "$accent" "$LOCAL_RUN_VERSION")"
  fi
  if [[ -n "$AUTH_SERVER_VERSION" ]]; then
    printf "  %-18s %s\n" "Auth server:" "$(paint "$accent" "$AUTH_SERVER_VERSION")"
  fi
  printf "  %-18s %s\n" "Account:" "$(paint "$C_MAGENTA" "$ACCOUNT_NAME")"
  printf "  %-18s %s\n" "Selected session:" "$(paint "$accent" "${SELECTED_SESSION_ID:-none}")"
  printf "  %-18s %s\n" "Menu style:" "$(paint "$accent" "$(style_label "$CURRENT_LAYOUT" "$CURRENT_FORMAT" "$CURRENT_COLOR")")"
}

status_block_legacy() {
  local accent
  accent="$(accent_color)"
  printf "  %-18s %s\n" "Login state:" "$(paint "$C_GREEN" "${LOGIN_STATE_TEXT}")"
  if [[ -n "$SERVER_RUN_VERSION" ]]; then
    printf "  %-18s %s\n" "Script version:" "$(paint "$accent" "$LOCAL_RUN_VERSION") (server $(paint "$accent" "$SERVER_RUN_VERSION"))"
  else
    printf "  %-18s %s\n" "Script version:" "$(paint "$accent" "$LOCAL_RUN_VERSION")"
  fi
  if [[ -n "$AUTH_SERVER_VERSION" ]]; then
    printf "  %-18s %s\n" "Auth server:" "$(paint "$accent" "$AUTH_SERVER_VERSION")"
  fi
  printf "  %-18s %s\n" "Account:" "$(paint "$C_MAGENTA" "$ACCOUNT_NAME")"
  printf "  %-18s %s\n" "Selected session:" "$(paint "$accent" "${SELECTED_SESSION_ID:-none}")"
}

menu_block() {
  if (( NO_MENU == 1 )); then
    return
  fi
  echo "1) Start Codex"
  echo "2) Choose session"
  echo "3) Login / Re-login"
  echo "4) Logout"
  echo "5) Update Codex"
  echo "6) Choose menu style"
  echo "7) Exit"
  echo "Choice [1]:"
}

render_plain_table() {
  local i n s_txt l5_txt l7_txt st st_col l5_col l7_col
  n="${#SESSION_NAMES[@]}"
  echo "  Sessions:"
  printf "    %-38s | %-33s | %-33s | %-8s\n" "Session" "5h limit" "7d limit" "State"
  for ((i = 0; i < n; i++)); do
    s_txt="$(session_label "$i")"
    l5_txt="$(metric_text "${LIMIT_5H[i]}" "${RESET_5H[i]}")"
    l7_txt="$(metric_text "${LIMIT_7D[i]}" "${RESET_7D[i]}")"
    st="${STATE[i]}"
    st_col="$(state_color "$st")"
    l5_col="$(tone_color "$(tone_for_percent "${LIMIT_5H[i]}")")"
    l7_col="$(tone_color "$(tone_for_percent "${LIMIT_7D[i]}")")"

    printf "    %s | %s | %s | %s\n" \
      "$(field "$s_txt" 38 "$(selected_name_color "$i")")" \
      "$(field "$l5_txt" 33 "$l5_col")" \
      "$(field "$l7_txt" 33 "$l7_col")" \
      "$(field "$st" 8 "$st_col")"
  done
}

render_table_box() {
  local border_color="$1"

  local top mid bot
  top="${BOX_TL}$(repeat_char "$BOX_H" $((SESSION_W + 2)))${BOX_TM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_TM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_TM}$(repeat_char "$BOX_H" $((STATE_W + 2)))${BOX_TR}"
  mid="${BOX_ML}$(repeat_char "$BOX_H" $((SESSION_W + 2)))${BOX_MM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_MM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_MM}$(repeat_char "$BOX_H" $((STATE_W + 2)))${BOX_MR}"
  bot="${BOX_BL}$(repeat_char "$BOX_H" $((SESSION_W + 2)))${BOX_BM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_BM}$(repeat_char "$BOX_H" $((LIMIT_W + 2)))${BOX_BM}$(repeat_char "$BOX_H" $((STATE_W + 2)))${BOX_BR}"

  echo "  $(paint "$border_color" "$top")"
  printf "  %s %s %s %s %s %s %s %s %s\n" \
    "$(paint "$border_color" "$BOX_V")" "$(field "Session" "$SESSION_W" "$C_DIM")" \
    "$(paint "$border_color" "$BOX_V")" "$(field "5h limit" "$LIMIT_W" "$C_DIM")" \
    "$(paint "$border_color" "$BOX_V")" "$(field "7d limit" "$LIMIT_W" "$C_DIM")" \
    "$(paint "$border_color" "$BOX_V")" "$(field "State" "$STATE_W" "$C_DIM")" \
    "$(paint "$border_color" "$BOX_V")"
  echo "  $(paint "$border_color" "$mid")"

  local i n s_txt l5_txt l7_txt st s_col l5_col l7_col st_col
  n="${#SESSION_NAMES[@]}"
  for ((i = 0; i < n; i++)); do
    s_txt="$(session_label "$i")"
    l5_txt="$(metric_text "${LIMIT_5H[i]}" "${RESET_5H[i]}")"
    l7_txt="$(metric_text "${LIMIT_7D[i]}" "${RESET_7D[i]}")"
    st="${STATE[i]}"
    s_col="$(selected_name_color "$i")"
    l5_col="$(tone_color "$(tone_for_percent "${LIMIT_5H[i]}")")"
    l7_col="$(tone_color "$(tone_for_percent "${LIMIT_7D[i]}")")"
    st_col="$(state_color "$st")"

    printf "  %s %s %s %s %s %s %s %s %s\n" \
      "$(paint "$border_color" "$BOX_V")" "$(field "$s_txt" "$SESSION_W" "$s_col")" \
      "$(paint "$border_color" "$BOX_V")" "$(field "$l5_txt" "$LIMIT_W" "$l5_col")" \
      "$(paint "$border_color" "$BOX_V")" "$(field "$l7_txt" "$LIMIT_W" "$l7_col")" \
      "$(paint "$border_color" "$BOX_V")" "$(field "$st" "$STATE_W" "$st_col")" \
      "$(paint "$border_color" "$BOX_V")"
  done

  echo "  $(paint "$border_color" "$bot")"
}

progress_bar() {
  local pct="$1"
  local width="$2"
  local filled_char="${3:-$PB_FILLED}"
  local empty_char="${4:-$PB_EMPTY}"
  local p filled empty
  p="$(percent_number "$pct")"
  if (( p < 0 )); then
    printf "%s" "$(repeat_char "$empty_char" "$width")"
    return
  fi
  (( p > 100 )) && p=100
  (( p < 0 )) && p=0
  filled=$(( (p * width) / 100 ))
  empty=$(( width - filled ))
  printf "%s%s" "$(repeat_char "$filled_char" "$filled")" "$(repeat_char "$empty_char" "$empty")"
}

session_badges() {
  local i="$1"
  local out=""
  if (( SESSION_SELECTED[i] == 1 )); then out+="sel "; fi
  if (( SESSION_DEFAULT[i] == 1 )); then out+="def "; fi
  [[ -n "$out" ]] || out="std "
  printf "%s" "${out% }"
}

render_split_cards() {
  local i n s_txt s_col st st_col l5_txt l7_txt l5_col l7_col
  n="${#SESSION_NAMES[@]}"
  for ((i = 0; i < n; i++)); do
    s_txt="${SESSION_NAMES[i]}"
    st="${STATE[i]}"
    s_col="$(selected_name_color "$i")"
    st_col="$(state_color "$st")"
    l5_txt="$(metric_text "${LIMIT_5H[i]}" "${RESET_5H[i]}")"
    l7_txt="$(metric_text "${LIMIT_7D[i]}" "${RESET_7D[i]}")"
    l5_col="$(tone_color "$(tone_for_percent "${LIMIT_5H[i]}")")"
    l7_col="$(tone_color "$(tone_for_percent "${LIMIT_7D[i]}")")"
    echo "  ${BOX_TL}${BOX_H} $(field "$(session_badges "$i")" 7 "$C_DIM") $(field "$s_txt" 34 "$s_col") $(field "$st" 8 "$st_col")"
    echo "  ${BOX_V} 5h: $(field "$l5_txt" 82 "$l5_col")"
    echo "  ${BOX_V} 7d: $(field "$l7_txt" 82 "$l7_col")"
    echo "  ${BOX_BL}$(repeat_char "$BOX_H" 100)"
  done
}

render_telemetry_stream() {
  local i n name st st_col p5 p7 b5 b7 c5 c7
  n="${#SESSION_NAMES[@]}"
  echo "  Telemetry stream:"
  for ((i = 0; i < n; i++)); do
    name="$(field "${SESSION_NAMES[i]}" 28 "$(selected_name_color "$i")")"
    st="${STATE[i]}"
    st_col="$(state_color "$st")"
    p5="${LIMIT_5H[i]}"
    p7="${LIMIT_7D[i]}"
    b5="$(progress_bar "$p5" 14)"
    b7="$(progress_bar "$p7" 14)"
    c5="$(tone_color "$(tone_for_percent "$p5")")"
    c7="$(tone_color "$(tone_for_percent "$p7")")"
    printf "  %s | 5h [%s] %s | 7d [%s] %s | %s\n" \
      "$name" \
      "$(paint "$c5" "$b5")" "$(field "$p5" 6 "$c5")" \
      "$(paint "$c7" "$b7")" "$(field "$p7" 6 "$c7")" \
      "$(field "$st" 7 "$st_col")"
    printf "  %s   reset5: %-19s reset7: %-19s\n" "$(paint "$C_DIM" " ")" "${RESET_5H[i]}" "${RESET_7D[i]}"
  done
}

render_bbs_threads() {
  local i n s_txt s_col st st_col l5_txt l7_txt l5_col l7_col
  n="${#SESSION_NAMES[@]}"
  echo "  [board: codex.sessions]"
  for ((i = 0; i < n; i++)); do
    s_txt="${SESSION_NAMES[i]}"
    st="${STATE[i]}"
    s_col="$(selected_name_color "$i")"
    st_col="$(state_color "$st")"
    l5_txt="$(metric_text "${LIMIT_5H[i]}" "${RESET_5H[i]}")"
    l7_txt="$(metric_text "${LIMIT_7D[i]}" "${RESET_7D[i]}")"
    l5_col="$(tone_color "$(tone_for_percent "${LIMIT_5H[i]}")")"
    l7_col="$(tone_color "$(tone_for_percent "${LIMIT_7D[i]}")")"
    printf "  > thread %-2d | %s | %s\n" "$((i + 1))" "$(paint "$s_col" "$s_txt")" "$(paint "$st_col" "$st")"
    printf "    %s 5h: %s\n" "$THREAD_MID" "$(paint "$l5_col" "$l5_txt")"
    printf "    %s 7d: %s\n" "$THREAD_END" "$(paint "$l7_col" "$l7_txt")"
  done
}

render_dos_listing() {
  local i n s_txt st st_col l5_txt l7_txt l5_col l7_col mark
  n="${#SESSION_NAMES[@]}"
  echo "  C:\\CODEX> whoami /all"
  printf "  account=%s selected=%s style=%s\n" \
    "$(paint "$C_MAGENTA" "$ACCOUNT_NAME")" \
    "$(paint "$(accent_color)" "${SELECTED_SESSION_ID:-none}")" \
    "$(paint "$(accent_color)" "$(style_codename "$CURRENT_LAYOUT" "$CURRENT_FORMAT" "$CURRENT_COLOR")")"
  echo "  C:\\CODEX> sessions /list /verbose"
  for ((i = 0; i < n; i++)); do
    mark=" "
    (( SESSION_SELECTED[i] == 1 )) && mark="*"
    st="${STATE[i]}"
    st_col="$(state_color "$st")"
    s_txt="${SESSION_NAMES[i]}"
    l5_txt="$(metric_text "${LIMIT_5H[i]}" "${RESET_5H[i]}")"
    l7_txt="$(metric_text "${LIMIT_7D[i]}" "${RESET_7D[i]}")"
    l5_col="$(tone_color "$(tone_for_percent "${LIMIT_5H[i]}")")"
    l7_col="$(tone_color "$(tone_for_percent "${LIMIT_7D[i]}")")"
    printf "  [%02d] %s %-31s state=%s\n" "$((i + 1))" "$mark" "$s_txt" "$(paint "$st_col" "$st")"
    printf "       5h=%s\n" "$(paint "$l5_col" "$l5_txt")"
    printf "       7d=%s\n" "$(paint "$l7_col" "$l7_txt")"
  done
}

render_legacy() {
  echo "Codex menu:"
  status_block_legacy
  render_plain_table
  menu_block
}

render_fortress() {
  local accent
  accent="$(accent_color)"
  title_box "CODEX CONTROL / FORTRESS GRID" "$accent"
  status_block
  render_table_box "$accent"
  menu_block
}

render_split() {
  local accent
  local panel_w=50
  local top_line
  local left_1 left_2 left_3 right_1 right_2 right_3
  accent="$(accent_color)"
  title_box "Codex menu / split panels" "$accent"

  top_line="${BOX_TL}$(repeat_char "$BOX_H" "$panel_w")${BOX_TR} ${BOX_TL}$(repeat_char "$BOX_H" "$panel_w")${BOX_TR}"
  echo "  $(paint "$accent" "$top_line")"
  left_1="$(pad_cell "Account: $ACCOUNT_NAME" 48)"
  left_2="$(pad_cell "Login: ${LOGIN_STATE_TEXT}" 48)"
  left_3="$(pad_cell "Script: ${LOCAL_RUN_VERSION}${SERVER_RUN_VERSION:+ (server $SERVER_RUN_VERSION)}" 48)"
  right_1="$(pad_cell "Style: $(style_label "$CURRENT_LAYOUT" "$CURRENT_FORMAT" "$CURRENT_COLOR")" 48)"
  right_2="$(pad_cell "Selected: ${SELECTED_SESSION_ID:-none}" 48)"
  right_3="$(pad_cell "Auth: ${AUTH_SERVER_VERSION:-n/a}" 48)"
  printf "  %s %s %s %s %s %s\n" "$BOX_V" "$left_1" "$BOX_V" "$BOX_V" "$right_1" "$BOX_V"
  printf "  %s %s %s %s %s %s\n" "$BOX_V" "$left_2" "$BOX_V" "$BOX_V" "$right_2" "$BOX_V"
  printf "  %s %s %s %s %s %s\n" "$BOX_V" "$left_3" "$BOX_V" "$BOX_V" "$right_3" "$BOX_V"
  echo "  $(paint "$accent" "${BOX_BL}$(repeat_char "$BOX_H" "$panel_w")${BOX_BR} ${BOX_BL}$(repeat_char "$BOX_H" "$panel_w")${BOX_BR}")"
  echo "  Session cards:"
  render_split_cards
  menu_block
}

render_telemetry() {
  local accent
  accent="$(accent_color)"
  title_box "Codex menu / telemetry stream" "$accent"
  status_block
  echo "  $(paint "$accent" "$(repeat_char "$BOX_H" 100)")"
  render_telemetry_stream
  menu_block
}

render_bbs() {
  local accent
  accent="$(accent_color)"
  title_box "CODEX BBS TERMINAL" "$accent"
  printf "  sysop=%s | login=%s | style=%s\n" \
    "$(paint "$C_MAGENTA" "$ACCOUNT_NAME")" \
    "$(paint "$C_GREEN" "$LOGIN_STATE_TEXT")" \
    "$(paint "$accent" "$(style_codename "$CURRENT_LAYOUT" "$CURRENT_FORMAT" "$CURRENT_COLOR")")"
  printf "  selected=%s | version=%s%s\n" \
    "$(paint "$accent" "${SELECTED_SESSION_ID:-none}")" \
    "$LOCAL_RUN_VERSION" \
    "${SERVER_RUN_VERSION:+ (srv $SERVER_RUN_VERSION)}"
  echo "  $(paint "$accent" "$(repeat_char "$BOX_H" 100)")"
  render_bbs_threads
  menu_block
}

render_dos() {
  local accent
  accent="$(accent_color)"
  title_box "CODEX.EXE :: DOS MATRIX" "$accent"
  render_dos_listing
  menu_block
}

render_stack() {
  local accent i n p5 p7 b5 b7 c5 c7 st stc
  accent="$(accent_color)"
  title_box "Codex menu / stacked bars" "$accent"
  status_block
  echo "  $(paint "$accent" "$(repeat_char "$BOX_H" 100)")"
  n="${#SESSION_NAMES[@]}"
  for ((i = 0; i < n; i++)); do
    st="${STATE[i]}"
    stc="$(state_color "$st")"
    p5="${LIMIT_5H[i]}"
    p7="${LIMIT_7D[i]}"
    b5="$(progress_bar "$p5" 24)"
    b7="$(progress_bar "$p7" 24)"
    c5="$(tone_color "$(tone_for_percent "$p5")")"
    c7="$(tone_color "$(tone_for_percent "$p7")")"
    printf "  %s %s (%s)\n" \
      "$(paint "$(selected_name_color "$i")" "${SESSION_NAMES[i]}")" \
      "$(paint "$stc" "$st")" \
      "${SESSION_IDS[i]}"
    printf "    5h [%s] %s  reset %s\n" "$(paint "$c5" "$b5")" "$(paint "$c5" "$(pad_cell "$p5" 6)")" "${RESET_5H[i]}"
    printf "    7d [%s] %s  reset %s\n" "$(paint "$c7" "$b7")" "$(paint "$c7" "$(pad_cell "$p7" 6)")" "${RESET_7D[i]}"
    echo "    $(paint "$C_DIM" "$(repeat_char "." 92)")"
  done
  menu_block
}

render_gridbar() {
  local accent i n st stc p5 p7 b5 b7 c5 c7
  accent="$(accent_color)"
  title_box "Codex menu / grid bars" "$accent"
  status_block
  echo "  $(paint "$accent" "${BOX_TL}$(repeat_char "$BOX_H" 30)${BOX_TM}$(repeat_char "$BOX_H" 30)${BOX_TM}$(repeat_char "$BOX_H" 30)${BOX_TM}$(repeat_char "$BOX_H" 8)${BOX_TR}")"
  printf "  %s %-28s %s %-28s %s %-28s %s %-6s %s\n" \
    "$BOX_V" "Session" "$BOX_V" "5h bar" "$BOX_V" "7d bar" "$BOX_V" "State" "$BOX_V"
  echo "  $(paint "$accent" "${BOX_ML}$(repeat_char "$BOX_H" 30)${BOX_MM}$(repeat_char "$BOX_H" 30)${BOX_MM}$(repeat_char "$BOX_H" 30)${BOX_MM}$(repeat_char "$BOX_H" 8)${BOX_MR}")"
  n="${#SESSION_NAMES[@]}"
  for ((i = 0; i < n; i++)); do
    st="${STATE[i]}"
    stc="$(state_color "$st")"
    p5="${LIMIT_5H[i]}"
    p7="${LIMIT_7D[i]}"
    b5="$(progress_bar "$p5" 16)"
    b7="$(progress_bar "$p7" 16)"
    c5="$(tone_color "$(tone_for_percent "$p5")")"
    c7="$(tone_color "$(tone_for_percent "$p7")")"
    printf "  %s %-28s %s %-28s %s %-28s %s %-6s %s\n" \
      "$BOX_V" "$(paint "$(selected_name_color "$i")" "$(pad_cell "${SESSION_NAMES[i]}" 28)")" \
      "$BOX_V" "$(paint "$c5" "$b5") $(paint "$c5" "$(pad_cell "$p5" 5)")" \
      "$BOX_V" "$(paint "$c7" "$b7") $(paint "$c7" "$(pad_cell "$p7" 5)")" \
      "$BOX_V" "$(paint "$stc" "$(pad_cell "$st" 6)")" \
      "$BOX_V"
  done
  echo "  $(paint "$accent" "${BOX_BL}$(repeat_char "$BOX_H" 30)${BOX_BM}$(repeat_char "$BOX_H" 30)${BOX_BM}$(repeat_char "$BOX_H" 30)${BOX_BM}$(repeat_char "$BOX_H" 8)${BOX_BR}")"
  menu_block
}

render_pulse() {
  local accent i n p5 p7 b5 b7 c5 c7 st stc
  accent="$(accent_color)"
  title_box "Codex menu / pulse meters" "$accent"
  status_block
  echo "  Pulse channels:"
  n="${#SESSION_NAMES[@]}"
  for ((i = 0; i < n; i++)); do
    st="${STATE[i]}"
    stc="$(state_color "$st")"
    p5="${LIMIT_5H[i]}"
    p7="${LIMIT_7D[i]}"
    b5="$(progress_bar "$p5" 12)"
    b7="$(progress_bar "$p7" 12)"
    c5="$(tone_color "$(tone_for_percent "$p5")")"
    c7="$(tone_color "$(tone_for_percent "$p7")")"
    printf "  %s %-26s %s 5h[%s]%s 7d[%s]%s\n" \
      "$BOX_V" \
      "$(paint "$(selected_name_color "$i")" "$(pad_cell "${SESSION_NAMES[i]}" 26)")" \
      "$(paint "$stc" "$(pad_cell "$st" 7)")" \
      "$(paint "$c5" "$b5")" "$(paint "$c5" "$(pad_cell "$p5" 5)")" \
      "$(paint "$c7" "$b7")" "$(paint "$c7" "$(pad_cell "$p7" 5)")"
    printf "  %s reset5=%s  reset7=%s\n" "$BOX_V" "${RESET_5H[i]}" "${RESET_7D[i]}"
  done
  menu_block
}

render_view() {
  case "$CURRENT_LAYOUT" in
    legacy) render_legacy ;;
    fortress) render_fortress ;;
    split) render_split ;;
    telemetry) render_telemetry ;;
    bbs) render_bbs ;;
    dos) render_dos ;;
    stack) render_stack ;;
    gridbar) render_gridbar ;;
    pulse) render_pulse ;;
    *) render_legacy ;;
  esac
}

normalize_style_selection
apply_format_symbols
load_real_data_if_available

if (( NO_CLEAR == 0 )) && [[ -t 1 ]]; then
  printf '\033[H\033[2J\033[3J'
fi

render_view

if (( NO_PAUSE == 0 )) && [[ -t 0 ]] && [[ -z "${CODEX_MENU_PREVIEW_NO_WAIT:-}" ]]; then
  :
fi
MENU_STYLE_RENDERER
    chmod +x "$OUT_DIR/menu-style-render.sh"

    cat > "$OUT_DIR/run.ps1" <<'RUNNER_PS1'
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$RUN_SCRIPT_VERSION = '2.1.16'
$ROOT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$AUTH_SERVER_URL = if ($env:AUTH_SERVER_URL) { $env:AUTH_SERVER_URL } else { 'https://codex.naskel.systems/codex-auth' }
$LEGACY_AUTH_SERVER_URL = if ($env:LEGACY_AUTH_SERVER_URL) { $env:LEGACY_AUTH_SERVER_URL } else { 'https://codex.naskel.petrsu.ru/codex-auth' }
$AUTH_VERSION_URL = if ($env:AUTH_VERSION_URL) { $env:AUTH_VERSION_URL } else { 'https://codex.naskel.systems/codex-auth/version' }
$LEGACY_AUTH_VERSION_URL = if ($env:LEGACY_AUTH_VERSION_URL) { $env:LEGACY_AUTH_VERSION_URL } else { 'https://codex.naskel.petrsu.ru/codex-auth/version' }
$INSTALL_SERVER_URL = if ($env:INSTALL_SERVER_URL) { $env:INSTALL_SERVER_URL } else { 'https://codex.naskel.systems/codex-install' }
$LEGACY_INSTALL_SERVER_URL = if ($env:LEGACY_INSTALL_SERVER_URL) { $env:LEGACY_INSTALL_SERVER_URL } else { 'https://codex.naskel.petrsu.ru/codex-install' }
$script:ORIGINAL_ARGS = @($args)

$CODEX_HOME = Join-Path $ROOT_DIR '.codex'
$TOKEN_PATH = Join-Path $CODEX_HOME 'install.token'
$AUTO_YOLO_PATH = Join-Path $CODEX_HOME 'auto_yolo'
$RAM_DIR = Join-Path ([System.IO.Path]::GetTempPath()) ("codex-auth.{0}" -f ([Guid]::NewGuid().ToString('N')))
$LAST_AUTH_ERROR = ''
$script:TOKEN = ''
$script:SELECTED_SESSION = ''
$script:AUTO_YOLO = $false

function Ensure-Dir {
  param([Parameter(Mandatory = $true)][string]$Path)
  if (-not (Test-Path -LiteralPath $Path)) {
    New-Item -ItemType Directory -Force -Path $Path | Out-Null
  }
}

function Write-Utf8NoBom {
  param(
    [Parameter(Mandatory = $true)][string]$Path,
    [Parameter(Mandatory = $true)][string]$Value
  )
  [System.IO.File]::WriteAllText($Path, $Value, [System.Text.UTF8Encoding]::new($false))
}

function Read-TrimmedText {
  param([Parameter(Mandatory = $true)][string]$Path)
  if (-not (Test-Path -LiteralPath $Path)) {
    return ''
  }
  try {
    return ([System.IO.File]::ReadAllText($Path)).Trim()
  } catch {
    return ''
  }
}

function Prompt {
  param(
    [Parameter(Mandatory = $true)][string]$Label,
    [string]$Default = ''
  )
  $text = if ([string]::IsNullOrEmpty($Default)) { "$Label" } else { "$Label [$Default]" }
  $value = Read-Host -Prompt $text
  if ([string]::IsNullOrWhiteSpace($value)) {
    return $Default
  }
  return $value
}

function Prompt-Secret {
  param([Parameter(Mandatory = $true)][string]$Label)
  $sec = Read-Host -Prompt $Label -AsSecureString
  $ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec)
  try {
    return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)
  } finally {
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr)
  }
}

function Split-VersionParts {
  param([Parameter(Mandatory = $true)][string]$Version)
  $parts = @()
  foreach ($segment in ($Version -split '[^0-9]+')) {
    if ($segment -eq '') {
      continue
    }
    $n = 0
    [void][int]::TryParse($segment, [ref]$n)
    $parts += $n
  }
  if ($parts.Count -eq 0) {
    return @(0)
  }
  return $parts
}

function Version-Gt {
  param(
    [Parameter(Mandatory = $true)][string]$Left,
    [Parameter(Mandatory = $true)][string]$Right
  )
  $leftParts = Split-VersionParts $Left
  $rightParts = Split-VersionParts $Right
  $max = [Math]::Max($leftParts.Count, $rightParts.Count)
  for ($i = 0; $i -lt $max; $i++) {
    $l = if ($i -lt $leftParts.Count) { [int]$leftParts[$i] } else { 0 }
    $r = if ($i -lt $rightParts.Count) { [int]$rightParts[$i] } else { 0 }
    if ($l -gt $r) { return $true }
    if ($l -lt $r) { return $false }
  }
  return $false
}

function Invoke-HttpPost {
  param(
    [Parameter(Mandatory = $true)][string]$Url,
    [Parameter(Mandatory = $true)][string]$Payload
  )
  try {
    $params = @{
      Uri = $Url
      Method = 'Post'
      ContentType = 'application/json'
      Body = $Payload
      TimeoutSec = 30
    }
    if ((Get-Command Invoke-WebRequest).Parameters.ContainsKey('UseBasicParsing')) {
      $params.UseBasicParsing = $true
    }
    $response = Invoke-WebRequest @params
    return [string]$response.Content
  } catch {
    return $null
  }
}

function Invoke-HttpGet {
  param([Parameter(Mandatory = $true)][string]$Url)
  try {
    $params = @{
      Uri = $Url
      Method = 'Get'
      TimeoutSec = 12
    }
    if ((Get-Command Invoke-WebRequest).Parameters.ContainsKey('UseBasicParsing')) {
      $params.UseBasicParsing = $true
    }
    $response = Invoke-WebRequest @params
    return [string]$response.Content
  } catch {
    return $null
  }
}

function Try-ParseJson {
  param([Parameter(Mandatory = $true)][string]$Payload)
  try {
    if ([string]::IsNullOrWhiteSpace($Payload)) {
      return $null
    }
    return ($Payload | ConvertFrom-Json -ErrorAction Stop)
  } catch {
    return $null
  }
}

function Has-JsonProp {
  param(
    [Parameter(Mandatory = $true)]$Object,
    [Parameter(Mandatory = $true)][string]$Name
  )
  if ($null -eq $Object) {
    return $false
  }
  return $Object.PSObject.Properties.Name -contains $Name
}

function Get-JsonField {
  param(
    [Parameter(Mandatory = $true)][string]$Payload,
    [Parameter(Mandatory = $true)][string]$Field
  )
  $obj = Try-ParseJson $Payload
  if ($null -eq $obj) {
    return ''
  }
  if (-not (Has-JsonProp $obj $Field)) {
    return ''
  }
  $value = $obj.$Field
  if ($null -eq $value) {
    return ''
  }
  return [string]$value
}

function Test-IsAuthResponseJson {
  param([Parameter(Mandatory = $true)][string]$Payload)
  $obj = Try-ParseJson $Payload
  if ($null -eq $obj) {
    return $false
  }
  return (Has-JsonProp $obj 'auth_json') -or (Has-JsonProp $obj 'error')
}

function Test-IsInteractive {
  try {
    if ([Console]::IsInputRedirected) {
      return $false
    }
    if ([Console]::IsOutputRedirected) {
      return $false
    }
    return $true
  } catch {
    return $true
  }
}

function Should-ClearUiScreen {
  if ($env:CODEX_NO_MENU_CLEAR -eq '1') {
    return $false
  }
  return $true
}

function Clear-UiScreen {
  if (-not (Should-ClearUiScreen)) {
    return
  }
  try {
    [Console]::Write("`e[H`e[2J`e[3J")
  } catch {
    try {
      Write-Host "`e[H`e[2J`e[3J" -NoNewline
    } catch {
    }
  }
}

function Get-NodeExe {
  $candidates = @(
    (Join-Path $ROOT_DIR 'node\\node.exe'),
    (Join-Path $ROOT_DIR 'node\\bin\\node.exe'),
    (Join-Path $ROOT_DIR 'node\\bin\\node')
  )
  foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }
  return $null
}

function Get-NpmExe {
  $candidates = @(
    (Join-Path $ROOT_DIR 'node\\npm.cmd'),
    (Join-Path $ROOT_DIR 'node\\npm'),
    (Join-Path $ROOT_DIR 'node\\bin\\npm.cmd'),
    (Join-Path $ROOT_DIR 'node\\bin\\npm')
  )
  foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }
  return $null
}

function Get-CodexJs {
  $candidates = @(
    (Join-Path $ROOT_DIR 'codex\\node_modules\\@openai\\codex\\bin\\codex.js'),
    (Join-Path $ROOT_DIR 'codex\\node_modules\\@openai\\codex\\dist\\cli.js'),
    (Join-Path $ROOT_DIR 'codex\\lib\\node_modules\\@openai\\codex\\bin\\codex.js'),
    (Join-Path $ROOT_DIR 'codex\\lib\\node_modules\\@openai\\codex\\dist\\cli.js'),
    (Join-Path $ROOT_DIR 'codex/node_modules/@openai/codex/bin/codex.js'),
    (Join-Path $ROOT_DIR 'codex/node_modules/@openai/codex/dist/cli.js'),
    (Join-Path $ROOT_DIR 'codex/lib/node_modules/@openai/codex/bin/codex.js'),
    (Join-Path $ROOT_DIR 'codex/lib/node_modules/@openai/codex/dist/cli.js')
  )
  foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }

  $codexRoot = Join-Path $ROOT_DIR 'codex'
  if (Test-Path -LiteralPath $codexRoot) {
    $found = Get-ChildItem -Path $codexRoot -Recurse -File -ErrorAction SilentlyContinue |
      Where-Object {
        ($_.Name -eq 'codex.js' -or $_.Name -eq 'cli.js') -and
        ($_.FullName -match '[\\/]@openai[\\/]codex[\\/]')
      } |
      Select-Object -First 1
    if ($null -ne $found) {
      return $found.FullName
    }
  }
  return $null
}

function Get-CodexCommand {
  $candidates = @(
    (Join-Path $ROOT_DIR 'codex\\bin\\codex.cmd'),
    (Join-Path $ROOT_DIR 'codex\\bin\\codex.ps1'),
    (Join-Path $ROOT_DIR 'codex\\codex.cmd'),
    (Join-Path $ROOT_DIR 'codex\\codex.ps1')
  )
  foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }
  return $null
}

function Invoke-InteractiveProcess {
  param(
    [Parameter(Mandatory = $true)][string]$FilePath,
    [string[]]$Arguments = @()
  )

  if ($FilePath.ToLowerInvariant().EndsWith('.ps1')) {
    $pwsh = $null
    try {
      $pwsh = (Get-Command pwsh -ErrorAction SilentlyContinue).Source
    } catch {
      $pwsh = $null
    }
    if ([string]::IsNullOrWhiteSpace($pwsh)) {
      try {
        $pwsh = (Get-Command powershell -ErrorAction SilentlyContinue).Source
      } catch {
        $pwsh = $null
      }
    }
    if ([string]::IsNullOrWhiteSpace($pwsh)) {
      throw 'powershell executable not found for launching .ps1 command'
    }
    $cmdArgs = @('-ExecutionPolicy', 'Bypass', '-File', $FilePath) + $Arguments
    $proc = Start-Process -FilePath $pwsh -ArgumentList $cmdArgs -NoNewWindow -Wait -PassThru
    return [int]$proc.ExitCode
  }

  $process = Start-Process -FilePath $FilePath -ArgumentList $Arguments -NoNewWindow -Wait -PassThru
  return [int]$process.ExitCode
}

function Load-Token {
  $script:TOKEN = Read-TrimmedText $TOKEN_PATH
}

function Load-SelectedSession {
  $script:SELECTED_SESSION = Read-TrimmedText (Join-Path $CODEX_HOME 'selected_session')
}

function Load-AutoYolo {
  $raw = (Read-TrimmedText $AUTO_YOLO_PATH).ToLowerInvariant()
  switch ($raw) {
    '1' { $script:AUTO_YOLO = $true; return }
    'y' { $script:AUTO_YOLO = $true; return }
    'yes' { $script:AUTO_YOLO = $true; return }
    'true' { $script:AUTO_YOLO = $true; return }
    'on' { $script:AUTO_YOLO = $true; return }
    default { $script:AUTO_YOLO = $false; return }
  }
}

function Save-AutoYolo {
  param([bool]$Enabled)
  if ($Enabled) {
    Write-Utf8NoBom -Path $AUTO_YOLO_PATH -Value "1`n"
    $script:AUTO_YOLO = $true
    return
  }
  Remove-Item -LiteralPath $AUTO_YOLO_PATH -Force -ErrorAction SilentlyContinue
  $script:AUTO_YOLO = $false
}

function Toggle-AutoYolo {
  Load-AutoYolo
  if ($script:AUTO_YOLO) {
    Save-AutoYolo -Enabled:$false
    Write-Host 'Auto YOLO mode disabled.'
  } else {
    Save-AutoYolo -Enabled:$true
    Write-Host 'Auto YOLO mode enabled. Codex will start with --yolo.'
  }
}

function Get-AutoYoloLabel {
  if ($script:AUTO_YOLO) {
    return 'enabled'
  }
  return 'disabled'
}

function Save-SelectedSession {
  param([string]$SessionId)
  $target = Join-Path $CODEX_HOME 'selected_session'
  if ([string]::IsNullOrWhiteSpace($SessionId)) {
    Remove-Item -LiteralPath $target -Force -ErrorAction SilentlyContinue
    return
  }
  Write-Utf8NoBom -Path $target -Value ($SessionId + "`n")
}

function Build-RequestPayload {
  param(
    [Parameter(Mandatory = $true)][string]$Token,
    [string]$SessionId = ''
  )
  $osDescription = ''
  $osArch = ''
  try {
    $osDescription = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
    $osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
  } catch {
    $osDescription = [Environment]::OSVersion.VersionString
    $osArch = ''
  }

  $machineParts = @($osArch, $env:PROCESSOR_ARCHITECTURE) | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) }
  $payload = @{
    token = $Token
    client = @{
      hostname = $env:COMPUTERNAME
      system = $osDescription
      machine = ($machineParts -join ' | ')
      script_version = $RUN_SCRIPT_VERSION
    }
  }

  if (-not [string]::IsNullOrWhiteSpace($SessionId)) {
    $payload.session_id = $SessionId
  }

  return ($payload | ConvertTo-Json -Depth 8 -Compress)
}

function Build-InstallPayload {
  param(
    [string]$Username,
    [Parameter(Mandatory = $true)][string]$Password
  )
  $payload = @{ password = $Password }
  if (-not [string]::IsNullOrWhiteSpace($Username)) {
    $payload.username = $Username
  }
  return ($payload | ConvertTo-Json -Depth 4 -Compress)
}

function Request-Install {
  param([Parameter(Mandatory = $true)][string]$Payload)
  $urls = @($INSTALL_SERVER_URL)
  if ($INSTALL_SERVER_URL -eq 'https://codex.naskel.systems/codex-install') {
    $urls += $LEGACY_INSTALL_SERVER_URL
  }

  foreach ($url in $urls) {
    $response = Invoke-HttpPost -Url $url -Payload $Payload
    if (-not [string]::IsNullOrWhiteSpace($response)) {
      return $response
    }
  }

  return $null
}

function Request-InstallTokenInteractive {
  if (-not (Test-IsInteractive)) {
    Write-Error 'Error: interactive login required, but no TTY is available.'
    return $false
  }

  try {
    $username = Prompt -Label 'Account username (leave empty for legacy password mode)' -Default ''
    $password = if (-not [string]::IsNullOrWhiteSpace($username)) {
      Prompt-Secret -Label 'Account password'
    } else {
      Prompt-Secret -Label 'Legacy install password'
    }
  } catch {
    Write-Error 'Error: interactive login required, but no TTY is available.'
    return $false
  }

  $payload = Build-InstallPayload -Username $username -Password $password
  Write-Host 'Requesting install token...'
  $response = Request-Install -Payload $payload
  if ([string]::IsNullOrWhiteSpace($response)) {
    Write-Error "Error: failed to contact install endpoint ($INSTALL_SERVER_URL or $LEGACY_INSTALL_SERVER_URL)."
    return $false
  }

  $installToken = Get-JsonField -Payload $response -Field 'install_token'
  $installError = Get-JsonField -Payload $response -Field 'error'
  if ([string]::IsNullOrWhiteSpace($installToken)) {
    switch ($installError) {
      'invalid_password' { Write-Error 'Error: invalid legacy install password.' }
      'invalid_credentials' { Write-Error 'Error: invalid account username or password.' }
      'account_inactive' { Write-Error 'Error: account is inactive.' }
      'no_active_sessions' { Write-Error 'Error: no active sessions are assigned to this account.' }
      'password_required' { Write-Error 'Error: password is required.' }
      default { Write-Error 'Error: install token was not returned.' }
    }
    Write-Error "Response: $response"
    return $false
  }

  Write-Utf8NoBom -Path $TOKEN_PATH -Value ($installToken + "`n")
  Write-Utf8NoBom -Path (Join-Path $CODEX_HOME 'install-meta.json') -Value ($response + "`n")
  $script:TOKEN = $installToken
  Write-Host 'Login successful.'
  return $true
}

function Request-Auth {
  param(
    [Parameter(Mandatory = $true)][string]$Token,
    [string]$SessionId = ''
  )
  $payload = Build-RequestPayload -Token $Token -SessionId $SessionId
  $urls = @($AUTH_SERVER_URL)
  if ($AUTH_SERVER_URL -eq 'https://codex.naskel.systems/codex-auth') {
    $urls += $LEGACY_AUTH_SERVER_URL
  }

  foreach ($attempt in 1,2,3) {
    foreach ($url in $urls) {
      $response = Invoke-HttpPost -Url $url -Payload $payload
      if (-not [string]::IsNullOrWhiteSpace($response) -and (Test-IsAuthResponseJson -Payload $response)) {
        return $response
      }
    }
    Start-Sleep -Seconds $attempt
  }

  foreach ($url in $urls) {
    $response = Invoke-HttpPost -Url $url -Payload $payload
    if (-not [string]::IsNullOrWhiteSpace($response) -and (Test-IsAuthResponseJson -Payload $response)) {
      return $response
    }
  }

  return $null
}

function Update-RunPs1FromInstaller {
  param([Parameter(Mandatory = $true)][string]$InstallerUrl)

  $tmpInstaller = Join-Path ([System.IO.Path]::GetTempPath()) ("codexport-runupd.{0}.sh" -f ([Guid]::NewGuid().ToString('N')))
  $tmpRun = Join-Path ([System.IO.Path]::GetTempPath()) ("run-upd.{0}.ps1" -f ([Guid]::NewGuid().ToString('N')))

  try {
    $params = @{
      Uri = $InstallerUrl
      OutFile = $tmpInstaller
      TimeoutSec = 30
    }
    if ((Get-Command Invoke-WebRequest).Parameters.ContainsKey('UseBasicParsing')) {
      $params.UseBasicParsing = $true
    }
    Invoke-WebRequest @params | Out-Null
  } catch {
    Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
    Remove-Item -LiteralPath $tmpRun -Force -ErrorAction SilentlyContinue
    Write-Warning "Warning: failed to download installer from $InstallerUrl"
    return $false
  }
  $template = Extract-RunPs1TemplateFromInstallerFile -InstallerPath $tmpInstaller
  if ([string]::IsNullOrWhiteSpace($template)) {
    Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
    Remove-Item -LiteralPath $tmpRun -Force -ErrorAction SilentlyContinue
    Write-Warning 'Warning: downloaded installer does not contain run.ps1 template'
    return $false
  }

  $remoteRunVersion = Get-RunPs1VersionFromTemplate -Template $template
  if ([string]::IsNullOrWhiteSpace($remoteRunVersion)) {
    Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
    Remove-Item -LiteralPath $tmpRun -Force -ErrorAction SilentlyContinue
    Write-Warning 'Warning: downloaded run.ps1 template looks invalid'
    return $false
  }

  Write-Utf8NoBom -Path $tmpRun -Value $template
  Move-Item -LiteralPath $tmpRun -Destination (Join-Path $ROOT_DIR 'run.ps1') -Force
  Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
  return $true
}

function Extract-RunPs1TemplateFromInstallerFile {
  param([Parameter(Mandatory = $true)][string]$InstallerPath)

  if (-not (Test-Path -LiteralPath $InstallerPath)) {
    return $null
  }

  $startMarker = 'cat > "$OUT_DIR/run.ps1" <<''RUNNER_PS1'''
  $endMarker = 'RUNNER_PS1'
  $lines = Get-Content -LiteralPath $InstallerPath
  $collect = $false
  $outLines = New-Object System.Collections.Generic.List[string]

  foreach ($line in $lines) {
    if (-not $collect) {
      if ($line.Trim() -eq $startMarker) {
        $collect = $true
      }
      continue
    }

    if ($line.Trim() -eq $endMarker) {
      break
    }

    [void]$outLines.Add($line)
  }

  if ($outLines.Count -eq 0) {
    return $null
  }

  return ($outLines -join "`n") + "`n"
}

function Get-RunPs1VersionFromTemplate {
  param([Parameter(Mandatory = $true)][string]$Template)
  $match = [regex]::Match($Template, "(?m)^\$RUN_SCRIPT_VERSION\s*=\s*['""]([^'""]+)['""]")
  if (-not $match.Success) {
    return ''
  }
  return [string]$match.Groups[1].Value.Trim()
}

function Get-RunPs1VersionFromInstaller {
  param([Parameter(Mandatory = $true)][string]$InstallerUrl)

  $tmpInstaller = Join-Path ([System.IO.Path]::GetTempPath()) ("codexport-runver.{0}.sh" -f ([Guid]::NewGuid().ToString('N')))
  try {
    $params = @{
      Uri = $InstallerUrl
      OutFile = $tmpInstaller
      TimeoutSec = 25
    }
    if ((Get-Command Invoke-WebRequest).Parameters.ContainsKey('UseBasicParsing')) {
      $params.UseBasicParsing = $true
    }
    Invoke-WebRequest @params | Out-Null
  } catch {
    Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
    return ''
  }

  try {
    $template = Extract-RunPs1TemplateFromInstallerFile -InstallerPath $tmpInstaller
    if ([string]::IsNullOrWhiteSpace($template)) {
      return ''
    }
    return (Get-RunPs1VersionFromTemplate -Template $template)
  } finally {
    Remove-Item -LiteralPath $tmpInstaller -Force -ErrorAction SilentlyContinue
  }
}

function Check-ServerUpdates {
  if ($env:CODEX_SKIP_RUN_UPDATE -eq '1') {
    return
  }

  $serverInfoUrls = @($AUTH_VERSION_URL)
  if ($AUTH_VERSION_URL -eq 'https://codex.naskel.systems/codex-auth/version') {
    $serverInfoUrls += $LEGACY_AUTH_VERSION_URL
  }

  $info = $null
  foreach ($url in $serverInfoUrls) {
    $info = Invoke-HttpGet -Url $url
    if (-not [string]::IsNullOrWhiteSpace($info)) {
      break
    }
  }

  $recommendedVersion = ''
  $recommendedInstallerUrl = ''
  if (-not [string]::IsNullOrWhiteSpace($info)) {
    $obj = Try-ParseJson $info
    if ($null -ne $obj) {
      if (Has-JsonProp $obj 'script_version') {
        $recommendedVersion = [string]$obj.script_version
      }
      if (Has-JsonProp $obj 'latest_codexport_url') {
        $recommendedInstallerUrl = [string]$obj.latest_codexport_url
      }
    }
  }

  $chosenVersion = ''
  $chosenInstallerUrl = ''

  if (
    -not [string]::IsNullOrWhiteSpace($recommendedVersion) -and
    -not [string]::IsNullOrWhiteSpace($recommendedInstallerUrl) -and
    (Version-Gt -Left $recommendedVersion -Right $RUN_SCRIPT_VERSION)
  ) {
    $chosenVersion = $recommendedVersion
    $chosenInstallerUrl = $recommendedInstallerUrl
  } else {
    $probeUrls = @()
    if (-not [string]::IsNullOrWhiteSpace($env:CODEX_EXPORT_UPDATE_URL)) {
      $probeUrls += [string]$env:CODEX_EXPORT_UPDATE_URL
    }
    if (-not [string]::IsNullOrWhiteSpace($recommendedInstallerUrl)) {
      $probeUrls += $recommendedInstallerUrl
    }
    $probeUrls += 'https://codex.naskel.systems/codexport.sh'
    $probeUrls += 'https://codex.naskel.petrsu.ru/codexport.sh'

    $probeUrls = $probeUrls |
      Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
      Select-Object -Unique

    foreach ($probeUrl in $probeUrls) {
      $probeVersion = Get-RunPs1VersionFromInstaller -InstallerUrl $probeUrl
      if ([string]::IsNullOrWhiteSpace($probeVersion)) {
        continue
      }
      if (-not (Version-Gt -Left $probeVersion -Right $RUN_SCRIPT_VERSION)) {
        continue
      }
      if ([string]::IsNullOrWhiteSpace($chosenVersion) -or (Version-Gt -Left $probeVersion -Right $chosenVersion)) {
        $chosenVersion = $probeVersion
        $chosenInstallerUrl = $probeUrl
      }
    }
  }

  if ([string]::IsNullOrWhiteSpace($chosenVersion) -or [string]::IsNullOrWhiteSpace($chosenInstallerUrl)) {
    return
  }

  Write-Host "Update available: run.ps1 $RUN_SCRIPT_VERSION -> latest $chosenVersion"
  Write-Host "Run script source URL: $chosenInstallerUrl"

  if ($env:CODEX_AUTO_RUN_UPDATE -ne '0') {
    if (Update-RunPs1FromInstaller -InstallerUrl $chosenInstallerUrl) {
      Write-Host "Updated: $ROOT_DIR\\run.ps1"
      Write-Host 'Restarting run.ps1 to apply the new version...'
      $env:CODEX_SKIP_RUN_UPDATE = '1'
      & (Join-Path $ROOT_DIR 'run.ps1') @script:ORIGINAL_ARGS
      $code = $LASTEXITCODE
      exit $code
    }
    return
  }

  if (-not (Test-IsInteractive)) {
    return
  }

  $answer = Prompt -Label "Update $ROOT_DIR\\run.ps1 now? (Y/n)" -Default 'Y'
  if ($answer -match '^(?:[Yy]|)$') {
    if (Update-RunPs1FromInstaller -InstallerUrl $chosenInstallerUrl) {
      Write-Host "Updated: $ROOT_DIR\\run.ps1"
      Write-Host 'Restarting run.ps1 to apply the new version...'
      $env:CODEX_SKIP_RUN_UPDATE = '1'
      & (Join-Path $ROOT_DIR 'run.ps1') @script:ORIGINAL_ARGS
      $code = $LASTEXITCODE
      exit $code
    }
  }
}

function Parse-AuthResponse {
  param([Parameter(Mandatory = $true)][string]$Response)

  $script:LAST_AUTH_ERROR = ''
  $obj = Try-ParseJson $Response
  if ($null -eq $obj) {
    $script:LAST_AUTH_ERROR = 'invalid_json'
    return $false
  }

  if ((Has-JsonProp $obj 'error') -and -not [string]::IsNullOrWhiteSpace([string]$obj.error)) {
    $script:LAST_AUTH_ERROR = [string]$obj.error
    return $false
  }

  if (-not (Has-JsonProp $obj 'auth_json')) {
    $script:LAST_AUTH_ERROR = 'missing_auth_json'
    return $false
  }

  $authPayload = if ($obj.auth_json -is [string]) {
    [string]$obj.auth_json
  } else {
    $obj.auth_json | ConvertTo-Json -Depth 50
  }

  if (-not $authPayload.EndsWith("`n")) {
    $authPayload += "`n"
  }

  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'auth.json') -Value $authPayload

  $proxyData = @{
    proxy_url = if ((Has-JsonProp $obj 'proxy_url') -and $null -ne $obj.proxy_url) { [string]$obj.proxy_url } else { '' }
    no_proxy = if ((Has-JsonProp $obj 'no_proxy') -and $null -ne $obj.no_proxy) { [string]$obj.no_proxy } else { '' }
  }
  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'proxy.json') -Value (($proxyData | ConvertTo-Json -Depth 4 -Compress) + "`n")

  $sessions = @()
  if ((Has-JsonProp $obj 'available_sessions') -and $null -ne $obj.available_sessions) {
    $sessions = @($obj.available_sessions)
  }
  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'sessions.json') -Value (($sessions | ConvertTo-Json -Depth 10) + "`n")

  $selected = if ((Has-JsonProp $obj 'session_id') -and $null -ne $obj.session_id) { [string]$obj.session_id } else { '' }
  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'selected_session.txt') -Value ($selected + "`n")

  $accountObj = $null
  if ((Has-JsonProp $obj 'account') -and $null -ne $obj.account) {
    $accountObj = $obj.account
  }
  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'account.json') -Value (($accountObj | ConvertTo-Json -Depth 6 -Compress) + "`n")

  $versionObj = @{
    script_version = if ((Has-JsonProp $obj 'script_version') -and $null -ne $obj.script_version) { [string]$obj.script_version } else { '' }
    server_version = if ((Has-JsonProp $obj 'server_version') -and $null -ne $obj.server_version) { [string]$obj.server_version } else { '' }
    latest_codexport_url = if ((Has-JsonProp $obj 'latest_codexport_url') -and $null -ne $obj.latest_codexport_url) { [string]$obj.latest_codexport_url } else { '' }
  }
  Write-Utf8NoBom -Path (Join-Path $RAM_DIR 'version.json') -Value (($versionObj | ConvertTo-Json -Depth 6 -Compress) + "`n")

  return $true
}

function Refresh-AuthSnapshot {
  Load-Token
  if ([string]::IsNullOrWhiteSpace($script:TOKEN)) {
    return $false
  }

  Load-SelectedSession
  $authResponse = $null
  if (-not [string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) {
    $authResponse = Request-Auth -Token $script:TOKEN -SessionId $script:SELECTED_SESSION
    $authError = if (-not [string]::IsNullOrWhiteSpace($authResponse)) { Get-JsonField -Payload $authResponse -Field 'error' } else { '' }
    if ($authError -eq 'invalid_session') {
      $authResponse = $null
      $script:SELECTED_SESSION = ''
    }
  }

  if ([string]::IsNullOrWhiteSpace($authResponse)) {
    $authResponse = Request-Auth -Token $script:TOKEN -SessionId ''
  }
  if ([string]::IsNullOrWhiteSpace($authResponse)) {
    return $false
  }
  if (-not (Parse-AuthResponse -Response $authResponse)) {
    return $false
  }

  $script:SELECTED_SESSION = Read-TrimmedText (Join-Path $RAM_DIR 'selected_session.txt')
  if (-not [string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) {
    Save-SelectedSession -SessionId $script:SELECTED_SESSION
  }
  return $true
}

function Get-ShortHealthMessage {
  param([string]$Message)
  if ([string]::IsNullOrWhiteSpace($Message)) { return '' }
  if ($Message -eq 'ok') { return '' }
  if ($Message.StartsWith('usage_request_failed')) { return 'usage unavailable' }
  if ($Message.StartsWith('http_401') -or $Message.StartsWith('http_403')) { return 'access denied' }
  if ($Message.StartsWith('access_token_expired')) { return 'token expired' }
  if ($Message -eq 'api_key_mode_no_wham_limits') { return 'no limits for api_key' }
  if ($Message.Length -gt 36) { return ($Message.Substring(0, 36) + '...') }
  return $Message
}

function Format-LimitSummary {
  param(
    [object]$Limit,
    [string]$Fallback = ''
  )
  if ($null -eq $Limit) {
    if ([string]::IsNullOrWhiteSpace($Fallback)) { return 'n/a' }
    return $Fallback
  }

  $remainingRaw = $null
  $resetRaw = $null
  if ($Limit -is [System.Collections.IDictionary]) {
    $remainingRaw = $Limit['remaining_percent']
    $resetRaw = $Limit['reset_at']
  } else {
    if ($Limit.PSObject.Properties.Name -contains 'remaining_percent') { $remainingRaw = $Limit.remaining_percent }
    if ($Limit.PSObject.Properties.Name -contains 'reset_at') { $resetRaw = $Limit.reset_at }
  }

  $remainingNum = 0.0
  if ($null -eq $remainingRaw -or -not [double]::TryParse([string]$remainingRaw, [ref]$remainingNum)) {
    if ([string]::IsNullOrWhiteSpace($Fallback)) { return 'n/a' }
    return $Fallback
  }

  if ($remainingNum -lt 0) { $remainingNum = 0 }
  if ($remainingNum -gt 100) { $remainingNum = 100 }
  $rounded = [Math]::Round($remainingNum, 1)
  $percent = if ([Math]::Abs($rounded - [Math]::Round($rounded)) -lt 0.0001) {
    ("{0:0}%" -f $rounded)
  } else {
    ("{0:0.0}%" -f $rounded)
  }

  $resetNum = 0.0
  $resetText = '-'
  if ($null -ne $resetRaw -and [double]::TryParse([string]$resetRaw, [ref]$resetNum) -and $resetNum -gt 0) {
    try {
      $dt = [DateTimeOffset]::FromUnixTimeSeconds([long][Math]::Floor($resetNum)).ToLocalTime().DateTime
      $resetText = $dt.ToString()
    } catch {
      $resetText = '-'
    }
  }
  return "$percent | reset $resetText"
}

function Show-AccountAndSessionsOverview {
  $sessions = @()
  $sessionsFile = Join-Path $RAM_DIR 'sessions.json'
  if (Test-Path -LiteralPath $sessionsFile) {
    $parsed = Try-ParseJson (Get-Content -LiteralPath $sessionsFile -Raw)
    if ($null -ne $parsed) { $sessions = @($parsed) }
  }

  $accountName = ''
  $accountFile = Join-Path $RAM_DIR 'account.json'
  if (Test-Path -LiteralPath $accountFile) {
    $accountObj = Try-ParseJson (Get-Content -LiteralPath $accountFile -Raw)
    if ($null -ne $accountObj -and (Has-JsonProp $accountObj 'username') -and -not [string]::IsNullOrWhiteSpace([string]$accountObj.username)) {
      $accountName = [string]$accountObj.username
    }
  }
  if ([string]::IsNullOrWhiteSpace($accountName)) {
    $installMetaPath = Join-Path $CODEX_HOME 'install-meta.json'
    if (Test-Path -LiteralPath $installMetaPath) {
      $meta = Try-ParseJson (Get-Content -LiteralPath $installMetaPath -Raw)
      if ($null -ne $meta -and (Has-JsonProp $meta 'account') -and $null -ne $meta.account) {
        if ((Has-JsonProp $meta.account 'username') -and -not [string]::IsNullOrWhiteSpace([string]$meta.account.username)) {
          $accountName = [string]$meta.account.username
        }
      }
    }
  }
  if ([string]::IsNullOrWhiteSpace($accountName)) {
    $accountName = 'legacy'
  }

  $serverRunVersion = ''
  $serverVersion = ''
  $versionFile = Join-Path $RAM_DIR 'version.json'
  if (Test-Path -LiteralPath $versionFile) {
    $versionObj = Try-ParseJson (Get-Content -LiteralPath $versionFile -Raw)
    if ($null -ne $versionObj) {
      if ((Has-JsonProp $versionObj 'script_version') -and $null -ne $versionObj.script_version) {
        $serverRunVersion = [string]$versionObj.script_version
      }
      if ((Has-JsonProp $versionObj 'server_version') -and $null -ne $versionObj.server_version) {
        $serverVersion = [string]$versionObj.server_version
      }
    }
  }

  $scriptLine = "  Script version: $RUN_SCRIPT_VERSION"
  if (-not [string]::IsNullOrWhiteSpace($serverRunVersion)) {
    $scriptLine = "$scriptLine (server $serverRunVersion)"
  }
  Write-Host $scriptLine
  if (-not [string]::IsNullOrWhiteSpace($serverVersion)) {
    Write-Host "  Auth server: $serverVersion"
  }
  Write-Host "  Account: $accountName"
  $selectedLabel = if ([string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) { 'none' } else { $script:SELECTED_SESSION }
  Write-Host "  Selected session: $selectedLabel"

  if ($sessions.Count -le 0) {
    Write-Host '  Sessions: unavailable'
    return
  }

  Write-Host '  Sessions:'
  foreach ($session in $sessions) {
    $id = if ((Has-JsonProp $session 'id') -and $null -ne $session.id) { [string]$session.id } else { '' }
    $name = if ((Has-JsonProp $session 'name') -and -not [string]::IsNullOrWhiteSpace([string]$session.name)) { [string]$session.name } else { $id }
    $marker = if ($id -eq $script:SELECTED_SESSION -and -not [string]::IsNullOrWhiteSpace($id)) { '*' } else { ' ' }
    $defaultMark = if ((Has-JsonProp $session 'is_default') -and $session.is_default -eq $true) { ' default' } else { '' }
    $health = if ((Has-JsonProp $session 'health') -and $null -ne $session.health) { $session.health } else { $null }
    $fallback = ''
    $state = 'unknown'
    if ($null -ne $health) {
      $msg = if ((Has-JsonProp $health 'message') -and $null -ne $health.message) { [string]$health.message } else { '' }
      $fallback = Get-ShortHealthMessage -Message $msg
      if ((Has-JsonProp $health 'is_valid') -and $health.is_valid -eq $true) {
        $state = 'valid'
      } elseif ((Has-JsonProp $health 'is_valid') -and $health.is_valid -eq $false) {
        $state = 'invalid'
      }
    }
    $limit5hObj = $null
    $limit7dObj = $null
    if ($null -ne $health -and (Has-JsonProp $health 'limit_5h')) { $limit5hObj = $health.limit_5h }
    if ($null -ne $health -and (Has-JsonProp $health 'limit_weekly')) { $limit7dObj = $health.limit_weekly }
    $limit5h = Format-LimitSummary -Limit $limit5hObj -Fallback $fallback
    $limit7d = Format-LimitSummary -Limit $limit7dObj -Fallback $fallback
    Write-Host ("    {0} {1} [{2}]{3} | 5h {4} | 7d {5} | {6}" -f $marker, $name, $id, $defaultMark, $limit5h, $limit7d, $state)
  }
}

function Choose-SessionIfNeeded {
  param([int]$ForcePrompt = 0)

  $sessionsFile = Join-Path $RAM_DIR 'sessions.json'
  if (-not (Test-Path -LiteralPath $sessionsFile)) {
    return $true
  }

  $sessions = Try-ParseJson (Get-Content -LiteralPath $sessionsFile -Raw)
  if ($null -eq $sessions) {
    $sessions = @()
  } else {
    $sessions = @($sessions)
  }

  $count = $sessions.Count
  if ($count -le 0) {
    Write-Error 'Error: auth server returned zero available sessions.'
    return $false
  }

  $selectedFromServer = Read-TrimmedText (Join-Path $RAM_DIR 'selected_session.txt')

  if ($count -eq 1) {
    $singleId = [string]$sessions[0].id
    if (-not [string]::IsNullOrWhiteSpace($singleId)) {
      $script:SELECTED_SESSION = $singleId
      Save-SelectedSession -SessionId $singleId
    }
    return $true
  }

  if ($ForcePrompt -ne 1 -and -not [string]::IsNullOrWhiteSpace($selectedFromServer)) {
    $script:SELECTED_SESSION = $selectedFromServer
    Save-SelectedSession -SessionId $selectedFromServer
    return $true
  }

  if (-not (Test-IsInteractive)) {
    $chosen = $sessions | Where-Object { [string]$_.id -eq $selectedFromServer } | Select-Object -First 1
    if ($null -eq $chosen) {
      $chosen = $sessions | Where-Object { $_.is_default -eq $true } | Select-Object -First 1
    }
    if ($null -eq $chosen) {
      $chosen = $sessions[0]
    }

    $chosenId = [string]$chosen.id
    if (-not [string]::IsNullOrWhiteSpace($chosenId)) {
      $script:SELECTED_SESSION = $chosenId
      Save-SelectedSession -SessionId $chosenId
    }
    return $true
  }

  Clear-UiScreen
  Write-Host 'Available sessions:'
  for ($i = 0; $i -lt $sessions.Count; $i++) {
    $session = $sessions[$i]
    $mark = ''
    if ([string]$session.id -eq $selectedFromServer) {
      $mark = ' (current)'
    } elseif ($session.is_default -eq $true) {
      $mark = ' (default)'
    }
    $name = if ([string]::IsNullOrWhiteSpace([string]$session.name)) { [string]$session.id } else { [string]$session.name }
    $health = if ((Has-JsonProp $session 'health') -and $null -ne $session.health) { $session.health } else { $null }
    $fallback = ''
    if ($null -ne $health -and (Has-JsonProp $health 'message') -and $null -ne $health.message) {
      $fallback = Get-ShortHealthMessage -Message ([string]$health.message)
    }
    $limit5hObj = $null
    $limit7dObj = $null
    if ($null -ne $health -and (Has-JsonProp $health 'limit_5h')) { $limit5hObj = $health.limit_5h }
    if ($null -ne $health -and (Has-JsonProp $health 'limit_weekly')) { $limit7dObj = $health.limit_weekly }
    $limit5h = Format-LimitSummary -Limit $limit5hObj -Fallback $fallback
    $limit7d = Format-LimitSummary -Limit $limit7dObj -Fallback $fallback
    Write-Host ("{0}) {1} [{2}]{3} | 5h {4} | 7d {5}" -f ($i + 1), $name, [string]$session.id, $mark, $limit5h, $limit7d)
  }

  $defaultIndex = 1
  for ($i = 0; $i -lt $sessions.Count; $i++) {
    if ([string]$sessions[$i].id -eq $selectedFromServer) {
      $defaultIndex = $i + 1
      break
    }
    if ($sessions[$i].is_default -eq $true) {
      $defaultIndex = $i + 1
    }
  }

  $choice = Prompt -Label 'Session number' -Default ([string]$defaultIndex)
  $chosenIndex = 0
  if (-not [int]::TryParse($choice, [ref]$chosenIndex) -or $chosenIndex -lt 1 -or $chosenIndex -gt $sessions.Count) {
    Write-Error 'Error: invalid session choice.'
    return $false
  }

  $chosenId = [string]$sessions[$chosenIndex - 1].id
  if ([string]::IsNullOrWhiteSpace($chosenId)) {
    Write-Error 'Error: invalid session choice.'
    return $false
  }

  if ($chosenId -ne $selectedFromServer) {
    $response = Request-Auth -Token $script:TOKEN -SessionId $chosenId
    if ([string]::IsNullOrWhiteSpace($response)) {
      Write-Error "Error: failed to request selected session '$chosenId'."
      return $false
    }
    if (-not (Parse-AuthResponse -Response $response)) {
      Write-Error 'Error while parsing selected session response.'
      return $false
    }
  }

  $script:SELECTED_SESSION = $chosenId
  Save-SelectedSession -SessionId $chosenId
  return $true
}

function Clear-LoginState {
  Remove-Item -LiteralPath $TOKEN_PATH -Force -ErrorAction SilentlyContinue
  Remove-Item -LiteralPath (Join-Path $CODEX_HOME 'install-meta.json') -Force -ErrorAction SilentlyContinue
  Remove-Item -LiteralPath (Join-Path $CODEX_HOME 'selected_session') -Force -ErrorAction SilentlyContinue
  Remove-Item -LiteralPath (Join-Path $CODEX_HOME 'auth.json') -Force -ErrorAction SilentlyContinue
}

function Update-Codex {
  $npmExe = Get-NpmExe
  if ($null -eq $npmExe) {
    Write-Error "Error: npm not found under $ROOT_DIR\\node"
    return $false
  }

  Write-Host 'Updating installed Codex CLI...'
  & $npmExe install -g --include=optional --prefix (Join-Path $ROOT_DIR 'codex') '@openai/codex@latest'
  if ($LASTEXITCODE -ne 0) {
    Write-Error 'Codex update failed.'
    return $false
  }

  Write-Host 'Codex update complete.'
  return $true
}

function Show-Help {
  @'
Usage:
  run.ps1                   # interactive menu
  run.ps1 menu
  run.ps1 login [codex args...]
  run.ps1 logout
  run.ps1 update-codex
  run.ps1 [codex args...]

Commands:
  menu          Open interactive menu
  login         Force re-login and refresh install token, then start Codex
  logout        Remove local token/session selection
  (Menu only)   Toggle auto YOLO mode (adds --yolo to Start Codex)
  update-codex  Update installed @openai/codex package
'@ | Write-Host
}

function Ensure-AuthReady {
  Load-Token
  if ([string]::IsNullOrWhiteSpace($script:TOKEN)) {
    Write-Host 'No saved login token found. Please login.'
    if (-not (Request-InstallTokenInteractive)) {
      return $false
    }
  }

  Load-SelectedSession

  while ($true) {
    $authResponse = $null

    if (-not [string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) {
      $authResponse = Request-Auth -Token $script:TOKEN -SessionId $script:SELECTED_SESSION
      $authError = if (-not [string]::IsNullOrWhiteSpace($authResponse)) { Get-JsonField -Payload $authResponse -Field 'error' } else { '' }
      if ($authError -eq 'invalid_session') {
        $authResponse = $null
        $script:SELECTED_SESSION = ''
        Save-SelectedSession -SessionId ''
      }
    }

    if ([string]::IsNullOrWhiteSpace($authResponse)) {
      $authResponse = Request-Auth -Token $script:TOKEN -SessionId ''
    }

    if ([string]::IsNullOrWhiteSpace($authResponse)) {
      Write-Error 'Error: failed to contact auth server.'
      return $false
    }

    if (Parse-AuthResponse -Response $authResponse) {
      $script:SELECTED_SESSION = Read-TrimmedText (Join-Path $RAM_DIR 'selected_session.txt')
      if (-not [string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) {
        Save-SelectedSession -SessionId $script:SELECTED_SESSION
      }
      return $true
    }

    $authError = $script:LAST_AUTH_ERROR
    if ([string]::IsNullOrWhiteSpace($authError)) {
      Write-Error 'Error while parsing auth response.'
      return $false
    }

    switch ($authError) {
      'invalid_token' {
        Write-Host "Saved token is invalid or expired ($authError)."
        Clear-LoginState
        $script:TOKEN = ''
        $script:SELECTED_SESSION = ''
        if (-not (Request-InstallTokenInteractive)) { return $false }
      }
      'account_inactive' {
        Write-Host "Saved token is invalid or expired ($authError)."
        Clear-LoginState
        $script:TOKEN = ''
        $script:SELECTED_SESSION = ''
        if (-not (Request-InstallTokenInteractive)) { return $false }
      }
      'no_sessions_assigned' {
        Write-Error 'Access denied: no sessions are assigned to this account.'
        return $false
      }
      'no_sessions_configured' {
        Write-Error 'Access denied: no sessions are assigned to this account.'
        return $false
      }
      default {
        Write-Error "Auth error: $authError"
        return $false
      }
    }
  }
}

function Prepare-RuntimeEnv {
  Copy-Item -LiteralPath (Join-Path $RAM_DIR 'auth.json') -Destination (Join-Path $CODEX_HOME 'auth.json') -Force

  $env:HOME = $ROOT_DIR
  $env:CODEX_HOME = $CODEX_HOME

  $proxyJsonPath = Join-Path $RAM_DIR 'proxy.json'
  $proxyUrl = ''
  $noProxy = ''
  if (Test-Path -LiteralPath $proxyJsonPath) {
    $proxyObj = Try-ParseJson (Get-Content -LiteralPath $proxyJsonPath -Raw)
    if ($null -ne $proxyObj) {
      if ((Has-JsonProp $proxyObj 'proxy_url') -and $null -ne $proxyObj.proxy_url) {
        $proxyUrl = [string]$proxyObj.proxy_url
      }
      if ((Has-JsonProp $proxyObj 'no_proxy') -and $null -ne $proxyObj.no_proxy) {
        $noProxy = [string]$proxyObj.no_proxy
      }
    }
  }

  if (-not [string]::IsNullOrWhiteSpace($proxyUrl)) {
    $codexHttpProxy = $proxyUrl
    if ($proxyUrl.StartsWith('https://')) {
      $codexHttpProxy = 'http://' + $proxyUrl.Substring(8)
    }

    $env:HTTP_PROXY = $codexHttpProxy
    $env:http_proxy = $codexHttpProxy
    $env:HTTPS_PROXY = $proxyUrl
    $env:https_proxy = $proxyUrl

    Remove-Item Env:\ALL_PROXY -ErrorAction SilentlyContinue
    Remove-Item Env:\all_proxy -ErrorAction SilentlyContinue
  }

  $openaiNoProxy = 'chatgpt.com,chat.openai.com,api.openai.com,auth.openai.com,openai.com'
  $combinedNoProxy = $noProxy
  # Default is strict proxy mode (no direct OpenAI bypass).
  if ($env:CODEX_BYPASS_PROXY_FOR_OPENAI -eq '1') {
    if (-not [string]::IsNullOrWhiteSpace($combinedNoProxy)) {
      $combinedNoProxy = "$combinedNoProxy,$openaiNoProxy"
    } else {
      $combinedNoProxy = $openaiNoProxy
    }
  }

  if (-not [string]::IsNullOrWhiteSpace($combinedNoProxy)) {
    $env:NO_PROXY = $combinedNoProxy
    $env:no_proxy = $combinedNoProxy
  }

  $pathParts = @(
    (Join-Path $ROOT_DIR 'node'),
    (Join-Path $ROOT_DIR 'node\\bin'),
    (Join-Path $ROOT_DIR 'codex\\bin'),
    $env:PATH
  ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
  $env:PATH = ($pathParts -join ';')
}

function Start-Codex {
  param([string[]]$CodexArgs = @())

  if (-not (Ensure-AuthReady)) {
    return 1
  }

  if (-not (Choose-SessionIfNeeded -ForcePrompt 0)) {
    return 1
  }

  Load-AutoYolo
  Prepare-RuntimeEnv
  $effectiveArgs = @($CodexArgs)
  if ($script:AUTO_YOLO) {
    $hasYolo = $false
    foreach ($arg in $effectiveArgs) {
      if ([string]$arg -eq '--yolo') {
        $hasYolo = $true
        break
      }
    }
    if (-not $hasYolo) {
      $effectiveArgs = @('--yolo') + $effectiveArgs
    }
  }

  $codexCommand = Get-CodexCommand
  if ($null -ne $codexCommand) {
    try {
      return (Invoke-InteractiveProcess -FilePath $codexCommand -Arguments $effectiveArgs)
    } catch {
      Write-Error "Error: failed to launch codex command $codexCommand ($_)"
      return 1
    }
  }

  $nodeExe = Get-NodeExe
  if ($null -eq $nodeExe) {
    Write-Error "Error: node executable not found under $ROOT_DIR\\node"
    return 1
  }

  $codexJs = Get-CodexJs
  if ($null -eq $codexJs) {
    Write-Error "Error: codex launcher not found under $ROOT_DIR\\codex"
    return 1
  }

  try {
    $nodeArgs = @($codexJs) + $effectiveArgs
    return (Invoke-InteractiveProcess -FilePath $nodeExe -Arguments $nodeArgs)
  } catch {
    Write-Error "Error: failed to launch codex.js via node ($_)"
    return 1
  }
}

function Open-InteractiveMenu {
  while ($true) {
    Load-Token
    Load-SelectedSession
    Load-AutoYolo
    $snapshotOk = $false
    if (-not [string]::IsNullOrWhiteSpace($script:TOKEN)) {
      $snapshotOk = Refresh-AuthSnapshot
      if ($snapshotOk) {
        Load-SelectedSession
      }
    }

    Clear-UiScreen
    Write-Host 'Codex menu:'
    if (-not [string]::IsNullOrWhiteSpace($script:TOKEN) -and $snapshotOk) {
      Write-Host '  Login state: logged in'
    } elseif (-not [string]::IsNullOrWhiteSpace($script:TOKEN)) {
      Write-Host '  Login state: token saved (re-login may be required)'
    } else {
      Write-Host '  Login state: not logged in'
    }
    if ($snapshotOk) {
      Show-AccountAndSessionsOverview
    } else {
      Write-Host "  Script version: $RUN_SCRIPT_VERSION"
      $selectedLabel = if ([string]::IsNullOrWhiteSpace($script:SELECTED_SESSION)) { 'none' } else { $script:SELECTED_SESSION }
      Write-Host "  Selected session: $selectedLabel"
    }
    Write-Host "  YOLO auto-start: $(Get-AutoYoloLabel)"
    Write-Host '1) Start Codex'
    Write-Host '2) Choose session'
    Write-Host '3) Login / Re-login'
    Write-Host '4) Logout'
    Write-Host '5) Update Codex'
    Write-Host '6) Toggle YOLO auto-start'
    Write-Host '7) Exit'

    $choice = Prompt -Label 'Choice' -Default '1'
    switch ($choice) {
      '1' {
        Clear-UiScreen
        return (Start-Codex -CodexArgs @())
      }
      '2' {
        if (Ensure-AuthReady) {
          [void](Choose-SessionIfNeeded -ForcePrompt 1)
        }
      }
      '3' {
        [void](Request-InstallTokenInteractive)
      }
      '4' {
        Clear-LoginState
        Write-Host 'Logged out. Local token and selected session removed.'
      }
      '5' {
        [void](Update-Codex)
      }
      '6' {
        Toggle-AutoYolo
      }
      { $_ -in @('7', 'q', 'quit', 'exit') } {
        return 0
      }
      default {
        Write-Host "Unknown choice: $choice"
      }
    }
  }
}

function Invoke-Main {
  param([string[]]$Args)

  $first = if ($Args.Count -gt 0) { $Args[0] } else { '' }

  switch ($first) {
    '--help' { Show-Help; return 0 }
    '-h' { Show-Help; return 0 }
    'help' { Show-Help; return 0 }
    'menu' {
      Check-ServerUpdates
      return (Open-InteractiveMenu)
    }
    'logout' {
      Clear-LoginState
      Write-Host 'Logged out. Local token and selected session removed.'
      return 0
    }
    'update-codex' {
      if (Update-Codex) {
        return 0
      }
      return 1
    }
    'login' {
      $rest = if ($Args.Count -gt 1) { $Args[1..($Args.Count - 1)] } else { @() }
      Check-ServerUpdates
      if (-not (Request-InstallTokenInteractive)) {
        return 1
      }
      return (Start-Codex -CodexArgs $rest)
    }
  }

  if ($Args.Count -eq 0 -and (Test-IsInteractive)) {
    Check-ServerUpdates
    return (Open-InteractiveMenu)
  }

  Check-ServerUpdates
  return (Start-Codex -CodexArgs $Args)
}

Ensure-Dir -Path $CODEX_HOME
Ensure-Dir -Path (Join-Path $CODEX_HOME 'sessions')
Ensure-Dir -Path $RAM_DIR

$exitCode = 1
try {
  $exitCode = Invoke-Main -Args $args
} finally {
  Remove-Item -LiteralPath $RAM_DIR -Recurse -Force -ErrorAction SilentlyContinue
  Remove-Item -LiteralPath (Join-Path $CODEX_HOME 'auth.json') -Force -ErrorAction SilentlyContinue
}

if ($exitCode -is [int]) {
  exit $exitCode
}

exit 0
RUNNER_PS1


    CODEX_HOME="$OUT_DIR/.codex"
    mkdir -p "$CODEX_HOME"
    if [[ -n "$LEGACY_BACKUP_HOME" && -d "$LEGACY_BACKUP_HOME" ]]; then
      if ! cp -a "$LEGACY_BACKUP_HOME/." "$CODEX_HOME/"; then
        echo "Error: failed to restore legacy .codex backup into $CODEX_HOME." >&2
        exit 1
      fi
      echo "Legacy chats/history restored into $CODEX_HOME."
    fi
    printf '%s\n' "$INSTALL_TOKEN" > "$CODEX_HOME/install.token"
    chmod 600 "$CODEX_HOME/install.token"
    printf '%s\n' "$install_response" > "$CODEX_HOME/install-meta.json"
    chmod 600 "$CODEX_HOME/install-meta.json"

    chmod +x "$OUT_DIR/run.sh"

    echo "Portable build ready: $OUT_DIR"
    echo "Run: $OUT_DIR/run.sh"
    echo "Run (PowerShell): $OUT_DIR/run.ps1"
    ;;

  pack)
    OUT_DIR="${2:-}"
    ARCHIVE="${3:-}"
    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi
    if [[ -z "$ARCHIVE" ]]; then
      ARCHIVE="$(prompt "ARCHIVE" "codex-portable.tgz")"
    fi
    if [[ ! -d "$OUT_DIR" ]]; then
      echo "Error: $OUT_DIR not found." >&2
      exit 1
    fi

    ARCHIVE_PASSWORD=""
    if [[ -t 0 ]]; then
      read -r -s -p "Archive password (leave empty for none): " ARCHIVE_PASSWORD
      echo
    else
      read -r -s -p "Archive password (leave empty for none): " ARCHIVE_PASSWORD < /dev/tty
      echo > /dev/tty
    fi

    if [[ -n "$ARCHIVE_PASSWORD" ]]; then
      if ! command -v openssl >/dev/null 2>&1; then
        echo "Error: openssl is required for encrypted archives." >&2
        exit 1
      fi
      if command -v pv >/dev/null 2>&1; then
        tar -czf - -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")" \
          | pv \
          | openssl enc -aes-256-cbc -pbkdf2 -salt \
            -pass "pass:$ARCHIVE_PASSWORD" \
            -out "$ARCHIVE"
      else
        tmp_archive="$(mktemp /tmp/codex-archive.XXXXXX.tgz)"
        tar -czf "$tmp_archive" -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")"
        openssl enc -aes-256-cbc -pbkdf2 -salt \
          -pass "pass:$ARCHIVE_PASSWORD" \
          -in "$tmp_archive" -out "$ARCHIVE"
        rm -f "$tmp_archive"
      fi
      echo "Packed (encrypted): $ARCHIVE"
    else
      if command -v pv >/dev/null 2>&1; then
        tar -czf - -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")" | pv > "$ARCHIVE"
      else
        tar -czf "$ARCHIVE" -C "$(dirname "$OUT_DIR")" "$(basename "$OUT_DIR")"
      fi
      echo "Packed: $ARCHIVE"
    fi
    ;;

  unpack)
    ARCHIVE="${2:-}"
    OUT_DIR="${3:-}"
    if [[ -z "$ARCHIVE" ]]; then
      ARCHIVE="$(prompt "ARCHIVE" "codex-portable.tgz")"
    fi
    if [[ -z "$OUT_DIR" ]]; then
      OUT_DIR="$(prompt "OUT_DIR" "codex-portable")"
    fi
    if [[ ! -f "$ARCHIVE" ]]; then
      echo "Error: $ARCHIVE not found." >&2
      exit 1
    fi

    ARCHIVE_PASSWORD=""
    if [[ -t 0 ]]; then
      read -r -s -p "Archive password (leave empty if none): " ARCHIVE_PASSWORD
      echo
    else
      read -r -s -p "Archive password (leave empty if none): " ARCHIVE_PASSWORD < /dev/tty
      echo > /dev/tty
    fi

    rm -rf "$OUT_DIR"
    if [[ -n "$ARCHIVE_PASSWORD" ]]; then
      if ! command -v openssl >/dev/null 2>&1; then
        echo "Error: openssl is required for encrypted archives." >&2
        exit 1
      fi
      if command -v pv >/dev/null 2>&1; then
        pv "$ARCHIVE" \
          | openssl enc -d -aes-256-cbc -pbkdf2 -pass "pass:$ARCHIVE_PASSWORD" \
          | tar -xzf - -C "$(dirname "$OUT_DIR")"
      else
        tmp_archive="$(mktemp /tmp/codex-archive.XXXXXX.tgz)"
        openssl enc -d -aes-256-cbc -pbkdf2 \
          -pass "pass:$ARCHIVE_PASSWORD" \
          -in "$ARCHIVE" -out "$tmp_archive"
        tar -xzf "$tmp_archive" -C "$(dirname "$OUT_DIR")"
        rm -f "$tmp_archive"
      fi
    else
      if command -v pv >/dev/null 2>&1; then
        pv "$ARCHIVE" | tar -xzf - -C "$(dirname "$OUT_DIR")"
      else
        tar -xzf "$ARCHIVE" -C "$(dirname "$OUT_DIR")"
      fi
    fi

    echo "Unpacked to: $OUT_DIR"
    ;;

  *)
    usage
    exit 1
    ;;
esac
