Ctrl K
ComunidadGuías…

website/install.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/sh
# Moltis installer script
# https://www.moltis.org/
#
# Usage:
#   curl -fsSL https://www.moltis.org/install.sh | sh
#
# Or with options:
#   curl -fsSL https://www.moltis.org/install.sh | sh -s -- --no-homebrew
#   curl -fsSL https://www.moltis.org/install.sh | sh -s -- --method=binary
#   curl -fsSL https://www.moltis.org/install.sh | sh -s -- --version=0.1.3

set -e

GITHUB_REPO="moltis-org/moltis"
HOMEBREW_TAP="moltis-org/tap"
BINARY_NAME="moltis"
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"

# Default options
USE_HOMEBREW=true
PREFERRED_METHOD=""
VERSION=""

# Colors (disabled if not a terminal)
if [ -t 1 ]; then
    RED='\033[0;31m'
    GREEN='\033[0;32m'
    YELLOW='\033[0;33m'
    BLUE='\033[0;34m'
    BOLD='\033[1m'
    NC='\033[0m'
else
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    BOLD=''
    NC=''
fi

info() {
    printf "${BLUE}==>${NC} ${BOLD}%s${NC}\n" "$1"
}

success() {
    printf "${GREEN}==>${NC} ${BOLD}%s${NC}\n" "$1"
}

warn() {
    printf "${YELLOW}Warning:${NC} %s\n" "$1" >&2
}

error() {
    printf "${RED}Error:${NC} %s\n" "$1" >&2
    exit 1
}

# Parse arguments
while [ $# -gt 0 ]; do
    case "$1" in
        --no-homebrew)
            USE_HOMEBREW=false
            ;;
        --method=*)
            PREFERRED_METHOD="${1#*=}"
            ;;
        --version=*)
            VERSION="${1#*=}"
            ;;
        -h|--help)
            cat <<EOF
Moltis installer

Usage:
    install.sh [OPTIONS]

Options:
    --no-homebrew       Skip Homebrew even if available (macOS)
    --method=METHOD     Force installation method: homebrew, binary, deb, rpm, arch, appimage, snap, source, proxmox
    --version=VERSION   Install a specific version (default: latest)
    -h, --help          Show this help message

Environment variables:
    INSTALL_DIR         Binary installation directory (default: ~/.local/bin)

Examples:
    curl -fsSL https://www.moltis.org/install.sh | sh
    curl -fsSL https://www.moltis.org/install.sh | sh -s -- --method=binary
    curl -fsSL https://www.moltis.org/install.sh | sh -s -- --version=0.1.3
EOF
            exit 0
            ;;
        *)
            warn "Unknown option: $1"
            ;;
    esac
    shift
done

detect_os() {
    OS="$(uname -s)"
    case "$OS" in
        Darwin)
            echo "macos"
            ;;
        Linux)
            echo "linux"
            ;;
        MINGW*|MSYS*|CYGWIN*)
            echo "windows"
            ;;
        *)
            echo "unknown"
            ;;
    esac
}

detect_arch() {
    ARCH="$(uname -m)"
    case "$ARCH" in
        x86_64|amd64)
            echo "x86_64"
            ;;
        aarch64|arm64)
            echo "aarch64"
            ;;
        armv7l)
            echo "armv7"
            ;;
        i386|i686)
            echo "i686"
            ;;
        *)
            echo "$ARCH"
            ;;
    esac
}

detect_linux_distro() {
    if [ -f /etc/os-release ]; then
        # shellcheck disable=SC1091
        . /etc/os-release
        echo "$ID"
    elif [ -f /etc/debian_version ]; then
        echo "debian"
    elif [ -f /etc/redhat-release ]; then
        echo "rhel"
    elif [ -f /etc/arch-release ]; then
        echo "arch"
    else
        echo "unknown"
    fi
}

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

