Skip to content

Commit b84f2aa

Browse files
committed
feat: auto-install Node.js when missing in hermes install script
If Node.js >= 18 is not found, the installer now automatically installs Node.js 22 via Homebrew (macOS) or NodeSource (Linux apt/dnf/yum) instead of just printing an error and exiting. Also adds colored output for better readability during installation.
1 parent b2d71b4 commit b84f2aa

1 file changed

Lines changed: 162 additions & 35 deletions

File tree

apps/memos-local-plugin/adapters/hermes/install.sh

Lines changed: 162 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,146 @@ set -euo pipefail
77
# bash install.sh [/path/to/hermes-agent]
88
#
99
# Prerequisites:
10-
# - Node.js >= 18
1110
# - hermes-agent repository cloned locally
11+
# - Node.js >= 18 (auto-installed if missing)
12+
13+
GREEN='\033[0;32m'
14+
BLUE='\033[0;34m'
15+
YELLOW='\033[1;33m'
16+
RED='\033[0;31m'
17+
BOLD='\033[1m'
18+
NC='\033[0m'
19+
20+
info() { echo -e "${BLUE}$1${NC}"; }
21+
success() { echo -e "${GREEN}$1${NC}"; }
22+
warn() { echo -e "${YELLOW}$1${NC}"; }
23+
error() { echo -e "${RED}$1${NC}"; }
24+
25+
# ─── Node.js auto-install helpers ───
26+
27+
node_major_version() {
28+
if ! command -v node >/dev/null 2>&1; then
29+
echo "0"
30+
return 0
31+
fi
32+
local node_version
33+
node_version="$(node -v 2>/dev/null || true)"
34+
node_version="${node_version#v}"
35+
echo "${node_version%%.*}"
36+
}
37+
38+
run_with_privilege() {
39+
if [[ "$(id -u)" -eq 0 ]]; then
40+
"$@"
41+
else
42+
sudo "$@"
43+
fi
44+
}
45+
46+
download_to_file() {
47+
local url="$1"
48+
local output="$2"
49+
if command -v curl >/dev/null 2>&1; then
50+
curl -fsSL --proto '=https' --tlsv1.2 "$url" -o "$output"
51+
return 0
52+
fi
53+
if command -v wget >/dev/null 2>&1; then
54+
wget -q --https-only --secure-protocol=TLSv1_2 "$url" -O "$output"
55+
return 0
56+
fi
57+
return 1
58+
}
59+
60+
install_node22() {
61+
local os_name
62+
os_name="$(uname -s)"
63+
64+
if [[ "$os_name" == "Darwin" ]]; then
65+
if ! command -v brew >/dev/null 2>&1; then
66+
error "Homebrew is required to auto-install Node.js on macOS"
67+
error "Install Homebrew first: https://brew.sh"
68+
exit 1
69+
fi
70+
info "Auto-installing Node.js 22 via Homebrew..."
71+
brew install node@22 >/dev/null
72+
brew link node@22 --overwrite --force >/dev/null 2>&1 || true
73+
local brew_node_prefix
74+
brew_node_prefix="$(brew --prefix node@22 2>/dev/null || true)"
75+
if [[ -n "$brew_node_prefix" && -x "${brew_node_prefix}/bin/node" ]]; then
76+
export PATH="${brew_node_prefix}/bin:${PATH}"
77+
fi
78+
return 0
79+
fi
80+
81+
if [[ "$os_name" == "Linux" ]]; then
82+
info "Auto-installing Node.js 22 on Linux..."
83+
local tmp_script
84+
tmp_script="$(mktemp)"
85+
if command -v apt-get >/dev/null 2>&1; then
86+
if ! download_to_file "https://deb.nodesource.com/setup_22.x" "$tmp_script"; then
87+
error "Failed to download NodeSource setup script"
88+
rm -f "$tmp_script"
89+
exit 1
90+
fi
91+
run_with_privilege bash "$tmp_script"
92+
run_with_privilege apt-get update -qq
93+
run_with_privilege apt-get install -y -qq nodejs
94+
rm -f "$tmp_script"
95+
return 0
96+
fi
97+
if command -v dnf >/dev/null 2>&1; then
98+
if ! download_to_file "https://rpm.nodesource.com/setup_22.x" "$tmp_script"; then
99+
error "Failed to download NodeSource setup script"
100+
rm -f "$tmp_script"
101+
exit 1
102+
fi
103+
run_with_privilege bash "$tmp_script"
104+
run_with_privilege dnf install -y -q nodejs
105+
rm -f "$tmp_script"
106+
return 0
107+
fi
108+
if command -v yum >/dev/null 2>&1; then
109+
if ! download_to_file "https://rpm.nodesource.com/setup_22.x" "$tmp_script"; then
110+
error "Failed to download NodeSource setup script"
111+
rm -f "$tmp_script"
112+
exit 1
113+
fi
114+
run_with_privilege bash "$tmp_script"
115+
run_with_privilege yum install -y -q nodejs
116+
rm -f "$tmp_script"
117+
return 0
118+
fi
119+
rm -f "$tmp_script"
120+
fi
121+
122+
error "Unsupported platform for auto-install. Please install Node.js >= 18 manually."
123+
exit 1
124+
}
125+
126+
ensure_node() {
127+
local required_major=18
128+
local current_major
129+
current_major="$(node_major_version)"
130+
131+
if [[ "$current_major" =~ ^[0-9]+$ ]] && (( current_major >= required_major )); then
132+
success "✓ Node.js $(node -v)"
133+
return 0
134+
fi
135+
136+
warn "Node.js >= ${required_major} is required but not found. Auto-installing..."
137+
install_node22
138+
139+
current_major="$(node_major_version)"
140+
if [[ "$current_major" =~ ^[0-9]+$ ]] && (( current_major >= required_major )); then
141+
success "✓ Node.js installed: $(node -v)"
142+
return 0
143+
fi
144+
145+
error "Node.js installation failed — still below >= ${required_major}."
146+
exit 1
147+
}
148+
149+
# ─── Main ───
12150

