install.sh
#!/usr/bin/env bash
set -euo pipefail

platform=$(uname -ms)

# Check for unsupported platforms
case $platform in
'MINGW64'* | 'MSYS'* | 'CYGWIN'*)
    echo "Windows installation is not supported yet. Please use WSL or install via npm:"
    echo "  npm install -g @composio/cli"
    exit 1
    ;;
esac

# Reset
Color_Off=''

# Regular Colors
Red=''
Green=''
Dim='' # White

# Bold
Bold_White=''
Bold_Green=''

if [[ -t 1 ]]; then
    # Reset
    Color_Off='\033[0m' # Text Reset

    # Regular Colors
    Red='\033[0;31m'   # Red
    Green='\033[0;32m' # Green
    Dim='\033[0;2m'    # White

    # Bold
    Bold_Green='\033[1;32m' # Bold Green
    Bold_White='\033[1m'    # Bold White
fi

error() {
    echo -e "${Red}error${Color_Off}:" "$@" >&2
    exit 1
}

info() {
    echo -e "${Dim}$@ ${Color_Off}"
}

info_bold() {
    echo -e "${Bold_White}$@ ${Color_Off}"
}

success() {
    echo -e "${Green}$@ ${Color_Off}"
}

# Check for required tools
command -v unzip >/dev/null ||
    error 'unzip is required to install Composio CLI'

command -v curl >/dev/null ||
    error 'curl is required to install Composio CLI'

if [[ $# -gt 1 ]]; then
    error 'Too many arguments, only 1 is allowed. You can specify a specific version to install. (e.g. "v0.1.24")'
fi

# Determine platform and architecture
case $platform in
'Darwin x86_64')
    target=darwin-x64
    ;;
'Darwin arm64')
    target=darwin-aarch64
    ;;
'Linux aarch64' | 'Linux arm64')
    target=linux-aarch64
    ;;
'Linux x86_64' | *)
    target=linux-x64
    ;;
esac

# Check for musl on Linux
case "$target" in
'linux'*)
    if [ -f /etc/alpine-release ]; then
        target="$target-musl"
    fi
    ;;
esac

# Special handling for Rosetta on macOS
if [[ $target = darwin-x64 ]]; then
    # Is this process running in Rosetta?
    # redirect stderr to devnull to avoid error message when not running in Rosetta
    if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then
        target=darwin-aarch64
        info "Your shell is running in Rosetta 2. Downloading Composio CLI for $target instead"
    fi
fi

COMPOSIO_GITHUB_OWNER=${COMPOSIO_GITHUB_OWNER-"ComposioHQ"}
COMPOSIO_GITHUB_REPO=${COMPOSIO_GITHUB_REPO-"composio"}
COMPOSIO_GITHUB_URL=${COMPOSIO_GITHUB_URL-"https://github.com"}
github_repo="$COMPOSIO_GITHUB_URL/$COMPOSIO_GITHUB_OWNER/$COMPOSIO_GITHUB_REPO"

exe_name=composio

# Determine version to install
if [[ $# = 0 ]]; then
    info "Finding latest CLI release..."
    
    # Get the latest version tag using OS-specific grep patterns
    case $platform in
    'Darwin'*)
        # BSD/MacOS: Use extended regex with -E
        version=$(git ls-remote --tags "$github_repo" \
            | awk -F'/' '{print $3}' \
            | grep -E "^v\d+\.\d+\.\d+.*" \
            | sort -V \
            | tail -n1)
        ;;
    *)
        # Unix/Linux: Use Perl-compatible regex with -P
        version=$(git ls-remote --tags "$github_repo" \
            | awk -F'/' '{print $3}' \
            | grep -P "^v\d+\.\d+\.\d+.*" \
            | sort -V \
            | tail -n1)
        ;;
    esac
    
    if [[ -z "$version" ]]; then
        error "Failed to determine the latest version. Please specify a version manually."
    fi
    
    info "Found latest version: $version"
    
    composio_uri="$github_repo/releases/download/$version/composio-$target.zip"
else
    version=$1
    composio_uri="$github_repo/releases/download/$version/composio-$target.zip"
fi

info "Installing Composio CLI $version for $target"

COMPOSIO_INSTALL_DIR=${COMPOSIO_INSTALL_DIR:-$HOME/.composio}
exe=$COMPOSIO_INSTALL_DIR/composio

if [[ ! -d $COMPOSIO_INSTALL_DIR ]]; then
    mkdir -p "$COMPOSIO_INSTALL_DIR" ||
        error "Failed to create install directory \"$COMPOSIO_INSTALL_DIR\""
fi

# Download
info "Downloading Composio CLI..."
curl --fail --location --progress-bar --output "$exe.zip" "$composio_uri" ||
    error "Failed to download Composio CLI from \"$composio_uri\""

# Extract
info "Extracting Composio CLI..."
unzip -oqd "$COMPOSIO_INSTALL_DIR" "$exe.zip" ||
    error 'Failed to extract Composio CLI'

# Move binary to final location
if [[ -f "$COMPOSIO_INSTALL_DIR/composio-$target/$exe_name" ]]; then
    mv "$COMPOSIO_INSTALL_DIR/composio-$target/$exe_name" "$exe" ||
        error 'Failed to move extracted binary to destination'
    rm -r "$COMPOSIO_INSTALL_DIR/composio-$target"
elif [[ -f "$COMPOSIO_INSTALL_DIR/$exe_name" ]]; then
    mv "$COMPOSIO_INSTALL_DIR/$exe_name" "$exe" ||
        error 'Failed to move extracted binary to destination'
else
    error 'Binary not found in extracted archive'
fi

chmod +x "$exe" ||
    error 'Failed to set permissions on Composio CLI executable'

rm "$exe.zip"

tildify() {
    if [[ $1 = $HOME/* ]]; then
        local replacement=\~/
        echo "${1/$HOME\//$replacement}"
    else
        echo "$1"
    fi
}

success "Composio CLI was installed successfully to $Bold_Green$(tildify "$exe")"

refresh_command=''

tilde_bin_dir=$(tildify "$COMPOSIO_INSTALL_DIR")
quoted_install_dir=\"${COMPOSIO_INSTALL_DIR//\"/\\\"}\"

if [[ $quoted_install_dir = \"$HOME/* ]]; then
    quoted_install_dir=${COMPOSIO_INSTALL_DIR/$HOME\//\$HOME/}
fi

echo

# Add to shell configuration
case $(basename "$SHELL") in
fish)
    commands=(
        "set --export COMPOSIO_INSTALL_DIR $COMPOSIO_INSTALL_DIR"
        "set --export PATH $COMPOSIO_INSTALL_DIR \$PATH"
    )

    fish_config=$HOME/.config/fish/config.fish
    tilde_fish_config=$(tildify "$fish_config")

    if [[ -w $fish_config ]]; then
        {
            echo -e '\n# Composio CLI'

            for command in "${commands[@]}"; do
                echo "$command"
            done
        } >>"$fish_config"

        info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_fish_config\""

        refresh_command="source $tilde_fish_config"
    else
        echo "Manually add the directory to $tilde_fish_config (or similar):"

        for command in "${commands[@]}"; do
            info_bold "  $command"
        done
    fi
    ;;
zsh)
    commands=(
        "export COMPOSIO_INSTALL_DIR=$COMPOSIO_INSTALL_DIR"
        "export PATH=\"$COMPOSIO_INSTALL_DIR:\$PATH\""
    )

    zsh_config=$HOME/.zshrc
    tilde_zsh_config=$(tildify "$zsh_config")

    # Create .zshrc if it doesn't exist and directory is writable
    if [[ ! -f $zsh_config && -w $(dirname "$zsh_config") ]]; then
        touch "$zsh_config"
    fi

    if [[ -w $zsh_config ]]; then
        {
            echo -e '\n# Composio CLI'

            for command in "${commands[@]}"; do
                echo "$command"
            done
        } >>"$zsh_config"

        info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_zsh_config\""

        refresh_command="exec $SHELL"
    else
        echo "Manually add the directory to $tilde_zsh_config (or similar):"

        for command in "${commands[@]}"; do
            info_bold "  $command"
        done
    fi
    ;;
bash)
    commands=(
        "export $install_env=$quoted_install_dir"
        "export PATH=\"$bin_env:\$PATH\""
    )

    bash_configs=(
        "$HOME/.bashrc"
        "$HOME/.bash_profile"
    )

    if [[ ${XDG_CONFIG_HOME:-} ]]; then
        bash_configs+=(
            "$XDG_CONFIG_HOME/.bash_profile"
            "$XDG_CONFIG_HOME/.bashrc"
            "$XDG_CONFIG_HOME/bash_profile"
            "$XDG_CONFIG_HOME/bashrc"
        )
    fi

    set_manually=true
    for bash_config in "${bash_configs[@]}"; do
        tilde_bash_config=$(tildify "$bash_config")

        if [[ -w $bash_config ]]; then
            {
                echo -e '\n# Composio CLI'

                for command in "${commands[@]}"; do
                    echo "$command"
                done
            } >>"$bash_config"

            info "Added \"$tilde_bin_dir\" to \$PATH in \"$tilde_bash_config\""

            refresh_command="source $bash_config"
            set_manually=false
            break
        fi
    done

    if [[ $set_manually = true ]]; then
        echo "Manually add the directory to $tilde_bash_config (or similar):"

        for command in "${commands[@]}"; do
            info_bold "  $command"
        done
    fi
    ;;

*)
    echo 'Manually add the directory to ~/.bashrc (or similar):'
    info_bold "  export $install_env=$quoted_install_dir"
    info_bold "  export PATH=\"$bin_env:\$PATH\""
    ;;
esac

echo
info "To get started, run:"
echo

if [[ $refresh_command ]]; then
    info_bold "  $refresh_command"
fi

info_bold "  composio --help"
info_bold "  composio login"
