Files
wehub-resource-sync 0418dc5cf9
Autopilot Pages / deploy (push) Waiting to run
CI / Python tests (3.10) (push) Waiting to run
CI / Python tests (3.11) (push) Waiting to run
CI / Python quality (push) Waiting to run
CI / Frontend typecheck (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:45:00 +08:00

388 lines
14 KiB
Bash
Executable File

#!/usr/bin/env bash
# OpenHarness one-click installer
# Usage: curl -fsSL https://raw.githubusercontent.com/HKUDS/OpenHarness/main/scripts/install.sh | bash
# bash scripts/install.sh [--from-source] [--with-channels]
set -euo pipefail
# ---------------------------------------------------------------------------
# Colors
# ---------------------------------------------------------------------------
if [ -t 1 ]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' BLUE='' CYAN='' BOLD='' RESET=''
fi
info() { echo -e "${CYAN}[INFO]${RESET} $*"; }
success() { echo -e "${GREEN}[OK]${RESET} $*"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; }
error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
step() { echo -e "\n${BOLD}${BLUE}==>${RESET}${BOLD} $*${RESET}"; }
# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
FROM_SOURCE=false
WITH_CHANNELS=false
for arg in "$@"; do
case "$arg" in
--from-source) FROM_SOURCE=true ;;
--with-channels) WITH_CHANNELS=true ;;
--help|-h)
echo "Usage: $0 [--from-source] [--with-channels]"
echo ""
echo " --from-source Clone from GitHub and install in editable mode"
echo " --with-channels Deprecated compatibility flag."
echo " Common IM channel dependencies are installed by default."
exit 0
;;
*)
error "Unknown argument: $arg"
exit 1
;;
esac
done
# ---------------------------------------------------------------------------
# Banner
# ---------------------------------------------------------------------------
echo ""
echo -e "${BOLD}${CYAN} ██████╗ ██╗ ██╗${RESET}"
echo -e "${BOLD}${CYAN} ██╔═══██╗██║ ██║${RESET}"
echo -e "${BOLD}${CYAN} ██║ ██║███████║${RESET} OpenHarness Installer"
echo -e "${BOLD}${CYAN} ██║ ██║██╔══██║${RESET} Open Agent Harness"
echo -e "${BOLD}${CYAN} ╚██████╔╝██║ ██║${RESET}"
echo -e "${BOLD}${CYAN} ╚═════╝ ╚═╝ ╚═╝${RESET}"
echo ""
# ---------------------------------------------------------------------------
# Step 1: Detect OS
# ---------------------------------------------------------------------------
step "Detecting operating system"
OS_TYPE="unknown"
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
# Check for WSL
if grep -qi microsoft /proc/version 2>/dev/null; then
OS_TYPE="WSL"
else
OS_TYPE="Linux"
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
OS_TYPE="macOS"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
OS_TYPE="Windows (Git Bash)"
fi
info "OS detected: ${BOLD}${OS_TYPE}${RESET}"
# ---------------------------------------------------------------------------
# Step 2: Check Python >= 3.10
# ---------------------------------------------------------------------------
step "Checking Python version (>= 3.10 required)"
PYTHON_CMD=""
for cmd in python3 python; do
if command -v "$cmd" &>/dev/null; then
PY_VER=$("$cmd" --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
PY_MAJOR=$(echo "$PY_VER" | cut -d. -f1)
PY_MINOR=$(echo "$PY_VER" | cut -d. -f2)
if [ "${PY_MAJOR}" -ge 3 ] && [ "${PY_MINOR}" -ge 10 ]; then
PYTHON_CMD="$cmd"
break
fi
fi
done
if [ -z "$PYTHON_CMD" ]; then
error "Python 3.10+ not found."
echo ""
echo " Please install Python 3.10 or newer:"
case "$OS_TYPE" in
macOS)
echo " brew install python@3.12"
echo " or download from: https://www.python.org/downloads/"
;;
Linux|WSL)
echo " sudo apt update && sudo apt install -y python3 python3-pip # Debian/Ubuntu"
echo " sudo dnf install -y python3 # Fedora/RHEL"
echo " or download from: https://www.python.org/downloads/"
;;
*)
echo " Download from: https://www.python.org/downloads/"
;;
esac
echo ""
exit 1
fi
PY_VERSION=$("$PYTHON_CMD" --version 2>&1)
success "Found ${PY_VERSION} (${PYTHON_CMD})"
# Determine pip command
PIP_CMD=""
for cmd in pip3 pip; do
if command -v "$cmd" &>/dev/null; then
PIP_CMD="$cmd"
break
fi
done
if [ -z "$PIP_CMD" ]; then
# Try python -m pip
if "$PYTHON_CMD" -m pip --version &>/dev/null 2>&1; then
PIP_CMD="$PYTHON_CMD -m pip"
else
error "pip not found. Please install pip:"
echo " $PYTHON_CMD -m ensurepip --upgrade"
exit 1
fi
fi
# ---------------------------------------------------------------------------
# Step 3: Check Node.js >= 18
# ---------------------------------------------------------------------------
step "Checking Node.js version (>= 18 required for React TUI)"
NODE_OK=false
if command -v node &>/dev/null; then
NODE_VER=$(node --version 2>&1 | grep -oE '[0-9]+' | head -1)
if [ "${NODE_VER}" -ge 18 ] 2>/dev/null; then
NODE_OK=true
success "Found Node.js $(node --version)"
else
warn "Node.js $(node --version) is too old (need >= 18). React TUI will be skipped."
fi
else
warn "Node.js not found. React TUI will be skipped."
echo " To enable the React terminal UI, install Node.js 18+:"
case "$OS_TYPE" in
macOS)
echo " brew install node"
;;
Linux|WSL)
echo " curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -"
echo " sudo apt install -y nodejs"
;;
*)
echo " Download from: https://nodejs.org/"
;;
esac
fi
# ---------------------------------------------------------------------------
# Step 4: Install OpenHarness
# ---------------------------------------------------------------------------
step "Installing OpenHarness"
REPO_URL="https://github.com/HKUDS/OpenHarness.git"
INSTALL_DIR="$HOME/.openharness-src"
VENV_DIR="$HOME/.openharness-venv"
BIN_DIR="$HOME/.local/bin"
# ---------------------------------------------------------------------------
# Create a virtual environment to avoid PEP 668 externally-managed errors
# ---------------------------------------------------------------------------
if [ -d "$VENV_DIR" ] && [ ! -f "$VENV_DIR/bin/activate" ]; then
warn "Found incomplete virtual environment at ${VENV_DIR}; recreating it..."
rm -rf "$VENV_DIR"
fi
if [ ! -f "$VENV_DIR/bin/activate" ]; then
info "Creating virtual environment at ${VENV_DIR}..."
"$PYTHON_CMD" -m venv "$VENV_DIR"
fi
# Activate the venv — all pip installs go here
source "$VENV_DIR/bin/activate"
PYTHON_CMD="python"
PIP_CMD="pip"
success "Virtual environment ready: ${VENV_DIR}"
if [ "$FROM_SOURCE" = true ]; then
info "Mode: --from-source (git clone + pip install -e .)"
if command -v git &>/dev/null; then
if [ -d "$INSTALL_DIR/.git" ]; then
info "Source directory exists, pulling latest changes..."
git -C "$INSTALL_DIR" pull --ff-only
else
info "Cloning OpenHarness into ${INSTALL_DIR}..."
git clone "$REPO_URL" "$INSTALL_DIR"
fi
else
error "git is required for --from-source installation."
echo " Install git and retry:"
case "$OS_TYPE" in
macOS) echo " brew install git" ;;
Linux|WSL) echo " sudo apt install -y git" ;;
esac
exit 1
fi
info "Installing in editable mode (pip install -e .)..."
$PIP_CMD install -e "$INSTALL_DIR" --quiet
else
info "Mode: pip install openharness-ai"
$PIP_CMD install openharness-ai --quiet --upgrade
fi
success "OpenHarness package installed"
# ---------------------------------------------------------------------------
# Step 5: Channel dependencies
# ---------------------------------------------------------------------------
if [ "$WITH_CHANNELS" = true ]; then
step "Channel dependencies"
info "--with-channels is no longer required; common IM channel dependencies are installed by default."
fi
# ---------------------------------------------------------------------------
# Step 6: Install frontend/terminal npm dependencies
# ---------------------------------------------------------------------------
if [ "$NODE_OK" = true ]; then
# Determine the frontend/terminal path
if [ "$FROM_SOURCE" = true ]; then
FRONTEND_DIR="$INSTALL_DIR/frontend/terminal"
else
FRONTEND_DIR="$(pwd)/frontend/terminal"
fi
if [ -d "$FRONTEND_DIR" ] && [ -f "$FRONTEND_DIR/package.json" ]; then
step "Installing React TUI dependencies"
info "Running npm install in ${FRONTEND_DIR}..."
(cd "$FRONTEND_DIR" && npm install --no-fund --no-audit --silent)
success "React TUI dependencies installed"
else
info "No frontend/terminal directory found — skipping npm install"
fi
fi
# ---------------------------------------------------------------------------
# Step 7: Create OpenHarness config directory
# ---------------------------------------------------------------------------
step "Setting up OpenHarness config directory"
mkdir -p "$HOME/.openharness"
mkdir -p "$HOME/.openharness/skills"
mkdir -p "$HOME/.openharness/plugins"
success "Config directory ready: ~/.openharness/"
# ---------------------------------------------------------------------------
# Step 8: Register global commands
# ---------------------------------------------------------------------------
step "Registering global commands"
mkdir -p "$BIN_DIR"
ln -snf "$VENV_DIR/bin/oh" "$BIN_DIR/oh"
ln -snf "$VENV_DIR/bin/ohmo" "$BIN_DIR/ohmo"
ln -snf "$VENV_DIR/bin/openharness" "$BIN_DIR/openharness"
success "Linked oh/ohmo into ${BIN_DIR}"
# ---------------------------------------------------------------------------
# Step 9: Verify installation
# ---------------------------------------------------------------------------
step "Verifying installation"
if [ -x "$BIN_DIR/oh" ] && [ -x "$BIN_DIR/ohmo" ]; then
OH_VERSION=$("$BIN_DIR/oh" --version 2>&1 || echo "(version check failed)")
OHMO_VERSION=$("$BIN_DIR/ohmo" --help >/dev/null 2>&1 && echo "available" || echo "not available")
success "Installation successful!"
echo ""
echo -e " ${BOLD}oh${RESET} is ready: ${GREEN}${OH_VERSION}${RESET}"
echo -e " ${BOLD}ohmo${RESET} is ready: ${GREEN}${OHMO_VERSION}${RESET}"
elif "$PYTHON_CMD" -m openharness --version &>/dev/null 2>&1; then
OH_VERSION=$("$PYTHON_CMD" -m openharness --version 2>&1)
warn "'oh'/'ohmo' command links are not executable yet. Run via: python -m openharness or python -m ohmo"
echo " Version: ${OH_VERSION}"
echo " To add them to PATH, ensure ${BIN_DIR} is in PATH:"
echo " export PATH=\"${BIN_DIR}:\$PATH\""
else
warn "Could not verify 'oh'/'ohmo' commands. The package may need a PATH update."
echo " Try: $PYTHON_CMD -m openharness --version"
echo " Or add ${BIN_DIR} to PATH and restart your shell."
fi
# ---------------------------------------------------------------------------
# Step 10: Add command directory to shell profile
# ---------------------------------------------------------------------------
step "Setting up shell integration"
ACTIVATION_LINE="export PATH=\"$BIN_DIR:\$PATH\""
FISH_CONFIG="$HOME/.config/fish/config.fish"
FISH_BLOCK=$(cat <<EOF
# OpenHarness
if not contains -- "$BIN_DIR" \$PATH
set -gx PATH "$BIN_DIR" \$PATH
end
EOF
)
configured_any=false
append_shell_path() {
local rc_file="$1"
if [ ! -f "$rc_file" ]; then
return
fi
if grep -q "$BIN_DIR" "$rc_file" 2>/dev/null; then
info "PATH already configured in $(basename "$rc_file")"
configured_any=true
return
fi
echo "" >> "$rc_file"
echo "# OpenHarness" >> "$rc_file"
echo "$ACTIVATION_LINE" >> "$rc_file"
success "Added $BIN_DIR to PATH in $(basename "$rc_file")"
configured_any=true
}
append_shell_path "$HOME/.zshrc"
append_shell_path "$HOME/.bashrc"
append_shell_path "$HOME/.bash_profile"
mkdir -p "$(dirname "$FISH_CONFIG")"
if [ -f "$FISH_CONFIG" ] && grep -q "$BIN_DIR" "$FISH_CONFIG" 2>/dev/null; then
info "PATH already configured in $(basename "$FISH_CONFIG")"
configured_any=true
else
echo "" >> "$FISH_CONFIG"
printf "%s\n" "$FISH_BLOCK" >> "$FISH_CONFIG"
success "Added $BIN_DIR to PATH in $(basename "$FISH_CONFIG")"
configured_any=true
fi
if [ "$configured_any" = false ]; then
warn "Could not find shell config file. Add this to your shell profile:"
echo " $ACTIVATION_LINE"
fi
# ---------------------------------------------------------------------------
# Done
# ---------------------------------------------------------------------------
echo ""
echo -e "${BOLD}${GREEN}OpenHarness is installed!${RESET}"
echo ""
echo " Next steps:"
echo " 1. Restart shell, or reload your shell config:"
echo " bash/zsh: source ~/.bashrc (or ~/.zshrc)"
echo " fish: source ~/.config/fish/config.fish"
echo " 2. Set your API key: export ANTHROPIC_API_KEY=your_key"
echo " 3. Launch: oh"
echo " 4. Launch ohmo: ohmo"
echo " 5. Docs: https://github.com/HKUDS/OpenHarness"
echo ""
echo " Notes:"
echo " - Commands are linked into: ${BIN_DIR}"
echo " - The virtual environment remains at: ${VENV_DIR}"
echo ""