#!/bin/bash # wxch uninstaller. Run it through the bootstrap (it asks for sudo once): # # curl -fsSL @@BASE@@/get | sh -s -- uninstall [options] # # By default it removes only what wxch itself added (agent, runner, its services and settings) and leaves # Ollama, LM Studio, the vLLM image and every downloaded model alone, so a reinstall is quick. # # --runtimes also remove Ollama, LM Studio and the vLLM image # --models also delete the downloaded model files (the ollama, lmstudio, vllm and gguf folders # inside the model folder). The disk itself, its mount and /etc/fstab are never touched. # --all both of the above (a clean slate) # -y do not ask for confirmation (or WXCH_YES=1) set -uo pipefail RUNTIMES=0; MODELS=0; YES="${WXCH_YES:-0}" for a in "$@"; do case "$a" in --runtimes) RUNTIMES=1 ;; --models) MODELS=1 ;; --all) RUNTIMES=1; MODELS=1 ;; -y|--yes) YES=1 ;; -h|--help) sed -n '2,/^set -uo/p' "$0" | sed '$d' | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "unknown option: $a (see --help)" >&2; exit 2 ;; esac done say() { printf '\033[1m[wxch]\033[0m %s\n' "$*"; } [ "$(id -u)" = 0 ] || { echo "must run as root: use the /get one-liner" >&2; exit 1; } OWNER="${WXCH_USER:-${SUDO_USER:-}}" OWNER_HOME="" [ -n "$OWNER" ] && OWNER_HOME="$(getent passwd "$OWNER" | cut -d: -f6)" MODELS_DIR="${WXCH_MODELS_DIR:-/mnt/models}" IMAGE="$(python3 -c 'import json;print(json.load(open("/etc/wxch/runner.json"))["image"])' 2>/dev/null || echo "${WXCH_VLLM_IMAGE:-nvcr.io/nvidia/vllm:26.09-py3}")" echo "this will remove:" echo " - the wxch agent, the runner, the lmstudio service and the wxch settings in /etc/wxch" echo " - any running vLLM containers started by wxch" [ "$RUNTIMES" = 1 ] && echo " - Ollama, LM Studio (${OWNER_HOME:-~}/.lmstudio) and the vLLM image $IMAGE" [ "$MODELS" = 1 ] && echo " - ALL downloaded model files under $MODELS_DIR/{ollama,lmstudio,vllm,gguf,modules}" echo "and keep: the model disk and its mount, /etc/fstab, the log /var/log/wxch-setup.log$([ "$RUNTIMES" = 0 ] && echo ', the runtimes')$([ "$MODELS" = 0 ] && echo ', the downloaded models')" if [ "$YES" != 1 ]; then if [ -r /dev/tty ]; then printf "type yes to continue: " > /dev/tty read -r answer < /dev/tty [ "$answer" = yes ] || { echo "cancelled"; exit 1; } else echo "no terminal to ask on: add -y to confirm" >&2; exit 1 fi fi say "stopping services" for u in wxch-agent wxch-runner lmstudio; do systemctl disable --now "$u" >/dev/null 2>&1 || true rm -f "/etc/systemd/system/$u.service" done rm -f /etc/systemd/system/ollama.service.d/wxch.conf /usr/local/bin/wxch-lms-start # the external disk's automount stays in fstab (never touched), but the helper that prepares the disk goes rm -f /etc/systemd/system/wxch-ext-prep@.service /usr/local/sbin/wxch-ext-prep /etc/systemd/system/*.mount.wants/wxch-ext-prep@*.service rmdir /etc/systemd/system/ollama.service.d 2>/dev/null || true systemctl daemon-reload if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then ids="$(docker ps -a -q --filter label=wxch.vllm 2>/dev/null)" [ -n "$ids" ] && { say "removing vLLM containers"; docker rm -f $ids >/dev/null 2>&1 || true; } ids="$(docker ps -a -q --filter label=wxch.module 2>/dev/null)" [ -n "$ids" ] && { say "removing AI module containers"; docker rm -f $ids >/dev/null 2>&1 || true; } fi say "removing wxch files" rm -rf /opt/wxch-agent /run/wxch rm -f /usr/local/bin/wxch-runner /usr/local/bin/wxch-agent /etc/wxch-agent.env /etc/wxch/runner.json /etc/wxch/node.json rmdir /etc/wxch 2>/dev/null || true # Ollama picks up its default store again after the drop-in is gone systemctl restart ollama >/dev/null 2>&1 || true if [ "$RUNTIMES" = 1 ]; then say "removing Ollama" systemctl disable --now ollama >/dev/null 2>&1 || true rm -f /etc/systemd/system/ollama.service /usr/local/bin/ollama rm -rf /usr/local/lib/ollama /usr/share/ollama getent passwd ollama >/dev/null && userdel ollama 2>/dev/null || true getent group ollama >/dev/null && groupdel ollama 2>/dev/null || true say "removing LM Studio" if [ -n "$OWNER_HOME" ] && [ -d "$OWNER_HOME/.lmstudio" ]; then sudo -u "$OWNER" -H "$OWNER_HOME/.lmstudio/bin/lms" daemon down >/dev/null 2>&1 || true rm -rf "$OWNER_HOME/.lmstudio" "$OWNER_HOME/.cache/lm-studio" fi systemctl daemon-reload if command -v docker >/dev/null 2>&1; then say "removing the vLLM image" docker rmi "$IMAGE" >/dev/null 2>&1 || true echo "the AI module engine image (nvcr.io/nvidia/pytorch, large) is left in place: remove it with docker rmi if no module is wanted" fi fi if [ "$MODELS" = 1 ]; then say "deleting model files" if [ -z "$MODELS_DIR" ] || [ "$MODELS_DIR" = / ] || [ ! -d "$MODELS_DIR" ]; then echo "model folder '$MODELS_DIR' not found; nothing to delete" elif [ ! -e "$MODELS_DIR/.wxch-models" ]; then echo "$MODELS_DIR has no .wxch-models marker (not created by the wxch installer): leaving it alone" >&2 else for d in ollama lmstudio vllm gguf modules; do [ -d "$MODELS_DIR/$d" ] && find "$MODELS_DIR/$d" -mindepth 1 -delete 2>/dev/null done df -h "$MODELS_DIR" | tail -1 fi fi say "done. Install again with: curl -fsSL @@BASE@@/get | WXCH_TOKEN= sh"