Files
opencode-public/openchamber-boostrap.sh
T

174 lines
7.0 KiB
Bash

# openchamber-prereqs.sh — bootstrap a Linux host for opencode + openchamber
#
# Intent: run this once on a fresh Linux host, then connect from the
# OpenChamber desktop app over SSH — the app spawns openchamber itself
# and inherits the pinned environment. No UI password is used.
#
# Run as the user who will own the install. Idempotent: safe to re-run,
# every step checks before acting. No root required.
#
# What it does:
# 1. Installs bun (if missing)
# 2. Creates the node -> bun shim (what makes `env node` work)
# 3. Pins PATH + OPENCHAMBER_RELAY_URL into shell rc files so
# non-interactive SSH sessions (OpenChamber's exec path) inherit them
# 4. Installs opencode (if missing)
# 5. Installs/updates @openchamber/web to latest via bun (every run)
# 6. Syncs ~/.config/opencode/opencode.json from CONFIG_URL
# 7. Verifies: versions + relay health
#
# Flags: --relay-url wss://host/ws (override the default relay endpoint)
# --invite [port] print an openchamber:// invite link (+QR)
# for importing this host into the desktop
# app; starts the instance on <port>
# (default 3000) if none is running
set -euo pipefail
RELAY_URL="wss://code.zuccaro.me/ws"
CONFIG_URL="https://git.zuccaro.me/bzuccaro/opencode-public/raw/branch/main/opencode.json"
INVITE=0
INVITE_PORT=3000
while [ $# -gt 0 ]; do
case "$1" in
--relay-url) RELAY_URL="$2"; shift 2 ;;
--invite)
INVITE=1
if [ $# -ge 2 ] && [ "${2#--}" = "$2" ]; then INVITE_PORT="$2"; shift; fi
shift ;;
*) echo "unknown arg: $1 (usage: $0 [--relay-url wss://host/ws] [--invite [port]])" >&2; exit 1 ;;
esac
done
info() { printf '\033[1;34m[info]\033[0m %s\n' "$*"; }
ok() { printf '\033[1;32m[ ok ]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; }
die() { printf '\033[1;31m[fail]\033[0m %s\n' "$*" >&2; exit 1; }
info "host: $(uname -srm)"
[ "$(uname -s)" = "Linux" ] || die "this script targets Linux hosts"
# ---- preflight -------------------------------------------------------------
for c in curl; do command -v "$c" >/dev/null || die "missing: $c (install it and re-run)"; done
command -v unzip >/dev/null || warn "unzip not found — bun's installer needs it; install unzip first if step 1 fails"
# ---- 1. bun ----------------------------------------------------------------
if command -v bun >/dev/null 2>&1; then
ok "bun already installed: $(bun --version)"
else
info "installing bun..."
curl -fsSL https://bun.sh/install | bash
fi
export PATH="$HOME/.bun/bin:$PATH"
command -v bun >/dev/null 2>&1 || die "bun not on PATH after install"
# ---- 2. node shim ----------------------------------------------------------
if [ -x "$HOME/.bun/bin/node" ]; then
ok "node shim present"
else
ln -sf "$HOME/.bun/bin/bun" "$HOME/.bun/bin/node"
ok "created node -> bun shim"
fi
# ---- 3. rc file pinning ----------------------------------------------------
# .bashrc: prepend to the TOP so non-interactive ssh execs (which source
# .bashrc but return early on Debian-style interactive guards) still get it.
# zsh/profile files: only touched if they already exist.
pin_top() { # pin_top <file> <line>
local f="$1" line="$2"
touch "$f"
if grep -qF "$line" "$f"; then ok "$(basename "$f"): already pinned"; return 0; fi
sed -i "1i $line" "$f"
ok "$(basename "$f"): pinned at top"
}
pin_append() { # pin_append <file> <line>
local f="$1" line="$2"
[ -f "$f" ] || { info "$(basename "$f") does not exist, skipping"; return 0; }
if grep -qF "$line" "$f"; then ok "$(basename "$f"): already pinned"; return 0; fi
printf '%s\n' "$line" >> "$f"
ok "$(basename "$f"): appended"
}
PATH_LINE='export PATH="$HOME/.bun/bin:$PATH"'
RELAY_LINE="export OPENCHAMBER_RELAY_URL=\"$RELAY_URL\""
if grep -q "OPENCHAMBER_RELAY_URL" ~/.bashrc 2>/dev/null; then
info "relay var already pinned in .bashrc (respecting existing value)"
else
pin_top ~/.bashrc "$PATH_LINE"
pin_top ~/.bashrc "$RELAY_LINE"
fi
pin_append ~/.zshenv "$PATH_LINE"
pin_append ~/.zshenv "$RELAY_LINE"
pin_append ~/.zshrc "$PATH_LINE"
pin_append ~/.zshrc "$RELAY_LINE"
pin_append ~/.profile "$PATH_LINE"
pin_append ~/.profile "$RELAY_LINE"
# ---- 4. opencode -----------------------------------------------------------
if command -v opencode >/dev/null 2>&1 || [ -x "$HOME/.opencode/bin/opencode" ]; then
ok "opencode already installed"
else
info "installing opencode..."
curl -fsSL https://opencode.ai/install | bash
fi
[ -x "$HOME/.opencode/bin/opencode" ] && export PATH="$HOME/.opencode/bin:$PATH"
# ---- 5. openchamber --------------------------------------------------------
# Always (re)install the latest published version so re-runs upgrade in place.
info "installing/updating @openchamber/web to latest..."
bun install -g @openchamber/web@latest
ok "openchamber version: $(openchamber --version 2>/dev/null || echo '?')"
# ---- 6. opencode config sync ------------------------------------------------
CFG_DIR="$HOME/.config/opencode"
CFG_FILE="$CFG_DIR/opencode.json"
TMP_CFG="$(mktemp)"
curl -fsSL --max-time 60 "$CONFIG_URL" -o "$TMP_CFG" || die "could not download config from $CONFIG_URL"
if [ -f "$CFG_FILE" ] && cmp -s "$TMP_CFG" "$CFG_FILE"; then
ok "opencode config already up to date"
rm -f "$TMP_CFG"
else
mkdir -p "$CFG_DIR"
if [ -f "$CFG_FILE" ]; then
BAK="$CFG_FILE.bak.$(date +%Y%m%d-%H%M%S)"
cp "$CFG_FILE" "$BAK"
info "existing config backed up to $(basename "$BAK")"
fi
mv "$TMP_CFG" "$CFG_FILE"
ok "opencode config installed from CONFIG_URL"
fi
if command -v python3 >/dev/null 2>&1; then
python3 -m json.tool "$CFG_FILE" >/dev/null && ok "config is valid JSON"
elif command -v bun >/dev/null 2>&1; then
bun -e "JSON.parse(require('fs').readFileSync('$CFG_FILE','utf8'))" && ok "config is valid JSON"
fi
# ---- 7. verify --------------------------------------------------------------
echo
info "==== verification ===="
echo "bun: $(bun --version 2>/dev/null || echo MISSING)"
echo "node shim: $(env node -e 'console.log("exec-ok")' 2>/dev/null || echo BROKEN)"
echo "opencode: $(opencode --version 2>/dev/null || echo MISSING)"
echo "openchamber:$(openchamber --version 2>/dev/null || echo MISSING)"
HEALTH_URL="$(printf '%s' "$RELAY_URL" | sed 's|^wss://|https://|; s|/ws$|/health|')"
if curl -fsS --max-time 15 "$HEALTH_URL" >/dev/null 2>&1; then
ok "relay reachable: $HEALTH_URL"
else
warn "relay health check failed: $HEALTH_URL (may not be deployed yet)"
fi
# ---- 8. invite link (optional) ----------------------------------------------
if [ "$INVITE" = "1" ]; then
info "generating invite link (relay transport embedded)..."
if openchamber connect-url --relay -p "$INVITE_PORT" --qr; then
ok "paste the openchamber:// link into OpenChamber: Settings > Remote Instances > add instance"
else
warn "invite link not generated - an instance may already be running on port $INVITE_PORT (openchamber stop -p $INVITE_PORT, then re-run with --invite)"
fi
fi
echo
ok "prerequisites complete"