Commit 645a7481 authored by Lisa (Hermes AI)'s avatar Lisa (Hermes AI)

fix: surface approval-required and denied node exec states

parent 3c675690
...@@ -673,20 +673,32 @@ class NodeGateway: ...@@ -673,20 +673,32 @@ class NodeGateway:
cmd_id = msg.get('id') cmd_id = msg.get('id')
exec_status = msg.get('status')
with self._commands_lock: with self._commands_lock:
if cmd_id in self.commands: if cmd_id in self.commands:
cmd = self.commands[cmd_id] cmd = self.commands[cmd_id]
cmd.status = 'completed' if msg.get('exit_code') == 0 else 'failed'
cmd.exit_code = msg.get('exit_code') cmd.exit_code = msg.get('exit_code')
cmd.completed_at = time.time() cmd.completed_at = time.time()
cmd.error = msg.get('error') cmd.error = msg.get('error')
if exec_status == 'approval_required':
cmd.status = 'approval_required'
elif exec_status == 'denied':
cmd.status = 'denied'
else:
cmd.status = 'completed' if cmd.exit_code == 0 and not cmd.error else 'failed'
# Notify waiter # Notify waiter
...@@ -2057,11 +2069,19 @@ class NodeGateway: ...@@ -2057,11 +2069,19 @@ class NodeGateway:
if self._matches_pattern(cmd_str, pattern): if self._matches_pattern(cmd_str, pattern):
raise PermissionError( return {
'status': 'denied',
f"Command denied: matches deny pattern '{pattern}'" 'exit_code': 126,
'stdout': '',
) 'stderr': '',
'error': f"Command denied: matches deny pattern '{pattern}'",
'denial': {
'type': 'deny',
'pattern': pattern,
'approved': bool(approved),
'command': normalized_command,
},
}
# Ask list - requires approval # Ask list - requires approval
...@@ -2072,11 +2092,18 @@ class NodeGateway: ...@@ -2072,11 +2092,18 @@ class NodeGateway:
if self._matches_pattern(cmd_str, pattern): if self._matches_pattern(cmd_str, pattern):
raise PermissionError( return {
'status': 'approval_required',
f"Command requires approval: matches pattern '{pattern}'" 'exit_code': 2,
'stdout': '',
) 'stderr': '',
'error': f"Command requires approval: matches pattern '{pattern}'",
'approval': {
'required': True,
'pattern': pattern,
'command': normalized_command,
},
}
# Check allow list (if configured) # Check allow list (if configured)
...@@ -2153,7 +2180,7 @@ class NodeGateway: ...@@ -2153,7 +2180,7 @@ class NodeGateway:
result = await asyncio.wait_for(future, timeout=timeout + 5) result = await asyncio.wait_for(future, timeout=timeout + 5)
return { response = {
'id': result.id, 'id': result.id,
...@@ -2173,6 +2200,28 @@ class NodeGateway: ...@@ -2173,6 +2200,28 @@ class NodeGateway:
} }
if result.status == 'approval_required':
response['approval'] = {
'required': True,
'command': normalized_command,
}
elif result.status == 'denied':
response['denial'] = {
'type': 'deny',
'command': normalized_command,
}
return response
except asyncio.TimeoutError: except asyncio.TimeoutError:
cmd.status = 'failed' cmd.status = 'failed'
......
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