get_latest_version() {
    # Extract tag_name, stripping optional leading "v" prefix.
    if command_exists curl; then
        curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"v?([^"]+)".*/\1/'
    elif command_exists wget; then
        wget -qO- "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"v?([^"]+)".*/\1/'
    else
        error "Neither curl nor wget found. Please install one of them."
    fi
}

# Return the GitHub release tag for a given version.
# Date-based versions (YYYYMMDD.NN) are bare tags; semver gets a "v" prefix.
release_tag() {
    v="$1"
    case "$v" in
        [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*)
            echo "$v" ;;
        *)
            echo "v$v" ;;
    esac
}

download() {
    url="$1"
    dest="$2"
    if command_exists curl; then
        curl -fsSL "$url" -o "$dest"
    elif command_exists wget; then
        wget -q "$url" -O "$dest"
    else
        error "Neither curl nor wget found. Please install one of them."
    fi
}

verify_checksum() {
    file="$1"
    expected_sha256="$2"

    if command_exists sha256sum; then
        actual=$(sha256sum "$file" | cut -d' ' -f1)
    elif command_exists shasum; then
        actual=$(shasum -a 256 "$file" | cut -d' ' -f1)
    else
        warn "Cannot verify checksum (sha256sum/shasum not found)"
        return 0
    fi

    if [ "$actual" != "$expected_sha256" ]; then
        error "Checksum verification failed!\nExpected: $expected_sha256\nActual: $actual"
    fi
}

ensure_install_dir() {
    if [ ! -d "$INSTALL_DIR" ]; then
        mkdir -p "$INSTALL_DIR"
    fi
}

install_shared_assets() {
    source_dir="$1"
    if [ ! -d "$source_dir" ]; then
        return 0
    fi

    share_dir="$HOME/.moltis/share"
    mkdir -p "$share_dir"
    cp -R "$source_dir"/. "$share_dir"/
    info "Installed shared assets to $share_dir"
}

add_to_path_instructions() {
    shell_name=$(basename "$SHELL")
    case "$shell_name" in
        bash)
            rc_file="$HOME/.bashrc"
            ;;
        zsh)
            rc_file="$HOME/.zshrc"
            ;;
        fish)
            rc_file="$HOME/.config/fish/config.fish"
            ;;
        *)
            rc_file="$HOME/.profile"
            ;;
    esac

    # Check if already in PATH
    case ":$PATH:" in
        *":$INSTALL_DIR:"*)
            return
            ;;
    esac

    printf "\n"
    warn "$INSTALL_DIR is not in your PATH."
    printf "Add it by running:\n\n"
    if [ "$shell_name" = "fish" ]; then
        printf "  ${BOLD}fish_add_path %s${NC}\n\n" "$INSTALL_DIR"
    else
        printf "  ${BOLD}echo 'export PATH=\"%s:\$PATH\"' >> %s${NC}\n\n" "$INSTALL_DIR" "$rc_file"
    fi
    printf "Then restart your shell or run:\n"
    printf "  ${BOLD}source %s${NC}\n" "$rc_file"
}

# Installation methods

install_homebrew() {
    info "Installing via Homebrew..."
    if ! command_exists brew; then
        error "Homebrew not found. Install it from https://brew.sh/"
    fi
    brew tap "$HOMEBREW_TAP" 2>/dev/null || true
    brew install moltis
    success "Moltis installed via Homebrew"
}

