Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
H
hermes-node-agent
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lisa
hermes-node-agent
Commits
aa563f0a
Commit
aa563f0a
authored
May 13, 2026
by
Lisa (AI Assistant)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: restore integrated exec and installer capability sync
parent
3f9ac2ad
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
626 additions
and
491 deletions
+626
-491
build-linux.sh
build-linux.sh
+3
-3
hermes_node_agent.py
hermes_node_agent.py
+191
-51
install-node.sh
package-hermes-node-agent/deploy/install-node.sh
+34
-339
hermes_node_agent.py
package-hermes-node-agent/hermes_node_agent.py
+201
-73
install.sh
package-hermes-node-agent/install.sh
+197
-25
No files found.
build-linux.sh
View file @
aa563f0a
...
...
@@ -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
/"
...
...
hermes_node_agent.py
View file @
aa563f0a
...
...
@@ -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
.
c
apabilities
.
get
(
'enable_browser'
)
and
HAS_BROWSER
:
if
self
.
c
onfig
.
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
.
c
apabilities
.
get
(
'enable_computer_control'
)
:
if
self
.
c
omputer
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
_h
andle_exec
(
self
,
ws
,
cmd_id
:
str
,
command
:
str
):
"""
Execute a shell command via sexec (respecting permissions)
."""
logger
.
info
(
f
"Exec: {command}"
)
async
def
_h
eartbeat_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
():
...
...
package-hermes-node-agent/deploy/install-node.sh
View file @
aa563f0a
...
...
@@ -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 -
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
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
"
e
cho
" Config:
$CONFIG_DIR
/config.json"
echo
"
❌ ERROR: install.sh missing from embedded payload
"
e
xit
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
""
...
...
package-hermes-node-agent/hermes_node_agent.py
View file @
aa563f0a
...
...
@@ -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_resul
t"
,
"type"
:
"exec_outpu
t"
,
"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_
resul
t"
,
"type"
:
"exec_
outpu
t"
,
"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
()
package-hermes-node-agent/install.sh
View file @
aa563f0a
...
...
@@ -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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment