#!/bin/sh # Adapted from Fly's installer (https://fly.io/install.sh), based on Deno's installer. # Copyright 2019 the Deno authors. All rights reserved. MIT license. # Attribution and full license: https://get.liveterminal.ai/installer-license.txt # Liveterminal modifications: verified releases, safe updates, strict inputs and PATH setup. # Copyright 2019 the Deno authors. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. set -eu fail() { printf 'Install failed: %s\n' "$*" >&2 exit 1 } fetch() { curl --proto '=https' --proto-redir '=https' --tlsv1.2 --fail --silent --show-error \ --location --retry 3 --connect-timeout 10 --max-time 300 --output "$2" "$1" } hash_file() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d ' ' -f 1 else shasum -a 256 "$1" | cut -d ' ' -f 1 fi } append_path() { rc_file=$1 if [ ! -L "$rc_file" ] && { [ ! -e "$rc_file" ] || [ -f "$rc_file" ]; }; then if ! grep -Fqx "$path_line" "$rc_file" 2>/dev/null; then if ! printf '\n# Liveterminal CLI\n%s\n' "$path_line" >>"$rc_file"; then printf 'Could not update %s; add the PATH line below manually.\n' "$rc_file" >&2 fi fi fi } setup_path() { # Quote literal paths before writing shell code; never evaluate path contents. quoted=$(printf '%s' "$bin_dir" | sed "s/'/'\\\\''/g") path_line="case \":\$PATH:\" in *:'$quoted':*) ;; *) export PATH='$quoted':\"\$PATH\" ;; esac" if [ "$modify_path" = true ]; then case "${SHELL:-}" in */zsh) append_path "${ZDOTDIR:-$HOME}/.zshrc" ;; */bash) append_path "$HOME/.bashrc" # Bash reads only the first existing login profile. Cover login and # non-login terminals without making one user's profile source another. login_file="$HOME/.profile" for candidate in .bash_profile .bash_login .profile; do if [ -e "$HOME/$candidate" ] || [ -L "$HOME/$candidate" ]; then login_file="$HOME/$candidate" break fi done append_path "$login_file" ;; esac fi case ":$PATH:" in *":$bin_dir:"*) return ;; esac printf 'To use lt in this terminal, run:\n %s\n' "$path_line" >&2 } main() { umask 077 version='' modify_path=true bin_dir="${HOME:?HOME must be set}/.local/bin" while [ "$#" -gt 0 ]; do case "$1" in --version | --bin-dir) [ "$#" -ge 2 ] || fail "$1 requires a value." case "$1" in --version) version=$2 ;; --bin-dir) bin_dir=$2 ;; esac shift 2 ;; --no-modify-path) modify_path=false shift ;; --help) printf 'Usage: sh install.sh [--version X.Y.Z] [--bin-dir /absolute/path] [--no-modify-path]\n' return ;; *) fail "Unknown option: $1. Run with --help for usage." ;; esac done case "$bin_dir" in /*) ;; *) fail '--bin-dir must be an absolute path.' ;; esac if printf '%s' "$bin_dir" | LC_ALL=C grep -q '[[:cntrl:]]'; then fail 'The install path must not contain control characters.' fi for tool in curl tar bash; do command -v "$tool" >/dev/null 2>&1 || fail "$tool is required. Install it and try again." done command -v sha256sum >/dev/null 2>&1 || command -v shasum >/dev/null 2>&1 || fail 'SHA-256 verification requires sha256sum or shasum.' os=$(uname -s) arch=$(uname -m) case "$os/$arch" in Darwin/arm64) platform=macos-arm64 ;; Darwin/x86_64) platform=macos-x86_64 ;; Linux/aarch64 | Linux/arm64) platform=linux-arm64 ;; Linux/x86_64) platform=linux-x86_64 ;; *) fail "Unsupported platform: $os/$arch. See https://get.liveterminal.ai for supported computers." ;; esac base_url=https://get.liveterminal.ai temporary=$(mktemp -d "${TMPDIR:-/tmp}/lt-install.XXXXXXXX") trap 'rm -rf -- "$temporary"' EXIT trap 'exit 130' INT trap 'exit 143' TERM HUP if [ -z "$version" ]; then fetch "$base_url/latest.txt" "$temporary/latest.txt" || fail 'Could not find the latest release. Retry later.' version=$(cat "$temporary/latest.txt") fi case "$version" in *[!0-9.]* | '') fail 'Invalid release version; expected X.Y.Z.' ;; esac printf '%s\n' "$version" | LC_ALL=C grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || fail 'Invalid release version; expected X.Y.Z.' name="lt-$platform" archive="$name.tar.gz" printf 'Downloading lt %s for %s...\n' "$version" "$platform" >&2 fetch "$base_url/releases/$version/$archive.sha256" "$temporary/checksum" || fail 'Could not download the release checksum. Check the version and retry.' fetch "$base_url/releases/$version/$archive" "$temporary/$archive" || fail 'Download failed. Your existing installation has not changed.' checksum=$(cat "$temporary/checksum") expected=$(printf '%s\n' "$checksum" | cut -d ' ' -f 1) printf '%s\n' "$expected" | LC_ALL=C grep -Eq '^[a-f0-9]{64}$' || fail 'Invalid release checksum.' [ "$checksum" = "$expected $archive" ] || fail 'Invalid checksum filename.' [ "$(hash_file "$temporary/$archive")" = "$expected" ] || fail 'Checksum mismatch. Your existing installation has not changed.' mkdir "$temporary/bundle" # Stream only known files to paths we create. Archive paths/links never control extraction. for file in lt install.sh build-info.txt SHA256SUMS; do tar -xzOf "$temporary/$archive" "$name/$file" >"$temporary/bundle/$file" || fail "Invalid bundle: missing $file." done identity=$(sed -n '1p' "$temporary/bundle/build-info.txt") case "$identity" in "$version+"*) ;; *) fail 'The downloaded build does not match the requested release.' ;; esac bash "$temporary/bundle/install.sh" --bin-dir "$bin_dir" || fail 'The verified bundle could not be installed. See https://get.liveterminal.ai for compatibility and recovery.' setup_path printf 'Run lt --version, then sign in:\n lt auth login --api-origin https://app.liveterminal.ai\n' >&2 } # Execute only after the complete script has arrived when piped into sh. main "$@"