install_binary() {
    os="$1"
    arch="$2"
    version="$3"

    # Determine target triple
    case "$os" in
        macos)
            target="${arch}-apple-darwin"
            ;;
        linux)
            target="${arch}-unknown-linux-gnu"
            ;;
        *)
            error "Unsupported OS for binary installation: $os"
            ;;
    esac

    tag=$(release_tag "$version")
    tarball="${BINARY_NAME}-${version}-${target}.tar.gz"
    url="https://github.com/${GITHUB_REPO}/releases/download/${tag}/${tarball}"
    checksum_url="${url}.sha256"

    info "Downloading ${BINARY_NAME} v${version} for ${target}..."

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    download "$url" "$tmpdir/$tarball" || error "Failed to download $tarball. Check if a release exists for your platform."

    # Verify checksum
    if download "$checksum_url" "$tmpdir/checksum.sha256" 2>/dev/null; then
        expected_sha=$(cut -d' ' -f1 "$tmpdir/checksum.sha256")
        verify_checksum "$tmpdir/$tarball" "$expected_sha"
        info "Checksum verified"
    else
        warn "Could not download checksum file, skipping verification"
    fi

    # Extract and install
    tar -xzf "$tmpdir/$tarball" -C "$tmpdir"

    ensure_install_dir
    mv "$tmpdir/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
    chmod +x "$INSTALL_DIR/$BINARY_NAME"

    if [ -d "$tmpdir/share/moltis" ]; then
        install_shared_assets "$tmpdir/share/moltis"
    elif [ -d "$tmpdir/share/web" ] && [ -d "$tmpdir/share/wasm" ]; then
        install_shared_assets "$tmpdir/share"
    fi

    success "Moltis installed to $INSTALL_DIR/$BINARY_NAME"
    add_to_path_instructions
}

install_deb() {
    arch="$1"
    version="$2"

    case "$arch" in
        x86_64) deb_arch="amd64" ;;
        aarch64) deb_arch="arm64" ;;
        *) error "Unsupported architecture for .deb: $arch" ;;
    esac

    # Package naming: moltis_VERSION_ARCH.deb (cargo-deb with --deb-version, no revision)
    tag=$(release_tag "$version")
    deb_file="moltis_${version}_${deb_arch}.deb"
    url="https://github.com/${GITHUB_REPO}/releases/download/${tag}/${deb_file}"

    info "Downloading ${deb_file}..."

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    download "$url" "$tmpdir/$deb_file" || error "Failed to download $deb_file"

    info "Installing .deb package (requires sudo)..."
    sudo dpkg -i "$tmpdir/$deb_file" || sudo apt-get install -f -y

    success "Moltis installed via .deb package"
}

install_rpm() {
    arch="$1"
    version="$2"

    case "$arch" in
        x86_64) rpm_arch="x86_64" ;;
        aarch64) rpm_arch="aarch64" ;;
        *) error "Unsupported architecture for .rpm: $arch" ;;
    esac

    # Package naming: moltis-VERSION-1.ARCH.rpm
    tag=$(release_tag "$version")
    rpm_file="moltis-${version}-1.${rpm_arch}.rpm"
    url="https://github.com/${GITHUB_REPO}/releases/download/${tag}/${rpm_file}"

    info "Downloading ${rpm_file}..."

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    download "$url" "$tmpdir/$rpm_file" || error "Failed to download $rpm_file"

    info "Installing .rpm package (requires sudo)..."
    if command_exists dnf; then
        sudo dnf install -y "$tmpdir/$rpm_file"
    elif command_exists yum; then
        sudo yum install -y "$tmpdir/$rpm_file"
    else
        sudo rpm -i "$tmpdir/$rpm_file"
    fi

    success "Moltis installed via .rpm package"
}

install_arch() {
    arch="$1"
    version="$2"

    tag=$(release_tag "$version")
    pkg_file="moltis-${version}-1-${arch}.pkg.tar.zst"
    url="https://github.com/${GITHUB_REPO}/releases/download/${tag}/${pkg_file}"

    info "Downloading ${pkg_file}..."

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    download "$url" "$tmpdir/$pkg_file" || error "Failed to download $pkg_file"

    info "Installing Arch package (requires sudo)..."
    sudo pacman -U --noconfirm "$tmpdir/$pkg_file"

    success "Moltis installed via Arch package"
}

