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
a3a00221
Commit
a3a00221
authored
May 14, 2026
by
Lisa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: sync approval flow in gateway and rebuild node-agent bundle
parent
dc1a4f86
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
76 additions
and
19 deletions
+76
-19
build-linux.sh
build-linux.sh
+4
-7
install-node.sh
deploy/linux/install-node.sh
+1
-1
hermes-node-agent-linux.tar.gz
dist/hermes-node-agent-linux.tar.gz
+0
-0
hermes_node_agent.py
hermes_node_agent.py
+68
-9
install.sh
install.sh
+2
-1
hermes-node-agent-installer.sh
release/bundle/hermes-node-agent-installer.sh
+1
-1
hermes-node-agent-bundle.zip
release/hermes-node-agent-bundle.zip
+0
-0
No files found.
build-linux.sh
View file @
a3a00221
...
@@ -25,11 +25,11 @@ mkdir -p "$PACKAGE_DIR"
...
@@ -25,11 +25,11 @@ mkdir -p "$PACKAGE_DIR"
echo
"Copying files to
$PACKAGE_DIR
..."
echo
"Copying files to
$PACKAGE_DIR
..."
# Copy core files from
the package
source of truth
# Copy core files from
repo-root
source of truth
cp
package-hermes-node-agent/
hermes_node_agent.py
"
$PACKAGE_DIR
/hermes_node_agent.py"
cp
hermes_node_agent.py
"
$PACKAGE_DIR
/hermes_node_agent.py"
cp
browser_controller.py
"
$PACKAGE_DIR
/"
cp
browser_controller.py
"
$PACKAGE_DIR
/"
cp
requirements.txt
"
$PACKAGE_DIR
/"
cp
requirements.txt
"
$PACKAGE_DIR
/"
cp
package-hermes-node-agent/
install.sh
"
$PACKAGE_DIR
/install.sh"
cp
install.sh
"
$PACKAGE_DIR
/install.sh"
cp
hermes-node-agent.init.d
"
$PACKAGE_DIR
/"
cp
hermes-node-agent.init.d
"
$PACKAGE_DIR
/"
cp
hermes-node-agent.service
"
$PACKAGE_DIR
/"
cp
hermes-node-agent.service
"
$PACKAGE_DIR
/"
...
@@ -51,7 +51,7 @@ python3 <<'PY'
...
@@ -51,7 +51,7 @@ python3 <<'PY'
import base64, pathlib, re
import base64, pathlib, re
src = pathlib.Path('dist/hermes-node-agent-linux.tar.gz').read_bytes()
src = pathlib.Path('dist/hermes-node-agent-linux.tar.gz').read_bytes()
b64 = base64.b64encode(src).decode('ascii')
b64 = base64.b64encode(src).decode('ascii')
installer_path = pathlib.Path('
package-hermes-node-agent/deploy
/install-node.sh')
installer_path = pathlib.Path('
deploy/linux
/install-node.sh')
text = installer_path.read_text()
text = installer_path.read_text()
pattern = r"(cat <<'TARBALL_DATA' \| base64 -d \| tar xzf -\n)(.*?)(\nTARBALL_DATA)"
pattern = r"(cat <<'TARBALL_DATA' \| base64 -d \| tar xzf -\n)(.*?)(\nTARBALL_DATA)"
new_text, count = re.subn(pattern, lambda m: m.group(1) + b64 + m.group(3), text, flags=re.S)
new_text, count = re.subn(pattern, lambda m: m.group(1) + b64 + m.group(3), text, flags=re.S)
...
@@ -61,9 +61,6 @@ pathlib.Path('deploy/linux/install-node.sh').write_text(new_text)
...
@@ -61,9 +61,6 @@ pathlib.Path('deploy/linux/install-node.sh').write_text(new_text)
print('✅ Re-embedded packaged payload into deploy/linux/install-node.sh')
print('✅ Re-embedded packaged payload into deploy/linux/install-node.sh')
PY
PY
# Keep the root installer copy in sync with the packaged source of truth
cp
package-hermes-node-agent/install.sh install.sh
# Cleanup
# Cleanup
rm
-rf
"
$BUILD_DIR
"
rm
-rf
"
$BUILD_DIR
"
...
...
deploy/linux/install-node.sh
View file @
a3a00221
This diff is collapsed.
Click to expand it.
dist/hermes-node-agent-linux.tar.gz
0 → 100644
View file @
a3a00221
File added
hermes_node_agent.py
View file @
a3a00221
...
@@ -269,9 +269,19 @@ class PosixCommandExecutor(CommandExecutor):
...
@@ -269,9 +269,19 @@ class PosixCommandExecutor(CommandExecutor):
def
execute
(
self
,
command
:
Any
,
approved
:
bool
=
False
)
->
Dict
[
str
,
Any
]:
def
execute
(
self
,
command
:
Any
,
approved
:
bool
=
False
)
->
Dict
[
str
,
Any
]:
allowed
,
reason
=
self
.
_check_permission
(
command
,
approved
)
allowed
,
reason
=
self
.
_check_permission
(
command
,
approved
)
if
not
allowed
and
reason
!=
'ask'
:
if
not
allowed
and
reason
!=
'ask'
:
return
{
'success'
:
False
,
'error'
:
f
'Permission denied: {reason}'
,
'exit_code'
:
127
}
return
{
'success'
:
False
,
'status'
:
'denied'
,
'error'
:
f
'Permission denied: {reason}'
,
'exit_code'
:
127
,
}
if
not
approved
and
reason
==
'ask'
:
if
not
approved
and
reason
==
'ask'
:
return
{
'success'
:
False
,
'error'
:
'Command requires approval'
,
'exit_code'
:
2
}
return
{
'success'
:
False
,
'status'
:
'approval_required'
,
'error'
:
'Command requires approval'
,
'exit_code'
:
2
,
}
if
isinstance
(
command
,
(
list
,
tuple
)):
if
isinstance
(
command
,
(
list
,
tuple
)):
cmd
=
' '
.
join
(
shlex
.
quote
(
str
(
part
))
for
part
in
command
)
cmd
=
' '
.
join
(
shlex
.
quote
(
str
(
part
))
for
part
in
command
)
else
:
else
:
...
@@ -328,9 +338,19 @@ class WindowsCommandExecutor(CommandExecutor):
...
@@ -328,9 +338,19 @@ class WindowsCommandExecutor(CommandExecutor):
def
execute
(
self
,
command
:
str
,
approved
:
bool
=
False
)
->
Dict
[
str
,
Any
]:
def
execute
(
self
,
command
:
str
,
approved
:
bool
=
False
)
->
Dict
[
str
,
Any
]:
allowed
,
reason
=
self
.
_check_permission
(
command
,
approved
)
allowed
,
reason
=
self
.
_check_permission
(
command
,
approved
)
if
not
allowed
and
reason
!=
'ask'
:
if
not
allowed
and
reason
!=
'ask'
:
return
{
'success'
:
False
,
'error'
:
f
'Permission denied: {reason}'
,
'exit_code'
:
127
}
return
{
'success'
:
False
,
'status'
:
'denied'
,
'error'
:
f
'Permission denied: {reason}'
,
'exit_code'
:
127
,
}
if
not
approved
and
reason
==
'ask'
:
if
not
approved
and
reason
==
'ask'
:
return
{
'success'
:
False
,
'error'
:
'Command requires approval'
,
'exit_code'
:
2
}
return
{
'success'
:
False
,
'status'
:
'approval_required'
,
'error'
:
'Command requires approval'
,
'exit_code'
:
2
,
}
try
:
try
:
import
base64
import
base64
encoded
=
base64
.
b64encode
(
command
.
encode
(
'utf-16le'
))
.
decode
(
'ascii'
)
encoded
=
base64
.
b64encode
(
command
.
encode
(
'utf-16le'
))
.
decode
(
'ascii'
)
...
@@ -1321,16 +1341,20 @@ class NodeAgent:
...
@@ -1321,16 +1341,20 @@ class NodeAgent:
})
})
exit_code
=
result
.
get
(
'exit_code'
,
-
1
)
exit_code
=
result
.
get
(
'exit_code'
,
-
1
)
error
=
result
.
get
(
'error'
)
error
=
result
.
get
(
'error'
)
status
=
result
.
get
(
'status'
)
if
error
:
if
error
:
_log_exec_failed
(
command
,
error
,
exit_code
)
_log_exec_failed
(
command
,
error
,
exit_code
)
else
:
else
:
_log_exec_completed
(
command
,
exit_code
)
_log_exec_completed
(
command
,
exit_code
)
await
self
.
_send_json
(
ws
,
{
payload
=
{
'type'
:
'exec_complete'
,
'type'
:
'exec_complete'
,
'id'
:
cmd_id
,
'id'
:
cmd_id
,
'exit_code'
:
exit_code
,
'exit_code'
:
exit_code
,
'error'
:
error
,
'error'
:
error
,
})
}
if
status
:
payload
[
'status'
]
=
status
await
self
.
_send_json
(
ws
,
payload
)
except
Exception
as
e
:
except
Exception
as
e
:
_log_exec_failed
(
command
,
str
(
e
),
-
1
)
_log_exec_failed
(
command
,
str
(
e
),
-
1
)
await
self
.
_send_json
(
ws
,
{
await
self
.
_send_json
(
ws
,
{
...
@@ -1488,8 +1512,7 @@ def main():
...
@@ -1488,8 +1512,7 @@ def main():
parser
.
add_argument
(
'--debug'
,
action
=
'store_true'
,
help
=
'Debug logging'
)
parser
.
add_argument
(
'--debug'
,
action
=
'store_true'
,
help
=
'Debug logging'
)
args
=
parser
.
parse_args
()
args
=
parser
.
parse_args
()
if
args
.
debug
:
_setup_logging
(
args
.
debug
)
logging
.
getLogger
()
.
setLevel
(
logging
.
DEBUG
)
# Load config to check token
# Load config to check token
config
=
NodeAgent
(
args
.
config
)
.
_load_config
(
args
.
config
)
config
=
NodeAgent
(
args
.
config
)
.
_load_config
(
args
.
config
)
...
@@ -1502,7 +1525,43 @@ def main():
...
@@ -1502,7 +1525,43 @@ def main():
try
:
try
:
asyncio
.
run
(
agent
.
connect_and_run
())
asyncio
.
run
(
agent
.
connect_and_run
())
except
KeyboardInterrupt
:
except
KeyboardInterrupt
:
logger
.
info
(
"Shutting down"
)
_log_shutdown
()
except
Exception
as
e
:
_log_connection_error
(
e
)
raise
finally
:
logging
.
shutdown
()
for
stream_name
in
(
'stdout'
,
'stderr'
):
stream
=
getattr
(
sys
,
stream_name
,
None
)
if
stream
is
not
None
:
try
:
stream
.
flush
()
except
Exception
:
pass
if
sys
.
stdout
:
try
:
sys
.
stdout
.
flush
()
except
Exception
:
pass
if
sys
.
stderr
:
try
:
sys
.
stderr
.
flush
()
except
Exception
:
pass
time
.
sleep
(
0.05
)
try
:
os
.
fsync
(
1
)
except
Exception
:
pass
try
:
os
.
fsync
(
2
)
except
Exception
:
pass
sys
.
exit
(
0
)
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
main
()
main
()
install.sh
View file @
a3a00221
...
@@ -322,7 +322,8 @@ echo ""
...
@@ -322,7 +322,8 @@ echo ""
echo
"Next steps:"
echo
"Next steps:"
echo
" 1. Edit config:
$CONFIG_DIR
/config.json"
echo
" 1. Edit config:
$CONFIG_DIR
/config.json"
echo
" 2. Start agent:
$AGENT_DIR
/hermes-node-agent --config
$CONFIG_DIR
/config.json"
echo
" 2. Start agent:
$AGENT_DIR
/hermes-node-agent --config
$CONFIG_DIR
/config.json"
echo
" 3. Verify logs: tail -f /tmp/hermes-node-agent.log"
echo
" 3. Verify logs live:
$AGENT_DIR
/hermes-node-agent --config
$CONFIG_DIR
/config.json"
echo
" (or with debug:
$AGENT_DIR
/hermes-node-agent --debug --config
$CONFIG_DIR
/config.json)"
echo
""
echo
""
echo
"For Windows nodes, see WINDOWS_INSTALL.md"
echo
"For Windows nodes, see WINDOWS_INSTALL.md"
echo
"For full deployment guide, see DEPLOYMENT.md"
echo
"For full deployment guide, see DEPLOYMENT.md"
release/bundle/hermes-node-agent-installer.sh
View file @
a3a00221
This diff is collapsed.
Click to expand it.
release/hermes-node-agent-bundle.zip
0 → 100644
View file @
a3a00221
File added
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