#!/usr/bin/env bash # swap — one-shot installer # # curl -fsSL https://swagg-neon.vercel.app/install.sh | bash # # Downloads a prebuilt binary for this OS/arch from the public Vercel # host (or SWAP_INSTALL_BASE). Binaries live in Vercel Blob and are # redirected from /cli/* — the git repo can stay private. # Installs to ~/.local/bin/swap (or SWAP_INSTALL_DIR). set -euo pipefail # Public CDN for prebuilt binaries (Vercel Blob). The git repo stays private — # assets are not served from GitHub. Override with SWAP_INSTALL_BASE if needed. # install.sh itself is hosted at https://swagg-neon.vercel.app/install.sh # (and /cli/* on that host redirects here after vercel.json is deployed). BASE="${SWAP_INSTALL_BASE:-https://jng5l4649ewvi7zy.public.blob.vercel-storage.com/cli}" INSTALL_DIR="${SWAP_INSTALL_DIR:-$HOME/.local/bin}" BIN_NAME="swap" err() { printf 'error: %s\n' "$*" >&2; exit 1; } info() { printf '%s\n' "$*" >&2; } need_cmd() { command -v "$1" >/dev/null 2>&1 || err "required command not found: $1" } detect_asset() { local os arch os="$(uname -s | tr '[:upper:]' '[:lower:]')" arch="$(uname -m)" case "$os" in darwin|linux) ;; msys*|mingw*|cygwin*) err "Windows is not supported by this installer yet. Build from source or download a release asset manually." ;; *) err "unsupported OS: $(uname -s). Supported: macOS (darwin), Linux." ;; esac case "$arch" in x86_64|amd64) arch="x64" ;; arm64|aarch64) arch="arm64" ;; *) err "unsupported architecture: $arch. Supported: x64, arm64." ;; esac printf '%s\n' "${BIN_NAME}-${os}-${arch}" } download() { local url="$1" dest="$2" if command -v curl >/dev/null 2>&1; then if ! curl -fsSL --connect-timeout 30 --max-time 600 -o "$dest" "$url"; then return 1 fi elif command -v wget >/dev/null 2>&1; then if ! wget -q -O "$dest" "$url"; then return 1 fi else err "need curl or wget to download the binary" fi return 0 } # Global so the EXIT trap can always see it (locals vanish when main returns). SWAP_TMPDIR="" cleanup_tmpdir() { if [ -n "${SWAP_TMPDIR}" ] && [ -d "${SWAP_TMPDIR}" ]; then rm -rf "${SWAP_TMPDIR}" fi } trap cleanup_tmpdir EXIT INT TERM main() { need_cmd uname need_cmd mktemp need_cmd mkdir need_cmd chmod need_cmd mv local asset url tmpbin size asset="$(detect_asset)" url="${BASE%/}/${asset}" SWAP_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/swap-install.XXXXXX")" tmpbin="${SWAP_TMPDIR}/${asset}" if ! download "$url" "$tmpbin"; then err "failed to download ${asset} from ${url} No prebuilt binary for this platform, or the asset is missing from the public CDN. Supported prebuilts: swap-darwin-arm64, swap-darwin-x64, swap-linux-arm64, swap-linux-x64 Build from source (requires repo access + Bun): git clone && cd swap-cli && bun install && SWAP_PUBLIC_BUILD=1 ./build install dist/swap ${INSTALL_DIR}/swap" fi if [ ! -s "$tmpbin" ]; then err "downloaded file is empty — check ${url}" fi # Bun-compiled binaries are tens of MB; a few KB almost always means 404 HTML. size="$(wc -c <"$tmpbin" | tr -d ' ')" if [ "$size" -lt 1000000 ]; then err "downloaded file is only ${size} bytes (expected a multi-MB binary). URL may be wrong or the asset missing: ${url}" fi mkdir -p "$INSTALL_DIR" chmod +x "$tmpbin" mv -f "$tmpbin" "${INSTALL_DIR}/${BIN_NAME}" info "" info "installed ${INSTALL_DIR}/${BIN_NAME}" case ":$PATH:" in *":${INSTALL_DIR}:"*) ;; *) info "" info "note: ${INSTALL_DIR} is not on your PATH." info " add this to your shell profile (~/.zshrc / ~/.bashrc):" info " export PATH=\"${INSTALL_DIR}:\$PATH\"" info " then open a new terminal (or: source ~/.zshrc)" ;; esac info "" info "try:" info " swap --help" info " swap 100 USDC WETH" info "" info "first run may prompt for an Alchemy key / RPC URL (saved to ~/.swap)." info "re-run this installer anytime to upgrade." } main "$@"