install_appimage() {
    os="$1"
    arch="$2"
    version="$3"

    if [ "$os" != "linux" ]; then
        error "AppImage installation is only supported on Linux"
    fi

    case "$arch" in
        x86_64|aarch64)
            ;;
        *)
            error "Unsupported architecture for AppImage: $arch"
            ;;
    esac

    tag=$(release_tag "$version")
    appimage_file="moltis-${version}-${arch}.AppImage"
    url="https://github.com/${GITHUB_REPO}/releases/download/${tag}/${appimage_file}"
    checksum_url="${url}.sha256"

    info "Downloading ${appimage_file}..."

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    download "$url" "$tmpdir/$appimage_file" || error "Failed to download $appimage_file"

    if download "$checksum_url" "$tmpdir/checksum.sha256" 2>/dev/null; then
        expected_sha=$(cut -d' ' -f1 "$tmpdir/checksum.sha256")
        verify_checksum "$tmpdir/$appimage_file" "$expected_sha"
        info "Checksum verified"
    else
        warn "Could not download checksum file, skipping verification"
    fi

    ensure_install_dir
    mv "$tmpdir/$appimage_file" "$INSTALL_DIR/$BINARY_NAME"
    chmod +x "$INSTALL_DIR/$BINARY_NAME"

    success "Moltis AppImage installed to $INSTALL_DIR/$BINARY_NAME"
    add_to_path_instructions
}

install_snap() {
    info "Installing via Snap..."

    if ! command_exists snap; then
        error "Snap not found. Install it first: https://snapcraft.io/docs/installing-snapd"
    fi

    sudo snap install moltis

    success "Moltis installed via Snap"
}

detect_proxmox() {
    command_exists pveversion && [ -d /etc/pve ]
}

install_proxmox() {
    info "Proxmox VE detected — installing Moltis as an LXC container..."

    if ! detect_proxmox; then
        error "This does not appear to be a Proxmox VE host."
    fi

    PROXMOX_REPO_URL="https://raw.githubusercontent.com/moltis-org/ProxmoxVED/feat/moltis"
    PROXMOX_SCRIPT_URL="$PROXMOX_REPO_URL/ct/moltis.sh"

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT
    proxmox_script="$tmpdir/moltis-proxmox.sh"
    patched_script="$tmpdir/moltis-proxmox-patched.sh"

    download "$PROXMOX_SCRIPT_URL" "$proxmox_script" || error "Failed to download Proxmox helper"

    # Patch the fetched helper at launch time for Moltis-specific fixes that live
    # in remote community-scripts files: keep /usr/bin/update on the Moltis fork,
    # make the Docker prompt safe when lxc-attach has no interactive stdin, and
    # keep optional CA certificate display failures from removing the container.
    awk -v repo_url="$PROXMOX_REPO_URL" '
        {
            if ($0 ~ /^source <\(curl -fsSL / && !curl_patched) {
                print "curl() {"
                print "  case \"${*: -1}\" in"
                print "    */install/moltis-install.sh)"
                print "      command curl \"$@\" | sed '\''s|^[[:space:]]*read -r -p \".*Docker for sandbox support.*\" prompt$|if [[ -t 0 ]]; then &; else prompt=\"${MOLTIS_INSTALL_DOCKER:-no}\"; fi|'\''"
                print "      ;;"
                print "    *) command curl \"$@\" ;;"
                print "  esac"
                print "}"
                curl_patched = 1
            }
            if ($0 == "CA_CERT=$(pct exec \"$CTID\" -- cat /etc/moltis/certs/ca.pem 2>/dev/null)" && !ca_cert_patched) {
                sub(/\)$/, " || true)")
                ca_cert_patched = 1
            }
            print
            if ($0 == "description" && !patched) {
                q = sprintf("%c", 39)
                print "pct exec \"$CTID\" -- bash -c \"cat > /usr/bin/update <<" q "MOLTIS_UPDATE_EOF" q
                print "bash -c \\\"\\$(curl -fsSL " repo_url "/ct/moltis.sh)\\\""
                print "MOLTIS_UPDATE_EOF"
                print "chmod +x /usr/bin/update\" || true"
                patched = 1
            }
        }
    ' "$proxmox_script" >"$patched_script"

    grep -q 'curl()' "$patched_script" || error "Failed to apply Docker prompt patch to Proxmox helper"
    grep -q 'MOLTIS_UPDATE_EOF' "$patched_script" || error "Failed to apply update URL patch to Proxmox helper"
    grep -q 'cat /etc/moltis/certs/ca.pem 2>/dev/null || true' "$patched_script" || warn "Could not apply optional CA certificate patch to Proxmox helper"

    info "Launching Proxmox VE helper script..."
    bash "$patched_script"
}