13151
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
14152
MEMOS_PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
@@ -29,93 +167,82 @@ fi
29167

30168
TARGET_DIR="$HERMES_REPO/plugins/memory/memtensor"
31169

32-
echo "=== MemTensor Memory Plugin Installer (hermes-agent) ==="
170+
echo -e "${BOLD}=== MemTensor Memory Plugin Installer (hermes-agent) ===${NC}"
33171
echo ""
34-
echo "Plugin source: $SCRIPT_DIR"
35-
echo "Plugin root: $MEMOS_PLUGIN_DIR"
36-
echo "Hermes repo: $HERMES_REPO"
37-
echo "Install target: $TARGET_DIR"
172+
info "Plugin source: $SCRIPT_DIR"
173+
info "Plugin root: $MEMOS_PLUGIN_DIR"
174+
info "Hermes repo: $HERMES_REPO"
175+
info "Install target: $TARGET_DIR"
38176
echo ""
39177

40178
# ─── Pre-flight checks ───
41179

42180
if [ ! -f "$HERMES_REPO/agent/memory_provider.py" ]; then
43-
echo "ERROR: $HERMES_REPO does not look like a hermes-agent repository."
44-
exit 1
45-
fi
46-
47-
if ! command -v node &>/dev/null; then
48-
echo "ERROR: Node.js is required (>= 18). Please install it first."
49-
exit 1
50-
fi
51-
52-
NODE_VERSION=$(node -v | sed 's/v//' | cut -d. -f1)
53-
if [ "$NODE_VERSION" -lt 18 ]; then
54-
echo "ERROR: Node.js >= 18 is required. Current: $(node -v)"
181+
error "ERROR: $HERMES_REPO does not look like a hermes-agent repository."
55182
exit 1
56183
fi
57184

58-
echo "✓ Node.js $(node -v)"
185+
ensure_node
59186

60187
# ─── Install plugin dependencies ───
61188

62189
echo ""
63-
echo "Installing plugin dependencies..."
190+
info "Installing plugin dependencies..."
64191
cd "$MEMOS_PLUGIN_DIR"
65192

66193
if command -v pnpm &>/dev/null; then
67194
pnpm install --frozen-lockfile 2>/dev/null || pnpm install
68195
elif command -v npm &>/dev/null; then
69196
npm install
70197
else
71-
echo "ERROR: npm or pnpm is required."
198+
error "ERROR: npm or pnpm is required."
72199
exit 1
73200
fi
74201

75-
echo "✓ Dependencies installed"
202+
success "✓ Dependencies installed"
76203

77204
# ─── Record bridge path for runtime discovery ───
78205

79206
BRIDGE_CTS="$MEMOS_PLUGIN_DIR/bridge.cts"
80207
echo "$BRIDGE_CTS" > "$SCRIPT_DIR/bridge_path.txt"
81208

82209
if [ -f "$BRIDGE_CTS" ]; then
83-
echo "✓ Bridge script found: $BRIDGE_CTS"
210+
success "✓ Bridge script found: $BRIDGE_CTS"
84211
else
85-
echo "WARNING: bridge.cts not found at $BRIDGE_CTS"
86-
echo " Make sure it exists before using the plugin."
212+
warn "WARNING: bridge.cts not found at $BRIDGE_CTS"
213+
warn " Make sure it exists before using the plugin."
87214
fi
88215

89216
# ─── Create symlink in hermes-agent plugins/memory/ ───
90217

91218
echo ""
92-
echo "Creating symlink: $TARGET_DIR -> $SCRIPT_DIR"
219+
info "Creating symlink: $TARGET_DIR -> $SCRIPT_DIR"
93220

94221
if [ -L "$TARGET_DIR" ]; then
95222
rm "$TARGET_DIR"
96-
echo " (removed old symlink)"
223+
info " (removed old symlink)"
97224
elif [ -d "$TARGET_DIR" ]; then
98225
rm -rf "$TARGET_DIR"
99-
echo " (removed old directory)"
226+
info " (removed old directory)"
100227
fi
101228

102229
ln -s "$SCRIPT_DIR" "$TARGET_DIR"
103-
echo "✓ Symlink created"
230+
success "✓ Symlink created"
104231

105232
echo ""
106-
echo "=== Installation complete ==="
233+
echo -e "${BOLD}=== Installation complete ===${NC}"
107234
echo ""
108-
echo "Activate the plugin by editing ~/.hermes/config.yaml:"
235+
info "Activate the plugin by editing ~/.hermes/config.yaml:"
109236
echo ""
110237
echo " memory:"
111238
echo " provider: memtensor"
112239
echo ""
113-
echo "Then start hermes normally. The bridge daemon and memory viewer"
114-
echo "will start automatically on first session."
240+
info "Then start hermes normally. The bridge daemon and memory viewer"
241+
info "will start automatically on first session."
115242
echo ""
116-
echo " Memory Viewer: http://127.0.0.1:18901"
243+
success " Memory Viewer: http://127.0.0.1:18901"
117244
echo ""
118-
echo "Optional environment variables:"
245+
info "Optional environment variables:"
119246
echo " MEMOS_STATE_DIR - Override memory database location"
120247
echo " MEMOS_DAEMON_PORT - Bridge daemon TCP port (default: 18990)"
121248
echo " MEMOS_VIEWER_PORT - Memory viewer HTTP port (default: 18899)"

0 commit comments

Comments
 (0)