#!/usr/bin/env bash # Install or update the Sessions server on this computer: # curl -fsSL https://sessions.olibuijr.com/install.sh | bash # # What it does, all inside your home directory and without sudo: # ~/.local/lib/agent-sessions/ the server and the `ses` command (Python, standard library only) # ~/.local/bin/ses session manager: `ses`, `ses new claude`, `ses pair` # ~/.config/agent-sessions/token random access token, created on the first run, never sent anywhere # ~/.config/systemd/user/agent-sessions.service starts the server (port 8170) at login # Options (environment): SESSIONS_WRAP=1 also makes plain `claude`, `codex` and `pi` in new bash # shells start inside tmux so they show up in the app; SESSIONS_BASE=URL installs from another mirror. # Uninstall: systemctl --user disable --now agent-sessions; rm -rf ~/.local/lib/agent-sessions # ~/.local/bin/ses ~/.config/agent-sessions ~/.config/systemd/user/agent-sessions.service set -euo pipefail main() { local base=${SESSIONS_BASE:-https://sessions.olibuijr.com} local lib=$HOME/.local/lib/agent-sessions conf=$HOME/.config/agent-sessions units=$HOME/.config/systemd/user [[ $(uname -s) == Linux ]] || die "this installer supports Linux (systemd). See $base/setup" need python3 "install Python 3.9 or newer" need tmux "install tmux (apt install tmux, dnf install tmux, pacman -S tmux)" need curl "install curl" need tar "install tar" python3 -c 'import sys; sys.exit(sys.version_info < (3, 9))' || die "Python 3.9 or newer is needed" systemctl --user show-environment >/dev/null 2>&1 || die "no systemd user session (systemctl --user). See $base/setup to run the server by hand" local tmp tmp=$(mktemp -d) trap "rm -rf '$tmp'" EXIT echo "Downloading the Sessions server…" curl -fsSL "$base/agent-sessions-server.tar.gz" -o "$tmp/server.tar.gz" tar -xzf "$tmp/server.tar.gz" -C "$tmp" [[ -f $tmp/server/sessions_server.py && -f $tmp/deploy/agent-sessions.service ]] || die "the download is incomplete" python3 -m py_compile "$tmp"/server/*.py || die "the download is damaged" mkdir -p "$lib" "$conf" "$units" "$HOME/.local/bin" install -m 644 "$tmp"/server/*.py "$lib/" chmod 755 "$lib/ses.py" ln -sf "$lib/ses.py" "$HOME/.local/bin/ses" install -m 644 "$tmp/deploy/agent-sessions.service" "$units/" install -m 644 "$tmp/deploy/shell.bash" "$conf/" if [[ ! -s $conf/token ]]; then (umask 077; python3 -c 'import secrets; print(secrets.token_urlsafe(32))' > "$conf/token") fi chmod 600 "$conf/token" if [[ ${SESSIONS_WRAP:-} == 1 ]]; then local line='[ -f ~/.config/agent-sessions/shell.bash ] && . ~/.config/agent-sessions/shell.bash' grep -qxF "$line" "$HOME/.bashrc" 2>/dev/null || printf '\n# Sessions: claude, codex and pi start inside tmux\n%s\n' "$line" >> "$HOME/.bashrc" fi systemctl --user daemon-reload systemctl --user enable agent-sessions.service >/dev/null 2>&1 systemctl --user restart agent-sessions.service # Keep the server running after you log out (best effort; needs no sudo on most systems). loginctl enable-linger "$USER" >/dev/null 2>&1 || true local i ok= for i in $(seq 30); do if curl -fsS -m 2 -H "Authorization: Bearer $(cat "$conf/token")" http://127.0.0.1:8170/api/health >/dev/null 2>&1; then ok=1 break fi sleep 0.5 done [[ -n $ok ]] || die "the server did not start. Look at: journalctl --user -u agent-sessions -n 30" echo echo "Sessions server $(cat "$tmp/VERSION" 2>/dev/null) is running on port 8170." case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) echo "Note: add ~/.local/bin to your PATH to use the ses command." ;; esac if command -v ufw >/dev/null 2>&1 || command -v firewall-cmd >/dev/null 2>&1; then echo "If a firewall is on, allow TCP port 8170 from your phone's network." fi command -v qrencode >/dev/null 2>&1 || echo "Tip: install qrencode to get a QR code from ses pair." echo echo "Next: pair your iPhone with" echo " ~/.local/bin/ses pair" echo "Start an agent the app can see with: ses new claude (or codex, pi, shell)" } need() { command -v "$1" >/dev/null 2>&1 || die "$1 is missing: $2"; } die() { echo "install: $*" >&2; exit 1; } # The whole script is read before anything runs, so a cut-off download does nothing. main "$@"