#!/usr/bin/env bash # Ziro installer for macOS and Linux. # # Installs ziro into its own virtual environment and puts a `ziro` shim on PATH. # Needs nothing but Python 3.11.9+; no pipx, no uv, no admin rights. # # curl -fsSL https://ziro-agent.com/install.sh | bash # # Served as a static asset by the web app (apps/web), which is why it lives in # its public/ directory. It is the ONLY copy in the repo: edit it here and it # ships on the next deploy. # # To run it straight from a checkout: bash apps/web/public/install.sh # # Environment overrides: # ZIRO_HOME install root (default ~/.ziro) # ZIRO_BIN_DIR where the shim goes (default ~/.local/bin) # ZIRO_VERSION pin a version (default: latest) # ZIRO_INDEX_URL package index (default https://test.pypi.org/simple/) # ZIRO_EXTRA_INDEX dependency index (default https://pypi.org/simple/) # ZIRO_PYTHON force a specific python interpreter # # Flags: --uninstall, --version , --help set -euo pipefail ZIRO_HOME="${ZIRO_HOME:-$HOME/.ziro}" ZIRO_BIN_DIR="${ZIRO_BIN_DIR:-$HOME/.local/bin}" ZIRO_INDEX_URL="${ZIRO_INDEX_URL:-https://test.pypi.org/simple/}" ZIRO_EXTRA_INDEX="${ZIRO_EXTRA_INDEX:-https://pypi.org/simple/}" ZIRO_VERSION="${ZIRO_VERSION:-}" VENV_DIR="$ZIRO_HOME/venv" MIN_PY="3.11.9" # Entry points from pyproject [project.scripts]. `ziro` is the one users type. SHIMS="ziro ziro-once manage-agents" # ANSI only when stdout is a terminal, so piping the log to a file stays clean. if [ -t 1 ]; then BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[31m'; GREEN=$'\033[32m' YELLOW=$'\033[33m'; LIME=$'\033[38;5;154m'; RESET=$'\033[0m' else BOLD=''; DIM=''; RED=''; GREEN=''; YELLOW=''; LIME=''; RESET='' fi say() { printf '%s\n' "$*"; } step() { printf '%s==>%s %s\n' "$LIME" "$RESET" "$*"; } warn() { printf '%swarning:%s %s\n' "$YELLOW" "$RESET" "$*" >&2; } die() { printf '%serror:%s %s\n' "$RED" "$RESET" "$*" >&2; exit 1; } usage() { cat <<'USAGE' Ziro installer install.sh install or upgrade ziro install.sh --version 0.2.5 install a specific version install.sh --uninstall remove the install and its shims install.sh --help this message Environment: ZIRO_HOME, ZIRO_BIN_DIR, ZIRO_VERSION, ZIRO_INDEX_URL, ZIRO_EXTRA_INDEX, ZIRO_PYTHON USAGE } # The venv layout differs by platform: POSIX gets bin/, but a Windows Python # (Git Bash, MSYS) gets Scripts/ with .exe suffixes. Resolve it rather than # assume, so the same script works under WSL, Git Bash, macOS, and Linux. venv_bin_dir() { if [ -d "$VENV_DIR/bin" ]; then printf '%s' "$VENV_DIR/bin" elif [ -d "$VENV_DIR/Scripts" ]; then printf '%s' "$VENV_DIR/Scripts" else return 1 fi } # First existing path among "/" and "/.exe". venv_exe() { local dir="$1" name="$2" if [ -f "$dir/$name" ]; then printf '%s' "$dir/$name" elif [ -f "$dir/$name.exe" ]; then printf '%s' "$dir/$name.exe" else return 1 fi } uninstall() { step "Removing shims from $ZIRO_BIN_DIR" for name in $SHIMS; do target="$ZIRO_BIN_DIR/$name" # Only remove a shim that points into our venv. Never touch an unrelated # binary that happens to share the name. if [ -f "$target" ] && grep -qF "$VENV_DIR" "$target" 2>/dev/null; then rm -f "$target" say " removed $target" elif [ -e "$target" ]; then warn "left $target alone: it does not point at $VENV_DIR" fi done if [ -d "$VENV_DIR" ]; then step "Removing $VENV_DIR" rm -rf "$VENV_DIR" fi say "" say "${GREEN}Uninstalled.${RESET}" # Config, memories, and threads are deliberately kept. Losing a user's # conversation history to an uninstall would be unrecoverable. if [ -d "$ZIRO_HOME" ]; then say "${DIM}Your settings and data are still in $ZIRO_HOME.${RESET}" say "${DIM}Delete it yourself if you want them gone: rm -rf $ZIRO_HOME${RESET}" fi } # Print the first interpreter that satisfies MIN_PY, or nothing. find_python() { local candidates="${ZIRO_PYTHON:-} python3.14 python3.13 python3.12 python3.11 python3 python" for c in $candidates; do [ -n "$c" ] || continue command -v "$c" >/dev/null 2>&1 || continue if "$c" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 11, 9) else 1)' 2>/dev/null; then command -v "$c" return 0 fi done return 1 } # Name the shell rc file we would tell the user to edit. rc_file() { case "${SHELL##*/}" in zsh) printf '%s' "$HOME/.zshrc" ;; bash) [ -f "$HOME/.bash_profile" ] && printf '%s' "$HOME/.bash_profile" || printf '%s' "$HOME/.bashrc" ;; fish) printf '%s' "$HOME/.config/fish/config.fish" ;; *) printf '%s' "$HOME/.profile" ;; esac } main() { while [ $# -gt 0 ]; do case "$1" in --uninstall) uninstall; exit 0 ;; --version) ZIRO_VERSION="${2:-}"; [ -n "$ZIRO_VERSION" ] || die "--version needs a value"; shift 2 ;; --help|-h) usage; exit 0 ;; *) die "unknown option: $1 (try --help)" ;; esac done say "" say " ${BOLD}${LIME}Ziro${RESET} installer" say "" step "Looking for Python $MIN_PY or newer" local py if ! py="$(find_python)"; then die "no Python $MIN_PY+ found on PATH. Install it from https://www.python.org/downloads/ (or your package manager), then run this script again. Set ZIRO_PYTHON=/path/to/python to force one." fi say " $py ($("$py" -c 'import platform; print(platform.python_version())'))" step "Creating the environment in $VENV_DIR" mkdir -p "$ZIRO_HOME" if [ -d "$VENV_DIR" ]; then say " ${DIM}reusing the existing environment${RESET}" else "$py" -m venv "$VENV_DIR" || die "could not create a virtual environment. On Debian and Ubuntu this usually means the venv module is missing: sudo apt install python3-venv" fi local vbin vpy vbin="$(venv_bin_dir)" || die "the environment looks broken: no bin/ or Scripts/ in $VENV_DIR" vpy="$(venv_exe "$vbin" python)" || die "the environment looks broken: no interpreter in $vbin" step "Installing ziro" local spec="ziro" [ -n "$ZIRO_VERSION" ] && spec="ziro==$ZIRO_VERSION" "$vpy" -m pip install --upgrade --quiet --disable-pip-version-check pip \ || warn "could not upgrade pip in the environment; continuing" # TestPyPI does not mirror third-party dependencies, so those resolve from # prod PyPI. This matches what `ziro upgrade` does (app/cli/upgrade.py). "$vpy" -m pip install --upgrade --disable-pip-version-check \ --index-url "$ZIRO_INDEX_URL" \ --extra-index-url "$ZIRO_EXTRA_INDEX" \ "$spec" || die "pip could not install $spec" step "Linking into $ZIRO_BIN_DIR" mkdir -p "$ZIRO_BIN_DIR" for name in $SHIMS; do local exe exe="$(venv_exe "$vbin" "$name")" || continue # A wrapper rather than a symlink: it is layout-agnostic (no .exe suffix # problem under Git Bash) and keeps the venv's python and pip off PATH. cat > "$ZIRO_BIN_DIR/$name" <> $rc${RESET}" fi say "" say "Or start it right now with the full path:" say "" say " ${BOLD}$ZIRO_BIN_DIR/ziro${RESET}" ;; esac say "" } main "$@"