#!/bin/sh
# What is in this machine, and what Kracht could do with it.
#
# Run it on the box you are asking about:
#
#     sh scripts/diagnose.sh
#
# It prints what it finds and exits. **It sends nothing anywhere.** There is no network call in
# this file, no telemetry, no identifier: the whole point is that somebody deciding whether to
# list a machine can find out what it has without first trusting us with it. If you are reading
# this because you are about to pipe it to a shell, that paragraph is the one to check, and it is
# checkable -- there is no `curl`, `wget` or `nc` below.
#
# POSIX sh on purpose. A provider box is as likely to be a Debian container as a Mac, and bash is
# not guaranteed on either.

set -eu

say() { printf '%s\n' "$*"; }
rule() { say "------------------------------------------------------------"; }
have() { command -v "$1" >/dev/null 2>&1; }

# A carriage return, built rather than typed. See the Windows branch below for why.
CR=$(printf '\r')

say ""
say "Kracht hardware check"
rule

# ---------------------------------------------------------------- what kind of machine this is
OS="$(uname -s 2>/dev/null || echo unknown)"
ARCH="$(uname -m 2>/dev/null || echo unknown)"
say "System      : $OS $ARCH"

# CPU and memory, best effort per platform. A missing figure prints as unknown rather than as a
# zero: nobody should read "0 GB of RAM" and wonder whether the machine is broken.
CPU="unknown"
RAM="unknown"
case "$OS" in
    Linux)
        if [ -r /proc/cpuinfo ]; then
            CPU="$(awk -F': ' '/model name/ {print $2; exit}' /proc/cpuinfo 2>/dev/null || echo unknown)"
        fi
        if [ -r /proc/meminfo ]; then
            RAM="$(awk '/MemTotal/ {printf "%.0f GB", $2/1048576; exit}' /proc/meminfo 2>/dev/null || echo unknown)"
        fi
        ;;
    Darwin)
        CPU="$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo unknown)"
        RAM="$(sysctl -n hw.memsize 2>/dev/null | awk '{printf "%.0f GB", $1/1073741824}' || echo unknown)"
        ;;
    MINGW*|MSYS*|CYGWIN*)
        # Git Bash and friends. Found by running this script on one: it reported the GPU
        # correctly and then said unknown for both CPU and memory, which reads like a broken
        # script rather than an unhandled platform.
        #
        # PowerShell rather than `wmic`, because `wmic` is absent on current Windows -- checked
        # on the machine that surfaced this, where it is gone and PowerShell is present.
        #
        # PowerShell writes CRLF, and the trailing return has to come off or it lands mid-line
        # in the output. CR is built once at the top with printf, because a literal backslash-r
        # did not survive being written through to this file, and failed silently rather than
        # loudly: the readings came out empty, not wrong.
        if have powershell; then
            CPU="$(powershell -NoProfile -Command "(Get-CimInstance Win32_Processor).Name" 2>/dev/null | tr -d "$CR" | sed 's/  *$//' || true)"
            RAM="$(powershell -NoProfile -Command "[math]::Round((Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory/1GB)" 2>/dev/null | tr -d "$CR " || true)"
            [ -n "$RAM" ] && RAM="$RAM GB"
        fi
        ;;
esac
[ -n "${CPU:-}" ] || CPU="unknown"
[ -n "${RAM:-}" ] || RAM="unknown"
say "CPU         : $CPU"
say "Memory      : $RAM"
rule

# ---------------------------------------------------------------------------------- the GPUs
# The order matters. `nvidia-smi` is authoritative where it exists, `rocm-smi` likewise for AMD,
# and the sysfs and lspci paths below are fallbacks that identify a card without claiming to know
# its VRAM. Guessing VRAM from a model name is how a listing ends up advertising memory the card
# does not have, so an unknown figure stays unknown.
FOUND=0

if have nvidia-smi; then
    say "GPUs (nvidia-smi)"
    # `--format=csv,noheader` keeps this parseable without pulling in a JSON tool.
    nvidia-smi --query-gpu=index,name,memory.total,driver_version \
               --format=csv,noheader,nounits 2>/dev/null \
        | while IFS=, read -r idx name mem drv; do
            printf '  [%s] %s\n' "$(echo "$idx" | tr -d ' ')" "$(echo "$name" | sed 's/^ *//')"
            printf '      VRAM   : %s GB\n' "$(echo "$mem" | awk '{printf "%.0f", $1/1024}')"
            printf '      Driver : %s\n' "$(echo "$drv" | sed 's/^ *//')"
        done
    FOUND=1
fi

if [ "$FOUND" -eq 0 ] && have rocm-smi; then
    say "GPUs (rocm-smi)"
    rocm-smi --showproductname --showmeminfo vram 2>/dev/null | sed 's/^/  /'
    FOUND=1
fi

if [ "$FOUND" -eq 0 ] && [ "$OS" = "Darwin" ]; then
    say "GPU (Apple)"
    system_profiler SPDisplaysDataType 2>/dev/null \
        | awk -F': ' '/Chipset Model/ {print "  " $2} /Total Number of Cores/ {print "      Cores  : " $2}'
    # Apple silicon shares one pool between CPU and GPU, so quoting a VRAM figure would be
    # inventing a split that does not exist.
    say "      VRAM   : unified with system memory"
    FOUND=1
fi

if [ "$FOUND" -eq 0 ] && have lspci; then
    GPUS="$(lspci 2>/dev/null | grep -Ei 'vga|3d controller|display' || true)"
    if [ -n "$GPUS" ]; then
        say "GPUs (lspci, no driver tools found)"
        printf '%s\n' "$GPUS" | sed 's/^/  /'
        say "      VRAM   : unknown without a driver tool"
        FOUND=1
    fi
fi

if [ "$FOUND" -eq 0 ]; then
    say "GPUs        : none detected"
    say ""
    say "  No NVIDIA, AMD or Apple GPU tooling answered. That usually means one of:"
    say "    - the machine has no discrete GPU"
    say "    - the driver is installed but not on this PATH"
    say "    - this is a container without the GPU passed through"
fi

rule
say ""
say "Nothing above was sent anywhere. This script makes no network calls."
say ""
say "To list a machine on Kracht, or to see which models a card can serve:"
say "  https://kracht.ai/cookbook"
say ""
