#!/bin/sh # CogNEXUS - run it in your boundary. https://cognexuslabs.ai/install # # Thin bootstrap, on purpose: everything real lives in the artzain CLI # (PyPI), which this script installs and hands off to. Read it before you # run it - it is short because there is nothing to hide. # # curl -fsSL https://cognexuslabs.ai/install.sh | sh # # Needs: Python 3.10+ and Docker (the CLI's own preflight checks Docker and # prints a one-line fix for anything missing). # # Install order: pipx (best), then pip --user, then a dedicated virtualenv. # The venv path is what makes Debian/Ubuntu 24.04+ work — those distros # reject `pip install --user` under PEP 668 when pipx is not installed. # # Written for `curl | sh`, where the shell runs the script as it arrives. # Everything below is a function definition until the last line calls # main, so a download cut off part-way runs nothing at all. And the CLI # gets your terminal as stdin, not the spent pipe (see handoff). set -eu say() { printf '%s\n' "$*"; } fail() { printf 'install.sh: %s\n' "$*" >&2; exit 1; } install_venv() { say "pip --user failed. Installing into a dedicated virtualenv: $VENV" mkdir -p "$(dirname "$VENV")" if ! "$PY" -m venv "$VENV"; then fail "Could not create a virtualenv (python3-venv is often missing). Install pipx, then re-run. $apt_hint" fi if ! "$VENV/bin/python" -m pip install --upgrade --quiet "$PKG"; then fail "pip install into $VENV failed. Install pipx, then re-run. $apt_hint" fi } # Replace this shell with the CLI. Under `curl | sh`, stdin is the pipe the # script came through, already at end-of-file, so a question from # `artzain local up` would read nothing. Give it the terminal instead when # one can be opened. With no controlling terminal (CI, cron, a container # without -t), opening /dev/tty fails and stdin is left as it is. handoff() { if [ ! -t 0 ] && (: /dev/null; then exec "$@" =0.6.0 is the first release that carries `artzain local`. Pinning # the floor makes an out-of-date index fail loudly instead of installing a # CLI without the installer in it. VENV="${XDG_DATA_HOME:-$HOME/.local/share}/artzain/venv" apt_hint="Debian/Ubuntu: sudo apt install pipx && pipx ensurepath" PY="" for candidate in python3 python; do if command -v "$candidate" >/dev/null 2>&1; then PY="$candidate"; break; fi done [ -n "$PY" ] || fail "Python 3.10+ is required. Install it (python.org or your package manager), then re-run." "$PY" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)' \ || fail "Python 3.10+ is required (found: $("$PY" -V 2>&1))." # The CLI this run installed, when we know where it is. Preferred over # whatever `artzain` PATH finds first, which may be an older install. BIN="" VIA_PIPX="" if command -v pipx >/dev/null 2>&1; then say "Installing the artzain CLI (pipx)..." pipx install --force "$PKG" VIA_PIPX=1 # pipx links the CLI into its bin dir, which stays off PATH until # `pipx ensurepath` has run AND a new shell has started: exactly where # "install pipx, then re-run" leaves you. The module fallback below cannot # help, because pipx installs into a private venv. Ask pipx for the bin # dir; a pipx too old for `environment --value` uses the default. PIPX_BIN=$(pipx environment --value PIPX_BIN_DIR 2>/dev/null) || PIPX_BIN="" BIN="${PIPX_BIN:-${PIPX_BIN_DIR:-$HOME/.local/bin}}/artzain" else say "pipx not found - trying pip --user..." if ! "$PY" -m pip install --user --upgrade --quiet "$PKG"; then install_venv BIN="$VENV/bin/artzain" fi fi say "" say "Starting CogNEXUS (docker compose, images pinned by digest)..." if [ -n "$BIN" ] && [ -x "$BIN" ]; then if [ -n "$VIA_PIPX" ] && [ "$(command -v artzain 2>/dev/null || true)" != "$BIN" ]; then say "Note: artzain is in ${BIN%/artzain}, which is not on PATH in this shell." say " Run 'pipx ensurepath' and open a new shell to use 'artzain' directly." fi handoff "$BIN" local up "$@" fi if command -v artzain >/dev/null 2>&1; then handoff artzain local up "$@" fi if [ -n "$VIA_PIPX" ]; then fail "pipx installed artzain, but it is not in ${BIN%/artzain} or on PATH. Run 'pipx ensurepath', open a new shell, then run: artzain local up" fi # pip --user installs sometimes land off PATH; the module entry point is the # same code either way. handoff "$PY" -c 'import sys; from artzain.cli import main; sys.argv = ["artzain", "local", "up"] + sys.argv[1:]; main()' "$@" } main "$@"