MAESTRO: add openclaw/install.sh script foundation with banner, platform detection, and dependency management
Creates the core installer script with: - ASCII banner and ANSI color utility functions (info/success/warn/error/prompt_user) - Automatic terminal color support detection - Platform detection (macOS, Linux, WSL, Windows/MINGW) - Bun detection, version checking (>=1.1.14), and auto-installation - uv detection and auto-installation - find_bun_path() helper returning full path to bun binary - --non-interactive flag for curl|bash piping safety - All dependency patterns translated from plugin/scripts/smart-install.js Passes bash -n syntax check and shellcheck with zero warnings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Executable
+296
@@ -0,0 +1,296 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# claude-mem OpenClaw Plugin Installer
|
||||||
|
# Installs the claude-mem persistent memory plugin for OpenClaw gateways.
|
||||||
|
# Usage: bash install.sh [--non-interactive]
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Constants
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
readonly MIN_BUN_VERSION="1.1.14"
|
||||||
|
readonly INSTALLER_VERSION="1.0.0"
|
||||||
|
readonly NON_INTERACTIVE="${1:-}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Color utilities — auto-detect terminal color support
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
if [[ -t 1 ]] && [[ "${TERM:-}" != "dumb" ]]; then
|
||||||
|
readonly COLOR_RED='\033[0;31m'
|
||||||
|
readonly COLOR_GREEN='\033[0;32m'
|
||||||
|
readonly COLOR_YELLOW='\033[0;33m'
|
||||||
|
readonly COLOR_BLUE='\033[0;34m'
|
||||||
|
readonly COLOR_MAGENTA='\033[0;35m'
|
||||||
|
readonly COLOR_CYAN='\033[0;36m'
|
||||||
|
readonly COLOR_BOLD='\033[1m'
|
||||||
|
readonly COLOR_RESET='\033[0m'
|
||||||
|
else
|
||||||
|
readonly COLOR_RED=''
|
||||||
|
readonly COLOR_GREEN=''
|
||||||
|
readonly COLOR_YELLOW=''
|
||||||
|
readonly COLOR_BLUE=''
|
||||||
|
readonly COLOR_MAGENTA=''
|
||||||
|
readonly COLOR_CYAN=''
|
||||||
|
readonly COLOR_BOLD=''
|
||||||
|
readonly COLOR_RESET=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
info() { echo -e "${COLOR_BLUE}ℹ${COLOR_RESET} $*"; }
|
||||||
|
success() { echo -e "${COLOR_GREEN}✓${COLOR_RESET} $*"; }
|
||||||
|
warn() { echo -e "${COLOR_YELLOW}⚠${COLOR_RESET} $*"; }
|
||||||
|
error() { echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2; }
|
||||||
|
|
||||||
|
prompt_user() {
|
||||||
|
if [[ "$NON_INTERACTIVE" == "--non-interactive" ]]; then
|
||||||
|
error "Cannot prompt in non-interactive mode: $*"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ ! -t 0 ]]; then
|
||||||
|
error "Cannot prompt when stdin is not a terminal: $*"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo -en "${COLOR_CYAN}?${COLOR_RESET} $* "
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Banner
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
print_banner() {
|
||||||
|
echo -e "${COLOR_MAGENTA}${COLOR_BOLD}"
|
||||||
|
cat << 'BANNER'
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ claude-mem × OpenClaw │
|
||||||
|
│ Persistent Memory Plugin Installer │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
BANNER
|
||||||
|
echo -e "${COLOR_RESET}"
|
||||||
|
info "Installer v${INSTALLER_VERSION}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Platform detection
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
PLATFORM=""
|
||||||
|
IS_WSL=false
|
||||||
|
|
||||||
|
detect_platform() {
|
||||||
|
local uname_out
|
||||||
|
uname_out="$(uname -s)"
|
||||||
|
|
||||||
|
case "${uname_out}" in
|
||||||
|
Darwin*)
|
||||||
|
PLATFORM="macos"
|
||||||
|
;;
|
||||||
|
Linux*)
|
||||||
|
if grep -qi microsoft /proc/version 2>/dev/null; then
|
||||||
|
PLATFORM="linux"
|
||||||
|
IS_WSL=true
|
||||||
|
else
|
||||||
|
PLATFORM="linux"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*)
|
||||||
|
PLATFORM="windows"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unsupported platform: ${uname_out}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
info "Detected platform: ${PLATFORM}${IS_WSL:+ (WSL)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Version comparison — returns 0 if $1 >= $2
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
version_gte() {
|
||||||
|
local v1="$1" v2="$2"
|
||||||
|
local -a parts1 parts2
|
||||||
|
IFS='.' read -ra parts1 <<< "$v1"
|
||||||
|
IFS='.' read -ra parts2 <<< "$v2"
|
||||||
|
|
||||||
|
for i in 0 1 2; do
|
||||||
|
local p1="${parts1[$i]:-0}"
|
||||||
|
local p2="${parts2[$i]:-0}"
|
||||||
|
if (( p1 > p2 )); then return 0; fi
|
||||||
|
if (( p1 < p2 )); then return 1; fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Bun detection and installation
|
||||||
|
# Translated from plugin/scripts/smart-install.js patterns
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
BUN_PATH=""
|
||||||
|
|
||||||
|
find_bun_path() {
|
||||||
|
# Try PATH first
|
||||||
|
if command -v bun &>/dev/null; then
|
||||||
|
BUN_PATH="$(command -v bun)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check common installation paths (handles fresh installs before PATH reload)
|
||||||
|
local -a bun_paths=(
|
||||||
|
"${HOME}/.bun/bin/bun"
|
||||||
|
"/usr/local/bin/bun"
|
||||||
|
"/opt/homebrew/bin/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
for candidate in "${bun_paths[@]}"; do
|
||||||
|
if [[ -x "$candidate" ]]; then
|
||||||
|
BUN_PATH="$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
BUN_PATH=""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_bun() {
|
||||||
|
if ! find_bun_path; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify minimum version
|
||||||
|
local bun_version
|
||||||
|
bun_version="$("$BUN_PATH" --version 2>/dev/null)" || return 1
|
||||||
|
|
||||||
|
if version_gte "$bun_version" "$MIN_BUN_VERSION"; then
|
||||||
|
success "Bun ${bun_version} found at ${BUN_PATH}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
warn "Bun ${bun_version} is below minimum required version ${MIN_BUN_VERSION}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_bun() {
|
||||||
|
info "Installing Bun runtime..."
|
||||||
|
|
||||||
|
if ! curl -fsSL https://bun.sh/install | bash; then
|
||||||
|
error "Failed to install Bun automatically"
|
||||||
|
error "Please install manually:"
|
||||||
|
error " curl -fsSL https://bun.sh/install | bash"
|
||||||
|
error " Or: brew install oven-sh/bun/bun (macOS)"
|
||||||
|
error "Then restart your terminal and re-run this installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Re-detect after install (installer may have placed it in ~/.bun/bin)
|
||||||
|
if ! find_bun_path; then
|
||||||
|
error "Bun installation completed but binary not found in expected locations"
|
||||||
|
error "Please restart your terminal and re-run this installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local bun_version
|
||||||
|
bun_version="$("$BUN_PATH" --version 2>/dev/null)" || true
|
||||||
|
success "Bun ${bun_version} installed at ${BUN_PATH}"
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# uv detection and installation
|
||||||
|
# Translated from plugin/scripts/smart-install.js patterns
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
UV_PATH=""
|
||||||
|
|
||||||
|
find_uv_path() {
|
||||||
|
# Try PATH first
|
||||||
|
if command -v uv &>/dev/null; then
|
||||||
|
UV_PATH="$(command -v uv)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check common installation paths (handles fresh installs before PATH reload)
|
||||||
|
local -a uv_paths=(
|
||||||
|
"${HOME}/.local/bin/uv"
|
||||||
|
"${HOME}/.cargo/bin/uv"
|
||||||
|
"/usr/local/bin/uv"
|
||||||
|
"/opt/homebrew/bin/uv"
|
||||||
|
)
|
||||||
|
|
||||||
|
for candidate in "${uv_paths[@]}"; do
|
||||||
|
if [[ -x "$candidate" ]]; then
|
||||||
|
UV_PATH="$candidate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
UV_PATH=""
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
check_uv() {
|
||||||
|
if ! find_uv_path; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local uv_version
|
||||||
|
uv_version="$("$UV_PATH" --version 2>/dev/null)" || return 1
|
||||||
|
success "uv ${uv_version} found at ${UV_PATH}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
install_uv() {
|
||||||
|
info "Installing uv (Python package manager for Chroma support)..."
|
||||||
|
|
||||||
|
if ! curl -LsSf https://astral.sh/uv/install.sh | sh; then
|
||||||
|
error "Failed to install uv automatically"
|
||||||
|
error "Please install manually:"
|
||||||
|
error " curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||||
|
error " Or: brew install uv (macOS)"
|
||||||
|
error "Then restart your terminal and re-run this installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Re-detect after install
|
||||||
|
if ! find_uv_path; then
|
||||||
|
error "uv installation completed but binary not found in expected locations"
|
||||||
|
error "Please restart your terminal and re-run this installer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local uv_version
|
||||||
|
uv_version="$("$UV_PATH" --version 2>/dev/null)" || true
|
||||||
|
success "uv ${uv_version} installed at ${UV_PATH}"
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Main
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
main() {
|
||||||
|
print_banner
|
||||||
|
detect_platform
|
||||||
|
|
||||||
|
# --- Step 1: Bun ---
|
||||||
|
echo ""
|
||||||
|
info "Checking dependencies..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if ! check_bun; then
|
||||||
|
install_bun
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Step 2: uv ---
|
||||||
|
if ! check_uv; then
|
||||||
|
install_uv
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
success "All dependencies satisfied"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user