install_from_source() {
    warn "Building from source. This may take several minutes..."

    if ! command_exists cargo; then
        info "Rust not found. Installing via rustup..."
        if command_exists curl; then
            curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
        else
            wget -qO- https://sh.rustup.rs | sh -s -- -y
        fi
        # shellcheck disable=SC1091
        . "$HOME/.cargo/env"
    fi

    if ! command_exists git; then
        error "Git is required to build from source. Please install it first."
    fi

    version="$1"

    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT

    info "Cloning repository..."
    tag=$(release_tag "$version")
    git clone --depth 1 --branch "$tag" "https://github.com/${GITHUB_REPO}.git" "$tmpdir/moltis"

    cd "$tmpdir/moltis"

    info "Building WASM tool components..."
    rustup target add wasm32-wasip2
    cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release

    info "Building release binary..."
    cargo build --release

    ensure_install_dir
    cp "target/release/$BINARY_NAME" "$INSTALL_DIR/$BINARY_NAME"
    chmod +x "$INSTALL_DIR/$BINARY_NAME"

    SHARE_STAGING="$tmpdir/moltis-share"
    mkdir -p "$SHARE_STAGING/web" "$SHARE_STAGING/wasm"
    cp -R "crates/web/src/assets/." "$SHARE_STAGING/web/"
    cp "target/wasm32-wasip2/release/moltis_wasm_calc.wasm" "$SHARE_STAGING/wasm/"
    cp "target/wasm32-wasip2/release/moltis_wasm_web_fetch.wasm" "$SHARE_STAGING/wasm/"
    cp "target/wasm32-wasip2/release/moltis_wasm_web_search.wasm" "$SHARE_STAGING/wasm/"
    install_shared_assets "$SHARE_STAGING"

    success "Moltis built and installed to $INSTALL_DIR/$BINARY_NAME"
    add_to_path_instructions
}

# Main installation logic

