#!/bin/bash set -e VERSION=${VERSION:-latest} OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) # Git Bash / MSYS / Cygwin report e.g. "mingw64_nt-10.0-26200". case "$OS" in mingw*|msys*|cygwin*) OS="windows" ;; esac case "$ARCH" in x86_64) ARCH="amd64" ;; arm64|aarch64) ARCH="arm64" ;; *) echo "unsupported architecture: $ARCH" && exit 1 ;; esac # No native windows-arm64 build yet; the amd64 build runs under emulation. if [ "$OS" = "windows" ] && [ "$ARCH" = "arm64" ]; then ARCH="amd64" fi # set install dir if [ "$(id -u)" -ne 0 ] && [ -z "$INSTALL_DIR" ]; then INSTALL_DIR="$HOME/.local/bin" mkdir -p "$INSTALL_DIR" PATH_LINE='export PATH="$HOME/.local/bin:$PATH"' # A read-only or missing rc file must not abort the install. add_path() { { [ -f "$1" ] && grep -q '\.local/bin' "$1"; } && return 0 { echo "$PATH_LINE" >> "$1"; } 2>/dev/null || PATH_WARN=1 } case ":$PATH:" in *":$INSTALL_DIR:"*) ;; *) PATH_MISSING=1 ;; esac if [ "$OS" = "darwin" ]; then add_path "$HOME/.zshrc" add_path "$HOME/.bash_profile" else add_path "$HOME/.bashrc" fi export PATH="$INSTALL_DIR:$PATH" else INSTALL_DIR=${INSTALL_DIR:-/usr/local/bin} fi # resolve download url and aliases from manifest if [ "$VERSION" = "latest" ]; then MANIFEST_URL="https://dist.inference.sh/engine/manifest.json" else MANIFEST_URL="https://dist.inference.sh/engine/${VERSION}/manifest.json" fi MANIFEST=$(curl -fsSL "$MANIFEST_URL") || { echo "error: version $VERSION not found"; exit 1; } URL=$(echo "$MANIFEST" | grep -o "\"$OS-$ARCH\" *: *{[^}]*}" | grep -o 'https[^"]*' | head -n 1) || true if [ -z "$URL" ]; then echo "error: no build for $OS-$ARCH in $MANIFEST_URL" exit 1 fi # parse aliases from manifest. defaults to "engine" when not specified or # when installing a pinned older version that predates the field. if [ -n "$MANIFEST" ]; then ALIASES=$(echo "$MANIFEST" | grep -o '"aliases" *: *\[[^]]*\]' | grep -o '"[^"]*"' | grep -v '^"aliases"$' | tr -d '"' | tr '\n' ' ') fi if [ -z "$ALIASES" ]; then ALIASES="engine" fi case "$URL" in *.zip) EXT=".zip" ;; *) EXT=".tar.gz" ;; esac NAME=$(basename "$URL" "$EXT") TARBALL_NAME=$(basename "$URL") TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT echo "downloading engine $VERSION for $OS-$ARCH..." curl -fsSL "$URL" -o "$TMP/$TARBALL_NAME" # verify checksum echo "verifying checksum..." CHECKSUMS_URL=$(echo "$MANIFEST_URL" | sed 's/manifest\.json/checksums.txt/') curl -fsSL "$CHECKSUMS_URL" -o "$TMP/checksums.txt" EXPECTED=$(grep "$TARBALL_NAME" "$TMP/checksums.txt" | awk '{print $1}') if [ -z "$EXPECTED" ]; then echo "warning: no checksum found for $TARBALL_NAME, skipping verification" else if command -v sha256sum >/dev/null 2>&1; then ACTUAL=$(sha256sum "$TMP/$TARBALL_NAME" | awk '{print $1}') elif command -v shasum >/dev/null 2>&1; then ACTUAL=$(shasum -a 256 "$TMP/$TARBALL_NAME" | awk '{print $1}') else echo "warning: no sha256sum or shasum found, skipping verification" ACTUAL="$EXPECTED" fi if [ "$ACTUAL" != "$EXPECTED" ]; then echo "error: checksum mismatch!" echo " expected: $EXPECTED" echo " got: $ACTUAL" echo "the download may be corrupted or tampered with." exit 1 fi echo "checksum verified." fi # verify cosign signature on checksums (optional) DIST_BASE=$(echo "$MANIFEST_URL" | sed 's/\/manifest\.json//') if command -v cosign >/dev/null 2>&1; then echo "verifying signature..." BUNDLE="$TMP/checksums.txt.bundle" curl -fsSL "$DIST_BASE/checksums.txt.bundle" -o "$BUNDLE" 2>/dev/null && \ cosign verify-blob "$TMP/checksums.txt" \ --bundle "$BUNDLE" \ --key "$DIST_BASE/cosign.pub" && \ echo "signature verified." || \ echo "warning: signature verification failed, continuing with checksum-only." fi if [ "$EXT" = ".zip" ]; then if command -v unzip >/dev/null 2>&1; then unzip -q "$TMP/$TARBALL_NAME" -d "$TMP" elif command -v powershell.exe >/dev/null 2>&1; then powershell.exe -NoProfile -Command "Expand-Archive -Force -Path '$(cygpath -w "$TMP/$TARBALL_NAME")' -DestinationPath '$(cygpath -w "$TMP")'" else echo "error: need unzip or powershell.exe to extract $TARBALL_NAME" exit 1 fi else tar -xzf "$TMP/$TARBALL_NAME" -C "$TMP" fi if [ "$OS" = "windows" ]; then # No symlinks without elevation on Windows: install one exe per name. BIN="$TMP/$NAME.exe" [ -f "$BIN" ] || BIN=$(find "$TMP" -maxdepth 1 -name '*.exe' | head -n 1) for name in inferencesh-engine $ALIASES; do cp -f "$BIN" "$INSTALL_DIR/$name.exe" done else BIN="$TMP/$NAME" chmod +x "$BIN" # mv over the target is atomic: the binary is never missing mid-install. mv -f "$BIN" "$INSTALL_DIR/inferencesh-engine" # Recreate aliases as symlinks (relative target so the link survives moves). for alias in $ALIASES; do ln -sf inferencesh-engine "$INSTALL_DIR/$alias" done fi # ensure config directory exists CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/inferencesh" mkdir -p "$CONFIG_DIR" echo "installed to $INSTALL_DIR/inferencesh-engine" ALIAS_LIST=$(echo "$ALIASES" | tr ' ' ',' | sed 's/,$//') echo "aliases: $ALIAS_LIST" # Give the user the right next step based on what's already set up if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then echo "" echo "run 'engine serve' to start the worker daemon" else echo "run 'inferencesh-engine setup docker' to set up dependencies" fi if [ -n "$PATH_MISSING" ]; then if [ -n "$PATH_WARN" ]; then echo "note: could not update your shell profile. add this line to it yourself:" else echo "note: $INSTALL_DIR was added to your shell profile. restart your shell, or run:" fi echo " $PATH_LINE" fi