Commit aa563f0a authored by Lisa (AI Assistant)'s avatar Lisa (AI Assistant)

fix: restore integrated exec and installer capability sync

parent 3f9ac2ad
......@@ -19,11 +19,11 @@ mkdir -p "$PACKAGE_DIR"
echo "Copying files to $PACKAGE_DIR..."
# Copy core files
cp hermes_node_agent.py "$PACKAGE_DIR/"
# Copy core files from the package source of truth
cp package-hermes-node-agent/hermes_node_agent.py "$PACKAGE_DIR/hermes_node_agent.py"
cp browser_controller.py "$PACKAGE_DIR/"
cp requirements.txt "$PACKAGE_DIR/"
cp install.sh "$PACKAGE_DIR/"
cp package-hermes-node-agent/install.sh "$PACKAGE_DIR/install.sh"
cp hermes-node-agent.init.d "$PACKAGE_DIR/"
cp hermes-node-agent.service "$PACKAGE_DIR/"
......
......@@ -57,6 +57,8 @@ DEFAULT_CONFIG = {
'capabilities': ['exec'],
'enable_browser': False,
'enable_computer_control': False,
'enable_desktop_observe': False,
'enable_audio_control': False,
}
# ═════════════════════════════════════════════════════════════════════════
......@@ -422,16 +424,15 @@ def make_computer_controller(config: Dict[str, Any]) -> Optional[ComputerControl
class NodeAgent:
def __init__(self, config_path: Optional[str] = None):
self.config = self._load_config(config_path)
self.capabilities = self._detect_capabilities()
self.computer = None
if self.capabilities.get('enable_computer_control'):
self.computer = ComputerController()
self.executor = make_command_executor(self.config)
self.computer = make_computer_controller(self.config)
self.browser = None
if self.capabilities.get('enable_browser') and HAS_BROWSER:
if self.config.get('enable_browser') and HAS_BROWSER:
try:
self.browser = BrowserController()
except Exception as e:
logger.warning(f"BrowserController init failed: {e}")
self.capabilities = self._detect_capabilities()
def _load_config(self, path: Optional[str]) -> Dict[str, Any]:
"""Load node configuration from JSON."""
......@@ -459,10 +460,12 @@ class NodeAgent:
caps = {
'enable_browser': self.config.get('enable_browser', False),
'enable_computer_control': self.config.get('enable_computer_control', False),
'enable_desktop_observe': self.config.get('enable_desktop_observe', False),
'enable_audio_control': self.config.get('enable_audio_control', False),
}
# Browser detection: extension installed signals availability via separate channel
# Computer control: check system tools
if caps['enable_computer_control']:
if self.browser is not None:
caps['browser_control'] = {'available': True}
if self.computer is not None:
cc_info = {
'display': os.environ.get('DISPLAY', ':0'),
'has_xdotool': subprocess.run(['which', 'xdotool'], capture_output=True).returncode == 0,
......@@ -470,6 +473,16 @@ class NodeAgent:
'has_scrot': subprocess.run(['which', 'scrot'], capture_output=True).returncode == 0,
}
caps['computer_control'] = cc_info
if caps['enable_desktop_observe']:
caps['desktop_observe'] = {
'available': self.computer is not None,
'display': os.environ.get('DISPLAY', ':0')
}
if caps['enable_audio_control']:
caps['audio_control'] = {
'available': False,
'reason': 'audio control implementation not present in this agent build'
}
return caps
async def connect_and_run(self):
......@@ -490,10 +503,17 @@ class NodeAgent:
"capabilities": self.capabilities
}))
# Listen for commands
heartbeat_task = asyncio.create_task(self._heartbeat_loop(ws))
try:
async for raw in ws:
msg = json.loads(raw)
await self._handle_message(ws, msg)
finally:
heartbeat_task.cancel()
try:
await heartbeat_task
except asyncio.CancelledError:
pass
except Exception as e:
logger.error(f"Connection error: {e}")
......@@ -502,11 +522,15 @@ class NodeAgent:
def _get_available_tools(self) -> list:
"""Return list of capability strings for gateway registration."""
tools = ['exec'] # always present
if self.capabilities.get('enable_browser'):
tools = ['exec']
if self.browser is not None:
tools.append('browser_control')
if self.capabilities.get('enable_computer_control'):
if self.computer is not None:
tools.append('computer_control')
if self.config.get('enable_desktop_observe') and self.computer is not None:
tools.append('desktop_observe')
if self.config.get('enable_audio_control'):
logger.warning('audio_control requested but not implemented in this node agent build')
return tools
async def _handle_message(self, ws, msg: Dict[str, Any]):
......@@ -514,53 +538,169 @@ class NodeAgent:
if req_type == 'exec':
cmd_id = msg['id']
command = msg['command']
await self._handle_exec(ws, cmd_id, command)
await self._handle_exec(ws, cmd_id, command, msg.get('approved', False))
elif req_type == 'computer_control':
action = msg['action']
params = msg.get('params', {})
await self._handle_cc(ws, msg.get('id'), action, params)
elif req_type == 'desktop_observe':
action = msg['action']
params = msg.get('params', {})
await self._handle_desktop_observe(ws, msg.get('id'), action, params)
elif req_type == 'browser_control':
command = msg.get('command')
params = msg.get('params', {})
await self._handle_browser_control(ws, msg.get('id'), command, params)
elif req_type == 'register_ack':
logger.info("Registration acknowledged by gateway")
elif req_type == 'heartbeat_ack':
return
else:
logger.warning(f"Unknown message type: {req_type}")
async def _handle_exec(self, ws, cmd_id: str, command: str):
"""Execute a shell command via sexec (respecting permissions)."""
logger.info(f"Exec: {command}")
async def _heartbeat_loop(self, ws):
"""Send periodic heartbeats so the gateway can track liveness."""
interval = max(5, int(self.config.get('heartbeat_interval', 30)))
try:
sexec = Path(self.config['sexec_path']).expanduser()
if not sexec.exists():
while True:
await asyncio.sleep(interval)
await ws.send(json.dumps({
"type": "exec_result",
"id": cmd_id,
"error": f"sexec not found at {sexec}",
"stdout": "", "stderr": "", "exit_code": 127
'type': 'heartbeat',
'node_name': self.config['node_name'],
'timestamp': time.time(),
}))
return
except asyncio.CancelledError:
raise
except Exception as e:
logger.warning(f"Heartbeat loop stopped: {e}")
env = os.environ.copy()
env['SEXEC_COMMAND'] = command
proc = subprocess.Popen(
[sexec],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
text=True
)
stdout, stderr = proc.communicate(timeout=300)
await ws.send(json.dumps({
"type": "exec_result",
"id": cmd_id,
"command": command,
"stdout": stdout,
"stderr": stderr,
"exit_code": proc.returncode
}))
except subprocess.CalledProcessError as e:
await ws.send(json.dumps({
"type": "exec_result",
"id": cmd_id,
"error": str(e),
"exit_code": -1
}))
async def _send_json(self, ws, payload: Dict[str, Any]):
await ws.send(json.dumps(payload))
async def _handle_exec(self, ws, cmd_id: str, command: str, approved: bool = False):
"""Execute a shell command via the configured executor.
Uses the gateway's current exec protocol:
- optional streamed chunks via ``exec_output``
- terminal result via ``exec_complete``
"""
logger.info(f"Exec: {command}")
try:
result = self.executor.execute(command, approved=approved)
stdout = result.get('stdout', '') or ''
stderr = result.get('stderr', '') or ''
if stdout:
await self._send_json(ws, {
'type': 'exec_output',
'id': cmd_id,
'stream': 'stdout',
'data': stdout,
})
if stderr:
await self._send_json(ws, {
'type': 'exec_output',
'id': cmd_id,
'stream': 'stderr',
'data': stderr,
})
await self._send_json(ws, {
'type': 'exec_complete',
'id': cmd_id,
'exit_code': result.get('exit_code', -1),
'error': result.get('error'),
})
except Exception as e:
await self._send_json(ws, {
'type': 'exec_complete',
'id': cmd_id,
'exit_code': -1,
'error': str(e),
})
async def _handle_cc(self, ws, cmd_id: str, action: str, params: Dict[str, Any]):
if self.computer is None:
await self._send_json(ws, {
'type': 'computer_control_result',
'id': cmd_id,
'action': action,
'success': False,
'error': 'computer_control not available on this node',
})
return
try:
if action == 'screenshot':
result = self.computer.screenshot(params.get('output_path'))
elif action == 'mouse_move':
result = self.computer.mouse_move(int(params['x']), int(params['y']))
elif action == 'mouse_click':
result = self.computer.mouse_click(int(params.get('button', 1)))
elif action == 'mouse_position':
result = self.computer.mouse_position()
elif action == 'type_text':
result = self.computer.type_text(params['text'])
elif action == 'key_press':
result = self.computer.key_press(params['key'])
elif action == 'get_active_window':
result = self.computer.get_active_window()
else:
result = {'success': False, 'error': f'Unknown computer_control action: {action}'}
except Exception as e:
result = {'success': False, 'error': str(e)}
payload = {'type': 'computer_control_result', 'id': cmd_id, 'action': action}
payload.update(result)
await self._send_json(ws, payload)
async def _handle_desktop_observe(self, ws, cmd_id: str, action: str, params: Dict[str, Any]):
if self.computer is None:
await self._send_json(ws, {
'type': 'desktop_observe_result',
'id': cmd_id,
'action': action,
'success': False,
'error': 'desktop_observe requires computer_control support on this node',
})
return
try:
if action in ('active_window', 'get_active_window'):
result = self.computer.get_active_window()
elif action == 'mouse_position':
result = self.computer.mouse_position()
elif action == 'screenshot':
result = self.computer.screenshot(params.get('output_path'))
else:
result = {'success': False, 'error': f'Unknown desktop_observe action: {action}'}
except Exception as e:
result = {'success': False, 'error': str(e)}
payload = {'type': 'desktop_observe_result', 'id': cmd_id, 'action': action}
payload.update(result)
await self._send_json(ws, payload)
async def _handle_browser_control(self, ws, cmd_id: str, command: str, params: Dict[str, Any]):
if self.browser is None:
await self._send_json(ws, {
'type': 'browser_control_result',
'id': cmd_id,
'command': command,
'success': False,
'error': 'browser_control not available on this node',
})
return
try:
if hasattr(self.browser, 'execute'):
result = self.browser.execute(command, params)
elif hasattr(self.browser, 'run'):
result = self.browser.run(command, params)
else:
result = {'success': False, 'error': 'BrowserController has no execute/run entrypoint'}
except Exception as e:
result = {'success': False, 'error': str(e)}
payload = {'type': 'browser_control_result', 'id': cmd_id, 'command': command}
if isinstance(result, dict):
payload.update(result)
else:
payload['result'] = result
payload.setdefault('success', True)
await self._send_json(ws, payload)
def main():
......
......@@ -76,318 +76,12 @@ mkdir -p "$CONFIG_DIR"
# Extract embedded agent files
echo "[2/5] Extracting embedded agent files..."
TMP_EXTRACT=$(mktemp -d)
ORIG_PWD=$(pwd)
cd "$TMP_EXTRACT"
# === EMBEDDED TARBALL (base64) ===
cat <<'TARBALL_DATA' | base64 -d | tar xzf -
H4sIAAAAAAAAA+w87XLbRpL+jaeY0KqY9IrgN7VhYl8Ui5ZZK4sqUYovlU2JIDAUEYEAAoCWuba37s/tr6u6qtv7dbVXdY9xz5MX
uFe47p4ZYACQlOz1bu5ql0lZImamp6e7p7+hBY+WPK77gcPr1jX3k8aDT/5pwufgoIc/Wwe9pv5TfR60eq2D7kGve9BpP2i2Os1u
5wHrfXpUyp9VnFgRYw88N7Z2zbtr/P/pZ1Hiv+sDRTzPjBefao87+H/QbfUK/O81ewcPWPNTIbDr8zfO/4efNWau35hZ8cJ4yF6Q
NLBTkAZ2FgVJYAcePH4WhOvIvV4krGrXWLvZ7rNJwudrVvX5m8Rd8ho7sfzfWeyrGB9/DU89a2b6PHkKqw89j9HqmEU85tFr7pjw
GP6/WLgxi4N5cmtFnLk47nEr5g5b+Q6PWLLg7OXogp24Nvdjzm7dZMEsZgM6Hp8nzPasVcwR2IRzmn0yejY8nQzZ3PU4mwcRm69g
9wSOFZuF8x2itLORkHbY7Od/+nfYyF+9Yd/yKHYDH+bL0ZhglxdbMWAzWcffMtd3E4ZnA0xh3Tn/aeXCYQcMCbvPztbJIvBZZ5+F
brjPoiAAUiJ6cknNMGKesDo3DG4vAlZ58uTJTmSrhGmNwbyKXFIxkFMLbt8wd86ile+7/jWiWNoNfiarsGbAtO9ZZW94OTqqwN4/
sSb74Us8qs8MBp/zy9Orw8nV+Xh88SSJVtzgXsxLI3NLPRV4/Pwf//U///2vDPBOSlggjeMbNwzxocJGKhwrAZqbFQ0S/HIRqGFB
bLlmHyEPWLxyArbX1NdUjLmbEQIPrWiPx/0MZGe5tHyH1V+zkAY67POnrOHw1w0fREUcXz/Nf/4LG56fj88HKSAhqMRgh81WCfPh
ZPMARFZh/waEoYWISBh/+qNcPGB7VbVtvf5ayFmtkkcYRKSMqxveH1GafBeSkvkaJyvsCUM2KyGgaTluSPmjezhgVpikzJGHqsPW
kgZKKHYAgMkpgHodbnLEbvksDuwbnsQCDNAwT9HsUmpzmefOIitaS3p/32r0flDTUNIk5xwectArvu3y2DSBDoJQKQZALbiEGtj2
05Te7N27Ar7F2eI67f0Dq/s8u0kFDj23QDE5LMnEOgNgsotorbhXGt0uWmU6qOXcIdE64qgAXT+9aswLbLpusXFPMTg8Hp5eXB2N
zp9UGqs4aiAADy2HQOvZ+PT56FiO88RuaG6FmDEZnn8Luvnq+ehk+KQCt+DWqTU0r6Pkh5ioUk04QCpJGg57L8Yvhw1zBxZyhh34
c/f6LnSE0ljeOG7E6iGQI92qoj/N4IsbC2aIEa4stiM3TJT4tQvip88hubMR2hYSXOGTK0GCcF3RkSkTCUAtloHDfvXmrnmZuCgj
4oAy2rkEjxhxK+FMUFEYVRCYpRvHcC513A4el9QXHlbMXUVCneNpScQ+Y/V5joQNMdH8MQ78yobb8n2XwCICGVhCgYDixIvxb4an
TzSNarOKuwyDCE2xHcGF+JKFkesnVfnVTIIb7l8t+Jtqq1+rVWoEBq4Ce7oDt6++YsPxc+MtTK5cAzlurfXVKvIqA1a5jQeNxnfj
y/P68eHF8NXhd/UX48nF4NcH/V5lH+cTM31ryXH2XnURxAl+q4lRQodG6CjiYczfcPsqtJIFjQg5DkBzgbdz2wDrCf5IgyaJf8FR
FwsjDmj73E6u4MhgJy1EsUdDC25FyQxIqQ91msZ7A0+mqSiQj2d0dhCOLfTIGduy2R+9PBufXxyeXgzY0AFldQ8w8MvPf/g38OES
ptEXleQ6WEXqGaty89rcZ7cxEv13HISQCF3bAoqIi0CWVmIvFJRHsRxAS5ug/ymUAs87MVL6hNBZHkihs0blG4OSxQ2UE1PY+07a
5S1YyXVkVXKTAt9b13aqZvb55zBIV0pXZRsvUq+gjkq7pheK9FIeHmlzoYrLOkIsUhro7qmr0AEu1CPbdFhpBpjnubXyymaXXM+8
jE4E4oPde24TVbla+VfxIM/FnTAZhovJuzgJwnfg4MsvVrKKyzJEhJ/scnchgEIvGXheKzuxWSQw0pc8C5ahBxY97/uLn6cQjgGG
PMQzqfO0THETbSmc26VTrWibEN/ByYTl2m0nwBOSuvlOsB0TQysX4kYvuIbYKAFnCGW4kSzDDQ4ATCqe7zlc2leu7wS34t7G+0BW
zl6NTo/GryZXo9PJxeHJibl09AUUAoLv5wXrJSJ8vXIdLhYeDc9Oxt+9hMPRml86GP8FPuX8zzZP7OP3wCRPv9/dlv/pH3Q7hfzP
Qbff/3v+56/xkfkfyv7sVuAPyXAIu0GuLJnQUpLCeAgz7cWN0jbtTrfHvuixZg+ew42lpS6GwuX8BqYHIo5hMa9LXwaCpVd8NqH4
htmeizsYDx8+ZN8Mj0enbHQ6uoB/no8B+FkUvIabDYol/Ww6hczNOHVScTR5z+fJbRDdsL2IL4OEX81jthevY9BA+QVBKIDvXnAk
DJm2AWuzDuuyXm5QAYNgscX6SN8FuK71o500IgjaBPE530E0PLxQ5CYRbnh6pJHNOD18CRHQBtt5dDh8OT6V8R7KiHSzK8bk2fno
bEs0KFZdiSkQhr3NJr/fHOEYZ6MjEYY1XltRI1r5jb23iNV7M3RBKZ+Mj7VhoHA6TAbiEvyVJxU0oRXj+Hx8eaa+pDkVxwIe+dJ7
S6MRjJgEspucJjmUZU0YyDp+QY97lVgzCEO0iLxHBlsPdHJ02LGDnLE5h9ST/uLQj1dRGoYVDgK7FUPuu2KrXfM3Y9InTEzW8NxZ
w4tnpCbq85VvUxpho8tmkHdUrbG3uu+Fz9Ad2kMepp4nHYYIJ6WhUspCwQDEehisZZNq6SgAwMiI1ZtiuKKjVICkMQGRwFSZcvFV
wrIKIBjCqUkE1QfCyFXks2aW29ITXTRjmT9IOihzWfJHEqwgKqnsSfGWDvgiuPXhIQr1YI/EuaLNKZJZuN79brcIR4lvQciePs0D
+7zFPs94s/cZhcE5vGOP85C1FI8UiUt82EFtndIkEaCdBHkJiIJRU5TekDkUi+dp7ozAmPJ650+eSmxLEfs9CmIQluQwEG75LyyH
NLk8MzcH7awLcQNQLOY/gbHoNGtfMifITZKbf/Zh26vPDMT/pjQiRVX/CIFomr3ciBP4+UvwoWTIGAMeOwRJuBi4k/KlRLL6F3dR
LcO2laemW7yR+Nl6a/PiC0LDnbsElZIKafHDzGWyQ4H9nFU2p/s2KVKQYNuKOYBvVUAMxMXEK5CJHX1Nv335pZwThPqUICzOkBHs
lklF6m3cJOJeYDnv5si2uvhSK1DlnJ4STeJViPk57uyrvYtc/nAMRPidbXp+eXo6Oj1+kunoe9xq/Oy+2RLQR4p1Khl3Whj9CHnJ
LZmaDeA1wWNVzBpwPBblTXdt1MyNaPeitKcgUngdAWO2i/E7cL2B5fVWiX7ACF8RbxMT7kUyWdFKj/aXIuHHUEwnHhqivWoVVHZd
LWS1WlGAHxcvzGUMlBywvSZ7uzXd9H6jvZNQeWzZhkHPm7uzGuX4P5cT+SQx5u74v9M8gLG/93/8Mp+/rZ4PdT64jkdZQvAYE4KG
8fixbPwYPH7MWmaTMXh0ZCUcvyMB6s1uvf2FYaQZ2GJSkYkmi4QCnFVY6hpJt8dEh1VMdbgUfME0EWPi13gNxF0CHLBJoHA9ywZQ
45D7zzzrllH5RxEoDfjrM6KmFdkLNwGwEDmahlGv1zFvwsavMQ3Nbw3jYhty3MfwFhmDqY20CyHDCncMQAFPJi/YDV/HJgGImTwH
eujAMKBKZHlp9ea1q+G4zxCiyr8zlIQIIKJ3v4BfrheiNBMiP4ACfsIyGpkMMacgGMk8VUWwKU6n0mRGOISRyR+w81CjCrDVmE6n
xrk4pzhDFTaM1/uM6kv7LA4t/2YfDgFy7bbUL22hsH/+wx+1PEuVdCjGxS4cGZygY1W3klTG4l+28MXFxRk7PBsZclRkwKrrYFUj
rADZ3/A1+4b7fO4mMSJbZz//6Z8Bz5Tw6CxhfwccWJJZznnuRvDN8+rzyOW+460FdhmLgNg1Ofecw32KM4IKqSrRUs6+wOKZErEV
Wu/EFb0EKTjLq6N+SAUnTiC4WGLBOJXCs4hTc0oMvIhFPk9R6wVQSZEMUVRdN+bBr+AbNkfAj3Osklk23IZYdDaValq0FDzNmGGN
kFVTPtVI9OBhH3YBHsD31xBYosgLRHRx2Lb/cIP05eo6dCmnWlplCqtOZdJQIg7XRF0OFA2NOlh6aQ1SkujlH4EjaOIQZ6hCopxp
GGNfVzoIdmCgOFF/n+2w3+cSu6G88g0lPdRQZWotoEIUSWffguM7MFpmuumGrhpWzZpR4I67wSJJIARpm6qTYCq2n6oLik0/Rscs
NBo4IL42iPaaTYupqrpEtTE1uiY7htsR4cIImBosRWk3JjXMLXtB6tTomdsLrkY/3dsLZIfDNE00btgXk45TjQntASZfQaHK2q9s
fMiITiTFmGLrSTQpUTd/+MZCE6NKdkJR0QTqQkiJfIXBVGVAMi4K/UBv7WGfHs5c37myHAc0YYw9BU2T/tN6EPCxyI5USP/hLKs1
a9sdp8t78z5GaPtinBQjjs/7vOd07c6sbbW0cdKYON6y2rOO3XV6vD/XxqUqxRn9eY93nY7dnrWs0ow24TCzHT5vYQ2hL6PE9xAO
SyqNqNfD8hM00BPrNbkD4DJIKUAjC+r0EfDd52RaQOByovEoLrSraIztDGQNNL1biqPbyiTqMosg9R6zwItXRxGpLCqLCmbTTmmB
9A6BFFC+C1YsBsPsOVjbHNDDNN2qW3p5IiCooawQ88jO4ky40lJAsL2ib2QWjuxotHVqL7V4WcwmMEuJ2h2oCnCJqipbn9pFG3tA
UJ4HDVFgIG2GKFHTFJoNrJjgcUVSdmC8pX4blM7vf5BSoqHkB7dMZHnRQQEdHMriSOZbxGZBC8P1Lvffyht+DooP7rWQOSq6k3Q9
fkzyJb0n3ACk855uhZlX79RhljX9AtqIjGE8j1DXgZ7X7cdApyXWvWlLI7YhTo+2KX6tjq9ajL5ufdE2W/1fm1340eoNqDpvSJiE
//1hylBBB9nJAyRK/BlItjsts93Pw5RkvT9ULBhlEHtNs7UJYPsjAbZaKbz8bWhnBlzIF9wnwWB08IDbyGKEqrE2jhfbOMXYQ6xS
WSFgFEbohJJ5/doNxb7nKz9tDI3ybgFhp1VJP5ELoFv+u007mPROBlpvmkRaTPOVxnKBmhwCuRnP2U/hjekuwp0+wSV1KonHoMo8
iNQ8UHz+TZw3Ecroc8k4arRBtmn9ihqpfcsPSo7AJgdA7i90y9zlnkOGIe8GbGpGLFzeLZ2IwsjnexDPDicXwzo1Itafn49fpi2N
oq1nU3NiYxEseSPtTfzLdSluMPjgT081AkwHWih2eX7CgnmhexDugtYEODrDCGGaUgXWv1xB5CGaBeniwVMsuhQaB5XLgKvpwVTq
6Tkq5Wzyds+1EBVMM5oCqDP4kXY+5mMyIGEuyNhoV0X0ljdUL60bMFAopGIUbnLaIE7REEYlsI0mqB/GWbwi1AcmAr6KanOtCMbl
rDTRVk1IUwk8ioIo1s1fT/lfWu/DnS6Y1qJ395w/z/1Ke9S2O1/PpF8BDMxivQHbdk/VfFHjVCHZhOwJvwZJEE4qORnktzyia/wI
HBFt1LJvwNEBzl7jix/rTP7l2yaY2aoVDFE/FZ9nqStkGN8AKDRJRUdjn9lErqzZhLwu3aLc4bqxd+zHn3KyAaS7HeS1W+rKUVTw
Vqa3K2UVJsIO0YMJAyla2aBnxckVsAX1XOug1e180W42m+nwKkQnBcfanfShJBdFMjJWoue2FVoz13MTV2BXwTtQ2QcM1rHrz4PK
DzTzPfz7g9JcOZcSrMYFp2stOIBf0NmbuGSynolLVCBm/R/Z2XhysYuoDaJIg274b2H3+gtWAXZi9qx+sQ45vrATejJZ0xCtITTP
YY/eVuTdpROl1/cHOBaSBm4oUqf5/pHGOYxSQ8F/GA9XSSlMdR3ix9Kpq1hSGhGNVyKT6qgBR2wluPtbP30M+gEfi+9Y17iysW17
wJriCaoP+IZlOHrgyIjuaon7QPyoWJFSvD1QpBbuwZjO8EuS3ZmjGNUX28iOrucjU9DoUe72OG4cenA74ecN+HygmkztpCBwl7GI
Ab0QwoiJeFklPekWd1a4XrFwC5Wip8Om6vsjljpzVv+YLcUdLfAQzB4WGkSqLj1Pqh/BeIvFFLWIUEMFB8qnl30UWa/1Hi2l3mq2
zdPfiKdYmFq2QiFf9UBTw3eqomoVg1onylqii7FBwq8jLZ8oYniRuZ/qiCEKU0amWW9m0ox8wqa/b5hijbLhRQiNqZQaKj8Kf0Zt
+QpfCgWfhFkgYUtMsZDNTzP4snQBoe0I8y3oa1EeUgbzA1QXZCLBAIKaeAp3RPTmpRKLIIROJ09Plhu4bo/FlYz/T9zKzo5bWTzq
CVhndkzyJnjiYxmHkvYiF/ZJTiyBfYozY84ST30NKxXC+B0zPx9AByXQF1GwworSAqLi1OyRW/cq8B8lyulAKkzWyzAJlpjQO037
VskxIxdBS9VIlzH13pBsEJmWU1AD6YrfmYubYrSqKidwiTx8y4DeJkPvDMBQKnc1v03nd7L9MORwMTaIML5F1aNSSxgrQGRKhRMR
XgDzZzy5BX9ENueCDVKoiAV0rCM+W13r0vCQjcUK6Zn6rNe8yzkVi3L5/bsWlxKLyK6RD+GY64hjbOJUxqOKmitCS5oceFQ6lEwS
dz9/ZEXf+6bKiVsUeCmapy98aZQfbEgz5IAAC89FX4XWKL1DZGR+RyyRtFF+xBH3Xe4UiKMGRfQTs4qYNWCUzy7Q5mKRlc0oSktT
QBBRiuDN4f6aUrAmG4JRwExOy6Si1Wups9MZ8g081Pyb4zedEsrTJ/kC0l6CCrdAP8/nPBIFWOmX6idWf+eAHWLSCbh+1+EtOe9K
vZt+TxKIs1fA+lTk4Y8hzqdXxhXIffGXCyJeR8skCnByP45qDVvppsUg5a9qSpRaRc4Xteg+K+L6/lF2+5SWwb+pAOIaBr5DijRH
7CmeaMCqBzXtNXOpMKc5PfkBlQwMkulegnHBr6yeeH4IGp8awKhAIG2evEYfAl9eo7y1mIAVjNyEYtHYdXgk31QnQrSUIlWzYJ14
IroIZEEcpOZ++RdWXeKt70M0yIJbX4TMi7T4PFSVIragPz+x8t2fVrLAlN+4363bCyuywKuLVKpxwd9g8Rs4hYXk0RwACB1FhmIJ
3F66sWiFvFaFTBR78ZokmwVJ+toqPdf0miQH3FNVVNYJosRFVGrI1ZjKQs0UDyjftGRVNHSUa5tjZwmeWBGdYdFQZi5iCPPcuWtn
U0FBlqqK+YxGBVNbl5S8lAY1WmFTCTqOHIliqwL+AO6KEhjGUhtL9lcoNA1ys9HuUvkGVFxqmj9uWV/sKwgJZuAs63iYiI6HnABw
n1pcQSMlMYqKVEm0XwNVbgNdY49ezcg4YFs+OvyzdWjF4pVFrbMiJnpLDxpENlNv6k93gE+IqsUtqLriOtxeLqRWGv6jiMvd5ZI7
WAjw1uKY6IecTBqvJhNWfb7CJhg29Bfo3mETU80wnq0i0vYqWUStISsfjEe0DvPvFVUpj1UzqUICeDkr8ssGku+0RzabxJPXhF4G
HJjNowSFykqozWIodkgjBhnxwLeJBKgt2Nh/Qq41Np0Gc5mpzXTKSwtF18eDCkJ8ixV7xaST4FoLHz+s6JoCIycoDwlrNVSBEdm6
+6UTNa2vHJMPrkDn9KoOaVMutYDlvRwfDUlZqSjheM9mEztkegexoi+4dTvqPQrCB9NBIruJDOQnZ00jG4pim/BVHc+7sBWr70XX
vCV8+b/tfdty41iS2Du/AsMOL8kakrrWTWHNmpJYVdrWbUTV1LSrKyiIgCiMSIIDgFKx1XI4NsL7tA8T6/HGPqz/wE8OP/pr+kuc
l3MDcEBRlKq6py1Gd4kEzjVPnrydzDyBVP+Jnkm/v1Jpl842KrB7h1wE6HSqCBOnDZ70B/QeBOL3re+PhZMWfjV9l9BujG4SwudJ
F0nxSsMnDM+1UiXVUYWVIKbGsQ0Ep+9j5f/5z1JwVQNX5uqQAhzdQa2goJbW40xhGJI827N4PcwoQRghZyVQRbxnsRlHj6aNEex3
5acgBGICgE8EBrV/IPxkISEmzs5ORKvHICMMBv7A8SYoGMgFZLqZgwKerSoJHBkXjk4MySgzVo5uS8TYGs4WdotjkJaYGG1S1mMk
g0xS+gA8EYBnIGkhGJQ+nTowkt4dq1yoJXFHW7ZQsSJ/QbTTKTOROGhCfRhfmsDVPQgDSoU4mXDXUNMgSP4Gj0JPoilaowiIPkLP
alJSaF3GEndbYlQzbHDEd+zxBLsuZdmyec7CQIURR5zcyokbbiVvcOcANtOiIrb8SI9Aot4TSZng69EkGodQ/8fSjw36iD/pr/BW
990BIQ1qnhYQ3aPjw5PD7UPMjXAKxfZB8CIjXxgNYVfimpoOw9ixWnq2F89oWwrWdkKO3cmmhAcR2u5TXYhEKz/OK7Xrmin/LZqH
OKVNzUCk3vjxTlMQNm04BIiZQ1OGF9Cd8NB03RrxjH0Yxi0FDKOLYnhk4XCgdQIJhGpoMLJabvR3w4LdKYogkTpFmAEFm5EcG93e
A+EuAlEWGuH2aCd1iAj8OI+duvPt7p5EZK7mhb0JSrBi/xjKJMeblUoopMI2nuB2jxxQ4mKifRvaHEUGrGpW3yIXFnE4KoP+pRHy
CtUtdF5F8EfK+5RsfGTyQ9pHcmwcCHcUQQeheJygE4rwHUWKIgFH6hYd8g5Tm5RNndACiro8ExT25dRZ68WGMuZWzIXFm+MMGGgT
SKcZ/5GP/xGqB7YZN5PPycNjTO7I/7G8vpyN/3mxtvr8Kf7na3x+7fE/2hntd5srL5rLJTwovaahwYPm89fNOwLkfuWf/P7fOj78
0Gkfdw2J4YF9zN7/q+trz3P7f3l95Wn/f43Pr33/f+NsReE1Gs/QZh8Be9USc+/CH7qsaQtO+4Y4bamEwz8T9XqinuDG6ORNNkaK
UIuTaMLBZBnfzGQ6Jjcl0UpXtMIOLOwYw8Zk6TXXCIQTDNAnn5xdLgB+XfI8/VETrR973pjL6ZMF+ZXdO+nlGEba5W7o64p8HLlD
HeyxtCTtiA1l46UimDA2NoMt2IY0DnFFJJDmmW83ErXmmDgUnQzI+ye8/JE9evA5DPKY3ugxskNs1rtqDwGHnlTvAFCNPXLZlWZS
1tL33MkI5GKBEVKegvd0fDKgt6f3X8je0JMAtixebrW4H+uClFmSV7/hyZD9nNA51hugqb1OIDF/03f0oXLKbpKA5C9Dw8uALMI7
mDyjHLHoKAxzSQeb3+C4odT51+vV1VXVDMqoIqjopnwdeOT9u/J6dZn6RrQkP6VXy7fKTc4824KP8okzwnckQuX8xu6DSwbsTfzh
J3nYUTiUaNb08FvFw5EXy69fN1eWX5dNZzHhUU4nfp+TLM706C2NEN4uiDur8+NOur95cUj6TA6nqZr0TsfYQgnK5a5eBaNe2Aft
LxQnktphEifjuYnb9YJII5fALN0iuoijX/NSEi6BeoME+iE4Rd1ydjJoeRs4dzjEo4GIVVvD3xPRmGbsjxrvO+UUVv4QjuhVawha
Xc9dOvCvu9+BWqdL9f1QJsim0aGdLJkQKq0vN1+urL5CX5hw1JdPGy/XmyDiGEM1Dnno6Ndsso6+reIogwp8UtVgZSK3S0F8tKcj
WtbyHxs8WRz2lTuY+GXdU9AfhZFPdeIu+8TkFvJP7pXbZX28y9HdXnZJ+YSq24vHsvYX3bKrRVtW4Ge3AGUt6GpuVlhL5wgwJLtN
R7DGyAcX3KBr829Q2VPh1iyaHW2fQ2HHrvPBW1kc1JYxcQqlz2EO6HtfaFnWipalQJ6YMZ/UsrhXARpWcssini+4LOv3WBbR032k
o7KIq6GtBcxRBBU1oVkZqnrtBkl3MkoCKohZjngd+StsdC8c9thXBJ8wjxZmpMBjavgl1nG9aB3vmJJ2zV5l/3igmAlTUhmSvBMO
XUz0aLLIQdC7zHFGfLjgwj6/B0PEbu61qjHoGxiBhuW+iSdnwyBpnE2SJBzJVRW/cEVB4yiz2GRuT/2mTEI5fhkGnifZG48KZjch
MrVia2E0GZ4B5wrPHSocc0XPh0mjW7tjflIVqYhyIByGQCk89EdAiyX9muAtFc4w/kK49dyCWyY2vEGD7O6InOnTKAH8f7AgRryY
HyOwl8URAmUMpUY5kt0SaVOvvhBgX9wBWHQ2c04sQih21H2A/PlyfuCqrh6w5Xw8aZLwpbYw4IOeomE+klE9ajOsLC/P3AOYCgXm
denHecSng2eg0mQY6IhxZAFIZBwKdOVAFwTkq/kBmetyYYA2BcLI/AsJsjkUFIM4AElP6ous7zEH8nz9XReD4XqeL25msChwKZh2
QBPxR/FFmMPGWL1ZEIqv54ei7ute4EMDEUtpKUm5rOJa0fNDt90cj/rlDAbGlG4iJKPTF6IHr4uYuDFreBr8Yevw+Hr527f9sAWf
g877i/b7fqtFGTVo1Ftu7L9Yd/wRHjV6ztHB29IdkzVXui1OtGXYUHq1xXm30C4WNZ4sz7/k6Q7vt2u4ygaKZXxo1mTx5kvZJZaL
VlA/mSFWtZH3oO2h/RmTWNGVdFnoiyKLwv0eVivZ1b0g7quRY8lqzdn8nTqwbBKtlyS5NRhUK26l1hz4o36i2IMb9YV9IbP/4AW1
gnuQfCtxL4p01Flaha7iwmM7C8C+n3SFgL4oDO9hvTF6mxOMXwQtZyvfbF35j7/ZOdw++e6o7Vwkw8Hv/iP9SyELGcCe4A6ygZW2
1qJAvYfGrfr6OUFaqDjPoz+RVxKaLeKcSRreEKOKFwXkPXRk3dlXAVmhjspDyAV+2xd2PkV91kJIM9en3JIIm699VYSdY+GFuYeO
m+rvq6yNTcczDT4z4vKlyaqeWjepCq/awb09QG83m+Wuh28eYrtbuYfqqDu7Ly3Rkyg6KKCmH3ZOsHIPRS3V372NkVkOylMrODej
rhad0j1UJuqncAXSp4CrG86ROjOleAJ9ErhDKYiMLIxGSURBKj70k4uQHDfTk9UnsYvOOKve6BbzGx+PDO6pGoJgxEcOzeG00RuA
aCTPF8qX1+LlTcasL0iiSDJ6KrqtSi2zduo0iNeLF5R6B7ju2ZRMAVX8R5WBOiSXnU0d2ge6LADDr+I/trKt492Wgy9TjQPdC7yq
+GvvAuj07g5WInFSqdPpwf8e3znyUb501wXx01IDvYfl4ziLY5j+aefIqW5fROEQ+Il/dRKGg1g5GNRm4VxRpRzC9bzxoseKWa3K
5jogwseafCx0fxFKOCQiIORsBRalW0aYtumbcqNMIpeuDDULw8pLLrYVelO54pJROWfwEMsf+RF5RY56PtbZ9zGiLJbFx/otbmR8
hZWO0VI/9JtKa8IxSSXrH9wrV6i42D7m0Oi5Y3Tt0KYOrLDNDx2tMWP5ncN9HMeO0G3kQOCx0nfMvJ7k1KI9LeZl2LkVzupvthXO
zvoBCtw1Xb3XlCeZzYvIP5eaGof4bk3/IAym4tbGHH1u4xGlciaJH+BGghD4nBVUtN+ISgpT5i6lNy3fQXaGnrHXGJFyjaTkOgoN
s4fe5+Qvss/5RUT86TvhTECPnaqQd2ol6WcUTUaxymv1frfuBJ7vDsj+6E4SEDqVpzp7wyOPUTDIuywYXHiVO/c96jrfoSNsedTr
JPbPJ9yth4kE+nidQVFPvmf2s9Z0WuwdQv2I7xTcIIJ8pG+UjKGg7N5AA5qZ5ZR9CLcURk3lk1LkdSKGchI67CojiSWn8eO8mmpO
GyqCqx+G/YHf6HHhRoNLNlTJBrobbGIX6VUWEhvZ1/Uy70rnB/VeL/VuHA4wx7cj5CUe2Sg0E5aTHTYLDylqKc+Kbi8RKGxztrjj
tHuVglZlh1Lw3DbHhGZKHGZ4GWByFoJ0B5gZbIS64ye9ogHqjvUIU4NRQ8z6glg8PtKodQBdeGq0+wDQAElikIEp2fIpakkgW9FQ
cQAraUk3C0sxgp/b1/KX+Mn7/x63Wzv77ce6+wM/d/j/v3zxfDXj/7u+trb+5P/7NT7fWK6YTN+EsSpuwjj2x2EcYOpWNvEFyX+C
/5va73cDQWQJaoZSsPu3ozCOG6DsJCiaGeGb4iZue0hdUyYHiue4VcLR11RQKKIMOiRhS8euikh+skEbcXm+i3IdSvkOiLWp0T57
tuHsBaPJZ+pBXkAs7rKi8uICTiM/EdY5CDPXYqjo1vPsBQ01aoZzW6Tj2rEhjp7HSRBgZXw7VjHSFnBwJJanQE8ZJZ8OyKU0DFSz
BdJIQ6VjxWrtzyBkYc8A4TMQzsPzcw5BjUUhqvdOpmnFKhj823AHwRVK2r0LdxTEQyolTfWOmTARa0ixRYh2dUqFMUn0EyMTmoC/
dN/FJcDgVleHsEI1Wt98Ft8GRpoJGKQC5PD+CBSZ3FhcDSpOiXlZMXlD5I4vAPoDo5NqE0BK6LY7AkbfAWl3jGvWETe0RLCqb9/v
OoBygNWoYko06ah7v3U6t+pBp7Mv0m0YkXu+h/sLc8RhOxMYwD90Dg/oMcgvWH4v7DvoOSmn7fIangPiXmi45S+MYNDpmP/cPQ+c
4+Wu9MSYYppyChmZd3XQJTcyT25WfTQvgI5heFuTYOCZIIf+JBA9/wpA0rsIRn4NE4Zg5g6Q//EbX0vrsG4Sf3+GrTTHU1GohPF7
mPn6VBbgRI/f54bWUD3jUp8ierS8IUyEUqui04AM9aN8z4gKsV78DecYTTgNcuZxAoCZ89M//Uv2UghD5DQfP3tG60N0dXbAJxJi
ARMqvb3xPZBKQKvhDohg5qS+T6W5SotOucTRaDUXDxsy/ZEtb/RwSm2Low2ZORpzFTfoVwMGIN5mMkVLofCxkkELSObDO7YluZly
0KdUVQzdSxBuShiFAfoqxEORqin7X+J7pHeaLwlNpP0Z8+owcS4txhtZTRHMkW80Em2qC5jQegB0IBhPeCvXDaNDLK5QQnRbQq3Z
mGA22pxWv2SIeKWf/vrvP/31v8J/9mQW6sOZUwSfrvZSTLFmtJJR13EPGc18k6X5BqCNRjQ1ciwfQcM0fcjXbIgt3hzHK6makooc
hUA4Oxf+IEXZB37f7U1rOaCYMgzTM9leNi56ZlXBj0RVJhkY7BRQ/qUEfcoSYJ9m/9kQ3CwkLGn3ofZfRW0BhSUbFA1o6HAaZMDE
99kJUY+D6aJgaRnEyDC+TE1NSzHzarp/zUAl+NJ1JQW3jZ15hEZ1UVVOXekuhVVxYBFnKorTZoAdM4acpcDUVYiwsfFGmTfoi5C9
+I1Kf9g92Dn80OnaauVgrqsZcZaysBJoUzHhKmKAqslVVlNO98RQTE9XTfXYZ1Vbka1Ayr0yY8LRYNIPSPw34vHH9DC2pmc4hX1E
Hs5olTevNGPBVmsXiygPJJFnKO9ihFZZ9ji6sFQyQw0RfDKo8KOIKvxUFV9qHF9IMfYUtSgpfnNmtgGRLB3zk7RULtONgjumVBIo
gCZRuxomK5jF5ItqCyyomcngML+BvimO7xzLSqCU14c1MuTnS5jWFNQLkbZU7JMRufJzEiKogeIf3dYXhZh3TFgeUa4XqeQEv0XD
ISZPQyYAkwNU8fCcLxzjPiI2uhUkvRAgXN062a6hrnB61lv58+D55WB69fJVsuw+7z9/PV1Lej9cvfL/NH09eLm+/Hnth/5yrw8C
G8pGbcyI6U+GTrV98o6bgBLnL5dfemvP19daL7bXlle21rbO11/6W731rfUXr/3llZfuysrrrXVuohMOgOA51c7hHjfw9mXwn3+/
Fvz+5eXz5HX7df/Vdy8nL4LnyXq05q9er/x5+ej14avdl+9ffPdcYtizB0TPNq3Bs8++kPUqb/+x8vIH9THb/rO+sgzf0/afl2vr
a0/2n6/x+eY3lLkMc5b5oyuhTK0VXJv66wkLL5fLpaxQOgy9iShqNUWVdvHsjoQy6kBlw8QsjZxwUSUPTQvU3F8poOtrQLGcgsgW
yp9n5J0rfwGVxrOKEqVzA7meiCa/2gl6oLO0RtO68oPkYqhbDYIzWQ6vkCmVEtBFSDriIsrJoEndd91x4Jjj6eoSdSmuqy/CWF8n
94i6OMID/Vg7TtAj6u5dq9M92mt99+F49+27E2fTOUEDvP+ZrlvjC3yo8Ia99BsVeiiXZzMzCnhAXhqbsDwj4EMIMSomQIfnsHv0
rNolxbXbrZVK5AlhtsSkjUcBq7NPQm6cO9mKxU214kwCkUPdi0uNNHFpsRH6x/PPnW4XVYNuFx0KztlaQR/82dRglhNIvRbdb6gV
/iiG/MlaXI5rg5DjI8h62TXDeje3KP/SrQSN38m5pBsSR1vyGCXfv9FcfhjksWeOARdI9yzOmrFz/JquOwBJF0qKTdHcg59VfR86
faF3BFq+XXcQ/OBngQursKteGoiplgc/IooyjXS6DfxEbgDURZyZE6JWy4Z/EFZXierIsLSBd8LKZ8Y+c/7u78xfsgBJo8FkWM7M
0Y4gLoaJ5DZotdYkM5oBJ94ETbyAJjVeDTBP9JiBaHwxSTB8zALPbSDAI1UAI9VcIy0G0O9wgglxUxBWVEfrXew7RnOX/pRmAdxR
veQznfaZKN2kyKu4Wku3R6MnqEClJrmEGWAQi2xF6YJmbGWt7VqnJaBhHYDcy7M6lmebc3TYScKxgVHWLvXrmb0a3CCGVmf1m0Ks
DMdE64lCD3mVj4HYgui36Q85PcSOv2Frnlw1qufCU0OkxpRNbzg3/q2JvcoxwyDNwvBOjjaqoEZzdiQgJK+rqwGQXgl6VkPipOkX
MNlPqb0gOiSDoVgyE+25RWhLfIFJkI8df6Qvh3qPLKpqZvAgrqcBJ3JzpItLH4psYeFPkSktvSzqRV4WugEp8JMVMd2K+QqbmkW9
clsfkJLHnMfFb+Sxns2rJFfaH3ljUAsTBOBN7i2tkBrYhnNevhGTv1VOyJnSePp2Hn5OFW6CRj8AYa5aIfhU6g5+WV6u1HAi4iFS
KQlvf4DnfXf0dO2fXQbJHB2trs3ZUa6f20J4AbgU6Gg9zeWsyyZrufq5BzCuNJJsmhC3ztykboqRZYmPbKMpTwHCK1Q8vXFVjjs/
OH9gG41a0cUHI5p4+Fjkmi8+FG7hfiOJ/a+8ELkHJqs4L7dEvGlqh9+YwLrVZzMJkHjR0W053ZV9ZooFYCrmIrphdjYf7SiCy510
pACZ7qIK9oWfY4szP+vqdU3N1bLZi2Y2x7qi03iWF6Efed35WGk0RmEjBj58Fn5GStZoeEGMp1eN2E8mgafefZqjo2+clucZ52Co
18ZIfPDWgwBty+xrDcN2whH6TGRbUHW7XDc96sxbnIBlWNBfthmUM+5LABFETcxyO/KqABY0jjdUu5sV57dOpV5p/glQvprprjYH
qKy7Oo0UTSHzWEcnZY9N+cWOqTiJTfwn9zY/xvT25+0JS1aw5atqBDfy222tPEMQFbfH3pTjCTnRw+5By0Jd+QrDgzTKawFrQ823
nvNJ1WIKM1qZnu3WPpT7y7T6Lh5ueUn0plR9Ld3OmCsJfXXt1AxiatWv3dpk3XSOsMVlXpH5zDVprBS3bWp1sdIzx3yUboEtDQTy
GIswp6htxjRldj/5MFjkVgYce8tKg0R6+DnJFj/fiLTRqL8qf9M8MVFexOnBaA/VnDxvQFWVspMYVJs3bdokJqGSy59vWFSVMDov
d7U78k3gVeFdzSJnFosYjzOO1NKhubaML28GPtsmlGHANjY7fZQ1Pspe0CQF3+fhRAL5hf2E7Fa4Ingy78cJJeDKzIR33ZhNkylV
TL+B1UailYeFNJFlrGvyE5yb7dtXQfSsjSMyFVkB6HWX5xwCokFN9jwbnBVoqcRH0QSCNWXYK4RrmkcwjJUKD5RQLtStU1U4uXmj
vuY4BH4kcbGLWhluUSABpiMh5SgKSuvQGfGtoJzphV5MIdJ8Zg72YsAxz2IYSwyI5niLCbLF+IvCK8VZFPAMuzE08GkRRsM7baRT
3SkLT5VCWTgEY7bRMUeQuirzAlAic8Bp2My7he67debcMnPKO3n8UxiMyJshoyogWyPaQ2QYgWBEhh5JclH5AaU/TxbfRO4+gW9i
0tRQ3aErD+mbzsVHDwDAMgufdXsi3nCKI9ydKBNRnqO7cFWmMmR39PfHeylMTCGQxjxCITFuw8pHpfBxsx8mYRWmYs5iU3+ty6Fu
ir8WMcZKBmeSP5E2gQaAfadfynwJxijpkbEVUmuIt1Xj9YcFi0iOitYVlPG14ucii0IJCEFLk5HBj7ImasQ0uhlLULxTjTRd8quV
omLKunlgQ0ciD4ATpeZz6T4RzqH9KICSoxdwokE+FrjqOgMf/bWCTyWlmwu/iC/S1yK6wOnlJGSXF4M1ZesjvqXvBT2b6h+PAnqc
uYmidEhPo9+kfx9xGQR7wT/WRcgltJtnMQpXQHwoiFKRc52qbpEFUVkANZ1AIn5G0oYr4jUfY1GskBBgpF426d9HXBuZ84/+pqiy
uKWJg6IKSHM6o5p92egV/bgLzDJdnI6n52vv+r4W5EahmGLtUQAuI9urPMw74WjFX92IZf46DF48cKP+Bs59TgnXkmXA0fdiOxy+
vjgsuL4qngaKHjsNex4sU+H0/CXD51EyL8IlHaBghaNKvLjhnIXhQPr7zCAEaBFdTKs4cS9RpzAyGy4MX9PSRQNKDdcAujF9LLeJ
/xiz3lTf7pSalaxP8TPcTpkvAqP47FHfFOhzphk9kO7ZNKHTD/sw7xyb2dKLdTxpIH+1JvzghJLVbF+1pufTi8okOW+8qsw711Ra
y3S3ubmjPoG9B6M+pYugMZWtG9tIumfBybuwiNKJIK68O9nfc0Q7i2OSaCC9HHJ082xNnaJPfCucM0vrD5gxNbD4VKl6eqJZBaJ4
mlL7oL+FUwStZcEJ9sQ91zSjB2lxxXNIq1cpIkquVZOxXVGSOcgWmJrwtXKM23z6ftZWL60X7MtmevDdV5U/L5MnJjZ0Hk5GMMYb
0fitzXQv4Gqxhtj1r7RLFAi0M6vOsSI5y4n9/MRMnWY1ct1jJUaU7UJKPnTPKkafJTENIc6uTcp6Yy6Q8vS8/xpJv1lzmcx+rGvF
JrSMGd2olF0ymzeeWrE7WlBfxMZwJmOA1JQhxP7LFGBi9VzlQpuAebhHxiThj9EmNk4jdzPAK5irtSJRAzeGHGiTwP+b9JmEBUgZ
czZOP2XNTgF5buqeM+PdCqohj8j0CblMQWDBYrR9dVVJgcbpk+a5EHkP2gE85vCrJXX+PhmN3d6lbxzX/3xHgqIz9HvPnKXXmuR6
kvMinWvftFOOCKntk+7FuoG+0aFqGM4gguMwapz8KYSPozxW5enVZWwZlo4dwxNkHnDp/oaTOEHFFhMj872wIpGSODOlw85Kxu2g
YhefpHaoS6f0RP34IcpirBRFjduSZsrUUiZ6iXiR63Ay8OhKbsw/B8p8MKIItGBETkeyJUxl0Y9w6dI293mA+l40TinWHI/S+A2m
DMFcZjUrACl7qxpMzI7Wd206TD+rr1zXta3QuMc8VPoNPJpUrv2UKGUvGPnEoCg1YkAKho0zzjWBvNM3p4ko8J81RC3lkF5L03nV
zqZ53p9mAZnnGa/uWQEX2XfFJFqkkTPpE4HFK6ckvD32eCtECBrz/LiAkETj+pVvkRvoSRe5EEzjo+arxArlEZE6V8txxDRp1I1J
76X8yeo8R6AzTxeowBwnDPi5nYdx0qhFdzz64r0oMel+0FdxCRm/b3pYAHw6RLZHN1hhn2puBvjFObX9jJphoTIu48kkCmd8MFkE
WbTr9AYTz5cnrIr8kjsc8lCT3d4dYHHPqeTSRs+cjzVmY/YE75S5Yi105RAIHROvMBrMw9zAsQ2ztGtlVySeshrAxLs5zd8kKmwQ
1goCVXc4ie8GsKK5Hbkkl3WjsyCJ3GiayY2cJsXs7Ul/AIUNjOae4Z34kva8Wsyg9g1IbZw3i+AizH0y8TGfwZVl4FhTJiZuNps1
cT6HX40xRAk7fVJrzXg8CJJquWnI3uHZn7JH76ktG9SpFRJFRpOhH6EJldr9uNFY+ZTZsHwFAnI1N0mi4AygnDYNJnRyAYDAb1Xo
nJufGeizew6SXwUYpkhCXXeuMWmuOH9n9VHEv45wnVhDynlpImgTTC+yQV02ubEqlvsYZPxeUfPEhFFnQH1xpJagK3STRMSz4ixD
leZI7S9b/GoJHhq5Pq5sfMqVKfYxM3qYxz9cF7dqmQjE8wCzkzFUNArgwy4/tC1c/BGRIKWxS7iZVXPqRso+b5asPkNg1J1nz3hX
GdFTuYmpZswG7mP/KLbrGzKeN55JwbhX8YPTBd+LGHV8PPdwr0mqlht1cXuqN7bYUlGDR9dDmErMRx/VtGE7syBQsBkjg5IbjufF
FO6hBybv/MGYb9POwTrllnQvb6p0VG7evCmY99nUkX5Agl3OMjjlwrfuaY0iGFH8LCWBbgvnobvtT9YTgntZjkx7w0y5pEAxmNt5
1RxcppnCFZ5lzEUDqt38Dmu3u7Oo7Ta/CsWWWvvkMlbWnztPxdPny3zy+V9sCSwe1sfs/C/PX7xcW8/mf1lZXn3K//I1Pv8f53/J
JT52Go7I49voqTy+nKmYraCYBHTbyEcsWpAZtuZIRtwsiRxhIFzIBLkJXgCykYqwSeXFlZmYmqVSawLLE2Ey4th1qjLN2m6ttEN+
UrgcjeX1xtqyU/VHF6Sx1mblnqGUoJnMM+JnGMtv8eRsHIUo8KgnU/UVl1w1HvXHqM0VZ6WxZLXRV56r/DZG8hpR6No/iwmqcWEm
mXEUjEDXax8fHx5vGBVAez4jnVfxvqbMj0tIlM7foesJtghTxTOEpLpSy+bUySfKksPNpZmhSph2ZOv48EOnfTxPWhxdlHPiAGr/
9D/+8mv9D2Z3tNc6eXN4vO+0tjonx63tk93DA2ev9V37+Fc+dZGcSGa7ZonNTEFTU2mKjlGw85zrC3/kyESsOqlkOPY5j1/snLsB
pyYqUd6cuCvyRVZJ6kTnK25TinyA5io3O2bgq0D5NUpO0Jv24XulploaYGbCOdrhJDUxbrJqhSoZjQzdXjjXYDY3nYrnRjiEX/8W
2D7c328d7DjtP7a33xP+G3vhVz57sQvEaVRbMFyF+a0zzMPd43xpDpdFri5cW4GhzJXwn7YENpnL1FU3qnQpS76Z0QrPAUgL/5TL
6KVqoWkr2wbZECqUg7+y4Xz8BBsKU/HL7258SV9v9aCEzKDsAabB2B1jKLzvpd0n57X/SiMrXqaEwg3AzIASDTd9rkha5EGYqMR3
vseJ3jQEKbtpVzczx7BpuMkEmvyIP9EpOsqcHlJkre3igqYqdky0IsYcylDM9+owLRfEmVqToeoMfXcUy1TKsRiDuDqiafam9Wvm
3pG24faGnmlOTqLATJb0jbMDg3OqeGch3kMHIkgYBYmRl4BP4MgKq9V1jS8UH8r4QLkIcgaYyG/Gvhv1LqqilToOCafa3H17cHjc
3m512hZjrSCk4hT4vAzjDDg9oRxN5UZ8u62Yx+otAHR16HuYGYLy4WKeBecMY6JjaWSRq5k1vqhVTg1nLhDgglkg8EAoGJBgOx31
Y84WUcdBD+lB0AuSlF23YKC0kWv3muJHUevTl54cb4RKyVJE+gMcsN2IitK5Us6LQDQmbFsNHnqJWC/n2D467Oz+Ufz4W/9PSl5h
HHzOMJ5q5reWwRgAgaSJnDgZ9wrdGSAvOGD9b3QVROGIYk2u3CjAg4KZLEjfmiAt7PfmSROoUq01VcPZFjJ+FbpHSqyZYJI0+QTd
p8YwD7xiC+jel2JRGQou/Q7zvEX0pDup5WgQNyWCK6i132wKNmvbFzcVYcuvKJ+ZCvnMwO/zinG3jkcUdMO54VZvUTJGvbCLnu5Q
eGX15W0RQTRHs7noaCrbintnOFp2KKu5gWQW+g6nuJkgoVZSduRM47MhkwvVRqPTphOCks1bpYkWnewR3+jqY6WDMnFXCMiVT5ot
p4qioQLxR5ksmkegFI3ySXAQD6uZodc+5b0P4sTDEC2zwd2jNsosHgAl/xzGugn/cyze5glfgqk/6YlBy1AjwiNibAUvLB9ORpjl
36/K4LC15WV7AIWxTFSZnyPQEcmWYRV47PCe+qnwkOEn/GtPWZBat0yj9wofnwOVOAw8gyuNlVuT1YgbFf7mmY1gMyIp/9yMRl7l
kGE1kxgld+MuE9J3OASmwTE5nmFwpNZ2/Djoj7jlhvMeGZXRwMvfOtXxdYyEwacshO6VG9Cpdp2TIuUvUBEttak7Zd9EVOCBVDH2
Z+XFANrDSPmrMPCcmDr78yTkqwDofgTRzkHYEFdZOuMQhLGpcz5w+7EzxCAuTIHr9C7wPgtpiORaUgdgRF9iBMcx6AswEbscHKME
6iNqf/fktGMEHsNAcji8PKGrn5s8NvdOeavBOPQgKH0uQD7wOF2N8zG9ESv68gi+DOh7vYbfv/xernqlfkc1p/r51Yva/JUFxnzP
18SsrcoHRgNXK83l7/UEMy19yilDJJyouRa5dhvkR5XNyrgV3WvlSar5m5Nq8plmU5n0zTeSHFrCFqVWL35WJMGq1HQAoxv3giAT
wHgPESO96zE94kF4xLfiVvjXSKXNuMpuI/pUGm0Zun1EdBHrbU3RsZ9aYPrrCShW6nLCjyDJKAEm3xQlgYEhEY02a24ft1sn7e7B
YZdZN+LMhRuTD5UuBgPPFqzUmNEs/1qlJSSioHy/QaHZOOiZMczcVjEYtpK/Z27YryGo/dxm4y9okCZz/NH7k/YxfDk4OT7c49Mo
0vXTd+E5rrBOY1j8rxsq2kxPh9P6jHPLlf6KIGVtpY30ICJeYvp6fXFbXR9fuf1RGCdBT7kiK46cyyYQ0t2d3QWTAsxhzx6Gk9jv
DoG/iR4/U46RusPpYB6neTMX0tkkSTCrBCcyWXmcDuhqMmmMf2iT2ew6KoHOQxu+9KddSk0hGobfj9IuutwxRxWHng8CQ9bwmb2t
GARjL0QXDue3Qg5Z2h26fX/f7cMq1xbUXTOGycxWq9p3n9Yc+arMP66smNeeonuKEHoaAwz2IseTGebI7EmXF8QY5pC215BZfGe3
gxeqUC775Urti+1gECfMBtKsTEnY0WRUPa8IobAhR31jzuHWEZeFOlEIvLR8YzR7Wy5IWWEwyeij+oHHeFiNJQfZiMFAI4YR/6zN
ytpRLMdaU9AqL2ZDAsO5W6WV+wFkPOpvNGwCKeICiiAsGeJFrnhrepdnLh5qYSxX35rmVqS/yYhkM4887Nk+8WMskjjK4MwhKEHB
rArmRDVBVXShWE5TEONjATGnHMxoMA5+QGEJQ5bSbVirLJQJdG4B7vYulM7Jm3rvkjeJ71VuH4lRGm7GYrNKGkrNYqvOzedb52Z6
axKTB3LP4l750u4bbs3S5fz8FBY3RYZUF0ACqC0KYcLlbDTY/mBEL4WZIFamdgalyR87Eh0PRqIcazCfOO4JX1mtI3jLyCbdMIJF
7Nvssu6gXR4LiCgqqFIHCFtLw8g/XiIBRxe8q3zAhIFl6Qkh6RSwRR0qjA38g5IZwvlAkeQbpx0DvfIdtJ8OfDJEistOY2XSxE/s
nmMECsWRyMtayhXMOVT5nv4rcJlnrCrLJaebAhoNSsTnrDiVG2z3Fis/UAQqRmSo79zAPykcXkAegnKc39aOydwaN1ZJWau4YiHK
ypxAtrFDu9wiRZvciJbKjNTlT7d3M2VB77kVqAyP1HjE1ijQ5GlcFZFuiLFO1ID2KjUDK40SOYYuhsR9GjIVzlMYFYdz5PHJ8foo
z+LJReBuRqxMOAZPvlu2mWUciQQLk84wBQBV1hMqD19V+bQdIfriJ0zWA6W8ED+eoqTcnwQgxh/t7i0qt9/zHOi+cr08h8nr0+JU
SE8DjaowkaWjAI3H80n5RYZVaKdJSs3byD2zFVDdpl7SLlf10L2gsB02larBbxa0qJ4237R29zqtN21pitfuPOisQGdOZyRLT4GH
XyFoiP1SCG0YjYzbgvI+2DZ0IwXRKAN0Xi5G1k9f27Izq0ErIbNmfykVybKGfUlxFeybffgnf29ioW4l2mnGLsh7Rqni1PJ5spzX
kW5tqBSEdZsCdDY5R/kibG5hisHdw+zg5eCgXN1hiX+zcnTwtoBr5MZ9f7WhUF2AISCDoHzF1fxhgqUlQ0/IVE6V/WIEEss/VKjP
4V16Szex1ZOw+hkam4uRf6XJLqpN5KZ7luAR380K4AsGJIHgsApfh4Hn0WHPGvygyKfKLd/ORX3VReHaLMjxELnCJnTzSwLf/JpR
DmCICSoboJqsanBeac/QH24qn+FfaLeC/tXT2y8Og8X0kLt2ynUU4IkW5fKmO8aBGGwuN5dXfhkLv5DOkpsztjJ0x9Zb+So8LCS9
aImN6JCLvqSexD36TTqdjT73kmiAJegv+ccSNcc/KNFeBOf0m79Y6ifuGb7HP1h+DGoglacv8ATTiqmn+oeNU/h4My4WE9+g9mSM
v+FfdLYFpYfe4l9LfaIRmrAIQqIoCjy5CIfUPv0lcHkMrIw7cJrlDjEvD6p5YjWIMsH3JrobRCisw4+ZtIkRgdv5ZaDnAtpukdhL
IVAwyzp/M32P5OfieoTwkyWbb/3kTRj5nG2OBcQMJZMqsFmFC54gIcEG0xW6dcxnKWtIJVDXuoh81zvix7uepYG5ZZ58AVOVxoZt
u8RUnS3vxYC5DZjIbIkmFwmZnUSeAcgBZDAT8KBFaCBXoWnkEjZ6fHwl842Lafmnzvlk1ONgvJ/dAfEOtZT4uXuJSR6FT6J5XaDe
MrSLigK0OnQfAfvvjKPANQKOZLPsrSZTL8swQRmCgy5RmSs7K0YYBZA1mYwlSEc05kxjBY6W1AG3kD57YafmdM/aLbhCEUpVdD9r
In2t1pwlp9JEpx/Q56+X4ktQ7OIlqrAk3f8rtZyN0BplQOXZ+TAW0ZG0ElmlEq0DMxZFqYl2C8Kn3CqNPI55HiW0UpTCKr1yKQMJ
tFPHUDZUPxH8nj+OHVqZUV8toHAQM6Hoj9CLNDebSn7NVPrFwsXN28Wyq501p+Suop+t5It0vtduNIJpgYaf0+zF5baeUxVzJ0DU
ZtyEpaaVxjhMEDmafF4aur3DjmlA1aGxM8dWpop50wPlhdFn2M7UT+4YlzEacxwZe+PHyvVF0MOtIO21aLy3GB1rpvHwN7ljvOw0
pPFXOTSxc00R3OeYSxGWFB2l53BkjmvRZmAIGtbEcdldWCFdaTABBeWf2LAb6OTlrbMsQpmTenVJKhvaKRc0P6sabWUzrLpj9ywY
gDZF2VS5qgfiai/pmu/ymVkFrcjkUJVBdGbduamCrfWZqzcrm+udIxH1KjUijEb2hczxhe0UPtNxLvdDNpjlXjc8akzLtWtDNY1A
5pLL1FM57Jkr9zflP+GWJpxdgNNf/EPn8CCdDPW8n4ojy0eQyftT2Nczy1C5jwp+N7IS0W/BUjBdScVcVWI3otsCf/DMTYbbvDNS
uchEA9m9aqb+kM8opoDcfWWtGi7gebpPtAwCFHC4TVyI6rkZNLzvR32VZ1WrE0N8jHL+zbNnO+03rfd7J93tw4M3u28xOyA2mXPF
5iofK0l46Y+yZ22piZdPsIjk2LyADNQ5Ji2IFvdm4JiNNsyTU3eH6jnEUTJpcBzM/qOiTxzANcqIDtzuAo+iU+jmjuOc+SC7oTdM
emjd8vL+5bq1nRyJKm4wVzTf8q2BBTJ5NAORbpbSGct16m8M23ERLgwTBDMnGop9TFKIotqFOxr5A6Pp7YzwtuFQzAGubeIPGdAm
KiEkPxZOI4NWvZ5MdmxRKIUzEZr5Z3uIWRTFCzfuStli43HkDzq8tPfEKv7MjkSRh/UT96JwdjdcYqFe0tYcXsb8+qFiw4uW3dFY
I5fjnfNbdYFoq4PrWmr77rvoOhKG4w1ZGE/V+iIJFmXx5ZnqIDRz306iAV2ce2NspI8VUR2v9Kl8uv17yj6HjgCb6WLqORT6OyJ7
mQKCFBoX8GYu6RCJvYAIwqBv8GIeyTfxA6sCZAdBPwfnN26O13mjmgImfJ0rZrrqKiPu6rJ4Ik/jV5aJfVwXpNU1hy5HzmmAJbRR
WqbclWYCFHNC5gdzo8HqpOQ8oB3ABjk5irUSZ8a8jjlBKrE0bzIcx5Zk3gpH0C6OSb3NniyZvVV5tarlFHlNLfeM6kTQZFXKuKk4
SJfeFbkpUO3UIDfygqK15m2tEMYYKwislkwdYkHsgCXswWKYDRe2VBEW4GcY902BIq5CHbsvFjVt5M4F7uABHMS1BdXruI6NZUa/
2LXj2zpJHj0qum48vQOP/Z7egzDt9P6N5Fu1a2Azx81m09IuT1OktGvGA98fV+9szBSSbaiiBBjMjpGiexxtytnYw3O9jaZoHoLJ
sCuZ3JmR3w8wJiTJXo3Dcs6mAxz3s98D8gw44w6gSuygNd3ch/PrLRlqRY7dIul+JZOnLu2w9TANLd1RvnSW4bDokeE4WRRldUUg
as7aZRqO/twlH7tNLMjjxt/pCepSmw5DPCPPDD12dYM2PlYCr5LOSi5NmOK9+JkpZNlv2BNtNm5fxZOa+b2zo8sLm+leeKuJkfCv
zEBE3moDIPzEsJ3OGHOvJ8kD1wVg1OqiW5kTe1Z+8pzO+n50OQqvUXOglaXzUgo25UkrzpvHBQKfRgQGouUOhbRkoi46EKHmcvFY
XkYbb9Wa+ypO30ObplfYKGWsprZM+mY5yGU7skijYlAiw5r8KZNVxWxA5etAI3JxfPM9ebLixzQIkavczhHLdKmLwFl7CX1BViYX
iOMmSMzh2W1R68KZE4ZSpmuL0TNQ/lJugWUKY8w1cFvLcwAmKxle9rNlFMGpzx94aytnC8TNlZMpRnIv5kk5woORUb/3CqW9B9bN
iXGzsa0sloMuZuEQ+XwZhVFiYtYSjGX8xVLCxLxMHG9a3arljMVm7DMp7OIgtuiI4avDUO5WEcU7e/KNldx8eW9RWt+Ib+6g781W
1J+gzkDXt0RVz+cr3ICYbpZziZUFzeRWmq7ndV1RvVppNITdr07sYZNIPFDv8WYFySg7bJLNDg2OlZktef7ZBBtilrVZiZMQ9OkE
dkRFtrmDRWSuY9GauJVENEp/sNlYpuMQV580qXm9nqIR5JV7xDGqNVjWZM+/8gdV+XKnvfX+rWgHdAM0qIrp4MTILEMqKxVQZnt1
JEDXqQguUksb8s03cpgZLRilCmlKfNs6aX9ofdc9Ofy2feCQcpIrW0kV6u6/75x0t9rdo+PDP+zutHcq6blr46JIusw2RmZglGZO
WMmctgdI/1+WxGiXDOvukmHZNThrKuuy0Z9gyYRZlRubUaDiUNZZ5O+oG4vc2jdFALUqi1ou6fNdzfbaVCQlA0hVBO0mnDo/a00R
FERQj2/96VnoRh4lgYgm4yQHYFb9OxeThKaEDkI4uACPiHDG3S6tW7c7dINRtytWCH8A8s6T/70o/7/xpImJ2oOev3CO+dn5/5dX
Vl68zOb/xysBnvL/f4XPrz3R/8f3oyD5VNox+FOOPZVa51B6E4Z+HUaXjXBEYW+AFkAdSh/cURIXvCt97PDW+FQ6Id5FR+8l1Bg6
SIU21d0K4l4Fhx5gDOCAHuf2WunYJ/q1yRYB+bPj9zZXlkvQ6MgDenHIFuI/hSCluAP1mIQO9ZQg1JtgQlrnAt76qJSVDsID//oo
Cq4AWH0/3kTmWMLfIPqdDMfyd4iHEpy7CTkyqODy4btw6G+iyxnCYgoDdL0P6LCKzDreXEqGYwCLSLD/iaDne1vTTXn9kIDcz432
Tx/xydN/sZ0esY876P/qynL2/pf15fXnT/T/a3wMUot3m+RofvamEWfJOQT1d3vgXiO3wOuWSyUjoxgQdEAp/2zq9CPa+3XnPAJC
jQbTC9z7dYrNwtvd/QhTh4VnCUgrKNwwkS9BySTFIzivYBz20DPNAxmoN9HJEM85OR3ygXJH1CjXqBPPByoYjIhHyFfETjBIG7gS
kTUybQV09ymOQb4eBMNA9IDVmZWVoNEJen/iOOvOMPSCc/zr07TGkzPAkYs6OiuJuyDrqB8OGLp1nMcSxhpjqDG0gKcvNFc9ujpf
khyyaSoRIKKrZq4v8OYUcyZBXDoHSg9d8pGQFwLIqMc/iSM5LH4eYtAZnw6NPIqUiDdKpRP09zvDOPueWnMQ3GGo4p5mWAAjN7x4
FV/gHSlnvgAYXy3uGtOJsHvhVThw8PiU/F8z02xC/+/aTufwzcmH1nHb2e04UsVwyq0O/C7XnQ+7J+8O3584UOK4dXDynXP4xmkd
fOd8u3uwU3fafzw6bnc6zuFxaXf/aG+3Dc92D7b33u/sHrx1tqDewSEg9i6gNzR6cuhgh6Kp3XYHG9tvH2+/g5+trd293ZPv6qU3
uycH2Oabw2On5Ry1jk92t9/vtY6do/fHR4cgYODNDAeHB7sHb46hl/Z+++CkCb3CM6f9B/jhdN619vawq1LrPYz+GMfnbB8efXe8
+/bdifPucG+nDQ+32jCy1tZem7uCSW3vtXb3685Oa7/1tk21DqGV4xIW49E5H9618RH214L/+HYUmAalpoKfdZjl8Ymq+mG30647
rePdDgLkzfHhfr2E4IQah9QI1DtocysIaie1IlAEf7/vtFWDoEi29qCtDlbGKcrCzSdm/jf8yfN/2Ionh9uHe82h90h9zOb/qy+e
v1zO8v8X68tP/P9rfH7t+p99fk5HXJlELL5UevbsD5h9OBxtPHvmrDSXHQce0Z1q8Fveqrb6mh4fTaJxGNOb2bfF4c/sVTQgM/ji
ajfoGl3cZJbimAw8SrY6lYEEpw5rrpjJudFogFoH0I16FwEqYxMA4eEV6qD+dal0enpa+umv//yzx5Ms+t9fYPT/mDVQwydzzd6M
D9TnNn7xYPhLZrj/aNwciEYF2AlVCkJ79fLF89qM+ebacRpOq4fmxVi57grkjGfBzdrOBPYXyHJ4LhTTKSoZi2fC39YOeoyhlC9G
JFwjZqxkQTvvTk6OMEuk6VyDEqfcXXO089cvtqL/65Ha+bfUiGd/sEgU0g2TBa+/+Hb41//7xXaEzA19HJI362+dlrzDqT3qY+Kr
+2Bg0rsw7uFE9WhEmevviYHknhA7ZTe+LLMrkLpYCvcHJR+B30PcfjPa6SSRTzeMkyXPwejhOZba+Uqo/Nh4/Asf6axJ6APRn/71
f9+xRDzd1Ippol4lERtjNsiMUTMKIVlb+tBJ15ynq5nb+v98FQDN30sBdz/2h2Him7fwzprvL5u1/8XJ7HS9/NuDAOf20z/9i2TI
senHm5pnthXmxGFDuTNiXIQXxPKXFViWRt75bpSc+W7ioNw6ddaWiyWC4kYEBXQndDg/UxywNfIlaMGjsN5/M8d515RgKfzgysY+
viyePg63zeGp5LQyLNnhuGTnOkLfzsgif9qR43gyijecJfRzW0pCFQztRBNMeCkltmazOaMNyRrTl7oUcsiCgYibYdT1L3cv6d8G
b9VY+osdYuHQC/3V5/tAdeOggQ8nBaIijv3WMXxJaoADkT9Bs4L/2e0lgylpx0qD3hceqcrgUUKLhfBT5Sg0dDdyQlTEFBVvYt1v
nJUmsysk5kIp3QCM0/7eaFA4HElCv/HsGXZOd62jm5dy7GI9yI/IqysVBFGOYZZTfn7Flgl8utJc5meZmIWP5CNGLpXTGF1GyuSG
WNZep1h7CWMtl1TagjsSGJRLtwyyZ8/eKpf2eAxapH/nhLqwWXmgceImExxiObw0pkk+a+YkZfhPZrJyDAj1VRvUFUvDcbYFV3Ni
ZJReXDjOC1mLO0drFgx0OEbP05cr62uvV5eXlxeYv2pXA2B22zivtaaybODUcI4bjvRkFoS5ZO+PV70knQDLvaHXcFfOVntr3rpA
FOVC+bHsnSOGNC4Ecgg/T3i1RmFlZXmxD/r20o3zCgBvAn/A4Gw4p4F3uuG8HwV/nugEH7s7pJLjFRiX6GUAxcQrKNuKIpgbHgCK
0r9lj7uq3+w3686pObLTGtYVQ4O6++5nw5qGz/mGVVpfLCoHDWV3z6FmNPFP684Z3ZBjamlV0szkTa+DqXHZEq3Cug27JFtkfwug
NcSaYII1hEqHGJTTu5iMLgtRgnYga3mzV4rbpm3BLq30FMN+8RndwcUUjz6d4Adgae+RvrXQfQ2//gdnP5xQ1odw9P3I2L8dZqFf
YqDoWZsaqHD+38DEGCAk0Ykth/gaQ0KAP58FcIxgxURZM/CeYlOwzOzRmu6tjOaeiGLvDmPyNF9bN4f1wjYsZXc45syeHkL1w4U/
Uig9FPYFjXGzAS2NBl2RK9SbfxvHEy/E7dIfJQ1O4dLz2Zs+HONfaNhreGdil/MFXdjqdmaoeKe2uHIYs5pBq47ZpIPtOc8qFj4A
Wx2WmY0ouK3qaIYfCfI4g+xmZ87UdPbMDaKUiBxVZXT7TcIu3YpGdxhnCBau48tZ6MXXYRcvIt7IPc8q8vVp8y9dNCQ6FxG5W7pz
hXAYxhJFQweqOkvGmuBMX9n4x47SDgu2kFYfy5lRSFYcX0wS8jeVnUnZSUaui1QUJOzIIH4dFPfsGTngqly4RwN3es0HSsKpAiUB
IVFt61i6tBx1oO3FIBk4p5lYtlPyMjbjWWF34zmKGX23UQCCLyh+wdfMSB9TIlPHQNo8T8BCSJIPdmOLDtTwPa7Ogc7gQYdksDTV
0yU/6RV5Yp/WNtIgM6KzicjHG0tLK69XmysvXjXX4c/K8w08p5gNSTIW4DOyhNGLBnDyyE8a9KrhnvWAHJcfDiiB0dngT2jpOb3S
cprxas2Q+LTdxjjae3+8J8hBqQgAS2RT/nsOTJ89Te5K7lwYAt1UGQuTitvH45LEoQgGT52YZJaFgTsnRH/w4b0qTL/ShT3/fP35
CwH+sTu61C3jr3Th/kXw8tXrHErK+ciTmlLpDciF4gCPFguZBh3ZJOYdtYi6R4edE4chuHSjkOh2CVcVFwWZr28hyY8j4x4LdmRK
eamuFN2Q8tls6azZbILEc5uuR+KSrpd6jZIKvpTCikHi37YlYMyR5gBBJRAMpGjLiKJyfivSY62a9WRmAf1y4MZJN/Zpv2q1Rb2e
jBGy8O7Vi/VlvuXntm7vldHuAb0+z/e68nL1lewW/v1UssIqhUTc72zw5UlW0WhnwCcDmweo8Nm9pby599GJwDBFqF3X6bzDmBJU
jOSjEZp6mYzE9P4S3pOyxviULSkPKOLUubE8LSs1SL5QL+m+G8OMXT0HXn8NglnjPAr8kTeY1pTyztFBGZ7VcNpu74KPhS/c2Jmw
XgmqQyO+cCPybMToqAZXjyVBBLY/m4M5whfDVjMTok/DxmRDkbOzVVNKucXWdMwWJcqeRMymlprApY9ZEZUvh+HC8VvUh/XgSHFF
6fWUhUy6x6Eh7h+ml/GlfCUEKD5ZlMIzlkHpUBcin89gOPQ9XL3BVGm1UqhUOswb6JcQ6z22iCKjIM+GqKwkyVkmu5/+6b9bX7Pp
mGLbYtMmt0hbeb1qsRHhkW9O57qrKRMYCCzYvgxFv1BfEspX5e/LdzUuGpzC7r/POFi8l2yMwzUWggiukcC4OE1nOHaVznnQlkOI
RCKkVip468pxDd3oUrh2uLGpWgC1hGK/n/gT40Z6mQtCpAw5vVExqiw5hufnGGNTvj0V1IZ3hxLnYukyBVTcZ0djPByAak71eVx3
Vpbhn1X8Z+h+dl4sx2JLy41wwjIBNL57zoP2Qj8eVRKpwFL7SGCkGUrZE0t4tCBnzUh1epOxSiAuDJCOs0KImUtgJnzHPdIIlFeN
a+z1UUWuJWXfyAbp1nVYr5JwbiX7E6MTgUOKYHucc804NUR3BSUIUx7BSeSr8vqsUd4x0wOKNUWojRqwQKjHqRVFc/kgRCtbj485
h5MYwQm0tabxascfD8IpJRpKDbUTAJ0BRiaChpxT0ydV0GpcA4q0Kq0iSeNkhD4zh9jKDWTNJS4jCC/QdYrMks1tiIrI9pqerT7H
k56W1gsGiGt1KkTbcyXrGrtm5vTI5fYUlPzeJXxNT+4OBke7QA4SdBNgMswrYY7tUUwtaA4UxEZSOdwGMqMjkMH1+YHCEbECJM+b
zh/8KDifGrIC1E3QHtk4d5auXIx161siSuGpQXC2hTckGQAYch9wcsoZkqYBqPfTv/83h3hwzKc6ToxXhRmzPAtGbjQtLpkCIF/w
UljY8NqE4Qay/4OQMu7hKdHI9z2OvVA8n+mqgDSJOAIV9oO+mTxTTg0R41sQG7ItCOklRoyQiMMElhbAHYSjfoyItWZijA0b1OJy
fCGs2Q4n09XgVdKbzMVobNo3E3IybY8uXJElDCbUcJ49O9nrLH3odECXG6Of4rNnhP2glkfTcULkXomPVH7bjxL2uPUbZ2SeQA8C
qIbA8l2PgkRoq3JxQa0vApTb0JpEYUATjCSH5aAy+5NBEqh9ihQMj+vQumE6PpL0ihxiMqb8o1EA/GpKodQ1amYr8EAa6AkLFomC
SeSO4nNuazLGqPslLI9fiG4q+wC3cOy7gwYdTsDQHKU/Ym25GdIHifPFZj99nj5Pn6fP0+fp8/R5+jx9nj5Pn6fP0+fp8/R5+jx9
nj4P/fw/oDIRVgCQAQA=
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
TARBALL_DATA
# Copy agent script
......@@ -405,40 +99,41 @@ fi
# Copy node_gateway.py (for plugin reference, if needed)
cp hermes-node-agent/node_gateway.py "$AGENT_DIR/hermes-node-gateway.py" 2>/dev/null || true
# Clean up
echo "[3/5] Cleaning up temporary files..."
cd /
rm -rf "$TMP_EXTRACT"
# Create config file if missing
echo "[4/5] Checking configuration..."
if [ ! -f "$CONFIG_DIR/config.json" ]; then
echo "[5/5] Creating config file..."
NODE_NAME=$(hostname)
TOKEN=$(python3 -c "import secrets; print(secrets.token_hex(16))")
cat > "$CONFIG_DIR/config.json" << EOF
{
"gateway_url": "ws://YOUR-GATEWAY-HOST:8765",
"node_name": "${NODE_NAME}",
"token": "${TOKEN}",
"sexec_path": "${HOME}/.openclaw/skills/sexec/sexec.sh",
"reconnect_interval": 5,
"heartbeat_interval": 30,
"capabilities": ["exec"]
}
EOF
echo "✓ Config: $CONFIG_DIR/config.json"
echo ""
echo "⚠️ IMPORTANT: Edit $CONFIG_DIR/config.json"
echo " → Set gateway_url to your gateway (e.g., wss://zeiss:8765)"
echo " → Set token to match gateway's token for this node"
echo " → Add capabilities: ["exec", "browser_control", "computer_control"] as needed"
# Run packaged interactive config installer
if [ -f "$TMP_EXTRACT/hermes-node-agent/install.sh" ]; then
chmod +x "$TMP_EXTRACT/hermes-node-agent/install.sh"
mkdir -p "$CONFIG_DIR"
echo "[4/5] Running interactive configuration installer..."
INSTALLER_TMP_ROOT=$(mktemp -d)
trap 'rm -rf "$INSTALLER_TMP_ROOT" "$TMP_EXTRACT"' EXIT
mkdir -p "$INSTALLER_TMP_ROOT/node-agent"
cp "$TMP_EXTRACT/hermes-node-agent/hermes_node_agent.py" "$INSTALLER_TMP_ROOT/node-agent/hermes_node_agent.py"
cp "$TMP_EXTRACT/hermes-node-agent/browser_controller.py" "$INSTALLER_TMP_ROOT/node-agent/browser_controller.py"
cp "$TMP_EXTRACT/hermes-node-agent/requirements.txt" "$INSTALLER_TMP_ROOT/node-agent/requirements.txt"
cp "$TMP_EXTRACT/hermes-node-agent/install.sh" "$INSTALLER_TMP_ROOT/node-agent/install.sh"
cp "$TMP_EXTRACT/hermes-node-agent/hermes-node-agent.init.d" "$INSTALLER_TMP_ROOT/node-agent/hermes-node-agent.init.d"
cp "$TMP_EXTRACT/hermes-node-agent/hermes-node-agent.service" "$INSTALLER_TMP_ROOT/node-agent/hermes-node-agent.service"
cp "$TMP_EXTRACT/hermes-node-agent/README.md" "$INSTALLER_TMP_ROOT/node-agent/README.md"
cp "$TMP_EXTRACT/hermes-node-agent/LICENSE" "$INSTALLER_TMP_ROOT/node-agent/LICENSE"
cp "$TMP_EXTRACT/hermes-node-agent/DEPLOYMENT.md" "$INSTALLER_TMP_ROOT/node-agent/DEPLOYMENT.md"
cp "$TMP_EXTRACT/hermes-node-agent/PROTOCOL.md" "$INSTALLER_TMP_ROOT/node-agent/PROTOCOL.md"
cp "$TMP_EXTRACT/hermes-node-agent/BROWSER_PROTOCOL.md" "$INSTALLER_TMP_ROOT/node-agent/BROWSER_PROTOCOL.md"
(
cd "$INSTALLER_TMP_ROOT"
bash ./node-agent/install.sh
)
rm -rf "$INSTALLER_TMP_ROOT"
trap 'rm -rf "$TMP_EXTRACT"' EXIT
else
echo "[5/5] Config already exists → skipping"
echo " Config: $CONFIG_DIR/config.json"
echo "❌ ERROR: install.sh missing from embedded payload"
exit 1
fi
echo "[3/5] Cleaning up temporary files..."
cd "$ORIG_PWD"
rm -rf "$TMP_EXTRACT"
trap - EXIT
# Install SysV init service (root only)
if [ "$USE_SERVICE" = true ]; then
echo ""
......
......@@ -19,6 +19,7 @@ import asyncio
import json
import logging
import os
import ssl
import subprocess
import sys
import time
......@@ -40,6 +41,114 @@ except ImportError:
logger = logging.getLogger(__name__)
LOG_PREVIEW_LEN = 120
def _preview_command(command: Any, limit: int = LOG_PREVIEW_LEN) -> str:
"""Return a compact single-line preview for logging."""
if isinstance(command, (list, tuple)):
text = ' '.join(str(part) for part in command)
else:
text = str(command)
text = ' '.join(text.split())
if len(text) > limit:
return text[:limit] + '…'
return text
def _setup_logging(debug: bool = False) -> None:
level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(level=level, format='%(message)s')
def _log_start(node_name: str, tools: list) -> None:
logger.info(f"start ▶ {node_name} — {', '.join(tools)}")
def _log_connect(url: str) -> None:
logger.info(f"connect ▶ {url}")
def _log_tls_disabled() -> None:
logger.info("tls verify disabled")
def _log_connected() -> None:
logger.info("connect ✓")
def _log_registering(node_name: str) -> None:
logger.info(f"register ▶ {node_name}")
def _log_registered(node_name: str) -> None:
logger.info(f"register ✓ {node_name}")
def _log_waiting() -> None:
logger.info("waiting for commands")
def _log_exec_received(command: Any) -> None:
logger.info(f"exec ▶ {_preview_command(command)}")
def _log_exec_completed(command: Any, exit_code: Any) -> None:
logger.info(f"exec ✓ exit={exit_code} — {_preview_command(command)}")
def _log_exec_failed(command: Any, error: Any, exit_code: Any = None) -> None:
prefix = f"exec ✗ exit={exit_code}" if exit_code is not None else "exec ✗"
logger.error(f"{prefix} — {_preview_command(command)} — {error}")
def _log_browser_received(command: Any) -> None:
logger.info(f"browser ▶ {_preview_command(command)}")
def _log_cc_received(action: Any) -> None:
logger.info(f"computer ▶ {_preview_command(action)}")
def _log_reconnect(delay: Any, reason: Any) -> None:
logger.info(f"reconnect in {delay}s — {reason}")
def _log_registration_ack() -> None:
logger.debug("register ack")
def _log_heartbeat_ack() -> None:
logger.debug("heartbeat ack")
def _log_unknown_message(req_type: Any) -> None:
logger.warning(f"unknown message: {req_type}")
def _log_connection_error(message: Any) -> None:
logger.error(f"connection error — {message}")
def _log_config_missing(path: Path) -> None:
logger.error(f"config missing — {path}")
def _log_token_missing() -> None:
logger.error("token missing in config")
def _log_init_warning(component: str, message: Any) -> None:
logger.warning(f"{component} init failed — {message}")
def _log_disabled(component: str, message: str) -> None:
logger.warning(f"{component} disabled — {message}")
def _log_shutdown() -> None:
logger.info("shutdown")
# ═════════════════════════════════════════════════════════════════════════
# DEFAULT CONFIGURATION
# ═════════════════════════════════════════════════════════════════════════
......@@ -89,12 +198,17 @@ class CommandExecutor:
"""Execute command respecting permission rules."""
raise NotImplementedError
def _check_permission(self, command: str, approved: bool) -> tuple[bool, str]:
def _check_permission(self, command: Any, approved: bool) -> tuple[bool, str]:
"""Check allow/deny/ask rules.
Returns (allowed, reason). 'ask' means requires approval gate.
Accepts command as string or argv list.
"""
import re
cmd = command.strip()
if isinstance(command, (list, tuple)):
cmd = ' '.join(str(part) for part in command)
else:
cmd = str(command)
cmd = cmd.strip()
# Deny (highest priority)
for pattern in self.permissions.get('deny', []):
if re.search(pattern, cmd, re.IGNORECASE):
......@@ -115,30 +229,38 @@ class CommandExecutor:
# ── POSIX ──────────────────────────────────────────────────────────────────
class PosixCommandExecutor(CommandExecutor):
"""POSIX implementation — uses sexec.sh and environment variable."""
def __init__(self, sexec_path: str, permission_rules: Dict[str, List[str]]):
super().__init__(permission_rules)
self.sexec_path = Path(sexec_path).expanduser()
"""POSIX implementation using integrated permission checks and /bin/sh."""
def execute(self, command: str, approved: bool = False) -> Dict[str, Any]:
def execute(self, command: Any, approved: bool = False) -> Dict[str, Any]:
allowed, reason = self._check_permission(command, approved)
if not allowed and reason != 'ask':
return {'success': False, 'error': f'Permission denied: {reason}', 'exit_code': 127}
if not approved and reason == 'ask':
return {'success': False, 'error': 'Command requires approval', 'exit_code': 2}
if not self.sexec_path.exists():
return {'success': False, 'error': f'sexec not found: {self.sexec_path}', 'exit_code': 127}
if isinstance(command, (list, tuple)):
cmd = ' '.join(subprocess.list2cmdline([str(part)]) if is_windows() else __import__('shlex').quote(str(part)) for part in command)
else:
cmd = str(command)
try:
env = os.environ.copy()
env['SEXEC_COMMAND'] = command
proc = subprocess.Popen(
[str(self.sexec_path)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, text=True
['/bin/sh', '-c', cmd],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
out, err = proc.communicate(timeout=300)
return {'success': proc.returncode == 0, 'stdout': out, 'stderr': err,
'exit_code': proc.returncode}
return {
'success': proc.returncode == 0,
'stdout': out,
'stderr': err,
'exit_code': proc.returncode,
}
except subprocess.TimeoutExpired:
try:
proc.kill()
except Exception:
pass
return {'success': False, 'error': 'Command timed out', 'exit_code': 124}
except Exception as e:
return {'success': False, 'error': str(e), 'exit_code': -1}
......@@ -390,8 +512,7 @@ def make_executor(config: Dict[str, Any]) -> CommandExecutor:
if is_windows():
return WindowsCommandExecutor(perms)
else:
sexec = config.get('sexec_path', str(Path.home() / '.openclaw/skills/sexec/sexec.sh'))
return PosixCommandExecutor(sexec, perms)
return PosixCommandExecutor(perms)
def make_computer_controller(config: Dict[str, Any]) -> Optional[ComputerControllerBase]:
"""Select and instantiate the appropriate computer controller, or None if deps missing."""
......@@ -401,50 +522,49 @@ def make_computer_controller(config: Dict[str, Any]) -> Optional[ComputerControl
try:
return WindowsComputerController()
except ImportError as e:
logger.warning(f"computer_control disabled (missing deps): {e}")
_log_disabled('computer_control', f"missing deps: {e}")
return None
else:
# Linux/macOS
if is_macos():
logger.warning("macOS computer_control not implemented yet")
_log_disabled('computer_control', 'macOS not implemented yet')
return None
# Linux
if subprocess.run(['which', 'xdotool'], capture_output=True).returncode != 0:
logger.warning("xdotool not found — computer_control disabled")
_log_disabled('computer_control', 'xdotool not found')
return None
try:
return PosixComputerController()
except Exception as e:
logger.warning(f"computer_control init failed: {e}")
_log_init_warning('computer_control', e)
return None
class NodeAgent:
def __init__(self, config_path: Optional[str] = None):
self.config = self._load_config(config_path)
self.executor = make_executor(self.config)
self.capabilities = self._detect_capabilities()
self.computer = None
if self.capabilities.get('enable_computer_control'):
self.computer = ComputerController()
self.computer = make_computer_controller(self.config)
self.browser = None
if self.capabilities.get('enable_browser') and HAS_BROWSER:
try:
self.browser = BrowserController()
except Exception as e:
logger.warning(f"BrowserController init failed: {e}")
_log_init_warning('browser_control', e)
def _load_config(self, path: Optional[str]) -> Dict[str, Any]:
"""Load node configuration from JSON."""
cfg_path = Path(path).expanduser() if path else Path.home() / '.config' / 'hermes-node' / 'config.json'
if not cfg_path.exists():
logger.error(f"Config not found: {cfg_path}")
_log_config_missing(cfg_path)
sys.exit(1)
with open(cfg_path) as f:
data = json.load(f)
# Merge defaults
merged = {**DEFAULT_CONFIG, **data}
if not merged['token']:
logger.error("Token missing from config")
_log_token_missing()
sys.exit(1)
return merged
......@@ -469,20 +589,30 @@ class NodeAgent:
async def connect_and_run(self):
"""Main loop: connect to gateway and process commands."""
url = f"{self.config['gateway_url']}?node_name={self.config['node_name']}&token={self.config['token']}"
logger.info(f"Connecting to {url}")
_log_connect(url)
ssl_context = None
if self.config['gateway_url'].startswith('wss://'):
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
_log_tls_disabled()
while True:
try:
async with websockets.connect(url, ping_interval=20, ping_timeout=10) as ws:
logger.info("Connected to gateway — awaiting commands")
async with websockets.connect(url, ping_interval=20, ping_timeout=10, ssl=ssl_context) as ws:
_log_connected()
# Send capabilities announcement
# Send registration expected by gateway
_log_registering(self.config['node_name'])
await ws.send(json.dumps({
"type": "capabilities",
"type": "register",
"node_name": self.config['node_name'],
"version": "node-agent-clean",
"tools": self._get_available_tools(),
"capabilities": self.capabilities
}))
_log_waiting()
# Listen for commands
async for raw in ws:
......@@ -490,8 +620,8 @@ class NodeAgent:
await self._handle_message(ws, msg)
except Exception as e:
logger.error(f"Connection error: {e}")
logger.info(f"Reconnecting in {self.config['reconnect_interval']}s...")
_log_connection_error(e)
_log_reconnect(self.config['reconnect_interval'], e)
await asyncio.sleep(self.config['reconnect_interval'])
def _get_available_tools(self) -> list:
......@@ -513,47 +643,45 @@ class NodeAgent:
action = msg['action']
params = msg.get('params', {})
await self._handle_cc(ws, msg.get('id'), action, params)
elif req_type == 'register_ack':
_log_registered(self.config['node_name'])
_log_registration_ack()
elif req_type == 'heartbeat_ack':
_log_heartbeat_ack()
else:
logger.warning(f"Unknown message type: {req_type}")
_log_unknown_message(req_type)
async def _handle_exec(self, ws, cmd_id: str, command: str):
"""Execute a shell command via sexec (respecting permissions)."""
logger.info(f"Exec: {command}")
try:
sexec = Path(self.config['sexec_path']).expanduser()
if not sexec.exists():
"""Execute a shell command and reply with the gateway's exec protocol."""
_log_exec_received(command)
result = self.executor.execute(command)
stdout = result.get('stdout', '') or ''
stderr = result.get('stderr', '') or ''
if stdout:
await ws.send(json.dumps({
"type": "exec_result",
"type": "exec_output",
"id": cmd_id,
"error": f"sexec not found at {sexec}",
"stdout": "", "stderr": "", "exit_code": 127
"stream": "stdout",
"data": stdout
}))
return
env = os.environ.copy()
env['SEXEC_COMMAND'] = command
proc = subprocess.Popen(
[sexec],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
text=True
)
stdout, stderr = proc.communicate(timeout=300)
if stderr:
await ws.send(json.dumps({
"type": "exec_result",
"type": "exec_output",
"id": cmd_id,
"command": command,
"stdout": stdout,
"stderr": stderr,
"exit_code": proc.returncode
"stream": "stderr",
"data": stderr
}))
except subprocess.CalledProcessError as e:
exit_code = result.get('exit_code', -1)
error = result.get('error')
if error:
_log_exec_failed(command, error, exit_code)
else:
_log_exec_completed(command, exit_code)
await ws.send(json.dumps({
"type": "exec_result",
"type": "exec_complete",
"id": cmd_id,
"error": str(e),
"exit_code": -1
"exit_code": exit_code,
"error": error
}))
......@@ -563,22 +691,22 @@ def main():
parser.add_argument('--debug', action='store_true', help='Debug logging')
args = parser.parse_args()
if args.debug:
logging.getLogger().setLevel(logging.DEBUG)
_setup_logging(args.debug)
# Load config to check token
config = NodeAgent(args.config)._load_config(args.config)
if config['token'] == DEFAULT_GATEWAY_TOKEN or config['token'] == 'GATEWAY_TOKEN_MUST_BE_PROVIDED':
logger.error("ERROR: Token not set in config. Edit ~/.config/hermes-node/config.json")
_log_token_missing()
sys.exit(1)
logger.info(f"Node '{config['node_name']}' starting — tools: {NodeAgent(args.config)._get_available_tools()}")
tools = NodeAgent(args.config)._get_available_tools()
_log_start(config['node_name'], tools)
agent = NodeAgent(args.config)
try:
asyncio.run(agent.connect_and_run())
except KeyboardInterrupt:
logger.info("Shutting down")
_log_shutdown()
if __name__ == '__main__':
main()
......@@ -42,14 +42,32 @@ if ! command -v pip3 &> /dev/null; then
exit 1
fi
# Install websockets library
# Install websockets library only if missing
# On modern Debian/Ubuntu with PEP 668, system pip may be externally managed.
echo "[1/5] Installing Python dependencies..."
pip3 install --quiet websockets 2>/dev/null || pip install --quiet websockets
if [ $? -ne 0 ]; then
echo "❌ Failed to install websockets. Try: pip3 install websockets"
if python3 -c "import websockets" 2>/dev/null; then
echo "✓ websockets library already installed"
else
PIP_OK=false
if command -v pip3 >/dev/null 2>&1; then
if pip3 install --quiet --user websockets 2>/dev/null; then
PIP_OK=true
fi
fi
if [ "$PIP_OK" != true ] && command -v pip >/dev/null 2>&1; then
if pip install --quiet --user websockets 2>/dev/null; then
PIP_OK=true
fi
fi
if [ "$PIP_OK" != true ]; then
echo "❌ Failed to install websockets in user site-packages."
echo " Try one of:"
echo " - apt install python3-websockets"
echo " - python3 -m venv ~/hermes-node-venv && ~/hermes-node-venv/bin/pip install websockets"
exit 1
fi
echo "✓ websockets library installed"
fi
echo "✓ websockets library installed"
# Determine install locations
if [ "$RUN_AS_ROOT" = true ]; then
......@@ -71,30 +89,184 @@ cp "$(pwd)/node-agent/hermes_node_agent.py" "$AGENT_DIR/hermes-node-agent"
chmod +x "$AGENT_DIR/hermes-node-agent"
echo "✓ Installed: $AGENT_DIR/hermes-node-agent"
# Create config file if missing
echo "[3/5] Checking configuration..."
if [ ! -f "$CONFIG_DIR/config.json" ]; then
echo "[4/5] Creating config file..."
TOKEN=$(python3 -c "import secrets; print(secrets.token_hex(16))")
cat > "$CONFIG_DIR/config.json" << EOF
# Create or update config interactively, using existing values as defaults
echo "[3/5] Configuring node..."
EXISTING_GATEWAY_URL="wss://YOUR-GATEWAY-HOST:8765"
EXISTING_NODE_NAME="$(hostname)"
EXISTING_TOKEN="$(python3 -c "import secrets; print(secrets.token_hex(16))")"
EXISTING_ENABLE_BROWSER="false"
EXISTING_ENABLE_COMPUTER_CONTROL="false"
EXISTING_ENABLE_DESKTOP_OBSERVE="false"
EXISTING_ENABLE_AUDIO_CONTROL="false"
if [ -f "$CONFIG_DIR/config.json" ]; then
EXISTING_GATEWAY_URL=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(data.get('gateway_url', 'wss://YOUR-GATEWAY-HOST:8765'))
PY
)
EXISTING_NODE_NAME=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(data.get('node_name', '$(hostname)'))
PY
)
EXISTING_TOKEN=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(data.get('token', ''))
PY
)
EXISTING_ENABLE_BROWSER=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(str(bool(data.get('enable_browser', False))).lower())
PY
)
EXISTING_ENABLE_COMPUTER_CONTROL=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(str(bool(data.get('enable_computer_control', False))).lower())
PY
)
EXISTING_ENABLE_DESKTOP_OBSERVE=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(str(bool(data.get('enable_desktop_observe', False))).lower())
PY
)
EXISTING_ENABLE_AUDIO_CONTROL=$(python3 - <<PY
import json
from pathlib import Path
p = Path(r'''$CONFIG_DIR/config.json''')
data = json.loads(p.read_text())
print(str(bool(data.get('enable_audio_control', False))).lower())
PY
)
echo " Existing config found: $CONFIG_DIR/config.json"
fi
read -r -p "Gateway URL [$EXISTING_GATEWAY_URL]: " GATEWAY_URL
GATEWAY_URL=${GATEWAY_URL:-$EXISTING_GATEWAY_URL}
read -r -p "Node name [$EXISTING_NODE_NAME]: " NODE_NAME
NODE_NAME=${NODE_NAME:-$EXISTING_NODE_NAME}
read -r -p "Token [$EXISTING_TOKEN]: " TOKEN
TOKEN=${TOKEN:-$EXISTING_TOKEN}
DEFAULT_BROWSER_CHOICE="n"
[ "$EXISTING_ENABLE_BROWSER" = "true" ] && DEFAULT_BROWSER_CHOICE="y"
read -r -p "Enable browser_control? (y/N) [$DEFAULT_BROWSER_CHOICE]: " ENABLE_BROWSER_INPUT
ENABLE_BROWSER_INPUT=${ENABLE_BROWSER_INPUT:-$DEFAULT_BROWSER_CHOICE}
case "$ENABLE_BROWSER_INPUT" in
y|Y|yes|YES) ENABLE_BROWSER=true ;;
*) ENABLE_BROWSER=false ;;
esac
DEFAULT_COMPUTER_CHOICE="n"
[ "$EXISTING_ENABLE_COMPUTER_CONTROL" = "true" ] && DEFAULT_COMPUTER_CHOICE="y"
read -r -p "Enable computer_control? (y/N) [$DEFAULT_COMPUTER_CHOICE]: " ENABLE_COMPUTER_INPUT
ENABLE_COMPUTER_INPUT=${ENABLE_COMPUTER_INPUT:-$DEFAULT_COMPUTER_CHOICE}
case "$ENABLE_COMPUTER_INPUT" in
y|Y|yes|YES) ENABLE_COMPUTER_CONTROL=true ;;
*) ENABLE_COMPUTER_CONTROL=false ;;
esac
DEFAULT_DESKTOP_CHOICE="n"
[ "$EXISTING_ENABLE_DESKTOP_OBSERVE" = "true" ] && DEFAULT_DESKTOP_CHOICE="y"
read -r -p "Enable desktop_observe? (y/N) [$DEFAULT_DESKTOP_CHOICE]: " ENABLE_DESKTOP_INPUT
ENABLE_DESKTOP_INPUT=${ENABLE_DESKTOP_INPUT:-$DEFAULT_DESKTOP_CHOICE}
case "$ENABLE_DESKTOP_INPUT" in
y|Y|yes|YES) ENABLE_DESKTOP_OBSERVE=true ;;
*) ENABLE_DESKTOP_OBSERVE=false ;;
esac
DEFAULT_AUDIO_CHOICE="n"
[ "$EXISTING_ENABLE_AUDIO_CONTROL" = "true" ] && DEFAULT_AUDIO_CHOICE="y"
read -r -p "Enable audio_control? (y/N) [$DEFAULT_AUDIO_CHOICE]: " ENABLE_AUDIO_INPUT
ENABLE_AUDIO_INPUT=${ENABLE_AUDIO_INPUT:-$DEFAULT_AUDIO_CHOICE}
case "$ENABLE_AUDIO_INPUT" in
y|Y|yes|YES) ENABLE_AUDIO_CONTROL=true ;;
*) ENABLE_AUDIO_CONTROL=false ;;
esac
CAPABILITIES='["exec"]'
if [ "$ENABLE_BROWSER" = true ]; then
CAPABILITIES=$(python3 - <<'PY' "$CAPABILITIES"
import json, sys
caps = json.loads(sys.argv[1])
if 'browser_control' not in caps:
caps.append('browser_control')
print(json.dumps(caps))
PY
)
fi
if [ "$ENABLE_COMPUTER_CONTROL" = true ]; then
CAPABILITIES=$(python3 - <<'PY' "$CAPABILITIES"
import json, sys
caps = json.loads(sys.argv[1])
if 'computer_control' not in caps:
caps.append('computer_control')
print(json.dumps(caps))
PY
)
fi
if [ "$ENABLE_DESKTOP_OBSERVE" = true ]; then
CAPABILITIES=$(python3 - <<'PY' "$CAPABILITIES"
import json, sys
caps = json.loads(sys.argv[1])
if 'desktop_observe' not in caps:
caps.append('desktop_observe')
print(json.dumps(caps))
PY
)
fi
if [ "$ENABLE_AUDIO_CONTROL" = true ]; then
CAPABILITIES=$(python3 - <<'PY' "$CAPABILITIES"
import json, sys
caps = json.loads(sys.argv[1])
if 'audio_control' not in caps:
caps.append('audio_control')
print(json.dumps(caps))
PY
)
fi
cat > "$CONFIG_DIR/config.json" << EOF
{
"gateway_url": "wss://YOUR-GATEWAY-HOST:8765",
"node_name": "$(hostname)",
"gateway_url": "$GATEWAY_URL",
"node_name": "$NODE_NAME",
"token": "$TOKEN",
"sexec_path": "$HOME/.config/hermes-node-agent/sexec/sexec.sh",
"reconnect_interval": 5,
"heartbeat_interval": 30
"heartbeat_interval": 30,
"capabilities": $CAPABILITIES,
"enable_browser": $ENABLE_BROWSER,
"enable_computer_control": $ENABLE_COMPUTER_CONTROL,
"enable_desktop_observe": $ENABLE_DESKTOP_OBSERVE,
"enable_audio_control": $ENABLE_AUDIO_CONTROL,
"permissions": {
"deny": ["sudo", "su", "doas", "dd if=", "mkfs", "fdisk", "wipe"],
"ask": ["rm -rf", "dd if=", "> /dev/", "chmod", "chown", "mv /", ":/usr/", ":/etc/", ":/bin/", ":/sbin/"],
"allow": ["cat", "ls", "pwd", "cd", "echo", "head", "tail", "wc", "grep", "find", "stat", "file", "which", "whereis", "df", "du", "free", "uptime", "who", "w", "last", "ps", "top", "htop", "uname", "env", "printenv"]
}
}
EOF
echo "✓ Config: $CONFIG_DIR/config.json"
echo ""
echo "⚠️ IMPORTANT: Edit $CONFIG_DIR/config.json"
echo " → Set gateway_url to your gateway (e.g., wss://zeiss:8765)"
echo " → Set token to match gateway's token for this node"
else
echo "[4/5] Config already exists → skipping"
echo " Config: $CONFIG_DIR/config.json"
fi
echo "[4/5] Wrote config: $CONFIG_DIR/config.json"
# Install SysV init service (root only)
if [ "$RUN_AS_ROOT" = true ] && [ -f "$SERVICE_FILE" ]; then
......
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