Commit a3a00221 authored by Lisa's avatar Lisa

fix: sync approval flow in gateway and rebuild node-agent bundle

parent dc1a4f86
...@@ -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"
......
This diff is collapsed.
...@@ -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()
...@@ -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"
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