main() {
    printf "\n"
    printf "  ${BOLD}Moltis Installer${NC}\n"
    printf "  Personal AI gateway - one binary, multiple LLM providers\n"
    printf "\n"

    OS=$(detect_os)
    ARCH=$(detect_arch)

    info "Detected: $OS ($ARCH)"

    if [ "$OS" = "windows" ]; then
        error "Windows is not supported by this installer. Please download the binary manually from:\nhttps://github.com/${GITHUB_REPO}/releases"
    fi

    if [ "$OS" = "unknown" ]; then
        error "Unsupported operating system: $(uname -s)"
    fi

    # Get version
    if [ -z "$VERSION" ]; then
        info "Fetching latest version..."
        VERSION=$(get_latest_version)
        if [ -z "$VERSION" ]; then
            error "Failed to determine latest version"
        fi
    fi
    info "Version: $VERSION"

    # Determine installation method
    if [ -n "$PREFERRED_METHOD" ]; then
        case "$PREFERRED_METHOD" in
            homebrew)
                install_homebrew
                ;;
            binary)
                install_binary "$OS" "$ARCH" "$VERSION"
                ;;
            deb)
                install_deb "$ARCH" "$VERSION"
                ;;
            rpm)
                install_rpm "$ARCH" "$VERSION"
                ;;
            arch)
                install_arch "$ARCH" "$VERSION"
                ;;
            appimage)
                install_appimage "$OS" "$ARCH" "$VERSION"
                ;;
            snap)
                install_snap
                ;;
            proxmox)
                install_proxmox
                return
                ;;
            source)
                install_from_source "$VERSION"
                ;;
            *)
                error "Unknown installation method: $PREFERRED_METHOD"
                ;;
        esac
    elif [ "$OS" = "linux" ] && detect_proxmox; then
        info "Proxmox VE host detected"
        printf "  Install as an LXC container (recommended) or directly on this host?\n"
        printf "  ${BOLD}1)${NC} LXC container (isolated, easy to manage)\n"
        printf "  ${BOLD}2)${NC} Install directly on this host\n"
        printf "\n"
        printf "  Choice [1]: "
        read -r choice </dev/tty 2>/dev/null || choice="1"
        choice="${choice:-1}"
        if [ "$choice" = "1" ]; then
            install_proxmox
            return
        fi
        # Fall through to normal Linux install
        DISTRO=$(detect_linux_distro)
        case "$DISTRO" in
            ubuntu|debian|linuxmint|pop|elementary|zorin)
                install_deb "$ARCH" "$VERSION" ;;
            fedora|rhel|centos|rocky|alma|ol)
                install_rpm "$ARCH" "$VERSION" ;;
            *)
                install_binary "$OS" "$ARCH" "$VERSION" ;;
        esac
    elif [ "$OS" = "macos" ]; then
        # macOS: prefer Homebrew, fall back to binary
        if [ "$USE_HOMEBREW" = true ] && command_exists brew; then
            install_homebrew
        else
            install_binary "$OS" "$ARCH" "$VERSION"
        fi
    elif [ "$OS" = "linux" ]; then
        # Linux: detect distro and use appropriate package manager
        DISTRO=$(detect_linux_distro)

        case "$DISTRO" in
            ubuntu|debian|linuxmint|pop|elementary|zorin)
                if command_exists apt-get; then
                    install_deb "$ARCH" "$VERSION"
                else
                    install_binary "$OS" "$ARCH" "$VERSION"
                fi
                ;;
            fedora|rhel|centos|rocky|alma|ol)
                if command_exists dnf || command_exists yum; then
                    install_rpm "$ARCH" "$VERSION"
                else
                    install_binary "$OS" "$ARCH" "$VERSION"
                fi
                ;;
            arch|manjaro|endeavouros|garuda)
                if command_exists pacman; then
                    install_arch "$ARCH" "$VERSION"
                else
                    install_binary "$OS" "$ARCH" "$VERSION"
                fi
                ;;
            *)
                # Unknown distro: try binary, offer source as fallback
                if [ "$ARCH" = "x86_64" ] || [ "$ARCH" = "aarch64" ]; then
                    install_binary "$OS" "$ARCH" "$VERSION"
                else
                    warn "No pre-built binary available for $ARCH architecture."
                    info "Falling back to building from source..."
                    install_from_source "$VERSION"
                fi
                ;;
        esac
    fi

    # Verify installation
    if command_exists "$BINARY_NAME"; then
        installed_version=$("$BINARY_NAME" --version 2>/dev/null | head -1 || echo "unknown")
        printf "\n"
        success "Installation complete!"
        printf "  ${BOLD}%s${NC}\n" "$installed_version"
        printf "\n"
        printf "Get started:\n"
        printf "  ${BOLD}moltis${NC}          # Start the gateway\n"
        printf "  ${BOLD}moltis --help${NC}   # Show help\n"
        printf "\n"
        printf "Documentation: ${BLUE}https://www.moltis.org/${NC}\n"
    elif [ -x "$INSTALL_DIR/$BINARY_NAME" ]; then
        printf "\n"
        success "Installation complete!"
        printf "\n"
        add_to_path_instructions
    fi
}

main