Add openchamber-boostrap.sh
This commit is contained in:
1 parent
8493008075
commit
a334f955ec
1 file changed
+176
@@ -0,0 +1,176 @@
|
||||
# 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 @openchamber/web globally via bun (if missing)
|
||||
# 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 --------------------------------------------------------
|
||||
if bun pm ls -g 2>/dev/null | grep -q "@openchamber/web"; then
|
||||
ok "openchamber already installed: $(openchamber --version 2>/dev/null || echo '?')"
|
||||
else
|
||||
info "installing @openchamber/web globally..."
|
||||
bun install -g @openchamber/web
|
||||
fi
|
||||
|
||||
# ---- 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"
|
||||
Reference in new issue
Block a user