Skip to content

Commit 2fc8ddf

Browse files
committed
feat: enhance setup script to install and manage TelAgent as a system service on Linux, macOS, and Windows
1 parent 63a6780 commit 2fc8ddf

File tree

1 file changed

+266
-10
lines changed

1 file changed

+266
-10
lines changed

scripts/setup.sh

Lines changed: 266 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
# 4. Generates a private key and passphrase
1010
# 5. Creates .env from template with generated values
1111
# 6. Builds workspace packages
12-
# 7. Prints next steps
12+
# 7. Installs and starts TelAgent as a system service
13+
# - Linux: systemd user service (~/.config/systemd/user/telagent.service)
14+
# - macOS: launchd agent (~/Library/LaunchAgents/org.telagent.node.plist)
15+
# - Windows: NSSM Windows service (auto-downloads nssm.exe if needed)
1316

1417
set -euo pipefail
1518

@@ -126,15 +129,268 @@ pnpm --filter @telagent/protocol build
126129
pnpm --filter @telagent/sdk build
127130
ok "Workspace packages built"
128131

129-
# ── Done ──────────────────────────────────────────────────────────────
130-
echo ""
131-
printf "${GREEN}${BOLD}TelAgent is ready!${RESET}\n"
132-
echo ""
133-
echo " Start the node:"
134-
echo " cd ${INSTALL_DIR} && pnpm dev"
132+
# ── Step 6: Detect OS and install service ─────────────────────────────
133+
OS="$(uname -s)"
134+
PNPM_PATH="$(command -v pnpm)"
135+
NODE_PATH="$(command -v node)"
136+
137+
install_linux_service() {
138+
info "Setting up systemd service..."
139+
SYSTEMD_DIR="$HOME/.config/systemd/user"
140+
mkdir -p "$SYSTEMD_DIR"
141+
142+
cat > "$SYSTEMD_DIR/telagent.service" << EOF
143+
[Unit]
144+
Description=TelAgent Node
145+
After=network-online.target
146+
Wants=network-online.target
147+
148+
[Service]
149+
Type=simple
150+
WorkingDirectory=${INSTALL_DIR}
151+
ExecStart=${PNPM_PATH} --filter @telagent/node start
152+
Restart=always
153+
RestartSec=3
154+
Environment=PATH=${NODE_PATH%/*}:/usr/local/bin:/usr/bin:/bin
155+
156+
[Install]
157+
WantedBy=default.target
158+
EOF
159+
160+
systemctl --user daemon-reload
161+
systemctl --user enable telagent.service
162+
systemctl --user start telagent.service
163+
164+
# Enable lingering so the user service runs without an active login session
165+
if command -v loginctl &>/dev/null; then
166+
loginctl enable-linger "$(whoami)" 2>/dev/null || true
167+
fi
168+
169+
ok "systemd user service installed and started"
170+
echo ""
171+
echo " Manage the service:"
172+
echo " systemctl --user status telagent"
173+
echo " systemctl --user stop telagent"
174+
echo " systemctl --user restart telagent"
175+
echo " journalctl --user -u telagent -f"
176+
}
177+
178+
install_macos_service() {
179+
info "Setting up launchd agent..."
180+
LAUNCH_DIR="$HOME/Library/LaunchAgents"
181+
PLIST="$LAUNCH_DIR/org.telagent.node.plist"
182+
LOG_DIR="$HOME/.telagent/logs"
183+
mkdir -p "$LAUNCH_DIR" "$LOG_DIR"
184+
185+
cat > "$PLIST" << EOF
186+
<?xml version="1.0" encoding="UTF-8"?>
187+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
188+
<plist version="1.0">
189+
<dict>
190+
<key>Label</key>
191+
<string>org.telagent.node</string>
192+
<key>WorkingDirectory</key>
193+
<string>${INSTALL_DIR}</string>
194+
<key>ProgramArguments</key>
195+
<array>
196+
<string>${PNPM_PATH}</string>
197+
<string>--filter</string>
198+
<string>@telagent/node</string>
199+
<string>start</string>
200+
</array>
201+
<key>EnvironmentVariables</key>
202+
<dict>
203+
<key>PATH</key>
204+
<string>${NODE_PATH%/*}:/usr/local/bin:/usr/bin:/bin</string>
205+
</dict>
206+
<key>RunAtLoad</key>
207+
<true/>
208+
<key>KeepAlive</key>
209+
<true/>
210+
<key>StandardOutPath</key>
211+
<string>${LOG_DIR}/telagent-stdout.log</string>
212+
<key>StandardErrorPath</key>
213+
<string>${LOG_DIR}/telagent-stderr.log</string>
214+
</dict>
215+
</plist>
216+
EOF
217+
218+
# Unload first if already loaded (ignore errors)
219+
launchctl unload "$PLIST" 2>/dev/null || true
220+
launchctl load "$PLIST"
221+
222+
ok "launchd agent installed and started"
223+
echo ""
224+
echo " Manage the service:"
225+
echo " launchctl list | grep telagent"
226+
echo " launchctl unload ~/Library/LaunchAgents/org.telagent.node.plist # stop"
227+
echo " launchctl load ~/Library/LaunchAgents/org.telagent.node.plist # start"
228+
echo " tail -f ~/.telagent/logs/telagent-stderr.log # logs"
229+
}
230+
231+
start_foreground() {
232+
warn "Unsupported OS for service install: ${OS}"
233+
warn "Starting TelAgent in the foreground instead..."
234+
echo ""
235+
echo " To start manually later:"
236+
echo " cd ${INSTALL_DIR} && pnpm dev"
237+
echo ""
238+
cd "$INSTALL_DIR"
239+
exec pnpm dev
240+
}
241+
242+
install_windows_service() {
243+
info "Setting up Windows service via NSSM..."
244+
LOG_DIR="$HOME/.telagent/logs"
245+
mkdir -p "$LOG_DIR"
246+
247+
# Convert Git Bash paths to Windows paths
248+
WIN_INSTALL_DIR=$(cygpath -w "$INSTALL_DIR")
249+
WIN_PNPM_PATH=$(cygpath -w "$PNPM_PATH" 2>/dev/null || echo "$PNPM_PATH")
250+
WIN_NODE_DIR=$(cygpath -w "${NODE_PATH%/*}" 2>/dev/null || echo "${NODE_PATH%/*}")
251+
WIN_LOG_DIR=$(cygpath -w "$LOG_DIR")
252+
253+
# Use pnpm.cmd on Windows
254+
PNPM_CMD="${WIN_PNPM_PATH%.exe}"
255+
if [ -f "$(cygpath "${WIN_NODE_DIR}/pnpm.cmd" 2>/dev/null)" ]; then
256+
PNPM_CMD="${WIN_NODE_DIR}\\pnpm.cmd"
257+
fi
258+
259+
# Check if NSSM is available
260+
NSSM_PATH=""
261+
if command -v nssm &>/dev/null; then
262+
NSSM_PATH="nssm"
263+
elif [ -f "$INSTALL_DIR/tools/nssm.exe" ]; then
264+
NSSM_PATH="$INSTALL_DIR/tools/nssm.exe"
265+
fi
266+
267+
# Download NSSM if not found
268+
if [ -z "$NSSM_PATH" ]; then
269+
info "Downloading NSSM..."
270+
NSSM_DIR="$INSTALL_DIR/tools"
271+
mkdir -p "$NSSM_DIR"
272+
NSSM_ZIP="$NSSM_DIR/nssm.zip"
273+
274+
powershell.exe -NoProfile -Command \
275+
"Invoke-WebRequest -Uri 'https://nssm.cc/release/nssm-2.24.zip' -OutFile '$(cygpath -w "$NSSM_ZIP")'" \
276+
|| fail "Failed to download NSSM. Download manually from https://nssm.cc and place nssm.exe in $NSSM_DIR"
277+
278+
# Extract the correct architecture binary
279+
ARCH=$(uname -m)
280+
if [ "$ARCH" = "x86_64" ]; then
281+
NSSM_SUBDIR="nssm-2.24/win64"
282+
else
283+
NSSM_SUBDIR="nssm-2.24/win32"
284+
fi
285+
286+
powershell.exe -NoProfile -Command \
287+
"Expand-Archive -Path '$(cygpath -w "$NSSM_ZIP")' -DestinationPath '$(cygpath -w "$NSSM_DIR")' -Force"
288+
289+
cp "$NSSM_DIR/$NSSM_SUBDIR/nssm.exe" "$NSSM_DIR/nssm.exe"
290+
rm -rf "$NSSM_DIR/nssm-2.24" "$NSSM_ZIP"
291+
NSSM_PATH="$NSSM_DIR/nssm.exe"
292+
ok "NSSM downloaded to $NSSM_DIR/nssm.exe"
293+
fi
294+
295+
# Remove existing service if present (ignore errors)
296+
"$NSSM_PATH" stop TelAgent 2>/dev/null || true
297+
"$NSSM_PATH" remove TelAgent confirm 2>/dev/null || true
298+
299+
# Install the service
300+
"$NSSM_PATH" install TelAgent "$PNPM_CMD" "--filter @telagent/node start"
301+
"$NSSM_PATH" set TelAgent AppDirectory "$WIN_INSTALL_DIR"
302+
"$NSSM_PATH" set TelAgent DisplayName "TelAgent Node"
303+
"$NSSM_PATH" set TelAgent Description "TelAgent decentralized messaging node"
304+
"$NSSM_PATH" set TelAgent Start SERVICE_AUTO_START
305+
"$NSSM_PATH" set TelAgent AppStdout "$WIN_LOG_DIR\\telagent-stdout.log"
306+
"$NSSM_PATH" set TelAgent AppStderr "$WIN_LOG_DIR\\telagent-stderr.log"
307+
"$NSSM_PATH" set TelAgent AppStdoutCreationDisposition 4
308+
"$NSSM_PATH" set TelAgent AppStderrCreationDisposition 4
309+
"$NSSM_PATH" set TelAgent AppRotateFiles 1
310+
"$NSSM_PATH" set TelAgent AppRotateBytes 10485760
311+
"$NSSM_PATH" set TelAgent AppExit Default Restart
312+
"$NSSM_PATH" set TelAgent AppRestartDelay 3000
313+
314+
# Start the service
315+
"$NSSM_PATH" start TelAgent
316+
317+
ok "Windows service 'TelAgent' installed and started"
318+
echo ""
319+
echo " Manage the service:"
320+
echo " nssm status TelAgent"
321+
echo " nssm stop TelAgent"
322+
echo " nssm start TelAgent"
323+
echo " nssm restart TelAgent"
324+
echo " nssm edit TelAgent # GUI editor"
325+
echo " type %USERPROFILE%\\.telagent\\logs\\telagent-stderr.log # logs"
326+
}
327+
328+
# ── Step 7: Start the service ─────────────────────────────────────────
135329
echo ""
136-
echo " Start the WebApp (in another terminal):"
137-
echo " cd ${INSTALL_DIR} && pnpm --filter @telagent/webapp dev"
330+
case "$OS" in
331+
Linux*)
332+
if command -v systemctl &>/dev/null; then
333+
install_linux_service
334+
else
335+
start_foreground
336+
fi
337+
;;
338+
Darwin*)
339+
install_macos_service
340+
;;
341+
MINGW*|MSYS*|CYGWIN*)
342+
install_windows_service
343+
;;
344+
*)
345+
start_foreground
346+
;;
347+
esac
348+
349+
# ── Wait for node to be ready ─────────────────────────────────────────
350+
info "Waiting for TelAgent node to start..."
351+
READY=false
352+
for i in $(seq 1 15); do
353+
if curl -fs http://127.0.0.1:9529/api/v1/node/ &>/dev/null; then
354+
READY=true
355+
break
356+
fi
357+
sleep 2
358+
done
359+
138360
echo ""
139-
echo " Then open http://localhost:5173 and enter your passphrase to connect."
361+
if [ "$READY" = true ]; then
362+
printf "${GREEN}${BOLD}TelAgent is running!${RESET}\n"
363+
echo ""
364+
NODE_INFO=$(curl -fs http://127.0.0.1:9529/api/v1/identities/self 2>/dev/null || echo '{}')
365+
DID=$(echo "$NODE_INFO" | jq -r '.data.did // empty' 2>/dev/null || true)
366+
if [ -n "$DID" ]; then
367+
printf " ${BOLD}Your DID:${RESET} %s\n" "$DID"
368+
fi
369+
echo ""
370+
echo " Node API: http://127.0.0.1:9529"
371+
echo ""
372+
echo " Start the WebApp (optional):"
373+
echo " cd ${INSTALL_DIR} && pnpm --filter @telagent/webapp dev"
374+
echo " Then open http://localhost:5173 and enter your passphrase to connect."
375+
else
376+
printf "${YELLOW}${BOLD}TelAgent installed but node may still be starting.${RESET}\n"
377+
echo ""
378+
echo " Check status:"
379+
case "$OS" in
380+
Darwin*)
381+
echo " launchctl list | grep telagent"
382+
echo " tail -f ~/.telagent/logs/telagent-stderr.log"
383+
;;
384+
MINGW*|MSYS*|CYGWIN*)
385+
echo " nssm status TelAgent"
386+
echo " type %USERPROFILE%\\.telagent\\logs\\telagent-stderr.log"
387+
;;
388+
*)
389+
echo " systemctl --user status telagent"
390+
echo " journalctl --user -u telagent -f"
391+
;;
392+
esac
393+
echo ""
394+
echo " Once running, the API is at http://127.0.0.1:9529"
395+
fi
140396
echo ""

0 commit comments

Comments
 (0)