export SNO_INSTALL_ENTRY='unknown' #!/bin/sh # Filled from uploaded GitHub assets by scripts/fill-installer-release-data.sh. # BEGIN SNO RELEASE DATA SNO_RELEASE_VERSION='1.1.13' SNO_X86_64_LINUX_URL='https://github.com/sno-ai/sno-cli/releases/download/v1.1.13/sno-x86_64-unknown-linux-gnu.tar.gz' SNO_X86_64_LINUX_DIGEST='sha256:582f70f048e2ab09cbca3bc665d12f42f0c58e97386fc02385ffad3499096a63' SNO_AARCH64_LINUX_URL='https://github.com/sno-ai/sno-cli/releases/download/v1.1.13/sno-aarch64-unknown-linux-gnu.tar.gz' SNO_AARCH64_LINUX_DIGEST='sha256:d17d56d997d586cfda5e94d87f77b33269a949418f0ab24d667c4458e37247a1' SNO_X86_64_MACOS_URL='https://github.com/sno-ai/sno-cli/releases/download/v1.1.13/sno-x86_64-apple-darwin.tar.gz' SNO_X86_64_MACOS_DIGEST='sha256:cf88314528ea60dbff9161d13337cf35d5dd552ed080b008046ad785fa37dbef' SNO_AARCH64_MACOS_URL='https://github.com/sno-ai/sno-cli/releases/download/v1.1.13/sno-aarch64-apple-darwin.tar.gz' SNO_AARCH64_MACOS_DIGEST='sha256:3e37328ec92cf292aa5b3fde8ee571fb2ef92a8ed6669c0dc42b80d2bc8fb024' # END SNO RELEASE DATA fail() { printf 'Sno installation failed: %s\n' "$1" >&2 exit 1 } update_only=false case "$#:$1" in 0:) ;; 1:--update-only) update_only=true ;; 1:--help|1:-h) printf 'Usage: sh sno-installer.sh [--update-only]\n'; exit 0 ;; *) fail "unknown option: $*. Run the installer with no option, or with --update-only." ;; esac case "$(uname -s):$(uname -m)" in Linux:x86_64) target=x86_64-unknown-linux-gnu; platform='Linux (x86-64)'; url=$SNO_X86_64_LINUX_URL; digest=$SNO_X86_64_LINUX_DIGEST ;; Linux:aarch64|Linux:arm64) target=aarch64-unknown-linux-gnu; platform='Linux (ARM64)'; url=$SNO_AARCH64_LINUX_URL; digest=$SNO_AARCH64_LINUX_DIGEST ;; Darwin:x86_64) target=x86_64-apple-darwin; platform='macOS (Intel)'; url=$SNO_X86_64_MACOS_URL; digest=$SNO_X86_64_MACOS_DIGEST ;; Darwin:arm64|Darwin:aarch64) target=aarch64-apple-darwin; platform='macOS (Apple silicon)'; url=$SNO_AARCH64_MACOS_URL; digest=$SNO_AARCH64_MACOS_DIGEST ;; *) fail 'Sno does not run on this computer. It runs on Linux (x86-64 or ARM64), macOS (Intel or Apple silicon) and WSL2 on Windows.' ;; esac if [ -z "$url" ] || [ -z "$digest" ] || [ -z "$SNO_RELEASE_VERSION" ]; then fail 'this copy of the installer is incomplete. Get the install command from https://sno.ai and run it again.' fi bin_dir=$HOME/.local/bin kept='Your current sno, if you have one, was not changed.' binary=$bin_dir/sno mkdir -p "$bin_dir" || fail "cannot create $bin_dir. Check that your home folder is writable, then run the install again. $kept" work_dir=$(mktemp -d "$bin_dir/.sno-install.XXXXXX") || fail "cannot write to $bin_dir. Check that your home folder is writable, then run the install again. $kept" trap 'rm -rf "$work_dir"' 0 trap 'exit 1' 1 2 15 printf 'Downloading Sno %s for %s\n' "$SNO_RELEASE_VERSION" "$platform" >&2 curl -fsSL "$url" -o "$work_dir/archive.tar.gz" || fail "could not download Sno. Check your internet connection, then run the same install command again. $kept" if command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$work_dir/archive.tar.gz") || fail "cannot check the downloaded file. Run the install command again. $kept" else actual=$(shasum -a 256 "$work_dir/archive.tar.gz") || fail "cannot check the downloaded file. Run the install command again. $kept" fi [ "sha256:${actual%% *}" = "$digest" ] || fail "the download was damaged or incomplete. Run the same install command again. $kept" tar -xzf "$work_dir/archive.tar.gz" -C "$work_dir" || fail "cannot open the downloaded file. Run the install command again. $kept" candidate=$work_dir/sno-$target/sno chmod +x "$candidate" || fail "the downloaded sno cannot be made runnable. Run the install command again. $kept" reported_version=$("$candidate" --version) || fail "the downloaded sno cannot run on this computer. Run the install command again. $kept" [ "$reported_version" = "sno $SNO_RELEASE_VERSION" ] || fail "the download is the wrong version (got $reported_version, expected sno $SNO_RELEASE_VERSION). Run the install command again in a few minutes. $kept" if [ -d "$binary" ]; then fail "$binary is a folder, not a file. Move it away, then run the install again." fi mv -f "$candidate" "$binary" || fail "cannot replace $binary. Check that you can write to it, then run the install again. $kept" partial=false told=' and up to date. Nothing else to do.' if [ "$update_only" = false ]; then next_step="\"$binary\" setup" told='. Next I run setup, which connects Sno to your agents.' repair_profile() { profile=$1 profile_tmp=$(mktemp "${profile}.sno.XXXXXX") || { printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$profile" "$binary" >&2; partial=true; return; } if [ -e "$profile" ]; then if ! cp -p "$profile" "$profile_tmp" || ! awk '/^# BEGIN SNO PATH$/{skip=1; next} /^# END SNO PATH$/{skip=0; next} !skip{print}' "$profile" > "$profile_tmp"; then printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$profile" "$binary" >&2 rm -f "$profile_tmp" partial=true return fi fi if ! cat >> "$profile_tmp" <<'EOF' # BEGIN SNO PATH case "$PATH" in "$HOME/.local/bin"|"$HOME/.local/bin":*) ;; *) export PATH="$HOME/.local/bin:$PATH" ;; esac # END SNO PATH EOF then printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$profile" "$binary" >&2 rm -f "$profile_tmp" partial=true return fi # A symlinked startup file (dotfile managers) is written through, so the link and its target stay. if [ -L "$profile" ]; then if cat "$profile_tmp" > "$profile"; then rm -f "$profile_tmp" else printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$profile" "$binary" >&2 rm -f "$profile_tmp" partial=true fi elif ! mv -f "$profile_tmp" "$profile"; then printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$profile" "$binary" >&2 rm -f "$profile_tmp" partial=true fi } case "${SHELL##*/}" in zsh) zsh_dir=${ZDOTDIR:-$HOME} repair_profile "$zsh_dir/.zprofile" repair_profile "$zsh_dir/.zshrc" ;; bash) if [ -e "$HOME/.bash_profile" ]; then login_profile=$HOME/.bash_profile elif [ -e "$HOME/.bash_login" ]; then login_profile=$HOME/.bash_login else login_profile=$HOME/.profile fi repair_profile "$login_profile" repair_profile "$HOME/.bashrc" ;; sh|dash|ksh|'') repair_profile "$HOME/.profile" ;; fish) fish_conf=${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d if ! { mkdir -p "$fish_conf" && printf 'fish_add_path -g "$HOME/.local/bin"\n' > "$fish_conf/sno.fish"; }; then printf 'Could not add the sno command to %s, so new terminals may not find it. Sno is installed: run it as %s\n' "$fish_conf/sno.fish" "$binary" >&2 partial=true fi ;; # The binary is installed and usable by its absolute path; only the PATH line is left to the user. *) printf 'Sno cannot set up your shell (%s) by itself. Run Sno by its full path: %s\n' "$SHELL" "$binary" >&2 ;; esac SNO_INSTALL_ENTRY=${SNO_INSTALL_ENTRY:-unknown} "$binary" init &2 partial=true next_step="\"$binary\" init" told=", but it did not finish preparing itself. Next I run $next_step." } "$binary" skills core "$work_dir/core" || { printf 'Sno is installed, but could not print the instructions for your AI agent. Run "%s" skills core to see them.\n' "$binary" >&2 partial=true } # Installing Sno means installing Sno Station, so the installer runs setup itself instead of leaving it to the agent. if [ "$next_step" = "\"$binary\" setup" ]; then printf '\nSetting up Sno Station...\n' if "$binary" setup &2 partial=true told=', but setting up Sno Station did not finish. Next I run setup again.' fi fi fi printf 'Installed sno %s\nLocation: %s\n' "$SNO_RELEASE_VERSION" "$binary" [ "$update_only" = true ] || printf 'Next command: %s\n' "$next_step" printf '\nFor your AI agent (you can skip these lines):\n' printf 'tell the user: Sno %s is installed at %s%s\n' "$SNO_RELEASE_VERSION" "$binary" "$told" if [ "$update_only" = false ]; then if [ "$next_step" = "\"$binary\" skills onboard" ]; then printf 'Sno Station setup already ran above as part of this install. The install ends with onboarding: run %s now and ask the user its questions, one per message, starting with how Sno should work.\n' "$next_step" else printf 'Installing Sno is not finished yet: the user asked for Sno, and %s is the second half of that same install. Run it now; if it asks the user a question, pass the question on and continue with the answer.\n' "$next_step" fi sed '/^Sno Station: /d' "$work_dir/core" if [ "$next_step" != "\"$binary\" skills onboard" ] && [ -f "$HOME/.config/sno/assemble.json" ]; then printf 'Station was already selected; read "%s" skills onboard for its current setup and choices.\n' "$binary" fi else printf 'Run sno by its full path, %s, for the next command.\n' "$binary" fi printf '\n' printf '%s\n' "$PATH" | tr ':' '\n' | while IFS= read -r directory; do old_binary=${directory:-.}/sno # The cargo launcher that ran this installer is the user's entry, not an old copy. if [ "${SNO_INSTALL_ENTRY:-}" = crate ] && [ "$old_binary" = "${CARGO_HOME:-$HOME/.cargo}/bin/sno" ]; then continue fi if [ "$old_binary" != "$binary" ] && [ -f "$old_binary" ] && [ -x "$old_binary" ]; then delete_path=$(printf '%s' "$old_binary" | sed 's/["\\$`]/\\&/g') printf 'An older copy of sno is at %s and may run instead of the new one.\nRemove it with: rm -- "%s"\nThen open a new terminal.\n' "$old_binary" "$delete_path" fi done [ "$partial" = false ]