Files
luzia/lib/chat_bash_executor.py
admin ec33ac1936 Refactor cockpit to use DockerTmuxController pattern
Based on claude-code-tools TmuxCLIController, this refactor:

- Added DockerTmuxController class for robust tmux session management
- Implements send_keys() with configurable delay_enter
- Implements capture_pane() for output retrieval
- Implements wait_for_prompt() for pattern-based completion detection
- Implements wait_for_idle() for content-hash-based idle detection
- Implements wait_for_shell_prompt() for shell prompt detection

Also includes workflow improvements:
- Pre-task git snapshot before agent execution
- Post-task commit protocol in agent guidelines

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 10:42:16 -03:00

124 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""
Chat Bash Executor - Safe, limited bash command execution
Only allows read-only system status commands
"""
import subprocess
import time
from typing import Dict
class ChatBashExecutor:
"""Execute safe read-only bash commands for chat interface"""
# Whitelist of allowed commands (read-only only)
ALLOWED_COMMANDS = {
'uptime': 'uptime',
'load': 'cat /proc/loadavg',
'disk': 'df -h /',
'memory': 'free -h',
'services': 'systemctl --no-pager list-units --type=service --all',
'active_services': 'systemctl --no-pager list-units --type=service --state=running',
'failed_services': 'systemctl --no-pager list-units --type=service --state=failed',
'ps': 'ps aux | head -20',
'docker_ps': 'docker ps',
'docker_stats': 'docker stats --no-stream',
'nginx_status': 'systemctl --no-pager status nginx',
'date': 'date',
'hostname': 'hostname',
'whoami': 'whoami',
'pwd': 'pwd',
'ls_home': 'ls -lah /home/admin | head -20',
'du_home': 'du -sh /home/admin/* 2>/dev/null | sort -h',
}
def __init__(self, timeout_ms: int = 300):
"""Initialize with execution timeout"""
self.timeout_ms = timeout_ms
self.timeout_seconds = timeout_ms / 1000.0
def execute(self, command_name: str) -> Dict:
"""Execute a whitelisted command"""
if command_name not in self.ALLOWED_COMMANDS:
return {
'error': f'Command "{command_name}" not allowed',
'allowed_commands': list(self.ALLOWED_COMMANDS.keys())
}
command = self.ALLOWED_COMMANDS[command_name]
try:
start_time = time.time()
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=self.timeout_seconds
)
execution_time_ms = (time.time() - start_time) * 1000
return {
'command': command_name,
'success': result.returncode == 0,
'output': result.stdout.strip(),
'error': result.stderr.strip() if result.stderr else None,
'exit_code': result.returncode,
'execution_time_ms': round(execution_time_ms, 2)
}
except subprocess.TimeoutExpired:
return {
'command': command_name,
'error': f'Command timed out after {self.timeout_ms}ms',
'success': False
}
except Exception as e:
return {
'command': command_name,
'error': str(e),
'success': False
}
def system_status(self) -> Dict:
"""Quick system status summary"""
status = {
'timestamp': time.time(),
'components': {}
}
for check_name in ['uptime', 'load', 'disk', 'memory']:
result = self.execute(check_name)
status['components'][check_name] = {
'success': result.get('success', False),
'output': result.get('output', '')[:200] # First 200 chars
}
return status
def list_allowed_commands(self) -> Dict:
"""List all allowed commands"""
return {
'allowed_commands': [
{'name': name, 'description': cmd}
for name, cmd in self.ALLOWED_COMMANDS.items()
],
'count': len(self.ALLOWED_COMMANDS),
'timeout_ms': self.timeout_ms
}
if __name__ == '__main__':
import json
executor = ChatBashExecutor()
print("System Status:")
print(json.dumps(executor.system_status(), indent=2, default=str))
print()
print("Uptime:")
print(json.dumps(executor.execute('uptime'), indent=2))