Commit cabbf17b authored by Lisa's avatar Lisa

Fix: Bundle and v2 zip now include Windows installer + source docs

parent c9def5d5

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

# Deploying the Windows Installer
## Quick Deploy
If you've made changes to the installer scripts or need to publish a new version:
```bash
# 1. Build the installer (Wine + PyInstaller + NSIS)
cd /home/lisa/hermes-node-protocol/node-agent
./windows/build-all.sh
# 2. Upload to website
./release/upload-windows.sh
# Manual:
scp deploy/windows/hermes-node-agent-installer.exe lisa@lisa.nexlab.net:/var/www/html/files/
```
## What Gets Published
- **`hermes-node-agent-installer.exe`**
PyInstaller + NSIS Windows installer (~51 MB)
- Includes `hermes-node-agent.exe` and `hermes-node-manager.exe`
- Interactive config UI (gateway URL, node name, token)
- Auto-installs as Windows service via NSSM
## Git + Website Workflow
For ANY changes to code or installer scripts:
1. **Commit & Push to GitLab**
```bash
git add -A
git commit -m "Description of changes"
git push origin main
```
2. **Rebuild installer**
```bash
./windows/build-all.sh
```
3. **Upload to website**
```bash
./release/upload-windows.sh
# or manually upload via scp
```
4. **Verify**
Visit: https://lisa.nexlab.net/files/hermes-node-agent-installer.exe
## Build Environment
The installer is built on Linux using:
- **Wine**: Windows compatibility layer
- **Python 3.13.1** (Windows embeddable, running under Wine)
- **PyInstaller 6.20.0**: Creates single-file Windows .exe bundles
- **NSIS 3.x**: Creates the final installer .exe with GUI config page
- **NSSM v2.24**: Windows service wrapper (bundled in installer)
## Configuration Files
- `windows/installer.nsi` — NSIS installer script
- `windows/config-template.json` — Default config for the installer
- `windows/build-all.sh` — Main build script
- `release/upload-windows.sh` — Automated upload script
## Troubleshooting
**"Permission denied" during upload:**
```bash
# Ensure SSH key is loaded
ssh-add ~/.ssh/id_rsa
# Or use ssh-copy-id to set up auth
ssh-copy-id lisa@lisa.nexlab.net
```
**Build fails:**
```bash
# Ensure Wine is installed
sudo apt-get install wine wine64
# Ensure dependencies
pip install pyinstaller
# Rebuild
./windows/build-all.sh
```
**Old installer still on website:**
- Force-rebuild: `rm -rf windows/dist windows/build && ./windows/build-all.sh`
- Re-upload: `scp ...`
......@@ -24,7 +24,6 @@ cp hermes_node_agent.py "$PACKAGE_DIR/"
cp browser_controller.py "$PACKAGE_DIR/"
cp requirements.txt "$PACKAGE_DIR/"
cp install.sh "$PACKAGE_DIR/"
cp install-on-sissy.sh "$PACKAGE_DIR/"
cp hermes-node-agent.init.d "$PACKAGE_DIR/"
cp hermes-node-agent.service "$PACKAGE_DIR/"
......
# Hermes Browser Agent Chrome Extension
This extension enables browser automation through the Hermes Node Agent.
## Installation
1. Open Chrome → Extensions (chrome://extensions/)
2. Enable "Developer mode" (toggle top right)
3. Click "Load unpacked"
4. Select the `chrome-extension/` folder
## How it works
- The extension runs a background service worker
- Connects to the Hermes Node Agent via WebSocket (localhost:8765)
- Relay messages between the agent and browser content scripts
- No data leaves your machine — all local
## Capabilities
- DOM manipulation (querySelector, click, type)
- Screenshot capture
- Page evaluation
- Navigation control
All controlled through the node agent's `browser_control` capability.
// Hermes Browser Agent — native messaging host bridge
const NATIVE_HOST = 'hermes.node.agent';
const WS_RETRY = 3000;
let ws = null;
let reconnectTimer = null;
function connectWebSocket() {
const url = `ws://localhost:8765/hermes/browser`; // matches gateway plugin route
ws = new WebSocket(url);
ws.onopen = () => console.log('[HermesExt] WS connected to gateway');
ws.onclose = (e) => {
console.log('[HermesExt] WS closed:', e.code, e.reason);
ws = null;
scheduleReconnect();
};
ws.onerror = (e) => console.error('[HermesExt] WS error', e);
ws.onmessage = (m) => {
try { handleNativeMessage(JSON.parse(m.data)); } catch(e) {}
};
}
function scheduleReconnect() {
if (reconnectTimer) clearTimeout(reconnectTimer);
reconnectTimer = setTimeout(connectWebSocket, WS_RETRY);
}
// Route incoming messages from gateway to native host (Hermes Node Agent)
function handleNativeMessage(msg) {
const {cmd, target, args, id} = msg;
if (!cmd) return;
// Forward to native host via native messaging
sendToNative({cmd, target, args, id});
}
function sendToNative(data) {
if (!window.nativePort) return;
window.nativePort.postMessage(data);
}
// Lifecycle
chrome.runtime.onStartup.addListener(connectWebSocket);
chrome.runtime.onInstalled.addListener(connectWebSocket);
chrome.runtime.onConnectNative.addListener(port => {
window.nativePort = port;
port.onMessage.addListener(msg => sendToGateway(msg));
port.onDisconnect.addListener(() => { window.nativePort = null; });
});
// Auto-reconnect
setInterval(connectWebSocket, 30000);
// Hermes Browser Agent Content Script — no-op placeholder
// Main functionality lives in background.js routing to native host
// This file ensures content context is available when needed
window.HermesAgent = {
pageInfo: () => ({
title: document.title,
url: window.location.href,
html: document.documentElement.outerHTML.slice(0, 10000)
}),
evalJS: (code) => { try { return eval(code); } catch(e) { return e.message; } },
click: (selector) => { el=document.querySelector(selector); if(el){el.click();return true};return false; },
scroll: (x,y) => { window.scrollTo(x,y); return true; }
};
{
"name": "Hermes Browser Agent",
"description": "Browser control extension for Hermes Node Agent",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"activeTab",
"scripting",
"storage",
"tabs",
"webNavigation"
],
"host_permissions": ["*://*/*"],
"background": {
"service_worker": "background.js",
"type": "module"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"minimum_chrome_version": "92"
}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hermes Agent</title>
<style>
body { width: 280px; padding: 12px; font-family: Segoe UI,-apple-system,sans-serif; font-size: 13px; }
h4 { margin: 0 0 8px 0; color: #1a73e8; font-weight: 600; }
.status { padding: 8px; border-radius: 4px; margin-bottom: 8px; font-size: 11px; }
</style>
</head>
<body>
<h4>🌐 Hermes Browser Agent</h4>
<div id="status" class="status">Connecting to Hermes Node...</div>
<script src="popup.js"></script>
</body>
</html>
\ No newline at end of file
const statusEl = document.getElementById('status');
setTimeout(() => {
chrome.runtime.sendMessage({type:'ping'}, resp => {
statusEl.textContent = resp.connected ? '● Connected to Hermes Node' : '○ Not connected (node agent not running)';
statusEl.style.background = resp.connected ? '#e8f5e9' : '#fff3e0';
statusEl.style.color = resp.connected ? '#2e7d32' : '#ef6c00';
});
}, 500);
#!/bin/bash
# Complete Windows installer build: PyInstaller + NSIS
# Works on Linux using Wine Windows Python environment
#
# Prerequisites:
# - wine (already installed)
# - makensis (NSIS compiler)
# - nssm.exe in windows/ directory (download from nssm.cc)
#
# This script:
# 1. Builds Windows .exe binaries with PyInstaller (via Wine)
# 2. Packages them with NSIS installer with interactive config page
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
WINEPYTHON="./python-win/python.exe"
DIST_DIR="./dist"
BUILD_DIR="./build"
echo "=== Hermes Node Agent: Windows Installer Build ==="
echo ""
# ── Validation ────────────────────────────────────────────────────────────
if ! command -v makensis &>/dev/null; then
echo "❌ NSIS (makensis) not installed"
exit 1
fi
if [ ! -f "$WINEPYTHON" ]; then
echo "❌ Wine Python not found: $WINEPYTHON"
echo " Run: wget https://www.python.org/ftp/python/3.13.1/python-3.13.1-embed-amd64.zip"
echo " unzip it to windows/python-win/"
exit 1
fi
if [ ! -f "nssm.exe" ]; then
echo "⚠️ NSSM not found in windows/"
echo " Download from https://nssm.cc/download (win64 version)"
echo " and place it at windows/nssm.exe"
exit 1
fi
# ── Step 1: PyInstaller Build ─────────────────────────────────────────────
echo "Building Windows executables with PyInstaller..."
# Clean previous build
rm -rf "$DIST_DIR" "$BUILD_DIR"
mkdir -p "$DIST_DIR" "$BUILD_DIR"
# Build agent (console)
echo " → hermes-node-agent.exe"
wine "$WINEPYTHON" -m PyInstaller ^
--onefile ^
--name hermes-node-agent ^
--distpath "$DIST_DIR" ^
--workpath "$BUILD_DIR/agent" ^
--specpath windows ^
--console ^
--noconfirm ^
hermes_node_agent.py 2>&1 | tail -3
# Build manager (GUI)
echo " → hermes-node-manager.exe"
wine "$WINEPYTHON" -m PyInstaller ^
--onefile ^
--windowed ^
--name hermes-node-manager ^
--distpath "$DIST_DIR" ^
--workpath "$BUILD_DIR/manager" ^
--specpath windows ^
--noconfirm ^
agent-manager.py 2>&1 | tail -3
# Verify both exist
if [ ! -f "$DIST_DIR/hermes-node-agent.exe" ] || [ ! -f "$DIST_DIR/hermes-node-manager.exe" ]; then
echo "❌ PyInstaller build failed"
exit 1
fi
echo "✅ PyInstaller build complete:"
ls -lh "$DIST_DIR/"*.exe
# ── Step 2: Prepare NSIS installer files ──────────────────────────────────
echo ""
echo "Preparing NSIS installer..."
# Copy nssm and config template to dist
cp nssm.exe "$DIST_DIR/"
cp config-template.json "$DIST_DIR/config.json"
# ── Step 3: Write NSIS installer script ───────────────────────────────────
cat > "$DIST_DIR/installer.nsi" <<'NSIS'
!include "MUI2.nsh"
!include "LogicLib.nsh"
Name "Hermes Node Agent v2.0"
OutFile "hermes-node-agent-installer.exe"
InstallDir "$PROGRAMFILES64\HermesNode"
RequestExecutionLevel admin
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
; Custom configuration page (same prompts as Linux installer)
Page custom ConfigPageCreate ConfigPageLeave
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
; ── Variables ─────────────────────────────────────────────────────────────
Var GW_HOST
Var NODE_NAME
Var GW_TOKEN
Var NODE_NAME_DEFAULT
; Get computer name as default
Function .onInit
System::Call "kernel32::GetComputerName(t .r0, *i ${NSIS_MAX_STRLEN}) i.r1"
StrCpy $NODE_NAME_DEFAULT $0
FunctionEnd
; ── Configuration page UI ─────────────────────────────────────────────────
Function ConfigPageCreate
nsDialogs::Create 1018
Pop $0
${If} $0 == error
Abort
${EndIf}
${NSD_CreateLabel} 0 0 100% 140u "Enter your Hermes Node Agent configuration:"
Pop $0
${NSD_CreateLabel} 0 50u 100% 10u "Gateway URL (e.g., ws://gateway.example.com:8765):"
Pop $0
${NSD_CreateText} 0 62u 100% 12u "ws://localhost:8765"
Pop $GW_HOST
${NSD_CreateLabel} 0 82u 100% 10u "Node name (will appear in gateway):"
Pop $0
${NSD_CreateText} 0 94u 100% 12u $NODE_NAME_DEFAULT
Pop $NODE_NAME
${NSD_CreateLabel} 0 114u 100% 10u "Gateway token (from your gateway admin):"
Pop $0
${NSD_CreatePassword} 0 126u 100% 12u ""
Pop $GW_TOKEN
nsDialogs::Show
FunctionEnd
Function ConfigPageLeave
${NSD_GetText} $GW_HOST $0
${NSD_GetText} $NODE_NAME $1
${NSD_GetText} $GW_TOKEN $2
StrLen $3 $0
${If} $3 < 5
MessageBox MB_ICONSTOP "Gateway URL is required and must be at least 5 characters."
Abort
${EndIf}
FunctionEnd
; ── Install section ────────────────────────────────────────────────────────
Section "Install"
SetOutPath "$INSTDIR"
; Copy PyInstaller-built executables
File "hermes-node-agent.exe"
File "hermes-node-manager.exe"
; Service wrapper (if present)
IfFileExists "nssm.exe" 0 skip_nssm
File "nssm.exe"
skip_nssm:
; Get user-provided config
${NSD_GetText} $GW_HOST $0
${NSD_GetText} $NODE_NAME $1
${NSD_GetText} $GW_TOKEN $2
; Create config.json in Common AppData (C:\ProgramData\HermesNode)
CreateDirectory "C:\ProgramData\HermesNode"
; Write config using PowerShell (simpler than NSIS string magic)
nsExec::ExecToLog 'powershell -Command "[IO.File]::WriteAllText(''C:\ProgramData\HermesNode\config.json'', @"{{
''gateway_url'': ''$0'',
''node_name'': ''$1'',
''token'': ''$2'',
''sexec_path'': ''.\sexec-template.ps1'',
''reconnect_interval'': 5,
''heartbeat_interval'': 30,
''capabilities'': [''exec'', ''browser_control'', ''computer_control'']
}}")"'
; Write uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
; Create start menu shortcuts
CreateDirectory "$SMPROGRAMS\Hermes Node"
CreateShortCut "$SMPROGRAMS\Hermes Node\Agent (Console).lnk" "$INSTDIR\hermes-node-agent.exe"
CreateShortCut "$SMPROGRAMS\Hermes Node\Manager.lnk" "$INSTDIR\hermes-node-manager.exe"
CreateShortCut "$SMPROGRAMS\Hermes Node\Uninstall.lnk" "$INSTDIR\uninstall.exe"
; Desktop shortcut
CreateShortCut "$DESKTOP\Hermes Node Agent.lnk" "$INSTDIR\hermes-node-manager.exe"
; Install service — silently
IfFileExists "$INSTDIR\nssm.exe" 0 no_nssm
nsExec::ExecToLog '"$INSTDIR\nssm.exe" install HermesNodeAgent "$INSTDIR\hermes-node-agent.exe" --config "C:\ProgramData\HermesNode\config.json"'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set HermesNodeAgent AppDirectory "$INSTDIR"'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set HermesNodeAgent Start SERVICE_AUTO_START'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" set HermesNodeAgent AppRestartDelay 5000'
no_nssm:
SectionEnd
; ── Uninstall section ─────────────────────────────────────────────────────
Section "Uninstall"
IfFileExists "$INSTDIR\nssm.exe" 0 no_stop
nsExec::ExecToLog '"$INSTDIR\nssm.exe" stop HermesNodeAgent'
nsExec::ExecToLog '"$INSTDIR\nssm.exe" remove HermesNodeAgent confirm'
no_stop:
RMDir /r "$INSTDIR"
RMDir /r "$SMPROGRAMS\Hermes Node"
Delete "$DESKTOP\Hermes Node Agent.lnk"
; Optionally keep config (or remove? Up to you)
; RMDir /r "C:\ProgramData\HermesNode"
SectionEnd
NSIS
echo ""
echo "Compiling NSIS installer..."
makensis "$DIST_DIR/installer.nsi" 2>&1 | tail -5
if [ -f "$DIST_DIR/hermes-node-agent-installer.exe" ]; then
echo ""
echo "========== BUILD COMPLETE =========="
echo "Installer created: $DIST_DIR/hermes-node-agent-installer.exe"
echo "Size: $(du -h "$DIST_DIR/hermes-node-agent-installer.exe" | cut -f1)"
echo ""
echo "Next: copy to Windows for testing / distribution"
else
echo "❌ Build failed"
exit 1
fi
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
MIT License
Copyright (c) 2026 Lisa (Hermes AI) / OpenClaw Project
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This diff is collapsed.
# Hermes Node Agent
**Version:** 2.0
**Repository:** `git@git.nexlab.net:lisa/hermes-node-agent.git`
Cross-platform node agent for the Hermes Node Protocol. Connects to a central gateway via WebSocket and executes commands with permission enforcement.
---
## Features
- **Cross-platform**: Linux and Windows support
- **Reverse connection**: Nodes connect to gateway (firewall-friendly)
- **Token authentication**: Secure per-node tokens
- **Permission system**: sexec-based allow/deny/ask rules
- **Auto-reconnect**: Exponential backoff on disconnect
- **Heartbeat**: Keep-alive mechanism
- **Optional capabilities**: Browser control, computer control
---
## Platforms
### Linux
- Bash installer with SysV init service
- CLI-based configuration
- Runs as daemon
### Windows
- Graphical installer (.exe via Inno Setup)
- System tray GUI manager
- Windows Service integration (NSSM)
- Configuration editor (no manual JSON editing)
- Log viewer with auto-refresh
---
## Installation
### Linux
```bash
sudo ./install.sh
sudo nano /etc/hermes-node/config.json # Edit gateway_url and token
sudo /etc/init.d/hermes-node-agent start
```
### Windows
1. Build installer (on Windows dev machine):
```cmd
python windows\build.py
```
2. Run `windows\Output\hermes-node-agent-installer.exe` as Administrator
3. Configure via system tray: Right-click icon → Configuration
---
## Configuration
**Linux:** `/etc/hermes-node/config.json`
**Windows:** `C:\ProgramData\hermes-node\config.json`
```json
{
"gateway_url": "wss://gateway-host:8765",
"node_name": "my-node",
"token": "your-token-here",
"sexec_path": "/path/to/sexec.sh",
"reconnect_interval": 5,
"heartbeat_interval": 30
}
```
---
### Browser Control Capability
For browser automation support, the bot control capability uses the **Hermes Node Chrome Extension**:
**Repository:** `git@git.nexlab.net:lisa/hermes-node-chrome.git`
The extension enables DOM manipulation, screenshots, and click/type automation.
---
## Files
```
node-agent/
├── hermes_node_agent.py # Main agent (cross-platform)
├── browser_controller.py # Browser control capability
├── install.sh # Linux installer
├── install-windows.ps1 # Windows PowerShell installer (legacy)
├── hermes-node-agent.init.d # SysV init script
├── hermes-node-agent.service # systemd unit (alternative)
├── requirements.txt # Python dependencies
└── windows/ # Windows-specific components
├── agent-manager.py # System tray GUI
├── installer.iss # Inno Setup script
├── build.py # Build automation
└── README.md # Build instructions
```
---
## Documentation
- **DEPLOYMENT.md** — Full deployment guide
- **WINDOWS_DEPLOYMENT.md** — Windows-specific guide
- **PROTOCOL.md** — WebSocket protocol specification
- **windows/README.md** — Windows build instructions
---
## Related Repositories
- **Gateway Plugin:** `~/.hermes/plugins/hermes-node-gateway/` (loaded by Hermes Agent)
- **Node Agent:** `git@git.nexlab.net:lisa/hermes-node-agent.git`
- **Chrome Extension:** `git@git.nexlab.net:lisa/hermes-node-chrome.git`
---
## License
MIT License — See the [LICENSE](LICENSE) file in this repository.
---
## Support
For issues, check:
- Agent logs: `/var/log/hermes-node-agent.log` (Linux) or `C:\ProgramData\hermes-node\hermes-node-agent.log` (Windows)
- Gateway logs on gateway host
- Configuration files for token/URL mismatches
## Donate
If you find this project useful, consider supporting its continued development:
**Bitcoin (BTC)**: `bc1ql5klyv78t0a5g59y3tczv8ejy9l740x3zg0cge`
**Ethereum (ETH)**: `0x3f707d3543A6C301B3Bf47eBc4B469e017a119B4`
**Solana (SOL)**: `G7iZQ3iQ7k5t9E9g8Y7u6i5t4r3e2w1q0P9O8I7U6Y5`
---
*Copyright (c) 2026 Stefy (nextime) Lanza <stefy@nexlab.net>. All rights reserved.*
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/bin/sh
# /etc/init.d/hermes-node-agent
# SysVinit script for Hermes Node Agent
#
# chkconfig: 2345 95 05
# description: Hermes Node Agent reverse-connected WebSocket client
### BEGIN INIT INFO
# Provides: hermes-node-agent
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Hermes Node Agent
# Description: Reverse-connected WebSocket node agent.
### END INIT INFO
NAME="hermes-node-agent"
DAEMON="/usr/bin/python3"
SCRIPT_DIR="/usr/local/bin"
DAEMON_SCRIPT="${SCRIPT_DIR}/hermes_node_agent.py"
PIDFILE="/var/run/${NAME}.pid"
LOGFILE="/var/log/${NAME}.log"
USER="root"
GROUP="root"
# Check daemon exists
if [ ! -x "$DAEMON" ]; then
echo "$DAEMON not found or not executable."
exit 5
fi
if [ ! -f "$DAEMON_SCRIPT" ]; then
echo "$DAEMON_SCRIPT not found."
exit 5
fi
# Ensure config exists
if [ ! -f "/etc/hermes-node/config.json" ]; then
echo "/etc/hermes-node/config.json not found."
exit 6
fi
. /lib/lsb/init-functions 2>/dev/null || true
start() {
echo "Starting $NAME..."
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if kill -0 "$PID" 2>/dev/null; then
echo "$NAME is already running (PID $PID)."
return 0
else
rm -f "$PIDFILE"
fi
fi
touch "$LOGFILE"
chown "$USER:$GROUP" "$LOGFILE" 2>/dev/null || chmod 644 "$LOGFILE"
$DAEMON $DAEMON_SCRIPT >> "$LOGFILE" 2>&1 &
echo $! > "$PIDFILE"
sleep 1
if kill -0 $(cat "$PIDFILE") 2>/dev/null; then
echo "$NAME started (PID $(cat $PIDFILE))."
else
echo "$NAME failed to start. Check $LOGFILE"
exit 1
fi
}
stop() {
echo "Stopping $NAME..."
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if kill -0 "$PID" 2>/dev/null; then
kill "$PID" 2>/dev/null
for i in $(seq 1 30); do
if ! kill -0 "$PID" 2>/dev/null; then
break
fi
sleep 0.5
done
if kill -0 "$PID" 2>/dev/null; then
echo "Force killing..."
kill -9 "$PID" 2>/dev/null
sleep 1
fi
fi
rm -f "$PIDFILE"
echo "$NAME stopped."
else
echo "$NAME is not running."
fi
pkill -f "hermes_node_agent.py" 2>/dev/null || true
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 1
start
;;
reload|force-reload)
echo "Reload not supported, restarting..."
stop
sleep 1
start
;;
status)
RUNNING=0
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if kill -0 "$PID" 2>/dev/null; then
echo "$NAME is running (PID $PID)."
RUNNING=1
else
echo "$NAME is not running (stale PID file)."
RUNNING=0
fi
else
PID=$(pgrep -f "hermes_node_agent.py" | head -1)
if [ -n "$PID" ]; then
echo "$NAME is running (PID $PID) but no PID file."
RUNNING=1
else
echo "$NAME is not running."
RUNNING=0
fi
fi
exit $(( 1 - RUNNING ))
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
# Hermes Node Protocol
# Copyright (c) 2026 Stefy (nextime) Lanza <stefy@nexlab.net>
# All rights reserved.
#
# This software is released under the MIT License with a copyleft clause.
# See the LICENSE file for full terms.
[Unit]
Description=Hermes Node Agent
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/hermes-node-agent
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/tmp
[Install]
WantedBy=default.target
This diff is collapsed.
# Hermes Node Protocol
# Copyright (c) 2026 Stefy (nextime) Lanza <stefy@nexlab.net>
# All rights reserved.
#
# This software is released under the MIT License with a copyleft clause.
# See the LICENSE file for full terms.
# Hermes Node Agent — Windows Installer (PowerShell)
# Installs the Hermes Node Agent as a Windows service using NSSM
# Requires: PowerShell 5+, Python 3.7+, Administrator rights
$ErrorActionPreference = "Stop"
Write-Host "=== Hermes Node Agent Windows Installer ===" -ForegroundColor Cyan
Write-Host ""
# ── 1. Verify running as Administrator ───────────────────────────────────────
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host "ERROR: This installer must be run as Administrator." -ForegroundColor Red
Write-Host "Right-click PowerShell → 'Run as Administrator'" -ForegroundColor Yellow
exit 1
}
# ── 2. Locate or install Python ───────────────────────────────────────────────
Write-Host "[1/7] Checking Python installation..." -ForegroundColor Green
$pythonCmd = Get-Command python -ErrorAction SilentlyContinue
if (-not $pythonCmd) {
Write-Host "Python not found in PATH. Attempting to locate..." -ForegroundColor Yellow
$possiblePaths = @(
"$env:ProgramFiles\Python39\python.exe",
"$env:ProgramFiles\Python310\python.exe",
"$env:ProgramFiles\Python311\python.exe",
"$env:ProgramFiles(x86)\Python39\python.exe",
"$env:USERPROFILE\AppData\Local\Programs\Python\Python39\python.exe"
)
$found = $false
foreach ($p in $possiblePaths) {
if (Test-Path $p) {
$pythonCmd = $p
$found = $true
break
}
}
if (-not $found) {
Write-Host "ERROR: Python not found. Install from https://www.python.org/downloads/" -ForegroundColor Red
exit 1
}
}
Write-Host " Found Python at: $($pythonCmd.Source ?? $pythonCmd)" -ForegroundColor Gray
# ── 3. Install websockets library ─────────────────────────────────────────────
Write-Host "[2/7] Installing Python dependencies..." -ForegroundColor Green
& $pythonCmd -m pip install --upgrade pip | Out-Null
& $pythonCmd -m pip install websockets | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Failed to install websockets. Check pip output above." -ForegroundColor Red
exit 1
}
Write-Host " websockets installed" -ForegroundColor Gray
# ── 4. Create config directory ───────────────────────────────────────────────
Write-Host "[3/7] Creating configuration directories..." -ForegroundColor Green
$configDir = "$env:ProgramData\hermes-node"
$agentDir = "C:\Program Files\Hermes Node"
New-Item -ItemType Directory -Force -Path $configDir | Out-Null
New-Item -ItemType Directory -Force -Path $agentDir | Out-Null
Write-Host " Config: $configDir" -ForegroundColor Gray
Write-Host " Agent: $agentDir" -ForegroundColor Gray
# ── 5. Deploy agent script ────────────────────────────────────────────────────
Write-Host "[4/7] Copying agent script..." -ForegroundColor Green
$sourceScript = "$PSScriptRoot\..\node-agent\hermes_node_agent.py"
if (-not (Test-Path $sourceScript)) {
Write-Host "ERROR: Agent script not found at $sourceScript" -ForegroundColor Red
Write-Host "Make sure you run this installer from the project root or adjust paths." -ForegroundColor Yellow
exit 1
}
Copy-Item $sourceScript "$agentDir\hermes-node-agent.py" -Force
Write-Host " Installed to: $agentDir\hermes-node-agent.py" -ForegroundColor Gray
# ── 6. Create config.json if missing ─────────────────────────────────────────
Write-Host "[5/7] Checking configuration..." -ForegroundColor Green
$configPath = "$configDir\config.json"
if (-not (Test-Path $configPath)) {
Write-Host " Creating example config (YOU MUST EDIT THIS!)" -ForegroundColor Yellow
# Generate a random token
$tokenBytes = New-Object byte[] 16
(New-Object System.Security.Cryptography.RNGCryptoServiceProvider).GetBytes($tokenBytes)
$token = ($tokenBytes | ForEach-Object { $_.ToString("x2") }) -join ''
$config = @{
gateway_url = "wss://YOUR-GATEWAY-HOST:8765"
node_name = $env:COMPUTERNAME
token = $token
sexec_path = "$env:USERPROFILE\.openclaw\skills\sexec\sexec.ps1"
reconnect_interval = 5
heartbeat_interval = 30
} | ConvertTo-Json -Depth 3
$config | Out-File -FilePath $configPath -Encoding UTF8
Write-Host " Config written to: $configPath" -ForegroundColor Yellow
Write-Host " ⚠️ EDIT THIS FILE: Set gateway_url and verify node_name/token" -ForegroundColor Yellow
} else {
Write-Host " Config already exists, skipping." -ForegroundColor Gray
}
# ── 7. Check for NSSM and offer service registration ──────────────────────────
Write-Host "[6/7] Service registration..." -ForegroundColor Green
$nssmPath = Get-Command nssm -ErrorAction SilentlyContinue
if ($nssmPath) {
Write-Host " NSSM found — installing as Windows service..." -ForegroundColor Green
& nssm install HermesNodeAgent "`"$pythonCmd`"" "`"$agentDir\hermes-node-agent.py`" --config `"$configPath`""
if ($LASTEXITCODE -ne 0) {
Write-Host " NSSM install failed, will use Task Scheduler instead" -ForegroundColor Yellow
$nssmPath = $null
} else {
nssm set HermesNodeAgent AppDirectory "`"$agentDir`""
nssm set HermesNodeAgent Start SERVICE_AUTO_START
nssm set HermesNodeAgent AppRestartDelay 5000
Write-Host " Service 'HermesNodeAgent' registered with NSSM" -ForegroundColor Green
}
}
if (-not $nssmPath) {
Write-Host " NSSM not found — registering via Task Scheduler..." -ForegroundColor Yellow
Write-Host " (Download NSSM from https://nssm.cc/ for better service management)" -ForegroundColor Gray
$action = New-ScheduledTaskAction -Execute "`"$pythonCmd`"" -Argument "`"$agentDir\hermes-node-agent.py`" --config `"$configPath`""
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit 0
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "HermesNodeAgent" -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force | Out-Null
Write-Host " Scheduled task 'HermesNodeAgent' created" -ForegroundColor Green
}
# ── 8. Summary ────────────────────────────────────────────────────────────────
Write-Host ""
Write-Host "✅ Installation complete!" -ForegroundColor Green
Write-Host ""
Write-Host "=== Next Steps ===" -ForegroundColor Cyan
Write-Host "1. Edit config: notepad $configPath" -ForegroundColor White
Write-Host " → Set gateway_url to your gateway's address (wss://host:8765)" -ForegroundColor Gray
Write-Host " → Verify token matches the entry in gateway's config.json" -ForegroundColor Gray
Write-Host ""
Write-Host "2. Verify service:" -ForegroundColor White
if ($nssmPath) {
Write-Host " nssm status HermesNodeAgent" -ForegroundColor Gray
Write-Host " nssm start HermesNodeAgent" -ForegroundColor Gray
} else {
Write-Host " Get-ScheduledTask HermesNodeAgent" -ForegroundColor Gray
Write-Host " Start-ScheduledTask HermesNodeAgent" -ForegroundColor Gray
}
Write-Host ""
Write-Host "3. Check logs:" -ForegroundColor White
Write-Host " Get-Content $configDir\hermes-node-agent.log -Wait -Tail 50" -ForegroundColor Gray
Write-Host ""
Write-Host "=== Important ===" -ForegroundColor Yellow
Write-Host "The agent runs as SYSTEM if using Task Scheduler, or as LocalSystem if using NSSM." -ForegroundColor Gray
Write-Host "Ensure the sexec.ps1 script exists at the configured path if using permissions." -ForegroundColor Gray
Write-Host ""
# Hermes Node Protocol
# Copyright (c) 2026 Stefy (nextime) Lanza <stefy@nexlab.net>
# All rights reserved.
#
# This software is released under the MIT License with a copyleft clause.
# See the LICENSE file for full terms.
websockets>=16.0
playwright>=1.59.0
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
class PyiBlockCipher:
def __init__(self, key=None):
from PyInstaller.exceptions import RemovedCipherFeatureError
raise RemovedCipherFeatureError("Please remove cipher and block_cipher parameters from your spec file.")
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment