#!/usr/bin/env bash # Initial application and native runtime come from the Sparkbot website. set -euo pipefail sparkbot_destination=${SPARKBOT_INSTALL_DIR:-"$HOME/.local/share/sparkbot-app"} sparkbot_base=${SPARKBOT_DOWNLOAD_BASE:-https://sparkbot.dev} sparkbot_source=https://github.com/x00byte/DGX-Sparkbot.git if [ "$(id -u)" -eq 0 ]; then echo 'Run as your normal Spark user. Missing host packages may require your administrator password.' >&2; exit 1; fi case "$(uname -s):$(uname -m)" in Linux:aarch64|Linux:arm64) ;; *) echo 'Sparkbot requires Linux arm64 on DGX Spark.' >&2; exit 1 ;; esac sparkbot_packages=() command -v git >/dev/null || sparkbot_packages+=(git) command -v python3 >/dev/null || sparkbot_packages+=(python3 python3-venv) if command -v ldconfig >/dev/null && ! ldconfig -p 2>/dev/null | grep 'libgomp.so.1' >/dev/null; then sparkbot_packages+=(libgomp1); fi if [ "${#sparkbot_packages[@]}" -gt 0 ]; then command -v apt-get >/dev/null || { echo 'Install git, Python 3, venv and libgomp, then retry.' >&2; exit 1; } echo "Installing missing packages: ${sparkbot_packages[*]}. Administrator access is needed for these packages." sudo apt-get update sudo apt-get install -y "${sparkbot_packages[@]}" fi sparkbot_work=$(mktemp -d) trap 'rm -rf -- "$sparkbot_work"' EXIT python3 - "$sparkbot_base" "$sparkbot_work" <<'PY' import hashlib,json,re,sys,urllib.request from pathlib import Path from urllib.parse import urlsplit base=sys.argv[1].rstrip('/');root=Path(sys.argv[2]);url=urlsplit(base) if url.scheme!='https' and not (url.scheme=='http' and url.hostname in ('127.0.0.1','localhost')):raise SystemExit('Downloads require HTTPS (loopback HTTP is allowed for testing).') with urllib.request.urlopen(base+'/downloads/release.json',timeout=30) as r:data=r.read(65537) if len(data)>65536:raise SystemExit('Release manifest is too large') manifest=json.loads(data) if not re.fullmatch('[0-9a-f]{40}',manifest['commit']):raise SystemExit('Invalid release commit') for key,limit in [('application',64*1024**2),('nativeRuntime',16*1024**2)]: row=manifest[key];name=row['file'] if not re.fullmatch('[A-Za-z0-9._-]+',name) or not 0row['bytes']:raise SystemExit('Download exceeds release size') h.update(chunk);output.write(chunk) if size!=row['bytes'] or h.hexdigest()!=row['sha256']:raise SystemExit('Release checksum verification failed') (root/'release.json').write_text(json.dumps(manifest)) PY sparkbot_bundle=$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["application"]["file"])' "$sparkbot_work/release.json") sparkbot_commit=$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["commit"])' "$sparkbot_work/release.json") if [ -e "$sparkbot_destination" ]; then if [ ! -d "$sparkbot_destination/.git" ] || [ "$(git -C "$sparkbot_destination" remote get-url origin)" != "$sparkbot_source" ]; then echo "Destination already exists: $sparkbot_destination. Choose an empty SPARKBOT_INSTALL_DIR." >&2; exit 1 fi echo 'Using existing checkout. Use Settings for reviewed application updates.' else mkdir -p -- "$(dirname -- "$sparkbot_destination")" git clone --branch main -- "$sparkbot_work/$sparkbot_bundle" "$sparkbot_destination" [ "$(git -C "$sparkbot_destination" rev-parse HEAD)" = "$sparkbot_commit" ] || { echo 'Application commit verification failed.' >&2; exit 1; } git -C "$sparkbot_destination" remote set-url origin "$sparkbot_source" fi bash "$sparkbot_destination/install.sh" --no-start "$sparkbot_destination/.venv/bin/python" - "$sparkbot_destination" "$sparkbot_work" <<'PY' import json,sys from pathlib import Path sys.path.insert(0,sys.argv[1]) from sparkbot.store import Store from sparkbot.config import DATA from sparkbot.native_cpu import NativeCPU class Install: def progress(self,*args):print(args[-1],flush=True) def check(self):pass work=Path(sys.argv[2]);manifest=json.loads((work/'release.json').read_text()) NativeCPU(Store(DATA)).install(Install(),work/manifest['nativeRuntime']['file']) PY if [ "${1:-}" != '--no-start' ]; then cd "$sparkbot_destination" ./manage.py service || true ./manage.py start echo 'Open http://:10005 and follow the setup link to set your password.' fi