#!/bin/sh # vitro-tunnel. One static binary, installed somewhere your shell will find # it. This script picks the target, verifies the checksum, and puts the # file on your PATH. # # In a sandbox or in CI, skip this and name the file directly — one fewer # round trip and no network-fetched code to execute: # # curl -fsSL "https://vitro.run/tunnel/linux-amd64" -o vitro-tunnel # curl -fsSL "https://vitro.run/tunnel/linux-amd64/sha256" | shasum -a 256 -c - # chmod +x vitro-tunnel # # Overrides: # VITRO_BIN where to install (default: the first writable of # /usr/local/bin, then ~/.local/bin) # VITRO_VERSION a specific version set -eu base="https://vitro.run" os="$(uname -s)" arch="$(uname -m)" query="" [ "${VITRO_VERSION:-}" = "" ] || query="?version=${VITRO_VERSION}" case "$os" in Darwin) os=macos ;; Linux) os=linux ;; *) echo "vitro-tunnel: no build for $os" >&2; exit 1 ;; esac case "$arch" in x86_64|amd64) arch=amd64 ;; arm64|aarch64) arch=arm64 ;; *) echo "vitro-tunnel: no build for $arch" >&2; exit 1 ;; esac target="$os-$arch" # Where it lands. # # It used to land in the working directory, and `./vitro-tunnel` is not a # command — it is a path, and only from the one directory the install # happened to run in. A tunnel is opened from inside whatever project has # the dev server, which is never that directory. # # /usr/local/bin first because it is on every default PATH, on macOS and on # Linux, and needs no advice afterwards. ~/.local/bin when it is not # writable, because **this script must never call sudo**: it is piped into # a shell, which is the one moment nobody has explicitly agreed to, and a # password prompt out of a curl pipeline is how people learn to type one # without reading. if [ -n "${VITRO_BIN:-}" ]; then bin="$VITRO_BIN" elif [ -w /usr/local/bin ] 2>/dev/null; then bin=/usr/local/bin elif [ -n "${HOME:-}" ]; then bin="$HOME/.local/bin" else # A container that sets no HOME. `$HOME/.local/bin` would be # `/.local/bin`, which is a directory nobody's PATH contains and which # this script probably cannot create — a confusing failure in place of an # answerable one. echo "vitro-tunnel: /usr/local/bin is not writable and HOME is unset." >&2 echo "Say where to install it: VITRO_BIN=/some/bin sh -" >&2 exit 1 fi if ! mkdir -p "$bin" 2>/dev/null; then echo "vitro-tunnel: cannot create $bin" >&2 echo "Say where to install it: VITRO_BIN=/some/bin sh -" >&2 exit 1 fi # curl's own failure here is unhelpful: EISDIR shows up as "Failure # writing output to destination, passed N returned 4294967295", which # reads like a broken download and is actually a name already taken by # something that is not a plain file — a stray directory from a previous # run, or (found the hard way, inside a checkout of this very repo) a # source tree that happens to share the binary's name. for f in "$bin/vitro-tunnel" "$bin/vitro-tunnel.part"; do if [ -e "$f" ] && [ ! -f "$f" ]; then echo "vitro-tunnel: $f exists and is not a plain file" >&2 exit 1 fi done echo "vitro-tunnel: fetching $target" >&2 curl -fsSL "$base/tunnel/$target$query" -o "$bin/vitro-tunnel.part" # Verified before it is given its real name, so an interrupted or corrupt # download cannot be mistaken for an installed one. if command -v shasum >/dev/null 2>&1; then curl -fsSL "$base/tunnel/$target/sha256$query" \ | sed "s| .*| $bin/vitro-tunnel.part|" | shasum -a 256 -c - >/dev/null elif command -v sha256sum >/dev/null 2>&1; then curl -fsSL "$base/tunnel/$target/sha256$query" \ | sed "s| .*| $bin/vitro-tunnel.part|" | sha256sum -c - >/dev/null else echo "vitro-tunnel: no shasum or sha256sum; cannot verify" >&2 rm -f "$bin/vitro-tunnel.part" exit 1 fi chmod +x "$bin/vitro-tunnel.part" mv "$bin/vitro-tunnel.part" "$bin/vitro-tunnel" echo "vitro-tunnel: installed $bin/vitro-tunnel ($("$bin/vitro-tunnel" version))" >&2 # An install nobody's shell can see is not an install. Said at install # time rather than left to be found by `command not found`. case ":${PATH}:" in *":$bin:"*) ;; *) echo "" >&2 echo "Add $bin to your PATH:" >&2 case "${SHELL:-}" in */fish) echo " fish_add_path $bin" >&2 ;; */bash) echo " echo 'export PATH=\"$bin:\$PATH\"' >> ~/.bash_profile" >&2 ;; *) echo " echo 'export PATH=\"$bin:\$PATH\"' >> ~/.zshrc" >&2 ;; esac ;; esac # Two copies on one PATH is a version that never changes no matter what you # install, and it is invisible until somebody spends an afternoon on it. hash -r 2>/dev/null || true resolved="$(command -v vitro-tunnel 2>/dev/null || true)" if [ -n "$resolved" ] && [ "$resolved" != "$bin/vitro-tunnel" ]; then echo "" >&2 echo "Warning: vitro-tunnel on your PATH is $resolved, not the copy just" >&2 echo "installed. Put $bin earlier in your PATH, or remove the other one." >&2 fi echo "" >&2 echo "Next:" >&2 echo " vitro-tunnel login sign in once, in a browser" >&2 echo " vitro-tunnel tunnel from a project, with the dev server up" >&2 echo "" >&2 echo " npx skills add https://vitro.run/skills" >&2 echo " teach your agent how to use it" >&2