2025-12-19 19:57:18 +02:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# OpenChamber Install Script
|
|
|
|
|
# Usage: curl -fsSL https://raw.githubusercontent.com/btriapitsyn/openchamber/main/scripts/install.sh | bash
|
|
|
|
|
|
2026-06-17 01:41:57 +03:00
|
|
|
set -euo pipefail
|
2025-12-19 19:57:18 +02:00
|
|
|
|
|
|
|
|
PACKAGE_NAME="@openchamber/web"
|
2026-06-17 01:41:57 +03:00
|
|
|
BIN_NAME="openchamber"
|
|
|
|
|
MIN_NODE_VERSION=22
|
2025-12-19 19:57:18 +02:00
|
|
|
|
|
|
|
|
# Colors
|
|
|
|
|
RED='\033[0;31m'
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
YELLOW='\033[1;33m'
|
|
|
|
|
BLUE='\033[0;34m'
|
|
|
|
|
NC='\033[0m' # No Color
|
|
|
|
|
|
|
|
|
|
info() {
|
|
|
|
|
echo -e "${BLUE}info${NC} $1"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
success() {
|
|
|
|
|
echo -e "${GREEN}success${NC} $1"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
warn() {
|
|
|
|
|
echo -e "${YELLOW}warn${NC} $1"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error() {
|
|
|
|
|
echo -e "${RED}error${NC} $1"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Check if a command exists
|
|
|
|
|
command_exists() {
|
|
|
|
|
command -v "$1" >/dev/null 2>&1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Get Node.js major version
|
|
|
|
|
get_node_version() {
|
|
|
|
|
if command_exists node; then
|
2026-06-17 01:41:57 +03:00
|
|
|
local version
|
|
|
|
|
version=$(node -v 2>/dev/null || true)
|
|
|
|
|
version=${version#v}
|
|
|
|
|
version=${version%%.*}
|
|
|
|
|
if [[ "$version" =~ ^[0-9]+$ ]]; then
|
|
|
|
|
echo "$version"
|
|
|
|
|
else
|
|
|
|
|
echo "0"
|
|
|
|
|
fi
|
2025-12-19 19:57:18 +02:00
|
|
|
else
|
|
|
|
|
echo "0"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Detect preferred package manager
|
|
|
|
|
detect_package_manager() {
|
|
|
|
|
# Check if running inside an npm/pnpm/yarn/bun context
|
2026-06-17 01:41:57 +03:00
|
|
|
if [ -n "${npm_config_user_agent:-}" ]; then
|
2025-12-19 19:57:18 +02:00
|
|
|
case "$npm_config_user_agent" in
|
|
|
|
|
pnpm*) echo "pnpm"; return ;;
|
|
|
|
|
yarn*) echo "yarn"; return ;;
|
|
|
|
|
bun*) echo "bun"; return ;;
|
|
|
|
|
npm*) echo "npm"; return ;;
|
|
|
|
|
esac
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check for lockfiles in current directory (user preference)
|
|
|
|
|
if [ -f "pnpm-lock.yaml" ]; then
|
|
|
|
|
echo "pnpm"; return
|
|
|
|
|
elif [ -f "yarn.lock" ]; then
|
|
|
|
|
echo "yarn"; return
|
2026-06-17 01:41:57 +03:00
|
|
|
elif [ -f "bun.lock" ] || [ -f "bun.lockb" ]; then
|
2025-12-19 19:57:18 +02:00
|
|
|
echo "bun"; return
|
|
|
|
|
elif [ -f "package-lock.json" ]; then
|
|
|
|
|
echo "npm"; return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Check which package managers are available (prefer pnpm > bun > yarn > npm)
|
|
|
|
|
if command_exists pnpm; then
|
|
|
|
|
echo "pnpm"
|
|
|
|
|
elif command_exists bun; then
|
|
|
|
|
echo "bun"
|
|
|
|
|
elif command_exists yarn; then
|
|
|
|
|
echo "yarn"
|
|
|
|
|
elif command_exists npm; then
|
|
|
|
|
echo "npm"
|
|
|
|
|
else
|
|
|
|
|
echo "none"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Get install command for package manager
|
|
|
|
|
get_install_command() {
|
|
|
|
|
local pm=$1
|
|
|
|
|
case "$pm" in
|
|
|
|
|
pnpm) echo "pnpm add -g $PACKAGE_NAME" ;;
|
|
|
|
|
yarn) echo "yarn global add $PACKAGE_NAME" ;;
|
|
|
|
|
bun) echo "bun add -g $PACKAGE_NAME" ;;
|
|
|
|
|
npm) echo "npm install -g $PACKAGE_NAME" ;;
|
|
|
|
|
*) echo "" ;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Install Node.js suggestion
|
|
|
|
|
suggest_node_install() {
|
|
|
|
|
echo ""
|
|
|
|
|
error "Node.js $MIN_NODE_VERSION+ is required but not found."
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Install Node.js using one of these methods:"
|
|
|
|
|
echo ""
|
|
|
|
|
|
2026-06-17 01:41:57 +03:00
|
|
|
if [[ "${OSTYPE:-}" == "darwin"* ]]; then
|
2025-12-19 19:57:18 +02:00
|
|
|
echo " Using Homebrew:"
|
|
|
|
|
echo " brew install node"
|
|
|
|
|
echo ""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo " Using nvm (recommended):"
|
|
|
|
|
echo " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
|
|
|
|
|
echo " nvm install $MIN_NODE_VERSION"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " Using fnm:"
|
|
|
|
|
echo " curl -fsSL https://fnm.vercel.app/install | bash"
|
|
|
|
|
echo " fnm install $MIN_NODE_VERSION"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " Official installer:"
|
|
|
|
|
echo " https://nodejs.org/"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Install package manager suggestion
|
|
|
|
|
suggest_pm_install() {
|
|
|
|
|
echo ""
|
|
|
|
|
error "No package manager found (npm, pnpm, yarn, or bun)."
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Install a package manager:"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " npm (comes with Node.js):"
|
|
|
|
|
echo " Install Node.js from https://nodejs.org/"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " pnpm (recommended):"
|
|
|
|
|
echo " curl -fsSL https://get.pnpm.io/install.sh | sh -"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " bun:"
|
|
|
|
|
echo " curl -fsSL https://bun.sh/install | bash"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " yarn:"
|
|
|
|
|
echo " npm install -g yarn"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main() {
|
|
|
|
|
echo ""
|
|
|
|
|
echo " ╭───────────────────────────────────╮"
|
|
|
|
|
echo " │ │"
|
|
|
|
|
echo " │ OpenChamber Installer │"
|
|
|
|
|
echo " │ Web interface for OpenCode │"
|
|
|
|
|
echo " │ │"
|
|
|
|
|
echo " ╰───────────────────────────────────╯"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
# Check Node.js
|
|
|
|
|
info "Checking Node.js..."
|
|
|
|
|
NODE_VERSION=$(get_node_version)
|
2026-06-17 01:41:57 +03:00
|
|
|
|
2025-12-19 19:57:18 +02:00
|
|
|
if [ "$NODE_VERSION" -lt "$MIN_NODE_VERSION" ]; then
|
|
|
|
|
if [ "$NODE_VERSION" -eq "0" ]; then
|
|
|
|
|
suggest_node_install
|
|
|
|
|
else
|
|
|
|
|
error "Node.js $MIN_NODE_VERSION+ required, found v$NODE_VERSION"
|
|
|
|
|
suggest_node_install
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
success "Node.js v$NODE_VERSION found"
|
|
|
|
|
|
2026-06-17 01:41:57 +03:00
|
|
|
# If OpenChamber is already installed, hand off to its own updater instead
|
|
|
|
|
# of guessing a package manager. `openchamber update` detects which manager
|
|
|
|
|
# actually owns the existing global install and reinstalls with that one —
|
|
|
|
|
# reinstalling with a different manager here would orphan files and break PATH.
|
|
|
|
|
if command_exists "$BIN_NAME"; then
|
|
|
|
|
info "OpenChamber is already installed — updating via 'openchamber update'..."
|
|
|
|
|
echo ""
|
|
|
|
|
if openchamber update; then
|
|
|
|
|
echo ""
|
|
|
|
|
success "OpenChamber is up to date!"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
echo ""
|
|
|
|
|
error "Update failed."
|
|
|
|
|
echo ""
|
|
|
|
|
echo " Try running it manually:"
|
|
|
|
|
echo " openchamber update"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2025-12-19 19:57:18 +02:00
|
|
|
# Detect package manager
|
|
|
|
|
info "Detecting package manager..."
|
|
|
|
|
PM=$(detect_package_manager)
|
|
|
|
|
|
|
|
|
|
if [ "$PM" = "none" ]; then
|
|
|
|
|
suggest_pm_install
|
|
|
|
|
fi
|
|
|
|
|
success "Using $PM"
|
|
|
|
|
|
|
|
|
|
# Get install command
|
|
|
|
|
INSTALL_CMD=$(get_install_command "$PM")
|
|
|
|
|
|
|
|
|
|
if [ -z "$INSTALL_CMD" ]; then
|
|
|
|
|
error "Could not determine install command"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Install
|
|
|
|
|
echo ""
|
|
|
|
|
info "Installing OpenChamber..."
|
|
|
|
|
echo " Running: $INSTALL_CMD"
|
|
|
|
|
echo ""
|
|
|
|
|
|
|
|
|
|
if eval "$INSTALL_CMD"; then
|
|
|
|
|
echo ""
|
2026-06-17 01:41:57 +03:00
|
|
|
# Wordmark (toilet "pagga", "Open"/"Chamber" stacked).
|
|
|
|
|
# Hardcoded so the user needs no extra tools.
|
|
|
|
|
printf '%b' "$BLUE"
|
|
|
|
|
cat <<'EOF'
|
|
|
|
|
░█▀█░█▀█░█▀▀░█▀█
|
|
|
|
|
░█░█░█▀▀░█▀▀░█░█
|
|
|
|
|
░▀▀▀░▀░░░▀▀▀░▀░▀
|
|
|
|
|
░█▀▀░█░█░█▀█░█▄█░█▀▄░█▀▀░█▀▄
|
|
|
|
|
░█░░░█▀█░█▀█░█░█░█▀▄░█▀▀░█▀▄
|
|
|
|
|
░▀▀▀░▀░▀░▀░▀░▀░▀░▀▀░░▀▀▀░▀░▀
|
|
|
|
|
EOF
|
|
|
|
|
printf '%b\n' "$NC"
|
2025-12-19 19:57:18 +02:00
|
|
|
success "OpenChamber installed successfully!"
|
|
|
|
|
echo ""
|
2026-06-17 01:41:57 +03:00
|
|
|
|
|
|
|
|
# Verify the binary is actually reachable. Global installs frequently
|
|
|
|
|
# land in a directory that isn't on PATH — surface that instead of
|
|
|
|
|
# letting the user hit a confusing "command not found".
|
|
|
|
|
if command_exists "$BIN_NAME"; then
|
|
|
|
|
echo " Get started:"
|
|
|
|
|
echo " openchamber # Start server on port 3000"
|
|
|
|
|
echo " openchamber --help # Show all options"
|
|
|
|
|
else
|
|
|
|
|
warn "'$BIN_NAME' was installed but isn't on your PATH yet."
|
|
|
|
|
echo ""
|
|
|
|
|
bin_dir=""
|
|
|
|
|
case "$PM" in
|
|
|
|
|
npm) bin_dir=$(npm prefix -g 2>/dev/null)/bin ;;
|
|
|
|
|
pnpm) bin_dir=$(pnpm bin -g 2>/dev/null || true) ;;
|
|
|
|
|
yarn) bin_dir=$(yarn global bin 2>/dev/null || true) ;;
|
|
|
|
|
bun) bin_dir="${BUN_INSTALL:-$HOME/.bun}/bin" ;;
|
|
|
|
|
esac
|
|
|
|
|
if [ -n "$bin_dir" ]; then
|
|
|
|
|
echo " Add it to your PATH (then restart your terminal):"
|
|
|
|
|
echo " export PATH=\"$bin_dir:\$PATH\""
|
|
|
|
|
else
|
|
|
|
|
echo " Add your package manager's global bin directory to PATH,"
|
|
|
|
|
echo " then restart your terminal and run: openchamber"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2025-12-19 19:57:18 +02:00
|
|
|
echo ""
|
|
|
|
|
echo " Prerequisites:"
|
|
|
|
|
echo " Make sure OpenCode is running: opencode serve"
|
|
|
|
|
echo ""
|
|
|
|
|
else
|
|
|
|
|
echo ""
|
|
|
|
|
error "Installation failed"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " Try running manually:"
|
|
|
|
|
echo " $INSTALL_CMD"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " If you get permission errors, see:"
|
|
|
|
|
echo " https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main "$@"
|